mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-09-20 20:55:42 +00:00
Add MSAA support to D3D12 PS debugging
This commit is contained in:
committed by
Baldur Karlsson
parent
a5583b83ee
commit
a6c3177736
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user