mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-10-25 08:08:50 +00:00
2271 lines
69 KiB
C++
2271 lines
69 KiB
C++
/******************************************************************************
|
|
* The MIT License (MIT)
|
|
*
|
|
* Copyright (c) 2019-2021 Baldur Karlsson
|
|
* Copyright (c) 2014 Crytek
|
|
*
|
|
* 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 <math.h>
|
|
#include "common/common.h"
|
|
#include "core/core.h"
|
|
#include "core/settings.h"
|
|
#include "serialise/serialiser.h"
|
|
#include "strings/string_utils.h"
|
|
#include "dxbc_bytecode_ops.h"
|
|
|
|
#include "dxbc_container.h"
|
|
|
|
RDOC_CONFIG(bool, DXBC_Disassembly_FriendlyNaming, true,
|
|
"Where possible (i.e. it is completely unambiguous) replace register names with "
|
|
"high-level variable names.");
|
|
RDOC_CONFIG(bool, DXBC_Disassembly_ProcessVendorShaderExts, true,
|
|
"Process vendor shader extensions from magic UAV encoded instructions into the real "
|
|
"operations.\n"
|
|
"If this is disabled, shader debugging won't produce correct results.");
|
|
|
|
namespace DXBCBytecode
|
|
{
|
|
rdcstr toString(const uint32_t values[], uint32_t numComps);
|
|
|
|
const DXBC::CBufferVariable *FindCBufferVar(const uint32_t minOffset, const uint32_t maxOffset,
|
|
const rdcarray<DXBC::CBufferVariable> &variables,
|
|
uint32_t &byteOffset, rdcstr &prefix)
|
|
{
|
|
for(const DXBC::CBufferVariable &v : variables)
|
|
{
|
|
// absolute byte offset of this variable in the cbuffer
|
|
const uint32_t voffs = byteOffset + v.offset;
|
|
|
|
// does minOffset-maxOffset reside in this variable? We don't handle the case where the range
|
|
// crosses a variable (and I don't think FXC emits that anyway).
|
|
if(voffs <= minOffset && voffs + v.type.descriptor.bytesize > maxOffset)
|
|
{
|
|
byteOffset = voffs;
|
|
|
|
// if it is a struct with members, recurse to find a closer match
|
|
if(!v.type.members.empty())
|
|
{
|
|
prefix += v.name + ".";
|
|
return FindCBufferVar(minOffset, maxOffset, v.type.members, byteOffset, prefix);
|
|
}
|
|
|
|
// otherwise return this variable.
|
|
return &v;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
bool Operand::operator==(const Operand &o) const
|
|
{
|
|
if(type != o.type)
|
|
return false;
|
|
if(numComponents != o.numComponents)
|
|
return false;
|
|
if(memcmp(comps, o.comps, 4) != 0)
|
|
return false;
|
|
if((flags & (FLAG_ABS | FLAG_NEG)) != (o.flags & (FLAG_ABS | FLAG_NEG)))
|
|
return false;
|
|
|
|
if(indices.size() != o.indices.size())
|
|
return false;
|
|
|
|
for(size_t i = 0; i < indices.size(); i++)
|
|
if(indices[i] != o.indices[i])
|
|
return false;
|
|
|
|
for(size_t i = 0; i < 4; i++)
|
|
if(values[i] != o.values[i])
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Operand::sameResource(const Operand &o) const
|
|
{
|
|
if(type != o.type)
|
|
return false;
|
|
|
|
if(indices.size() == o.indices.size() && indices.empty())
|
|
return true;
|
|
|
|
if(indices.size() < 1 || o.indices.size() < 1)
|
|
return false;
|
|
|
|
return indices[0] == o.indices[0];
|
|
}
|
|
|
|
rdcstr Operand::toString(const DXBC::Reflection *reflection, ToString toStrFlags) const
|
|
{
|
|
rdcstr str, regstr;
|
|
|
|
const bool decl = toStrFlags & ToString::IsDecl;
|
|
const bool swizzle = toStrFlags & ToString::ShowSwizzle;
|
|
const bool friendly = toStrFlags & ToString::FriendlyNameRegisters;
|
|
|
|
char swiz[6] = {0, 0, 0, 0, 0, 0};
|
|
|
|
char compchars[] = {'x', 'y', 'z', 'w'};
|
|
|
|
for(int i = 0; i < 4; i++)
|
|
{
|
|
if(comps[i] < 4)
|
|
{
|
|
swiz[0] = '.';
|
|
swiz[i + 1] = compchars[comps[i]];
|
|
}
|
|
}
|
|
|
|
if(type == TYPE_NULL)
|
|
{
|
|
str = "null";
|
|
}
|
|
else if(type == TYPE_INTERFACE)
|
|
{
|
|
RDCASSERT(indices.size() == 2);
|
|
|
|
str = StringFormat::Fmt("fp%s[%s][%u]", indices[0].toString(reflection, toStrFlags).c_str(),
|
|
indices[1].toString(reflection, toStrFlags).c_str(), values[0]);
|
|
}
|
|
else if(type == TYPE_RESOURCE || type == TYPE_SAMPLER || type == TYPE_UNORDERED_ACCESS_VIEW)
|
|
{
|
|
// pre-DX11, just an index
|
|
if(indices.size() == 1)
|
|
{
|
|
if(type == TYPE_RESOURCE)
|
|
str = "t";
|
|
if(type == TYPE_SAMPLER)
|
|
str = "s";
|
|
if(type == TYPE_UNORDERED_ACCESS_VIEW)
|
|
str = "u";
|
|
|
|
str += indices[0].toString(reflection, toStrFlags);
|
|
|
|
if(friendly && reflection && indices[0].absolute)
|
|
{
|
|
uint32_t idx = (uint32_t)indices[0].index;
|
|
|
|
const rdcarray<DXBC::ShaderInputBind> *list = NULL;
|
|
|
|
if(type == TYPE_RESOURCE)
|
|
list = &reflection->SRVs;
|
|
else if(type == TYPE_UNORDERED_ACCESS_VIEW)
|
|
list = &reflection->UAVs;
|
|
else if(type == TYPE_SAMPLER)
|
|
list = &reflection->Samplers;
|
|
|
|
if(list)
|
|
{
|
|
for(const DXBC::ShaderInputBind &b : *list)
|
|
{
|
|
if(b.reg != idx || b.space != 0)
|
|
continue;
|
|
|
|
if(decl)
|
|
regstr = str;
|
|
str = b.name;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if(indices.size() == 3)
|
|
{
|
|
if(type == TYPE_RESOURCE)
|
|
str = "T";
|
|
if(type == TYPE_SAMPLER)
|
|
str = "S";
|
|
if(type == TYPE_UNORDERED_ACCESS_VIEW)
|
|
str = "U";
|
|
|
|
// DX12 declaration
|
|
|
|
// if declaration pointer is NULL we're printing inside the declaration itself.
|
|
// Upper/lower bounds are printed with the space too, but print them here as
|
|
// operand indices refer relative to those bounds.
|
|
|
|
// detect common case of non-arrayed resources and simplify
|
|
RDCASSERT(indices[1].absolute && indices[2].absolute);
|
|
if(indices[1].index == indices[2].index)
|
|
{
|
|
str += indices[0].toString(reflection, toStrFlags);
|
|
}
|
|
else
|
|
{
|
|
if(indices[2].index == 0xffffffff)
|
|
str += StringFormat::Fmt("%s[%s:unbound]",
|
|
indices[0].toString(reflection, toStrFlags).c_str(),
|
|
indices[1].toString(reflection, toStrFlags).c_str());
|
|
else
|
|
str += StringFormat::Fmt("%s[%s:%s]", indices[0].toString(reflection, toStrFlags).c_str(),
|
|
indices[1].toString(reflection, toStrFlags).c_str(),
|
|
indices[2].toString(reflection, toStrFlags).c_str());
|
|
}
|
|
}
|
|
else if(indices.size() == 2)
|
|
{
|
|
if(type == TYPE_RESOURCE)
|
|
str = "T";
|
|
if(type == TYPE_SAMPLER)
|
|
str = "S";
|
|
if(type == TYPE_UNORDERED_ACCESS_VIEW)
|
|
str = "U";
|
|
|
|
// DX12 lookup
|
|
|
|
// if we have a declaration, see if it's non-arrayed
|
|
if(declaration && declaration->operand.indices[1].index == declaration->operand.indices[2].index)
|
|
{
|
|
// resource index should be equal to the bound
|
|
RDCASSERT(indices[1].absolute && indices[1].index == declaration->operand.indices[1].index);
|
|
|
|
// just include ID
|
|
str += indices[0].toString(reflection, toStrFlags);
|
|
}
|
|
else
|
|
{
|
|
if(indices[1].relative)
|
|
str += StringFormat::Fmt("%s%s", indices[0].toString(reflection, toStrFlags).c_str(),
|
|
indices[1].toString(reflection, toStrFlags).c_str());
|
|
else
|
|
str += StringFormat::Fmt("%s[%s]", indices[0].toString(reflection, toStrFlags).c_str(),
|
|
indices[1].toString(reflection, toStrFlags).c_str());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RDCERR("Unexpected dimensions for resource-type operand: %x, %u", type,
|
|
(uint32_t)indices.size());
|
|
}
|
|
}
|
|
else if(type == TYPE_CONSTANT_BUFFER)
|
|
{
|
|
if(indices.size() == 3)
|
|
{
|
|
str = "CB";
|
|
|
|
if(declaration)
|
|
{
|
|
// see if the declaration was non-arrayed
|
|
if(declaration->operand.indices[1].index == declaration->operand.indices[2].index)
|
|
{
|
|
// resource index should be equal to the bound
|
|
RDCASSERT(indices[1].absolute && indices[1].index == declaration->operand.indices[1].index);
|
|
|
|
// just include ID and vector index
|
|
if(indices[2].relative)
|
|
str += StringFormat::Fmt("%s%s", indices[0].toString(reflection, toStrFlags).c_str(),
|
|
indices[2].toString(reflection, toStrFlags).c_str());
|
|
else
|
|
str += StringFormat::Fmt("%s[%s]", indices[0].toString(reflection, toStrFlags).c_str(),
|
|
indices[2].toString(reflection, toStrFlags).c_str());
|
|
}
|
|
else
|
|
{
|
|
str += indices[0].toString(reflection, toStrFlags);
|
|
|
|
if(indices[1].relative)
|
|
str += indices[1].toString(reflection, toStrFlags);
|
|
else
|
|
str += "[" + indices[1].toString(reflection, toStrFlags) + "]";
|
|
|
|
if(indices[2].relative)
|
|
str += indices[1].toString(reflection, toStrFlags);
|
|
else
|
|
str += "[" + indices[2].toString(reflection, toStrFlags) + "]";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// if declaration pointer is NULL we're printing inside the declaration itself.
|
|
// Because of the operand format, the size of the constant buffer is also in a
|
|
// separate DWORD printed elsewhere.
|
|
// Upper/lower bounds are printed with the space too, but print them here as
|
|
// operand indices refer relative to those bounds.
|
|
|
|
// detect common case of non-arrayed resources and simplify
|
|
RDCASSERT(indices[1].absolute && indices[2].absolute);
|
|
if(indices[1].index == indices[2].index)
|
|
{
|
|
str += indices[0].toString(reflection, toStrFlags);
|
|
}
|
|
else
|
|
{
|
|
if(indices[2].index == 0xffffffff)
|
|
str += StringFormat::Fmt("%s[%s:unbound]",
|
|
indices[0].toString(reflection, toStrFlags).c_str(),
|
|
indices[1].toString(reflection, toStrFlags).c_str());
|
|
else
|
|
str += StringFormat::Fmt("%s[%s:%s]", indices[0].toString(reflection, toStrFlags).c_str(),
|
|
indices[1].toString(reflection, toStrFlags).c_str(),
|
|
indices[2].toString(reflection, toStrFlags).c_str());
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
str = "cb";
|
|
|
|
if(indices[1].relative)
|
|
str += StringFormat::Fmt("%s%s", indices[0].toString(reflection, toStrFlags).c_str(),
|
|
indices[1].toString(reflection, toStrFlags).c_str());
|
|
else
|
|
str += StringFormat::Fmt("%s[%s]", indices[0].toString(reflection, toStrFlags).c_str(),
|
|
indices[1].toString(reflection, toStrFlags).c_str());
|
|
|
|
if(friendly && reflection && indices[0].absolute)
|
|
{
|
|
const DXBC::CBuffer *cbuffer = NULL;
|
|
|
|
for(const DXBC::CBuffer &cb : reflection->CBuffers)
|
|
{
|
|
if(cb.space == 0 && cb.reg == uint32_t(indices[0].index))
|
|
{
|
|
cbuffer = &cb;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(cbuffer)
|
|
{
|
|
// if the second index is constant then this is easy enough, we just find the matching
|
|
// cbuffer variable and use its name, possibly rebasing the swizzle.
|
|
// Unfortunately for many cases it's something like cbX[r0.x + 0] then in the next
|
|
// instruction cbX[r0.x + 1] and so on, and it's obvious that it's indexing into the same
|
|
// array for subsequent entries. However without knowing r0 we have no way to look up the
|
|
// matching variable
|
|
if(indices[1].absolute && !indices[1].relative)
|
|
{
|
|
uint8_t minComp = comps[0];
|
|
uint8_t maxComp = comps[0];
|
|
for(int i = 1; i < 4; i++)
|
|
{
|
|
if(comps[i] < 4)
|
|
{
|
|
minComp = RDCMIN(minComp, comps[i]);
|
|
maxComp = RDCMAX(maxComp, comps[i]);
|
|
}
|
|
}
|
|
|
|
uint32_t minOffset = uint32_t(indices[1].index) * 16 + minComp * 4;
|
|
uint32_t maxOffset = uint32_t(indices[1].index) * 16 + maxComp * 4;
|
|
|
|
uint32_t baseOffset = 0;
|
|
|
|
rdcstr prefix;
|
|
const DXBC::CBufferVariable *var =
|
|
FindCBufferVar(minOffset, maxOffset, cbuffer->variables, baseOffset, prefix);
|
|
|
|
if(var)
|
|
{
|
|
str = prefix + var->name;
|
|
|
|
// for indices, look at just which register is selected
|
|
minOffset &= ~0xf;
|
|
uint32_t varOffset = minOffset - baseOffset;
|
|
|
|
// if it's an array, add the index based on the relative index to the base offset
|
|
if(var->type.descriptor.elements > 1)
|
|
{
|
|
uint32_t byteSize = var->type.descriptor.bytesize;
|
|
|
|
// round up the byte size to a the nearest vec4 in case it's not quite a multiple
|
|
byteSize = AlignUp16(byteSize);
|
|
|
|
const uint32_t elementSize = byteSize / var->type.descriptor.elements;
|
|
|
|
const uint32_t elementIndex = varOffset / elementSize;
|
|
|
|
str += StringFormat::Fmt("[%u]", elementIndex);
|
|
|
|
// subtract off so that if there's any further offset, it can be processed
|
|
varOffset -= elementIndex;
|
|
}
|
|
|
|
// or if it's a matrix
|
|
if((var->type.descriptor.varClass == DXBC::CLASS_MATRIX_ROWS &&
|
|
var->type.descriptor.cols > 1) ||
|
|
(var->type.descriptor.varClass == DXBC::CLASS_MATRIX_COLUMNS &&
|
|
var->type.descriptor.rows > 1))
|
|
{
|
|
str += StringFormat::Fmt("[%u]", varOffset / 16);
|
|
}
|
|
|
|
// rebase swizzle if necessary
|
|
uint32_t vecOffset = (var->offset & 0xf);
|
|
if(vecOffset > 0)
|
|
{
|
|
for(int i = 0; i < 4; i++)
|
|
{
|
|
if(swiz[i + 1])
|
|
swiz[i + 1] = compchars[comps[i] - uint8_t(vecOffset / 4)];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if(type == TYPE_TEMP || type == TYPE_OUTPUT || type == TYPE_STREAM ||
|
|
type == TYPE_THREAD_GROUP_SHARED_MEMORY || type == TYPE_FUNCTION_BODY)
|
|
{
|
|
if(type == TYPE_TEMP)
|
|
str = "r";
|
|
if(type == TYPE_OUTPUT)
|
|
str = "o";
|
|
if(type == TYPE_STREAM)
|
|
str = "m";
|
|
if(type == TYPE_THREAD_GROUP_SHARED_MEMORY)
|
|
str = "g";
|
|
if(type == TYPE_FUNCTION_BODY)
|
|
str = "fb";
|
|
|
|
RDCASSERTEQUAL(indices.size(), 1);
|
|
|
|
str += indices[0].toString(reflection, toStrFlags);
|
|
}
|
|
else if(type == TYPE_IMMEDIATE_CONSTANT_BUFFER || type == TYPE_INDEXABLE_TEMP ||
|
|
type == TYPE_INPUT || type == TYPE_INPUT_CONTROL_POINT ||
|
|
type == TYPE_INPUT_PATCH_CONSTANT || type == TYPE_THIS_POINTER ||
|
|
type == TYPE_OUTPUT_CONTROL_POINT)
|
|
{
|
|
if(type == TYPE_IMMEDIATE_CONSTANT_BUFFER)
|
|
str = "icb";
|
|
if(type == TYPE_INDEXABLE_TEMP)
|
|
str = "x";
|
|
if(type == TYPE_INPUT)
|
|
str = "v";
|
|
if(type == TYPE_INPUT_CONTROL_POINT)
|
|
str = "vicp";
|
|
if(type == TYPE_INPUT_PATCH_CONSTANT)
|
|
str = "vpc";
|
|
if(type == TYPE_OUTPUT_CONTROL_POINT)
|
|
str = "vocp";
|
|
if(type == TYPE_THIS_POINTER)
|
|
str = "this";
|
|
|
|
if(indices.size() == 1 && type != TYPE_IMMEDIATE_CONSTANT_BUFFER)
|
|
{
|
|
str += indices[0].toString(reflection, toStrFlags);
|
|
}
|
|
else
|
|
{
|
|
for(size_t i = 0; i < indices.size(); i++)
|
|
{
|
|
if(i == 0 && type == TYPE_INDEXABLE_TEMP)
|
|
{
|
|
str += indices[i].toString(reflection, toStrFlags);
|
|
continue;
|
|
}
|
|
|
|
if(indices[i].relative)
|
|
str += indices[i].toString(reflection, toStrFlags);
|
|
else
|
|
str += "[" + indices[i].toString(reflection, toStrFlags) + "]";
|
|
}
|
|
}
|
|
}
|
|
else if(type == TYPE_IMMEDIATE32)
|
|
{
|
|
RDCASSERT(indices.size() == 0);
|
|
|
|
str = "l(" + DXBCBytecode::toString(values, numComponents == NUMCOMPS_1 ? 1U : 4U) + ")";
|
|
}
|
|
else if(type == TYPE_IMMEDIATE64)
|
|
{
|
|
double *dv = (double *)values;
|
|
str += StringFormat::Fmt("d(%lfl, %lfl)", dv[0], dv[1]);
|
|
}
|
|
else if(type == TYPE_RASTERIZER)
|
|
str = "rasterizer";
|
|
else if(type == TYPE_OUTPUT_CONTROL_POINT_ID)
|
|
str = "vOutputControlPointID";
|
|
else if(type == TYPE_INPUT_DOMAIN_POINT)
|
|
str = "vDomain";
|
|
else if(type == TYPE_INPUT_PRIMITIVEID)
|
|
str = "vPrim";
|
|
else if(type == TYPE_INPUT_COVERAGE_MASK)
|
|
str = "vCoverageMask";
|
|
else if(type == TYPE_INPUT_GS_INSTANCE_ID)
|
|
str = "vGSInstanceID";
|
|
else if(type == TYPE_INPUT_THREAD_ID)
|
|
str = "vThreadID";
|
|
else if(type == TYPE_INPUT_THREAD_GROUP_ID)
|
|
str = "vThreadGroupID";
|
|
else if(type == TYPE_INPUT_THREAD_ID_IN_GROUP)
|
|
str = "vThreadIDInGroup";
|
|
else if(type == TYPE_INPUT_THREAD_ID_IN_GROUP_FLATTENED)
|
|
str = "vThreadIDInGroupFlattened";
|
|
else if(type == TYPE_INPUT_FORK_INSTANCE_ID)
|
|
str = "vForkInstanceID";
|
|
else if(type == TYPE_INPUT_JOIN_INSTANCE_ID)
|
|
str = "vJoinInstanceID";
|
|
else if(type == TYPE_OUTPUT_DEPTH)
|
|
str = "oDepth";
|
|
else if(type == TYPE_OUTPUT_DEPTH_LESS_EQUAL)
|
|
str = "oDepthLessEqual";
|
|
else if(type == TYPE_OUTPUT_DEPTH_GREATER_EQUAL)
|
|
str = "oDepthGreaterEqual";
|
|
else if(type == TYPE_OUTPUT_COVERAGE_MASK)
|
|
str = "oMask";
|
|
else if(type == TYPE_OUTPUT_STENCIL_REF)
|
|
str = "oStencilRef";
|
|
else
|
|
{
|
|
RDCERR("Unsupported system value semantic %d", type);
|
|
str = "oUnsupported";
|
|
}
|
|
|
|
if(swizzle)
|
|
str += swiz;
|
|
|
|
if(precision != PRECISION_DEFAULT)
|
|
{
|
|
str += " {";
|
|
if(precision == PRECISION_FLOAT10)
|
|
str += "min10f";
|
|
if(precision == PRECISION_FLOAT16)
|
|
str += "min16f";
|
|
if(precision == PRECISION_UINT16)
|
|
str += "min16u";
|
|
if(precision == PRECISION_SINT16)
|
|
str += "min16i";
|
|
if(precision == PRECISION_ANY16)
|
|
str += "any16";
|
|
if(precision == PRECISION_ANY10)
|
|
str += "any10";
|
|
str += "}";
|
|
}
|
|
|
|
if(flags & FLAG_ABS)
|
|
str = "abs(" + str + ")";
|
|
if(flags & FLAG_NEG)
|
|
str = "-" + str;
|
|
|
|
if(decl && !regstr.empty())
|
|
str += StringFormat::Fmt(" (%s)", regstr.c_str());
|
|
|
|
if(!name.empty())
|
|
{
|
|
rdcstr n = name;
|
|
str = n + "=" + str;
|
|
}
|
|
|
|
return str;
|
|
}
|
|
|
|
rdcstr RegIndex::toString(const DXBC::Reflection *reflection, ToString toStrFlags) const
|
|
{
|
|
if(relative)
|
|
{
|
|
return StringFormat::Fmt(
|
|
"[%s + %llu]", operand.toString(reflection, toStrFlags | ToString::ShowSwizzle).c_str(),
|
|
index);
|
|
}
|
|
else
|
|
{
|
|
return ToStr(index);
|
|
}
|
|
}
|
|
|
|
void Program::FetchComputeProperties(DXBC::Reflection *reflection)
|
|
{
|
|
if(m_ProgramWords.empty())
|
|
return;
|
|
|
|
uint32_t *begin = &m_ProgramWords.front();
|
|
uint32_t *cur = begin;
|
|
uint32_t *end = &m_ProgramWords.back();
|
|
|
|
// skip header dword above
|
|
cur++;
|
|
|
|
// skip length dword
|
|
cur++;
|
|
|
|
while(cur < end)
|
|
{
|
|
uint32_t OpcodeToken0 = cur[0];
|
|
|
|
OpcodeType op = Opcode::Type.Get(OpcodeToken0);
|
|
|
|
if(op == OPCODE_DCL_THREAD_GROUP)
|
|
{
|
|
reflection->DispatchThreadsDimension[0] = cur[1];
|
|
reflection->DispatchThreadsDimension[1] = cur[2];
|
|
reflection->DispatchThreadsDimension[2] = cur[3];
|
|
}
|
|
else if(op == OPCODE_DCL_INPUT)
|
|
{
|
|
OperandType type = Oper::Type.Get(cur[1]);
|
|
|
|
SigParameter param;
|
|
|
|
param.varType = VarType::UInt;
|
|
param.regIndex = ~0U;
|
|
|
|
switch(type)
|
|
{
|
|
case TYPE_INPUT_THREAD_ID:
|
|
param.systemValue = ShaderBuiltin::DispatchThreadIndex;
|
|
param.compCount = 3;
|
|
param.regChannelMask = param.channelUsedMask = 0x7;
|
|
param.semanticIdxName = param.semanticName = "vThreadID";
|
|
reflection->InputSig.push_back(param);
|
|
break;
|
|
case TYPE_INPUT_THREAD_GROUP_ID:
|
|
param.systemValue = ShaderBuiltin::GroupIndex;
|
|
param.compCount = 3;
|
|
param.regChannelMask = param.channelUsedMask = 0x7;
|
|
param.semanticIdxName = param.semanticName = "vThreadGroupID";
|
|
reflection->InputSig.push_back(param);
|
|
break;
|
|
case TYPE_INPUT_THREAD_ID_IN_GROUP:
|
|
param.systemValue = ShaderBuiltin::GroupThreadIndex;
|
|
param.compCount = 3;
|
|
param.regChannelMask = param.channelUsedMask = 0x7;
|
|
param.semanticIdxName = param.semanticName = "vThreadIDInGroup";
|
|
reflection->InputSig.push_back(param);
|
|
break;
|
|
case TYPE_INPUT_THREAD_ID_IN_GROUP_FLATTENED:
|
|
param.systemValue = ShaderBuiltin::GroupFlatIndex;
|
|
param.compCount = 1;
|
|
param.regChannelMask = param.channelUsedMask = 0x1;
|
|
param.semanticIdxName = param.semanticName = "vThreadIDInGroupFlattened";
|
|
reflection->InputSig.push_back(param);
|
|
break;
|
|
default: RDCERR("Unexpected input parameter %d", type); break;
|
|
}
|
|
}
|
|
|
|
if(op == OPCODE_CUSTOMDATA)
|
|
{
|
|
// length in opcode token is 0, full length is in second dword
|
|
cur += cur[1];
|
|
}
|
|
else
|
|
{
|
|
cur += Opcode::Length.Get(OpcodeToken0);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Program::DecodeProgram()
|
|
{
|
|
if(m_Disassembled)
|
|
return;
|
|
|
|
if(m_ProgramWords.empty())
|
|
return;
|
|
|
|
m_Disassembled = true;
|
|
|
|
uint32_t *begin = &m_ProgramWords.front();
|
|
uint32_t *cur = begin;
|
|
uint32_t *end = &m_ProgramWords.back();
|
|
|
|
// check supported types
|
|
if(!(m_Major == 0x5 && m_Minor == 0x1) && !(m_Major == 0x5 && m_Minor == 0x0) &&
|
|
!(m_Major == 0x4 && m_Minor == 0x1) && !(m_Major == 0x4 && m_Minor == 0x0))
|
|
{
|
|
RDCERR("Unsupported shader bytecode version: %u.%u", m_Major, m_Minor);
|
|
return;
|
|
}
|
|
|
|
RDCASSERT(LengthToken::Length.Get(cur[1]) == m_ProgramWords.size()); // length token
|
|
|
|
cur += 2;
|
|
|
|
// count how many declarations are so we can get the vector statically sized
|
|
size_t numDecls = 0;
|
|
uint32_t *tmp = cur;
|
|
|
|
while(tmp < end)
|
|
{
|
|
uint32_t OpcodeToken0 = tmp[0];
|
|
|
|
OpcodeType op = Opcode::Type.Get(OpcodeToken0);
|
|
|
|
if(IsDeclaration(op))
|
|
numDecls++;
|
|
|
|
if(op == OPCODE_CUSTOMDATA)
|
|
{
|
|
// length in opcode token is 0, full length is in second dword
|
|
tmp += tmp[1];
|
|
}
|
|
else
|
|
{
|
|
tmp += Opcode::Length.Get(OpcodeToken0);
|
|
}
|
|
}
|
|
|
|
m_Declarations.reserve(numDecls);
|
|
|
|
const bool friendly = DXBC_Disassembly_FriendlyNaming();
|
|
|
|
while(cur < end)
|
|
{
|
|
Operation op;
|
|
Declaration decl;
|
|
|
|
uintptr_t offset = cur - begin;
|
|
|
|
op.offset = offset * sizeof(uint32_t);
|
|
|
|
if(!DecodeOperation(cur, op, friendly))
|
|
{
|
|
if(!DecodeDecl(cur, decl, friendly))
|
|
{
|
|
RDCERR("Unexpected non-operation and non-decl in token stream at 0x%x", cur - begin);
|
|
}
|
|
else
|
|
{
|
|
// once we have a set of late declarations, push it into the most recent one. Otherwise
|
|
if(m_LateDeclarations.empty())
|
|
m_Declarations.push_back(decl);
|
|
else
|
|
m_LateDeclarations.back().push_back(decl);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_Instructions.push_back(op);
|
|
|
|
// each HS phase, start a new set of late declarations
|
|
if(op.operation == OPCODE_HS_CONTROL_POINT_PHASE || op.operation == OPCODE_HS_FORK_PHASE ||
|
|
op.operation == OPCODE_HS_JOIN_PHASE)
|
|
m_LateDeclarations.push_back({});
|
|
}
|
|
}
|
|
|
|
RDCASSERT(m_Declarations.size() <= numDecls);
|
|
|
|
OpcodeType lastRealOp = NUM_REAL_OPCODES;
|
|
|
|
for(size_t i = 0; i < m_Instructions.size(); i++)
|
|
{
|
|
OpcodeType o = m_Instructions[m_Instructions.size() - i - 1].operation;
|
|
if(o == OPCODE_CUSTOMDATA || o == OPCODE_OPAQUE_CUSTOMDATA)
|
|
continue;
|
|
|
|
lastRealOp = o;
|
|
break;
|
|
}
|
|
|
|
if(lastRealOp != OPCODE_RET)
|
|
{
|
|
Operation implicitRet;
|
|
implicitRet.offset = (end - begin) * sizeof(uint32_t);
|
|
implicitRet.operation = OPCODE_RET;
|
|
implicitRet.str = "ret";
|
|
|
|
m_Instructions.push_back(implicitRet);
|
|
}
|
|
|
|
if(DXBC_Disassembly_ProcessVendorShaderExts() && m_ShaderExt.second != ~0U)
|
|
PostprocessVendorExtensions();
|
|
}
|
|
|
|
void Program::MakeDisassemblyString()
|
|
{
|
|
DecodeProgram();
|
|
|
|
if(m_ProgramWords.empty())
|
|
{
|
|
m_Disassembly = "No bytecode in this blob";
|
|
return;
|
|
}
|
|
|
|
rdcstr shadermodel = "xs_";
|
|
|
|
switch(m_Type)
|
|
{
|
|
case DXBC::ShaderType::Pixel: shadermodel = "ps_"; break;
|
|
case DXBC::ShaderType::Vertex: shadermodel = "vs_"; break;
|
|
case DXBC::ShaderType::Geometry: shadermodel = "gs_"; break;
|
|
case DXBC::ShaderType::Hull: shadermodel = "hs_"; break;
|
|
case DXBC::ShaderType::Domain: shadermodel = "ds_"; break;
|
|
case DXBC::ShaderType::Compute: shadermodel = "cs_"; break;
|
|
default: RDCERR("Unknown shader type: %u", m_Type); break;
|
|
}
|
|
|
|
m_Disassembly = StringFormat::Fmt("%s%d_%d\n", shadermodel.c_str(), m_Major, m_Minor);
|
|
|
|
uint32_t linenum = 2;
|
|
|
|
int indent = 0;
|
|
|
|
LineColumnInfo prevLineInfo;
|
|
rdcarray<rdcstr> prevCallstack;
|
|
|
|
size_t debugInst = 0;
|
|
|
|
rdcarray<rdcarray<rdcstr>> fileLines;
|
|
|
|
// generate fileLines by splitting each file in the debug info
|
|
if(m_DebugInfo)
|
|
{
|
|
fileLines.resize(m_DebugInfo->Files.size());
|
|
|
|
for(size_t i = 0; i < m_DebugInfo->Files.size(); i++)
|
|
split(m_DebugInfo->Files[i].second, fileLines[i], '\n');
|
|
}
|
|
|
|
for(size_t d = 0; d < m_Declarations.size(); d++)
|
|
{
|
|
m_Disassembly += StringFormat::Fmt("% 4s %s\n", "", m_Declarations[d].str.c_str());
|
|
linenum++;
|
|
|
|
int32_t nl = m_Declarations[d].str.indexOf('\n');
|
|
while(nl >= 0)
|
|
{
|
|
linenum++;
|
|
nl = m_Declarations[d].str.indexOf('\n', nl + 1);
|
|
}
|
|
}
|
|
|
|
size_t lt = 0;
|
|
|
|
for(size_t i = 0; i < m_Instructions.size(); i++)
|
|
{
|
|
if(m_Instructions[i].operation == OPCODE_CUSTOMDATA ||
|
|
m_Instructions[i].operation == OPCODE_OPAQUE_CUSTOMDATA)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if(m_Instructions[i].operation == OPCODE_ENDIF || m_Instructions[i].operation == OPCODE_ENDLOOP)
|
|
{
|
|
indent--;
|
|
}
|
|
|
|
if(m_DebugInfo)
|
|
{
|
|
LineColumnInfo lineInfo;
|
|
rdcarray<rdcstr> callstack;
|
|
|
|
m_DebugInfo->GetLineInfo(debugInst, m_Instructions[i].offset, lineInfo);
|
|
m_DebugInfo->GetCallstack(debugInst, m_Instructions[i].offset, callstack);
|
|
|
|
if(lineInfo.fileIndex >= 0 && (lineInfo.fileIndex != prevLineInfo.fileIndex ||
|
|
lineInfo.lineStart != prevLineInfo.lineStart))
|
|
{
|
|
rdcstr line = "";
|
|
if(lineInfo.fileIndex >= (int32_t)fileLines.size())
|
|
{
|
|
line = "Unknown file";
|
|
}
|
|
else if(fileLines[lineInfo.fileIndex].empty())
|
|
{
|
|
line = "";
|
|
}
|
|
else
|
|
{
|
|
rdcarray<rdcstr> &lines = fileLines[lineInfo.fileIndex];
|
|
|
|
int32_t lineIdx = RDCMIN(lineInfo.lineStart, (uint32_t)lines.size() - 1);
|
|
|
|
// line numbers are 1-based but we want a 0-based index
|
|
if(lineIdx > 0)
|
|
lineIdx--;
|
|
line = lines[lineIdx];
|
|
}
|
|
|
|
int startLine = line.find_first_not_of(" \t");
|
|
|
|
if(startLine >= 0)
|
|
line = line.substr(startLine);
|
|
|
|
m_Disassembly += "\n";
|
|
linenum++;
|
|
|
|
if(((lineInfo.fileIndex != prevLineInfo.fileIndex || callstack.back() != prevCallstack.back()) &&
|
|
lineInfo.fileIndex < (int32_t)fileLines.size()) ||
|
|
line == "")
|
|
{
|
|
m_Disassembly += " "; // "0000: "
|
|
for(int in = 0; in < indent; in++)
|
|
m_Disassembly += " ";
|
|
|
|
rdcstr func = callstack.back();
|
|
|
|
if(!func.empty())
|
|
{
|
|
m_Disassembly += StringFormat::Fmt("%s:%d - %s()\n",
|
|
m_DebugInfo->Files[lineInfo.fileIndex].first.c_str(),
|
|
lineInfo.lineStart, func.c_str());
|
|
linenum++;
|
|
}
|
|
else
|
|
{
|
|
m_Disassembly += StringFormat::Fmt(
|
|
"%s:%d\n", m_DebugInfo->Files[lineInfo.fileIndex].first.c_str(), lineInfo.lineStart);
|
|
linenum++;
|
|
}
|
|
}
|
|
|
|
if(line != "")
|
|
{
|
|
m_Disassembly += " "; // "0000: "
|
|
for(int in = 0; in < indent; in++)
|
|
m_Disassembly += " ";
|
|
m_Disassembly += line + "\n";
|
|
linenum++;
|
|
}
|
|
}
|
|
|
|
prevLineInfo = lineInfo;
|
|
prevCallstack = callstack;
|
|
}
|
|
|
|
int curIndent = indent;
|
|
if(m_Instructions[i].operation == OPCODE_ELSE)
|
|
curIndent--;
|
|
|
|
rdcstr whitespace;
|
|
whitespace.fill(curIndent * 2, ' ');
|
|
|
|
m_Instructions[i].line = linenum;
|
|
|
|
m_Disassembly +=
|
|
StringFormat::Fmt("% 4u: %s%s\n", i, whitespace.c_str(), m_Instructions[i].str.c_str());
|
|
linenum++;
|
|
|
|
if(m_Instructions[i].operation == OPCODE_IF || m_Instructions[i].operation == OPCODE_LOOP)
|
|
{
|
|
indent++;
|
|
}
|
|
|
|
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)
|
|
{
|
|
debugInst++;
|
|
}
|
|
else
|
|
{
|
|
if(lt < m_LateDeclarations.size())
|
|
{
|
|
for(size_t d = 0; d < m_LateDeclarations[lt].size(); d++)
|
|
{
|
|
m_Disassembly += StringFormat::Fmt("% 4s %s\n", "", m_LateDeclarations[lt][d].str.c_str());
|
|
linenum++;
|
|
|
|
int32_t nl = m_LateDeclarations[lt][d].str.indexOf('\n');
|
|
while(nl >= 0)
|
|
{
|
|
linenum++;
|
|
nl = m_LateDeclarations[lt][d].str.indexOf('\n', nl + 1);
|
|
}
|
|
}
|
|
|
|
lt++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool Program::DecodeOperand(uint32_t *&tokenStream, ToString flags, Operand &retOper)
|
|
{
|
|
uint32_t OperandToken0 = tokenStream[0];
|
|
|
|
retOper.type = Oper::Type.Get(OperandToken0);
|
|
retOper.numComponents = Oper::NumComponents.Get(OperandToken0);
|
|
|
|
SelectionMode selMode = Oper::SelectionMode.Get(OperandToken0);
|
|
|
|
if(selMode == SELECTION_MASK)
|
|
{
|
|
int i = 0;
|
|
|
|
if(Oper::ComponentMaskX.Get(OperandToken0))
|
|
retOper.comps[i++] = 0;
|
|
if(Oper::ComponentMaskY.Get(OperandToken0))
|
|
retOper.comps[i++] = 1;
|
|
if(Oper::ComponentMaskZ.Get(OperandToken0))
|
|
retOper.comps[i++] = 2;
|
|
if(Oper::ComponentMaskW.Get(OperandToken0))
|
|
retOper.comps[i++] = 3;
|
|
}
|
|
else if(selMode == SELECTION_SWIZZLE)
|
|
{
|
|
retOper.comps[0] = Oper::ComponentSwizzleX.Get(OperandToken0);
|
|
retOper.comps[1] = Oper::ComponentSwizzleY.Get(OperandToken0);
|
|
retOper.comps[2] = Oper::ComponentSwizzleZ.Get(OperandToken0);
|
|
retOper.comps[3] = Oper::ComponentSwizzleW.Get(OperandToken0);
|
|
}
|
|
else if(selMode == SELECTION_SELECT_1)
|
|
{
|
|
retOper.comps[0] = Oper::ComponentSel1.Get(OperandToken0);
|
|
}
|
|
|
|
uint32_t indexDim = Oper::IndexDimension.Get(OperandToken0);
|
|
|
|
OperandIndexType rep[] = {
|
|
Oper::Index0.Get(OperandToken0), Oper::Index1.Get(OperandToken0),
|
|
Oper::Index2.Get(OperandToken0),
|
|
};
|
|
|
|
bool extended = Oper::Extended.Get(OperandToken0);
|
|
|
|
tokenStream++;
|
|
|
|
while(extended)
|
|
{
|
|
uint32_t OperandTokenN = tokenStream[0];
|
|
|
|
ExtendedOperandType type = ExtendedOperand::Type.Get(OperandTokenN);
|
|
|
|
if(type == EXTENDED_OPERAND_MODIFIER)
|
|
{
|
|
retOper.flags = Operand::Flags(retOper.flags | ExtendedOperand::Modifier.Get(OperandTokenN));
|
|
retOper.precision = ExtendedOperand::MinPrecision.Get(OperandTokenN);
|
|
}
|
|
else
|
|
{
|
|
RDCERR("Unexpected extended operand modifier");
|
|
}
|
|
|
|
extended = ExtendedOperand::Extended.Get(OperandTokenN) == 1;
|
|
|
|
tokenStream++;
|
|
}
|
|
|
|
retOper.indices.resize(indexDim);
|
|
|
|
if(retOper.type == TYPE_IMMEDIATE32 || retOper.type == TYPE_IMMEDIATE64)
|
|
{
|
|
RDCASSERT(retOper.indices.empty());
|
|
|
|
uint32_t numComps = 1;
|
|
|
|
if(retOper.numComponents == NUMCOMPS_1)
|
|
numComps = 1;
|
|
else if(retOper.numComponents == NUMCOMPS_4)
|
|
numComps = 4;
|
|
else
|
|
RDCERR("N-wide vectors not supported.");
|
|
|
|
for(uint32_t i = 0; i < numComps; i++)
|
|
{
|
|
retOper.values[i] = tokenStream[0];
|
|
tokenStream++;
|
|
}
|
|
}
|
|
|
|
for(int idx = 0; idx < (int)indexDim; idx++)
|
|
{
|
|
if(rep[idx] == INDEX_IMMEDIATE32_PLUS_RELATIVE || rep[idx] == INDEX_IMMEDIATE32)
|
|
{
|
|
retOper.indices[idx].absolute = true;
|
|
retOper.indices[idx].index = tokenStream[0];
|
|
|
|
tokenStream++;
|
|
}
|
|
else if(rep[idx] == INDEX_IMMEDIATE64_PLUS_RELATIVE || rep[idx] == INDEX_IMMEDIATE64)
|
|
{
|
|
retOper.indices[idx].absolute = true;
|
|
|
|
// hi/lo words
|
|
retOper.indices[idx].index = tokenStream[0];
|
|
retOper.indices[idx].index <<= 32;
|
|
tokenStream++;
|
|
|
|
retOper.indices[idx].index |= tokenStream[0];
|
|
tokenStream++;
|
|
|
|
RDCCOMPILE_ASSERT(sizeof(retOper.indices[idx].index) == 8, "Index is the wrong byte width");
|
|
}
|
|
|
|
if(rep[idx] == INDEX_IMMEDIATE64_PLUS_RELATIVE || rep[idx] == INDEX_IMMEDIATE32_PLUS_RELATIVE ||
|
|
rep[idx] == INDEX_RELATIVE)
|
|
{
|
|
// relative addressing
|
|
retOper.indices[idx].relative = true;
|
|
|
|
bool ret = DecodeOperand(tokenStream, flags, retOper.indices[idx].operand);
|
|
RDCASSERT(ret);
|
|
}
|
|
|
|
RDCASSERT(retOper.indices[idx].relative || retOper.indices[idx].absolute);
|
|
}
|
|
|
|
if(retOper.type == TYPE_RESOURCE || retOper.type == TYPE_SAMPLER ||
|
|
retOper.type == TYPE_UNORDERED_ACCESS_VIEW || retOper.type == TYPE_CONSTANT_BUFFER)
|
|
{
|
|
// shader linkage can access cbuffers with a relative first index
|
|
if(retOper.indices[0].relative)
|
|
{
|
|
// ignore, we can't find the declaration
|
|
}
|
|
else
|
|
{
|
|
// try and find a declaration with a matching ID
|
|
RDCASSERT(retOper.indices.size() > 0);
|
|
for(size_t i = 0; i < m_Declarations.size(); i++)
|
|
{
|
|
// does the ID match, if so, it's our declaration
|
|
if(m_Declarations[i].operand.type == retOper.type &&
|
|
m_Declarations[i].operand.indices[0] == retOper.indices[0])
|
|
{
|
|
retOper.declaration = &m_Declarations[i];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Program::DecodeDecl(uint32_t *&tokenStream, Declaration &retDecl, bool friendlyName)
|
|
{
|
|
uint32_t *begin = tokenStream;
|
|
uint32_t OpcodeToken0 = tokenStream[0];
|
|
|
|
ToString flags = friendlyName ? ToString::FriendlyNameRegisters : ToString::None;
|
|
flags = flags | ToString::IsDecl;
|
|
|
|
const bool sm51 = (m_Major == 0x5 && m_Minor == 0x1);
|
|
|
|
OpcodeType op = Opcode::Type.Get(OpcodeToken0);
|
|
|
|
RDCASSERT(op < NUM_REAL_OPCODES);
|
|
|
|
if(!IsDeclaration(op))
|
|
return false;
|
|
|
|
retDecl.declaration = op;
|
|
|
|
if(op == OPCODE_CUSTOMDATA)
|
|
{
|
|
CustomDataClass customClass = Opcode::CustomClass.Get(OpcodeToken0);
|
|
|
|
tokenStream++;
|
|
// DWORD length including OpcodeToken0 and this length token
|
|
uint32_t customDataLength = tokenStream[0];
|
|
tokenStream++;
|
|
|
|
uint32_t dataLength = customDataLength - 2;
|
|
|
|
RDCASSERT(customDataLength >= 2);
|
|
|
|
switch(customClass)
|
|
{
|
|
case CUSTOMDATA_DCL_IMMEDIATE_CONSTANT_BUFFER:
|
|
{
|
|
retDecl.declaration = OPCODE_DCL_IMMEDIATE_CONSTANT_BUFFER;
|
|
retDecl.str = "dcl_immediateConstantBuffer {";
|
|
|
|
RDCASSERT(dataLength % 4 == 0);
|
|
|
|
for(uint32_t i = 0; i < dataLength; i++)
|
|
{
|
|
if(i % 4 == 0)
|
|
retDecl.str += "\n\t\t\t{ ";
|
|
|
|
m_Immediate.push_back(tokenStream[0]);
|
|
|
|
retDecl.str += toString(tokenStream, 1);
|
|
|
|
tokenStream++;
|
|
|
|
if((i + 1) % 4 == 0)
|
|
retDecl.str += "}";
|
|
|
|
if(i + 1 < dataLength)
|
|
retDecl.str += ", ";
|
|
}
|
|
|
|
retDecl.str += " }";
|
|
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
// add this as an opaque declaration
|
|
retDecl.declaration = OPCODE_OPAQUE_CUSTOMDATA;
|
|
|
|
retDecl.customDataIndex = (uint32_t)m_CustomDatas.size();
|
|
|
|
m_CustomDatas.push_back(make_rdcpair(customClass, rdcarray<uint32_t>()));
|
|
m_CustomDatas.back().second.assign(tokenStream, dataLength);
|
|
|
|
// unsupported custom data should be treated in operation as it's first
|
|
RDCWARN("Unsupported custom data class %d treated as declaration", customClass);
|
|
tokenStream += dataLength;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
uint32_t declLength = Opcode::Length.Get(OpcodeToken0);
|
|
|
|
tokenStream++;
|
|
|
|
retDecl.str = ToStr(op);
|
|
|
|
if(op == OPCODE_DCL_GLOBAL_FLAGS)
|
|
{
|
|
retDecl.global_flags.refactoringAllowed = Decl::RefactoringAllowed.Get(OpcodeToken0);
|
|
retDecl.global_flags.doublePrecisionFloats = Decl::DoubleFloatOps.Get(OpcodeToken0);
|
|
retDecl.global_flags.forceEarlyDepthStencil = Decl::ForceEarlyDepthStencil.Get(OpcodeToken0);
|
|
retDecl.global_flags.enableRawAndStructuredBuffers =
|
|
Decl::EnableRawStructuredBufs.Get(OpcodeToken0);
|
|
retDecl.global_flags.skipOptimisation = Decl::SkipOptimisation.Get(OpcodeToken0);
|
|
retDecl.global_flags.enableMinPrecision = Decl::EnableMinPrecision.Get(OpcodeToken0);
|
|
retDecl.global_flags.enableD3D11_1DoubleExtensions =
|
|
Decl::EnableD3D11_1DoubleExtensions.Get(OpcodeToken0);
|
|
retDecl.global_flags.enableD3D11_1ShaderExtensions =
|
|
Decl::EnableD3D11_1ShaderExtensions.Get(OpcodeToken0);
|
|
retDecl.global_flags.enableD3D12AllResourcesBound =
|
|
Decl::EnableD3D12AllResourcesBound.Get(OpcodeToken0);
|
|
|
|
retDecl.str += " ";
|
|
|
|
bool added = false;
|
|
|
|
if(retDecl.global_flags.refactoringAllowed)
|
|
{
|
|
retDecl.str += "refactoringAllowed";
|
|
added = true;
|
|
}
|
|
if(retDecl.global_flags.doublePrecisionFloats)
|
|
{
|
|
if(added)
|
|
retDecl.str += ", ";
|
|
retDecl.str += "doublePrecisionFloats";
|
|
added = true;
|
|
}
|
|
if(retDecl.global_flags.forceEarlyDepthStencil)
|
|
{
|
|
if(added)
|
|
retDecl.str += ", ";
|
|
retDecl.str += "forceEarlyDepthStencil";
|
|
added = true;
|
|
}
|
|
if(retDecl.global_flags.enableRawAndStructuredBuffers)
|
|
{
|
|
if(added)
|
|
retDecl.str += ", ";
|
|
retDecl.str += "enableRawAndStructuredBuffers";
|
|
added = true;
|
|
}
|
|
if(retDecl.global_flags.skipOptimisation)
|
|
{
|
|
if(added)
|
|
retDecl.str += ", ";
|
|
retDecl.str += "skipOptimisation";
|
|
added = true;
|
|
}
|
|
if(retDecl.global_flags.enableMinPrecision)
|
|
{
|
|
if(added)
|
|
retDecl.str += ", ";
|
|
retDecl.str += "enableMinPrecision";
|
|
added = true;
|
|
}
|
|
if(retDecl.global_flags.enableD3D11_1DoubleExtensions)
|
|
{
|
|
if(added)
|
|
retDecl.str += ", ";
|
|
retDecl.str += "doubleExtensions";
|
|
added = true;
|
|
}
|
|
if(retDecl.global_flags.enableD3D11_1ShaderExtensions)
|
|
{
|
|
if(added)
|
|
retDecl.str += ", ";
|
|
retDecl.str += "shaderExtensions";
|
|
added = true;
|
|
}
|
|
if(retDecl.global_flags.enableD3D12AllResourcesBound)
|
|
{
|
|
if(added)
|
|
retDecl.str += ", ";
|
|
retDecl.str += "d3d12AllResourcesBound";
|
|
added = true;
|
|
}
|
|
}
|
|
else if(op == OPCODE_DCL_CONSTANT_BUFFER)
|
|
{
|
|
CBufferAccessPattern accessPattern = Decl::AccessPattern.Get(OpcodeToken0);
|
|
|
|
bool ret = DecodeOperand(tokenStream, flags, retDecl.operand);
|
|
RDCASSERT(ret);
|
|
|
|
retDecl.str += " ";
|
|
retDecl.str += retDecl.operand.toString(m_Reflection, flags);
|
|
if(sm51)
|
|
{
|
|
// Store the size provided. If there's no reflection data, this will be
|
|
// necessary to guess the buffer size properly
|
|
retDecl.cbufferVectorSize = tokenStream[0];
|
|
tokenStream++;
|
|
|
|
retDecl.str += StringFormat::Fmt("[%u]", retDecl.cbufferVectorSize);
|
|
}
|
|
|
|
retDecl.str += ", ";
|
|
|
|
if(accessPattern == ACCESS_IMMEDIATE_INDEXED)
|
|
retDecl.str += "immediateIndexed";
|
|
else if(accessPattern == ACCESS_DYNAMIC_INDEXED)
|
|
retDecl.str += "dynamicIndexed";
|
|
else
|
|
RDCERR("Unexpected cbuffer access pattern");
|
|
|
|
retDecl.space = 0;
|
|
|
|
if(sm51)
|
|
{
|
|
retDecl.space = tokenStream[0];
|
|
tokenStream++;
|
|
retDecl.str += StringFormat::Fmt(" space=%u", retDecl.space);
|
|
|
|
if(retDecl.operand.indices[1].index == retDecl.operand.indices[2].index)
|
|
retDecl.str += StringFormat::Fmt(",reg=%u", retDecl.operand.indices[1].index);
|
|
else if(retDecl.operand.indices[2].index == 0xffffffff)
|
|
retDecl.str += StringFormat::Fmt(",regs=%u:unbound", retDecl.operand.indices[1].index);
|
|
else
|
|
retDecl.str += StringFormat::Fmt(",regs=%u:%u", retDecl.operand.indices[1].index,
|
|
retDecl.operand.indices[2].index);
|
|
}
|
|
}
|
|
else if(op == OPCODE_DCL_INPUT)
|
|
{
|
|
retDecl.str += " ";
|
|
|
|
bool ret = DecodeOperand(tokenStream, flags, retDecl.operand);
|
|
RDCASSERT(ret);
|
|
|
|
if(retDecl.operand.type == TYPE_INPUT_COVERAGE_MASK)
|
|
m_InputCoverage = true;
|
|
|
|
retDecl.str += retDecl.operand.toString(m_Reflection, flags | ToString::ShowSwizzle);
|
|
}
|
|
else if(op == OPCODE_DCL_TEMPS)
|
|
{
|
|
retDecl.numTemps = tokenStream[0];
|
|
|
|
m_NumTemps = retDecl.numTemps;
|
|
|
|
tokenStream++;
|
|
|
|
retDecl.str += StringFormat::Fmt(" %u", retDecl.numTemps);
|
|
}
|
|
else if(op == OPCODE_DCL_INDEXABLE_TEMP)
|
|
{
|
|
retDecl.indexable_temp.tempReg = tokenStream[0];
|
|
tokenStream++;
|
|
|
|
retDecl.indexable_temp.numTemps = tokenStream[0];
|
|
tokenStream++;
|
|
|
|
retDecl.indexable_temp.tempComponentCount = tokenStream[0];
|
|
tokenStream++;
|
|
|
|
// I don't think the compiler will ever declare a non-compact list of indexable temps, but just
|
|
// to be sure our indexing works let's be safe.
|
|
if(retDecl.indexable_temp.tempReg >= m_IndexTempSizes.size())
|
|
m_IndexTempSizes.resize(retDecl.indexable_temp.tempReg + 1);
|
|
m_IndexTempSizes[retDecl.indexable_temp.tempReg] = retDecl.indexable_temp.numTemps;
|
|
|
|
retDecl.str += StringFormat::Fmt(" x%u[%u], %u", retDecl.indexable_temp.tempReg,
|
|
retDecl.indexable_temp.numTemps,
|
|
retDecl.indexable_temp.tempComponentCount);
|
|
}
|
|
else if(op == OPCODE_DCL_OUTPUT)
|
|
{
|
|
retDecl.str += " ";
|
|
|
|
bool ret = DecodeOperand(tokenStream, flags, retDecl.operand);
|
|
RDCASSERT(ret);
|
|
|
|
retDecl.str += retDecl.operand.toString(m_Reflection, flags | ToString::ShowSwizzle);
|
|
}
|
|
else if(op == OPCODE_DCL_MAX_OUTPUT_VERTEX_COUNT)
|
|
{
|
|
retDecl.str += " ";
|
|
|
|
retDecl.maxVertexOutCount = tokenStream[0];
|
|
|
|
tokenStream++;
|
|
|
|
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)
|
|
{
|
|
bool ret = DecodeOperand(tokenStream, flags, retDecl.operand);
|
|
RDCASSERT(ret);
|
|
|
|
retDecl.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);
|
|
}
|
|
else if(op == OPCODE_DCL_STREAM)
|
|
{
|
|
bool ret = DecodeOperand(tokenStream, flags, retDecl.operand);
|
|
RDCASSERT(ret);
|
|
|
|
retDecl.str += " ";
|
|
retDecl.str += retDecl.operand.toString(m_Reflection, flags);
|
|
}
|
|
else if(op == OPCODE_DCL_SAMPLER)
|
|
{
|
|
retDecl.samplerMode = Decl::SamplerMode.Get(OpcodeToken0);
|
|
|
|
bool ret = DecodeOperand(tokenStream, flags, retDecl.operand);
|
|
RDCASSERT(ret);
|
|
|
|
retDecl.str += " ";
|
|
retDecl.str += retDecl.operand.toString(m_Reflection, flags);
|
|
|
|
retDecl.str += ", ";
|
|
if(retDecl.samplerMode == SAMPLER_MODE_DEFAULT)
|
|
retDecl.str += "mode_default";
|
|
if(retDecl.samplerMode == SAMPLER_MODE_COMPARISON)
|
|
retDecl.str += "mode_comparison";
|
|
if(retDecl.samplerMode == SAMPLER_MODE_MONO)
|
|
retDecl.str += "mode_mono";
|
|
|
|
retDecl.space = 0;
|
|
|
|
if(sm51)
|
|
{
|
|
retDecl.space = tokenStream[0];
|
|
tokenStream++;
|
|
retDecl.str += StringFormat::Fmt(" space=%u", retDecl.space);
|
|
|
|
if(retDecl.operand.indices[1].index == retDecl.operand.indices[2].index)
|
|
retDecl.str += StringFormat::Fmt(",reg=%u", retDecl.operand.indices[1].index);
|
|
else
|
|
retDecl.str += StringFormat::Fmt(",regs=%u:%u", retDecl.operand.indices[1].index,
|
|
retDecl.operand.indices[2].index);
|
|
}
|
|
}
|
|
else if(op == OPCODE_DCL_RESOURCE)
|
|
{
|
|
retDecl.resource.dim = Decl::ResourceDim.Get(OpcodeToken0);
|
|
|
|
retDecl.resource.sampleCount = 0;
|
|
if(retDecl.resource.dim == RESOURCE_DIMENSION_TEXTURE2DMS ||
|
|
retDecl.resource.dim == RESOURCE_DIMENSION_TEXTURE2DMSARRAY)
|
|
{
|
|
retDecl.resource.sampleCount = Decl::SampleCount.Get(OpcodeToken0);
|
|
}
|
|
|
|
bool ret = DecodeOperand(tokenStream, flags, retDecl.operand);
|
|
RDCASSERT(ret);
|
|
|
|
uint32_t ResourceReturnTypeToken = tokenStream[0];
|
|
tokenStream++;
|
|
|
|
retDecl.resource.resType[0] = Decl::ReturnTypeX.Get(ResourceReturnTypeToken);
|
|
retDecl.resource.resType[1] = Decl::ReturnTypeY.Get(ResourceReturnTypeToken);
|
|
retDecl.resource.resType[2] = Decl::ReturnTypeZ.Get(ResourceReturnTypeToken);
|
|
retDecl.resource.resType[3] = Decl::ReturnTypeW.Get(ResourceReturnTypeToken);
|
|
|
|
retDecl.str += "_";
|
|
retDecl.str += ToStr(retDecl.resource.dim);
|
|
if(retDecl.resource.sampleCount > 0)
|
|
{
|
|
retDecl.str += "(";
|
|
retDecl.str += ToStr(retDecl.resource.sampleCount);
|
|
retDecl.str += ")";
|
|
}
|
|
retDecl.str += " ";
|
|
|
|
retDecl.str += "(";
|
|
retDecl.str += ToStr(retDecl.resource.resType[0]);
|
|
retDecl.str += ",";
|
|
retDecl.str += ToStr(retDecl.resource.resType[1]);
|
|
retDecl.str += ",";
|
|
retDecl.str += ToStr(retDecl.resource.resType[2]);
|
|
retDecl.str += ",";
|
|
retDecl.str += ToStr(retDecl.resource.resType[3]);
|
|
retDecl.str += ")";
|
|
|
|
retDecl.str += " " + retDecl.operand.toString(m_Reflection, flags);
|
|
|
|
retDecl.space = 0;
|
|
|
|
if(sm51)
|
|
{
|
|
retDecl.space = tokenStream[0];
|
|
tokenStream++;
|
|
retDecl.str += StringFormat::Fmt(" space=%u", retDecl.space);
|
|
|
|
if(retDecl.operand.indices[1].index == retDecl.operand.indices[2].index)
|
|
retDecl.str += StringFormat::Fmt(",reg=%u", retDecl.operand.indices[1].index);
|
|
else
|
|
retDecl.str += StringFormat::Fmt(",regs=%u:%u", retDecl.operand.indices[1].index,
|
|
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);
|
|
RDCASSERT(ret);
|
|
|
|
retDecl.str += " ";
|
|
retDecl.str += retDecl.operand.toString(m_Reflection, flags | ToString::ShowSwizzle);
|
|
|
|
retDecl.indexRange = tokenStream[0];
|
|
tokenStream++;
|
|
|
|
retDecl.str += StringFormat::Fmt(" %u", retDecl.indexRange);
|
|
}
|
|
else if(op == OPCODE_DCL_THREAD_GROUP)
|
|
{
|
|
retDecl.groupSize[0] = tokenStream[0];
|
|
tokenStream++;
|
|
|
|
retDecl.groupSize[1] = tokenStream[0];
|
|
tokenStream++;
|
|
|
|
retDecl.groupSize[2] = tokenStream[0];
|
|
tokenStream++;
|
|
|
|
retDecl.str += StringFormat::Fmt(" %u, %u, %u", retDecl.groupSize[0], retDecl.groupSize[1],
|
|
retDecl.groupSize[2]);
|
|
}
|
|
else if(op == OPCODE_DCL_THREAD_GROUP_SHARED_MEMORY_RAW)
|
|
{
|
|
retDecl.str += " ";
|
|
|
|
bool ret = DecodeOperand(tokenStream, flags, retDecl.operand);
|
|
RDCASSERT(ret);
|
|
|
|
retDecl.tgsmCount = tokenStream[0];
|
|
tokenStream++;
|
|
|
|
retDecl.str += retDecl.operand.toString(m_Reflection, flags);
|
|
retDecl.str += StringFormat::Fmt(", %u", retDecl.tgsmCount);
|
|
}
|
|
else if(op == OPCODE_DCL_THREAD_GROUP_SHARED_MEMORY_STRUCTURED)
|
|
{
|
|
retDecl.str += " ";
|
|
|
|
bool ret = DecodeOperand(tokenStream, flags, retDecl.operand);
|
|
RDCASSERT(ret);
|
|
|
|
retDecl.tsgm_structured.stride = tokenStream[0];
|
|
tokenStream++;
|
|
|
|
retDecl.tsgm_structured.count = tokenStream[0];
|
|
tokenStream++;
|
|
|
|
retDecl.str += retDecl.operand.toString(m_Reflection, flags);
|
|
retDecl.str +=
|
|
StringFormat::Fmt(", %u, %u", retDecl.tsgm_structured.stride, retDecl.tsgm_structured.count);
|
|
}
|
|
else if(op == OPCODE_DCL_INPUT_CONTROL_POINT_COUNT || op == OPCODE_DCL_OUTPUT_CONTROL_POINT_COUNT)
|
|
{
|
|
retDecl.controlPointCount = Decl::ControlPointCount.Get(OpcodeToken0);
|
|
|
|
retDecl.str += StringFormat::Fmt(" %u", retDecl.controlPointCount);
|
|
}
|
|
else if(op == OPCODE_DCL_TESS_DOMAIN)
|
|
{
|
|
retDecl.tessDomain = Decl::TessDomain.Get(OpcodeToken0);
|
|
|
|
retDecl.str += " ";
|
|
if(retDecl.tessDomain == DOMAIN_ISOLINE)
|
|
retDecl.str += "domain_isoline";
|
|
else if(retDecl.tessDomain == DOMAIN_TRI)
|
|
retDecl.str += "domain_tri";
|
|
else if(retDecl.tessDomain == DOMAIN_QUAD)
|
|
retDecl.str += "domain_quad";
|
|
else
|
|
RDCERR("Unexpected Tessellation domain");
|
|
}
|
|
else if(op == OPCODE_DCL_TESS_PARTITIONING)
|
|
{
|
|
retDecl.tessPartition = Decl::TessPartitioning.Get(OpcodeToken0);
|
|
|
|
retDecl.str += " ";
|
|
if(retDecl.tessPartition == PARTITIONING_INTEGER)
|
|
retDecl.str += "partitioning_integer";
|
|
else if(retDecl.tessPartition == PARTITIONING_POW2)
|
|
retDecl.str += "partitioning_pow2";
|
|
else if(retDecl.tessPartition == PARTITIONING_FRACTIONAL_ODD)
|
|
retDecl.str += "partitioning_fractional_odd";
|
|
else if(retDecl.tessPartition == PARTITIONING_FRACTIONAL_EVEN)
|
|
retDecl.str += "partitioning_fractional_even";
|
|
else
|
|
RDCERR("Unexpected Partitioning");
|
|
}
|
|
else if(op == OPCODE_DCL_GS_INPUT_PRIMITIVE)
|
|
{
|
|
retDecl.geomInputPrimitive = Decl::InputPrimitive.Get(OpcodeToken0);
|
|
|
|
retDecl.str += " ";
|
|
if(retDecl.geomInputPrimitive == PRIMITIVE_POINT)
|
|
retDecl.str += "point";
|
|
else if(retDecl.geomInputPrimitive == PRIMITIVE_LINE)
|
|
retDecl.str += "line";
|
|
else if(retDecl.geomInputPrimitive == PRIMITIVE_TRIANGLE)
|
|
retDecl.str += "triangle";
|
|
else if(retDecl.geomInputPrimitive == PRIMITIVE_LINE_ADJ)
|
|
retDecl.str += "line_adj";
|
|
else if(retDecl.geomInputPrimitive == PRIMITIVE_TRIANGLE_ADJ)
|
|
retDecl.str += "triangle_adj";
|
|
else if(retDecl.geomInputPrimitive >= PRIMITIVE_1_CONTROL_POINT_PATCH &&
|
|
retDecl.geomInputPrimitive <= PRIMITIVE_32_CONTROL_POINT_PATCH)
|
|
{
|
|
retDecl.str +=
|
|
StringFormat::Fmt("control_point_patch_%u",
|
|
1 + int(retDecl.geomInputPrimitive - PRIMITIVE_1_CONTROL_POINT_PATCH));
|
|
}
|
|
else
|
|
RDCERR("Unexpected primitive type");
|
|
}
|
|
else if(op == OPCODE_DCL_GS_OUTPUT_PRIMITIVE_TOPOLOGY)
|
|
{
|
|
retDecl.geomOutputTopology = Decl::OutputPrimitiveTopology.Get(OpcodeToken0);
|
|
|
|
retDecl.str += " ";
|
|
if(retDecl.geomOutputTopology == D3D_PRIMITIVE_TOPOLOGY_POINTLIST)
|
|
retDecl.str += "point";
|
|
else if(retDecl.geomOutputTopology == D3D_PRIMITIVE_TOPOLOGY_LINELIST)
|
|
retDecl.str += "linelist";
|
|
else if(retDecl.geomOutputTopology == D3D_PRIMITIVE_TOPOLOGY_LINESTRIP)
|
|
retDecl.str += "linestrip";
|
|
else if(retDecl.geomOutputTopology == D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST)
|
|
retDecl.str += "trianglelist";
|
|
else if(retDecl.geomOutputTopology == D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP)
|
|
retDecl.str += "trianglestrip";
|
|
else if(retDecl.geomOutputTopology == D3D_PRIMITIVE_TOPOLOGY_LINELIST_ADJ)
|
|
retDecl.str += "linelist_adj";
|
|
else if(retDecl.geomOutputTopology == D3D_PRIMITIVE_TOPOLOGY_LINESTRIP_ADJ)
|
|
retDecl.str += "linestrip_adj";
|
|
else if(retDecl.geomOutputTopology == D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST_ADJ)
|
|
retDecl.str += "trianglelist_adj";
|
|
else if(retDecl.geomOutputTopology == D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP_ADJ)
|
|
retDecl.str += "trianglestrip_adj";
|
|
else
|
|
RDCERR("Unexpected primitive topology");
|
|
}
|
|
else if(op == OPCODE_DCL_TESS_OUTPUT_PRIMITIVE)
|
|
{
|
|
retDecl.tessOutputPrimitive = Decl::OutputPrimitive.Get(OpcodeToken0);
|
|
|
|
retDecl.str += " ";
|
|
if(retDecl.tessOutputPrimitive == OUTPUT_PRIMITIVE_POINT)
|
|
retDecl.str += "output_point";
|
|
else if(retDecl.tessOutputPrimitive == OUTPUT_PRIMITIVE_LINE)
|
|
retDecl.str += "output_line";
|
|
else if(retDecl.tessOutputPrimitive == OUTPUT_PRIMITIVE_TRIANGLE_CW)
|
|
retDecl.str += "output_triangle_cw";
|
|
else if(retDecl.tessOutputPrimitive == OUTPUT_PRIMITIVE_TRIANGLE_CCW)
|
|
retDecl.str += "output_triangle_ccw";
|
|
else
|
|
RDCERR("Unexpected output primitive");
|
|
}
|
|
else if(op == OPCODE_DCL_UNORDERED_ACCESS_VIEW_RAW || op == OPCODE_DCL_RESOURCE_RAW)
|
|
{
|
|
retDecl.raw.rov = (op == OPCODE_DCL_UNORDERED_ACCESS_VIEW_RAW) &&
|
|
Decl::RasterizerOrderedAccess.Get(OpcodeToken0);
|
|
|
|
retDecl.raw.globallyCoherant =
|
|
(op == OPCODE_DCL_UNORDERED_ACCESS_VIEW_RAW) & Decl::GloballyCoherent.Get(OpcodeToken0);
|
|
|
|
retDecl.str += " ";
|
|
|
|
bool ret = DecodeOperand(tokenStream, flags, retDecl.operand);
|
|
RDCASSERT(ret);
|
|
|
|
retDecl.str += retDecl.operand.toString(m_Reflection, flags);
|
|
|
|
if(retDecl.raw.globallyCoherant)
|
|
retDecl.str += ", globallyCoherant";
|
|
|
|
if(retDecl.raw.rov)
|
|
retDecl.str += ", rasterizerOrderedAccess";
|
|
|
|
retDecl.space = 0;
|
|
|
|
if(sm51)
|
|
{
|
|
retDecl.space = tokenStream[0];
|
|
tokenStream++;
|
|
retDecl.str += StringFormat::Fmt(" space=%u", retDecl.space);
|
|
|
|
if(retDecl.operand.indices[1].index == retDecl.operand.indices[2].index)
|
|
retDecl.str += StringFormat::Fmt(",reg=%u", retDecl.operand.indices[1].index);
|
|
else
|
|
retDecl.str += StringFormat::Fmt(",regs=%u:%u", retDecl.operand.indices[1].index,
|
|
retDecl.operand.indices[2].index);
|
|
}
|
|
}
|
|
else if(op == OPCODE_DCL_UNORDERED_ACCESS_VIEW_STRUCTURED || op == OPCODE_DCL_RESOURCE_STRUCTURED)
|
|
{
|
|
retDecl.structured.hasCounter = (op == OPCODE_DCL_UNORDERED_ACCESS_VIEW_STRUCTURED) &&
|
|
Decl::HasOrderPreservingCounter.Get(OpcodeToken0);
|
|
|
|
retDecl.structured.rov = (op == OPCODE_DCL_UNORDERED_ACCESS_VIEW_STRUCTURED) &&
|
|
Decl::RasterizerOrderedAccess.Get(OpcodeToken0);
|
|
|
|
retDecl.structured.globallyCoherant = (op == OPCODE_DCL_UNORDERED_ACCESS_VIEW_STRUCTURED) &
|
|
Decl::GloballyCoherent.Get(OpcodeToken0);
|
|
|
|
retDecl.str += " ";
|
|
|
|
bool ret = DecodeOperand(tokenStream, flags, retDecl.operand);
|
|
RDCASSERT(ret);
|
|
|
|
retDecl.structured.stride = tokenStream[0];
|
|
tokenStream++;
|
|
|
|
retDecl.str += retDecl.operand.toString(m_Reflection, flags);
|
|
retDecl.str += StringFormat::Fmt(", %u", retDecl.structured.stride);
|
|
|
|
if(retDecl.structured.hasCounter)
|
|
retDecl.str += ", hasOrderPreservingCounter";
|
|
|
|
if(retDecl.structured.globallyCoherant)
|
|
retDecl.str += ", globallyCoherant";
|
|
|
|
if(retDecl.structured.rov)
|
|
retDecl.str += ", rasterizerOrderedAccess";
|
|
|
|
retDecl.space = 0;
|
|
|
|
if(sm51)
|
|
{
|
|
retDecl.space = tokenStream[0];
|
|
tokenStream++;
|
|
retDecl.str += StringFormat::Fmt(" space=%u", retDecl.space);
|
|
|
|
if(retDecl.operand.indices[1].index == retDecl.operand.indices[2].index)
|
|
retDecl.str += StringFormat::Fmt(",reg=%u", retDecl.operand.indices[1].index);
|
|
else
|
|
retDecl.str += StringFormat::Fmt(",regs=%u:%u", retDecl.operand.indices[1].index,
|
|
retDecl.operand.indices[2].index);
|
|
}
|
|
}
|
|
else if(op == OPCODE_DCL_UNORDERED_ACCESS_VIEW_TYPED)
|
|
{
|
|
retDecl.uav_typed.dim = Decl::ResourceDim.Get(OpcodeToken0);
|
|
|
|
retDecl.uav_typed.globallyCoherant = Decl::GloballyCoherent.Get(OpcodeToken0);
|
|
|
|
retDecl.uav_typed.rov = Decl::RasterizerOrderedAccess.Get(OpcodeToken0);
|
|
|
|
retDecl.str += "_";
|
|
retDecl.str += ToStr(retDecl.uav_typed.dim);
|
|
|
|
if(retDecl.uav_typed.globallyCoherant)
|
|
retDecl.str += "_glc";
|
|
|
|
bool ret = DecodeOperand(tokenStream, flags, retDecl.operand);
|
|
RDCASSERT(ret);
|
|
|
|
uint32_t ResourceReturnTypeToken = tokenStream[0];
|
|
tokenStream++;
|
|
|
|
retDecl.uav_typed.resType[0] = Decl::ReturnTypeX.Get(ResourceReturnTypeToken);
|
|
retDecl.uav_typed.resType[1] = Decl::ReturnTypeY.Get(ResourceReturnTypeToken);
|
|
retDecl.uav_typed.resType[2] = Decl::ReturnTypeZ.Get(ResourceReturnTypeToken);
|
|
retDecl.uav_typed.resType[3] = Decl::ReturnTypeW.Get(ResourceReturnTypeToken);
|
|
|
|
retDecl.str += " ";
|
|
|
|
retDecl.str += "(";
|
|
retDecl.str += ToStr(retDecl.uav_typed.resType[0]);
|
|
retDecl.str += ",";
|
|
retDecl.str += ToStr(retDecl.uav_typed.resType[1]);
|
|
retDecl.str += ",";
|
|
retDecl.str += ToStr(retDecl.uav_typed.resType[2]);
|
|
retDecl.str += ",";
|
|
retDecl.str += ToStr(retDecl.uav_typed.resType[3]);
|
|
retDecl.str += ")";
|
|
|
|
retDecl.str += " ";
|
|
|
|
retDecl.str += retDecl.operand.toString(m_Reflection, flags);
|
|
|
|
if(retDecl.uav_typed.rov)
|
|
retDecl.str += ", rasterizerOrderedAccess";
|
|
|
|
retDecl.space = 0;
|
|
|
|
if(sm51)
|
|
{
|
|
retDecl.space = tokenStream[0];
|
|
tokenStream++;
|
|
retDecl.str += StringFormat::Fmt(" space=%u", retDecl.space);
|
|
|
|
if(retDecl.operand.indices[1].index == retDecl.operand.indices[2].index)
|
|
retDecl.str += StringFormat::Fmt(",reg=%u", retDecl.operand.indices[1].index);
|
|
else
|
|
retDecl.str += StringFormat::Fmt(",regs=%u:%u", retDecl.operand.indices[1].index,
|
|
retDecl.operand.indices[2].index);
|
|
}
|
|
}
|
|
else if(op == OPCODE_DCL_HS_FORK_PHASE_INSTANCE_COUNT ||
|
|
op == OPCODE_DCL_HS_JOIN_PHASE_INSTANCE_COUNT || op == OPCODE_DCL_GS_INSTANCE_COUNT)
|
|
{
|
|
retDecl.instanceCount = tokenStream[0];
|
|
tokenStream++;
|
|
|
|
retDecl.str += StringFormat::Fmt(" %u", retDecl.instanceCount);
|
|
}
|
|
else if(op == OPCODE_DCL_HS_MAX_TESSFACTOR)
|
|
{
|
|
float *f = (float *)tokenStream;
|
|
retDecl.maxTessFactor = *f;
|
|
tokenStream++;
|
|
|
|
retDecl.str += StringFormat::Fmt(" l(%f)", retDecl.maxTessFactor);
|
|
}
|
|
else if(op == OPCODE_DCL_FUNCTION_BODY)
|
|
{
|
|
retDecl.functionBody = tokenStream[0];
|
|
tokenStream++;
|
|
|
|
retDecl.str += StringFormat::Fmt(" fb%u", retDecl.functionBody);
|
|
}
|
|
else if(op == OPCODE_DCL_FUNCTION_TABLE)
|
|
{
|
|
retDecl.functionTable = tokenStream[0];
|
|
tokenStream++;
|
|
|
|
retDecl.str += StringFormat::Fmt(" ft%u", retDecl.functionTable);
|
|
|
|
uint32_t TableLength = tokenStream[0];
|
|
tokenStream++;
|
|
|
|
retDecl.str += " = {";
|
|
|
|
for(uint32_t i = 0; i < TableLength; i++)
|
|
{
|
|
retDecl.str += StringFormat::Fmt("fb%u", tokenStream[0]);
|
|
|
|
if(i + 1 < TableLength)
|
|
retDecl.str += ", ";
|
|
|
|
retDecl.functionTableContents.push_back(tokenStream[0]);
|
|
tokenStream++;
|
|
}
|
|
|
|
retDecl.str += "}";
|
|
}
|
|
else if(op == OPCODE_DCL_INTERFACE)
|
|
{
|
|
retDecl.iface.interfaceID = tokenStream[0];
|
|
tokenStream++;
|
|
|
|
retDecl.iface.numTypes = tokenStream[0];
|
|
tokenStream++;
|
|
|
|
uint32_t CountToken = tokenStream[0];
|
|
tokenStream++;
|
|
|
|
retDecl.iface.numInterfaces = Decl::NumInterfaces.Get(CountToken);
|
|
uint32_t TableLength = Decl::TableLength.Get(CountToken);
|
|
|
|
retDecl.str += StringFormat::Fmt(" fp%u[%u][%u]", retDecl.iface.interfaceID,
|
|
retDecl.iface.numInterfaces, retDecl.iface.numTypes);
|
|
|
|
retDecl.str += " = {";
|
|
|
|
for(uint32_t i = 0; i < TableLength; i++)
|
|
{
|
|
retDecl.str += StringFormat::Fmt("ft%u", tokenStream[0]);
|
|
|
|
if(i + 1 < TableLength)
|
|
retDecl.str += ", ";
|
|
|
|
retDecl.functionTableContents.push_back(tokenStream[0]);
|
|
tokenStream++;
|
|
}
|
|
|
|
retDecl.str += "}";
|
|
}
|
|
else if(op == OPCODE_HS_DECLS)
|
|
{
|
|
}
|
|
else
|
|
{
|
|
RDCERR("Unexpected opcode decl %d", op);
|
|
}
|
|
|
|
if(op == OPCODE_DCL_OUTPUT || op == OPCODE_DCL_OUTPUT_SIV || op == OPCODE_DCL_OUTPUT_SGV)
|
|
{
|
|
if(retDecl.operand.type == TYPE_OUTPUT_COVERAGE_MASK)
|
|
m_OutputCoverage = true;
|
|
else if(retDecl.operand.type == TYPE_OUTPUT_STENCIL_REF)
|
|
m_OutputStencil = true;
|
|
else if(retDecl.operand.type == TYPE_OUTPUT_DEPTH ||
|
|
retDecl.operand.type == TYPE_OUTPUT_DEPTH_GREATER_EQUAL ||
|
|
retDecl.operand.type == TYPE_OUTPUT_DEPTH_LESS_EQUAL)
|
|
m_OutputDepth = true;
|
|
else if(retDecl.operand.indices[0].absolute && retDecl.operand.indices[0].index < 0xffff)
|
|
m_NumOutputs = RDCMAX(m_NumOutputs, uint32_t(retDecl.operand.indices[0].index) + 1);
|
|
}
|
|
|
|
// make sure we consumed all uint32s
|
|
RDCASSERT((uint32_t)(tokenStream - begin) == declLength);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Program::DecodeOperation(uint32_t *&tokenStream, Operation &retOp, bool friendlyName)
|
|
{
|
|
uint32_t *begin = tokenStream;
|
|
uint32_t OpcodeToken0 = tokenStream[0];
|
|
|
|
ToString flags = friendlyName ? ToString::FriendlyNameRegisters : ToString::None;
|
|
|
|
OpcodeType op = Opcode::Type.Get(OpcodeToken0);
|
|
|
|
RDCASSERT(op < NUM_REAL_OPCODES);
|
|
|
|
if(IsDeclaration(op) && op != OPCODE_CUSTOMDATA)
|
|
return false;
|
|
|
|
uint32_t opLength = Opcode::Length.Get(OpcodeToken0);
|
|
|
|
// possibly only set these when applicable
|
|
retOp.operation = op;
|
|
if(Opcode::TestNonZero.Get(OpcodeToken0))
|
|
retOp.flags = Operation::Flags(retOp.flags | Operation::FLAG_NONZERO);
|
|
if(Opcode::Saturate.Get(OpcodeToken0))
|
|
retOp.flags = Operation::Flags(retOp.flags | Operation::FLAG_SATURATE);
|
|
retOp.preciseValues = Opcode::PreciseValues.Get(OpcodeToken0);
|
|
|
|
if(op == OPCODE_RESINFO)
|
|
retOp.resinfoRetType = Opcode::ResinfoReturn.Get(OpcodeToken0);
|
|
|
|
if(op == OPCODE_SYNC)
|
|
retOp.syncFlags = Opcode::SyncFlags.Get(OpcodeToken0);
|
|
|
|
bool extended = Opcode::Extended.Get(OpcodeToken0) == 1;
|
|
|
|
if(op == OPCODE_CUSTOMDATA)
|
|
{
|
|
CustomDataClass customClass = Opcode::CustomClass.Get(OpcodeToken0);
|
|
|
|
tokenStream++;
|
|
// DWORD length including OpcodeToken0 and this length token
|
|
uint32_t customDataLength = tokenStream[0];
|
|
tokenStream++;
|
|
|
|
uint32_t dataLength = customDataLength - 2;
|
|
|
|
RDCASSERT(customDataLength >= 2);
|
|
|
|
switch(customClass)
|
|
{
|
|
case CUSTOMDATA_SHADER_MESSAGE:
|
|
{
|
|
uint32_t *end = tokenStream + dataLength;
|
|
|
|
retOp.operation = OPCODE_SHADER_MESSAGE;
|
|
retOp.customDataIndex = (uint32_t)m_CustomDatas.size();
|
|
|
|
m_CustomDatas.push_back(make_rdcpair(customClass, rdcarray<uint32_t>()));
|
|
m_CustomDatas.back().second.assign(tokenStream, dataLength);
|
|
|
|
// uint32_t infoQueueMsgId = tokenStream[0];
|
|
uint32_t messageFormat = tokenStream[1]; // enum. 0 == text only, 1 == printf
|
|
// uint32_t formatStringLen = tokenStream[2]; // length NOT including null terminator
|
|
retOp.operands.resize(tokenStream[3]);
|
|
// uint32_t operandDwordLen = tokenStream[4];
|
|
|
|
tokenStream += 5;
|
|
|
|
for(uint32_t i = 0; i < retOp.operands.size(); i++)
|
|
{
|
|
bool ret = DecodeOperand(tokenStream, flags, retOp.operands[i]);
|
|
RDCASSERT(ret);
|
|
}
|
|
|
|
rdcstr formatString = (char *)&tokenStream[0];
|
|
|
|
// escape any newlines
|
|
int32_t nl = formatString.find("\n");
|
|
while(nl >= 0)
|
|
{
|
|
formatString[nl] = '\\';
|
|
formatString.insert(nl + 1, 'n');
|
|
nl = formatString.find("\n", nl);
|
|
}
|
|
|
|
retOp.str = (messageFormat ? "errorf" : "error");
|
|
retOp.str += " \"" + formatString + "\"";
|
|
|
|
for(uint32_t i = 0; i < retOp.operands.size(); i++)
|
|
{
|
|
retOp.str += ", ";
|
|
retOp.str += retOp.operands[i].toString(m_Reflection, flags | ToString::ShowSwizzle);
|
|
}
|
|
|
|
tokenStream = end;
|
|
|
|
break;
|
|
}
|
|
case CUSTOMDATA_DCL_IMMEDIATE_CONSTANT_BUFFER:
|
|
{
|
|
// handle as declaration
|
|
tokenStream = begin;
|
|
return false;
|
|
}
|
|
default:
|
|
{
|
|
// if we haven't seen any instructions yet, handle as declaration
|
|
if(m_Instructions.empty())
|
|
{
|
|
tokenStream = begin;
|
|
return false;
|
|
}
|
|
|
|
RDCWARN("Unsupported custom data class %d!", customClass);
|
|
|
|
RDCLOG("Data length seems to be %d uint32s", dataLength);
|
|
|
|
retOp.operation = OPCODE_OPAQUE_CUSTOMDATA;
|
|
|
|
retOp.customDataIndex = (uint32_t)m_CustomDatas.size();
|
|
|
|
m_CustomDatas.push_back(make_rdcpair(customClass, rdcarray<uint32_t>()));
|
|
m_CustomDatas.back().second.assign(tokenStream, dataLength);
|
|
|
|
tokenStream += dataLength;
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
tokenStream++;
|
|
|
|
retOp.str = ToStr(op);
|
|
|
|
while(extended)
|
|
{
|
|
uint32_t OpcodeTokenN = tokenStream[0];
|
|
|
|
ExtendedOpcodeType type = ExtendedOpcode::Type.Get(OpcodeTokenN);
|
|
|
|
if(type == EXTENDED_OPCODE_SAMPLE_CONTROLS)
|
|
{
|
|
retOp.texelOffset[0] = ExtendedOpcode::TexelOffsetU.Get(OpcodeTokenN);
|
|
retOp.texelOffset[1] = ExtendedOpcode::TexelOffsetV.Get(OpcodeTokenN);
|
|
retOp.texelOffset[2] = ExtendedOpcode::TexelOffsetW.Get(OpcodeTokenN);
|
|
|
|
// apply 4-bit two's complement as per spec
|
|
if(retOp.texelOffset[0] > 7)
|
|
retOp.texelOffset[0] -= 16;
|
|
if(retOp.texelOffset[1] > 7)
|
|
retOp.texelOffset[1] -= 16;
|
|
if(retOp.texelOffset[2] > 7)
|
|
retOp.texelOffset[2] -= 16;
|
|
|
|
retOp.str += StringFormat::Fmt("(%d,%d,%d)", retOp.texelOffset[0], retOp.texelOffset[1],
|
|
retOp.texelOffset[2]);
|
|
}
|
|
else if(type == EXTENDED_OPCODE_RESOURCE_DIM)
|
|
{
|
|
retOp.resDim = ExtendedOpcode::ResourceDim.Get(OpcodeTokenN);
|
|
|
|
if(op == OPCODE_LD_STRUCTURED)
|
|
{
|
|
retOp.stride = ExtendedOpcode::BufferStride.Get(OpcodeTokenN);
|
|
|
|
retOp.str += StringFormat::Fmt("_indexable(%s, stride=%u)", ToStr(retOp.resDim).c_str(),
|
|
retOp.stride);
|
|
}
|
|
else
|
|
{
|
|
retOp.str += "(";
|
|
retOp.str += ToStr(retOp.resDim);
|
|
retOp.str += ")";
|
|
}
|
|
}
|
|
else if(type == EXTENDED_OPCODE_RESOURCE_RETURN_TYPE)
|
|
{
|
|
retOp.resType[0] = ExtendedOpcode::ReturnTypeX.Get(OpcodeTokenN);
|
|
retOp.resType[1] = ExtendedOpcode::ReturnTypeY.Get(OpcodeTokenN);
|
|
retOp.resType[2] = ExtendedOpcode::ReturnTypeZ.Get(OpcodeTokenN);
|
|
retOp.resType[3] = ExtendedOpcode::ReturnTypeW.Get(OpcodeTokenN);
|
|
|
|
retOp.str += "(";
|
|
retOp.str += ToStr(retOp.resType[0]);
|
|
retOp.str += ",";
|
|
retOp.str += ToStr(retOp.resType[1]);
|
|
retOp.str += ",";
|
|
retOp.str += ToStr(retOp.resType[2]);
|
|
retOp.str += ",";
|
|
retOp.str += ToStr(retOp.resType[3]);
|
|
retOp.str += ")";
|
|
}
|
|
|
|
extended = ExtendedOpcode::Extended.Get(OpcodeTokenN) == 1;
|
|
|
|
tokenStream++;
|
|
}
|
|
|
|
if(op == OPCODE_RESINFO)
|
|
{
|
|
retOp.str += "_";
|
|
retOp.str += ToStr(retOp.resinfoRetType);
|
|
}
|
|
|
|
if(op == OPCODE_SYNC)
|
|
{
|
|
if(Opcode::Sync_UAV_Global.Get(retOp.syncFlags))
|
|
{
|
|
retOp.str += "_uglobal";
|
|
}
|
|
if(Opcode::Sync_UAV_Group.Get(retOp.syncFlags))
|
|
{
|
|
retOp.str += "_ugroup";
|
|
}
|
|
if(Opcode::Sync_TGSM.Get(retOp.syncFlags))
|
|
{
|
|
retOp.str += "_g";
|
|
}
|
|
if(Opcode::Sync_Threads.Get(retOp.syncFlags))
|
|
{
|
|
retOp.str += "_t";
|
|
}
|
|
}
|
|
|
|
uint32_t func = 0;
|
|
if(op == OPCODE_INTERFACE_CALL)
|
|
{
|
|
func = tokenStream[0];
|
|
tokenStream++;
|
|
}
|
|
|
|
retOp.operands.resize(NumOperands(op));
|
|
|
|
for(size_t i = 0; i < retOp.operands.size(); i++)
|
|
{
|
|
bool ret = DecodeOperand(tokenStream, flags, retOp.operands[i]);
|
|
RDCASSERT(ret);
|
|
}
|
|
|
|
if(op == OPCODE_INTERFACE_CALL)
|
|
{
|
|
retOp.operands[0].values[0] = func;
|
|
}
|
|
|
|
if(op == OPCODE_IF || op == OPCODE_BREAKC || op == OPCODE_CALLC || op == OPCODE_CONTINUEC ||
|
|
op == OPCODE_RETC || op == OPCODE_DISCARD)
|
|
retOp.str += retOp.nonzero() ? "_nz" : "_z";
|
|
|
|
if(op != OPCODE_SYNC)
|
|
{
|
|
retOp.str += retOp.saturate() ? "_sat" : "";
|
|
}
|
|
|
|
if(retOp.preciseValues)
|
|
{
|
|
rdcstr preciseStr;
|
|
if(retOp.preciseValues & 0x1)
|
|
preciseStr += "x";
|
|
if(retOp.preciseValues & 0x2)
|
|
preciseStr += "y";
|
|
if(retOp.preciseValues & 0x4)
|
|
preciseStr += "z";
|
|
if(retOp.preciseValues & 0x8)
|
|
preciseStr += "w";
|
|
|
|
retOp.str += StringFormat::Fmt(" [precise(%s)] ", preciseStr.c_str());
|
|
}
|
|
|
|
for(size_t i = 0; i < retOp.operands.size(); i++)
|
|
{
|
|
if(i == 0)
|
|
retOp.str += " ";
|
|
else
|
|
retOp.str += ", ";
|
|
retOp.str += retOp.operands[i].toString(m_Reflection, flags | ToString::ShowSwizzle);
|
|
}
|
|
|
|
#if ENABLED(RDOC_DEVEL)
|
|
if((uint32_t)(tokenStream - begin) > 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;
|
|
tokenStream -= overread;
|
|
}
|
|
else if((uint32_t)(tokenStream - begin) < 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);
|
|
for(uint32_t i = 0; i < missing; i++)
|
|
{
|
|
RDCLOG("missing token %d: 0x%08x", i, tokenStream[0]);
|
|
tokenStream++;
|
|
}
|
|
}
|
|
|
|
// make sure we consumed all uint32s
|
|
RDCASSERT((uint32_t)(tokenStream - begin) == opLength);
|
|
#else
|
|
// there's no good documentation for this, we're freewheeling blind in a nightmarish hellscape.
|
|
// Instead of assuming we can predictably decode the whole of every opcode, just advance by the
|
|
// defined length.
|
|
tokenStream = begin + opLength;
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
}; // namespace DXBCBytecode
|