mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-05-13 21:40:41 +00:00
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:
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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."
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user