mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-08-22 14:36:35 +00:00
Refactor D3D ps input fetch to use quad swizzle helper
* Also centralises and shares more code between D3D11 and D3D12 to use the same shader
This commit is contained in:
@@ -43,19 +43,6 @@
|
||||
|
||||
RDOC_EXTERN_CONFIG(bool, D3D_Hack_EnableGroups);
|
||||
|
||||
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 D3D11DebugAPIWrapper : public DXBCDebug::DebugAPIWrapper
|
||||
{
|
||||
public:
|
||||
@@ -1448,9 +1435,6 @@ void AddCBuffersToGlobalState(const DXBCBytecode::Program &program, D3D11DebugMa
|
||||
ShaderDebugTrace *D3D11Replay::DebugVertex(uint32_t eventId, uint32_t vertid, uint32_t instid,
|
||||
uint32_t idx, uint32_t view)
|
||||
{
|
||||
using namespace DXBCBytecode;
|
||||
using namespace DXBCDebug;
|
||||
|
||||
D3D11MarkerRegion region(
|
||||
StringFormat::Fmt("DebugVertex @ %u of (%u,%u,%u)", eventId, vertid, instid, idx));
|
||||
|
||||
@@ -1538,11 +1522,11 @@ ShaderDebugTrace *D3D11Replay::DebugVertex(uint32_t eventId, uint32_t vertid, ui
|
||||
}
|
||||
}
|
||||
|
||||
InterpretDebugger *interpreter = new InterpretDebugger;
|
||||
DXBCDebug::InterpretDebugger *interpreter = new DXBCDebug::InterpretDebugger;
|
||||
interpreter->eventId = eventId;
|
||||
ShaderDebugTrace *ret = interpreter->BeginDebug(dxbc, refl, 0);
|
||||
GlobalState &global = interpreter->global;
|
||||
ThreadState &state = interpreter->activeLane();
|
||||
DXBCDebug::GlobalState &global = interpreter->global;
|
||||
DXBCDebug::ThreadState &state = interpreter->activeLane();
|
||||
|
||||
AddCBuffersToGlobalState(*dxbc->GetDXBCByteCode(), *GetDebugManager(), global, ret->sourceVars,
|
||||
rs->VS, refl);
|
||||
@@ -1814,9 +1798,6 @@ ShaderDebugTrace *D3D11Replay::DebugVertex(uint32_t eventId, uint32_t vertid, ui
|
||||
ShaderDebugTrace *D3D11Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t y,
|
||||
const DebugPixelInputs &inputs)
|
||||
{
|
||||
using namespace DXBCBytecode;
|
||||
using namespace DXBCDebug;
|
||||
|
||||
uint32_t sample = inputs.sample;
|
||||
uint32_t primitive = inputs.primitive;
|
||||
|
||||
@@ -1877,37 +1858,11 @@ ShaderDebugTrace *D3D11Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t
|
||||
prevdxbc = vs->GetDXBC();
|
||||
RDCASSERT(prevdxbc);
|
||||
|
||||
rdcarray<PSInputElement> initialValues;
|
||||
rdcarray<rdcstr> floatInputs;
|
||||
rdcarray<rdcstr> inputVarNames;
|
||||
rdcstr extractHlsl;
|
||||
int structureStride = 0;
|
||||
DXDebug::PSInputFetcherConfig cfg;
|
||||
DXDebug::PSInputFetcher fetcher;
|
||||
|
||||
rdcarray<DXBC::InterpolationMode> interpModes;
|
||||
const rdcarray<SigParameter> &inputSig = dxbc->GetReflection()->InputSig;
|
||||
DXBCDebug::GetInterpolationModeForInputParams(inputSig, dxbc->GetDXBCByteCode(), interpModes);
|
||||
|
||||
std::map<ShaderBuiltin, rdcstr> usedInputs; // not used for D3D11
|
||||
DXDebug::GatherPSInputDataForInitialValues(inputSig, prevdxbc->GetReflection()->OutputSig,
|
||||
interpModes, initialValues, floatInputs, inputVarNames,
|
||||
extractHlsl, structureStride, usedInputs);
|
||||
|
||||
uint32_t overdrawLevels = 100; // maximum number of overdraw levels
|
||||
|
||||
// If the pipe contains a geometry shader, then SV_PrimitiveID cannot be used in the pixel
|
||||
// shader without being emitted from the geometry shader. For now, check if this semantic
|
||||
// will succeed in a new pixel shader with the rest of the pipe unchanged
|
||||
bool usePrimitiveID = (prevdxbc->m_Type != DXBC::ShaderType::Geometry);
|
||||
for(const PSInputElement &e : initialValues)
|
||||
{
|
||||
if(e.sysattribute == ShaderBuiltin::PrimitiveIndex)
|
||||
{
|
||||
usePrimitiveID = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t uavslot = 0;
|
||||
cfg.x = x;
|
||||
cfg.y = y;
|
||||
|
||||
ID3D11DepthStencilView *depthView = NULL;
|
||||
ID3D11RenderTargetView *rtView = NULL;
|
||||
@@ -1915,10 +1870,13 @@ ShaderDebugTrace *D3D11Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t
|
||||
// we have the right multisample level on output either way
|
||||
m_pImmediateContext->OMGetRenderTargets(1, &rtView, &depthView);
|
||||
if(rtView != NULL)
|
||||
uavslot = 1;
|
||||
cfg.uavslot = 1;
|
||||
else
|
||||
cfg.uavslot = 0;
|
||||
cfg.uavspace = 0;
|
||||
|
||||
// get the multisample count
|
||||
uint32_t outputSampleCount = 1;
|
||||
cfg.outputSampleCount = 1;
|
||||
|
||||
{
|
||||
ID3D11Resource *res = NULL;
|
||||
@@ -1938,257 +1896,20 @@ ShaderDebugTrace *D3D11Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t
|
||||
D3D11_TEXTURE2D_DESC desc;
|
||||
((ID3D11Texture2D *)res)->GetDesc(&desc);
|
||||
|
||||
outputSampleCount = RDCMAX(1U, desc.SampleDesc.Count);
|
||||
cfg.outputSampleCount = RDCMAX(1U, desc.SampleDesc.Count);
|
||||
}
|
||||
|
||||
SAFE_RELEASE(res);
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
// scan the instructions to see if it contains any evaluates.
|
||||
for(size_t i = 0; i < dxbc->GetDXBCByteCode()->GetNumInstructions(); 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"(
|
||||
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(u" + ToStr(uavslot) + ");\n\n";
|
||||
|
||||
if(!evalSampleCacheData.empty())
|
||||
{
|
||||
// float4 is wasteful in some cases but it's easier than using ByteAddressBuffer and manual
|
||||
// packing
|
||||
extractHlsl += "RWBuffer<float4> PSEvalBuffer : register(u" + ToStr(uavslot + 1) + ");\n\n";
|
||||
}
|
||||
|
||||
if(usePrimitiveID)
|
||||
{
|
||||
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)
|
||||
{
|
||||
)";
|
||||
}
|
||||
else
|
||||
{
|
||||
extractHlsl += R"(
|
||||
void ExtractInputsPS(PSInput IN, float4 debug_pixelPos : SV_Position,
|
||||
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";
|
||||
|
||||
if(usePrimitiveID)
|
||||
extractHlsl += " PSInitialBuffer[idx].prim = prim;\n";
|
||||
else
|
||||
extractHlsl += " PSInitialBuffer[idx].prim = 0;\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";
|
||||
|
||||
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];
|
||||
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}";
|
||||
DXDebug::CreatePSInputFetcher(dxbc, prevdxbc, cfg, fetcher);
|
||||
|
||||
ID3D11PixelShader *extract =
|
||||
m_pDevice->GetShaderCache()->MakePShader(extractHlsl.c_str(), "ExtractInputsPS", "ps_5_0");
|
||||
m_pDevice->GetShaderCache()->MakePShader(fetcher.hlsl.c_str(), "ExtractInputsPS", "ps_5_0");
|
||||
|
||||
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;
|
||||
uint32_t structStride =
|
||||
sizeof(DXDebug::PixelDebugHit) + 4 * (sizeof(DXDebug::LaneData) + fetcher.stride);
|
||||
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
@@ -2198,7 +1919,7 @@ void ExtractInputsPS(PSInput IN, float4 debug_pixelPos : SV_Position,
|
||||
bdesc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED;
|
||||
bdesc.Usage = D3D11_USAGE_DEFAULT;
|
||||
bdesc.StructureByteStride = structStride;
|
||||
bdesc.ByteWidth = structStride * (overdrawLevels + 1);
|
||||
bdesc.ByteWidth = structStride * (DXDebug::maxPixelHits + 1);
|
||||
|
||||
ID3D11Buffer *initialBuf = NULL;
|
||||
hr = m_pDevice->CreateBuffer(&bdesc, NULL, &initialBuf);
|
||||
@@ -2210,11 +1931,12 @@ void ExtractInputsPS(PSInput IN, float4 debug_pixelPos : SV_Position,
|
||||
}
|
||||
|
||||
ID3D11Buffer *evalBuf = NULL;
|
||||
if(!evalSampleCacheData.empty())
|
||||
if(!fetcher.evalSampleCacheData.empty())
|
||||
{
|
||||
bdesc.StructureByteStride = 0;
|
||||
bdesc.MiscFlags = 0;
|
||||
bdesc.ByteWidth = UINT(evalSampleCacheData.size() * sizeof(Vec4f) * (overdrawLevels + 1));
|
||||
bdesc.ByteWidth =
|
||||
UINT(fetcher.evalSampleCacheData.size() * sizeof(Vec4f) * (DXDebug::maxPixelHits + 1));
|
||||
|
||||
hr = m_pDevice->CreateBuffer(&bdesc, NULL, &evalBuf);
|
||||
|
||||
@@ -2230,7 +1952,7 @@ void ExtractInputsPS(PSInput IN, float4 debug_pixelPos : SV_Position,
|
||||
bdesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
|
||||
bdesc.Usage = D3D11_USAGE_STAGING;
|
||||
bdesc.StructureByteStride = 0;
|
||||
bdesc.ByteWidth = structStride * (overdrawLevels + 1);
|
||||
bdesc.ByteWidth = structStride * (DXDebug::maxPixelHits + 1);
|
||||
|
||||
ID3D11Buffer *initialStageBuf = NULL;
|
||||
hr = m_pDevice->CreateBuffer(&bdesc, NULL, &initialStageBuf);
|
||||
@@ -2241,12 +1963,12 @@ void ExtractInputsPS(PSInput IN, float4 debug_pixelPos : SV_Position,
|
||||
return new ShaderDebugTrace; // @NoCoverage
|
||||
}
|
||||
|
||||
uint32_t evalStructStride = uint32_t(evalSampleCacheData.size() * sizeof(Vec4f));
|
||||
uint32_t evalStructStride = uint32_t(fetcher.evalSampleCacheData.size() * sizeof(Vec4f));
|
||||
|
||||
ID3D11Buffer *evalStageBuf = NULL;
|
||||
if(evalBuf)
|
||||
{
|
||||
bdesc.ByteWidth = evalStructStride * (overdrawLevels + 1);
|
||||
bdesc.ByteWidth = evalStructStride * (DXDebug::maxPixelHits + 1);
|
||||
|
||||
hr = m_pDevice->CreateBuffer(&bdesc, NULL, &evalStageBuf);
|
||||
|
||||
@@ -2261,7 +1983,7 @@ void ExtractInputsPS(PSInput IN, float4 debug_pixelPos : SV_Position,
|
||||
uavdesc.Format = DXGI_FORMAT_UNKNOWN;
|
||||
uavdesc.Buffer.FirstElement = 0;
|
||||
uavdesc.Buffer.Flags = 0;
|
||||
uavdesc.Buffer.NumElements = overdrawLevels + 1;
|
||||
uavdesc.Buffer.NumElements = DXDebug::maxPixelHits + 1;
|
||||
uavdesc.ViewDimension = D3D11_UAV_DIMENSION_BUFFER;
|
||||
|
||||
ID3D11UnorderedAccessView *initialUAV = NULL;
|
||||
@@ -2276,7 +1998,8 @@ void ExtractInputsPS(PSInput IN, float4 debug_pixelPos : SV_Position,
|
||||
ID3D11UnorderedAccessView *evalUAV = NULL;
|
||||
if(evalBuf)
|
||||
{
|
||||
uavdesc.Buffer.NumElements = (overdrawLevels + 1) * (uint32_t)evalSampleCacheData.size();
|
||||
uavdesc.Buffer.NumElements =
|
||||
(DXDebug::maxPixelHits + 1) * (uint32_t)fetcher.evalSampleCacheData.size();
|
||||
uavdesc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
|
||||
hr = m_pDevice->CreateUnorderedAccessView(evalBuf, &uavdesc, &evalUAV);
|
||||
|
||||
@@ -2295,8 +2018,8 @@ void ExtractInputsPS(PSInput IN, float4 debug_pixelPos : SV_Position,
|
||||
ID3D11UnorderedAccessView *uavs[] = {initialUAV, evalUAV};
|
||||
|
||||
UINT count = (UINT)-1;
|
||||
m_pImmediateContext->OMSetRenderTargetsAndUnorderedAccessViews(uavslot, &rtView, depthView,
|
||||
uavslot, 2, uavs, &count);
|
||||
m_pImmediateContext->OMSetRenderTargetsAndUnorderedAccessViews(cfg.uavslot, &rtView, depthView,
|
||||
cfg.uavslot, 2, uavs, &count);
|
||||
m_pImmediateContext->PSSetShader(extract, NULL, 0);
|
||||
|
||||
SAFE_RELEASE(rtView);
|
||||
@@ -2321,12 +2044,13 @@ void ExtractInputsPS(PSInput IN, float4 debug_pixelPos : SV_Position,
|
||||
return new ShaderDebugTrace; // @NoCoverage
|
||||
}
|
||||
|
||||
byte *initialData = new byte[structStride * (overdrawLevels + 1)];
|
||||
memcpy(initialData, mapped.pData, structStride * (overdrawLevels + 1));
|
||||
bytebuf initialData;
|
||||
initialData.resize(structStride * (DXDebug::maxPixelHits + 1));
|
||||
memcpy(initialData.data(), mapped.pData, structStride * (DXDebug::maxPixelHits + 1));
|
||||
|
||||
m_pImmediateContext->Unmap(initialStageBuf, 0);
|
||||
|
||||
byte *evalData = NULL;
|
||||
bytebuf evalData;
|
||||
|
||||
if(evalStageBuf)
|
||||
{
|
||||
@@ -2335,12 +2059,11 @@ void ExtractInputsPS(PSInput IN, float4 debug_pixelPos : SV_Position,
|
||||
if(FAILED(hr))
|
||||
{
|
||||
RDCERR("Failed to map stage buff HRESULT: %s", ToStr(hr).c_str()); // @NoCoverage
|
||||
SAFE_DELETE_ARRAY(initialData); // @NoCoverage
|
||||
return new ShaderDebugTrace; // @NoCoverage
|
||||
}
|
||||
|
||||
evalData = new byte[evalStructStride * (overdrawLevels + 1)];
|
||||
memcpy(evalData, mapped.pData, evalStructStride * (overdrawLevels + 1));
|
||||
evalData.resize(evalStructStride * (DXDebug::maxPixelHits + 1));
|
||||
memcpy(evalData.data(), mapped.pData, evalStructStride * (DXDebug::maxPixelHits + 1));
|
||||
|
||||
m_pImmediateContext->Unmap(evalStageBuf, 0);
|
||||
}
|
||||
@@ -2355,15 +2078,13 @@ void ExtractInputsPS(PSInput IN, float4 debug_pixelPos : SV_Position,
|
||||
|
||||
SAFE_RELEASE(extract);
|
||||
|
||||
DebugHit *buf = (DebugHit *)initialData;
|
||||
DXDebug::PixelDebugHit *buf = (DXDebug::PixelDebugHit *)initialData.data();
|
||||
|
||||
D3D11MarkerRegion::Set(StringFormat::Fmt("Got %u hits", buf[0].numHits));
|
||||
|
||||
if(buf[0].numHits == 0)
|
||||
{
|
||||
RDCLOG("No hit for this event");
|
||||
SAFE_DELETE_ARRAY(initialData);
|
||||
SAFE_DELETE_ARRAY(evalData);
|
||||
return new ShaderDebugTrace;
|
||||
}
|
||||
|
||||
@@ -2373,14 +2094,6 @@ void ExtractInputsPS(PSInput IN, float4 debug_pixelPos : SV_Position,
|
||||
// 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
|
||||
|
||||
// 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);
|
||||
|
||||
D3D11_COMPARISON_FUNC depthFunc = D3D11_COMPARISON_LESS;
|
||||
|
||||
if(rs->OM.DepthStencilState)
|
||||
@@ -2390,37 +2103,38 @@ void ExtractInputsPS(PSInput IN, float4 debug_pixelPos : SV_Position,
|
||||
depthFunc = desc.DepthFunc;
|
||||
}
|
||||
|
||||
DebugHit *winner = NULL;
|
||||
float *evalSampleCache = (float *)evalData;
|
||||
DXDebug::PixelDebugHit *winner = NULL;
|
||||
float *evalSampleCache = (float *)evalData.data();
|
||||
size_t winnerIdx = 0;
|
||||
|
||||
if(sample == ~0U)
|
||||
sample = 0;
|
||||
|
||||
if(primitive != ~0U)
|
||||
{
|
||||
for(size_t i = 0; i < buf[0].numHits && i < overdrawLevels; i++)
|
||||
for(size_t i = 0; i < buf[0].numHits && i < DXDebug::maxPixelHits; i++)
|
||||
{
|
||||
DebugHit *hit = (DebugHit *)(initialData + i * structStride);
|
||||
DXDebug::PixelDebugHit *hit = (DXDebug::PixelDebugHit *)(initialData.data() + i * structStride);
|
||||
|
||||
if(hit->primitive == primitive && hit->sample == sample)
|
||||
{
|
||||
winner = hit;
|
||||
evalSampleCache = ((float *)evalData) + evalSampleCacheData.size() * 4 * i;
|
||||
winnerIdx = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(winner == NULL)
|
||||
{
|
||||
for(size_t i = 0; i < buf[0].numHits && i < overdrawLevels; i++)
|
||||
for(size_t i = 0; i < buf[0].numHits && i < DXDebug::maxPixelHits; i++)
|
||||
{
|
||||
DebugHit *hit = (DebugHit *)(initialData + i * structStride);
|
||||
DXDebug::PixelDebugHit *hit = (DXDebug::PixelDebugHit *)(initialData.data() + i * structStride);
|
||||
|
||||
if(winner == NULL)
|
||||
{
|
||||
// If we haven't picked a winner at all yet, use the first one
|
||||
winner = hit;
|
||||
evalSampleCache = ((float *)evalData) + evalSampleCacheData.size() * 4 * i;
|
||||
winnerIdx = i;
|
||||
}
|
||||
else if(hit->sample == sample)
|
||||
{
|
||||
@@ -2429,14 +2143,14 @@ void ExtractInputsPS(PSInput IN, float4 debug_pixelPos : SV_Position,
|
||||
{
|
||||
// The previously selected winner was for the wrong sample, use this one
|
||||
winner = hit;
|
||||
evalSampleCache = ((float *)evalData) + evalSampleCacheData.size() * 4 * i;
|
||||
winnerIdx = 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
|
||||
winner = hit;
|
||||
evalSampleCache = ((float *)evalData) + evalSampleCacheData.size() * 4 * i;
|
||||
winnerIdx = i;
|
||||
}
|
||||
else if((depthFunc == D3D11_COMPARISON_LESS && hit->depth < winner->depth) ||
|
||||
(depthFunc == D3D11_COMPARISON_LESS_EQUAL && hit->depth <= winner->depth) ||
|
||||
@@ -2445,111 +2159,103 @@ void ExtractInputsPS(PSInput IN, float4 debug_pixelPos : SV_Position,
|
||||
{
|
||||
// For depth functions with an inequality, find the hit that "wins" the most
|
||||
winner = hit;
|
||||
evalSampleCache = ((float *)evalData) + evalSampleCacheData.size() * 4 * i;
|
||||
winnerIdx = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
evalSampleCache =
|
||||
(float *)(evalData.data() + fetcher.evalSampleCacheData.size() * sizeof(Vec4f) * 4 * winnerIdx);
|
||||
|
||||
if(winner == NULL)
|
||||
{
|
||||
RDCLOG("Couldn't find any pixels that passed depth test at target co-ordinates");
|
||||
SAFE_DELETE_ARRAY(initialData);
|
||||
SAFE_DELETE_ARRAY(evalData);
|
||||
return new ShaderDebugTrace;
|
||||
}
|
||||
|
||||
tracker.State().ApplyState(m_pImmediateContext);
|
||||
|
||||
InterpretDebugger *interpreter = new InterpretDebugger;
|
||||
DXDebug::PixelDebugHit *hit = winner;
|
||||
|
||||
// ddx(SV_Position.x) MUST be 1.0
|
||||
if(hit->derivValid != 1.0f)
|
||||
{
|
||||
RDCERR("Derivatives invalid");
|
||||
return new ShaderDebugTrace;
|
||||
}
|
||||
|
||||
DXBCDebug::InterpretDebugger *interpreter = new DXBCDebug::InterpretDebugger;
|
||||
interpreter->eventId = eventId;
|
||||
ShaderDebugTrace *ret = interpreter->BeginDebug(dxbc, refl, destIdx);
|
||||
GlobalState &global = interpreter->global;
|
||||
ThreadState &state = interpreter->activeLane();
|
||||
ShaderDebugTrace *ret = interpreter->BeginDebug(dxbc, refl, hit->quadLaneIndex);
|
||||
DXBCDebug::GlobalState &global = interpreter->global;
|
||||
|
||||
AddCBuffersToGlobalState(*dxbc->GetDXBCByteCode(), *GetDebugManager(), global, ret->sourceVars,
|
||||
rs->PS, refl);
|
||||
|
||||
global.sampleEvalRegisterMask = sampleEvalRegisterMask;
|
||||
global.sampleEvalRegisterMask = fetcher.sampleEvalRegisterMask;
|
||||
|
||||
byte *data = (byte *)(hit + 1);
|
||||
|
||||
for(uint32_t q = 0; q < 4; q++)
|
||||
{
|
||||
DebugHit *hit = winner;
|
||||
DXDebug::LaneData *lane = (DXDebug::LaneData *)data;
|
||||
|
||||
DXBCDebug::ThreadState &state = interpreter->workgroup[q];
|
||||
rdcarray<ShaderVariable> &ins = state.inputs;
|
||||
if(!ins.empty() && ins.back().name == dxbc->GetDXBCByteCode()->GetRegisterName(
|
||||
DXBCBytecode::TYPE_INPUT_COVERAGE_MASK, 0))
|
||||
ins.back().value.u32v[0] = hit->coverage;
|
||||
|
||||
state.semantics.coverage = hit->coverage;
|
||||
if(q != hit->quadLaneIndex)
|
||||
ins = interpreter->workgroup[hit->quadLaneIndex].inputs;
|
||||
|
||||
state.semantics.coverage = lane->coverage;
|
||||
state.semantics.primID = hit->primitive;
|
||||
state.semantics.isFrontFace = hit->isFrontFace;
|
||||
|
||||
uint32_t *data = &hit->rawdata;
|
||||
if(!ins.empty() && ins.back().name == dxbc->GetDXBCByteCode()->GetRegisterName(
|
||||
DXBCBytecode::TYPE_INPUT_COVERAGE_MASK, 0))
|
||||
ins.back().value.u32v[0] = lane->coverage;
|
||||
|
||||
float *pos_ddx = (float *)data;
|
||||
if(lane->isHelper)
|
||||
state.SetHelper();
|
||||
|
||||
// ddx(SV_Position.x) MUST be 1.0
|
||||
if(*pos_ddx != 1.0f)
|
||||
data += sizeof(DXDebug::LaneData);
|
||||
|
||||
for(size_t i = 0; i < fetcher.inputs.size(); i++)
|
||||
{
|
||||
RDCERR("Derivatives invalid");
|
||||
SAFE_DELETE_ARRAY(initialData);
|
||||
SAFE_DELETE_ARRAY(evalData);
|
||||
delete interpreter;
|
||||
delete ret;
|
||||
return new ShaderDebugTrace;
|
||||
}
|
||||
|
||||
data++;
|
||||
|
||||
for(size_t i = 0; i < initialValues.size(); i++)
|
||||
{
|
||||
int32_t *rawout = NULL;
|
||||
|
||||
if(initialValues[i].reg >= 0)
|
||||
if(fetcher.inputs[i].reg >= 0)
|
||||
{
|
||||
ShaderVariable &invar = ins[initialValues[i].reg];
|
||||
ShaderVariable &invar = ins[fetcher.inputs[i].reg];
|
||||
|
||||
if(initialValues[i].sysattribute == ShaderBuiltin::PrimitiveIndex)
|
||||
if(fetcher.inputs[i].sysattribute == ShaderBuiltin::PrimitiveIndex)
|
||||
{
|
||||
invar.value.u32v[initialValues[i].elem] = hit->primitive;
|
||||
invar.value.u32v[fetcher.inputs[i].elem] = hit->primitive;
|
||||
}
|
||||
else if(initialValues[i].sysattribute == ShaderBuiltin::MSAASampleIndex)
|
||||
else if(fetcher.inputs[i].sysattribute == ShaderBuiltin::MSAASampleIndex)
|
||||
{
|
||||
invar.value.u32v[initialValues[i].elem] = hit->sample;
|
||||
invar.value.u32v[fetcher.inputs[i].elem] = hit->sample;
|
||||
}
|
||||
else if(initialValues[i].sysattribute == ShaderBuiltin::MSAACoverage)
|
||||
else if(fetcher.inputs[i].sysattribute == ShaderBuiltin::MSAACoverage)
|
||||
{
|
||||
invar.value.u32v[initialValues[i].elem] = hit->coverage;
|
||||
invar.value.u32v[fetcher.inputs[i].elem] = lane->coverage;
|
||||
}
|
||||
else if(initialValues[i].sysattribute == ShaderBuiltin::IsFrontFace)
|
||||
else if(fetcher.inputs[i].sysattribute == ShaderBuiltin::IsFrontFace)
|
||||
{
|
||||
invar.value.u32v[initialValues[i].elem] = hit->isFrontFace ? ~0U : 0;
|
||||
invar.value.u32v[fetcher.inputs[i].elem] = hit->isFrontFace ? ~0U : 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
rawout = &invar.value.s32v[initialValues[i].elem];
|
||||
int32_t *rawout = &invar.value.s32v[fetcher.inputs[i].elem];
|
||||
|
||||
memcpy(rawout, data, initialValues[i].numwords * 4);
|
||||
memcpy(rawout, data, fetcher.inputs[i].numwords * 4);
|
||||
}
|
||||
}
|
||||
|
||||
if(initialValues[i].included)
|
||||
data += initialValues[i].numwords;
|
||||
}
|
||||
|
||||
for(int i = 0; i < 4; i++)
|
||||
{
|
||||
if(i != destIdx)
|
||||
{
|
||||
interpreter->workgroup[i].inputs = state.inputs;
|
||||
interpreter->workgroup[i].semantics = state.semantics;
|
||||
interpreter->workgroup[i].variables = state.variables;
|
||||
interpreter->workgroup[i].SetHelper();
|
||||
}
|
||||
if(fetcher.inputs[i].included)
|
||||
data += fetcher.inputs[i].numwords * sizeof(uint32_t);
|
||||
}
|
||||
|
||||
// fetch any inputs that were evaluated at sample granularity
|
||||
for(const GlobalState::SampleEvalCacheKey &key : evalSampleCacheData)
|
||||
for(const DXDebug::SampleEvalCacheKey &key : fetcher.evalSampleCacheData)
|
||||
{
|
||||
// start with the basic input value
|
||||
ShaderVariable var = state.inputs[key.inputRegisterIndex];
|
||||
@@ -2557,27 +2263,19 @@ void ExtractInputsPS(PSInput IN, float4 debug_pixelPos : SV_Position,
|
||||
// copy over the value into the variable
|
||||
memcpy(var.value.f32v.data(), 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;
|
||||
}
|
||||
// store in the global cache for this thread
|
||||
DXDebug::SampleEvalCacheKey k = key;
|
||||
k.quadIndex = q;
|
||||
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);
|
||||
}
|
||||
|
||||
ret->inputs = state.inputs;
|
||||
ret->inputs = interpreter->activeLane().inputs;
|
||||
ret->constantBlocks = global.constantBlocks;
|
||||
|
||||
SAFE_DELETE_ARRAY(initialData);
|
||||
SAFE_DELETE_ARRAY(evalData);
|
||||
|
||||
dxbc->FillTraceLineInfo(*ret);
|
||||
|
||||
return ret;
|
||||
@@ -2587,9 +2285,6 @@ ShaderDebugTrace *D3D11Replay::DebugThread(uint32_t eventId,
|
||||
const rdcfixedarray<uint32_t, 3> &groupid,
|
||||
const rdcfixedarray<uint32_t, 3> &threadid)
|
||||
{
|
||||
using namespace DXBCBytecode;
|
||||
using namespace DXBCDebug;
|
||||
|
||||
D3D11MarkerRegion region(StringFormat::Fmt("DebugThread @ %u: [%u, %u, %u] (%u, %u, %u)", eventId,
|
||||
groupid[0], groupid[1], groupid[2], threadid[0],
|
||||
threadid[1], threadid[2]));
|
||||
@@ -2624,11 +2319,11 @@ ShaderDebugTrace *D3D11Replay::DebugThread(uint32_t eventId,
|
||||
threadid[2] * refl.dispatchThreadsDimension[0] * refl.dispatchThreadsDimension[1];
|
||||
}
|
||||
|
||||
InterpretDebugger *interpreter = new InterpretDebugger;
|
||||
DXBCDebug::InterpretDebugger *interpreter = new DXBCDebug::InterpretDebugger;
|
||||
interpreter->eventId = eventId;
|
||||
ShaderDebugTrace *ret = interpreter->BeginDebug(dxbc, refl, activeIndex);
|
||||
GlobalState &global = interpreter->global;
|
||||
ThreadState &state = interpreter->activeLane();
|
||||
DXBCDebug::GlobalState &global = interpreter->global;
|
||||
DXBCDebug::ThreadState &state = interpreter->activeLane();
|
||||
|
||||
AddCBuffersToGlobalState(*dxbc->GetDXBCByteCode(), *GetDebugManager(), global, ret->sourceVars,
|
||||
rs->CS, refl);
|
||||
@@ -2648,28 +2343,29 @@ ShaderDebugTrace *D3D11Replay::DebugThread(uint32_t eventId,
|
||||
{
|
||||
const DXBCBytecode::Declaration &decl = dxbc->GetDXBCByteCode()->GetDeclaration(i);
|
||||
|
||||
if(decl.declaration == OPCODE_DCL_INPUT &&
|
||||
(decl.operand.type == TYPE_INPUT_THREAD_ID || decl.operand.type == TYPE_INPUT_THREAD_GROUP_ID ||
|
||||
decl.operand.type == TYPE_INPUT_THREAD_ID_IN_GROUP ||
|
||||
decl.operand.type == TYPE_INPUT_THREAD_ID_IN_GROUP_FLATTENED))
|
||||
if(decl.declaration == DXBCBytecode::OPCODE_DCL_INPUT &&
|
||||
(decl.operand.type == DXBCBytecode::TYPE_INPUT_THREAD_ID ||
|
||||
decl.operand.type == DXBCBytecode::TYPE_INPUT_THREAD_GROUP_ID ||
|
||||
decl.operand.type == DXBCBytecode::TYPE_INPUT_THREAD_ID_IN_GROUP ||
|
||||
decl.operand.type == DXBCBytecode::TYPE_INPUT_THREAD_ID_IN_GROUP_FLATTENED))
|
||||
{
|
||||
ShaderVariable v;
|
||||
|
||||
v.name = decl.operand.toString(dxbc->GetReflection(), ToString::IsDecl);
|
||||
v.name = decl.operand.toString(dxbc->GetReflection(), DXBCBytecode::ToString::IsDecl);
|
||||
v.rows = 1;
|
||||
v.type = VarType::UInt;
|
||||
|
||||
switch(decl.operand.type)
|
||||
{
|
||||
case TYPE_INPUT_THREAD_GROUP_ID:
|
||||
case DXBCBytecode::TYPE_INPUT_THREAD_GROUP_ID:
|
||||
memcpy(v.value.u32v.data(), state.semantics.GroupID, sizeof(uint32_t) * 3);
|
||||
v.columns = 3;
|
||||
break;
|
||||
case TYPE_INPUT_THREAD_ID_IN_GROUP:
|
||||
case DXBCBytecode::TYPE_INPUT_THREAD_ID_IN_GROUP:
|
||||
memcpy(v.value.u32v.data(), state.semantics.ThreadID, sizeof(uint32_t) * 3);
|
||||
v.columns = 3;
|
||||
break;
|
||||
case TYPE_INPUT_THREAD_ID:
|
||||
case DXBCBytecode::TYPE_INPUT_THREAD_ID:
|
||||
v.value.u32v[0] =
|
||||
state.semantics.GroupID[0] * dxbc->GetReflection()->DispatchThreadsDimension[0] +
|
||||
state.semantics.ThreadID[0];
|
||||
@@ -2681,7 +2377,7 @@ ShaderDebugTrace *D3D11Replay::DebugThread(uint32_t eventId,
|
||||
state.semantics.ThreadID[2];
|
||||
v.columns = 3;
|
||||
break;
|
||||
case TYPE_INPUT_THREAD_ID_IN_GROUP_FLATTENED:
|
||||
case DXBCBytecode::TYPE_INPUT_THREAD_ID_IN_GROUP_FLATTENED:
|
||||
v.value.u32v[0] =
|
||||
state.semantics.ThreadID[2] * dxbc->GetReflection()->DispatchThreadsDimension[0] *
|
||||
dxbc->GetReflection()->DispatchThreadsDimension[1] +
|
||||
|
||||
@@ -44,19 +44,6 @@ RDOC_EXTERN_CONFIG(bool, D3D_Hack_EnableGroups);
|
||||
|
||||
using namespace DXBCBytecode;
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
static bool IsShaderParameterVisible(DXBC::ShaderType shaderType,
|
||||
D3D12_SHADER_VISIBILITY shaderVisibility)
|
||||
{
|
||||
@@ -1645,9 +1632,6 @@ void GatherConstantBuffers(WrappedID3D12Device *pDevice, const DXBCBytecode::Pro
|
||||
ShaderDebugTrace *D3D12Replay::DebugVertex(uint32_t eventId, uint32_t vertid, uint32_t instid,
|
||||
uint32_t idx, uint32_t view)
|
||||
{
|
||||
using namespace DXBCBytecode;
|
||||
using namespace DXBCDebug;
|
||||
|
||||
D3D12MarkerRegion region(
|
||||
m_pDevice->GetQueue()->GetReal(),
|
||||
StringFormat::Fmt("DebugVertex @ %u of (%u,%u,%u)", eventId, vertid, instid, idx));
|
||||
@@ -1759,11 +1743,11 @@ ShaderDebugTrace *D3D12Replay::DebugVertex(uint32_t eventId, uint32_t vertid, ui
|
||||
ShaderDebugTrace *ret = NULL;
|
||||
if(dxbc->GetDXBCByteCode())
|
||||
{
|
||||
InterpretDebugger *interpreter = new InterpretDebugger;
|
||||
DXBCDebug::InterpretDebugger *interpreter = new DXBCDebug::InterpretDebugger;
|
||||
interpreter->eventId = eventId;
|
||||
ret = interpreter->BeginDebug(dxbc, refl, 0);
|
||||
GlobalState &global = interpreter->global;
|
||||
ThreadState &state = interpreter->activeLane();
|
||||
DXBCDebug::GlobalState &global = interpreter->global;
|
||||
DXBCDebug::ThreadState &state = interpreter->activeLane();
|
||||
|
||||
// Fetch constant buffer data from root signature
|
||||
GatherConstantBuffers(m_pDevice, *dxbc->GetDXBCByteCode(), rs.graphics, refl, global,
|
||||
@@ -2287,10 +2271,6 @@ ShaderDebugTrace *D3D12Replay::DebugVertex(uint32_t eventId, uint32_t vertid, ui
|
||||
ShaderDebugTrace *D3D12Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t y,
|
||||
const DebugPixelInputs &inputs)
|
||||
{
|
||||
using namespace DXBC;
|
||||
using namespace DXBCBytecode;
|
||||
using namespace DXBCDebug;
|
||||
|
||||
uint32_t sample = inputs.sample;
|
||||
uint32_t primitive = inputs.primitive;
|
||||
|
||||
@@ -2316,7 +2296,7 @@ ShaderDebugTrace *D3D12Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t
|
||||
return new ShaderDebugTrace;
|
||||
}
|
||||
|
||||
DXBCContainer *dxbc = ps->GetDXBC();
|
||||
DXBC::DXBCContainer *dxbc = ps->GetDXBC();
|
||||
const ShaderReflection &refl = ps->GetDetails();
|
||||
|
||||
if(!dxbc)
|
||||
@@ -2336,7 +2316,7 @@ ShaderDebugTrace *D3D12Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t
|
||||
ShaderDebugTrace *ret = NULL;
|
||||
|
||||
// Fetch the previous stage's disassembly, to match outputs to PS inputs
|
||||
DXBCContainer *prevDxbc = NULL;
|
||||
DXBC::DXBCContainer *prevDxbc = NULL;
|
||||
// Check for geometry shader first
|
||||
{
|
||||
WrappedID3D12Shader *gs = (WrappedID3D12Shader *)pso->graphics->GS.pShaderBytecode;
|
||||
@@ -2365,166 +2345,6 @@ ShaderDebugTrace *D3D12Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t
|
||||
prevDxbc = vs->GetDXBC();
|
||||
}
|
||||
|
||||
rdcarray<PSInputElement> initialValues;
|
||||
rdcarray<rdcstr> floatInputs;
|
||||
rdcarray<rdcstr> inputVarNames;
|
||||
rdcstr extractHlsl;
|
||||
int structureStride = 0;
|
||||
|
||||
rdcarray<DXBC::InterpolationMode> interpModes;
|
||||
const rdcarray<SigParameter> &inputSig = dxbc->GetReflection()->InputSig;
|
||||
if(dxbc->GetDXBCByteCode())
|
||||
DXBCDebug::GetInterpolationModeForInputParams(inputSig, dxbc->GetDXBCByteCode(), interpModes);
|
||||
else
|
||||
DXILDebug::GetInterpolationModeForInputParams(inputSig, dxbc->GetDXILByteCode(), interpModes);
|
||||
|
||||
std::map<ShaderBuiltin, rdcstr> usedInputs; // only used for DXIL
|
||||
DXDebug::GatherPSInputDataForInitialValues(inputSig, prevDxbc->GetReflection()->OutputSig,
|
||||
interpModes, initialValues, floatInputs, inputVarNames,
|
||||
extractHlsl, structureStride, usedInputs);
|
||||
|
||||
uint32_t overdrawLevels = 100; // maximum number of overdraw levels
|
||||
|
||||
// If the pipe contains a mesh/geometry shader, then SV_PrimitiveID cannot be used in the pixel
|
||||
// shader without being emitted from the mesh/geometry shader. For now, check if this semantic
|
||||
// will succeed in a new pixel shader with the rest of the pipe unchanged
|
||||
bool usePrimitiveID =
|
||||
((prevDxbc->m_Type != ShaderType::Geometry) && (prevDxbc->m_Type != ShaderType::Mesh));
|
||||
for(const PSInputElement &e : initialValues)
|
||||
{
|
||||
if(e.sysattribute == ShaderBuiltin::PrimitiveIndex)
|
||||
{
|
||||
usePrimitiveID = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Store a copy of the event's render state to restore later
|
||||
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)
|
||||
{
|
||||
if(dxbc->GetDXBCByteCode())
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
RDCWARN("TODO DXIL Pixel Shader Debugging support for MSAA Evaluate");
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
)";
|
||||
|
||||
WrappedID3D12RootSignature *sig =
|
||||
m_pDevice->GetResourceManager()->GetCurrentAs<WrappedID3D12RootSignature>(rs.graphics.rootsig);
|
||||
|
||||
@@ -2532,157 +2352,30 @@ struct PSInitialData
|
||||
RDCASSERT(sig->sig.dwordLength < 64);
|
||||
D3D12RootSignature modsig = sig->sig;
|
||||
|
||||
UINT regSpace = GetFreeRegSpace(modsig, 0, D3D12DescriptorType::UAV, D3D12_SHADER_VISIBILITY_PIXEL);
|
||||
DXDebug::PSInputFetcherConfig cfg;
|
||||
DXDebug::PSInputFetcher fetcher;
|
||||
|
||||
// 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 += StringFormat::Fmt(
|
||||
"RWStructuredBuffer<PSInitialData> PSInitialBuffer : register(u1, space%u);\n\n", regSpace);
|
||||
D3D12_EXPANDED_PIPELINE_STATE_STREAM_DESC pipeDesc;
|
||||
m_pDevice->GetResourceManager()->GetCurrentAs<WrappedID3D12PipelineState>(rs.pipe)->Fill(pipeDesc);
|
||||
|
||||
if(!evalSampleCacheData.empty())
|
||||
{
|
||||
// float4 is wasteful in some cases but it's easier than using byte buffers and manual packing
|
||||
extractHlsl +=
|
||||
StringFormat::Fmt("RWBuffer<float4> PSEvalBuffer : register(u2, space%u);\n\n", regSpace);
|
||||
}
|
||||
// Store a copy of the event's render state to restore later
|
||||
D3D12RenderState prevState = rs;
|
||||
|
||||
// The semantics that RenderDoc requires in the shader
|
||||
bool inputHas_SV_Position = false;
|
||||
bool inputHas_SV_PrimitiveID = false;
|
||||
// SV_Coverage, SV_IsFrontFace, SV_SampleIndex : are not in the input structure, see
|
||||
// GatherPSInputDataForInitialValues
|
||||
bool inputHas_SV_Coverage = false;
|
||||
bool inputHas_SV_IsFrontFace = false;
|
||||
bool inputHas_SV_SampleIndex = false;
|
||||
cfg.x = x;
|
||||
cfg.y = y;
|
||||
cfg.uavslot = 1;
|
||||
cfg.uavspace = GetFreeRegSpace(modsig, 0, D3D12DescriptorType::UAV, D3D12_SHADER_VISIBILITY_PIXEL);
|
||||
cfg.outputSampleCount = RDCMAX(1U, pipeDesc.SampleDesc.Count);
|
||||
|
||||
// DXC compiler errors if a semantic input is declared in multiple places
|
||||
if(dxbc->GetDXILByteCode())
|
||||
{
|
||||
inputHas_SV_Position = usedInputs.count(ShaderBuiltin::Position) > 0;
|
||||
inputHas_SV_PrimitiveID = usedInputs.count(ShaderBuiltin::PrimitiveIndex) > 0;
|
||||
}
|
||||
|
||||
extractHlsl += "void ExtractInputsPS(PSInput IN";
|
||||
if(!inputHas_SV_Position)
|
||||
extractHlsl += ", float4 debug_pixelPos : SV_Position";
|
||||
if(usePrimitiveID && !inputHas_SV_PrimitiveID)
|
||||
extractHlsl += ", uint prim : SV_PrimitiveID";
|
||||
if(!inputHas_SV_SampleIndex)
|
||||
extractHlsl += ", uint sample : SV_SampleIndex";
|
||||
if(!inputHas_SV_Coverage)
|
||||
extractHlsl += ", uint covge : SV_Coverage";
|
||||
if(!inputHas_SV_IsFrontFace)
|
||||
extractHlsl += ", bool fface : SV_IsFrontFace";
|
||||
|
||||
extractHlsl += ")\n{\n";
|
||||
|
||||
// Only used for DXIL shaders: copy any SV inputs we need from the input structure
|
||||
if(inputHas_SV_Position)
|
||||
extractHlsl += " float4 debug_pixelPos = IN." + usedInputs[ShaderBuiltin::Position] + ";\n";
|
||||
if(usePrimitiveID && inputHas_SV_PrimitiveID)
|
||||
extractHlsl += " uint prim = IN." + usedInputs[ShaderBuiltin::PrimitiveIndex] + ";\n";
|
||||
|
||||
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";
|
||||
|
||||
if(usePrimitiveID)
|
||||
extractHlsl += " PSInitialBuffer[idx].prim = prim;\n";
|
||||
else
|
||||
extractHlsl += " PSInitialBuffer[idx].prim = 0;\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";
|
||||
|
||||
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];
|
||||
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}";
|
||||
DXDebug::CreatePSInputFetcher(dxbc, prevDxbc, cfg, fetcher);
|
||||
|
||||
// Create pixel shader to get initial values from previous stage output
|
||||
ID3DBlob *psBlob = NULL;
|
||||
UINT flags = D3DCOMPILE_WARNINGS_ARE_ERRORS;
|
||||
if(dxbc->GetDXBCByteCode())
|
||||
{
|
||||
if(m_pDevice->GetShaderCache()->GetShaderBlob(extractHlsl.c_str(), "ExtractInputsPS", flags, {},
|
||||
"ps_5_1", &psBlob) != "")
|
||||
if(m_pDevice->GetShaderCache()->GetShaderBlob(fetcher.hlsl.c_str(), "ExtractInputsPS", flags,
|
||||
{}, "ps_5_1", &psBlob) != "")
|
||||
{
|
||||
RDCERR("Failed to create shader to extract inputs");
|
||||
return new ShaderDebugTrace;
|
||||
@@ -2704,11 +2397,11 @@ struct PSInitialData
|
||||
ShaderCompileFlags compileFlags =
|
||||
DXBC::EncodeFlags(m_pDevice->GetShaderCache()->GetCompileFlags(), profile);
|
||||
|
||||
const GlobalShaderFlags shaderFlags = dxbc->GetGlobalShaderFlags();
|
||||
if(shaderFlags & GlobalShaderFlags::NativeLowPrecision)
|
||||
const DXBC::GlobalShaderFlags shaderFlags = dxbc->GetGlobalShaderFlags();
|
||||
if(shaderFlags & DXBC::GlobalShaderFlags::NativeLowPrecision)
|
||||
compileFlags.flags.push_back({"@compile_option", "-enable-16bit-types"});
|
||||
|
||||
if(m_pDevice->GetShaderCache()->GetShaderBlob(extractHlsl.c_str(), "ExtractInputsPS",
|
||||
if(m_pDevice->GetShaderCache()->GetShaderBlob(fetcher.hlsl.c_str(), "ExtractInputsPS",
|
||||
compileFlags, {}, profile, &psBlob) != "")
|
||||
{
|
||||
RDCERR("Failed to create shader to extract inputs");
|
||||
@@ -2716,15 +2409,8 @@ struct PSInitialData
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
uint32_t structStride =
|
||||
sizeof(DXDebug::PixelDebugHit) + 4 * (sizeof(DXDebug::LaneData) + fetcher.stride);
|
||||
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
@@ -2732,7 +2418,7 @@ struct PSInitialData
|
||||
D3D12_RESOURCE_DESC rdesc;
|
||||
ZeroMemory(&rdesc, sizeof(D3D12_RESOURCE_DESC));
|
||||
rdesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
|
||||
rdesc.Width = structStride * (overdrawLevels + 1);
|
||||
rdesc.Width = structStride * (DXDebug::maxPixelHits + 1);
|
||||
rdesc.Height = 1;
|
||||
rdesc.DepthOrArraySize = 1;
|
||||
rdesc.MipLevels = 1;
|
||||
@@ -2763,9 +2449,10 @@ struct PSInitialData
|
||||
|
||||
// Create buffer to store MSAA evaluations captured in pixel shader
|
||||
ID3D12Resource *pMsaaEvalBuffer = NULL;
|
||||
if(!evalSampleCacheData.empty())
|
||||
if(!fetcher.evalSampleCacheData.empty())
|
||||
{
|
||||
rdesc.Width = UINT(evalSampleCacheData.size() * sizeof(Vec4f) * (overdrawLevels + 1));
|
||||
rdesc.Width =
|
||||
UINT(fetcher.evalSampleCacheData.size() * sizeof(Vec4f) * (DXDebug::maxPixelHits + 1));
|
||||
hr = m_pDevice->CreateCommittedResource(&heapProps, D3D12_HEAP_FLAG_NONE, &rdesc, resourceState,
|
||||
NULL, __uuidof(ID3D12Resource),
|
||||
(void **)&pMsaaEvalBuffer);
|
||||
@@ -2784,7 +2471,7 @@ struct PSInitialData
|
||||
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.NumElements = DXDebug::maxPixelHits + 1;
|
||||
uavDesc.Buffer.StructureByteStride = structStride;
|
||||
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE uav = m_pDevice->GetDebugManager()->GetCPUHandle(SHADER_DEBUG_UAV);
|
||||
@@ -2792,7 +2479,7 @@ struct PSInitialData
|
||||
|
||||
uavDesc.Format = DXGI_FORMAT_R32_UINT;
|
||||
uavDesc.Buffer.FirstElement = 0;
|
||||
uavDesc.Buffer.NumElements = structStride * (overdrawLevels + 1) / sizeof(uint32_t);
|
||||
uavDesc.Buffer.NumElements = structStride * (DXDebug::maxPixelHits + 1) / sizeof(uint32_t);
|
||||
uavDesc.Buffer.StructureByteStride = 0;
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE clearUav =
|
||||
m_pDevice->GetDebugManager()->GetUAVClearHandle(SHADER_DEBUG_UAV);
|
||||
@@ -2806,12 +2493,13 @@ struct PSInitialData
|
||||
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();
|
||||
uavDesc.Buffer.NumElements =
|
||||
(DXDebug::maxPixelHits + 1) * (uint32_t)fetcher.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);
|
||||
(UINT)fetcher.evalSampleCacheData.size() * (DXDebug::maxPixelHits + 1) / sizeof(uint32_t);
|
||||
m_pDevice->CreateUnorderedAccessView(pMsaaEvalBuffer, NULL, &uavDesc, msaaClearUav);
|
||||
}
|
||||
|
||||
@@ -2820,7 +2508,7 @@ struct PSInitialData
|
||||
descRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
|
||||
descRange.NumDescriptors = pMsaaEvalBuffer ? 2 : 1;
|
||||
descRange.BaseShaderRegister = 1;
|
||||
descRange.RegisterSpace = regSpace;
|
||||
descRange.RegisterSpace = cfg.uavspace;
|
||||
descRange.Flags = D3D12_DESCRIPTOR_RANGE_FLAG_NONE;
|
||||
descRange.OffsetInDescriptorsFromTableStart = 0;
|
||||
|
||||
@@ -2958,7 +2646,7 @@ struct PSInitialData
|
||||
SAFE_RELEASE(pMsaaEvalBuffer);
|
||||
SAFE_RELEASE(initialPso);
|
||||
|
||||
DebugHit *buf = (DebugHit *)initialData.data();
|
||||
DXDebug::PixelDebugHit *buf = (DXDebug::PixelDebugHit *)initialData.data();
|
||||
|
||||
D3D12MarkerRegion::Set(m_pDevice->GetQueue()->GetReal(),
|
||||
StringFormat::Fmt("Got %u hits", buf[0].numHits));
|
||||
@@ -2974,46 +2662,41 @@ struct PSInitialData
|
||||
// depth test and debug that, just by checking if the depth test is ordered and picking the final
|
||||
// fragment in the series
|
||||
|
||||
// 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);
|
||||
|
||||
// Get depth func and determine "winner" pixel
|
||||
DebugHit *pWinnerHit = NULL;
|
||||
DXDebug::PixelDebugHit *pWinnerHit = NULL;
|
||||
float *evalSampleCache = (float *)evalData.data();
|
||||
size_t winnerIdx = 0;
|
||||
|
||||
if(sample == ~0U)
|
||||
sample = 0;
|
||||
|
||||
if(primitive != ~0U)
|
||||
{
|
||||
for(size_t i = 0; i < buf[0].numHits && i < overdrawLevels; i++)
|
||||
for(size_t i = 0; i < buf[0].numHits && i < DXDebug::maxPixelHits; i++)
|
||||
{
|
||||
DebugHit *pHit = (DebugHit *)(initialData.data() + i * structStride);
|
||||
DXDebug::PixelDebugHit *pHit =
|
||||
(DXDebug::PixelDebugHit *)(initialData.data() + i * structStride);
|
||||
|
||||
if(pHit->primitive == primitive && pHit->sample == sample)
|
||||
{
|
||||
pWinnerHit = pHit;
|
||||
evalSampleCache = ((float *)evalData.data() + evalSampleCacheData.size() * 4 * i);
|
||||
winnerIdx = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(pWinnerHit == NULL)
|
||||
{
|
||||
for(size_t i = 0; i < buf[0].numHits && i < overdrawLevels; i++)
|
||||
for(size_t i = 0; i < buf[0].numHits && i < DXDebug::maxPixelHits; i++)
|
||||
{
|
||||
DebugHit *pHit = (DebugHit *)(initialData.data() + i * structStride);
|
||||
DXDebug::PixelDebugHit *pHit =
|
||||
(DXDebug::PixelDebugHit *)(initialData.data() + i * structStride);
|
||||
|
||||
if(pWinnerHit == NULL)
|
||||
{
|
||||
// If we haven't picked a winner at all yet, use the first one
|
||||
pWinnerHit = pHit;
|
||||
evalSampleCache = ((float *)evalData.data()) + evalSampleCacheData.size() * 4 * i;
|
||||
winnerIdx = i;
|
||||
}
|
||||
else if(pHit->sample == sample)
|
||||
{
|
||||
@@ -3022,7 +2705,7 @@ struct PSInitialData
|
||||
{
|
||||
// The previously selected winner was for the wrong sample, use this one
|
||||
pWinnerHit = pHit;
|
||||
evalSampleCache = ((float *)evalData.data()) + evalSampleCacheData.size() * 4 * i;
|
||||
winnerIdx = i;
|
||||
}
|
||||
else if(depthFunc == D3D12_COMPARISON_FUNC_EQUAL && existingDepth >= 0.0f)
|
||||
{
|
||||
@@ -3030,7 +2713,7 @@ struct PSInitialData
|
||||
if(fabs(pHit->depth - existingDepth) < fabs(pWinnerHit->depth - existingDepth))
|
||||
{
|
||||
pWinnerHit = pHit;
|
||||
evalSampleCache = ((float *)evalData.data()) + evalSampleCacheData.size() * 4 * i;
|
||||
winnerIdx = i;
|
||||
}
|
||||
}
|
||||
else if(depthFunc == D3D12_COMPARISON_FUNC_ALWAYS ||
|
||||
@@ -3039,7 +2722,7 @@ struct PSInitialData
|
||||
{
|
||||
// For depth functions without a sensible comparison, use the last sample encountered
|
||||
pWinnerHit = pHit;
|
||||
evalSampleCache = ((float *)evalData.data()) + evalSampleCacheData.size() * 4 * i;
|
||||
winnerIdx = i;
|
||||
}
|
||||
else if((depthFunc == D3D12_COMPARISON_FUNC_LESS && pHit->depth < pWinnerHit->depth) ||
|
||||
(depthFunc == D3D12_COMPARISON_FUNC_LESS_EQUAL && pHit->depth <= pWinnerHit->depth) ||
|
||||
@@ -3048,103 +2731,105 @@ struct PSInitialData
|
||||
{
|
||||
// For depth functions with an inequality, find the hit that "wins" the most
|
||||
pWinnerHit = pHit;
|
||||
evalSampleCache = ((float *)evalData.data()) + evalSampleCacheData.size() * 4 * i;
|
||||
winnerIdx = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
evalSampleCache =
|
||||
(float *)(evalData.data() + fetcher.evalSampleCacheData.size() * sizeof(Vec4f) * 4 * winnerIdx);
|
||||
|
||||
if(pWinnerHit == NULL)
|
||||
{
|
||||
RDCLOG("Couldn't find any pixels that passed depth test at target coordinates");
|
||||
return new ShaderDebugTrace;
|
||||
}
|
||||
|
||||
DebugHit *pHit = pWinnerHit;
|
||||
uint32_t *data = &pHit->rawdata;
|
||||
float *pos_ddx = (float *)data;
|
||||
DXDebug::PixelDebugHit *hit = pWinnerHit;
|
||||
|
||||
// ddx(SV_Position.x) MUST be 1.0
|
||||
if(*pos_ddx != 1.0f)
|
||||
if(hit->derivValid != 1.0f)
|
||||
{
|
||||
RDCERR("Derivatives invalid");
|
||||
delete ret;
|
||||
return new ShaderDebugTrace;
|
||||
}
|
||||
data++;
|
||||
|
||||
byte *data = (byte *)(hit + 1);
|
||||
|
||||
if(dxbc->GetDXBCByteCode())
|
||||
{
|
||||
InterpretDebugger *interpreter = new InterpretDebugger;
|
||||
DXBCDebug::InterpretDebugger *interpreter = new DXBCDebug::InterpretDebugger;
|
||||
interpreter->eventId = eventId;
|
||||
ret = interpreter->BeginDebug(dxbc, refl, destIdx);
|
||||
GlobalState &global = interpreter->global;
|
||||
ThreadState &state = interpreter->activeLane();
|
||||
ret = interpreter->BeginDebug(dxbc, refl, hit->quadLaneIndex);
|
||||
DXBCDebug::GlobalState &global = interpreter->global;
|
||||
|
||||
// Fetch constant buffer data from root signature
|
||||
GatherConstantBuffers(m_pDevice, *dxbc->GetDXBCByteCode(), rs.graphics, refl, global,
|
||||
ret->sourceVars);
|
||||
|
||||
global.sampleEvalRegisterMask = sampleEvalRegisterMask;
|
||||
global.sampleEvalRegisterMask = fetcher.sampleEvalRegisterMask;
|
||||
|
||||
for(uint32_t q = 0; q < 4; q++)
|
||||
{
|
||||
DXDebug::LaneData *lane = (DXDebug::LaneData *)data;
|
||||
|
||||
DXBCDebug::ThreadState &state = interpreter->workgroup[q];
|
||||
rdcarray<ShaderVariable> &ins = state.inputs;
|
||||
if(!ins.empty() && ins.back().name == "vCoverage")
|
||||
ins.back().value.u32v[0] = pHit->coverage;
|
||||
|
||||
state.semantics.coverage = pHit->coverage;
|
||||
state.semantics.primID = pHit->primitive;
|
||||
state.semantics.isFrontFace = pHit->isFrontFace;
|
||||
if(q != hit->quadLaneIndex)
|
||||
ins = interpreter->workgroup[hit->quadLaneIndex].inputs;
|
||||
|
||||
for(size_t i = 0; i < initialValues.size(); i++)
|
||||
state.semantics.coverage = lane->coverage;
|
||||
state.semantics.primID = hit->primitive;
|
||||
state.semantics.isFrontFace = hit->isFrontFace;
|
||||
|
||||
if(!ins.empty() && ins.back().name == dxbc->GetDXBCByteCode()->GetRegisterName(
|
||||
DXBCBytecode::TYPE_INPUT_COVERAGE_MASK, 0))
|
||||
ins.back().value.u32v[0] = lane->coverage;
|
||||
|
||||
if(lane->isHelper)
|
||||
state.SetHelper();
|
||||
|
||||
data += sizeof(DXDebug::LaneData);
|
||||
|
||||
for(size_t i = 0; i < fetcher.inputs.size(); i++)
|
||||
{
|
||||
int32_t *rawout = NULL;
|
||||
|
||||
if(initialValues[i].reg >= 0)
|
||||
if(fetcher.inputs[i].reg >= 0)
|
||||
{
|
||||
ShaderVariable &invar = ins[initialValues[i].reg];
|
||||
ShaderVariable &invar = ins[fetcher.inputs[i].reg];
|
||||
|
||||
if(initialValues[i].sysattribute == ShaderBuiltin::PrimitiveIndex)
|
||||
if(fetcher.inputs[i].sysattribute == ShaderBuiltin::PrimitiveIndex)
|
||||
{
|
||||
invar.value.u32v[initialValues[i].elem] = pHit->primitive;
|
||||
invar.value.u32v[fetcher.inputs[i].elem] = hit->primitive;
|
||||
}
|
||||
else if(initialValues[i].sysattribute == ShaderBuiltin::MSAASampleIndex)
|
||||
else if(fetcher.inputs[i].sysattribute == ShaderBuiltin::MSAASampleIndex)
|
||||
{
|
||||
invar.value.u32v[initialValues[i].elem] = pHit->sample;
|
||||
invar.value.u32v[fetcher.inputs[i].elem] = hit->sample;
|
||||
}
|
||||
else if(initialValues[i].sysattribute == ShaderBuiltin::MSAACoverage)
|
||||
else if(fetcher.inputs[i].sysattribute == ShaderBuiltin::MSAACoverage)
|
||||
{
|
||||
invar.value.u32v[initialValues[i].elem] = pHit->coverage;
|
||||
invar.value.u32v[fetcher.inputs[i].elem] = lane->coverage;
|
||||
}
|
||||
else if(initialValues[i].sysattribute == ShaderBuiltin::IsFrontFace)
|
||||
else if(fetcher.inputs[i].sysattribute == ShaderBuiltin::IsFrontFace)
|
||||
{
|
||||
invar.value.u32v[initialValues[i].elem] = pHit->isFrontFace ? ~0U : 0;
|
||||
invar.value.u32v[fetcher.inputs[i].elem] = hit->isFrontFace ? ~0U : 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
rawout = &invar.value.s32v[initialValues[i].elem];
|
||||
int32_t *rawout = &invar.value.s32v[fetcher.inputs[i].elem];
|
||||
|
||||
memcpy(rawout, data, initialValues[i].numwords * 4);
|
||||
memcpy(rawout, data, fetcher.inputs[i].numwords * 4);
|
||||
}
|
||||
}
|
||||
|
||||
if(initialValues[i].included)
|
||||
data += initialValues[i].numwords;
|
||||
if(fetcher.inputs[i].included)
|
||||
data += fetcher.inputs[i].numwords * sizeof(uint32_t);
|
||||
}
|
||||
|
||||
for(int i = 0; i < 4; i++)
|
||||
{
|
||||
if(i != destIdx)
|
||||
{
|
||||
interpreter->workgroup[i].inputs = state.inputs;
|
||||
interpreter->workgroup[i].semantics = state.semantics;
|
||||
interpreter->workgroup[i].variables = state.variables;
|
||||
interpreter->workgroup[i].SetHelper();
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch any inputs that were evaluated at sample granularity
|
||||
for(const GlobalState::SampleEvalCacheKey &key : evalSampleCacheData)
|
||||
// fetch any inputs that were evaluated at sample granularity
|
||||
for(const DXDebug::SampleEvalCacheKey &key : fetcher.evalSampleCacheData)
|
||||
{
|
||||
// start with the basic input value
|
||||
ShaderVariable var = state.inputs[key.inputRegisterIndex];
|
||||
@@ -3152,33 +2837,25 @@ struct PSInitialData
|
||||
// copy over the value into the variable
|
||||
memcpy(var.value.f32v.data(), 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;
|
||||
}
|
||||
// store in the global cache for this thread
|
||||
DXDebug::SampleEvalCacheKey k = key;
|
||||
k.quadIndex = q;
|
||||
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);
|
||||
}
|
||||
|
||||
ret->inputs = interpreter->activeLane().inputs;
|
||||
ret->constantBlocks = global.constantBlocks;
|
||||
ret->inputs = state.inputs;
|
||||
}
|
||||
else
|
||||
{
|
||||
DXILDebug::Debugger *debugger = new DXILDebug::Debugger();
|
||||
uint32_t activeLaneIdx = destIdx;
|
||||
ret = debugger->BeginDebug(eventId, dxbc, refl, activeLaneIdx);
|
||||
ret = debugger->BeginDebug(eventId, dxbc, refl, hit->quadLaneIndex);
|
||||
|
||||
DXILDebug::GlobalState &globalState = debugger->GetGlobalState();
|
||||
DXILDebug::ThreadState &activeState = debugger->GetActiveLane();
|
||||
rdcarray<ShaderVariable> &ins = activeState.m_Input.members;
|
||||
const rdcarray<DXIL::EntryPointInterface::Signature> &dxilInputs =
|
||||
debugger->GetDXILEntryPointInputs();
|
||||
|
||||
@@ -3186,52 +2863,60 @@ struct PSInitialData
|
||||
DXILDebug::FetchConstantBufferData(m_pDevice, dxbc->GetDXILByteCode(), rs.graphics, refl,
|
||||
globalState, ret->sourceVars);
|
||||
|
||||
// TODO: SAMPLE EVALUTE MASK
|
||||
// globalState.sampleEvalRegisterMask = sampleEvalRegisterMask;
|
||||
|
||||
// The initial values are packed into register and elements
|
||||
// DXIL Inputs are not packed and contain the register and element linkage
|
||||
rdcarray<DXILDebug::PSInputData> psInputDatas;
|
||||
for(int i = 0; i < initialValues.count(); i++)
|
||||
for(uint32_t q = 0; q < 4; q++)
|
||||
{
|
||||
PSInputElement &inputElement = initialValues[i];
|
||||
int packedRegister = inputElement.reg;
|
||||
if(packedRegister >= 0)
|
||||
{
|
||||
int dxilInputIdx = -1;
|
||||
int dxilArrayIdx = 0;
|
||||
int packedElement = inputElement.elem;
|
||||
int row = packedRegister;
|
||||
// Find the DXIL Input index and element from that matches the register and element
|
||||
for(int j = 0; j < dxilInputs.count(); ++j)
|
||||
{
|
||||
const DXIL::EntryPointInterface::Signature &dxilParam = dxilInputs[j];
|
||||
if((dxilParam.startRow <= row) && (row < (int)(dxilParam.startRow + dxilParam.rows)) &&
|
||||
(dxilParam.startCol == packedElement))
|
||||
{
|
||||
dxilInputIdx = j;
|
||||
dxilArrayIdx = row - dxilParam.startRow;
|
||||
break;
|
||||
}
|
||||
}
|
||||
RDCASSERT(dxilInputIdx >= 0);
|
||||
RDCASSERT(dxilArrayIdx >= 0);
|
||||
DXDebug::LaneData *lane = (DXDebug::LaneData *)data;
|
||||
|
||||
psInputDatas.emplace_back(dxilInputIdx, dxilArrayIdx, inputElement.numwords,
|
||||
inputElement.sysattribute, inputElement.included, data);
|
||||
DXILDebug::ThreadState &state = debugger->GetWorkgroup(q);
|
||||
rdcarray<ShaderVariable> &ins = state.m_Input.members;
|
||||
|
||||
if(q != hit->quadLaneIndex)
|
||||
state.InitialiseHelper(debugger->GetActiveLane());
|
||||
|
||||
data += sizeof(DXDebug::LaneData);
|
||||
|
||||
// TODO: SAMPLE EVALUTE MASK
|
||||
// globalState.sampleEvalRegisterMask = sampleEvalRegisterMask;
|
||||
|
||||
// The initial values are packed into register and elements
|
||||
// DXIL Inputs are not packed and contain the register and element linkage
|
||||
rdcarray<DXILDebug::PSInputData> psInputDatas;
|
||||
for(int i = 0; i < fetcher.inputs.count(); i++)
|
||||
{
|
||||
DXDebug::PSInputElement &inputElement = fetcher.inputs[i];
|
||||
int packedRegister = inputElement.reg;
|
||||
if(packedRegister >= 0)
|
||||
{
|
||||
int dxilInputIdx = -1;
|
||||
int dxilArrayIdx = 0;
|
||||
int packedElement = inputElement.elem;
|
||||
int row = packedRegister;
|
||||
// Find the DXIL Input index and element from that matches the register and element
|
||||
for(int j = 0; j < dxilInputs.count(); ++j)
|
||||
{
|
||||
const DXIL::EntryPointInterface::Signature &dxilParam = dxilInputs[j];
|
||||
if((dxilParam.startRow <= row) && (row < (int)(dxilParam.startRow + dxilParam.rows)) &&
|
||||
(dxilParam.startCol == packedElement))
|
||||
{
|
||||
dxilInputIdx = j;
|
||||
dxilArrayIdx = row - dxilParam.startRow;
|
||||
break;
|
||||
}
|
||||
}
|
||||
RDCASSERT(dxilInputIdx >= 0);
|
||||
RDCASSERT(dxilArrayIdx >= 0);
|
||||
|
||||
psInputDatas.emplace_back(dxilInputIdx, dxilArrayIdx, inputElement.numwords,
|
||||
inputElement.sysattribute, inputElement.included, data);
|
||||
}
|
||||
|
||||
if(inputElement.included)
|
||||
data += inputElement.numwords * sizeof(uint32_t);
|
||||
}
|
||||
|
||||
if(inputElement.included)
|
||||
data += inputElement.numwords;
|
||||
}
|
||||
|
||||
{
|
||||
if(!ins.empty() && ins.back().name == "vCoverage")
|
||||
ins.back().value.u32v[0] = pHit->coverage;
|
||||
|
||||
activeState.m_Semantics.coverage = pHit->coverage;
|
||||
activeState.m_Semantics.primID = pHit->primitive;
|
||||
activeState.m_Semantics.isFrontFace = pHit->isFrontFace;
|
||||
state.m_Semantics.coverage = lane->coverage;
|
||||
state.m_Semantics.primID = hit->primitive;
|
||||
state.m_Semantics.isFrontFace = hit->isFrontFace;
|
||||
|
||||
for(const DXILDebug::PSInputData &psInput : psInputDatas)
|
||||
{
|
||||
@@ -3242,19 +2927,19 @@ struct PSInitialData
|
||||
|
||||
if(psInput.sysattribute == ShaderBuiltin::PrimitiveIndex)
|
||||
{
|
||||
invar.value.u32v[outElement] = pHit->primitive;
|
||||
invar.value.u32v[outElement] = hit->primitive;
|
||||
}
|
||||
else if(psInput.sysattribute == ShaderBuiltin::MSAASampleIndex)
|
||||
{
|
||||
invar.value.u32v[outElement] = pHit->sample;
|
||||
invar.value.u32v[outElement] = hit->sample;
|
||||
}
|
||||
else if(psInput.sysattribute == ShaderBuiltin::MSAACoverage)
|
||||
{
|
||||
invar.value.u32v[outElement] = pHit->coverage;
|
||||
invar.value.u32v[outElement] = lane->coverage;
|
||||
}
|
||||
else if(psInput.sysattribute == ShaderBuiltin::IsFrontFace)
|
||||
{
|
||||
invar.value.u32v[outElement] = pHit->isFrontFace ? ~0U : 0;
|
||||
invar.value.u32v[outElement] = hit->isFrontFace ? ~0U : 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -3266,20 +2951,10 @@ struct PSInitialData
|
||||
memcpy(rawout, psInput.data, psInput.numwords * 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(int i = 0; i < 4; i++)
|
||||
{
|
||||
if(i != destIdx)
|
||||
{
|
||||
DXILDebug::ThreadState &workgroup = debugger->GetWorkgroup(i);
|
||||
workgroup.InitialiseHelper(activeState);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: UPDATE INPUTS FROM SAMPLE CACHE
|
||||
// TODO: UPDATE INPUTS FROM SAMPLE CACHE
|
||||
#if 0
|
||||
for(const GlobalState::SampleEvalCacheKey &key : evalSampleCacheData)
|
||||
for(const DXDebug::SampleEvalCacheKey &key : fetcher.evalSampleCacheData)
|
||||
{
|
||||
// start with the basic input value
|
||||
ShaderVariable var = activeState.m_Input.members[key.inputRegisterIndex];
|
||||
@@ -3288,7 +2963,7 @@ struct PSInitialData
|
||||
memcpy(var.value.f32v.data(), 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;
|
||||
DXDebug::SampleEvalCacheKey k = key;
|
||||
for(int i = 0; i < 4; i++)
|
||||
{
|
||||
k.quadIndex = i;
|
||||
@@ -3299,10 +2974,9 @@ struct PSInitialData
|
||||
evalSampleCache += 4;
|
||||
}
|
||||
#endif
|
||||
DXILDebug::ApplyAllDerivatives(globalState, debugger->GetWorkgroups(), destIdx, psInputDatas,
|
||||
(float *)data);
|
||||
}
|
||||
|
||||
ret->inputs = {activeState.m_Input};
|
||||
ret->inputs = {debugger->GetActiveLane().m_Input};
|
||||
ret->constantBlocks = globalState.constantBlocks;
|
||||
}
|
||||
|
||||
@@ -3315,9 +2989,6 @@ ShaderDebugTrace *D3D12Replay::DebugThread(uint32_t eventId,
|
||||
const rdcfixedarray<uint32_t, 3> &groupid,
|
||||
const rdcfixedarray<uint32_t, 3> &threadid)
|
||||
{
|
||||
using namespace DXBCBytecode;
|
||||
using namespace DXBCDebug;
|
||||
|
||||
D3D12MarkerRegion simloop(
|
||||
m_pDevice->GetQueue()->GetReal(),
|
||||
StringFormat::Fmt("DebugThread @ %u: [%u, %u, %u] (%u, %u, %u)", eventId, groupid[0],
|
||||
@@ -3373,11 +3044,11 @@ ShaderDebugTrace *D3D12Replay::DebugThread(uint32_t eventId,
|
||||
threadid[2] * refl.dispatchThreadsDimension[0] * refl.dispatchThreadsDimension[1];
|
||||
}
|
||||
|
||||
InterpretDebugger *interpreter = new InterpretDebugger;
|
||||
DXBCDebug::InterpretDebugger *interpreter = new DXBCDebug::InterpretDebugger;
|
||||
interpreter->eventId = eventId;
|
||||
ret = interpreter->BeginDebug(dxbc, refl, activeIndex);
|
||||
GlobalState &global = interpreter->global;
|
||||
ThreadState &state = interpreter->activeLane();
|
||||
DXBCDebug::GlobalState &global = interpreter->global;
|
||||
DXBCDebug::ThreadState &state = interpreter->activeLane();
|
||||
|
||||
GatherConstantBuffers(m_pDevice, *dxbc->GetDXBCByteCode(), rs.compute, refl, global,
|
||||
ret->sourceVars);
|
||||
|
||||
@@ -24,18 +24,30 @@
|
||||
|
||||
#include "dx_debug.h"
|
||||
#include "common/formatting.h"
|
||||
#include "driver/shaders/dxil/dxil_debug.h"
|
||||
#include "dxbc_bytecode.h"
|
||||
#include "dxbc_common.h"
|
||||
#include "dxbc_container.h"
|
||||
#include "dxbc_debug.h"
|
||||
|
||||
namespace DXDebug
|
||||
{
|
||||
void GatherPSInputDataForInitialValues(const rdcarray<SigParameter> &stageInputSig,
|
||||
const rdcarray<SigParameter> &prevStageOutputSig,
|
||||
const rdcarray<DXBC::InterpolationMode> &interpModes,
|
||||
rdcarray<PSInputElement> &initialValues,
|
||||
rdcarray<rdcstr> &floatInputs, rdcarray<rdcstr> &inputVarNames,
|
||||
rdcstr &psInputDefinition, int &structureStride,
|
||||
std::map<ShaderBuiltin, rdcstr> &usedInputs)
|
||||
void GatherPSInputDataForInitialValues(const DXBC::DXBCContainer *dxbc,
|
||||
const DXBC::DXBCContainer *prevdxbc, PSInputFetcher &fetcher,
|
||||
rdcarray<rdcstr> &floatInputs, rdcarray<rdcstr> &inputVarNames)
|
||||
{
|
||||
rdcarray<DXBC::InterpolationMode> interpModes;
|
||||
|
||||
const rdcarray<SigParameter> &stageInputSig = dxbc->GetReflection()->InputSig;
|
||||
const rdcarray<SigParameter> &prevStageOutputSig = prevdxbc->GetReflection()->OutputSig;
|
||||
|
||||
if(dxbc->GetDXBCByteCode())
|
||||
DXBCDebug::GetInterpolationModeForInputParams(stageInputSig, dxbc->GetDXBCByteCode(),
|
||||
interpModes);
|
||||
else
|
||||
DXILDebug::GetInterpolationModeForInputParams(stageInputSig, dxbc->GetDXILByteCode(),
|
||||
interpModes);
|
||||
|
||||
// When debugging a pixel shader, we need to get the initial values of each pixel shader
|
||||
// input for the pixel that we are debugging, from whichever the previous shader stage was
|
||||
// configured in the pipeline. This function returns the input element definitions, other
|
||||
@@ -46,20 +58,21 @@ void GatherPSInputDataForInitialValues(const rdcarray<SigParameter> &stageInputS
|
||||
// needed for gathering initial values, such as primitive ID, and also does not provide the
|
||||
// shader function body.
|
||||
|
||||
initialValues.clear();
|
||||
fetcher.inputs.clear();
|
||||
floatInputs.clear();
|
||||
inputVarNames.clear();
|
||||
psInputDefinition = "struct PSInput\n{\n";
|
||||
structureStride = 0;
|
||||
fetcher.hlsl += "struct PSInput\n{\n";
|
||||
rdcstr defines;
|
||||
fetcher.stride = 0;
|
||||
|
||||
if(stageInputSig.empty())
|
||||
{
|
||||
psInputDefinition += "float4 input_dummy : SV_Position;\n";
|
||||
usedInputs[ShaderBuiltin::Position] = "input_dummy";
|
||||
fetcher.hlsl += "float4 input_dummy : SV_Position;\n";
|
||||
fetcher.hlsl += "#define POSITION_VAR input_dummy\n";
|
||||
|
||||
initialValues.push_back(PSInputElement(-1, 0, 4, ShaderBuiltin::Undefined, true));
|
||||
fetcher.inputs.push_back(PSInputElement(-1, 0, 4, ShaderBuiltin::Undefined, true));
|
||||
|
||||
structureStride += 4;
|
||||
fetcher.stride += 4;
|
||||
}
|
||||
|
||||
// name, pair<start semantic index, end semantic index>
|
||||
@@ -74,7 +87,7 @@ void GatherPSInputDataForInitialValues(const rdcarray<SigParameter> &stageInputS
|
||||
{
|
||||
const SigParameter &sig = stageInputSig[i];
|
||||
|
||||
psInputDefinition += " ";
|
||||
fetcher.hlsl += " ";
|
||||
|
||||
bool included = true;
|
||||
|
||||
@@ -83,7 +96,7 @@ void GatherPSInputDataForInitialValues(const rdcarray<SigParameter> &stageInputS
|
||||
sig.systemValue == ShaderBuiltin::IsFrontFace ||
|
||||
sig.systemValue == ShaderBuiltin::MSAASampleIndex)
|
||||
{
|
||||
psInputDefinition += "//";
|
||||
fetcher.hlsl += "//";
|
||||
included = false;
|
||||
}
|
||||
|
||||
@@ -92,7 +105,7 @@ void GatherPSInputDataForInitialValues(const rdcarray<SigParameter> &stageInputS
|
||||
// an interpolant. Only comment it out if it's the last input.
|
||||
if(i + 1 == numInputs && sig.systemValue == ShaderBuiltin::PrimitiveIndex)
|
||||
{
|
||||
psInputDefinition += "//";
|
||||
fetcher.hlsl += "//";
|
||||
included = false;
|
||||
}
|
||||
|
||||
@@ -103,7 +116,7 @@ void GatherPSInputDataForInitialValues(const rdcarray<SigParameter> &stageInputS
|
||||
if(sig.semanticName == arrays[a].first && arrays[a].second.first <= sig.semanticIndex &&
|
||||
arrays[a].second.second >= sig.semanticIndex)
|
||||
{
|
||||
psInputDefinition += "//";
|
||||
fetcher.hlsl += "//";
|
||||
included = false;
|
||||
arrayIndex = sig.semanticIndex - arrays[a].second.first;
|
||||
}
|
||||
@@ -127,13 +140,13 @@ void GatherPSInputDataForInitialValues(const rdcarray<SigParameter> &stageInputS
|
||||
uint32_t bytesPerColumn = (varType != VarType::Half) ? 4 : 2;
|
||||
|
||||
if(varType == VarType::Float)
|
||||
psInputDefinition += "float";
|
||||
fetcher.hlsl += "float";
|
||||
else if(varType == VarType::Half)
|
||||
psInputDefinition += "half";
|
||||
fetcher.hlsl += "half";
|
||||
else if(varType == VarType::SInt)
|
||||
psInputDefinition += "int";
|
||||
fetcher.hlsl += "int";
|
||||
else if(varType == VarType::UInt)
|
||||
psInputDefinition += "uint";
|
||||
fetcher.hlsl += "uint";
|
||||
else
|
||||
RDCERR("Unexpected input signature type: %s",
|
||||
ToStr(prevStageOutputSig[os].varType).c_str());
|
||||
@@ -144,12 +157,13 @@ void GatherPSInputDataForInitialValues(const rdcarray<SigParameter> &stageInputS
|
||||
(prevStageOutputSig[os].regChannelMask & 0x8 ? 1 : 0);
|
||||
|
||||
rdcstr name = prevStageOutputSig[os].semanticIdxName;
|
||||
psInputDefinition += ToStr((uint32_t)numCols) + " input_" + name + " : " + name + ";\n";
|
||||
fetcher.hlsl += ToStr((uint32_t)numCols) + " input_" + name + " : " + name + ";\n";
|
||||
|
||||
uint32_t byteSize = AlignUp4(numCols * bytesPerColumn);
|
||||
structureStride += byteSize;
|
||||
fetcher.stride += byteSize;
|
||||
|
||||
initialValues.push_back(PSInputElement(-1, 0, byteSize / 4, ShaderBuiltin::Undefined, true));
|
||||
fetcher.inputs.push_back(
|
||||
PSInputElement(-1, 0, byteSize / 4, ShaderBuiltin::Undefined, true));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,11 +171,11 @@ void GatherPSInputDataForInitialValues(const rdcarray<SigParameter> &stageInputS
|
||||
{
|
||||
rdcstr dummy_reg = "dummy_register";
|
||||
dummy_reg += ToStr((uint32_t)nextreg + dummy);
|
||||
psInputDefinition += "float4 var_" + dummy_reg + " : semantic_" + dummy_reg + ";\n";
|
||||
fetcher.hlsl += "float4 var_" + dummy_reg + " : semantic_" + dummy_reg + ";\n";
|
||||
|
||||
initialValues.push_back(PSInputElement(-1, 0, 4, ShaderBuiltin::Undefined, true));
|
||||
fetcher.inputs.push_back(PSInputElement(-1, 0, 4, ShaderBuiltin::Undefined, true));
|
||||
|
||||
structureStride += 4 * sizeof(float);
|
||||
fetcher.stride += 4 * sizeof(float);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,8 +183,8 @@ void GatherPSInputDataForInitialValues(const rdcarray<SigParameter> &stageInputS
|
||||
|
||||
DXBC::InterpolationMode interpolation = interpModes[i];
|
||||
if(interpolation != DXBC::InterpolationMode::INTERPOLATION_UNDEFINED)
|
||||
psInputDefinition += ToStr(interpolation) + " ";
|
||||
psInputDefinition += ToStr(sig.varType);
|
||||
fetcher.hlsl += ToStr(interpolation) + " ";
|
||||
fetcher.hlsl += ToStr(sig.varType);
|
||||
|
||||
int numCols = (sig.regChannelMask & 0x1 ? 1 : 0) + (sig.regChannelMask & 0x2 ? 1 : 0) +
|
||||
(sig.regChannelMask & 0x4 ? 1 : 0) + (sig.regChannelMask & 0x8 ? 1 : 0);
|
||||
@@ -240,7 +254,7 @@ void GatherPSInputDataForInitialValues(const rdcarray<SigParameter> &stageInputS
|
||||
if(included)
|
||||
{
|
||||
// in UAV structs, arrays are packed tightly, so just multiply by arrayLength
|
||||
structureStride += 4 * numCols * RDCMAX(1, arrayLength);
|
||||
fetcher.stride += 4 * numCols * RDCMAX(1, arrayLength);
|
||||
}
|
||||
|
||||
// as another side effect of the above, an element declared as a 1-length array won't be
|
||||
@@ -283,12 +297,16 @@ void GatherPSInputDataForInitialValues(const rdcarray<SigParameter> &stageInputS
|
||||
}
|
||||
|
||||
rdcstr inputName = "input_" + name;
|
||||
psInputDefinition += ToStr((uint32_t)numCols) + " " + inputName;
|
||||
fetcher.hlsl += ToStr((uint32_t)numCols) + " " + inputName;
|
||||
if(arrayLength > 0)
|
||||
psInputDefinition += "[" + ToStr(arrayLength) + "]";
|
||||
psInputDefinition += " : " + name;
|
||||
if(sig.systemValue != ShaderBuiltin::Undefined)
|
||||
usedInputs[sig.systemValue] = inputName;
|
||||
fetcher.hlsl += "[" + ToStr(arrayLength) + "]";
|
||||
fetcher.hlsl += " : " + name;
|
||||
// DXIL does not allow redeclaring SV_ variables, any that we might need which could already be
|
||||
// in PSInput must be obtained from there and not redeclared in our entry point
|
||||
if(sig.systemValue == ShaderBuiltin::Position)
|
||||
defines += "#define POSITION_VAR " + inputName + "\n";
|
||||
else if(sig.systemValue == ShaderBuiltin::PrimitiveIndex)
|
||||
defines += "#define PRIM_VAR " + inputName + "\n";
|
||||
|
||||
inputVarNames[i] = inputName;
|
||||
if(arrayLength > 0)
|
||||
@@ -307,7 +325,7 @@ void GatherPSInputDataForInitialValues(const rdcarray<SigParameter> &stageInputS
|
||||
}
|
||||
}
|
||||
|
||||
psInputDefinition += ";\n";
|
||||
fetcher.hlsl += ";\n";
|
||||
|
||||
int firstElem = sig.regChannelMask & 0x1 ? 0
|
||||
: sig.regChannelMask & 0x2 ? 1
|
||||
@@ -323,21 +341,303 @@ void GatherPSInputDataForInitialValues(const rdcarray<SigParameter> &stageInputS
|
||||
{
|
||||
if(arrayLength == 0)
|
||||
{
|
||||
initialValues.push_back(
|
||||
fetcher.inputs.push_back(
|
||||
PSInputElement(sig.regIndex, firstElem, byteSize / 4, sig.systemValue, included));
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int a = 0; a < arrayLength; a++)
|
||||
{
|
||||
initialValues.push_back(
|
||||
fetcher.inputs.push_back(
|
||||
PSInputElement(sig.regIndex + a, firstElem, byteSize / 4, sig.systemValue, included));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
psInputDefinition += "};\n\n";
|
||||
fetcher.hlsl += "};\n\n" + defines;
|
||||
}
|
||||
|
||||
void CreatePSInputFetcher(const DXBC::DXBCContainer *dxbc, const DXBC::DXBCContainer *prevdxbc,
|
||||
const PSInputFetcherConfig &cfg, PSInputFetcher &fetcher)
|
||||
{
|
||||
// If the pipe contains a geometry/mesh shader, then SV_PrimitiveID cannot be used in the pixel
|
||||
// shader without being emitted from the geometry shader. For now, check if this semantic
|
||||
// will succeed in a new pixel shader with the rest of the pipe unchanged
|
||||
bool usePrimitiveID = ((prevdxbc->m_Type != DXBC::ShaderType::Geometry) &&
|
||||
(prevdxbc->m_Type != DXBC::ShaderType::Mesh));
|
||||
|
||||
rdcarray<rdcstr> floatInputs;
|
||||
rdcarray<rdcstr> inputVarNames;
|
||||
DXDebug::GatherPSInputDataForInitialValues(dxbc, prevdxbc, fetcher, floatInputs, inputVarNames);
|
||||
|
||||
for(const PSInputElement &e : fetcher.inputs)
|
||||
{
|
||||
if(e.sysattribute == ShaderBuiltin::PrimitiveIndex)
|
||||
{
|
||||
usePrimitiveID = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fetcher.hlsl += StringFormat::Fmt(
|
||||
"#define DESTX %u.5\n"
|
||||
"#define DESTY %u.5\n"
|
||||
"#define USEPRIM %u\n"
|
||||
"#define MAXHIT %u\n",
|
||||
cfg.x, cfg.y, usePrimitiveID ? 1 : 0, DXDebug::maxPixelHits);
|
||||
|
||||
if(cfg.uavspace == 0)
|
||||
fetcher.hlsl += StringFormat::Fmt(
|
||||
"#define HITBUFFER u%u\n"
|
||||
"#define EVALCACHEBUFFER u%u\n",
|
||||
cfg.uavslot, cfg.uavslot + 1);
|
||||
else
|
||||
fetcher.hlsl += StringFormat::Fmt(
|
||||
"#define HITBUFFER u%u, space%u\n"
|
||||
"#define EVALCACHEBUFFER u%u, space%u\n",
|
||||
cfg.uavslot, cfg.uavspace, cfg.uavslot + 1, cfg.uavspace);
|
||||
|
||||
fetcher.hlsl += "\n";
|
||||
|
||||
fetcher.hlsl += GetEmbeddedResource(quadswizzle_hlsl);
|
||||
|
||||
fetcher.hlsl += R"(
|
||||
struct LaneData
|
||||
{
|
||||
float4 pixelPos;
|
||||
|
||||
uint isHelper;
|
||||
uint quadId;
|
||||
uint quadLane;
|
||||
uint coverage;
|
||||
|
||||
PSInput IN;
|
||||
};
|
||||
|
||||
struct PixelDebugHit
|
||||
{
|
||||
// only used in the first instance
|
||||
uint numHits;
|
||||
float3 pos_depth; // xy position and depth
|
||||
|
||||
float derivValid;
|
||||
uint primitive;
|
||||
uint isFrontFace;
|
||||
uint sample;
|
||||
|
||||
uint quadLaneIndex;
|
||||
uint3 pad;
|
||||
|
||||
// input values
|
||||
LaneData quad[4];
|
||||
};
|
||||
|
||||
RWStructuredBuffer<PixelDebugHit> HitBuffer : register(HITBUFFER);
|
||||
|
||||
// float4 is wasteful in some cases but it's easier than using ByteAddressBuffer and manual
|
||||
// packing
|
||||
RWBuffer<float4> EvalCacheBuffer : register(EVALCACHEBUFFER);
|
||||
|
||||
void ExtractInputsPS(PSInput IN,
|
||||
#ifndef POSITION_VAR
|
||||
float4 debug_pixelPos : SV_Position,
|
||||
#endif
|
||||
#if USEPRIM && !defined(PRIM_VAR)
|
||||
uint primitive : SV_PrimitiveID,
|
||||
#endif
|
||||
// sample, coverage and isFrontFace are deliberately omittted from the
|
||||
// IN struct for SV_ ordering reasons
|
||||
uint sample : SV_SampleIndex,
|
||||
uint coverage : SV_Coverage,
|
||||
bool isFrontFace : SV_IsFrontFace)
|
||||
{
|
||||
#ifdef POSITION_VAR
|
||||
float4 debug_pixelPos = IN.POSITION_VAR;
|
||||
#endif
|
||||
|
||||
#if USEPRIM && defined(PRIM_VAR)
|
||||
uint primitive = IN.PRIM_VAR;
|
||||
#elif !USEPRIM
|
||||
uint primitive = 0;
|
||||
#endif
|
||||
|
||||
const uint quadLaneIndex = (2u * (uint(debug_pixelPos.y) & 1u)) + (uint(debug_pixelPos.x) & 1u);
|
||||
|
||||
// grab our output slot
|
||||
uint idx = MAXHIT;
|
||||
if(abs(debug_pixelPos.x - DESTX) < 0.5f && abs(debug_pixelPos.y - DESTY) < 0.5f)
|
||||
InterlockedAdd(HitBuffer[0].numHits, 1, idx);
|
||||
idx = min(idx, MAXHIT);
|
||||
|
||||
HitBuffer[idx].pos_depth = debug_pixelPos.xyz;
|
||||
|
||||
HitBuffer[idx].derivValid = ddx(debug_pixelPos.x);
|
||||
|
||||
HitBuffer[idx].primitive = primitive;
|
||||
|
||||
HitBuffer[idx].isFrontFace = isFrontFace;
|
||||
HitBuffer[idx].sample = sample;
|
||||
|
||||
HitBuffer[idx].quadLaneIndex = quadLaneIndex;
|
||||
|
||||
// quad pixelPos will be set with other derivatives for float inputs
|
||||
|
||||
// for the simple quad case, only the desired thread is considered non-helper
|
||||
HitBuffer[idx].quad[0].isHelper = 1u;
|
||||
HitBuffer[idx].quad[1].isHelper = 1u;
|
||||
HitBuffer[idx].quad[2].isHelper = 1u;
|
||||
HitBuffer[idx].quad[3].isHelper = 1u;
|
||||
HitBuffer[idx].quad[quadLaneIndex].isHelper = 0u;
|
||||
|
||||
// quadId is a single value that's unique for this quad and uniform across the quad. Degenerate
|
||||
// for the simple quad case
|
||||
uint quadId = quadSwizzleHelper(quadLaneIndex, quadLaneIndex, 0u);
|
||||
HitBuffer[idx].quad[0].quadId = quadId;
|
||||
HitBuffer[idx].quad[1].quadId = quadId;
|
||||
HitBuffer[idx].quad[2].quadId = quadId;
|
||||
HitBuffer[idx].quad[3].quadId = quadId;
|
||||
|
||||
// per-quad lane identifier, degenerate for the simple quad case
|
||||
HitBuffer[idx].quad[0].quadLane = 0;
|
||||
HitBuffer[idx].quad[1].quadLane = 1;
|
||||
HitBuffer[idx].quad[2].quadLane = 2;
|
||||
HitBuffer[idx].quad[3].quadLane = 3;
|
||||
|
||||
// coverage is handled with pixelPos as it can vary per-thread
|
||||
|
||||
// start off with just copying all the inputs to all the quad. For float inputs or uints that may
|
||||
// vary across the quad we will quadSwizzle them
|
||||
HitBuffer[idx].quad[0].IN = IN;
|
||||
HitBuffer[idx].quad[1].IN = IN;
|
||||
HitBuffer[idx].quad[2].IN = IN;
|
||||
HitBuffer[idx].quad[3].IN = IN;
|
||||
)";
|
||||
|
||||
for(int q = 0; q < 4; q++)
|
||||
{
|
||||
fetcher.hlsl += StringFormat::Fmt(
|
||||
" HitBuffer[idx].quad[%i].pixelPos = "
|
||||
"quadSwizzleHelper(debug_pixelPos, quadLaneIndex, %i);\n",
|
||||
q, q);
|
||||
fetcher.hlsl += StringFormat::Fmt(
|
||||
" HitBuffer[idx].quad[%i].coverage = "
|
||||
"quadSwizzleHelper(coverage, quadLaneIndex, %i);\n",
|
||||
q, q);
|
||||
}
|
||||
|
||||
for(size_t i = 0; i < floatInputs.size(); i++)
|
||||
{
|
||||
const rdcstr &name = floatInputs[i];
|
||||
for(int q = 0; q < 4; q++)
|
||||
{
|
||||
fetcher.hlsl += StringFormat::Fmt(
|
||||
" HitBuffer[idx].quad[%i].IN.%s = quadSwizzleHelper(IN.%s, quadLaneIndex, %i);\n", q,
|
||||
name.c_str(), name.c_str(), q);
|
||||
}
|
||||
}
|
||||
|
||||
// if we're not rendering at MSAA, no need to fill the cache because evaluates will all return the
|
||||
// plain input anyway.
|
||||
if(cfg.outputSampleCount > 1)
|
||||
{
|
||||
if(dxbc->GetDXBCByteCode())
|
||||
{
|
||||
dxbc->GetDXBCByteCode()->CalculateEvalSampleCache(cfg, fetcher);
|
||||
}
|
||||
else
|
||||
{
|
||||
RDCWARN("TODO DXIL Pixel Shader Debugging support for MSAA Evaluate");
|
||||
}
|
||||
}
|
||||
|
||||
if(!fetcher.evalSampleCacheData.empty())
|
||||
{
|
||||
fetcher.hlsl += StringFormat::Fmt(" uint stride = %zu;\n", fetcher.evalSampleCacheData.size());
|
||||
fetcher.hlsl += StringFormat::Fmt(" uint evalIdx = idx * stride * 4;\n");
|
||||
fetcher.hlsl += StringFormat::Fmt(" float4 evalCacheVal;\n");
|
||||
|
||||
uint32_t evalIdx = 0;
|
||||
for(const SampleEvalCacheKey &key : fetcher.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);
|
||||
fetcher.hlsl += StringFormat::Fmt(" EvalCacheBuffer[evalIdx+stride*0+%u] = 0;\n", evalIdx);
|
||||
fetcher.hlsl += StringFormat::Fmt(" EvalCacheBuffer[evalIdx+stride*1+%u] = 0;\n", evalIdx);
|
||||
fetcher.hlsl += StringFormat::Fmt(" EvalCacheBuffer[evalIdx+stride*2+%u] = 0;\n", evalIdx);
|
||||
fetcher.hlsl += StringFormat::Fmt(" EvalCacheBuffer[evalIdx+stride*3+%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)
|
||||
{
|
||||
fetcher.hlsl +=
|
||||
StringFormat::Fmt(" evalCacheVal = EvaluateAttributeAtSample(%s, %d).%s;\n",
|
||||
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
|
||||
fetcher.hlsl +=
|
||||
StringFormat::Fmt(" evalCacheVal = EvaluateAttributeSnapped(%s, int2(%d, %d)).%s;\n",
|
||||
name.c_str(), key.offsetx, key.offsety, expandSwizzle.c_str());
|
||||
}
|
||||
|
||||
fetcher.hlsl += StringFormat::Fmt(
|
||||
" EvalCacheBuffer[evalIdx+stride*0+%u] = "
|
||||
"quadSwizzleHelper(evalCacheVal, quadLaneIndex, 0);\n",
|
||||
evalIdx);
|
||||
fetcher.hlsl += StringFormat::Fmt(
|
||||
" EvalCacheBuffer[evalIdx+stride*1+%u] = "
|
||||
"quadSwizzleHelper(evalCacheVal, quadLaneIndex, 1);\n",
|
||||
evalIdx);
|
||||
fetcher.hlsl += StringFormat::Fmt(
|
||||
" EvalCacheBuffer[evalIdx+stride*2+%u] = "
|
||||
"quadSwizzleHelper(evalCacheVal, quadLaneIndex, 2);\n",
|
||||
evalIdx);
|
||||
fetcher.hlsl += StringFormat::Fmt(
|
||||
" EvalCacheBuffer[evalIdx+stride*3+%u] = "
|
||||
"quadSwizzleHelper(evalCacheVal, quadLaneIndex, 3);\n",
|
||||
evalIdx);
|
||||
|
||||
evalIdx++;
|
||||
}
|
||||
}
|
||||
|
||||
fetcher.hlsl += "\n}\n";
|
||||
}
|
||||
|
||||
// "NaN has special handling. If one source operand is NaN, then the other source operand is
|
||||
|
||||
@@ -25,11 +25,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include "maths/vec.h"
|
||||
|
||||
namespace DXBC
|
||||
{
|
||||
enum ResourceRetType;
|
||||
enum class InterpolationMode : uint8_t;
|
||||
class DXBCContainer;
|
||||
};
|
||||
|
||||
namespace DXBCBytecode
|
||||
@@ -44,6 +46,41 @@ typedef DXBC::ResourceRetType ResourceRetType;
|
||||
typedef DXBCBytecode::ResourceDimension ResourceDimension;
|
||||
typedef DXBCBytecode::SamplerMode SamplerMode;
|
||||
|
||||
struct LaneData
|
||||
{
|
||||
Vec4f pixelPos;
|
||||
|
||||
uint32_t isHelper;
|
||||
uint32_t quadId;
|
||||
uint32_t quadLane;
|
||||
uint32_t coverage;
|
||||
|
||||
// user data PSInput below here
|
||||
};
|
||||
|
||||
struct PixelDebugHit
|
||||
{
|
||||
// only used in the first instance
|
||||
uint32_t numHits;
|
||||
// below here are per-hit properties
|
||||
float posx;
|
||||
float posy;
|
||||
float depth;
|
||||
|
||||
float derivValid;
|
||||
uint32_t primitive;
|
||||
uint32_t isFrontFace;
|
||||
uint32_t sample;
|
||||
|
||||
uint32_t quadLaneIndex;
|
||||
uint32_t pad[3];
|
||||
|
||||
// LaneData quad[4] below here
|
||||
};
|
||||
|
||||
// maximum number of overdraw levels before we start losing potential pixel hits
|
||||
static const uint32_t maxPixelHits = 100;
|
||||
|
||||
struct PSInputElement
|
||||
{
|
||||
PSInputElement(int regster, int element, int numWords, ShaderBuiltin attr, bool inc)
|
||||
@@ -64,13 +101,64 @@ struct PSInputElement
|
||||
bool included;
|
||||
};
|
||||
|
||||
void GatherPSInputDataForInitialValues(const rdcarray<SigParameter> &stageInputSig,
|
||||
const rdcarray<SigParameter> &prevStageOutputSig,
|
||||
const rdcarray<DXBC::InterpolationMode> &interpModes,
|
||||
rdcarray<PSInputElement> &initialValues,
|
||||
rdcarray<rdcstr> &floatInputs, rdcarray<rdcstr> &inputVarNames,
|
||||
rdcstr &psInputDefinition, int &structureStride,
|
||||
std::map<ShaderBuiltin, rdcstr> &usedInputs);
|
||||
struct SampleEvalCacheKey
|
||||
{
|
||||
int32_t quadIndex = -1; // index of this thread in the quad
|
||||
int32_t inputRegisterIndex = -1; // index of the input register
|
||||
int32_t firstComponent = 0; // the first component in the register
|
||||
int32_t numComponents = 0; // how many components in the register
|
||||
int32_t sample = -1; // -1 for offset-from-centroid lookups
|
||||
int32_t offsetx = 0, offsety = 0; // integer offset from centroid
|
||||
|
||||
bool operator<(const SampleEvalCacheKey &o) const
|
||||
{
|
||||
if(quadIndex != o.quadIndex)
|
||||
return quadIndex < o.quadIndex;
|
||||
|
||||
if(inputRegisterIndex != o.inputRegisterIndex)
|
||||
return inputRegisterIndex < o.inputRegisterIndex;
|
||||
|
||||
if(firstComponent != o.firstComponent)
|
||||
return firstComponent < o.firstComponent;
|
||||
|
||||
if(numComponents != o.numComponents)
|
||||
return numComponents < o.numComponents;
|
||||
|
||||
if(sample != o.sample)
|
||||
return sample < o.sample;
|
||||
|
||||
if(offsetx != o.offsetx)
|
||||
return offsetx < o.offsetx;
|
||||
|
||||
return offsety < o.offsety;
|
||||
}
|
||||
bool operator==(const SampleEvalCacheKey &o) const { return !(*this < o) && !(o < *this); }
|
||||
};
|
||||
|
||||
struct PSInputFetcherConfig
|
||||
{
|
||||
uint32_t x = 0, y = 0;
|
||||
uint32_t uavslot = 0;
|
||||
uint32_t uavspace = 0;
|
||||
uint32_t outputSampleCount = 1;
|
||||
};
|
||||
|
||||
struct PSInputFetcher
|
||||
{
|
||||
// stride of the generated PSInput struct
|
||||
uint32_t stride = 0;
|
||||
// members of the PSInput struct
|
||||
rdcarray<PSInputElement> inputs;
|
||||
|
||||
// per-sample evaluation cache
|
||||
rdcarray<SampleEvalCacheKey> evalSampleCacheData;
|
||||
uint64_t sampleEvalRegisterMask = 0;
|
||||
|
||||
rdcstr hlsl;
|
||||
};
|
||||
|
||||
void CreatePSInputFetcher(const DXBC::DXBCContainer *dxbc, const DXBC::DXBCContainer *prevdxbc,
|
||||
const PSInputFetcherConfig &cfg, PSInputFetcher &fetcher);
|
||||
|
||||
enum class GatherChannel : uint8_t
|
||||
{
|
||||
|
||||
@@ -68,6 +68,90 @@ Program::Program(const rdcarray<uint32_t> &words)
|
||||
m_Minor = VersionToken::MinorVersion.Get(cur[0]);
|
||||
}
|
||||
|
||||
void Program::CalculateEvalSampleCache(const DXDebug::PSInputFetcherConfig &cfg,
|
||||
DXDebug::PSInputFetcher &fetcher) const
|
||||
{
|
||||
// scan the instructions to see if it contains any evaluates.
|
||||
for(size_t i = 0; i < GetNumInstructions(); i++)
|
||||
{
|
||||
const Operation &op = 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
|
||||
DXDebug::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]];
|
||||
|
||||
fetcher.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
|
||||
if(!fetcher.evalSampleCacheData.contains(key))
|
||||
fetcher.evalSampleCacheData.push_back(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];
|
||||
|
||||
if(!fetcher.evalSampleCacheData.contains(key))
|
||||
fetcher.evalSampleCacheData.push_back(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 < cfg.outputSampleCount; c++)
|
||||
{
|
||||
key.sample = (int32_t)c;
|
||||
fetcher.evalSampleCacheData.push_back(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];
|
||||
|
||||
if(!fetcher.evalSampleCacheData.contains(key))
|
||||
fetcher.evalSampleCacheData.push_back(key);
|
||||
}
|
||||
else
|
||||
{
|
||||
RDCWARN(
|
||||
"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++)
|
||||
if(!fetcher.evalSampleCacheData.contains(key))
|
||||
fetcher.evalSampleCacheData.push_back(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HandleResourceArrayIndices(const rdcarray<DXBCBytecode::RegIndex> &indices,
|
||||
DXBC::ShaderInputBind &desc)
|
||||
{
|
||||
|
||||
@@ -1117,6 +1117,9 @@ public:
|
||||
void FetchComputeProperties(DXBC::Reflection *reflection);
|
||||
DXBC::Reflection *GuessReflection();
|
||||
|
||||
void CalculateEvalSampleCache(const DXDebug::PSInputFetcherConfig &cfg,
|
||||
DXDebug::PSInputFetcher &fetcher) const;
|
||||
|
||||
const rdcarray<uint32_t> &GetTokens() const { return m_ProgramWords; }
|
||||
rdcstr GetDebugStatus();
|
||||
|
||||
|
||||
@@ -3599,7 +3599,7 @@ void ThreadState::StepNext(ShaderDebugState *state, DebugAPIWrapper *apiWrapper,
|
||||
// opcodes only seem to be supported for regular inputs
|
||||
RDCASSERT(op.operands[1].type == TYPE_INPUT);
|
||||
|
||||
GlobalState::SampleEvalCacheKey key;
|
||||
DXDebug::SampleEvalCacheKey key;
|
||||
|
||||
RDCASSERT(program->GetShaderType() == DXBC::ShaderType::Pixel);
|
||||
|
||||
@@ -4692,174 +4692,6 @@ void AddCBufferToGlobalState(const DXBCBytecode::Program &program, GlobalState &
|
||||
}
|
||||
}
|
||||
|
||||
void ApplyDerivatives(GlobalState &global, rdcarray<ThreadState> &quad, int reg, int element,
|
||||
int numWords, float *data, float signmul, int32_t quadIdxA, int32_t quadIdxB)
|
||||
{
|
||||
for(int w = 0; w < numWords; w++)
|
||||
{
|
||||
quad[quadIdxA].inputs[reg].value.f32v[element + w] += signmul * data[w];
|
||||
if(quadIdxB >= 0)
|
||||
quad[quadIdxB].inputs[reg].value.f32v[element + w] += signmul * data[w];
|
||||
}
|
||||
|
||||
// quick check to see if this register was evaluated
|
||||
if(global.sampleEvalRegisterMask & (1ULL << reg))
|
||||
{
|
||||
// apply derivative to any cached sample evaluations on these quad indices
|
||||
for(auto it = global.sampleEvalCache.begin(); it != global.sampleEvalCache.end(); ++it)
|
||||
{
|
||||
if((it->first.quadIndex == quadIdxA || it->first.quadIndex == quadIdxB) &&
|
||||
reg == it->first.inputRegisterIndex)
|
||||
{
|
||||
for(int w = 0; w < numWords; w++)
|
||||
it->second.value.f32v[element + w] += data[w];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ApplyAllDerivatives(GlobalState &global, rdcarray<ThreadState> &quad, int destIdx,
|
||||
const rdcarray<PSInputElement> &initialValues, float *data)
|
||||
{
|
||||
// We make the assumption that the coarse derivatives are generated from (0,0) in the quad, and
|
||||
// fine derivatives are generated from the destination index and its neighbours in X and Y.
|
||||
// This isn't spec'd but we must assume something and this will hopefully get us closest to
|
||||
// reproducing actual results.
|
||||
//
|
||||
// For debugging, we need members of the quad to be able to generate coarse and fine
|
||||
// derivatives.
|
||||
//
|
||||
// For (0,0) we only need the coarse derivatives to get our neighbours (1,0) and (0,1) which
|
||||
// will give us coarse and fine derivatives being identical.
|
||||
//
|
||||
// For the others we will need to use a combination of coarse and fine derivatives to get the
|
||||
// diagonal element in the quad. In the examples below, remember that the quad indices are:
|
||||
//
|
||||
// +---+---+
|
||||
// | 0 | 1 |
|
||||
// +---+---+
|
||||
// | 2 | 3 |
|
||||
// +---+---+
|
||||
//
|
||||
// And that we have definitions of the derivatives:
|
||||
//
|
||||
// ddx_coarse = (1,0) - (0,0)
|
||||
// ddy_coarse = (0,1) - (0,0)
|
||||
//
|
||||
// i.e. the same for all members of the quad
|
||||
//
|
||||
// ddx_fine = (x,y) - (1-x,y)
|
||||
// ddy_fine = (x,y) - (x,1-y)
|
||||
//
|
||||
// i.e. the difference to the neighbour of our desired invocation (the one we have the actual
|
||||
// inputs for, from gathering above).
|
||||
//
|
||||
// So e.g. if our thread is at (1,1) destIdx = 3
|
||||
//
|
||||
// (1,0) = (1,1) - ddx_fine
|
||||
// (0,1) = (1,1) - ddy_fine
|
||||
// (0,0) = (1,1) - ddy_fine - ddx_coarse
|
||||
//
|
||||
// and ddy_coarse is unused. For (1,0) destIdx = 1:
|
||||
//
|
||||
// (1,1) = (1,0) + ddy_fine
|
||||
// (0,1) = (1,0) - ddx_coarse + ddy_coarse
|
||||
// (0,0) = (1,0) - ddx_coarse
|
||||
//
|
||||
// and ddx_fine is unused (it's identical to ddx_coarse anyway)
|
||||
|
||||
// this is the value of input[1] - input[0]
|
||||
float *ddx_coarse = (float *)data;
|
||||
|
||||
for(size_t i = 0; i < initialValues.size(); i++)
|
||||
{
|
||||
if(!initialValues[i].included)
|
||||
continue;
|
||||
|
||||
if(initialValues[i].reg >= 0)
|
||||
{
|
||||
if(destIdx == 0)
|
||||
ApplyDerivatives(global, quad, initialValues[i].reg, initialValues[i].elem,
|
||||
initialValues[i].numwords, ddx_coarse, 1.0f, 1, 3);
|
||||
else if(destIdx == 1)
|
||||
ApplyDerivatives(global, quad, initialValues[i].reg, initialValues[i].elem,
|
||||
initialValues[i].numwords, ddx_coarse, -1.0f, 0, 2);
|
||||
else if(destIdx == 2)
|
||||
ApplyDerivatives(global, quad, initialValues[i].reg, initialValues[i].elem,
|
||||
initialValues[i].numwords, ddx_coarse, 1.0f, 1, -1);
|
||||
else if(destIdx == 3)
|
||||
ApplyDerivatives(global, quad, initialValues[i].reg, initialValues[i].elem,
|
||||
initialValues[i].numwords, ddx_coarse, -1.0f, 0, -1);
|
||||
}
|
||||
|
||||
ddx_coarse += initialValues[i].numwords;
|
||||
}
|
||||
|
||||
// this is the value of input[2] - input[0]
|
||||
float *ddy_coarse = ddx_coarse;
|
||||
|
||||
for(size_t i = 0; i < initialValues.size(); i++)
|
||||
{
|
||||
if(!initialValues[i].included)
|
||||
continue;
|
||||
|
||||
if(initialValues[i].reg >= 0)
|
||||
{
|
||||
if(destIdx == 0)
|
||||
ApplyDerivatives(global, quad, initialValues[i].reg, initialValues[i].elem,
|
||||
initialValues[i].numwords, ddy_coarse, 1.0f, 2, 3);
|
||||
else if(destIdx == 1)
|
||||
ApplyDerivatives(global, quad, initialValues[i].reg, initialValues[i].elem,
|
||||
initialValues[i].numwords, ddy_coarse, 1.0f, 2, -1);
|
||||
else if(destIdx == 2)
|
||||
ApplyDerivatives(global, quad, initialValues[i].reg, initialValues[i].elem,
|
||||
initialValues[i].numwords, ddy_coarse, -1.0f, 0, 1);
|
||||
}
|
||||
|
||||
ddy_coarse += initialValues[i].numwords;
|
||||
}
|
||||
|
||||
float *ddxfine = ddy_coarse;
|
||||
|
||||
for(size_t i = 0; i < initialValues.size(); i++)
|
||||
{
|
||||
if(!initialValues[i].included)
|
||||
continue;
|
||||
|
||||
if(initialValues[i].reg >= 0)
|
||||
{
|
||||
if(destIdx == 2)
|
||||
ApplyDerivatives(global, quad, initialValues[i].reg, initialValues[i].elem,
|
||||
initialValues[i].numwords, ddxfine, 1.0f, 3, -1);
|
||||
else if(destIdx == 3)
|
||||
ApplyDerivatives(global, quad, initialValues[i].reg, initialValues[i].elem,
|
||||
initialValues[i].numwords, ddxfine, -1.0f, 2, -1);
|
||||
}
|
||||
|
||||
ddxfine += initialValues[i].numwords;
|
||||
}
|
||||
|
||||
float *ddyfine = ddxfine;
|
||||
|
||||
for(size_t i = 0; i < initialValues.size(); i++)
|
||||
{
|
||||
if(!initialValues[i].included)
|
||||
continue;
|
||||
|
||||
if(initialValues[i].reg >= 0)
|
||||
{
|
||||
if(destIdx == 1)
|
||||
ApplyDerivatives(global, quad, initialValues[i].reg, initialValues[i].elem,
|
||||
initialValues[i].numwords, ddyfine, 1.0f, 3, -1);
|
||||
else if(destIdx == 3)
|
||||
ApplyDerivatives(global, quad, initialValues[i].reg, initialValues[i].elem,
|
||||
initialValues[i].numwords, ddyfine, -1.0f, 0, 1);
|
||||
}
|
||||
|
||||
ddyfine += initialValues[i].numwords;
|
||||
}
|
||||
}
|
||||
|
||||
void FillViewFmt(DXGI_FORMAT format, GlobalState::ViewFmt &viewFmt)
|
||||
{
|
||||
if(format != DXGI_FORMAT_UNKNOWN)
|
||||
|
||||
@@ -48,8 +48,6 @@ enum DXGI_FORMAT;
|
||||
|
||||
namespace DXBCDebug
|
||||
{
|
||||
using namespace DXDebug;
|
||||
|
||||
typedef DXDebug::SampleGatherResourceData SampleGatherResourceData;
|
||||
typedef DXDebug::SampleGatherSamplerData SampleGatherSamplerData;
|
||||
typedef DXDebug::BindingSlot BindingSlot;
|
||||
@@ -129,42 +127,9 @@ public:
|
||||
|
||||
rdcarray<groupsharedMem> groupshared;
|
||||
|
||||
struct SampleEvalCacheKey
|
||||
{
|
||||
int32_t quadIndex = -1; // index of this thread in the quad
|
||||
int32_t inputRegisterIndex = -1; // index of the input register
|
||||
int32_t firstComponent = 0; // the first component in the register
|
||||
int32_t numComponents = 0; // how many components in the register
|
||||
int32_t sample = -1; // -1 for offset-from-centroid lookups
|
||||
int32_t offsetx = 0, offsety = 0; // integer offset from centroid
|
||||
|
||||
bool operator<(const SampleEvalCacheKey &o) const
|
||||
{
|
||||
if(quadIndex != o.quadIndex)
|
||||
return quadIndex < o.quadIndex;
|
||||
|
||||
if(inputRegisterIndex != o.inputRegisterIndex)
|
||||
return inputRegisterIndex < o.inputRegisterIndex;
|
||||
|
||||
if(firstComponent != o.firstComponent)
|
||||
return firstComponent < o.firstComponent;
|
||||
|
||||
if(numComponents != o.numComponents)
|
||||
return numComponents < o.numComponents;
|
||||
|
||||
if(sample != o.sample)
|
||||
return sample < o.sample;
|
||||
|
||||
if(offsetx != o.offsetx)
|
||||
return offsetx < o.offsetx;
|
||||
|
||||
return offsety < o.offsety;
|
||||
}
|
||||
};
|
||||
|
||||
// a bitmask of which registers were fetched into the cache, for quick checking
|
||||
uint64_t sampleEvalRegisterMask = 0;
|
||||
std::map<SampleEvalCacheKey, ShaderVariable> sampleEvalCache;
|
||||
std::map<DXDebug::SampleEvalCacheKey, ShaderVariable> sampleEvalCache;
|
||||
|
||||
// copied from the parent trace
|
||||
rdcarray<ShaderVariable> constantBlocks;
|
||||
@@ -301,9 +266,6 @@ uint32_t GetLogicalIdentifierForBindingSlot(const DXBCBytecode::Program &program
|
||||
DXBCBytecode::OperandType declType,
|
||||
const DXBCDebug::BindingSlot &slot);
|
||||
|
||||
void ApplyAllDerivatives(GlobalState &global, rdcarray<ThreadState> &quad, int destIdx,
|
||||
const rdcarray<PSInputElement> &initialValues, float *data);
|
||||
|
||||
void AddCBufferToGlobalState(const DXBCBytecode::Program &program, GlobalState &global,
|
||||
rdcarray<SourceVariableMapping> &sourceVars,
|
||||
const ShaderReflection &refl, const BindingSlot &slot,
|
||||
|
||||
@@ -1435,161 +1435,6 @@ bool ExecutionPoint::IsAfter(const ExecutionPoint &from, const ControlFlow &cont
|
||||
return instruction > from.instruction;
|
||||
return controlFlow.IsForwardConnection(from.block, block);
|
||||
}
|
||||
static void ApplyDerivatives(GlobalState &global, rdcarray<ThreadState> &quad, int input,
|
||||
int numWords, float *data, float signmul, int32_t quadIdxA,
|
||||
int32_t quadIdxB)
|
||||
{
|
||||
for(int w = 0; w < numWords; w++)
|
||||
{
|
||||
quad[quadIdxA].m_Input.members[input].value.f32v[w] += signmul * data[w];
|
||||
if(quadIdxB >= 0)
|
||||
quad[quadIdxB].m_Input.members[input].value.f32v[w] += signmul * data[w];
|
||||
}
|
||||
|
||||
// TODO: SAMPLE EVALUATE
|
||||
#if 0
|
||||
// quick check to see if this register was evaluated
|
||||
if(global.sampleEvalRegisterMask & (1ULL << reg))
|
||||
{
|
||||
// apply derivative to any cached sample evaluations on these quad indices
|
||||
for(auto it = global.sampleEvalCache.begin(); it != global.sampleEvalCache.end(); ++it)
|
||||
{
|
||||
if((it->first.quadIndex == quadIdxA || it->first.quadIndex == quadIdxB) &&
|
||||
reg == it->first.inputRegisterIndex)
|
||||
{
|
||||
for(int w = 0; w < numWords; w++)
|
||||
it->second.value.f32v[w] += data[w];
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void ApplyAllDerivatives(GlobalState &global, rdcarray<ThreadState> &quad, int destIdx,
|
||||
const rdcarray<PSInputData> &psInputs, float *data)
|
||||
{
|
||||
// We make the assumption that the coarse derivatives are generated from (0,0) in the quad, and
|
||||
// fine derivatives are generated from the destination index and its neighbours in X and Y.
|
||||
// This isn't spec'd but we must assume something and this will hopefully get us closest to
|
||||
// reproducing actual results.
|
||||
//
|
||||
// For debugging, we need members of the quad to be able to generate coarse and fine
|
||||
// derivatives.
|
||||
//
|
||||
// For (0,0) we only need the coarse derivatives to get our neighbours (1,0) and (0,1) which
|
||||
// will give us coarse and fine derivatives being identical.
|
||||
//
|
||||
// For the others we will need to use a combination of coarse and fine derivatives to get the
|
||||
// diagonal element in the quad. In the examples below, remember that the quad indices are:
|
||||
//
|
||||
// +---+---+
|
||||
// | 0 | 1 |
|
||||
// +---+---+
|
||||
// | 2 | 3 |
|
||||
// +---+---+
|
||||
//
|
||||
// And that we have definitions of the derivatives:
|
||||
//
|
||||
// ddx_coarse = (1,0) - (0,0)
|
||||
// ddy_coarse = (0,1) - (0,0)
|
||||
//
|
||||
// i.e. the same for all members of the quad
|
||||
//
|
||||
// ddx_fine = (x,y) - (1-x,y)
|
||||
// ddy_fine = (x,y) - (x,1-y)
|
||||
//
|
||||
// i.e. the difference to the neighbour of our desired invocation (the one we have the actual
|
||||
// inputs for, from gathering above).
|
||||
//
|
||||
// So e.g. if our thread is at (1,1) destIdx = 3
|
||||
//
|
||||
// (1,0) = (1,1) - ddx_fine
|
||||
// (0,1) = (1,1) - ddy_fine
|
||||
// (0,0) = (1,1) - ddy_fine - ddx_coarse
|
||||
//
|
||||
// and ddy_coarse is unused. For (1,0) destIdx = 1:
|
||||
//
|
||||
// (1,1) = (1,0) + ddy_fine
|
||||
// (0,1) = (1,0) - ddx_coarse + ddy_coarse
|
||||
// (0,0) = (1,0) - ddx_coarse
|
||||
//
|
||||
// and ddx_fine is unused (it's identical to ddx_coarse anyway)
|
||||
|
||||
// this is the value of input[1] - input[0]
|
||||
float *ddx_coarse = (float *)data;
|
||||
|
||||
for(const PSInputData &psInput : psInputs)
|
||||
{
|
||||
if(!psInput.included)
|
||||
continue;
|
||||
|
||||
const int input = psInput.input;
|
||||
const int numWords = psInput.numwords;
|
||||
if(destIdx == 0)
|
||||
ApplyDerivatives(global, quad, input, numWords, ddx_coarse, 1.0f, 1, 3);
|
||||
else if(destIdx == 1)
|
||||
ApplyDerivatives(global, quad, input, numWords, ddx_coarse, -1.0f, 0, 2);
|
||||
else if(destIdx == 2)
|
||||
ApplyDerivatives(global, quad, input, numWords, ddx_coarse, 1.0f, 1, -1);
|
||||
else if(destIdx == 3)
|
||||
ApplyDerivatives(global, quad, input, numWords, ddx_coarse, -1.0f, 0, -1);
|
||||
|
||||
ddx_coarse += numWords;
|
||||
}
|
||||
|
||||
// this is the value of input[2] - input[0]
|
||||
float *ddy_coarse = ddx_coarse;
|
||||
|
||||
for(const PSInputData &psInput : psInputs)
|
||||
{
|
||||
if(!psInput.included)
|
||||
continue;
|
||||
const int input = psInput.input;
|
||||
const int numWords = psInput.numwords;
|
||||
if(destIdx == 0)
|
||||
ApplyDerivatives(global, quad, input, numWords, ddy_coarse, 1.0f, 2, 3);
|
||||
else if(destIdx == 1)
|
||||
ApplyDerivatives(global, quad, input, numWords, ddy_coarse, 1.0f, 2, -1);
|
||||
else if(destIdx == 2)
|
||||
ApplyDerivatives(global, quad, input, numWords, ddy_coarse, -1.0f, 0, 1);
|
||||
|
||||
ddy_coarse += numWords;
|
||||
}
|
||||
|
||||
float *ddxfine = ddy_coarse;
|
||||
|
||||
for(const PSInputData &psInput : psInputs)
|
||||
{
|
||||
if(!psInput.included)
|
||||
continue;
|
||||
const int input = psInput.input;
|
||||
const int numWords = psInput.numwords;
|
||||
|
||||
if(destIdx == 2)
|
||||
ApplyDerivatives(global, quad, input, numWords, ddxfine, 1.0f, 3, -1);
|
||||
else if(destIdx == 3)
|
||||
ApplyDerivatives(global, quad, input, numWords, ddxfine, -1.0f, 2, -1);
|
||||
|
||||
ddxfine += numWords;
|
||||
}
|
||||
|
||||
float *ddyfine = ddxfine;
|
||||
|
||||
for(const PSInputData &psInput : psInputs)
|
||||
{
|
||||
if(!psInput.included)
|
||||
continue;
|
||||
const int input = psInput.input;
|
||||
const int numWords = psInput.numwords;
|
||||
|
||||
if(destIdx == 1)
|
||||
ApplyDerivatives(global, quad, input, numWords, ddyfine, 1.0f, 3, -1);
|
||||
else if(destIdx == 3)
|
||||
ApplyDerivatives(global, quad, input, numWords, ddyfine, -1.0f, 0, 1);
|
||||
|
||||
ddyfine += numWords;
|
||||
}
|
||||
}
|
||||
|
||||
void ResourceReferenceInfo::Create(const DXIL::ResourceReference *resRef, uint32_t arrayIndex)
|
||||
{
|
||||
|
||||
@@ -85,9 +85,6 @@ struct PSInputData
|
||||
bool included;
|
||||
};
|
||||
|
||||
void ApplyAllDerivatives(GlobalState &global, rdcarray<ThreadState> &quad, int destIdx,
|
||||
const rdcarray<PSInputData> &psInputs, float *data);
|
||||
|
||||
struct FunctionInfo
|
||||
{
|
||||
typedef std::set<Id> ReferencedIds;
|
||||
|
||||
Reference in New Issue
Block a user