Implement D3D12Replay::DebugPixel

This enables the core loop of capturing PS inputs, building shader
debug state, and walking through a shader's instructions.
This commit is contained in:
Steve Karolewics
2019-12-12 17:58:25 +00:00
committed by Baldur Karlsson
parent c63baeb609
commit a88deaeba0
4 changed files with 704 additions and 8 deletions
+2
View File
@@ -66,6 +66,8 @@ enum CBVUAVSRVSlot
PICK_RESULT_UAV,
PICK_RESULT_CLEAR_UAV,
SHADER_DEBUG_UAV,
TMP_UAV,
MSAA_SRV2x,
@@ -22,10 +22,27 @@
* THE SOFTWARE.
******************************************************************************/
#include "driver/dx/official/d3dcompiler.h"
#include "driver/shaders/dxbc/dxbc_debug.h"
#include "d3d12_command_queue.h"
#include "d3d12_debug.h"
#include "d3d12_resources.h"
#include "d3d12_shader_cache.h"
#define D3D12SHADERDEBUG_PIXEL 0
struct DebugHit
{
uint32_t numHits;
float posx;
float posy;
float depth;
uint32_t primitive;
uint32_t isFrontFace;
uint32_t sample;
uint32_t coverage;
uint32_t rawdata; // arbitrary, depending on shader
};
class D3D12DebugAPIWrapper : public ShaderDebug::DebugAPIWrapper
{
@@ -417,6 +434,8 @@ ShaderDebugTrace D3D12Replay::DebugVertex(uint32_t eventId, uint32_t vertid, uin
return ret;
}
#if D3D12SHADERDEBUG_PIXEL == 0
ShaderDebugTrace D3D12Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t y, uint32_t sample,
uint32_t primitive)
{
@@ -425,6 +444,675 @@ ShaderDebugTrace D3D12Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t
return ret;
}
#else
ShaderDebugTrace D3D12Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t y, uint32_t sample,
uint32_t primitive)
{
using namespace DXBC;
using namespace DXBCBytecode;
using namespace ShaderDebug;
D3D12MarkerRegion debugpixRegion(
m_pDevice->GetQueue()->GetReal(),
StringFormat::Fmt("DebugPixel @ %u of (%u,%u) %u / %u", eventId, x, y, sample, primitive));
const D3D12Pipe::State *pipelineState = GetD3D12PipelineState();
ShaderDebugTrace empty;
// Fetch the disassembly info from the pixel shader
const D3D12Pipe::Shader &pixelShader = pipelineState->pixelShader;
WrappedID3D12Shader *ps =
m_pDevice->GetResourceManager()->GetCurrentAs<WrappedID3D12Shader>(pixelShader.resourceId);
if(!ps)
return empty;
DXBCContainer *dxbc = ps->GetDXBC();
const ShaderReflection &refl = ps->GetDetails();
if(!dxbc)
return empty;
dxbc->GetDisassembly();
// Fetch the previous stage's disassembly, to match outputs to PS inputs
DXBCContainer *prevDxbc = NULL;
// Check for geometry shader first
{
const D3D12Pipe::Shader &geometryShader = pipelineState->geometryShader;
WrappedID3D12Shader *gs =
m_pDevice->GetResourceManager()->GetCurrentAs<WrappedID3D12Shader>(geometryShader.resourceId);
if(gs)
prevDxbc = gs->GetDXBC();
}
// Check for domain shader next
if(prevDxbc == NULL)
{
const D3D12Pipe::Shader &domainShader = pipelineState->domainShader;
WrappedID3D12Shader *ds =
m_pDevice->GetResourceManager()->GetCurrentAs<WrappedID3D12Shader>(domainShader.resourceId);
if(ds)
prevDxbc = ds->GetDXBC();
}
// Check for vertex shader last
if(prevDxbc == NULL)
{
const D3D12Pipe::Shader &vertexShader = pipelineState->vertexShader;
WrappedID3D12Shader *vs =
m_pDevice->GetResourceManager()->GetCurrentAs<WrappedID3D12Shader>(vertexShader.resourceId);
if(vs)
prevDxbc = vs->GetDXBC();
}
std::vector<PSInputElement> initialValues;
std::vector<std::string> floatInputs;
std::vector<std::string> inputVarNames;
std::string extractHlsl;
int structureStride = 0;
ShaderDebug::GatherPSInputDataForInitialValues(*dxbc->GetReflection(), *prevDxbc->GetReflection(),
initialValues, floatInputs, inputVarNames,
extractHlsl, structureStride);
uint32_t overdrawLevels = 100; // maximum number of overdraw levels
// get the multisample count
uint32_t outputSampleCount = RDCMAX(1U, pipelineState->outputMerger.multiSampleCount);
// 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 empty;
}
extractHlsl += R"(
struct PSInitialData
{
// metadata we need ourselves
uint hit;
float3 pos;
uint prim;
uint fface;
uint sample;
uint covge;
float derivValid;
// input values
PSInput IN;
PSInput INddx;
PSInput INddy;
PSInput INddxfine;
PSInput INddyfine;
};
)";
extractHlsl += "RWStructuredBuffer<PSInitialData> PSInitialBuffer : register(u0);\n\n";
extractHlsl += R"(
void ExtractInputsPS(PSInput IN, float4 debug_pixelPos : SV_Position, uint prim : SV_PrimitiveID,
uint sample : SV_SampleIndex, uint covge : SV_Coverage,
bool fface : SV_IsFrontFace)
{
)";
extractHlsl += " uint idx = " + ToStr(overdrawLevels) + ";\n";
extractHlsl += StringFormat::Fmt(
" if(abs(debug_pixelPos.x - %u.5) < 0.5f && abs(debug_pixelPos.y - %u.5) < 0.5f)\n", x, y);
extractHlsl += " InterlockedAdd(PSInitialBuffer[0].hit, 1, idx);\n\n";
extractHlsl += " idx = min(idx, " + ToStr(overdrawLevels) + ");\n\n";
extractHlsl += " PSInitialBuffer[idx].pos = debug_pixelPos.xyz;\n";
extractHlsl += " PSInitialBuffer[idx].prim = prim;\n";
extractHlsl += " PSInitialBuffer[idx].fface = fface;\n";
extractHlsl += " PSInitialBuffer[idx].covge = covge;\n";
extractHlsl += " PSInitialBuffer[idx].sample = sample;\n";
extractHlsl += " PSInitialBuffer[idx].IN = IN;\n";
extractHlsl += " PSInitialBuffer[idx].derivValid = ddx(debug_pixelPos.x);\n";
extractHlsl += " PSInitialBuffer[idx].INddx = (PSInput)0;\n";
extractHlsl += " PSInitialBuffer[idx].INddy = (PSInput)0;\n";
extractHlsl += " PSInitialBuffer[idx].INddxfine = (PSInput)0;\n";
extractHlsl += " PSInitialBuffer[idx].INddyfine = (PSInput)0;\n";
for(size_t i = 0; i < floatInputs.size(); i++)
{
const std::string &name = floatInputs[i];
extractHlsl += " PSInitialBuffer[idx].INddx." + name + " = ddx(IN." + name + ");\n";
extractHlsl += " PSInitialBuffer[idx].INddy." + name + " = ddy(IN." + name + ");\n";
extractHlsl += " PSInitialBuffer[idx].INddxfine." + name + " = ddx_fine(IN." + name + ");\n";
extractHlsl += " PSInitialBuffer[idx].INddyfine." + name + " = ddy_fine(IN." + name + ");\n";
}
extractHlsl += "\n}";
// Create pixel shader to get initial values from previous stage output
ID3DBlob *psBlob = NULL;
UINT flags = D3DCOMPILE_DEBUG | D3DCOMPILE_WARNINGS_ARE_ERRORS | D3DCOMPILE_DEBUG_NAME_FOR_SOURCE;
if(m_pDevice->GetShaderCache()->GetShaderBlob(extractHlsl.c_str(), "ExtractInputsPS", flags,
"ps_5_0", &psBlob) != "")
{
RDCERR("Failed to create shader to extract inputs");
return empty;
}
uint32_t structStride = sizeof(uint32_t) // uint hit;
+ sizeof(float) * 3 // float3 pos;
+ sizeof(uint32_t) // uint prim;
+ sizeof(uint32_t) // uint fface;
+ sizeof(uint32_t) // uint sample;
+ sizeof(uint32_t) // uint covge;
+ sizeof(float) // float derivValid;
+
structureStride * 5; // PSInput IN, INddx, INddy, INddxfine, INddyfine;
HRESULT hr = S_OK;
// Create buffer to store initial values captured in pixel shader
D3D12_RESOURCE_DESC rdesc;
ZeroMemory(&rdesc, sizeof(D3D12_RESOURCE_DESC));
rdesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
rdesc.Width = structStride * (overdrawLevels + 1);
rdesc.Height = 1;
rdesc.DepthOrArraySize = 1;
rdesc.MipLevels = 1;
rdesc.Format = DXGI_FORMAT_UNKNOWN;
rdesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
rdesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
rdesc.SampleDesc.Count = 1; // TODO: Support MSAA
rdesc.SampleDesc.Quality = 0;
D3D12_HEAP_PROPERTIES heapProps;
heapProps.Type = D3D12_HEAP_TYPE_DEFAULT;
heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
heapProps.CreationNodeMask = 1;
heapProps.VisibleNodeMask = 1;
ID3D12Resource *pInitialValuesBuffer = NULL;
D3D12_RESOURCE_STATES resourceState = D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
hr = m_pDevice->CreateCommittedResource(&heapProps, D3D12_HEAP_FLAG_NONE, &rdesc, resourceState,
NULL, __uuidof(ID3D12Resource),
(void **)&pInitialValuesBuffer);
if(FAILED(hr))
{
RDCERR("Failed to create buffer for pixel shader debugging HRESULT: %s", ToStr(hr).c_str());
SAFE_RELEASE(psBlob);
return empty;
}
// Create UAV of initial values buffer
D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc;
ZeroMemory(&uavDesc, sizeof(D3D12_UNORDERED_ACCESS_VIEW_DESC));
uavDesc.Format = DXGI_FORMAT_UNKNOWN;
uavDesc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER;
uavDesc.Buffer.NumElements = overdrawLevels + 1;
uavDesc.Buffer.StructureByteStride = structStride;
D3D12_CPU_DESCRIPTOR_HANDLE uav = m_pDevice->GetDebugManager()->GetCPUHandle(SHADER_DEBUG_UAV);
m_pDevice->CreateUnorderedAccessView(pInitialValuesBuffer, NULL, &uavDesc, uav);
uavDesc.Format = DXGI_FORMAT_R32_UINT;
uavDesc.Buffer.FirstElement = 0;
uavDesc.Buffer.NumElements = structStride * (overdrawLevels + 1) / sizeof(uint32_t);
uavDesc.Buffer.StructureByteStride = 0;
D3D12_CPU_DESCRIPTOR_HANDLE clearUav =
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;
WrappedID3D12RootSignature *sig =
m_pDevice->GetResourceManager()->GetCurrentAs<WrappedID3D12RootSignature>(rs.graphics.rootsig);
// Need to be able to add a descriptor table with our UAV without hitting the 64 DWORD limit
RDCASSERT(sig->sig.dwordLength < 64);
D3D12RootSignature modsig = sig->sig;
UINT regSpace = modsig.maxSpaceIndex + 1;
MoveRootSignatureElementsToRegisterSpace(modsig, regSpace, D3D12DescriptorType::UAV,
D3D12_SHADER_VISIBILITY_PIXEL);
// 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.RegisterSpace = 0;
descRange.Flags = D3D12_DESCRIPTOR_RANGE_FLAG_NONE;
descRange.OffsetInDescriptorsFromTableStart = 0;
modsig.Parameters.push_back(D3D12RootSignatureParameter());
D3D12RootSignatureParameter &param = modsig.Parameters.back();
param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
param.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
param.DescriptorTable.NumDescriptorRanges = 1;
param.DescriptorTable.pDescriptorRanges = &descRange;
uint32_t sigElem = uint32_t(modsig.Parameters.size() - 1);
// Create the root signature for gathering initial pixel shader values
ID3DBlob *root = m_pDevice->GetShaderCache()->MakeRootSig(modsig);
ID3D12RootSignature *pRootSignature = NULL;
hr = m_pDevice->CreateRootSignature(0, root->GetBufferPointer(), root->GetBufferSize(),
__uuidof(ID3D12RootSignature), (void **)&pRootSignature);
if(FAILED(hr))
{
RDCERR("Failed to create root signature for pixel shader debugging HRESULT: %s",
ToStr(hr).c_str());
SAFE_RELEASE(root);
SAFE_RELEASE(psBlob);
SAFE_RELEASE(pInitialValuesBuffer);
return empty;
}
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();
pipeDesc.pRootSignature = pRootSignature;
ID3D12PipelineState *initialPso = NULL;
hr = m_pDevice->CreatePipeState(pipeDesc, &initialPso);
if(FAILED(hr))
{
RDCERR("Failed to create PSO for pixel shader debugging HRESULT: %s", ToStr(hr).c_str());
SAFE_RELEASE(psBlob);
SAFE_RELEASE(pInitialValuesBuffer);
SAFE_RELEASE(pRootSignature);
return empty;
}
// 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);
ID3D12GraphicsCommandListX *cmdList = m_pDevice->GetDebugManager()->ResetDebugList();
rs.ApplyDescriptorHeaps(cmdList);
D3D12_GPU_DESCRIPTOR_HANDLE gpuUav = m_pDevice->GetDebugManager()->GetGPUHandle(SHADER_DEBUG_UAV);
UINT zero[4] = {0, 0, 0, 0};
cmdList->ClearUnorderedAccessViewUint(gpuUav, clearUav, pInitialValuesBuffer, zero, 0, NULL);
// Execute the command to ensure that UAV clear and resource creation occur before replay
hr = cmdList->Close();
if(FAILED(hr))
{
RDCERR("Failed to close command list HRESULT: %s", ToStr(hr).c_str());
SAFE_RELEASE(psBlob);
SAFE_RELEASE(pInitialValuesBuffer);
SAFE_RELEASE(pRootSignature);
SAFE_RELEASE(initialPso);
return empty;
}
{
ID3D12CommandList *l = cmdList;
m_pDevice->GetQueue()->ExecuteCommandLists(1, &l);
m_pDevice->GPUSync();
}
{
D3D12MarkerRegion initState(m_pDevice->GetQueue()->GetReal(),
"Replaying event for initial states");
// Set the PSO and root signature
rs.pipe = GetResID(initialPso);
rs.graphics.rootsig = GetResID(pRootSignature);
// Replay the event with our modified state
m_pDevice->ReplayLog(0, eventId, eReplay_OnlyDraw);
// Restore D3D12 state to what the event uses
rs = prevState;
}
bytebuf initialData;
m_pDevice->GetDebugManager()->GetBufferData(pInitialValuesBuffer, 0, 0, initialData);
// 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(initialPso);
DebugHit *buf = (DebugHit *)initialData.data();
D3D12MarkerRegion::Set(m_pDevice->GetQueue()->GetReal(),
StringFormat::Fmt("Got %u hits", buf[0].numHits));
if(buf[0].numHits == 0)
{
RDCLOG("No hit for this event");
return empty;
}
// if we encounter multiple hits at our destination pixel co-ord (or any other) we
// check to see if a specific primitive was requested (via primitive parameter not
// being set to ~0U). If it was, debug that pixel, otherwise do a best-estimate
// of which fragment was the last to successfully depth test and debug that, just by
// checking if the depth test is ordered and picking the final fragment in the series
// our debugging quad. Order is TL, TR, BL, BR
State quad[4];
// figure out the TL pixel's coords. Assume even top left (towards 0,0)
// this isn't spec'd but is a reasonable assumption.
int xTL = x & (~1);
int yTL = y & (~1);
// get the index of our desired pixel
int destIdx = (x - xTL) + 2 * (y - yTL);
// Fetch constant buffer data from root signature
bytebuf cbufData[D3D12_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT];
GatherConstantBuffers(m_pDevice, dxbc->m_Type, rs.graphics, cbufData);
// Get depth func and determine "winner" pixel
D3D12_COMPARISON_FUNC depthFunc = pipeDesc.DepthStencilState.DepthFunc;
DebugHit *pWinnerHit = NULL;
if(sample == ~0U)
sample = 0;
if(primitive != ~0U)
{
for(size_t i = 0; i < buf[0].numHits && i < overdrawLevels; i++)
{
DebugHit *pHit = (DebugHit *)(initialData.data() + i * structStride);
if(pHit->primitive == primitive && pHit->sample == sample)
{
pWinnerHit = pHit;
}
}
}
if(pWinnerHit == NULL)
{
for(size_t i = 0; i < buf[0].numHits && i < overdrawLevels; i++)
{
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)
{
pWinnerHit = pHit;
continue;
}
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))
{
if(pHit->sample == sample)
{
pWinnerHit = pHit;
}
}
}
}
if(pWinnerHit == NULL)
{
RDCLOG("Couldn't find any pixels that passed depth test at target coordinates");
return empty;
}
ShaderDebugTrace traces[4];
GlobalState global;
GetDebugManager()->CreateShaderGlobalState(global, dxbc);
{
DebugHit *pHit = pWinnerHit;
State initialState;
CreateShaderDebugStateAndTrace(initialState, traces[destIdx], destIdx, dxbc, refl, cbufData);
rdcarray<ShaderVariable> &ins = traces[destIdx].inputs;
if(!ins.empty() && ins.back().name == "vCoverage")
ins.back().value.u.x = pHit->coverage;
initialState.semantics.coverage = pHit->coverage;
initialState.semantics.primID = pHit->primitive;
initialState.semantics.isFrontFace = pHit->isFrontFace;
uint32_t *data = &pHit->rawdata;
float *pos_ddx = (float *)data;
// ddx(SV_Position.x) MUST be 1.0
if(*pos_ddx != 1.0f)
{
RDCERR("Derivatives invalid");
return empty;
}
data++;
for(size_t i = 0; i < initialValues.size(); i++)
{
int32_t *rawout = NULL;
if(initialValues[i].reg >= 0)
{
ShaderVariable &invar = traces[destIdx].inputs[initialValues[i].reg];
if(initialValues[i].sysattribute == ShaderBuiltin::PrimitiveIndex)
{
invar.value.u.x = pHit->primitive;
}
else if(initialValues[i].sysattribute == ShaderBuiltin::MSAASampleIndex)
{
invar.value.u.x = pHit->sample;
}
else if(initialValues[i].sysattribute == ShaderBuiltin::MSAACoverage)
{
invar.value.u.x = pHit->coverage;
}
else if(initialValues[i].sysattribute == ShaderBuiltin::IsFrontFace)
{
invar.value.u.x = pHit->isFrontFace ? ~0U : 0;
}
else
{
rawout = &invar.value.iv[initialValues[i].elem];
memcpy(rawout, data, initialValues[i].numwords * 4);
}
}
if(initialValues[i].included)
data += initialValues[i].numwords;
}
for(int i = 0; i < 4; i++)
{
if(i != destIdx)
traces[i] = traces[destIdx];
quad[i] = initialState;
quad[i].SetTrace(i, &traces[i]);
if(i != destIdx)
quad[i].SetHelper();
}
// TODO: Handle inputs that were evaluated at sample granularity (MSAA)
ApplyAllDerivatives(global, traces, destIdx, initialValues, (float *)data);
}
std::vector<ShaderDebugState> states;
if(dxbc->GetDebugInfo())
dxbc->GetDebugInfo()->GetLocals(0, dxbc->GetDXBCByteCode()->GetInstruction(0).offset,
quad[destIdx].locals);
states.push_back(quad[destIdx]);
// ping pong between so that we can have 'current' quad to update into new one
State quad2[4];
State *curquad = quad;
State *newquad = quad2;
// marks any threads stalled waiting for others to catch up
bool activeMask[4] = {true, true, true, true};
int cycleCounter = 0;
D3D12MarkerRegion simloop(m_pDevice->GetQueue()->GetReal(), "Simulation Loop");
D3D12DebugAPIWrapper apiWrapper(m_pDevice, dxbc, global);
// simulate lockstep until all threads are finished
bool finished = true;
do
{
for(size_t i = 0; i < 4; i++)
{
if(activeMask[i])
newquad[i] = curquad[i].GetNext(global, &apiWrapper, curquad);
else
newquad[i] = curquad[i];
}
State *a = curquad;
curquad = newquad;
newquad = a;
// if our destination quad is paused don't record multiple identical states.
if(activeMask[destIdx])
{
State &s = curquad[destIdx];
if(dxbc->GetDebugInfo())
{
size_t inst =
RDCMIN((size_t)s.nextInstruction, dxbc->GetDXBCByteCode()->GetNumInstructions() - 1);
const Operation &op = dxbc->GetDXBCByteCode()->GetInstruction(inst);
dxbc->GetDebugInfo()->GetLocals(s.nextInstruction, op.offset, s.locals);
}
states.push_back(s);
}
// we need to make sure that control flow which converges stays in lockstep so that
// derivatives are still valid. While diverged, we don't have to keep threads in lockstep
// since using derivatives is invalid.
// Threads diverge either in ifs, loops, or switches. Due to the nature of the bytecode,
// all threads *must* pass through the same exit instruction for each, there's no jumping
// around with gotos. Note also for the same reason, the only time threads are on earlier
// instructions is if they are still catching up to a thread that has exited the control
// flow.
// So the scheme is as follows:
// * If all threads have the same nextInstruction, just continue we are still in lockstep.
// * If threads are out of lockstep, find any thread which has nextInstruction pointing
// immediately *after* an ENDIF, ENDLOOP or ENDSWITCH. Pointing directly at one is not
// an indication the thread is done, as the next step for an ENDLOOP will jump back to
// the matching LOOP and continue iterating.
// * Pause any thread matching the above until all threads are pointing to the same
// instruction. By the assumption above, all threads will eventually pass through this
// terminating instruction so we just pause any other threads and don't do anything
// until the control flow has converged and we can continue stepping in lockstep.
// mark all threads as active again.
// if we've converged, or we were never diverged, this keeps everything ticking
activeMask[0] = activeMask[1] = activeMask[2] = activeMask[3] = true;
if(curquad[0].nextInstruction != curquad[1].nextInstruction ||
curquad[0].nextInstruction != curquad[2].nextInstruction ||
curquad[0].nextInstruction != curquad[3].nextInstruction)
{
// this isn't *perfect* but it will still eventually continue. We look for the most
// advanced thread, and check to see if it's just finished a control flow. If it has
// then we assume it's at the convergence point and wait for every other thread to
// catch up, pausing any threads that reach the convergence point before others.
// Note this might mean we don't have any threads paused even within divergent flow.
// This is fine and all we care about is pausing to make sure threads don't run ahead
// into code that should be lockstep. We don't care at all about what they do within
// the code that is divergent.
// The reason this isn't perfect is that the most advanced thread could be on an
// inner loop or inner if, not the convergence point, and we could be pausing it
// fruitlessly. Worse still - it could be on a branch none of the other threads will
// take so they will never reach that exact instruction.
// But we know that all threads will eventually go through the convergence point, so
// even in that worst case if we didn't pick the right waiting point, another thread
// will overtake and become the new most advanced thread and the previous waiting
// thread will resume. So in this case we caused a thread to wait more than it should
// have but that's not a big deal as it's within divergent flow so they don't have to
// stay in lockstep. Also if all threads will eventually pass that point we picked,
// we just waited to converge even in technically divergent code which is also
// harmless.
// Phew!
uint32_t convergencePoint = 0;
// find which thread is most advanced
for(size_t i = 0; i < 4; i++)
if(curquad[i].nextInstruction > convergencePoint)
convergencePoint = curquad[i].nextInstruction;
if(convergencePoint > 0)
{
OpcodeType op = dxbc->GetDXBCByteCode()->GetInstruction(convergencePoint - 1).operation;
// if the most advnaced thread hasn't just finished control flow, then all
// threads are still running, so don't converge
if(op != OPCODE_ENDIF && op != OPCODE_ENDLOOP && op != OPCODE_ENDSWITCH)
convergencePoint = 0;
}
// pause any threads at that instruction (could be none)
for(size_t i = 0; i < 4; i++)
if(curquad[i].nextInstruction == convergencePoint)
activeMask[i] = false;
}
finished = curquad[destIdx].Finished();
cycleCounter++;
if(cycleCounter == SHADER_DEBUG_WARN_THRESHOLD)
{
if(PromptDebugTimeout(cycleCounter))
break;
}
} while(!finished);
traces[destIdx].states = states;
traces[destIdx].hasLocals = dxbc->GetDebugInfo() && dxbc->GetDebugInfo()->HasLocals();
traces[destIdx].lineInfo.resize(dxbc->GetDXBCByteCode()->GetNumInstructions());
for(size_t i = 0; dxbc->GetDebugInfo() && i < dxbc->GetDXBCByteCode()->GetNumInstructions(); i++)
{
const Operation &op = dxbc->GetDXBCByteCode()->GetInstruction(i);
dxbc->GetDebugInfo()->GetLineInfo(i, op.offset, traces[destIdx].lineInfo[i]);
}
return traces[destIdx];
}
#endif // D3D12SHADERDEBUG_PIXEL
ShaderDebugTrace D3D12Replay::DebugThread(uint32_t eventId, const uint32_t groupid[3],
const uint32_t threadid[3])
{
+13 -8
View File
@@ -157,14 +157,7 @@ void D3D12RenderState::ApplyState(WrappedID3D12Device *dev, ID3D12GraphicsComman
}
}
std::vector<ID3D12DescriptorHeap *> descHeaps;
descHeaps.resize(heaps.size());
for(size_t i = 0; i < heaps.size(); i++)
descHeaps[i] = GetResourceManager()->GetCurrentAs<ID3D12DescriptorHeap>(heaps[i]);
if(!descHeaps.empty())
cmd->SetDescriptorHeaps((UINT)descHeaps.size(), &descHeaps[0]);
ApplyDescriptorHeaps(cmd);
if(graphics.rootsig != ResourceId())
{
@@ -183,6 +176,18 @@ void D3D12RenderState::ApplyState(WrappedID3D12Device *dev, ID3D12GraphicsComman
}
}
void D3D12RenderState::ApplyDescriptorHeaps(ID3D12GraphicsCommandListX *cmd) const
{
std::vector<ID3D12DescriptorHeap *> descHeaps;
descHeaps.resize(heaps.size());
for(size_t i = 0; i < heaps.size(); i++)
descHeaps[i] = GetResourceManager()->GetCurrentAs<ID3D12DescriptorHeap>(heaps[i]);
if(!descHeaps.empty())
cmd->SetDescriptorHeaps((UINT)descHeaps.size(), &descHeaps[0]);
}
void D3D12RenderState::ApplyComputeRootElements(ID3D12GraphicsCommandListX *cmd) const
{
for(size_t i = 0; i < compute.sigelems.size(); i++)
+1
View File
@@ -47,6 +47,7 @@ struct D3D12RenderState
D3D12RenderState &operator=(const D3D12RenderState &o);
void ApplyState(WrappedID3D12Device *dev, ID3D12GraphicsCommandListX *list) const;
void ApplyDescriptorHeaps(ID3D12GraphicsCommandListX *list) const;
void ApplyComputeRootElements(ID3D12GraphicsCommandListX *cmd) const;
void ApplyGraphicsRootElements(ID3D12GraphicsCommandListX *cmd) const;