DXBC Debug changes to support DXIL Debugging

Moved DXBCDebug::GatherPSDataForIntialValues() to DXDebug namespace and altered the input parameters.
Moved DXBCDebug::PSInputElement to DXDebug namespace
Added DXBCDebug::GetInterpolationModeForInputParams()
Added DXILDebug::GetInterpolationModeForInputParams()
This commit is contained in:
Jake Turner
2024-08-27 11:59:24 +01:00
parent 1d4cf8fb7b
commit 2cd162144d
6 changed files with 360 additions and 333 deletions
+8 -3
View File
@@ -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<DXBC::InterpolationMode> interpModes;
const rdcarray<SigParameter> &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
+7 -3
View File
@@ -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<DXBC::InterpolationMode> interpModes;
const rdcarray<SigParameter> &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
+303
View File
@@ -23,9 +23,312 @@
******************************************************************************/
#include "dx_debug.h"
#include "common/formatting.h"
#include "dxbc_common.h"
namespace DXDebug
{
void GatherPSInputDataForInitialValues(const rdcarray<SigParameter> &stageInputSig,
const rdcarray<SigParameter> &prevStageOutputSig,
const rdcarray<DXBC::InterpolationMode> &interpModes,
rdcarray<PSInputElement> &initialValues,
rdcarray<rdcstr> &floatInputs, rdcarray<rdcstr> &inputVarNames,
rdcstr &psInputDefinition, int &structureStride)
{
// 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<start semantic index, end semantic index>
rdcarray<rdcpair<rdcstr, rdcpair<uint32_t, uint32_t>>> 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."
+28
View File
@@ -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<SigParameter> &stageInputSig,
const rdcarray<SigParameter> &prevStageOutputSig,
const rdcarray<DXBC::InterpolationMode> &interpModes,
rdcarray<PSInputElement> &initialValues,
rdcarray<rdcstr> &floatInputs, rdcarray<rdcstr> &inputVarNames,
rdcstr &psInputDefinition, int &structureStride);
enum class GatherChannel : uint8_t
{
Red = 0,
+10 -300
View File
@@ -4731,7 +4731,7 @@ void LookupSRVFormatFromShaderReflection(const DXBC::Reflection &reflection,
}
DXBC::InterpolationMode GetInterpolationModeForInputParam(const SigParameter &sig,
const DXBC::Reflection &psDxbc,
const rdcarray<SigParameter> &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<PSInputElement> &initialValues,
rdcarray<rdcstr> &floatInputs, rdcarray<rdcstr> &inputVarNames,
rdcstr &psInputDefinition, int &structureStride)
void GetInterpolationModeForInputParams(const rdcarray<SigParameter> &inputSig,
const DXBCBytecode::Program *program,
rdcarray<DXBC::InterpolationMode> &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<start semantic index, end semantic index>
rdcarray<rdcpair<rdcstr, rdcpair<uint32_t, uint32_t>>> 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,
+4 -27
View File
@@ -170,26 +170,6 @@ public:
rdcarray<ShaderVariable> 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<ShaderVariable> &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<PSInputElement> &initialValues,
rdcarray<rdcstr> &floatInputs, rdcarray<rdcstr> &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<SigParameter> &stageInputSig,
const DXBCBytecode::Program *program,
rdcarray<DXBC::InterpolationMode> &interpModes);
}; // namespace DXBCDebug