diff --git a/renderdoc/driver/shaders/dxbc/dxbc_bytecode.cpp b/renderdoc/driver/shaders/dxbc/dxbc_bytecode.cpp index d727f4275..1c0261415 100644 --- a/renderdoc/driver/shaders/dxbc/dxbc_bytecode.cpp +++ b/renderdoc/driver/shaders/dxbc/dxbc_bytecode.cpp @@ -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; diff --git a/renderdoc/driver/shaders/dxbc/dxbc_bytecode.h b/renderdoc/driver/shaders/dxbc/dxbc_bytecode.h index f21a1944c..872bf6c4e 100644 --- a/renderdoc/driver/shaders/dxbc/dxbc_bytecode.h +++ b/renderdoc/driver/shaders/dxbc/dxbc_bytecode.h @@ -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 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 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 &tokenStream, const Operand &oper); + void EncodeDecl(rdcarray &tokenStream, const Declaration &decl); + void EncodeOperation(rdcarray &tokenStream, const Operation &op); }; }; diff --git a/renderdoc/driver/shaders/dxbc/dxbc_bytecode_ops.cpp b/renderdoc/driver/shaders/dxbc/dxbc_bytecode_ops.cpp index 14c25156d..72d10afb5 100644 --- a/renderdoc/driver/shaders/dxbc/dxbc_bytecode_ops.cpp +++ b/renderdoc/driver/shaders/dxbc/dxbc_bytecode_ops.cpp @@ -791,6 +791,45 @@ void Program::DecodeProgram() PostprocessVendorExtensions(); } +rdcarray Program::EncodeProgram() +{ + rdcarray 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 &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 &tokenStream, const Declaration &decl) +{ + rdcarray 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 &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 &tokenStream, const Operation &op) +{ + rdcarray 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 &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]); diff --git a/renderdoc/driver/shaders/dxbc/dxbc_bytecode_ops.h b/renderdoc/driver/shaders/dxbc/dxbc_bytecode_ops.h index a422749ae..977ae2449 100644 --- a/renderdoc/driver/shaders/dxbc/dxbc_bytecode_ops.h +++ b/renderdoc/driver/shaders/dxbc/dxbc_bytecode_ops.h @@ -165,6 +165,7 @@ static MaskedElement ReturnTypeZ; static MaskedElement ReturnTypeW; // OPCODE_DCL_INPUT_PS +// OPCODE_DCL_INPUT_PS_SIV static MaskedElement InterpolationMode; // OPCODE_DCL_INPUT_CONTROL_POINT_COUNT diff --git a/renderdoc/driver/shaders/dxbc/dxbc_container.cpp b/renderdoc/driver/shaders/dxbc/dxbc_container.cpp index 1c175fc17..2d455cc9e 100644 --- a/renderdoc/driver/shaders/dxbc/dxbc_container.cpp +++ b/renderdoc/driver/shaders/dxbc/dxbc_container.cpp @@ -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 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)) diff --git a/renderdoc/driver/shaders/dxbc/dxbc_container.h b/renderdoc/driver/shaders/dxbc/dxbc_container.h index ec436949e..f4431e808 100644 --- a/renderdoc/driver/shaders/dxbc/dxbc_container.h +++ b/renderdoc/driver/shaders/dxbc/dxbc_container.h @@ -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; diff --git a/renderdoc/driver/shaders/dxbc/dxbc_debug.cpp b/renderdoc/driver/shaders/dxbc/dxbc_debug.cpp index 1c2981df2..50a1bf72d 100644 --- a/renderdoc/driver/shaders/dxbc/dxbc_debug.cpp +++ b/renderdoc/driver/shaders/dxbc/dxbc_debug.cpp @@ -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; } } diff --git a/renderdoc/driver/shaders/dxil/dxil_bytecode.h b/renderdoc/driver/shaders/dxil/dxil_bytecode.h index 668d17711..8e97cafd9 100644 --- a/renderdoc/driver/shaders/dxil/dxil_bytecode.h +++ b/renderdoc/driver/shaders/dxil/dxil_bytecode.h @@ -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);