diff --git a/renderdoc/driver/d3d11/d3d11_shaderdebug.cpp b/renderdoc/driver/d3d11/d3d11_shaderdebug.cpp index 56fde2e15..fed21023e 100644 --- a/renderdoc/driver/d3d11/d3d11_shaderdebug.cpp +++ b/renderdoc/driver/d3d11/d3d11_shaderdebug.cpp @@ -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 initialValues; - rdcarray floatInputs; - rdcarray inputVarNames; - rdcstr extractHlsl; - int structureStride = 0; + DXDebug::PSInputFetcherConfig cfg; + DXDebug::PSInputFetcher fetcher; - rdcarray interpModes; - const rdcarray &inputSig = dxbc->GetReflection()->InputSig; - DXBCDebug::GetInterpolationModeForInputParams(inputSig, dxbc->GetDXBCByteCode(), interpModes); - - std::map 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 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 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 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 &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 &groupid, const rdcfixedarray &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] + diff --git a/renderdoc/driver/d3d12/d3d12_shaderdebug.cpp b/renderdoc/driver/d3d12/d3d12_shaderdebug.cpp index 0ff2f850e..d5591e0ff 100644 --- a/renderdoc/driver/d3d12/d3d12_shaderdebug.cpp +++ b/renderdoc/driver/d3d12/d3d12_shaderdebug.cpp @@ -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 initialValues; - rdcarray floatInputs; - rdcarray inputVarNames; - rdcstr extractHlsl; - int structureStride = 0; - - rdcarray interpModes; - const rdcarray &inputSig = dxbc->GetReflection()->InputSig; - if(dxbc->GetDXBCByteCode()) - DXBCDebug::GetInterpolationModeForInputParams(inputSig, dxbc->GetDXBCByteCode(), interpModes); - else - DXILDebug::GetInterpolationModeForInputParams(inputSig, dxbc->GetDXILByteCode(), interpModes); - - std::map 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(rs.pipe); - - D3D12_EXPANDED_PIPELINE_STATE_STREAM_DESC pipeDesc; - origPSO->Fill(pipeDesc); - uint32_t outputSampleCount = RDCMAX(1U, pipeDesc.SampleDesc.Count); - - std::set 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(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 PSInitialBuffer : register(u1, space%u);\n\n", regSpace); + D3D12_EXPANDED_PIPELINE_STATE_STREAM_DESC pipeDesc; + m_pDevice->GetResourceManager()->GetCurrentAs(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 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 &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 &ins = activeState.m_Input.members; const rdcarray &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 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 &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 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 &groupid, const rdcfixedarray &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); diff --git a/renderdoc/driver/shaders/dxbc/dx_debug.cpp b/renderdoc/driver/shaders/dxbc/dx_debug.cpp index 4d374d30b..aaca5718e 100644 --- a/renderdoc/driver/shaders/dxbc/dx_debug.cpp +++ b/renderdoc/driver/shaders/dxbc/dx_debug.cpp @@ -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 &stageInputSig, - const rdcarray &prevStageOutputSig, - const rdcarray &interpModes, - rdcarray &initialValues, - rdcarray &floatInputs, rdcarray &inputVarNames, - rdcstr &psInputDefinition, int &structureStride, - std::map &usedInputs) +void GatherPSInputDataForInitialValues(const DXBC::DXBCContainer *dxbc, + const DXBC::DXBCContainer *prevdxbc, PSInputFetcher &fetcher, + rdcarray &floatInputs, rdcarray &inputVarNames) { + rdcarray interpModes; + + const rdcarray &stageInputSig = dxbc->GetReflection()->InputSig; + const rdcarray &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 &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 @@ -74,7 +87,7 @@ void GatherPSInputDataForInitialValues(const rdcarray &stageInputS { const SigParameter &sig = stageInputSig[i]; - psInputDefinition += " "; + fetcher.hlsl += " "; bool included = true; @@ -83,7 +96,7 @@ void GatherPSInputDataForInitialValues(const rdcarray &stageInputS sig.systemValue == ShaderBuiltin::IsFrontFace || sig.systemValue == ShaderBuiltin::MSAASampleIndex) { - psInputDefinition += "//"; + fetcher.hlsl += "//"; included = false; } @@ -92,7 +105,7 @@ void GatherPSInputDataForInitialValues(const rdcarray &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 &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 &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 &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 &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 &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 &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 &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 &stageInputS } } - psInputDefinition += ";\n"; + fetcher.hlsl += ";\n"; int firstElem = sig.regChannelMask & 0x1 ? 0 : sig.regChannelMask & 0x2 ? 1 @@ -323,21 +341,303 @@ void GatherPSInputDataForInitialValues(const rdcarray &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 floatInputs; + rdcarray 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 HitBuffer : register(HITBUFFER); + +// float4 is wasteful in some cases but it's easier than using ByteAddressBuffer and manual +// packing +RWBuffer 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 diff --git a/renderdoc/driver/shaders/dxbc/dx_debug.h b/renderdoc/driver/shaders/dxbc/dx_debug.h index 1eac08aba..90a7d5b3c 100644 --- a/renderdoc/driver/shaders/dxbc/dx_debug.h +++ b/renderdoc/driver/shaders/dxbc/dx_debug.h @@ -25,11 +25,13 @@ #pragma once #include +#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 &stageInputSig, - const rdcarray &prevStageOutputSig, - const rdcarray &interpModes, - rdcarray &initialValues, - rdcarray &floatInputs, rdcarray &inputVarNames, - rdcstr &psInputDefinition, int &structureStride, - std::map &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 inputs; + + // per-sample evaluation cache + rdcarray 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 { diff --git a/renderdoc/driver/shaders/dxbc/dxbc_bytecode.cpp b/renderdoc/driver/shaders/dxbc/dxbc_bytecode.cpp index 0c62f4e05..9df8a2f56 100644 --- a/renderdoc/driver/shaders/dxbc/dxbc_bytecode.cpp +++ b/renderdoc/driver/shaders/dxbc/dxbc_bytecode.cpp @@ -68,6 +68,90 @@ Program::Program(const rdcarray &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 &indices, DXBC::ShaderInputBind &desc) { diff --git a/renderdoc/driver/shaders/dxbc/dxbc_bytecode.h b/renderdoc/driver/shaders/dxbc/dxbc_bytecode.h index 78aa6c9b7..03aa4b024 100644 --- a/renderdoc/driver/shaders/dxbc/dxbc_bytecode.h +++ b/renderdoc/driver/shaders/dxbc/dxbc_bytecode.h @@ -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 &GetTokens() const { return m_ProgramWords; } rdcstr GetDebugStatus(); diff --git a/renderdoc/driver/shaders/dxbc/dxbc_debug.cpp b/renderdoc/driver/shaders/dxbc/dxbc_debug.cpp index dbcf14c69..7ad8b6354 100644 --- a/renderdoc/driver/shaders/dxbc/dxbc_debug.cpp +++ b/renderdoc/driver/shaders/dxbc/dxbc_debug.cpp @@ -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 &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 &quad, int destIdx, - const rdcarray &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) diff --git a/renderdoc/driver/shaders/dxbc/dxbc_debug.h b/renderdoc/driver/shaders/dxbc/dxbc_debug.h index 1947f2ffa..50a9b11ad 100644 --- a/renderdoc/driver/shaders/dxbc/dxbc_debug.h +++ b/renderdoc/driver/shaders/dxbc/dxbc_debug.h @@ -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 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 sampleEvalCache; + std::map sampleEvalCache; // copied from the parent trace rdcarray constantBlocks; @@ -301,9 +266,6 @@ uint32_t GetLogicalIdentifierForBindingSlot(const DXBCBytecode::Program &program DXBCBytecode::OperandType declType, const DXBCDebug::BindingSlot &slot); -void ApplyAllDerivatives(GlobalState &global, rdcarray &quad, int destIdx, - const rdcarray &initialValues, float *data); - void AddCBufferToGlobalState(const DXBCBytecode::Program &program, GlobalState &global, rdcarray &sourceVars, const ShaderReflection &refl, const BindingSlot &slot, diff --git a/renderdoc/driver/shaders/dxil/dxil_debug.cpp b/renderdoc/driver/shaders/dxil/dxil_debug.cpp index f7af77118..67316246b 100644 --- a/renderdoc/driver/shaders/dxil/dxil_debug.cpp +++ b/renderdoc/driver/shaders/dxil/dxil_debug.cpp @@ -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 &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 &quad, int destIdx, - const rdcarray &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) { diff --git a/renderdoc/driver/shaders/dxil/dxil_debug.h b/renderdoc/driver/shaders/dxil/dxil_debug.h index 0afabd3d1..25484e8af 100644 --- a/renderdoc/driver/shaders/dxil/dxil_debug.h +++ b/renderdoc/driver/shaders/dxil/dxil_debug.h @@ -85,9 +85,6 @@ struct PSInputData bool included; }; -void ApplyAllDerivatives(GlobalState &global, rdcarray &quad, int destIdx, - const rdcarray &psInputs, float *data); - struct FunctionInfo { typedef std::set ReferencedIds; diff --git a/util/test/tests/D3D11/D3D11_Shader_Debug_Zoo.py b/util/test/tests/D3D11/D3D11_Shader_Debug_Zoo.py index 2a62da4ed..5b33390bd 100644 --- a/util/test/tests/D3D11/D3D11_Shader_Debug_Zoo.py +++ b/util/test/tests/D3D11/D3D11_Shader_Debug_Zoo.py @@ -83,31 +83,32 @@ class D3D11_Shader_Debug_Zoo(rdtest.TestCase): action = self.find_action("MSAA Test").next self.controller.SetFrameEvent(action.eventId, False) pipe: rd.PipeState = self.controller.GetPipelineState() - for test in range(4): - # Debug the shader - inputs = rd.DebugPixelInputs() - inputs.sample = test - trace: rd.ShaderDebugTrace = self.controller.DebugPixel(4, 4, inputs) + for (x,y) in [(4, 4), (4, 5), (3, 4), (3, 5)]: + for test in range(4): + # Debug the shader + inputs = rd.DebugPixelInputs() + inputs.sample = test + trace: rd.ShaderDebugTrace = self.controller.DebugPixel(x, y, inputs) - # Validate that the correct sample index was debugged - sampRegister = self.find_input_source_var(trace, rd.ShaderBuiltin.MSAASampleIndex) - sampInput = [var for var in trace.inputs if var.name == sampRegister.variables[0].name][0] - if sampInput.value.u32v[0] != test: - rdtest.log.error("Test {} did not pick the correct sample.".format(test)) + # Validate that the correct sample index was debugged + sampRegister = self.find_input_source_var(trace, rd.ShaderBuiltin.MSAASampleIndex) + sampInput = [var for var in trace.inputs if var.name == sampRegister.variables[0].name][0] + if sampInput.value.u32v[0] != test: + rdtest.log.error("Test {} did not pick the correct sample.".format(test)) - cycles, variables = self.process_trace(trace) + cycles, variables = self.process_trace(trace) - output = self.find_output_source_var(trace, rd.ShaderBuiltin.ColorOutput, 0) + output = self.find_output_source_var(trace, rd.ShaderBuiltin.ColorOutput, 0) - debugged = self.evaluate_source_var(output, variables) + debugged = self.evaluate_source_var(output, variables) - # Validate the debug output result - try: - self.check_pixel_value(pipe.GetOutputTargets()[0].resource, 4, 4, debugged.value.f32v[0:4], sub=rd.Subresource(0, 0, test)) - except rdtest.TestFailureException as ex: - failed = True - rdtest.log.error("Test {} did not match. {}".format(test, str(ex))) - continue + # Validate the debug output result + try: + self.check_pixel_value(pipe.GetOutputTargets()[0].resource, x, y, debugged.value.f32v[0:4], sub=rd.Subresource(0, 0, test)) + except rdtest.TestFailureException as ex: + failed = True + rdtest.log.error("Test {} did not match. {}".format(test, str(ex))) + continue rdtest.log.end_section("MSAA tests") diff --git a/util/test/tests/D3D12/D3D12_Shader_Debug_Zoo.py b/util/test/tests/D3D12/D3D12_Shader_Debug_Zoo.py index 445f22f9f..abdbe0d4a 100644 --- a/util/test/tests/D3D12/D3D12_Shader_Debug_Zoo.py +++ b/util/test/tests/D3D12/D3D12_Shader_Debug_Zoo.py @@ -87,32 +87,33 @@ class D3D12_Shader_Debug_Zoo(rdtest.TestCase): action = test_marker.next self.controller.SetFrameEvent(action.eventId, False) pipe: rd.PipeState = self.controller.GetPipelineState() - for test in range(4): - # Debug the shader - inputs = rd.DebugPixelInputs() - inputs.sample = test - trace: rd.ShaderDebugTrace = self.controller.DebugPixel(4, 4, inputs) + for (x,y) in [(4, 4), (4, 5), (3, 4), (3, 5)]: + for test in range(4): + # Debug the shader + inputs = rd.DebugPixelInputs() + inputs.sample = test + trace: rd.ShaderDebugTrace = self.controller.DebugPixel(x, y, inputs) - # Validate that the correct sample index was debugged - sampRegister = self.find_input_source_var(trace, rd.ShaderBuiltin.MSAASampleIndex) - sampInput = [var for var in trace.inputs if var.name == sampRegister.variables[0].name][0] - if sampInput.value.u32v[0] != test: - rdtest.log.error("Test {} did not pick the correct sample.".format(test)) + # Validate that the correct sample index was debugged + sampRegister = self.find_input_source_var(trace, rd.ShaderBuiltin.MSAASampleIndex) + sampInput = [var for var in trace.inputs if var.name == sampRegister.variables[0].name][0] + if sampInput.value.u32v[0] != test: + rdtest.log.error("Test {} did not pick the correct sample.".format(test)) - cycles, variables = self.process_trace(trace) + cycles, variables = self.process_trace(trace) - output = self.find_output_source_var(trace, rd.ShaderBuiltin.ColorOutput, 0) + output = self.find_output_source_var(trace, rd.ShaderBuiltin.ColorOutput, 0) - debugged = self.evaluate_source_var(output, variables) - self.controller.FreeTrace(trace) + debugged = self.evaluate_source_var(output, variables) + self.controller.FreeTrace(trace) - # Validate the debug output result - try: - self.check_pixel_value(pipe.GetOutputTargets()[0].resource, 4, 4, debugged.value.f32v[0:4], sub=rd.Subresource(0, 0, test)) - except rdtest.TestFailureException as ex: - failed = True - rdtest.log.error("Test {} did not match. {}".format(test, str(ex))) - continue + # Validate the debug output result + try: + self.check_pixel_value(pipe.GetOutputTargets()[0].resource, x, y, debugged.value.f32v[0:4], sub=rd.Subresource(0, 0, test)) + except rdtest.TestFailureException as ex: + failed = True + rdtest.log.error("Test {} did not match. {}".format(test, str(ex))) + continue rdtest.log.end_section("MSAA tests")