mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-05-05 09:30:44 +00:00
f79ced7a21
* This still is subject to the general limitation that RenderDoc only simulates one thread or at most one pixel quad.
846 lines
27 KiB
C++
846 lines
27 KiB
C++
/******************************************************************************
|
|
* The MIT License (MIT)
|
|
*
|
|
* Copyright (c) 2019-2020 Baldur Karlsson
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
* THE SOFTWARE.
|
|
******************************************************************************/
|
|
|
|
#include "dxbc_bytecode.h"
|
|
#include "common/formatting.h"
|
|
#include "os/os_specific.h"
|
|
#include "dxbc_container.h"
|
|
|
|
static ShaderVariable makeReg(rdcstr name)
|
|
{
|
|
ShaderVariable ret(name, 0U, 0U, 0U, 0U);
|
|
ret.type = VarType::Unknown;
|
|
return ret;
|
|
}
|
|
|
|
namespace DXBCBytecode
|
|
{
|
|
Program::Program(const byte *bytes, size_t length)
|
|
{
|
|
RDCASSERT((length % 4) == 0);
|
|
m_HexDump.resize(length / 4);
|
|
memcpy(m_HexDump.data(), bytes, length);
|
|
|
|
FetchTypeVersion();
|
|
}
|
|
|
|
void HandleResourceArrayIndices(const rdcarray<DXBCBytecode::RegIndex> &indices,
|
|
DXBC::ShaderInputBind &desc)
|
|
{
|
|
// If there are 3 indices, we're using SM5.1 and this binding may be a resource array
|
|
if(indices.size() == 3)
|
|
{
|
|
// With SM5.1, the first index is the logical identifier,
|
|
// and the 2nd index is the starting shader register
|
|
desc.reg = (uint32_t)indices[1].index;
|
|
|
|
// Start/end registers are inclusive, so one resource will have the same start/end register
|
|
desc.bindCount = uint32_t(indices[2].index - indices[1].index + 1);
|
|
|
|
// If it's an unbounded resource array, mark the bind count as ~0U
|
|
if(indices[2].index == 0xffffffff)
|
|
desc.bindCount = ~0U;
|
|
}
|
|
}
|
|
|
|
DXBC::Reflection *Program::GuessReflection()
|
|
{
|
|
DisassembleHexDump();
|
|
|
|
// we don't store this, since it's just a guess. If our m_Reflection is NULL that indicates no
|
|
// useful reflection is present
|
|
DXBC::Reflection *ret = new DXBC::Reflection;
|
|
|
|
for(size_t i = 0; i < m_Declarations.size(); i++)
|
|
{
|
|
Declaration &dcl = m_Declarations[i];
|
|
|
|
switch(dcl.declaration)
|
|
{
|
|
case OPCODE_DCL_SAMPLER:
|
|
{
|
|
DXBC::ShaderInputBind desc;
|
|
|
|
RDCASSERT(dcl.operand.type == TYPE_SAMPLER);
|
|
RDCASSERT(dcl.operand.indices.size() == 1 || dcl.operand.indices.size() == 3);
|
|
RDCASSERT(dcl.operand.indices[0].absolute);
|
|
|
|
uint32_t idx = (uint32_t)dcl.operand.indices[0].index;
|
|
|
|
desc.name = StringFormat::Fmt("sampler%u", idx);
|
|
desc.type = DXBC::ShaderInputBind::TYPE_SAMPLER;
|
|
desc.space = dcl.space;
|
|
desc.reg = idx;
|
|
desc.bindCount = 1;
|
|
desc.retType = DXBC::RETURN_TYPE_UNKNOWN;
|
|
desc.dimension = DXBC::ShaderInputBind::DIM_UNKNOWN;
|
|
desc.numComps = 0;
|
|
|
|
HandleResourceArrayIndices(dcl.operand.indices, desc);
|
|
|
|
ret->Samplers.push_back(desc);
|
|
|
|
break;
|
|
}
|
|
case OPCODE_DCL_RESOURCE:
|
|
{
|
|
DXBC::ShaderInputBind desc;
|
|
|
|
RDCASSERT(dcl.operand.type == TYPE_RESOURCE);
|
|
RDCASSERT(dcl.operand.indices.size() == 1 || dcl.operand.indices.size() == 3);
|
|
RDCASSERT(dcl.operand.indices[0].absolute);
|
|
|
|
uint32_t idx = (uint32_t)dcl.operand.indices[0].index;
|
|
|
|
desc.name = StringFormat::Fmt("texture%u", idx);
|
|
desc.type = DXBC::ShaderInputBind::TYPE_TEXTURE;
|
|
desc.space = dcl.space;
|
|
desc.reg = idx;
|
|
desc.bindCount = 1;
|
|
desc.retType = dcl.resType[0];
|
|
|
|
switch(dcl.dim)
|
|
{
|
|
case RESOURCE_DIMENSION_BUFFER: desc.dimension = DXBC::ShaderInputBind::DIM_BUFFER; break;
|
|
case RESOURCE_DIMENSION_TEXTURE1D:
|
|
desc.dimension = DXBC::ShaderInputBind::DIM_TEXTURE1D;
|
|
break;
|
|
case RESOURCE_DIMENSION_TEXTURE2D:
|
|
desc.dimension = DXBC::ShaderInputBind::DIM_TEXTURE2D;
|
|
break;
|
|
case RESOURCE_DIMENSION_TEXTURE3D:
|
|
desc.dimension = DXBC::ShaderInputBind::DIM_TEXTURE3D;
|
|
break;
|
|
case RESOURCE_DIMENSION_TEXTURECUBE:
|
|
desc.dimension = DXBC::ShaderInputBind::DIM_TEXTURECUBE;
|
|
break;
|
|
case RESOURCE_DIMENSION_TEXTURE1DARRAY:
|
|
desc.dimension = DXBC::ShaderInputBind::DIM_TEXTURE1DARRAY;
|
|
break;
|
|
case RESOURCE_DIMENSION_TEXTURE2DARRAY:
|
|
desc.dimension = DXBC::ShaderInputBind::DIM_TEXTURE2DARRAY;
|
|
break;
|
|
case RESOURCE_DIMENSION_TEXTURECUBEARRAY:
|
|
desc.dimension = DXBC::ShaderInputBind::DIM_TEXTURECUBEARRAY;
|
|
break;
|
|
case RESOURCE_DIMENSION_TEXTURE2DMS:
|
|
desc.dimension = DXBC::ShaderInputBind::DIM_TEXTURE2DMS;
|
|
break;
|
|
case RESOURCE_DIMENSION_TEXTURE2DMSARRAY:
|
|
desc.dimension = DXBC::ShaderInputBind::DIM_TEXTURE2DMSARRAY;
|
|
break;
|
|
default: desc.dimension = DXBC::ShaderInputBind::DIM_UNKNOWN; break;
|
|
}
|
|
// can't tell, fxc seems to default to 4
|
|
desc.numComps = 4;
|
|
|
|
RDCASSERT(desc.dimension != DXBC::ShaderInputBind::DIM_UNKNOWN);
|
|
|
|
HandleResourceArrayIndices(dcl.operand.indices, desc);
|
|
|
|
ret->SRVs.push_back(desc);
|
|
|
|
break;
|
|
}
|
|
case OPCODE_DCL_UNORDERED_ACCESS_VIEW_RAW:
|
|
case OPCODE_DCL_RESOURCE_RAW:
|
|
{
|
|
DXBC::ShaderInputBind desc;
|
|
|
|
RDCASSERT(dcl.operand.type == TYPE_RESOURCE ||
|
|
dcl.operand.type == TYPE_UNORDERED_ACCESS_VIEW);
|
|
RDCASSERT(dcl.operand.indices.size() == 1 || dcl.operand.indices.size() == 3);
|
|
RDCASSERT(dcl.operand.indices[0].absolute);
|
|
|
|
uint32_t idx = (uint32_t)dcl.operand.indices[0].index;
|
|
|
|
desc.name =
|
|
StringFormat::Fmt("%sbytebuffer%u", dcl.operand.type != TYPE_RESOURCE ? "rw" : "", idx);
|
|
desc.type = dcl.operand.type == TYPE_RESOURCE
|
|
? DXBC::ShaderInputBind::TYPE_BYTEADDRESS
|
|
: DXBC::ShaderInputBind::TYPE_UAV_RWBYTEADDRESS;
|
|
desc.space = dcl.space;
|
|
desc.reg = idx;
|
|
desc.bindCount = 1;
|
|
desc.retType = DXBC::RETURN_TYPE_MIXED;
|
|
desc.dimension = DXBC::ShaderInputBind::DIM_BUFFER;
|
|
desc.numComps = 0;
|
|
|
|
HandleResourceArrayIndices(dcl.operand.indices, desc);
|
|
|
|
if(dcl.operand.type == TYPE_RESOURCE)
|
|
ret->SRVs.push_back(desc);
|
|
else
|
|
ret->UAVs.push_back(desc);
|
|
|
|
break;
|
|
}
|
|
case OPCODE_DCL_RESOURCE_STRUCTURED:
|
|
{
|
|
DXBC::ShaderInputBind desc;
|
|
|
|
RDCASSERT(dcl.operand.type == TYPE_RESOURCE);
|
|
RDCASSERT(dcl.operand.indices.size() == 1 || dcl.operand.indices.size() == 3);
|
|
RDCASSERT(dcl.operand.indices[0].absolute);
|
|
|
|
uint32_t idx = (uint32_t)dcl.operand.indices[0].index;
|
|
|
|
desc.name = StringFormat::Fmt("structuredbuffer%u", idx);
|
|
desc.type = DXBC::ShaderInputBind::TYPE_STRUCTURED;
|
|
desc.space = dcl.space;
|
|
desc.reg = idx;
|
|
desc.bindCount = 1;
|
|
desc.retType = DXBC::RETURN_TYPE_MIXED;
|
|
desc.dimension = DXBC::ShaderInputBind::DIM_BUFFER;
|
|
desc.numComps = dcl.stride;
|
|
|
|
HandleResourceArrayIndices(dcl.operand.indices, desc);
|
|
|
|
ret->SRVs.push_back(desc);
|
|
|
|
break;
|
|
}
|
|
case OPCODE_DCL_UNORDERED_ACCESS_VIEW_STRUCTURED:
|
|
{
|
|
DXBC::ShaderInputBind desc;
|
|
|
|
RDCASSERT(dcl.operand.type == TYPE_UNORDERED_ACCESS_VIEW);
|
|
RDCASSERT(dcl.operand.indices.size() == 1 || dcl.operand.indices.size() == 3);
|
|
RDCASSERT(dcl.operand.indices[0].absolute);
|
|
|
|
uint32_t idx = (uint32_t)dcl.operand.indices[0].index;
|
|
|
|
desc.name = StringFormat::Fmt("uav%u", idx);
|
|
desc.type =
|
|
DXBC::ShaderInputBind::TYPE_UAV_RWSTRUCTURED; // doesn't seem to be anything that
|
|
// determines append vs consume vs
|
|
// rwstructured
|
|
if(dcl.hasCounter)
|
|
desc.type = DXBC::ShaderInputBind::TYPE_UAV_RWSTRUCTURED_WITH_COUNTER;
|
|
desc.space = dcl.space;
|
|
desc.reg = idx;
|
|
desc.bindCount = 1;
|
|
desc.retType = DXBC::RETURN_TYPE_MIXED;
|
|
desc.dimension = DXBC::ShaderInputBind::DIM_BUFFER;
|
|
desc.numComps = dcl.stride;
|
|
|
|
HandleResourceArrayIndices(dcl.operand.indices, desc);
|
|
|
|
ret->UAVs.push_back(desc);
|
|
|
|
break;
|
|
}
|
|
case OPCODE_DCL_UNORDERED_ACCESS_VIEW_TYPED:
|
|
{
|
|
DXBC::ShaderInputBind desc;
|
|
|
|
RDCASSERT(dcl.operand.type == TYPE_UNORDERED_ACCESS_VIEW);
|
|
RDCASSERT(dcl.operand.indices.size() == 1 || dcl.operand.indices.size() == 3);
|
|
RDCASSERT(dcl.operand.indices[0].absolute);
|
|
|
|
uint32_t idx = (uint32_t)dcl.operand.indices[0].index;
|
|
|
|
desc.name = StringFormat::Fmt("uav%u", idx);
|
|
desc.type = DXBC::ShaderInputBind::TYPE_UAV_RWTYPED;
|
|
desc.space = dcl.space;
|
|
desc.reg = idx;
|
|
desc.bindCount = 1;
|
|
desc.retType = dcl.resType[0];
|
|
|
|
switch(dcl.dim)
|
|
{
|
|
case RESOURCE_DIMENSION_BUFFER: desc.dimension = DXBC::ShaderInputBind::DIM_BUFFER; break;
|
|
case RESOURCE_DIMENSION_TEXTURE1D:
|
|
desc.dimension = DXBC::ShaderInputBind::DIM_TEXTURE1D;
|
|
break;
|
|
case RESOURCE_DIMENSION_TEXTURE2D:
|
|
desc.dimension = DXBC::ShaderInputBind::DIM_TEXTURE2D;
|
|
break;
|
|
case RESOURCE_DIMENSION_TEXTURE3D:
|
|
desc.dimension = DXBC::ShaderInputBind::DIM_TEXTURE3D;
|
|
break;
|
|
case RESOURCE_DIMENSION_TEXTURECUBE:
|
|
desc.dimension = DXBC::ShaderInputBind::DIM_TEXTURECUBE;
|
|
break;
|
|
case RESOURCE_DIMENSION_TEXTURE1DARRAY:
|
|
desc.dimension = DXBC::ShaderInputBind::DIM_TEXTURE1DARRAY;
|
|
break;
|
|
case RESOURCE_DIMENSION_TEXTURE2DARRAY:
|
|
desc.dimension = DXBC::ShaderInputBind::DIM_TEXTURE2DARRAY;
|
|
break;
|
|
case RESOURCE_DIMENSION_TEXTURECUBEARRAY:
|
|
desc.dimension = DXBC::ShaderInputBind::DIM_TEXTURECUBEARRAY;
|
|
break;
|
|
case RESOURCE_DIMENSION_TEXTURE2DMS:
|
|
desc.dimension = DXBC::ShaderInputBind::DIM_TEXTURE2DMS;
|
|
break;
|
|
case RESOURCE_DIMENSION_TEXTURE2DMSARRAY:
|
|
desc.dimension = DXBC::ShaderInputBind::DIM_TEXTURE2DMSARRAY;
|
|
break;
|
|
default: desc.dimension = DXBC::ShaderInputBind::DIM_UNKNOWN; break;
|
|
}
|
|
desc.numComps = 4;
|
|
|
|
HandleResourceArrayIndices(dcl.operand.indices, desc);
|
|
|
|
ret->UAVs.push_back(desc);
|
|
|
|
break;
|
|
}
|
|
case OPCODE_DCL_CONSTANT_BUFFER:
|
|
{
|
|
DXBC::ShaderInputBind desc;
|
|
|
|
RDCASSERT(dcl.operand.type == TYPE_CONSTANT_BUFFER);
|
|
RDCASSERT(dcl.operand.indices.size() == 2 || dcl.operand.indices.size() == 3);
|
|
RDCASSERT(dcl.operand.indices[0].absolute && dcl.operand.indices[1].absolute);
|
|
|
|
// Constant buffer declarations differ between SM5 and SM5.1. For SM5.1, the indices are
|
|
// logical identifier, start shader register, and end shader register. Register space and
|
|
// buffer size are stored elsewhere in the declaration. For SM5 and earlier, the indices
|
|
// are the shader register and buffer size (measured in float4's)
|
|
bool isShaderModel51 = IsShaderModel51();
|
|
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.float4size : (uint32_t)dcl.operand.indices[1].index;
|
|
|
|
desc.name = StringFormat::Fmt("cbuffer%u", idx);
|
|
desc.type = DXBC::ShaderInputBind::TYPE_CBUFFER;
|
|
desc.space = dcl.space;
|
|
desc.reg = reg;
|
|
desc.bindCount = 1;
|
|
desc.retType = DXBC::RETURN_TYPE_UNKNOWN;
|
|
desc.dimension = DXBC::ShaderInputBind::DIM_UNKNOWN;
|
|
desc.numComps = 0;
|
|
|
|
HandleResourceArrayIndices(dcl.operand.indices, desc);
|
|
|
|
DXBC::CBuffer cb;
|
|
|
|
cb.name = desc.name;
|
|
|
|
cb.identifier = idx;
|
|
cb.space = dcl.space;
|
|
cb.reg = reg;
|
|
cb.bindCount = desc.bindCount;
|
|
|
|
cb.descriptor.byteSize = numVecs * 4 * sizeof(float);
|
|
cb.descriptor.type = DXBC::CBuffer::Descriptor::TYPE_CBUFFER;
|
|
|
|
bool isArray = desc.bindCount > 1;
|
|
if(isArray)
|
|
{
|
|
// If the constant buffer is an array, then we need to add an entry for the struct
|
|
// itself. This mimics what is loaded for a constant buffer array when reflection
|
|
// information is not stripped.
|
|
DXBC::CBufferVariable var;
|
|
var.name = cb.name;
|
|
var.offset = 0;
|
|
var.type.descriptor.varClass = DXBC::VariableClass::CLASS_STRUCT;
|
|
var.type.descriptor.varType = VarType::Unknown;
|
|
var.type.descriptor.rows = 1;
|
|
var.type.descriptor.cols = 4;
|
|
var.type.descriptor.elements = 1;
|
|
var.type.descriptor.bytesize = 4 * sizeof(float);
|
|
var.type.descriptor.name = "struct";
|
|
cb.variables.push_back(var);
|
|
}
|
|
rdcarray<DXBC::CBufferVariable> &fillVars =
|
|
isArray ? cb.variables[0].type.members : cb.variables;
|
|
fillVars.reserve(numVecs);
|
|
|
|
for(uint32_t v = 0; v < numVecs; v++)
|
|
{
|
|
DXBC::CBufferVariable var;
|
|
|
|
var.name = StringFormat::Fmt("cb%u_v%u", cb.identifier, v);
|
|
|
|
var.offset = 4 * sizeof(float) * v;
|
|
|
|
var.type.descriptor.bytesize = 4 * sizeof(float);
|
|
var.type.descriptor.rows = 1;
|
|
var.type.descriptor.cols = 4;
|
|
var.type.descriptor.elements = 0;
|
|
var.type.descriptor.varType = VarType::Float;
|
|
var.type.descriptor.varClass = DXBC::CLASS_VECTOR;
|
|
var.type.descriptor.name = TypeName(var.type.descriptor);
|
|
|
|
fillVars.push_back(var);
|
|
}
|
|
|
|
ret->CBuffers.push_back(cb);
|
|
|
|
break;
|
|
}
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
rdcstr Program::GetDebugStatus()
|
|
{
|
|
// if there are no vendor extensions this is always debuggable
|
|
if(m_ShaderExt.second == ~0U)
|
|
return rdcstr();
|
|
|
|
// otherwise we need to check that no unsupported vendor extensions are used
|
|
DisassembleHexDump();
|
|
|
|
for(const Operation &op : m_Instructions)
|
|
{
|
|
if(op.operation >= OPCODE_VENDOR_FIRST)
|
|
{
|
|
bool supported = false;
|
|
|
|
// whitelist supported instructions here
|
|
switch(op.operation)
|
|
{
|
|
case OPCODE_AMD_U64_ATOMIC:
|
|
case OPCODE_NV_U64_ATOMIC: supported = true; break;
|
|
default: break;
|
|
}
|
|
|
|
if(!supported)
|
|
return StringFormat::Fmt("Unsupported shader extension '%s' used",
|
|
ToStr(op.operation).c_str());
|
|
}
|
|
}
|
|
|
|
// no unsupported instructions used
|
|
return rdcstr();
|
|
}
|
|
|
|
D3D_PRIMITIVE_TOPOLOGY Program::GetOutputTopology()
|
|
{
|
|
DisassembleHexDump();
|
|
|
|
if(m_Type != DXBC::ShaderType::Geometry && m_Type != DXBC::ShaderType::Domain)
|
|
return D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
|
|
|
|
for(const DXBCBytecode::Declaration &decl : m_Declarations)
|
|
{
|
|
if(decl.declaration == DXBCBytecode::OPCODE_DCL_GS_OUTPUT_PRIMITIVE_TOPOLOGY)
|
|
return decl.outTopology;
|
|
if(decl.declaration == DXBCBytecode::OPCODE_DCL_TESS_DOMAIN)
|
|
{
|
|
if(decl.domain == DXBCBytecode::DOMAIN_ISOLINE)
|
|
return D3D_PRIMITIVE_TOPOLOGY_LINELIST;
|
|
else
|
|
return D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
|
|
}
|
|
|
|
void Program::SetupRegisterFile(rdcarray<ShaderVariable> ®isters) const
|
|
{
|
|
size_t numRegisters = m_NumTemps + m_IndexTempSizes.size() + m_NumOutputs;
|
|
|
|
if(m_OutputDepth)
|
|
numRegisters++;
|
|
if(m_OutputStencil)
|
|
numRegisters++;
|
|
if(m_OutputCoverage)
|
|
numRegisters++;
|
|
|
|
registers.reserve(numRegisters);
|
|
|
|
for(uint32_t i = 0; i < m_NumTemps; i++)
|
|
registers.push_back(makeReg(GetRegisterName(TYPE_TEMP, i)));
|
|
|
|
for(size_t i = 0; i < m_IndexTempSizes.size(); i++)
|
|
{
|
|
registers.push_back(makeReg(GetRegisterName(TYPE_INDEXABLE_TEMP, (uint32_t)i)));
|
|
registers.back().members.resize(m_IndexTempSizes[i]);
|
|
for(uint32_t t = 0; t < m_IndexTempSizes[i]; t++)
|
|
registers.back().members[t] = makeReg(StringFormat::Fmt("[%u]", t));
|
|
}
|
|
|
|
for(uint32_t i = 0; i < m_NumOutputs; i++)
|
|
registers.push_back(makeReg(rdcstr()));
|
|
|
|
// this could be oDepthGE or oDepthLE, that will be fixed up when the external code sets up the
|
|
// names etc of all outputs with reflection info
|
|
if(m_OutputDepth)
|
|
registers.push_back(makeReg(rdcstr()));
|
|
if(m_OutputStencil)
|
|
registers.push_back(makeReg(rdcstr()));
|
|
if(m_OutputCoverage)
|
|
registers.push_back(makeReg(rdcstr()));
|
|
}
|
|
|
|
uint32_t Program::GetRegisterIndex(OperandType type, uint32_t index) const
|
|
{
|
|
if(type == TYPE_TEMP)
|
|
{
|
|
RDCASSERT(index < m_NumTemps, index, m_NumTemps);
|
|
return index;
|
|
}
|
|
else if(type == TYPE_INDEXABLE_TEMP)
|
|
{
|
|
RDCASSERT(index < m_IndexTempSizes.size(), index, m_IndexTempSizes.size());
|
|
return m_NumTemps + index;
|
|
}
|
|
else if(type == TYPE_OUTPUT)
|
|
{
|
|
RDCASSERT(index < m_NumOutputs, index, m_NumOutputs);
|
|
return m_NumTemps + (uint32_t)m_IndexTempSizes.size() + index;
|
|
}
|
|
else if(type == TYPE_OUTPUT_DEPTH)
|
|
{
|
|
RDCASSERT(m_OutputDepth);
|
|
return m_NumTemps + (uint32_t)m_IndexTempSizes.size() + m_NumOutputs;
|
|
}
|
|
else if(type == TYPE_OUTPUT_STENCIL_REF)
|
|
{
|
|
RDCASSERT(m_OutputStencil);
|
|
return m_NumTemps + (uint32_t)m_IndexTempSizes.size() + m_NumOutputs + (m_OutputDepth ? 1 : 0);
|
|
}
|
|
else if(type == TYPE_OUTPUT_COVERAGE_MASK)
|
|
{
|
|
RDCASSERT(m_OutputCoverage);
|
|
return m_NumTemps + (uint32_t)m_IndexTempSizes.size() + m_NumOutputs + (m_OutputDepth ? 1 : 0) +
|
|
(m_OutputStencil ? 1 : 0);
|
|
}
|
|
|
|
RDCERR("Unexpected type for register index: %s", ToStr(type).c_str());
|
|
|
|
return ~0U;
|
|
}
|
|
|
|
rdcstr Program::GetRegisterName(OperandType oper, uint32_t index) const
|
|
{
|
|
if(oper == TYPE_TEMP)
|
|
return StringFormat::Fmt("r%u", index);
|
|
else if(oper == TYPE_INDEXABLE_TEMP)
|
|
return StringFormat::Fmt("x%u", index);
|
|
else if(oper == TYPE_INPUT)
|
|
return StringFormat::Fmt("v%u", index);
|
|
else if(oper == TYPE_CONSTANT_BUFFER)
|
|
return StringFormat::Fmt("%s%u", IsShaderModel51() ? "CB" : "cb", index);
|
|
else if(oper == TYPE_OUTPUT)
|
|
return StringFormat::Fmt("o%u", index);
|
|
else if(oper == TYPE_OUTPUT_DEPTH)
|
|
return "oDepth";
|
|
else if(oper == TYPE_OUTPUT_DEPTH_LESS_EQUAL)
|
|
return "oDepthLessEqual";
|
|
else if(oper == TYPE_OUTPUT_DEPTH_GREATER_EQUAL)
|
|
return "oDepthGreaterEqual";
|
|
else if(oper == TYPE_OUTPUT_COVERAGE_MASK)
|
|
return "oMask";
|
|
else if(oper == TYPE_OUTPUT_STENCIL_REF)
|
|
return "oStencilRef";
|
|
else if(oper == TYPE_OUTPUT_CONTROL_POINT_ID)
|
|
return "vOutputControlPointID";
|
|
else if(oper == TYPE_INPUT_DOMAIN_POINT)
|
|
return "vDomain";
|
|
else if(oper == TYPE_INPUT_PRIMITIVEID)
|
|
return "vPrim";
|
|
else if(oper == TYPE_INPUT_COVERAGE_MASK)
|
|
return "vCoverageMask";
|
|
else if(oper == TYPE_INPUT_GS_INSTANCE_ID)
|
|
return "vGSInstanceID";
|
|
else if(oper == TYPE_INPUT_THREAD_ID)
|
|
return "vThreadID";
|
|
else if(oper == TYPE_INPUT_THREAD_GROUP_ID)
|
|
return "vThreadGroupID";
|
|
else if(oper == TYPE_INPUT_THREAD_ID_IN_GROUP)
|
|
return "vThreadIDInGroup";
|
|
else if(oper == TYPE_INPUT_THREAD_ID_IN_GROUP_FLATTENED)
|
|
return "vThreadIDInGroupFlattened";
|
|
else if(oper == TYPE_INPUT_FORK_INSTANCE_ID)
|
|
return "vForkInstanceID";
|
|
else if(oper == TYPE_INPUT_JOIN_INSTANCE_ID)
|
|
return "vJoinInstanceID";
|
|
|
|
RDCERR("Unknown register requiring name: %s", ToStr(oper).c_str());
|
|
return "??";
|
|
}
|
|
|
|
// see http://msdn.microsoft.com/en-us/library/windows/desktop/bb219840(v=vs.85).aspx
|
|
// for details of these opcodes
|
|
size_t NumOperands(OpcodeType op)
|
|
{
|
|
switch(op)
|
|
{
|
|
case OPCODE_BREAK:
|
|
case OPCODE_CONTINUE:
|
|
case OPCODE_CUT:
|
|
case OPCODE_DEFAULT:
|
|
case OPCODE_ELSE:
|
|
case OPCODE_EMIT:
|
|
case OPCODE_EMITTHENCUT:
|
|
case OPCODE_ENDIF:
|
|
case OPCODE_ENDLOOP:
|
|
case OPCODE_ENDSWITCH:
|
|
case OPCODE_LOOP:
|
|
case OPCODE_NOP:
|
|
case OPCODE_RET:
|
|
case OPCODE_SYNC:
|
|
case OPCODE_ABORT:
|
|
case OPCODE_DEBUGBREAK:
|
|
|
|
case OPCODE_HS_CONTROL_POINT_PHASE:
|
|
case OPCODE_HS_FORK_PHASE:
|
|
case OPCODE_HS_JOIN_PHASE:
|
|
case OPCODE_HS_DECLS: return 0;
|
|
case OPCODE_BREAKC:
|
|
case OPCODE_CONTINUEC:
|
|
case OPCODE_CALL:
|
|
case OPCODE_CASE:
|
|
case OPCODE_CUT_STREAM:
|
|
case OPCODE_DISCARD:
|
|
case OPCODE_EMIT_STREAM:
|
|
case OPCODE_EMITTHENCUT_STREAM:
|
|
case OPCODE_IF:
|
|
case OPCODE_INTERFACE_CALL:
|
|
case OPCODE_LABEL:
|
|
case OPCODE_RETC:
|
|
case OPCODE_SWITCH: return 1;
|
|
case OPCODE_BFREV:
|
|
case OPCODE_BUFINFO:
|
|
case OPCODE_CALLC:
|
|
case OPCODE_COUNTBITS:
|
|
case OPCODE_DERIV_RTX:
|
|
case OPCODE_DERIV_RTY:
|
|
case OPCODE_DERIV_RTX_COARSE:
|
|
case OPCODE_DERIV_RTX_FINE:
|
|
case OPCODE_DERIV_RTY_COARSE:
|
|
case OPCODE_DERIV_RTY_FINE:
|
|
case OPCODE_DMOV:
|
|
case OPCODE_DTOF:
|
|
case OPCODE_EXP:
|
|
case OPCODE_F32TOF16:
|
|
case OPCODE_F16TOF32:
|
|
case OPCODE_FIRSTBIT_HI:
|
|
case OPCODE_FIRSTBIT_LO:
|
|
case OPCODE_FIRSTBIT_SHI:
|
|
case OPCODE_FRC:
|
|
case OPCODE_FTOD:
|
|
case OPCODE_FTOI:
|
|
case OPCODE_FTOU:
|
|
case OPCODE_IMM_ATOMIC_ALLOC:
|
|
case OPCODE_IMM_ATOMIC_CONSUME:
|
|
case OPCODE_INEG:
|
|
case OPCODE_ITOF:
|
|
case OPCODE_LOG:
|
|
case OPCODE_MOV:
|
|
case OPCODE_NOT:
|
|
case OPCODE_RCP:
|
|
case OPCODE_ROUND_NE:
|
|
case OPCODE_ROUND_NI:
|
|
case OPCODE_ROUND_PI:
|
|
case OPCODE_ROUND_Z:
|
|
case OPCODE_RSQ:
|
|
case OPCODE_SAMPLE_INFO:
|
|
case OPCODE_SQRT:
|
|
case OPCODE_UTOF:
|
|
case OPCODE_EVAL_CENTROID:
|
|
case OPCODE_DRCP:
|
|
case OPCODE_DTOI:
|
|
case OPCODE_DTOU:
|
|
case OPCODE_ITOD:
|
|
case OPCODE_UTOD:
|
|
case OPCODE_CHECK_ACCESS_FULLY_MAPPED: return 2;
|
|
case OPCODE_AND:
|
|
case OPCODE_ADD:
|
|
case OPCODE_ATOMIC_AND:
|
|
case OPCODE_ATOMIC_OR:
|
|
case OPCODE_ATOMIC_XOR:
|
|
case OPCODE_ATOMIC_IADD:
|
|
case OPCODE_ATOMIC_IMAX:
|
|
case OPCODE_ATOMIC_IMIN:
|
|
case OPCODE_ATOMIC_UMAX:
|
|
case OPCODE_ATOMIC_UMIN:
|
|
case OPCODE_DADD:
|
|
case OPCODE_DIV:
|
|
case OPCODE_DP2:
|
|
case OPCODE_DP3:
|
|
case OPCODE_DP4:
|
|
case OPCODE_DEQ:
|
|
case OPCODE_DGE:
|
|
case OPCODE_DLT:
|
|
case OPCODE_DMAX:
|
|
case OPCODE_DMIN:
|
|
case OPCODE_DMUL:
|
|
case OPCODE_DNE:
|
|
case OPCODE_EQ:
|
|
case OPCODE_GE:
|
|
case OPCODE_IADD:
|
|
case OPCODE_IEQ:
|
|
case OPCODE_IGE:
|
|
case OPCODE_ILT:
|
|
case OPCODE_IMAX:
|
|
case OPCODE_IMIN:
|
|
case OPCODE_INE:
|
|
case OPCODE_ISHL:
|
|
case OPCODE_ISHR:
|
|
case OPCODE_LD:
|
|
case OPCODE_LD_RAW:
|
|
case OPCODE_LD_UAV_TYPED:
|
|
case OPCODE_LT:
|
|
case OPCODE_MAX:
|
|
case OPCODE_MIN:
|
|
case OPCODE_MUL:
|
|
case OPCODE_NE:
|
|
case OPCODE_OR:
|
|
case OPCODE_RESINFO:
|
|
case OPCODE_SAMPLE_POS:
|
|
case OPCODE_SINCOS:
|
|
case OPCODE_STORE_RAW:
|
|
case OPCODE_STORE_UAV_TYPED:
|
|
case OPCODE_UGE:
|
|
case OPCODE_ULT:
|
|
case OPCODE_UMAX:
|
|
case OPCODE_UMIN:
|
|
case OPCODE_USHR:
|
|
case OPCODE_XOR:
|
|
case OPCODE_EVAL_SNAPPED:
|
|
case OPCODE_EVAL_SAMPLE_INDEX:
|
|
case OPCODE_DDIV: return 3;
|
|
case OPCODE_ATOMIC_CMP_STORE:
|
|
case OPCODE_DMOVC:
|
|
case OPCODE_GATHER4:
|
|
case OPCODE_IBFE:
|
|
case OPCODE_IMAD:
|
|
case OPCODE_IMM_ATOMIC_IADD:
|
|
case OPCODE_IMM_ATOMIC_AND:
|
|
case OPCODE_IMM_ATOMIC_OR:
|
|
case OPCODE_IMM_ATOMIC_XOR:
|
|
case OPCODE_IMM_ATOMIC_EXCH:
|
|
case OPCODE_IMM_ATOMIC_IMAX:
|
|
case OPCODE_IMM_ATOMIC_IMIN:
|
|
case OPCODE_IMM_ATOMIC_UMAX:
|
|
case OPCODE_IMM_ATOMIC_UMIN:
|
|
case OPCODE_IMUL:
|
|
case OPCODE_LD_MS:
|
|
case OPCODE_LD_STRUCTURED:
|
|
case OPCODE_LOD:
|
|
case OPCODE_MAD:
|
|
case OPCODE_MOVC:
|
|
case OPCODE_SAMPLE:
|
|
case OPCODE_STORE_STRUCTURED:
|
|
case OPCODE_UADDC:
|
|
case OPCODE_UBFE:
|
|
case OPCODE_UDIV:
|
|
case OPCODE_UMAD:
|
|
case OPCODE_UMUL:
|
|
case OPCODE_USUBB:
|
|
case OPCODE_DFMA:
|
|
case OPCODE_MSAD:
|
|
case OPCODE_LD_FEEDBACK:
|
|
case OPCODE_LD_RAW_FEEDBACK:
|
|
case OPCODE_LD_UAV_TYPED_FEEDBACK: return 4;
|
|
case OPCODE_BFI:
|
|
case OPCODE_GATHER4_C:
|
|
case OPCODE_GATHER4_PO:
|
|
case OPCODE_IMM_ATOMIC_CMP_EXCH:
|
|
case OPCODE_SAMPLE_C:
|
|
case OPCODE_SAMPLE_C_LZ:
|
|
case OPCODE_SAMPLE_L:
|
|
case OPCODE_SAMPLE_B:
|
|
case OPCODE_SWAPC:
|
|
case OPCODE_GATHER4_FEEDBACK:
|
|
case OPCODE_LD_MS_FEEDBACK:
|
|
case OPCODE_LD_STRUCTURED_FEEDBACK: return 5;
|
|
case OPCODE_GATHER4_PO_C:
|
|
case OPCODE_SAMPLE_D:
|
|
case OPCODE_SAMPLE_CLAMP_FEEDBACK:
|
|
case OPCODE_SAMPLE_C_CLAMP_FEEDBACK:
|
|
case OPCODE_SAMPLE_C_LZ_FEEDBACK:
|
|
case OPCODE_SAMPLE_L_FEEDBACK:
|
|
case OPCODE_SAMPLE_B_CLAMP_FEEDBACK:
|
|
case OPCODE_GATHER4_C_FEEDBACK:
|
|
case OPCODE_GATHER4_PO_FEEDBACK: return 6;
|
|
case OPCODE_SAMPLE_D_CLAMP_FEEDBACK:
|
|
case OPCODE_GATHER4_PO_C_FEEDBACK:
|
|
return 7;
|
|
|
|
// custom data doesn't have particular operands
|
|
case OPCODE_CUSTOMDATA:
|
|
default: break;
|
|
}
|
|
|
|
RDCERR("Unknown opcode: %u", op);
|
|
return 0xffffffff;
|
|
}
|
|
|
|
bool IsDeclaration(OpcodeType op)
|
|
{
|
|
// isDecl means not a real instruction, just a declaration type token
|
|
bool isDecl = false;
|
|
isDecl = isDecl || (op >= OPCODE_DCL_RESOURCE && op <= OPCODE_DCL_GLOBAL_FLAGS);
|
|
isDecl = isDecl || (op >= OPCODE_DCL_STREAM && op <= OPCODE_DCL_RESOURCE_STRUCTURED);
|
|
isDecl = isDecl || (op == OPCODE_DCL_GS_INSTANCE_COUNT);
|
|
isDecl = isDecl || (op == OPCODE_HS_DECLS);
|
|
isDecl = isDecl || (op == OPCODE_CUSTOMDATA);
|
|
|
|
return isDecl;
|
|
}
|
|
|
|
bool IsInput(OperandType oper)
|
|
{
|
|
switch(oper)
|
|
{
|
|
case TYPE_INPUT:
|
|
case TYPE_INPUT_PRIMITIVEID:
|
|
case TYPE_INPUT_FORK_INSTANCE_ID:
|
|
case TYPE_INPUT_JOIN_INSTANCE_ID:
|
|
case TYPE_INPUT_CONTROL_POINT:
|
|
// this is an input, yes it's confusing.
|
|
case TYPE_OUTPUT_CONTROL_POINT_ID:
|
|
case TYPE_INPUT_PATCH_CONSTANT:
|
|
case TYPE_INPUT_DOMAIN_POINT:
|
|
case TYPE_INPUT_THREAD_ID:
|
|
case TYPE_INPUT_THREAD_GROUP_ID:
|
|
case TYPE_INPUT_THREAD_ID_IN_GROUP:
|
|
case TYPE_INPUT_COVERAGE_MASK:
|
|
case TYPE_INPUT_THREAD_ID_IN_GROUP_FLATTENED:
|
|
case TYPE_INPUT_GS_INSTANCE_ID: return true;
|
|
default: break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool IsOutput(OperandType oper)
|
|
{
|
|
switch(oper)
|
|
{
|
|
case TYPE_OUTPUT:
|
|
case TYPE_OUTPUT_DEPTH:
|
|
case TYPE_OUTPUT_COVERAGE_MASK:
|
|
case TYPE_OUTPUT_DEPTH_GREATER_EQUAL:
|
|
case TYPE_OUTPUT_DEPTH_LESS_EQUAL:
|
|
case TYPE_OUTPUT_STENCIL_REF: return true;
|
|
default: break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
}; // namespace DXBCBytecode
|