Add MSAA support to D3D12 PS debugging

This commit is contained in:
Steve Karolewics
2020-03-04 19:11:56 +00:00
committed by Baldur Karlsson
parent a5583b83ee
commit a6c3177736
6 changed files with 392 additions and 61 deletions
+24 -19
View File
@@ -1843,15 +1843,15 @@ void MoveRootSignatureElementsToRegisterSpace(D3D12RootSignature &sig, uint32_t
}
}
void AddDebugDescriptorToRenderState(WrappedID3D12Device *pDevice, D3D12RenderState &rs,
const PortableHandle &handle,
D3D12_DESCRIPTOR_HEAP_TYPE heapType, uint32_t sigElem,
std::set<ResourceId> &copiedHeaps)
void AddDebugDescriptorsToRenderState(WrappedID3D12Device *pDevice, D3D12RenderState &rs,
const rdcarray<PortableHandle> &handles,
D3D12_DESCRIPTOR_HEAP_TYPE heapType, uint32_t sigElem,
std::set<ResourceId> &copiedHeaps)
{
if(rs.graphics.sigelems.size() <= sigElem)
rs.graphics.sigelems.resize(sigElem + 1);
PortableHandle newHandle = handle;
PortableHandle newHandle = handles[0];
// If a CBV_SRV_UAV heap is already set and hasn't had a debug descriptor copied in,
// copy the desired descriptor in and add the heap to the set of heaps that have had
@@ -1866,33 +1866,38 @@ void AddDebugDescriptorToRenderState(WrappedID3D12Device *pDevice, D3D12RenderSt
pDevice->GetResourceManager()->GetCurrentAs<WrappedID3D12DescriptorHeap>(rs.heaps[i]);
if(h->GetDesc().Type == heapType)
{
// use the last descriptor
// use the last descriptors
D3D12_CPU_DESCRIPTOR_HANDLE dst = h->GetCPUDescriptorHandleForHeapStart();
dst.ptr += (h->GetDesc().NumDescriptors - 1) * sizeof(D3D12Descriptor);
dst.ptr += (h->GetDesc().NumDescriptors - handles.size()) * sizeof(D3D12Descriptor);
newHandle = ToPortableHandle(dst);
if(copiedHeaps.find(rs.heaps[i]) == copiedHeaps.end())
{
WrappedID3D12DescriptorHeap *h2 =
pDevice->GetResourceManager()->GetCurrentAs<WrappedID3D12DescriptorHeap>(handle.heap);
D3D12_CPU_DESCRIPTOR_HANDLE src = h2->GetCPUDescriptorHandleForHeapStart();
src.ptr += handle.index * sizeof(D3D12Descriptor);
for(size_t j = 0; j < handles.size(); ++j)
{
WrappedID3D12DescriptorHeap *h2 =
pDevice->GetResourceManager()->GetCurrentAs<WrappedID3D12DescriptorHeap>(
handles[j].heap);
D3D12_CPU_DESCRIPTOR_HANDLE src = h2->GetCPUDescriptorHandleForHeapStart();
src.ptr += handles[j].index * sizeof(D3D12Descriptor);
// can't do a copy because the src heap is CPU write-only (shader visible). So instead,
// create directly
D3D12Descriptor *srcDesc = (D3D12Descriptor *)src.ptr;
srcDesc->Create(heapType, pDevice, dst);
// can't do a copy because the src heap is CPU write-only (shader visible). So instead,
// create directly
D3D12Descriptor *srcDesc = (D3D12Descriptor *)src.ptr;
srcDesc->Create(heapType, pDevice, dst);
dst.ptr += sizeof(D3D12Descriptor);
}
copiedHeaps.insert(rs.heaps[i]);
}
newHandle = ToPortableHandle(dst);
break;
}
}
if(newHandle.heap == handle.heap)
rs.heaps.push_back(handle.heap);
if(newHandle.heap == handles[0].heap)
rs.heaps.push_back(handles[0].heap);
rs.graphics.sigelems[sigElem] =
D3D12RenderState::SignatureElement(eRootTable, newHandle.heap, newHandle.index);
+5 -4
View File
@@ -60,6 +60,7 @@ enum CBVUAVSRVSlot
PICK_RESULT_CLEAR_UAV,
SHADER_DEBUG_UAV,
SHADER_DEBUG_MSAA_UAV,
TMP_UAV,
@@ -222,7 +223,7 @@ void MoveRootSignatureElementsToRegisterSpace(D3D12RootSignature &sig, uint32_t
D3D12DescriptorType type,
D3D12_SHADER_VISIBILITY visibility);
void AddDebugDescriptorToRenderState(WrappedID3D12Device *pDevice, D3D12RenderState &rs,
const PortableHandle &handle,
D3D12_DESCRIPTOR_HEAP_TYPE heapType, uint32_t sigElem,
std::set<ResourceId> &copiedHeaps);
void AddDebugDescriptorsToRenderState(WrappedID3D12Device *pDevice, D3D12RenderState &rs,
const rdcarray<PortableHandle> &handles,
D3D12_DESCRIPTOR_HEAP_TYPE heapType, uint32_t sigElem,
std::set<ResourceId> &copiedHeaps);
+2 -2
View File
@@ -145,8 +145,8 @@ struct D3D12QuadOverdrawCallback : public D3D12DrawcallCallback
rs.pipe = GetResID(cache.pipe);
rs.graphics.rootsig = GetResID(cache.sig);
AddDebugDescriptorToRenderState(m_pDevice, rs, m_UAV, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV,
cache.sigElem, m_CopiedHeaps);
AddDebugDescriptorsToRenderState(m_pDevice, rs, {m_UAV}, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV,
cache.sigElem, m_CopiedHeaps);
// as we're changing the root signature, we need to reapply all elements,
// so just apply all state
+268 -32
View File
@@ -2025,15 +2025,102 @@ ShaderDebugTrace *D3D12Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t
}
}
// get the multisample count
uint32_t outputSampleCount = RDCMAX(1U, pipelineState->outputMerger.multiSampleCount);
// Store a copy of the event's render state to restore later
D3D12RenderState &rs = m_pDevice->GetQueue()->GetCommandData()->m_RenderState;
D3D12RenderState prevState = rs;
// Fetch the multisample count from the PSO
WrappedID3D12PipelineState *origPSO =
m_pDevice->GetResourceManager()->GetCurrentAs<WrappedID3D12PipelineState>(rs.pipe);
D3D12_EXPANDED_PIPELINE_STATE_STREAM_DESC pipeDesc;
origPSO->Fill(pipeDesc);
uint32_t outputSampleCount = RDCMAX(1U, pipeDesc.SampleDesc.Count);
std::set<GlobalState::SampleEvalCacheKey> evalSampleCacheData;
uint64_t sampleEvalRegisterMask = 0;
// if we're not rendering at MSAA, no need to fill the cache because evaluates will all return the
// plain input anyway.
if(outputSampleCount > 1)
{
RDCUNIMPLEMENTED("MSAA debugging not yet implemented for D3D12");
return new ShaderDebugTrace;
// scan the instructions to see if it contains any evaluates.
size_t numInstructions = dxbc->GetDXBCByteCode()->GetNumInstructions();
for(size_t i = 0; i < numInstructions; ++i)
{
const Operation &op = dxbc->GetDXBCByteCode()->GetInstruction(i);
// skip any non-eval opcodes
if(op.operation != OPCODE_EVAL_CENTROID && op.operation != OPCODE_EVAL_SAMPLE_INDEX &&
op.operation != OPCODE_EVAL_SNAPPED)
continue;
// the generation of this key must match what we'll generate in the corresponding lookup
GlobalState::SampleEvalCacheKey key;
// all the eval opcodes have rDst, vIn as the first two operands
key.inputRegisterIndex = (int32_t)op.operands[1].indices[0].index;
for(int c = 0; c < 4; c++)
{
if(op.operands[0].comps[c] == 0xff)
break;
key.numComponents = c + 1;
}
key.firstComponent = op.operands[1].comps[op.operands[0].comps[0]];
sampleEvalRegisterMask |= 1ULL << key.inputRegisterIndex;
if(op.operation == OPCODE_EVAL_CENTROID)
{
// nothing to do - default key is centroid, sample is -1 and offset x/y is 0
evalSampleCacheData.insert(key);
}
else if(op.operation == OPCODE_EVAL_SAMPLE_INDEX)
{
if(op.operands[2].type == TYPE_IMMEDIATE32 || op.operands[2].type == TYPE_IMMEDIATE64)
{
// hooray, only sampling a single index, just add this key
key.sample = (int32_t)op.operands[2].values[0];
evalSampleCacheData.insert(key);
}
else
{
// parameter is a register and we don't know which sample will be needed, fetch them all.
// In most cases this will be a loop over them all, so they'll all be needed anyway
for(uint32_t c = 0; c < outputSampleCount; c++)
{
key.sample = (int32_t)c;
evalSampleCacheData.insert(key);
}
}
}
else if(op.operation == OPCODE_EVAL_SNAPPED)
{
if(op.operands[2].type == TYPE_IMMEDIATE32 || op.operands[2].type == TYPE_IMMEDIATE64)
{
// hooray, only sampling a single offset, just add this key
key.offsetx = (int32_t)op.operands[2].values[0];
key.offsety = (int32_t)op.operands[2].values[1];
evalSampleCacheData.insert(key);
}
else
{
m_pDevice->AddDebugMessage(
MessageCategory::Shaders, MessageSeverity::Medium, MessageSource::RuntimeWarning,
"EvaluateAttributeSnapped called with dynamic parameter, caching all possible "
"evaluations which could have performance impact.");
for(key.offsetx = -8; key.offsetx <= 7; key.offsetx++)
for(key.offsety = -8; key.offsety <= 7; key.offsety++)
evalSampleCacheData.insert(key);
}
}
}
}
extractHlsl += R"(
@@ -2058,7 +2145,15 @@ struct PSInitialData
)";
extractHlsl += "RWStructuredBuffer<PSInitialData> PSInitialBuffer : register(u0);\n\n";
// If this event uses MSAA, then at least one render target must be preserved to get
// multisampling info. leave u0 alone and start with register u1
extractHlsl += "RWStructuredBuffer<PSInitialData> PSInitialBuffer : register(u1);\n\n";
if(!evalSampleCacheData.empty())
{
// float4 is wasteful in some cases but it's easier than using byte buffers and manual packing
extractHlsl += "RWBuffer<float4> PSEvalBuffer : register(u2);\n\n";
}
if(usePrimitiveID)
{
@@ -2101,6 +2196,69 @@ void ExtractInputsPS(PSInput IN, float4 debug_pixelPos : SV_Position,
extractHlsl += " PSInitialBuffer[idx].INddxfine = (PSInput)0;\n";
extractHlsl += " PSInitialBuffer[idx].INddyfine = (PSInput)0;\n";
if(!evalSampleCacheData.empty())
{
extractHlsl += StringFormat::Fmt(" uint evalIndex = idx * %zu;\n", evalSampleCacheData.size());
uint32_t evalIdx = 0;
for(const GlobalState::SampleEvalCacheKey &key : evalSampleCacheData)
{
uint32_t keyMask = 0;
for(int32_t i = 0; i < key.numComponents; i++)
keyMask |= (1 << (key.firstComponent + i));
// find the name of the variable matching the operand, in the case of merged input variables.
rdcstr name, swizzle = "xyzw";
for(size_t i = 0; i < dxbc->GetReflection()->InputSig.size(); i++)
{
if(dxbc->GetReflection()->InputSig[i].regIndex == (uint32_t)key.inputRegisterIndex &&
dxbc->GetReflection()->InputSig[i].systemValue == ShaderBuiltin::Undefined &&
(dxbc->GetReflection()->InputSig[i].regChannelMask & keyMask) == keyMask)
{
name = inputVarNames[i];
if(!name.empty())
break;
}
}
swizzle.resize(key.numComponents);
if(name.empty())
{
RDCERR("Couldn't find matching input variable for v%d [%d:%d]", key.inputRegisterIndex,
key.firstComponent, key.numComponents);
extractHlsl += StringFormat::Fmt(" PSEvalBuffer[evalIndex+%u] = 0;\n", evalIdx);
evalIdx++;
continue;
}
name = StringFormat::Fmt("IN.%s.%s", name.c_str(), swizzle.c_str());
// we must write all components, so just swizzle the values - they'll be ignored later.
rdcstr expandSwizzle = swizzle;
while(expandSwizzle.size() < 4)
expandSwizzle.push_back('x');
if(key.sample >= 0)
{
extractHlsl += StringFormat::Fmt(
" PSEvalBuffer[evalIndex+%u] = EvaluateAttributeAtSample(%s, %d).%s;\n", evalIdx,
name.c_str(), key.sample, expandSwizzle.c_str());
}
else
{
// we don't need to special-case EvaluateAttributeAtCentroid, since it's just a case with
// 0,0
extractHlsl += StringFormat::Fmt(
" PSEvalBuffer[evalIndex+%u] = EvaluateAttributeSnapped(%s, int2(%d, %d)).%s;\n",
evalIdx, name.c_str(), key.offsetx, key.offsety, expandSwizzle.c_str());
}
evalIdx++;
}
}
for(size_t i = 0; i < floatInputs.size(); i++)
{
const rdcstr &name = floatInputs[i];
@@ -2166,6 +2324,24 @@ void ExtractInputsPS(PSInput IN, float4 debug_pixelPos : SV_Position,
return new ShaderDebugTrace;
}
// Create buffer to store MSAA evaluations captured in pixel shader
ID3D12Resource *pMsaaEvalBuffer = NULL;
if(!evalSampleCacheData.empty())
{
rdesc.Width = UINT(evalSampleCacheData.size() * sizeof(Vec4f) * (overdrawLevels + 1));
hr = m_pDevice->CreateCommittedResource(&heapProps, D3D12_HEAP_FLAG_NONE, &rdesc, resourceState,
NULL, __uuidof(ID3D12Resource),
(void **)&pMsaaEvalBuffer);
if(FAILED(hr))
{
RDCERR("Failed to create MSAA buffer for pixel shader debugging HRESULT: %s",
ToStr(hr).c_str());
SAFE_RELEASE(pInitialValuesBuffer);
SAFE_RELEASE(psBlob);
return new ShaderDebugTrace;
}
}
// Create UAV of initial values buffer
D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc;
ZeroMemory(&uavDesc, sizeof(D3D12_UNORDERED_ACCESS_VIEW_DESC));
@@ -2185,9 +2361,22 @@ void ExtractInputsPS(PSInput IN, float4 debug_pixelPos : SV_Position,
m_pDevice->GetDebugManager()->GetUAVClearHandle(SHADER_DEBUG_UAV);
m_pDevice->CreateUnorderedAccessView(pInitialValuesBuffer, NULL, &uavDesc, clearUav);
// Store a copy of the event's render state to restore later
D3D12RenderState &rs = m_pDevice->GetQueue()->GetCommandData()->m_RenderState;
D3D12RenderState prevState = rs;
// Create UAV of MSAA eval buffer
D3D12_CPU_DESCRIPTOR_HANDLE msaaClearUav =
m_pDevice->GetDebugManager()->GetUAVClearHandle(SHADER_DEBUG_MSAA_UAV);
if(pMsaaEvalBuffer)
{
D3D12_CPU_DESCRIPTOR_HANDLE msaaUav =
m_pDevice->GetDebugManager()->GetCPUHandle(SHADER_DEBUG_MSAA_UAV);
uavDesc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
uavDesc.Buffer.NumElements = (overdrawLevels + 1) * (uint32_t)evalSampleCacheData.size();
m_pDevice->CreateUnorderedAccessView(pMsaaEvalBuffer, NULL, &uavDesc, msaaUav);
uavDesc.Format = DXGI_FORMAT_R32_UINT;
uavDesc.Buffer.NumElements =
(UINT)evalSampleCacheData.size() * (overdrawLevels + 1) / sizeof(uint32_t);
m_pDevice->CreateUnorderedAccessView(pMsaaEvalBuffer, NULL, &uavDesc, msaaClearUav);
}
WrappedID3D12RootSignature *sig =
m_pDevice->GetResourceManager()->GetCurrentAs<WrappedID3D12RootSignature>(rs.graphics.rootsig);
@@ -2203,8 +2392,8 @@ void ExtractInputsPS(PSInput IN, float4 debug_pixelPos : SV_Position,
// Create the descriptor table for our UAV
D3D12_DESCRIPTOR_RANGE1 descRange;
descRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
descRange.NumDescriptors = 1;
descRange.BaseShaderRegister = 0;
descRange.NumDescriptors = pMsaaEvalBuffer ? 2 : 1;
descRange.BaseShaderRegister = 1;
descRange.RegisterSpace = 0;
descRange.Flags = D3D12_DESCRIPTOR_RANGE_FLAG_NONE;
descRange.OffsetInDescriptorsFromTableStart = 0;
@@ -2230,18 +2419,11 @@ void ExtractInputsPS(PSInput IN, float4 debug_pixelPos : SV_Position,
SAFE_RELEASE(root);
SAFE_RELEASE(psBlob);
SAFE_RELEASE(pInitialValuesBuffer);
SAFE_RELEASE(pMsaaEvalBuffer);
return new ShaderDebugTrace;
}
SAFE_RELEASE(root);
WrappedID3D12PipelineState *origPSO =
m_pDevice->GetResourceManager()->GetCurrentAs<WrappedID3D12PipelineState>(rs.pipe);
RDCASSERT(origPSO->IsGraphics());
D3D12_EXPANDED_PIPELINE_STATE_STREAM_DESC pipeDesc;
origPSO->Fill(pipeDesc);
// All PSO state is the same as the event's, except for the pixel shader and root signature
pipeDesc.PS.BytecodeLength = psBlob->GetBufferSize();
pipeDesc.PS.pShaderBytecode = psBlob->GetBufferPointer();
@@ -2254,15 +2436,19 @@ void ExtractInputsPS(PSInput IN, float4 debug_pixelPos : SV_Position,
RDCERR("Failed to create PSO for pixel shader debugging HRESULT: %s", ToStr(hr).c_str());
SAFE_RELEASE(psBlob);
SAFE_RELEASE(pInitialValuesBuffer);
SAFE_RELEASE(pMsaaEvalBuffer);
SAFE_RELEASE(pRootSignature);
return new ShaderDebugTrace;
}
// Add the descriptor for our UAV, then clear it
std::set<ResourceId> copiedHeaps;
PortableHandle shaderDebugUav = ToPortableHandle(GetDebugManager()->GetCPUHandle(SHADER_DEBUG_UAV));
AddDebugDescriptorToRenderState(m_pDevice, rs, shaderDebugUav,
D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, sigElem, copiedHeaps);
rdcarray<PortableHandle> debugHandles;
debugHandles.push_back(ToPortableHandle(GetDebugManager()->GetCPUHandle(SHADER_DEBUG_UAV)));
if(pMsaaEvalBuffer)
debugHandles.push_back(ToPortableHandle(GetDebugManager()->GetCPUHandle(SHADER_DEBUG_MSAA_UAV)));
AddDebugDescriptorsToRenderState(m_pDevice, rs, debugHandles,
D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, sigElem, copiedHeaps);
ID3D12GraphicsCommandListX *cmdList = m_pDevice->GetDebugManager()->ResetDebugList();
rs.ApplyDescriptorHeaps(cmdList);
@@ -2270,6 +2456,13 @@ void ExtractInputsPS(PSInput IN, float4 debug_pixelPos : SV_Position,
UINT zero[4] = {0, 0, 0, 0};
cmdList->ClearUnorderedAccessViewUint(gpuUav, clearUav, pInitialValuesBuffer, zero, 0, NULL);
if(pMsaaEvalBuffer)
{
D3D12_GPU_DESCRIPTOR_HANDLE gpuMsaaUav =
m_pDevice->GetDebugManager()->GetGPUHandle(SHADER_DEBUG_MSAA_UAV);
cmdList->ClearUnorderedAccessViewUint(gpuMsaaUav, msaaClearUav, pMsaaEvalBuffer, zero, 0, NULL);
}
// Execute the command to ensure that UAV clear and resource creation occur before replay
hr = cmdList->Close();
if(FAILED(hr))
@@ -2277,6 +2470,7 @@ void ExtractInputsPS(PSInput IN, float4 debug_pixelPos : SV_Position,
RDCERR("Failed to close command list HRESULT: %s", ToStr(hr).c_str());
SAFE_RELEASE(psBlob);
SAFE_RELEASE(pInitialValuesBuffer);
SAFE_RELEASE(pMsaaEvalBuffer);
SAFE_RELEASE(pRootSignature);
SAFE_RELEASE(initialPso);
return new ShaderDebugTrace;
@@ -2306,11 +2500,16 @@ void ExtractInputsPS(PSInput IN, float4 debug_pixelPos : SV_Position,
bytebuf initialData;
m_pDevice->GetDebugManager()->GetBufferData(pInitialValuesBuffer, 0, 0, initialData);
bytebuf evalData;
if(pMsaaEvalBuffer)
m_pDevice->GetDebugManager()->GetBufferData(pMsaaEvalBuffer, 0, 0, evalData);
// Replaying the event has finished, and the data has been copied out.
// Free all the resources that were created.
SAFE_RELEASE(psBlob);
SAFE_RELEASE(pRootSignature);
SAFE_RELEASE(pInitialValuesBuffer);
SAFE_RELEASE(pMsaaEvalBuffer);
SAFE_RELEASE(initialPso);
DebugHit *buf = (DebugHit *)initialData.data();
@@ -2340,6 +2539,7 @@ void ExtractInputsPS(PSInput IN, float4 debug_pixelPos : SV_Position,
// Get depth func and determine "winner" pixel
D3D12_COMPARISON_FUNC depthFunc = pipeDesc.DepthStencilState.DepthFunc;
DebugHit *pWinnerHit = NULL;
float *evalSampleCache = (float *)evalData.data();
if(sample == ~0U)
sample = 0;
@@ -2353,6 +2553,7 @@ void ExtractInputsPS(PSInput IN, float4 debug_pixelPos : SV_Position,
if(pHit->primitive == primitive && pHit->sample == sample)
{
pWinnerHit = pHit;
evalSampleCache = ((float *)evalData.data() + evalSampleCacheData.size() * 4 * i);
}
}
}
@@ -2363,22 +2564,36 @@ void ExtractInputsPS(PSInput IN, float4 debug_pixelPos : SV_Position,
{
DebugHit *pHit = (DebugHit *)(initialData.data() + i * structStride);
if(pWinnerHit == NULL || (pWinnerHit->sample != sample && pHit->sample == sample) ||
depthFunc == D3D12_COMPARISON_FUNC_ALWAYS || depthFunc == D3D12_COMPARISON_FUNC_NEVER ||
depthFunc == D3D12_COMPARISON_FUNC_NOT_EQUAL || depthFunc == D3D12_COMPARISON_FUNC_EQUAL)
if(pWinnerHit == NULL)
{
// If we haven't picked a winner at all yet, use the first one
pWinnerHit = pHit;
continue;
evalSampleCache = ((float *)evalData.data()) + evalSampleCacheData.size() * 4 * i;
}
if((depthFunc == D3D12_COMPARISON_FUNC_LESS && pHit->depth < pWinnerHit->depth) ||
(depthFunc == D3D12_COMPARISON_FUNC_LESS_EQUAL && pHit->depth <= pWinnerHit->depth) ||
(depthFunc == D3D12_COMPARISON_FUNC_GREATER && pHit->depth > pWinnerHit->depth) ||
(depthFunc == D3D12_COMPARISON_FUNC_GREATER_EQUAL && pHit->depth >= pWinnerHit->depth))
else if(pHit->sample == sample)
{
if(pHit->sample == sample)
// If this hit is for the sample we want, check whether it's a better pick
if(pWinnerHit->sample != sample)
{
// The previously selected winner was for the wrong sample, use this one
pWinnerHit = pHit;
evalSampleCache = ((float *)evalData.data()) + evalSampleCacheData.size() * 4 * i;
}
else if((depthFunc == D3D11_COMPARISON_ALWAYS || depthFunc == D3D11_COMPARISON_NEVER ||
depthFunc == D3D11_COMPARISON_NOT_EQUAL || depthFunc == D3D11_COMPARISON_EQUAL))
{
// For depth functions without an inequality comparison, use the last sample encountered
pWinnerHit = pHit;
evalSampleCache = ((float *)evalData.data()) + evalSampleCacheData.size() * 4 * i;
}
else if((depthFunc == D3D11_COMPARISON_LESS && pHit->depth < pWinnerHit->depth) ||
(depthFunc == D3D11_COMPARISON_LESS_EQUAL && pHit->depth <= pWinnerHit->depth) ||
(depthFunc == D3D11_COMPARISON_GREATER && pHit->depth > pWinnerHit->depth) ||
(depthFunc == D3D11_COMPARISON_GREATER_EQUAL && pHit->depth >= pWinnerHit->depth))
{
// For depth functions with an inequality, find the hit that "wins" the most
pWinnerHit = pHit;
evalSampleCache = ((float *)evalData.data()) + evalSampleCacheData.size() * 4 * i;
}
}
}
@@ -2399,6 +2614,8 @@ void ExtractInputsPS(PSInput IN, float4 debug_pixelPos : SV_Position,
GatherConstantBuffers(m_pDevice, *dxbc->GetDXBCByteCode(), rs.graphics, refl,
origPSO->PS()->GetMapping(), global, ret->sourceVars);
global.sampleEvalRegisterMask = sampleEvalRegisterMask;
{
DebugHit *pHit = pWinnerHit;
@@ -2472,7 +2689,26 @@ void ExtractInputsPS(PSInput IN, float4 debug_pixelPos : SV_Position,
}
}
// TODO: Handle inputs that were evaluated at sample granularity (MSAA)
// Fetch any inputs that were evaluated at sample granularity
for(const GlobalState::SampleEvalCacheKey &key : evalSampleCacheData)
{
// start with the basic input value
ShaderVariable var = state.inputs[key.inputRegisterIndex];
// copy over the value into the variable
memcpy(var.value.fv, evalSampleCache, var.columns * sizeof(float));
// store in the global cache for each quad. We'll apply derivatives below to adjust for each
GlobalState::SampleEvalCacheKey k = key;
for(int i = 0; i < 4; i++)
{
k.quadIndex = i;
global.sampleEvalCache[k] = var;
}
// advance past this data - always by float4 as that's the buffer stride
evalSampleCache += 4;
}
ApplyAllDerivatives(global, interpreter->workgroup, destIdx, initialValues, (float *)data);
}
@@ -576,6 +576,31 @@ float4 main(v2f IN) : SV_Target0
return float4(0.4f, 0.4f, 0.4f, 0.4f);
}
)EOSHADER";
std::string msaaPixel = R"EOSHADER(
struct v2f
{
float4 pos : SV_POSITION;
float4 col : COLOR0;
float2 uv : TEXCOORD0;
};
float4 main(v2f IN, uint samp : SV_SampleIndex) : SV_Target0
{
float2 uvCentroid = EvaluateAttributeCentroid(IN.uv);
float2 uvSamp0 = EvaluateAttributeAtSample(IN.uv, 0) - IN.uv;
float2 uvSampThis = EvaluateAttributeAtSample(IN.uv, samp) - IN.uv;
float2 uvOffset = EvaluateAttributeSnapped(IN.uv, int2(1, 1));
float x = (uvCentroid.x + uvCentroid.y) * 0.5f;
float y = (uvSamp0.x + uvSamp0.y) * 0.5f;
float z = (uvSampThis.x + uvSampThis.y) * 0.5f;
float w = (uvOffset.x + uvOffset.y) * 0.5f;
return float4(x, y, z, w);
}
)EOSHADER";
int main()
@@ -705,6 +730,22 @@ float4 main(v2f IN) : SV_Target0
D3D12_CPU_DESCRIPTOR_HANDLE uav2cpu = uavView2.CreateClearCPU(5);
D3D12_GPU_DESCRIPTOR_HANDLE uav2gpu = uavView2.CreateGPU(5);
// Create resources for MSAA draw
ID3DBlobPtr vsmsaablob = Compile(D3DDefaultVertex, "main", "vs_5_0");
ID3DBlobPtr psmsaablob = Compile(msaaPixel, "main", "ps_5_0");
ID3D12RootSignaturePtr sigmsaa = MakeSig({});
ID3D12PipelineStatePtr psomsaa =
MakePSO().RootSig(sigmsaa).InputLayout().VS(vsmsaablob).PS(psmsaablob).SampleCount(4);
ID3D12ResourcePtr vbmsaa = MakeBuffer().Data(DefaultTri);
ID3D12ResourcePtr msaaTex = MakeTexture(DXGI_FORMAT_R32G32B32A32_FLOAT, 8, 8)
.RTV()
.Multisampled(4)
.InitialState(D3D12_RESOURCE_STATE_RENDER_TARGET);
D3D12_CPU_DESCRIPTOR_HANDLE msaaRTV = MakeRTV(msaaTex).CreateCPU(1);
vsblob = Compile(D3DFullscreenQuadVertex, "main", "vs_4_0");
psblob = Compile(pixelBlit, "main", "ps_5_0");
ID3D12RootSignaturePtr blitSig = MakeSig({
@@ -721,7 +762,7 @@ float4 main(v2f IN) : SV_Target0
ID3D12ResourcePtr bb = StartUsingBackbuffer(cmd, D3D12_RESOURCE_STATE_RENDER_TARGET);
D3D12_CPU_DESCRIPTOR_HANDLE rtv =
MakeRTV(bb).Format(DXGI_FORMAT_R8G8B8A8_UNORM_SRGB).CreateCPU(1);
MakeRTV(bb).Format(DXGI_FORMAT_R8G8B8A8_UNORM_SRGB).CreateCPU(2);
ClearRenderTargetView(cmd, rtv, {0.2f, 0.2f, 0.2f, 1.0f});
ID3D12PipelineStatePtr psos[2] = {pso_5_0, pso_5_1};
@@ -732,13 +773,12 @@ float4 main(v2f IN) : SV_Target0
// Clear, draw, and blit to backbuffer twice - once for SM 5.0 and again for SM 5.1
for(int i = 0; i < 2; ++i)
{
OMSetRenderTargets(cmd, {fltRTV}, {});
ClearRenderTargetView(cmd, fltRTV, {0.2f, 0.2f, 0.2f, 1.0f});
IASetVertexBuffer(cmd, vb, sizeof(ConstsA2V), 0);
cmd->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
OMSetRenderTargets(cmd, {fltRTV}, {});
cmd->SetGraphicsRootSignature(sig);
cmd->SetDescriptorHeaps(1, &m_CBVUAVSRV.GetInterfacePtr());
cmd->SetGraphicsRootDescriptorTable(0, m_CBVUAVSRV->GetGPUDescriptorHandleForHeapStart());
@@ -775,6 +815,21 @@ float4 main(v2f IN) : SV_Target0
D3D12_RESOURCE_STATE_RENDER_TARGET);
}
// Render MSAA test
OMSetRenderTargets(cmd, {msaaRTV}, {});
ClearRenderTargetView(cmd, msaaRTV, {0.2f, 0.2f, 0.2f, 1.0f});
IASetVertexBuffer(cmd, vbmsaa, sizeof(DefaultA2V), 0);
cmd->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
cmd->SetGraphicsRootSignature(sigmsaa);
cmd->SetPipelineState(psomsaa);
RSSetViewport(cmd, {0.0f, 0.0f, 8.0f, 8.0f, 0.0f, 1.0f});
RSSetScissorRect(cmd, {0, 0, 8, 8});
// Add a marker so we can easily locate this draw
cmd->SetMarker(1, "MSAA", 4);
cmd->DrawInstanced(3, 1, 0, 0);
FinishUsingBackbuffer(cmd, D3D12_RESOURCE_STATE_RENDER_TARGET);
cmd->Close();
@@ -21,7 +21,7 @@ class D3D12_Shader_Debug_Zoo(rdtest.TestCase):
# Jump to the draw
test_marker: rd.DrawcallDescription = self.find_draw(shaderModels[sm])
draw = test_marker.next
self.controller.SetFrameEvent(draw.eventId, True)
self.controller.SetFrameEvent(draw.eventId, False)
pipe: rd.PipeState = self.controller.GetPipelineState()
@@ -50,6 +50,40 @@ class D3D12_Shader_Debug_Zoo(rdtest.TestCase):
rdtest.log.dedent()
rdtest.log.print("Performing MSAA tests:")
rdtest.log.indent()
test_marker: rd.DrawcallDescription = self.find_draw("MSAA")
draw = test_marker.next
self.controller.SetFrameEvent(draw.eventId, False)
pipe: rd.PipeState = self.controller.GetPipelineState()
for test in range(4):
# Debug the shader
trace: rd.ShaderDebugTrace = self.controller.DebugPixel(4, 4, test,
rd.ReplayController.NoPreference)
# Validate that the correct sample index was debugged
inputs: List[rd.ShaderVariable] = list(trace.inputs)
sampRegister = self.find_input_source_var(trace, rd.ShaderBuiltin.MSAASampleIndex)
sampInput = [var for var in inputs if var.name == sampRegister.variables[0].name][0]
if sampInput.value.uv[0] != test:
rdtest.log.error("Test {} did not pick the correct sample.".format(test))
cycles, variables = self.process_trace(trace)
output = self.find_output_source_var(trace, rd.ShaderBuiltin.ColorOutput, 0)
debugged = self.evalute_source_var(output, variables)
# Validate the debug output result
try:
self.check_pixel_sample_value(pipe.GetOutputTargets()[0].resourceId, 4, 4, test, debugged.value.fv[0:4], 0.0)
except rdtest.TestFailureException as ex:
failed = True
rdtest.log.error("Test {} did not match. {}".format(test, str(ex)))
continue
rdtest.log.dedent()
if failed:
raise rdtest.TestFailureException("Some tests were not as expected")