diff --git a/renderdoc/driver/d3d11/d3d11_shaderdebug.cpp b/renderdoc/driver/d3d11/d3d11_shaderdebug.cpp index 0d7725952..3684b2f4a 100644 --- a/renderdoc/driver/d3d11/d3d11_shaderdebug.cpp +++ b/renderdoc/driver/d3d11/d3d11_shaderdebug.cpp @@ -24,6 +24,7 @@ ******************************************************************************/ #include "data/resource.h" +#include "driver/shaders/dxbc/dx_debug.h" #include "driver/shaders/dxbc/dxbc_bytecode.h" #include "driver/shaders/dxbc/dxbc_debug.h" #include "maths/formatpacking.h" @@ -1878,9 +1879,13 @@ ShaderDebugTrace *D3D11Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t rdcstr extractHlsl; int structureStride = 0; - DXBCDebug::GatherPSInputDataForInitialValues(dxbc, *prevdxbc->GetReflection(), initialValues, - floatInputs, inputVarNames, extractHlsl, - structureStride); + rdcarray interpModes; + const rdcarray &inputSig = dxbc->GetReflection()->InputSig; + DXBCDebug::GetInterpolationModeForInputParams(inputSig, dxbc->GetDXBCByteCode(), interpModes); + + DXDebug::GatherPSInputDataForInitialValues(inputSig, prevdxbc->GetReflection()->OutputSig, + interpModes, initialValues, floatInputs, inputVarNames, + extractHlsl, structureStride); uint32_t overdrawLevels = 100; // maximum number of overdraw levels diff --git a/renderdoc/driver/d3d12/d3d12_shaderdebug.cpp b/renderdoc/driver/d3d12/d3d12_shaderdebug.cpp index 4d8bccd55..5a20305c7 100644 --- a/renderdoc/driver/d3d12/d3d12_shaderdebug.cpp +++ b/renderdoc/driver/d3d12/d3d12_shaderdebug.cpp @@ -2026,9 +2026,13 @@ ShaderDebugTrace *D3D12Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t rdcstr extractHlsl; int structureStride = 0; - DXBCDebug::GatherPSInputDataForInitialValues(dxbc, *prevDxbc->GetReflection(), initialValues, - floatInputs, inputVarNames, extractHlsl, - structureStride); + rdcarray interpModes; + const rdcarray &inputSig = dxbc->GetReflection()->InputSig; + DXBCDebug::GetInterpolationModeForInputParams(inputSig, dxbc->GetDXBCByteCode(), interpModes); + + DXDebug::GatherPSInputDataForInitialValues(inputSig, prevDxbc->GetReflection()->OutputSig, + interpModes, initialValues, floatInputs, + inputVarNames, extractHlsl, structureStride); uint32_t overdrawLevels = 100; // maximum number of overdraw levels diff --git a/renderdoc/driver/shaders/dxbc/dx_debug.cpp b/renderdoc/driver/shaders/dxbc/dx_debug.cpp index 2a7c34c58..d74ffe2ea 100644 --- a/renderdoc/driver/shaders/dxbc/dx_debug.cpp +++ b/renderdoc/driver/shaders/dxbc/dx_debug.cpp @@ -23,9 +23,312 @@ ******************************************************************************/ #include "dx_debug.h" +#include "common/formatting.h" +#include "dxbc_common.h" namespace DXDebug { +void GatherPSInputDataForInitialValues(const rdcarray &stageInputSig, + const rdcarray &prevStageOutputSig, + const rdcarray &interpModes, + rdcarray &initialValues, + rdcarray &floatInputs, rdcarray &inputVarNames, + rdcstr &psInputDefinition, int &structureStride) +{ + // 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 + // associated data, the HLSL definition to use when gathering pixel shader initial values, + // and the stride of that HLSL structure. + + // This function does not provide any HLSL definitions for additional metadata that may be + // needed for gathering initial values, such as primitive ID, and also does not provide the + // shader function body. + + initialValues.clear(); + floatInputs.clear(); + inputVarNames.clear(); + psInputDefinition = "struct PSInput\n{\n"; + structureStride = 0; + + if(stageInputSig.empty()) + { + psInputDefinition += "float4 input_dummy : SV_Position;\n"; + + initialValues.push_back(PSInputElement(-1, 0, 4, ShaderBuiltin::Undefined, true)); + + structureStride += 4; + } + + // name, pair + rdcarray>> arrays; + + uint32_t nextreg = 0; + + size_t numInputs = stageInputSig.size(); + inputVarNames.resize(numInputs); + + for(size_t i = 0; i < numInputs; i++) + { + const SigParameter &sig = stageInputSig[i]; + + psInputDefinition += " "; + + bool included = true; + + // handled specially to account for SV_ ordering + if(sig.systemValue == ShaderBuiltin::MSAACoverage || + sig.systemValue == ShaderBuiltin::IsFrontFace || + sig.systemValue == ShaderBuiltin::MSAASampleIndex) + { + psInputDefinition += "//"; + included = false; + } + + // it seems sometimes primitive ID can be included within inputs and isn't subject to the SV_ + // ordering restrictions - possibly to allow for geometry shaders to output the primitive ID as + // an interpolant. Only comment it out if it's the last input. + if(i + 1 == numInputs && sig.systemValue == ShaderBuiltin::PrimitiveIndex) + { + psInputDefinition += "//"; + included = false; + } + + int arrayIndex = -1; + + for(size_t a = 0; a < arrays.size(); a++) + { + if(sig.semanticName == arrays[a].first && arrays[a].second.first <= sig.semanticIndex && + arrays[a].second.second >= sig.semanticIndex) + { + psInputDefinition += "//"; + included = false; + arrayIndex = sig.semanticIndex - arrays[a].second.first; + } + } + + int missingreg = int(sig.regIndex) - int(nextreg); + + // fill in holes from output sig of previous shader if possible, to try and + // ensure the same register order + for(int dummy = 0; dummy < missingreg; dummy++) + { + bool filled = false; + + size_t numPrevOutputs = prevStageOutputSig.size(); + for(size_t os = 0; os < numPrevOutputs; os++) + { + if(prevStageOutputSig[os].regIndex == nextreg + dummy) + { + filled = true; + + if(prevStageOutputSig[os].varType == VarType::Float) + psInputDefinition += "float"; + else if(prevStageOutputSig[os].varType == VarType::SInt) + psInputDefinition += "int"; + else if(prevStageOutputSig[os].varType == VarType::UInt) + psInputDefinition += "uint"; + else + RDCERR("Unexpected input signature type: %s", + ToStr(prevStageOutputSig[os].varType).c_str()); + + int numCols = (prevStageOutputSig[os].regChannelMask & 0x1 ? 1 : 0) + + (prevStageOutputSig[os].regChannelMask & 0x2 ? 1 : 0) + + (prevStageOutputSig[os].regChannelMask & 0x4 ? 1 : 0) + + (prevStageOutputSig[os].regChannelMask & 0x8 ? 1 : 0); + + structureStride += 4 * numCols; + + initialValues.push_back(PSInputElement(-1, 0, numCols, ShaderBuiltin::Undefined, true)); + + rdcstr name = prevStageOutputSig[os].semanticIdxName; + + psInputDefinition += ToStr((uint32_t)numCols) + " input_" + name + " : " + name + ";\n"; + } + } + + if(!filled) + { + rdcstr dummy_reg = "dummy_register"; + dummy_reg += ToStr((uint32_t)nextreg + dummy); + psInputDefinition += "float4 var_" + dummy_reg + " : semantic_" + dummy_reg + ";\n"; + + initialValues.push_back(PSInputElement(-1, 0, 4, ShaderBuiltin::Undefined, true)); + + structureStride += 4 * sizeof(float); + } + } + + nextreg = sig.regIndex + 1; + + DXBC::InterpolationMode interpolation = interpModes[i]; + if(interpolation != DXBC::InterpolationMode::INTERPOLATION_UNDEFINED) + psInputDefinition += ToStr(interpolation) + " "; + psInputDefinition += 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); + + rdcstr name = sig.semanticIdxName; + + // arrays of interpolators are handled really weirdly. They use cbuffer + // packing rules where each new value is in a new register (rather than + // e.g. 2 x float2 in a single register), but that's pointless because + // you can't dynamically index into input registers. + // If we declare those elements as a non-array, the float2s or floats + // will be packed into registers and won't match up to the previous + // shader. + // HOWEVER to add an extra bit of fun, fxc will happily pack other + // parameters not in the array into spare parts of the registers. + // + // So I think the upshot is that we can detect arrays reliably by + // whenever we encounter a float or float2 at the start of a register, + // search forward to see if the next register has an element that is the + // same semantic name and one higher semantic index. If so, there's an + // array, so keep searching to enumerate its length. + // I think this should be safe if the packing just happens to place those + // registers together. + + int arrayLength = 0; + + if(included && numCols <= 2 && (sig.regChannelMask & 0x1)) + { + uint32_t nextIdx = sig.semanticIndex + 1; + + for(size_t j = i + 1; j < numInputs; j++) + { + const SigParameter &jSig = stageInputSig[j]; + + // if we've found the 'next' semantic + if(sig.semanticName == jSig.semanticName && nextIdx == jSig.semanticIndex) + { + int jNumCols = (jSig.regChannelMask & 0x1 ? 1 : 0) + (jSig.regChannelMask & 0x2 ? 1 : 0) + + (jSig.regChannelMask & 0x4 ? 1 : 0) + (jSig.regChannelMask & 0x8 ? 1 : 0); + + DXBC::InterpolationMode jInterp = interpModes[j]; + + // if it's the same size, type, and interpolation mode, then it could potentially be + // packed into an array. Check if it's using the first channel component to tell whether + // it's tightly packed with another semantic. + if(jNumCols == numCols && interpolation == jInterp && sig.varType == jSig.varType && + jSig.regChannelMask & 0x1) + { + if(arrayLength == 0) + arrayLength = 2; + else + arrayLength++; + + // continue searching now + nextIdx++; + j = i + 1; + continue; + } + } + } + + if(arrayLength > 0) + arrays.push_back( + make_rdcpair(sig.semanticName, make_rdcpair((uint32_t)sig.semanticIndex, nextIdx - 1))); + } + + if(included) + { + // in UAV structs, arrays are packed tightly, so just multiply by arrayLength + structureStride += 4 * numCols * RDCMAX(1, arrayLength); + } + + // as another side effect of the above, an element declared as a 1-length array won't be + // detected but it WILL be put in its own register (not packed together), so detect this + // case too. + // Note we have to search *backwards* because we need to know if this register should have + // been packed into the previous register, but wasn't. float/float2/float3 can be packed after + // an array just fine, so long as the sum of their components doesn't exceed a register width + if(included && i > 0 && arrayLength == 0) + { + const SigParameter &prev = stageInputSig[i - 1]; + + if(prev.regIndex != sig.regIndex && prev.compCount + sig.compCount <= 4) + arrayLength = 1; + } + + // The compiler is also really annoying and will go to great lengths to rearrange elements + // and screw up our declaration, to pack things together. E.g.: + // float2 a : TEXCOORD1; + // float4 b : TEXCOORD2; + // float4 c : TEXCOORD3; + // float2 d : TEXCOORD4; + // the compiler will move d up and pack it into the last two components of a. + // To prevent this, we look forward and backward to check that we aren't expecting to pack + // with anything, and if not then we just make it a 1-length array to ensure no packing. + // Note the regChannelMask & 0x1 means it is using .x, so it's not the tail-end of a pack + if(included && arrayLength == 0 && numCols <= 2 && (sig.regChannelMask & 0x1)) + { + if(i == numInputs - 1) + { + // the last element is never packed + arrayLength = 1; + } + else + { + // if the next reg is using .x, it wasn't packed with us + if(stageInputSig[i + 1].regChannelMask & 0x1) + arrayLength = 1; + } + } + + psInputDefinition += ToStr((uint32_t)numCols) + " input_" + name; + if(arrayLength > 0) + psInputDefinition += "[" + ToStr(arrayLength) + "]"; + psInputDefinition += " : " + name; + + inputVarNames[i] = "input_" + name; + if(arrayLength > 0) + inputVarNames[i] += StringFormat::Fmt("[%d]", RDCMAX(0, arrayIndex)); + + if(included && sig.varType == VarType::Float) + { + if(arrayLength == 0) + { + floatInputs.push_back("input_" + name); + } + else + { + for(int a = 0; a < arrayLength; a++) + floatInputs.push_back("input_" + name + "[" + ToStr(a) + "]"); + } + } + + psInputDefinition += ";\n"; + + int firstElem = sig.regChannelMask & 0x1 ? 0 + : sig.regChannelMask & 0x2 ? 1 + : sig.regChannelMask & 0x4 ? 2 + : sig.regChannelMask & 0x8 ? 3 + : -1; + + // arrays get added all at once (because in the struct data, they are contiguous even if + // in the input signature they're not). + if(arrayIndex < 0) + { + if(arrayLength == 0) + { + initialValues.push_back( + PSInputElement(sig.regIndex, firstElem, numCols, sig.systemValue, included)); + } + else + { + for(int a = 0; a < arrayLength; a++) + { + initialValues.push_back( + PSInputElement(sig.regIndex + a, firstElem, numCols, sig.systemValue, included)); + } + } + } + } + + psInputDefinition += "};\n\n"; +} + // "NaN has special handling. If one source operand is NaN, then the other source operand is // returned. If both are NaN, any NaN representation is returned." diff --git a/renderdoc/driver/shaders/dxbc/dx_debug.h b/renderdoc/driver/shaders/dxbc/dx_debug.h index 9bdd72d11..3c4c1c7ef 100644 --- a/renderdoc/driver/shaders/dxbc/dx_debug.h +++ b/renderdoc/driver/shaders/dxbc/dx_debug.h @@ -27,6 +27,7 @@ namespace DXBC { enum ResourceRetType; +enum class InterpolationMode : uint8_t; }; namespace DXBCBytecode @@ -41,6 +42,33 @@ typedef DXBC::ResourceRetType ResourceRetType; typedef DXBCBytecode::ResourceDimension ResourceDimension; typedef DXBCBytecode::SamplerMode SamplerMode; +struct PSInputElement +{ + PSInputElement(int regster, int element, int numWords, ShaderBuiltin attr, bool inc) + { + reg = regster; + elem = element; + numwords = numWords; + sysattribute = attr; + included = inc; + } + + int reg; + int elem; + ShaderBuiltin sysattribute; + + int numwords; + + bool included; +}; + +void GatherPSInputDataForInitialValues(const rdcarray &stageInputSig, + const rdcarray &prevStageOutputSig, + const rdcarray &interpModes, + rdcarray &initialValues, + rdcarray &floatInputs, rdcarray &inputVarNames, + rdcstr &psInputDefinition, int &structureStride); + enum class GatherChannel : uint8_t { Red = 0, diff --git a/renderdoc/driver/shaders/dxbc/dxbc_debug.cpp b/renderdoc/driver/shaders/dxbc/dxbc_debug.cpp index b959ae465..87142098d 100644 --- a/renderdoc/driver/shaders/dxbc/dxbc_debug.cpp +++ b/renderdoc/driver/shaders/dxbc/dxbc_debug.cpp @@ -4731,7 +4731,7 @@ void LookupSRVFormatFromShaderReflection(const DXBC::Reflection &reflection, } DXBC::InterpolationMode GetInterpolationModeForInputParam(const SigParameter &sig, - const DXBC::Reflection &psDxbc, + const rdcarray &stageInputSig, const DXBCBytecode::Program *program) { if(sig.varType == VarType::SInt || sig.varType == VarType::UInt) @@ -4740,10 +4740,10 @@ DXBC::InterpolationMode GetInterpolationModeForInputParam(const SigParameter &si if(sig.varType == VarType::Float) { // if we're packed with ints on either side, we must be nointerpolation - size_t numInputs = psDxbc.InputSig.size(); + size_t numInputs = stageInputSig.size(); for(size_t j = 0; j < numInputs; j++) { - if(sig.regIndex == psDxbc.InputSig[j].regIndex && psDxbc.InputSig[j].varType != VarType::Float) + if(sig.regIndex == stageInputSig[j].regIndex && stageInputSig[j].varType != VarType::Float) return DXBC::InterpolationMode::INTERPOLATION_CONSTANT; } @@ -4771,307 +4771,17 @@ DXBC::InterpolationMode GetInterpolationModeForInputParam(const SigParameter &si return DXBC::InterpolationMode::INTERPOLATION_UNDEFINED; } -void GatherPSInputDataForInitialValues(const DXBC::DXBCContainer *dxbc, - const DXBC::Reflection &prevStageDxbc, - rdcarray &initialValues, - rdcarray &floatInputs, rdcarray &inputVarNames, - rdcstr &psInputDefinition, int &structureStride) +void GetInterpolationModeForInputParams(const rdcarray &inputSig, + const DXBCBytecode::Program *program, + rdcarray &interpModes) { - const DXBC::Reflection &psDxbc = *dxbc->GetReflection(); - const DXBCBytecode::Program *program = dxbc->GetDXBCByteCode(); - - // 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 - // associated data, the HLSL definition to use when gathering pixel shader initial values, - // and the stride of that HLSL structure. - - // This function does not provide any HLSL definitions for additional metadata that may be - // needed for gathering initial values, such as primitive ID, and also does not provide the - // shader function body. - - initialValues.clear(); - floatInputs.clear(); - inputVarNames.clear(); - psInputDefinition = "struct PSInput\n{\n"; - structureStride = 0; - - if(psDxbc.InputSig.empty()) - { - psInputDefinition += "float4 input_dummy : SV_Position;\n"; - - initialValues.push_back(PSInputElement(-1, 0, 4, ShaderBuiltin::Undefined, true)); - - structureStride += 4; - } - - // name, pair - rdcarray>> arrays; - - uint32_t nextreg = 0; - - size_t numInputs = psDxbc.InputSig.size(); - inputVarNames.resize(numInputs); - + size_t numInputs = inputSig.size(); + interpModes.resize(numInputs); for(size_t i = 0; i < numInputs; i++) { - const SigParameter &sig = psDxbc.InputSig[i]; - - psInputDefinition += " "; - - bool included = true; - - // handled specially to account for SV_ ordering - if(sig.systemValue == ShaderBuiltin::MSAACoverage || - sig.systemValue == ShaderBuiltin::IsFrontFace || - sig.systemValue == ShaderBuiltin::MSAASampleIndex) - { - psInputDefinition += "//"; - included = false; - } - - // it seems sometimes primitive ID can be included within inputs and isn't subject to the SV_ - // ordering restrictions - possibly to allow for geometry shaders to output the primitive ID as - // an interpolant. Only comment it out if it's the last input. - if(i + 1 == numInputs && sig.systemValue == ShaderBuiltin::PrimitiveIndex) - { - psInputDefinition += "//"; - included = false; - } - - int arrayIndex = -1; - - for(size_t a = 0; a < arrays.size(); a++) - { - if(sig.semanticName == arrays[a].first && arrays[a].second.first <= sig.semanticIndex && - arrays[a].second.second >= sig.semanticIndex) - { - psInputDefinition += "//"; - included = false; - arrayIndex = sig.semanticIndex - arrays[a].second.first; - } - } - - int missingreg = int(sig.regIndex) - int(nextreg); - - // fill in holes from output sig of previous shader if possible, to try and - // ensure the same register order - for(int dummy = 0; dummy < missingreg; dummy++) - { - bool filled = false; - - size_t numPrevOutputs = prevStageDxbc.OutputSig.size(); - for(size_t os = 0; os < numPrevOutputs; os++) - { - if(prevStageDxbc.OutputSig[os].regIndex == nextreg + dummy) - { - filled = true; - - if(prevStageDxbc.OutputSig[os].varType == VarType::Float) - psInputDefinition += "float"; - else if(prevStageDxbc.OutputSig[os].varType == VarType::SInt) - psInputDefinition += "int"; - else if(prevStageDxbc.OutputSig[os].varType == VarType::UInt) - psInputDefinition += "uint"; - else - RDCERR("Unexpected input signature type: %s", - ToStr(prevStageDxbc.OutputSig[os].varType).c_str()); - - int numCols = (prevStageDxbc.OutputSig[os].regChannelMask & 0x1 ? 1 : 0) + - (prevStageDxbc.OutputSig[os].regChannelMask & 0x2 ? 1 : 0) + - (prevStageDxbc.OutputSig[os].regChannelMask & 0x4 ? 1 : 0) + - (prevStageDxbc.OutputSig[os].regChannelMask & 0x8 ? 1 : 0); - - structureStride += 4 * numCols; - - initialValues.push_back(PSInputElement(-1, 0, numCols, ShaderBuiltin::Undefined, true)); - - rdcstr name = prevStageDxbc.OutputSig[os].semanticIdxName; - - psInputDefinition += ToStr((uint32_t)numCols) + " input_" + name + " : " + name + ";\n"; - } - } - - if(!filled) - { - rdcstr dummy_reg = "dummy_register"; - dummy_reg += ToStr((uint32_t)nextreg + dummy); - psInputDefinition += "float4 var_" + dummy_reg + " : semantic_" + dummy_reg + ";\n"; - - initialValues.push_back(PSInputElement(-1, 0, 4, ShaderBuiltin::Undefined, true)); - - structureStride += 4 * sizeof(float); - } - } - - nextreg = sig.regIndex + 1; - - DXBC::InterpolationMode interpolation = GetInterpolationModeForInputParam(sig, psDxbc, program); - if(interpolation != DXBC::InterpolationMode::INTERPOLATION_UNDEFINED) - psInputDefinition += ToStr(interpolation) + " "; - psInputDefinition += 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); - - rdcstr name = sig.semanticIdxName; - - // arrays of interpolators are handled really weirdly. They use cbuffer - // packing rules where each new value is in a new register (rather than - // e.g. 2 x float2 in a single register), but that's pointless because - // you can't dynamically index into input registers. - // If we declare those elements as a non-array, the float2s or floats - // will be packed into registers and won't match up to the previous - // shader. - // HOWEVER to add an extra bit of fun, fxc will happily pack other - // parameters not in the array into spare parts of the registers. - // - // So I think the upshot is that we can detect arrays reliably by - // whenever we encounter a float or float2 at the start of a register, - // search forward to see if the next register has an element that is the - // same semantic name and one higher semantic index. If so, there's an - // array, so keep searching to enumerate its length. - // I think this should be safe if the packing just happens to place those - // registers together. - - int arrayLength = 0; - - if(included && numCols <= 2 && (sig.regChannelMask & 0x1)) - { - uint32_t nextIdx = sig.semanticIndex + 1; - - for(size_t j = i + 1; j < numInputs; j++) - { - const SigParameter &jSig = psDxbc.InputSig[j]; - - // if we've found the 'next' semantic - if(sig.semanticName == jSig.semanticName && nextIdx == jSig.semanticIndex) - { - int jNumCols = (jSig.regChannelMask & 0x1 ? 1 : 0) + (jSig.regChannelMask & 0x2 ? 1 : 0) + - (jSig.regChannelMask & 0x4 ? 1 : 0) + (jSig.regChannelMask & 0x8 ? 1 : 0); - - DXBC::InterpolationMode jInterp = GetInterpolationModeForInputParam(jSig, psDxbc, program); - - // if it's the same size, type, and interpolation mode, then it could potentially be - // packed into an array. Check if it's using the first channel component to tell whether - // it's tightly packed with another semantic. - if(jNumCols == numCols && interpolation == jInterp && sig.varType == jSig.varType && - jSig.regChannelMask & 0x1) - { - if(arrayLength == 0) - arrayLength = 2; - else - arrayLength++; - - // continue searching now - nextIdx++; - j = i + 1; - continue; - } - } - } - - if(arrayLength > 0) - arrays.push_back( - make_rdcpair(sig.semanticName, make_rdcpair((uint32_t)sig.semanticIndex, nextIdx - 1))); - } - - if(included) - { - // in UAV structs, arrays are packed tightly, so just multiply by arrayLength - structureStride += 4 * numCols * RDCMAX(1, arrayLength); - } - - // as another side effect of the above, an element declared as a 1-length array won't be - // detected but it WILL be put in its own register (not packed together), so detect this - // case too. - // Note we have to search *backwards* because we need to know if this register should have - // been packed into the previous register, but wasn't. float/float2/float3 can be packed after - // an array just fine, so long as the sum of their components doesn't exceed a register width - if(included && i > 0 && arrayLength == 0) - { - const SigParameter &prev = psDxbc.InputSig[i - 1]; - - if(prev.regIndex != sig.regIndex && prev.compCount + sig.compCount <= 4) - arrayLength = 1; - } - - // The compiler is also really annoying and will go to great lengths to rearrange elements - // and screw up our declaration, to pack things together. E.g.: - // float2 a : TEXCOORD1; - // float4 b : TEXCOORD2; - // float4 c : TEXCOORD3; - // float2 d : TEXCOORD4; - // the compiler will move d up and pack it into the last two components of a. - // To prevent this, we look forward and backward to check that we aren't expecting to pack - // with anything, and if not then we just make it a 1-length array to ensure no packing. - // Note the regChannelMask & 0x1 means it is using .x, so it's not the tail-end of a pack - if(included && arrayLength == 0 && numCols <= 2 && (sig.regChannelMask & 0x1)) - { - if(i == numInputs - 1) - { - // the last element is never packed - arrayLength = 1; - } - else - { - // if the next reg is using .x, it wasn't packed with us - if(psDxbc.InputSig[i + 1].regChannelMask & 0x1) - arrayLength = 1; - } - } - - psInputDefinition += ToStr((uint32_t)numCols) + " input_" + name; - if(arrayLength > 0) - psInputDefinition += "[" + ToStr(arrayLength) + "]"; - psInputDefinition += " : " + name; - - inputVarNames[i] = "input_" + name; - if(arrayLength > 0) - inputVarNames[i] += StringFormat::Fmt("[%d]", RDCMAX(0, arrayIndex)); - - if(included && sig.varType == VarType::Float) - { - if(arrayLength == 0) - { - floatInputs.push_back("input_" + name); - } - else - { - for(int a = 0; a < arrayLength; a++) - floatInputs.push_back("input_" + name + "[" + ToStr(a) + "]"); - } - } - - psInputDefinition += ";\n"; - - int firstElem = sig.regChannelMask & 0x1 ? 0 - : sig.regChannelMask & 0x2 ? 1 - : sig.regChannelMask & 0x4 ? 2 - : sig.regChannelMask & 0x8 ? 3 - : -1; - - // arrays get added all at once (because in the struct data, they are contiguous even if - // in the input signature they're not). - if(arrayIndex < 0) - { - if(arrayLength == 0) - { - initialValues.push_back( - PSInputElement(sig.regIndex, firstElem, numCols, sig.systemValue, included)); - } - else - { - for(int a = 0; a < arrayLength; a++) - { - initialValues.push_back( - PSInputElement(sig.regIndex + a, firstElem, numCols, sig.systemValue, included)); - } - } - } + const SigParameter &sig = inputSig[i]; + interpModes[i] = DXBCDebug::GetInterpolationModeForInputParam(sig, inputSig, program); } - - psInputDefinition += "};\n\n"; } ShaderDebugTrace *InterpretDebugger::BeginDebug(const DXBC::DXBCContainer *dxbcContainer, diff --git a/renderdoc/driver/shaders/dxbc/dxbc_debug.h b/renderdoc/driver/shaders/dxbc/dxbc_debug.h index 6cd31a411..3a2c0f448 100644 --- a/renderdoc/driver/shaders/dxbc/dxbc_debug.h +++ b/renderdoc/driver/shaders/dxbc/dxbc_debug.h @@ -170,26 +170,6 @@ public: rdcarray constantBlocks; }; -struct PSInputElement -{ - PSInputElement(int regster, int element, int numWords, ShaderBuiltin attr, bool inc) - { - reg = regster; - elem = element; - numwords = numWords; - sysattribute = attr; - included = inc; - } - - int reg; - int elem; - ShaderBuiltin sysattribute; - - int numwords; - - bool included; -}; - void FlattenSingleVariable(uint32_t byteOffset, const rdcstr &basename, const ShaderVariable &v, rdcarray &outvars); @@ -198,12 +178,6 @@ void FillViewFmt(DXGI_FORMAT format, GlobalState::ViewFmt &viewFmt); void LookupSRVFormatFromShaderReflection(const DXBC::Reflection &reflection, const BindingSlot &slot, GlobalState::ViewFmt &viewFmt); -void GatherPSInputDataForInitialValues(const DXBC::DXBCContainer *dxbc, - const DXBC::Reflection &prevStageDxbc, - rdcarray &initialValues, - rdcarray &floatInputs, rdcarray &inputVarNames, - rdcstr &psInputDefinition, int &structureStride); - class DebugAPIWrapper { public: @@ -333,4 +307,7 @@ void AddCBufferToGlobalState(const DXBCBytecode::Program &program, GlobalState & const ShaderReflection &refl, const BindingSlot &slot, bytebuf &cbufData); -}; // namespace ShaderDebug +void GetInterpolationModeForInputParams(const rdcarray &stageInputSig, + const DXBCBytecode::Program *program, + rdcarray &interpModes); +}; // namespace DXBCDebug