Add support for encoding/writing DXBC bytecode

* In a couple of places we need to store information that's redundant/pointless
  to try and stay bitwise identical to DXBC (so we can parse a file, then write
  it out and get the exact same result).
* This also means staying bug-compatible with fxc:
  - it sometimes outputs an extra 0x00000000 DWORD after an operation.
  - it outputs interpolation modifiers which the spec says should be 0, and are ignored.
  - it also sometimes outputs extended operands with no actual use of the extended bits.
  - there's ambiguity between swizzle mode and mask mode for .xyzw swizzles, and fxc uses both so we need to ensure we match.
This commit is contained in:
baldurk
2021-07-09 16:38:52 +01:00
parent 54be369c93
commit 47df902b46
8 changed files with 834 additions and 41 deletions
@@ -441,7 +441,7 @@ DXBC::Reflection *Program::GuessReflection()
uint32_t idx = (uint32_t)dcl.operand.indices[0].index;
uint32_t reg = isShaderModel51 ? (uint32_t)dcl.operand.indices[1].index : idx;
uint32_t numVecs =
isShaderModel51 ? dcl.cbufferVectorSize : (uint32_t)dcl.operand.indices[1].index;
isShaderModel51 ? dcl.cbuffer.vectorSize : (uint32_t)dcl.operand.indices[1].index;
desc.name = StringFormat::Fmt("cbuffer%u", idx);
desc.type = DXBC::ShaderInputBind::TYPE_CBUFFER;
+48 -7
View File
@@ -775,6 +775,17 @@ struct Operand
FLAG_ABS = 0x02,
FLAG_NONUNIFORM = 0x04,
// these are only used to ensure we can output using the same register swizzling as fxc, since
// fxc unpredictably uses masks and swizzles for .xyzw
FLAG_SELECTED = 0x08,
FLAG_SWIZZLED = 0x10,
FLAG_MASKED = 0x20,
// for some reason fxc sometimes emits extended operands with... nothing. No modifier or
// precision. To try and round-trip cleanly we store the extended flag here instead of only
// emitting an extended operand when we see a modifier or precision
FLAG_EXTENDED = 0x40,
} flags;
MinimumPrecision precision;
@@ -818,6 +829,15 @@ struct Operand
void setComps(uint8_t x, uint8_t y, uint8_t z, uint8_t w)
{
flags = Flags(flags & ~(FLAG_SWIZZLED | FLAG_MASKED | FLAG_SELECTED));
if(y == 0xff && z == 0xff && w == 0xff)
flags = Flags(flags | FLAG_SELECTED);
else if(x == 0xff || y == 0xff || z == 0xff || w == 0xff)
flags = Flags(flags | FLAG_MASKED);
else
flags = Flags(flags | FLAG_SWIZZLED);
comps[0] = x;
comps[1] = y;
comps[2] = z;
@@ -968,18 +988,23 @@ struct Declaration
uint32_t groupSize[3];
// OPCODE_DCL_CONSTANT_BUFFER
uint32_t cbufferVectorSize;
struct
{
CBufferAccessPattern accessPattern;
uint32_t vectorSize;
} cbuffer;
// OPCODE_DCL_INPUT_PS
InterpolationMode inputInterpolation;
// OPCODE_DCL_INPUT_PS_SIV
// OPCODE_DCL_INPUT_SIV
// OPCODE_DCL_INPUT_SGV
// OPCODE_DCL_INPUT_PS_SIV
// OPCODE_DCL_INPUT_PS_SGV
// OPCODE_DCL_OUTPUT_SIV
// OPCODE_DCL_OUTPUT_SGV
DXBC::SVSemantic systemValue;
struct
{
// only used for PS inputs
InterpolationMode inputInterpolation;
DXBC::SVSemantic systemValue;
} inputOutput;
// OPCODE_DCL_MAX_OUTPUT_VERTEX_COUNT
uint32_t maxVertexOutCount;
@@ -1088,6 +1113,13 @@ struct Operation
FLAG_NONE = 0x0,
FLAG_NONZERO = 0x01,
FLAG_SATURATE = 0x02,
FLAG_TEXEL_OFFSETS = 0x04,
FLAG_RESOURCE_DIMS = 0x08,
FLAG_RET_TYPE = 0x10,
// sometimes fxc emits a 0 dword after the operand. For bitwise-compatibility we store a flag
// here for that case to emit it again
FLAG_TRAILING_ZERO_TOKEN = 0x20,
} flags;
rdcarray<Operand> operands;
@@ -1097,6 +1129,9 @@ class Program
{
public:
Program(const byte *bytes, size_t length);
Program(const Program &o) = delete;
Program(Program &&o) = delete;
Program &operator=(const Program &o) = delete;
void SetShaderEXTUAV(GraphicsAPI api, uint32_t space, uint32_t reg)
{
@@ -1108,6 +1143,8 @@ public:
rdcstr GetDebugStatus();
rdcarray<uint32_t> EncodeProgram();
void SetReflection(const DXBC::Reflection *refl) { m_Reflection = refl; }
void SetDebugInfo(const DXBC::IDebugInfo *debug) { m_DebugInfo = debug; }
DXBC::ShaderType GetShaderType() const { return m_Type; }
@@ -1184,5 +1221,9 @@ protected:
bool DecodeOperation(uint32_t *&tokenStream, Operation &op, bool friendlyName);
bool DecodeDecl(uint32_t *&tokenStream, Declaration &decl, bool friendlyName);
bool DecodeOperand(uint32_t *&tokenStream, ToString flags, Operand &oper);
void EncodeOperand(rdcarray<uint32_t> &tokenStream, const Operand &oper);
void EncodeDecl(rdcarray<uint32_t> &tokenStream, const Declaration &decl);
void EncodeOperation(rdcarray<uint32_t> &tokenStream, const Operation &op);
};
};
@@ -791,6 +791,45 @@ void Program::DecodeProgram()
PostprocessVendorExtensions();
}
rdcarray<uint32_t> Program::EncodeProgram()
{
rdcarray<uint32_t> tokenStream;
tokenStream.push_back(0U);
tokenStream.push_back(0U);
VersionToken::ProgramType.Set(tokenStream[0], m_Type);
VersionToken::MajorVersion.Set(tokenStream[0], m_Major);
VersionToken::MinorVersion.Set(tokenStream[0], m_Minor);
for(size_t d = 0; d < m_Declarations.size(); d++)
EncodeDecl(tokenStream, m_Declarations[d]);
size_t lt = 0;
for(size_t i = 0; i < m_Instructions.size(); i++)
{
EncodeOperation(tokenStream, m_Instructions[i]);
if(m_Instructions[i].operation == OPCODE_HS_CONTROL_POINT_PHASE ||
m_Instructions[i].operation == OPCODE_HS_FORK_PHASE ||
m_Instructions[i].operation == OPCODE_HS_JOIN_PHASE)
{
if(lt < m_LateDeclarations.size())
{
for(size_t d = 0; d < m_LateDeclarations[lt].size(); d++)
EncodeDecl(tokenStream, m_LateDeclarations[lt][d]);
lt++;
}
}
}
LengthToken::Length.Set(tokenStream[1], (uint32_t)tokenStream.size());
return tokenStream;
}
void Program::MakeDisassemblyString()
{
DecodeProgram();
@@ -990,8 +1029,151 @@ void Program::MakeDisassemblyString()
}
}
void Program::EncodeOperand(rdcarray<uint32_t> &tokenStream, const Operand &oper)
{
uint32_t OperandToken0 = 0;
Oper::Type.Set(OperandToken0, oper.type);
Oper::NumComponents.Set(OperandToken0, oper.numComponents);
if(oper.flags & Operand::FLAG_SELECTED)
{
Oper::SelectionMode.Set(OperandToken0, SELECTION_SELECT_1);
Oper::ComponentSel1.Set(OperandToken0, oper.comps[0]);
}
else if(oper.flags & Operand::FLAG_MASKED)
{
Oper::SelectionMode.Set(OperandToken0, SELECTION_MASK);
for(int i = 0; i < 4; i++)
{
if(oper.comps[i] == 0)
Oper::ComponentMaskX.Set(OperandToken0, true);
if(oper.comps[i] == 1)
Oper::ComponentMaskY.Set(OperandToken0, true);
if(oper.comps[i] == 2)
Oper::ComponentMaskZ.Set(OperandToken0, true);
if(oper.comps[i] == 3)
Oper::ComponentMaskW.Set(OperandToken0, true);
}
}
else
{
Oper::SelectionMode.Set(OperandToken0, SELECTION_SWIZZLE);
Oper::ComponentSwizzleX.Set(OperandToken0, oper.comps[0]);
Oper::ComponentSwizzleY.Set(OperandToken0, oper.comps[1]);
Oper::ComponentSwizzleZ.Set(OperandToken0, oper.comps[2]);
Oper::ComponentSwizzleW.Set(OperandToken0, oper.comps[3]);
}
Oper::IndexDimension.Set(OperandToken0, (uint32_t)oper.indices.size());
OperandIndexType rep[3] = {};
for(int i = 0; i < oper.indices.size(); i++)
{
OperandIndexType indextype = INDEX_IMMEDIATE32;
if(oper.indices[i].relative)
{
if(oper.indices[i].absolute)
indextype = INDEX_IMMEDIATE32_PLUS_RELATIVE;
else
indextype = INDEX_RELATIVE;
}
// technically this means immediate64 with indices that can be truncated changes, but we don't
// expect fxc to emit that (I've never seen it emit immediate64).
// immediate64 types come after coresponding immediate32 types
RDCCOMPILE_ASSERT(INDEX_IMMEDIATE32 + 1 == INDEX_IMMEDIATE64, "enums aren't in order expected");
RDCCOMPILE_ASSERT(INDEX_IMMEDIATE32_PLUS_RELATIVE + 1 == INDEX_IMMEDIATE64_PLUS_RELATIVE,
"enums aren't in order expected");
if((oper.indices[i].index & 0xFFFFFFFFULL) != oper.indices[i].index)
indextype = OperandIndexType(indextype + 1);
rep[i] = indextype;
if(i == 0)
Oper::Index0.Set(OperandToken0, indextype);
if(i == 1)
Oper::Index1.Set(OperandToken0, indextype);
if(i == 2)
Oper::Index2.Set(OperandToken0, indextype);
}
bool extended = false;
if(oper.flags & Operand::FLAG_EXTENDED)
{
Oper::Extended.Set(OperandToken0, true);
extended = true;
}
tokenStream.push_back(OperandToken0);
if(extended)
{
uint32_t ExtendedToken = 0;
ExtendedOperand::Type.Set(ExtendedToken, EXTENDED_OPERAND_MODIFIER);
if((oper.flags & (Operand::FLAG_ABS | Operand::FLAG_NEG)) != 0)
ExtendedOperand::Modifier.Set(
ExtendedToken, OperandModifier(oper.flags & (Operand::FLAG_ABS | Operand::FLAG_NEG)));
if(oper.precision != PRECISION_DEFAULT)
ExtendedOperand::MinPrecision.Set(ExtendedToken, oper.precision);
if(oper.flags & Operand::FLAG_NONUNIFORM)
ExtendedOperand::NonUniform.Set(ExtendedToken, true);
tokenStream.push_back(ExtendedToken);
}
// immediates now have 1 or 4 values
if(oper.type == TYPE_IMMEDIATE32 || oper.type == TYPE_IMMEDIATE64)
{
RDCASSERT(oper.indices.empty());
uint32_t numComps = 1;
if(oper.numComponents == NUMCOMPS_1)
numComps = 1;
else if(oper.numComponents == NUMCOMPS_4)
numComps = 4;
else
RDCERR("N-wide vectors not supported.");
tokenStream.append(oper.values, numComps);
}
// now encode the indices
for(size_t idx = 0; idx < oper.indices.size(); idx++)
{
// if there's an immediate, push it first
if(rep[idx] == INDEX_IMMEDIATE32_PLUS_RELATIVE || rep[idx] == INDEX_IMMEDIATE32)
{
tokenStream.push_back(oper.indices[idx].index & 0xFFFFFFFFU);
}
else if(rep[idx] == INDEX_IMMEDIATE64_PLUS_RELATIVE || rep[idx] == INDEX_IMMEDIATE64)
{
// for 64-bit values, push the high value first
tokenStream.push_back(oper.indices[idx].index >> 32U);
tokenStream.push_back(oper.indices[idx].index & 0xFFFFFFFFU);
}
// if there's a relative component, push another operand here
if(rep[idx] == INDEX_IMMEDIATE64_PLUS_RELATIVE || rep[idx] == INDEX_IMMEDIATE32_PLUS_RELATIVE ||
rep[idx] == INDEX_RELATIVE)
{
EncodeOperand(tokenStream, oper.indices[idx].operand);
}
}
}
bool Program::DecodeOperand(uint32_t *&tokenStream, ToString flags, Operand &retOper)
{
RDCCOMPILE_ASSERT(sizeof(Operand) <= 64, "Operand shouldn't increase in size");
uint32_t OperandToken0 = tokenStream[0];
retOper.type = Oper::Type.Get(OperandToken0);
@@ -1001,6 +1183,8 @@ bool Program::DecodeOperand(uint32_t *&tokenStream, ToString flags, Operand &ret
if(selMode == SELECTION_MASK)
{
retOper.flags = Operand::FLAG_MASKED;
int i = 0;
if(Oper::ComponentMaskX.Get(OperandToken0))
@@ -1014,6 +1198,8 @@ bool Program::DecodeOperand(uint32_t *&tokenStream, ToString flags, Operand &ret
}
else if(selMode == SELECTION_SWIZZLE)
{
retOper.flags = Operand::FLAG_SWIZZLED;
retOper.comps[0] = Oper::ComponentSwizzleX.Get(OperandToken0);
retOper.comps[1] = Oper::ComponentSwizzleY.Get(OperandToken0);
retOper.comps[2] = Oper::ComponentSwizzleZ.Get(OperandToken0);
@@ -1021,6 +1207,8 @@ bool Program::DecodeOperand(uint32_t *&tokenStream, ToString flags, Operand &ret
}
else if(selMode == SELECTION_SELECT_1)
{
retOper.flags = Operand::FLAG_SELECTED;
retOper.comps[0] = Oper::ComponentSel1.Get(OperandToken0);
}
@@ -1041,6 +1229,8 @@ bool Program::DecodeOperand(uint32_t *&tokenStream, ToString flags, Operand &ret
ExtendedOperandType type = ExtendedOperand::Type.Get(OperandTokenN);
retOper.flags = Operand::Flags(retOper.flags | Operand::FLAG_EXTENDED);
if(type == EXTENDED_OPERAND_MODIFIER)
{
retOper.flags = Operand::Flags(retOper.flags | ExtendedOperand::Modifier.Get(OperandTokenN));
@@ -1145,6 +1335,366 @@ bool Program::DecodeOperand(uint32_t *&tokenStream, ToString flags, Operand &ret
return true;
}
void Program::EncodeDecl(rdcarray<uint32_t> &tokenStream, const Declaration &decl)
{
rdcarray<uint32_t> declStream;
declStream.reserve(16);
uint32_t OpcodeToken0 = 0;
const bool sm51 = (m_Major == 0x5 && m_Minor == 0x1);
Opcode::Type.Set(OpcodeToken0, decl.declaration);
OpcodeType op = decl.declaration;
if(op == OPCODE_DCL_IMMEDIATE_CONSTANT_BUFFER)
{
OpcodeToken0 = 0;
Opcode::Type.Set(OpcodeToken0, OPCODE_CUSTOMDATA);
Opcode::CustomClass.Set(OpcodeToken0, CUSTOMDATA_DCL_IMMEDIATE_CONSTANT_BUFFER);
declStream.push_back(OpcodeToken0);
// push overall length, including OpcodeToken0 and this length
declStream.push_back((uint32_t)m_Immediate.size() + 2);
declStream.append(m_Immediate);
// append immediately and return, so we don't do any length fixup
tokenStream.append(declStream);
return;
}
else if(op == OPCODE_OPAQUE_CUSTOMDATA)
{
OpcodeToken0 = 0;
Opcode::Type.Set(OpcodeToken0, OPCODE_CUSTOMDATA);
Opcode::CustomClass.Set(OpcodeToken0, m_CustomDatas[decl.customDataIndex].first);
declStream.push_back(OpcodeToken0);
const rdcarray<uint32_t> &customData = m_CustomDatas[decl.customDataIndex].second;
// push overall length, including OpcodeToken0 and this length
declStream.push_back((uint32_t)customData.size() + 2);
declStream.append(customData);
// append immediately and return, so we don't do any length fixup
tokenStream.append(declStream);
return;
}
else if(op == OPCODE_CUSTOMDATA)
{
RDCERR("Unexpected raw customdata declaration");
return;
}
else if(op == OPCODE_DCL_GLOBAL_FLAGS)
{
Decl::RefactoringAllowed.Set(OpcodeToken0, decl.global_flags.refactoringAllowed);
Decl::DoubleFloatOps.Set(OpcodeToken0, decl.global_flags.doublePrecisionFloats);
Decl::ForceEarlyDepthStencil.Set(OpcodeToken0, decl.global_flags.forceEarlyDepthStencil);
Decl::EnableRawStructuredBufs.Set(OpcodeToken0, decl.global_flags.enableRawAndStructuredBuffers);
Decl::SkipOptimisation.Set(OpcodeToken0, decl.global_flags.skipOptimisation);
Decl::EnableMinPrecision.Set(OpcodeToken0, decl.global_flags.enableMinPrecision);
Decl::EnableD3D11_1DoubleExtensions.Set(OpcodeToken0,
decl.global_flags.enableD3D11_1DoubleExtensions);
Decl::EnableD3D11_1ShaderExtensions.Set(OpcodeToken0,
decl.global_flags.enableD3D11_1ShaderExtensions);
Decl::EnableD3D12AllResourcesBound.Set(OpcodeToken0,
decl.global_flags.enableD3D12AllResourcesBound);
declStream.push_back(OpcodeToken0);
}
else if(op == OPCODE_DCL_CONSTANT_BUFFER)
{
Decl::AccessPattern.Set(OpcodeToken0, decl.cbuffer.accessPattern);
declStream.push_back(OpcodeToken0);
EncodeOperand(declStream, decl.operand);
if(sm51)
{
declStream.push_back(decl.cbuffer.vectorSize);
declStream.push_back(decl.space);
}
}
else if(op == OPCODE_DCL_INPUT)
{
declStream.push_back(OpcodeToken0);
EncodeOperand(declStream, decl.operand);
}
else if(op == OPCODE_DCL_TEMPS)
{
declStream.push_back(OpcodeToken0);
declStream.push_back(decl.numTemps);
}
else if(op == OPCODE_DCL_INDEXABLE_TEMP)
{
declStream.push_back(OpcodeToken0);
declStream.push_back(decl.indexable_temp.tempReg);
declStream.push_back(decl.indexable_temp.numTemps);
declStream.push_back(decl.indexable_temp.tempComponentCount);
}
else if(op == OPCODE_DCL_OUTPUT)
{
declStream.push_back(OpcodeToken0);
EncodeOperand(declStream, decl.operand);
}
else if(op == OPCODE_DCL_MAX_OUTPUT_VERTEX_COUNT)
{
declStream.push_back(OpcodeToken0);
declStream.push_back(decl.maxVertexOutCount);
}
else if(op == OPCODE_DCL_INPUT_SIV || op == OPCODE_DCL_INPUT_SGV || op == OPCODE_DCL_OUTPUT_SIV ||
op == OPCODE_DCL_OUTPUT_SGV)
{
declStream.push_back(OpcodeToken0);
EncodeOperand(declStream, decl.operand);
declStream.push_back((uint32_t)decl.inputOutput.systemValue);
}
else if(op == OPCODE_DCL_INPUT_PS || op == OPCODE_DCL_INPUT_PS_SIV || op == OPCODE_DCL_INPUT_PS_SGV)
{
// the (minimal) spec says this is 0 for SGV, but it's not and fxc displays the interp mode
Decl::InterpolationMode.Set(OpcodeToken0, decl.inputOutput.inputInterpolation);
declStream.push_back(OpcodeToken0);
EncodeOperand(declStream, decl.operand);
if(op == OPCODE_DCL_INPUT_PS_SIV || op == OPCODE_DCL_INPUT_PS_SGV)
declStream.push_back((uint32_t)decl.inputOutput.systemValue);
}
else if(op == OPCODE_DCL_STREAM)
{
declStream.push_back(OpcodeToken0);
EncodeOperand(declStream, decl.operand);
}
else if(op == OPCODE_DCL_SAMPLER)
{
Decl::SamplerMode.Set(OpcodeToken0, decl.samplerMode);
declStream.push_back(OpcodeToken0);
EncodeOperand(declStream, decl.operand);
if(sm51)
declStream.push_back(decl.space);
}
else if(op == OPCODE_DCL_RESOURCE)
{
Decl::ResourceDim.Set(OpcodeToken0, decl.resource.dim);
if(decl.resource.dim == RESOURCE_DIMENSION_TEXTURE2DMS ||
decl.resource.dim == RESOURCE_DIMENSION_TEXTURE2DMSARRAY)
{
Decl::SampleCount.Set(OpcodeToken0, decl.resource.sampleCount);
}
declStream.push_back(OpcodeToken0);
EncodeOperand(declStream, decl.operand);
uint32_t ResourceReturnTypeToken = 0;
Decl::ReturnTypeX.Set(ResourceReturnTypeToken, decl.resource.resType[0]);
Decl::ReturnTypeY.Set(ResourceReturnTypeToken, decl.resource.resType[1]);
Decl::ReturnTypeZ.Set(ResourceReturnTypeToken, decl.resource.resType[2]);
Decl::ReturnTypeW.Set(ResourceReturnTypeToken, decl.resource.resType[3]);
declStream.push_back(ResourceReturnTypeToken);
if(sm51)
declStream.push_back(decl.space);
}
else if(op == OPCODE_DCL_INDEX_RANGE)
{
declStream.push_back(OpcodeToken0);
EncodeOperand(declStream, decl.operand);
declStream.push_back(decl.indexRange);
}
else if(op == OPCODE_DCL_THREAD_GROUP)
{
declStream.push_back(OpcodeToken0);
declStream.push_back(decl.groupSize[0]);
declStream.push_back(decl.groupSize[1]);
declStream.push_back(decl.groupSize[2]);
}
else if(op == OPCODE_DCL_THREAD_GROUP_SHARED_MEMORY_RAW)
{
declStream.push_back(OpcodeToken0);
EncodeOperand(declStream, decl.operand);
declStream.push_back(decl.tgsmCount);
}
else if(op == OPCODE_DCL_THREAD_GROUP_SHARED_MEMORY_STRUCTURED)
{
declStream.push_back(OpcodeToken0);
EncodeOperand(declStream, decl.operand);
declStream.push_back(decl.tsgm_structured.stride);
declStream.push_back(decl.tsgm_structured.count);
}
else if(op == OPCODE_DCL_INPUT_CONTROL_POINT_COUNT || op == OPCODE_DCL_OUTPUT_CONTROL_POINT_COUNT)
{
Decl::ControlPointCount.Set(OpcodeToken0, decl.controlPointCount);
declStream.push_back(OpcodeToken0);
}
else if(op == OPCODE_DCL_TESS_DOMAIN)
{
Decl::TessDomain.Set(OpcodeToken0, decl.tessDomain);
declStream.push_back(OpcodeToken0);
}
else if(op == OPCODE_DCL_TESS_PARTITIONING)
{
Decl::TessPartitioning.Set(OpcodeToken0, decl.tessPartition);
declStream.push_back(OpcodeToken0);
}
else if(op == OPCODE_DCL_GS_INPUT_PRIMITIVE)
{
Decl::InputPrimitive.Set(OpcodeToken0, decl.geomInputPrimitive);
declStream.push_back(OpcodeToken0);
}
else if(op == OPCODE_DCL_GS_OUTPUT_PRIMITIVE_TOPOLOGY)
{
Decl::OutputPrimitiveTopology.Set(OpcodeToken0, decl.geomOutputTopology);
declStream.push_back(OpcodeToken0);
}
else if(op == OPCODE_DCL_TESS_OUTPUT_PRIMITIVE)
{
Decl::OutputPrimitive.Set(OpcodeToken0, decl.tessOutputPrimitive);
declStream.push_back(OpcodeToken0);
}
else if(op == OPCODE_DCL_UNORDERED_ACCESS_VIEW_RAW || op == OPCODE_DCL_RESOURCE_RAW)
{
if(op == OPCODE_DCL_UNORDERED_ACCESS_VIEW_RAW)
{
Decl::RasterizerOrderedAccess.Set(OpcodeToken0, decl.raw.rov);
Decl::GloballyCoherent.Set(OpcodeToken0, decl.raw.globallyCoherant);
}
declStream.push_back(OpcodeToken0);
EncodeOperand(declStream, decl.operand);
if(sm51)
declStream.push_back(decl.space);
}
else if(op == OPCODE_DCL_UNORDERED_ACCESS_VIEW_STRUCTURED || op == OPCODE_DCL_RESOURCE_STRUCTURED)
{
if(op == OPCODE_DCL_UNORDERED_ACCESS_VIEW_STRUCTURED)
{
Decl::HasOrderPreservingCounter.Set(OpcodeToken0, decl.structured.hasCounter);
Decl::RasterizerOrderedAccess.Set(OpcodeToken0, decl.structured.rov);
Decl::GloballyCoherent.Set(OpcodeToken0, decl.structured.globallyCoherant);
}
declStream.push_back(OpcodeToken0);
EncodeOperand(declStream, decl.operand);
declStream.push_back(decl.structured.stride);
if(sm51)
declStream.push_back(decl.space);
}
else if(op == OPCODE_DCL_UNORDERED_ACCESS_VIEW_TYPED)
{
Decl::ResourceDim.Set(OpcodeToken0, decl.uav_typed.dim);
Decl::GloballyCoherent.Set(OpcodeToken0, decl.uav_typed.globallyCoherant);
Decl::RasterizerOrderedAccess.Set(OpcodeToken0, decl.uav_typed.rov);
declStream.push_back(OpcodeToken0);
EncodeOperand(declStream, decl.operand);
uint32_t ResourceReturnTypeToken = 0;
Decl::ReturnTypeX.Set(ResourceReturnTypeToken, decl.uav_typed.resType[0]);
Decl::ReturnTypeY.Set(ResourceReturnTypeToken, decl.uav_typed.resType[1]);
Decl::ReturnTypeZ.Set(ResourceReturnTypeToken, decl.uav_typed.resType[2]);
Decl::ReturnTypeW.Set(ResourceReturnTypeToken, decl.uav_typed.resType[3]);
declStream.push_back(ResourceReturnTypeToken);
if(sm51)
declStream.push_back(decl.space);
}
else if(op == OPCODE_DCL_HS_FORK_PHASE_INSTANCE_COUNT ||
op == OPCODE_DCL_HS_JOIN_PHASE_INSTANCE_COUNT || op == OPCODE_DCL_GS_INSTANCE_COUNT)
{
declStream.push_back(OpcodeToken0);
declStream.push_back(decl.instanceCount);
}
else if(op == OPCODE_DCL_HS_MAX_TESSFACTOR)
{
declStream.push_back(OpcodeToken0);
uint32_t token;
memcpy(&token, &decl.maxTessFactor, sizeof(token));
declStream.push_back(token);
}
else if(op == OPCODE_DCL_FUNCTION_BODY)
{
declStream.push_back(OpcodeToken0);
declStream.push_back(decl.functionBody);
}
else if(op == OPCODE_DCL_FUNCTION_TABLE)
{
declStream.push_back(OpcodeToken0);
declStream.push_back(decl.functionTable);
declStream.push_back((uint32_t)decl.functionTableContents.size());
declStream.append(decl.functionTableContents);
}
else if(op == OPCODE_DCL_INTERFACE)
{
declStream.push_back(OpcodeToken0);
declStream.push_back(decl.iface.interfaceID);
declStream.push_back(decl.iface.numTypes);
uint32_t CountToken = 0;
Decl::NumInterfaces.Set(CountToken, decl.iface.numInterfaces);
Decl::TableLength.Set(CountToken, (uint32_t)decl.functionTableContents.size());
declStream.push_back(CountToken);
declStream.append(decl.functionTableContents);
}
else if(op == OPCODE_HS_DECLS)
{
declStream.push_back(OpcodeToken0);
}
else
{
RDCERR("Unexpected opcode decl %d", op);
}
// fixup the declaration now and append
Opcode::Length.Set(declStream[0], (uint32_t)declStream.size());
tokenStream.append(declStream);
}
bool Program::DecodeDecl(uint32_t *&tokenStream, Declaration &retDecl, bool friendlyName)
{
uint32_t *begin = tokenStream;
@@ -1317,7 +1867,7 @@ bool Program::DecodeDecl(uint32_t *&tokenStream, Declaration &retDecl, bool frie
}
else if(op == OPCODE_DCL_CONSTANT_BUFFER)
{
CBufferAccessPattern accessPattern = Decl::AccessPattern.Get(OpcodeToken0);
retDecl.cbuffer.accessPattern = Decl::AccessPattern.Get(OpcodeToken0);
bool ret = DecodeOperand(tokenStream, flags, retDecl.operand);
RDCASSERT(ret);
@@ -1328,17 +1878,17 @@ bool Program::DecodeDecl(uint32_t *&tokenStream, Declaration &retDecl, bool frie
{
// Store the size provided. If there's no reflection data, this will be
// necessary to guess the buffer size properly
retDecl.cbufferVectorSize = tokenStream[0];
retDecl.cbuffer.vectorSize = tokenStream[0];
tokenStream++;
retDecl.str += StringFormat::Fmt("[%u]", retDecl.cbufferVectorSize);
retDecl.str += StringFormat::Fmt("[%u]", retDecl.cbuffer.vectorSize);
}
retDecl.str += ", ";
if(accessPattern == ACCESS_IMMEDIATE_INDEXED)
if(retDecl.cbuffer.accessPattern == ACCESS_IMMEDIATE_INDEXED)
retDecl.str += "immediateIndexed";
else if(accessPattern == ACCESS_DYNAMIC_INDEXED)
else if(retDecl.cbuffer.accessPattern == ACCESS_DYNAMIC_INDEXED)
retDecl.str += "dynamicIndexed";
else
RDCERR("Unexpected cbuffer access pattern");
@@ -1422,21 +1972,46 @@ bool Program::DecodeDecl(uint32_t *&tokenStream, Declaration &retDecl, bool frie
retDecl.str += StringFormat::Fmt(" %u", retDecl.maxVertexOutCount);
}
else if(op == OPCODE_DCL_INPUT_SIV || op == OPCODE_DCL_INPUT_SGV ||
op == OPCODE_DCL_INPUT_PS_SIV || op == OPCODE_DCL_INPUT_PS_SGV ||
op == OPCODE_DCL_OUTPUT_SIV || op == OPCODE_DCL_OUTPUT_SGV)
else if(op == OPCODE_DCL_INPUT_SIV || op == OPCODE_DCL_INPUT_SGV || op == OPCODE_DCL_OUTPUT_SIV ||
op == OPCODE_DCL_OUTPUT_SGV)
{
bool ret = DecodeOperand(tokenStream, flags, retDecl.operand);
RDCASSERT(ret);
retDecl.systemValue = (DXBC::SVSemantic)tokenStream[0];
retDecl.inputOutput.systemValue = (DXBC::SVSemantic)tokenStream[0];
tokenStream++;
retDecl.str += " ";
retDecl.str += retDecl.operand.toString(m_Reflection, flags | ToString::ShowSwizzle);
retDecl.str += ", ";
retDecl.str += ToStr(retDecl.systemValue);
retDecl.str += ToStr(retDecl.inputOutput.systemValue);
}
else if(op == OPCODE_DCL_INPUT_PS || op == OPCODE_DCL_INPUT_PS_SIV || op == OPCODE_DCL_INPUT_PS_SGV)
{
// the (minimal) spec says this is 0 for SGV, but it's not and fxc displays the interp mode
retDecl.inputOutput.inputInterpolation = Decl::InterpolationMode.Get(OpcodeToken0);
bool ret = DecodeOperand(tokenStream, flags, retDecl.operand);
RDCASSERT(ret);
if(op == OPCODE_DCL_INPUT_PS_SIV || op == OPCODE_DCL_INPUT_PS_SGV)
{
retDecl.inputOutput.systemValue = (DXBC::SVSemantic)tokenStream[0];
tokenStream++;
}
retDecl.str += " ";
retDecl.str += ToStr(retDecl.inputOutput.inputInterpolation);
retDecl.str += " ";
retDecl.str += retDecl.operand.toString(m_Reflection, flags | ToString::ShowSwizzle);
if(op == OPCODE_DCL_INPUT_PS_SIV || op == OPCODE_DCL_INPUT_PS_SGV)
{
retDecl.str += ", ";
retDecl.str += ToStr(retDecl.inputOutput.systemValue);
}
}
else if(op == OPCODE_DCL_STREAM)
{
@@ -1542,19 +2117,6 @@ bool Program::DecodeDecl(uint32_t *&tokenStream, Declaration &retDecl, bool frie
retDecl.operand.indices[2].index);
}
}
else if(op == OPCODE_DCL_INPUT_PS)
{
retDecl.inputInterpolation = Decl::InterpolationMode.Get(OpcodeToken0);
bool ret = DecodeOperand(tokenStream, flags, retDecl.operand);
RDCASSERT(ret);
retDecl.str += " ";
retDecl.str += ToStr(retDecl.inputInterpolation);
retDecl.str += " ";
retDecl.str += retDecl.operand.toString(m_Reflection, flags | ToString::ShowSwizzle);
}
else if(op == OPCODE_DCL_INDEX_RANGE)
{
bool ret = DecodeOperand(tokenStream, flags, retDecl.operand);
@@ -1970,6 +2532,114 @@ bool Program::DecodeDecl(uint32_t *&tokenStream, Declaration &retDecl, bool frie
return true;
}
void Program::EncodeOperation(rdcarray<uint32_t> &tokenStream, const Operation &op)
{
rdcarray<uint32_t> opStream;
opStream.reserve(16);
uint32_t OpcodeToken0 = 0;
const bool sm51 = (m_Major == 0x5 && m_Minor == 0x1);
Opcode::Type.Set(OpcodeToken0, op.operation);
Opcode::TestNonZero.Set(OpcodeToken0, op.nonzero());
Opcode::Saturate.Set(OpcodeToken0, op.saturate());
Opcode::PreciseValues.Set(OpcodeToken0, op.preciseValues);
if(op.operation == OPCODE_RESINFO)
Opcode::ResinfoReturn.Set(OpcodeToken0, op.resinfoRetType);
if(op.operation == OPCODE_SYNC)
Opcode::SyncFlags.Set(OpcodeToken0, op.syncFlags);
if(op.operation == OPCODE_SHADER_MESSAGE || op.operation == OPCODE_OPAQUE_CUSTOMDATA)
{
OpcodeToken0 = 0;
Opcode::Type.Set(OpcodeToken0, OPCODE_CUSTOMDATA);
Opcode::CustomClass.Set(OpcodeToken0, m_CustomDatas[op.customDataIndex].first);
opStream.push_back(OpcodeToken0);
const rdcarray<uint32_t> &customData = m_CustomDatas[op.customDataIndex].second;
// push overall length, including OpcodeToken0 and this length
opStream.push_back((uint32_t)customData.size() + 2);
opStream.append(customData);
// append immediately and return, so we don't do any length fixup
tokenStream.append(opStream);
return;
}
else if(op.operation == OPCODE_CUSTOMDATA)
{
RDCERR("Unexpected raw customdata operation");
return;
}
opStream.push_back(OpcodeToken0);
if(op.flags & Operation::FLAG_TEXEL_OFFSETS)
{
ExtendedOpcode::Extended.Set(opStream.back(), true);
uint32_t OpcodeTokenN = 0;
ExtendedOpcode::Type.Set(OpcodeTokenN, EXTENDED_OPCODE_SAMPLE_CONTROLS);
ExtendedOpcode::TexelOffsetU.Set(OpcodeTokenN, op.texelOffset[0]);
ExtendedOpcode::TexelOffsetV.Set(OpcodeTokenN, op.texelOffset[1]);
ExtendedOpcode::TexelOffsetW.Set(OpcodeTokenN, op.texelOffset[2]);
opStream.push_back(OpcodeTokenN);
}
if(op.flags & Operation::FLAG_RESOURCE_DIMS)
{
ExtendedOpcode::Extended.Set(opStream.back(), true);
uint32_t OpcodeTokenN = 0;
ExtendedOpcode::Type.Set(OpcodeTokenN, EXTENDED_OPCODE_RESOURCE_DIM);
ExtendedOpcode::ResourceDim.Set(OpcodeTokenN, op.resDim);
if(op.resDim == RESOURCE_DIMENSION_STRUCTURED_BUFFER)
ExtendedOpcode::BufferStride.Set(OpcodeTokenN, op.stride);
opStream.push_back(OpcodeTokenN);
}
if(op.flags & Operation::FLAG_RET_TYPE)
{
ExtendedOpcode::Extended.Set(opStream.back(), true);
uint32_t OpcodeTokenN = 0;
ExtendedOpcode::Type.Set(OpcodeTokenN, EXTENDED_OPCODE_RESOURCE_RETURN_TYPE);
ExtendedOpcode::ReturnTypeX.Set(OpcodeTokenN, op.resType[0]);
ExtendedOpcode::ReturnTypeY.Set(OpcodeTokenN, op.resType[1]);
ExtendedOpcode::ReturnTypeZ.Set(OpcodeTokenN, op.resType[2]);
ExtendedOpcode::ReturnTypeW.Set(OpcodeTokenN, op.resType[3]);
opStream.push_back(OpcodeTokenN);
}
if(op.operation == OPCODE_INTERFACE_CALL)
opStream.push_back(op.operands[0].values[0]);
for(size_t i = 0; i < op.operands.size(); i++)
EncodeOperand(opStream, op.operands[i]);
if(op.flags & Operation::FLAG_TRAILING_ZERO_TOKEN)
opStream.push_back(0x0);
// fixup the declaration now and append
Opcode::Length.Set(opStream[0], (uint32_t)opStream.size());
tokenStream.append(opStream);
}
bool Program::DecodeOperation(uint32_t *&tokenStream, Operation &retOp, bool friendlyName)
{
uint32_t *begin = tokenStream;
@@ -2112,6 +2782,8 @@ bool Program::DecodeOperation(uint32_t *&tokenStream, Operation &retOp, bool fri
if(type == EXTENDED_OPCODE_SAMPLE_CONTROLS)
{
retOp.flags = Operation::Flags(retOp.flags | Operation::FLAG_TEXEL_OFFSETS);
retOp.texelOffset[0] = ExtendedOpcode::TexelOffsetU.Get(OpcodeTokenN);
retOp.texelOffset[1] = ExtendedOpcode::TexelOffsetV.Get(OpcodeTokenN);
retOp.texelOffset[2] = ExtendedOpcode::TexelOffsetW.Get(OpcodeTokenN);
@@ -2129,6 +2801,8 @@ bool Program::DecodeOperation(uint32_t *&tokenStream, Operation &retOp, bool fri
}
else if(type == EXTENDED_OPCODE_RESOURCE_DIM)
{
retOp.flags = Operation::Flags(retOp.flags | Operation::FLAG_RESOURCE_DIMS);
retOp.resDim = ExtendedOpcode::ResourceDim.Get(OpcodeTokenN);
if(retOp.resDim == RESOURCE_DIMENSION_STRUCTURED_BUFFER)
@@ -2147,6 +2821,8 @@ bool Program::DecodeOperation(uint32_t *&tokenStream, Operation &retOp, bool fri
}
else if(type == EXTENDED_OPCODE_RESOURCE_RETURN_TYPE)
{
retOp.flags = Operation::Flags(retOp.flags | Operation::FLAG_RET_TYPE);
retOp.resType[0] = ExtendedOpcode::ReturnTypeX.Get(OpcodeTokenN);
retOp.resType[1] = ExtendedOpcode::ReturnTypeY.Get(OpcodeTokenN);
retOp.resType[2] = ExtendedOpcode::ReturnTypeZ.Get(OpcodeTokenN);
@@ -2247,22 +2923,33 @@ bool Program::DecodeOperation(uint32_t *&tokenStream, Operation &retOp, bool fri
retOp.str += retOp.operands[i].toString(m_Reflection, flags | ToString::ShowSwizzle);
}
uint32_t consumedTokens = (uint32_t)(tokenStream - begin);
if(consumedTokens + 1 == opLength && begin[consumedTokens] == 0x0)
{
retOp.flags = Operation::Flags(retOp.flags | Operation::FLAG_TRAILING_ZERO_TOKEN);
RDCDEBUG("Consuming extra unused 0 dword");
tokenStream++;
consumedTokens++;
}
#if ENABLED(RDOC_DEVEL)
if((uint32_t)(tokenStream - begin) > opLength)
if(consumedTokens > opLength)
{
RDCERR("Consumed too many tokens for %d!", retOp.operation);
// try to recover by rewinding the stream, this instruction will be garbage but at least the
// next ones will be correct
uint32_t overread = (uint32_t)(tokenStream - begin) - opLength;
uint32_t overread = consumedTokens - opLength;
tokenStream -= overread;
}
else if((uint32_t)(tokenStream - begin) < opLength)
else if(consumedTokens < opLength)
{
// sometimes this just happens, which is why we only print this in non-release so we can
// inspect it. There's probably not much we can do though, it's just magic.
RDCWARN("Consumed too few tokens for %d!", retOp.operation);
uint32_t missing = opLength - (uint32_t)(tokenStream - begin);
uint32_t missing = opLength - consumedTokens;
for(uint32_t i = 0; i < missing; i++)
{
RDCLOG("missing token %d: 0x%08x", i, tokenStream[0]);
@@ -165,6 +165,7 @@ static MaskedElement<DXBC::ResourceRetType, 0x00000F00> ReturnTypeZ;
static MaskedElement<DXBC::ResourceRetType, 0x0000F000> ReturnTypeW;
// OPCODE_DCL_INPUT_PS
// OPCODE_DCL_INPUT_PS_SIV
static MaskedElement<InterpolationMode, 0x00007800> InterpolationMode;
// OPCODE_DCL_INPUT_CONTROL_POINT_COUNT
@@ -652,6 +652,66 @@ void DXBCContainer::FillStateInstructionInfo(ShaderDebugState &state) const
}
}
void DXBCContainer::ReplaceProgram(bytebuf &ByteCode, DXBCBytecode::Program *program)
{
FileHeader *header = (FileHeader *)ByteCode.data();
if(header->fourcc != FOURCC_DXBC)
return;
if(header->fileLength != (uint32_t)ByteCode.size())
return;
uint32_t *chunkOffsets =
(uint32_t *)(ByteCode.data() + sizeof(FileHeader)); // right after the header
rdcarray<uint32_t> replacement = program->EncodeProgram();
for(uint32_t chunkIdx = 0; chunkIdx < header->numChunks; chunkIdx++)
{
uint32_t offs = chunkOffsets[chunkIdx];
uint32_t *fourcc = (uint32_t *)(ByteCode.data() + offs);
uint32_t *chunkSize = (uint32_t *)(fourcc + 1);
if(*fourcc == FOURCC_SHEX || *fourcc == FOURCC_SHDR)
{
int32_t diff = int32_t(replacement.byteSize()) - int32_t(*chunkSize);
*chunkSize = (uint32_t)replacement.byteSize();
if(diff == 0)
{
memcpy(ByteCode.data() + offs + 8, replacement.data(), replacement.byteSize());
}
else if(diff > 0)
{
const byte *replaceBytes = (const byte *)replacement.data();
ByteCode.insert(offs + 8, replaceBytes, diff);
memcpy(ByteCode.data() + offs + 8 + diff, replaceBytes + diff, replacement.byteSize() - diff);
}
else if(diff < 0)
{
ByteCode.erase(offs + 8, -diff);
memcpy(ByteCode.data() + offs + 8, replacement.data(), replacement.byteSize());
}
// fixup offsets of chunks after this point
header = (FileHeader *)ByteCode.data();
chunkOffsets = (uint32_t *)(ByteCode.data() + sizeof(FileHeader));
header->fileLength += diff;
chunkIdx++;
for(; chunkIdx < header->numChunks; chunkIdx++)
chunkOffsets[chunkIdx] += diff;
break;
}
}
}
void DXBCContainer::GetHash(uint32_t hash[4], const void *ByteCode, size_t BytecodeLength)
{
if(BytecodeLength < sizeof(FileHeader))
@@ -146,6 +146,10 @@ public:
DXBCContainer(bytebuf &ByteCode, const rdcstr &debugInfoPath, GraphicsAPI api,
uint32_t shaderExtReg, uint32_t shaderExtSpace);
~DXBCContainer();
DXBCContainer(const DXBCContainer &o) = delete;
DXBCContainer(DXBCContainer &&o) = delete;
DXBCContainer &operator=(const DXBCContainer &o) = delete;
DXBC::ShaderType m_Type = DXBC::ShaderType::Max;
struct
{
@@ -162,6 +166,8 @@ public:
void FillTraceLineInfo(ShaderDebugTrace &trace) const;
void FillStateInstructionInfo(ShaderDebugState &state) const;
static void ReplaceProgram(bytebuf &ByteCode, DXBCBytecode::Program *program);
const DXBCBytecode::Program *GetDXBCByteCode() const { return m_DXBCByteCode; }
DXBCBytecode::Program *GetDXBCByteCode() { return m_DXBCByteCode; }
const DXIL::Program *GetDXILByteCode() { return m_DXILByteCode; }
@@ -178,9 +184,6 @@ public:
static rdcstr GetDebugBinaryPath(const void *ByteCode, size_t ByteCodeLength);
private:
DXBCContainer(const DXBCContainer &o);
DXBCContainer &operator=(const DXBCContainer &o);
void TryFetchSeparateDebugInfo(bytebuf &byteCode, const rdcstr &debugInfoPath);
bytebuf m_DebugShaderBlob;
+1 -1
View File
@@ -4856,7 +4856,7 @@ DXBCBytecode::InterpolationMode GetInterpolationModeForInputParam(const SigParam
if(decl.declaration == DXBCBytecode::OPCODE_DCL_INPUT_PS &&
decl.operand.indices[0].absolute && decl.operand.indices[0].index == sig.regIndex)
{
interpolation = decl.inputInterpolation;
interpolation = decl.inputOutput.inputInterpolation;
break;
}
}
@@ -497,8 +497,9 @@ class Program : public DXBC::IDebugInfo
{
public:
Program(const byte *bytes, size_t length);
Program(const Program &o) = default;
Program &operator=(const Program &o) = default;
Program(const Program &o) = delete;
Program(Program &&o) = delete;
Program &operator=(const Program &o) = delete;
virtual ~Program() {}
static bool Valid(const byte *bytes, size_t length);