Files
renderdoc/renderdoc/driver/shaders/dxbc/dxbc_inspect.cpp
T
baldurk 203f6b5255 Silence some warnings about casting from one enum type to another
* Reported by Coverity Scan - I don't think any of these were actually
  a problem since the enum definitions all matched.
2017-11-22 19:11:19 +00:00

2541 lines
78 KiB
C++

/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2015-2017 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_inspect.h"
#include "api/app/renderdoc_app.h"
#include "common/common.h"
#include "serialise/serialiser.h"
#include "strings/string_utils.h"
#include "dxbc_sdbg.h"
#include "dxbc_spdb.h"
using std::make_pair;
namespace DXBC
{
struct RDEFCBufferVariable
{
uint32_t nameOffset;
uint32_t startOffset; // start offset in bytes of this variable in the cbuffer
uint32_t size; // size in bytes of this type
uint32_t flags;
uint32_t typeOffset; // offset to a RDEFCBufferType
uint32_t defaultValueOffset; // offset to [size] bytes where the default value can be found, or
// 0 for no default value
uint32_t unknown[4]; // this is only present for RDEFHeader.targetVersion >= 0x500. In earlier
// versions, this is not in the file.
};
struct RDEFCBuffer
{
uint32_t nameOffset; // relative to the same offset base position as others in this chunk -
// after FourCC and chunk length.
DXBC::CountOffset variables;
uint32_t size; // size in bytes of this cbuffer
uint32_t flags;
uint32_t type;
// followed immediately by [variables.count] RDEFCBufferVariables
};
// mostly for nested structures
struct RDEFCBufferChildType
{
uint32_t nameOffset;
uint32_t typeOffset; // offset to a RDEFCBufferType
uint32_t memberOffset; // byte offset in the parent structure - not a file offset
};
struct RDEFCBufferType
{
uint16_t varClass; // D3D_SHADER_VARIABLE_CLASS
uint16_t varType; // D3D_SHADER_VARIABLE_TYPE
uint16_t rows;
uint16_t cols;
uint16_t numElems;
uint16_t numMembers;
uint32_t memberOffset; // offset to [numMembers] RDEFCBufferChildTypes that point to the member
// types
// my own guessing - not in wine structures
// looks like these are only present for RD11 shaders
uint32_t unknown[4];
uint32_t nameOffset; // offset to type name
};
// this isn't a proper chunk, it's the file header before all the chunks.
struct FileHeader
{
uint32_t fourcc; // "DXBC"
uint32_t hashValue[4]; // unknown hash function and data
uint32_t unknown;
uint32_t fileLength;
uint32_t numChunks;
// uint32 chunkOffsets[numChunks]; follows
};
struct RDEFHeader
{
uint32_t fourcc; // "RDEF"
uint32_t chunkLength; // length of this chunk
//////////////////////////////////////////////////////
// offsets are relative to this position in the file.
// NOT the end of this structure. Note this differs
// from the SDBG chunk, but matches the SIGN chunks
// note that these two actually come in the opposite order after
// this header. So cbuffers offset will be higher than resources
// offset
CountOffset cbuffers;
CountOffset resources;
uint16_t targetVersion; // 0x0501 is the latest.
uint16_t targetShaderStage; // 0xffff for pixel shaders, 0xfffe for vertex shaders
uint32_t flags;
uint32_t creatorOffset; // null terminated ascii string
uint32_t unknown[8]; // this is only present for targetVersion >= 0x500. In earlier versions,
// this is not in the file.
};
struct RDEFResource
{
uint32_t nameOffset; // relative to the same offset base position as others in this chunk -
// after FourCC and chunk length.
uint32_t type;
uint32_t retType;
uint32_t dimension;
int32_t sampleCount;
uint32_t bindPoint;
uint32_t bindCount;
uint32_t flags;
// this is only present for RDEFHeader.targetVersion >= 0x501.
uint32_t space;
// the ID seems to be a 0-based name fxc generates to refer to the object.
// We don't use it, and it's easy enough to re-generate
uint32_t ID;
};
struct SIGNHeader
{
uint32_t fourcc; // "ISGN", "OSGN, "OSG5", "PCSG"
uint32_t chunkLength; // length of this chunk
//////////////////////////////////////////////////////
// offsets are relative to this position in the file.
// NOT the end of this structure. Note this differs
// from the SDBG chunk, but matches the RDEF chunk
uint32_t numElems;
uint32_t unknown;
// followed by SIGNElement elements[numElems]; - note that SIGNElement's size depends on the type.
// for OSG5 you should use SIGNElement7
};
struct PRIVHeader
{
uint32_t fourcc; // "PRIV"
uint32_t chunkLength; // length of this chunk
GUID debugInfoGUID; // GUID/magic number, since PRIV data could be used for something else.
// Set to the value of RENDERDOC_ShaderDebugMagicValue from
// renderdoc_app.h which can also be used as a GUID to set the path
// at runtime via SetPrivateData (see documentation)
static const GUID RENDERDOC_ShaderDebugMagicValue;
void *data;
};
const GUID PRIVHeader::RENDERDOC_ShaderDebugMagicValue = RENDERDOC_ShaderDebugMagicValue_struct;
struct SIGNElement
{
uint32_t nameOffset; // relative to the same offset base position as others in similar chunks -
// after FourCC and chunk length.
uint32_t semanticIdx;
SVSemantic systemType;
uint32_t componentType;
uint32_t registerNum;
byte mask;
byte rwMask;
uint16_t unused;
};
struct SIGNElement7
{
uint32_t stream;
SIGNElement elem;
};
struct SIGNElement1
{
uint32_t stream;
SIGNElement elem;
MinimumPrecision precision;
};
static const uint32_t STATSizeDX10 = 29 * 4; // either 29 uint32s
static const uint32_t STATSizeDX11 = 37 * 4; // or 37 uint32s
static const uint32_t FOURCC_DXBC = MAKE_FOURCC('D', 'X', 'B', 'C');
static const uint32_t FOURCC_RDEF = MAKE_FOURCC('R', 'D', 'E', 'F');
static const uint32_t FOURCC_RD11 = MAKE_FOURCC('R', 'D', '1', '1');
static const uint32_t FOURCC_STAT = MAKE_FOURCC('S', 'T', 'A', 'T');
static const uint32_t FOURCC_SHEX = MAKE_FOURCC('S', 'H', 'E', 'X');
static const uint32_t FOURCC_SHDR = MAKE_FOURCC('S', 'H', 'D', 'R');
static const uint32_t FOURCC_SDBG = MAKE_FOURCC('S', 'D', 'B', 'G');
static const uint32_t FOURCC_SPDB = MAKE_FOURCC('S', 'P', 'D', 'B');
static const uint32_t FOURCC_ISGN = MAKE_FOURCC('I', 'S', 'G', 'N');
static const uint32_t FOURCC_OSGN = MAKE_FOURCC('O', 'S', 'G', 'N');
static const uint32_t FOURCC_ISG1 = MAKE_FOURCC('I', 'S', 'G', '1');
static const uint32_t FOURCC_OSG1 = MAKE_FOURCC('O', 'S', 'G', '1');
static const uint32_t FOURCC_OSG5 = MAKE_FOURCC('O', 'S', 'G', '5');
static const uint32_t FOURCC_PCSG = MAKE_FOURCC('P', 'C', 'S', 'G');
static const uint32_t FOURCC_Aon9 = MAKE_FOURCC('A', 'o', 'n', '9');
static const uint32_t FOURCC_PRIV = MAKE_FOURCC('P', 'R', 'I', 'V');
int TypeByteSize(VariableType t)
{
switch(t)
{
case VARTYPE_UINT8: return 1;
case VARTYPE_BOOL:
case VARTYPE_INT:
case VARTYPE_FLOAT:
case VARTYPE_UINT:
return 4;
// we pretend for our purposes that the 'min' formats round up to 4 bytes. For any external
// interfaces they are treated as regular types, only using lower precision internally.
case VARTYPE_MIN8FLOAT:
case VARTYPE_MIN10FLOAT:
case VARTYPE_MIN16FLOAT:
case VARTYPE_MIN12INT:
case VARTYPE_MIN16INT:
case VARTYPE_MIN16UINT: return 4;
case VARTYPE_DOUBLE:
return 8;
// 'virtual' type. Just return 1
case VARTYPE_INTERFACE_POINTER: return 1;
default: RDCERR("Trying to take size of undefined type %d", t); return 1;
}
}
ShaderBuiltin GetSystemValue(SVSemantic systemValue)
{
switch(systemValue)
{
case SVNAME_UNDEFINED: return ShaderBuiltin::Undefined;
case SVNAME_POSITION: return ShaderBuiltin::Position;
case SVNAME_CLIP_DISTANCE: return ShaderBuiltin::ClipDistance;
case SVNAME_CULL_DISTANCE: return ShaderBuiltin::CullDistance;
case SVNAME_RENDER_TARGET_ARRAY_INDEX: return ShaderBuiltin::RTIndex;
case SVNAME_VIEWPORT_ARRAY_INDEX: return ShaderBuiltin::ViewportIndex;
case SVNAME_VERTEX_ID: return ShaderBuiltin::VertexIndex;
case SVNAME_PRIMITIVE_ID: return ShaderBuiltin::PrimitiveIndex;
case SVNAME_INSTANCE_ID: return ShaderBuiltin::InstanceIndex;
case SVNAME_IS_FRONT_FACE: return ShaderBuiltin::IsFrontFace;
case SVNAME_SAMPLE_INDEX: return ShaderBuiltin::MSAASampleIndex;
case SVNAME_FINAL_QUAD_EDGE_TESSFACTOR: return ShaderBuiltin::OuterTessFactor;
case SVNAME_FINAL_QUAD_INSIDE_TESSFACTOR: return ShaderBuiltin::InsideTessFactor;
case SVNAME_FINAL_TRI_EDGE_TESSFACTOR: return ShaderBuiltin::OuterTessFactor;
case SVNAME_FINAL_TRI_INSIDE_TESSFACTOR: return ShaderBuiltin::InsideTessFactor;
case SVNAME_FINAL_LINE_DETAIL_TESSFACTOR: return ShaderBuiltin::OuterTessFactor;
case SVNAME_FINAL_LINE_DENSITY_TESSFACTOR: return ShaderBuiltin::InsideTessFactor;
case SVNAME_TARGET: return ShaderBuiltin::ColorOutput;
case SVNAME_DEPTH: return ShaderBuiltin::DepthOutput;
case SVNAME_COVERAGE: return ShaderBuiltin::MSAACoverage;
case SVNAME_DEPTH_GREATER_EQUAL: return ShaderBuiltin::DepthOutputGreaterEqual;
case SVNAME_DEPTH_LESS_EQUAL: return ShaderBuiltin::DepthOutputLessEqual;
}
return ShaderBuiltin::Undefined;
}
string TypeName(CBufferVariableType::Descriptor desc)
{
string ret;
char *type = "";
switch(desc.type)
{
case VARTYPE_BOOL: type = "bool"; break;
case VARTYPE_INT: type = "int"; break;
case VARTYPE_FLOAT: type = "float"; break;
case VARTYPE_DOUBLE: type = "double"; break;
case VARTYPE_UINT: type = "uint"; break;
case VARTYPE_UINT8: type = "ubyte"; break;
case VARTYPE_VOID: type = "void"; break;
case VARTYPE_INTERFACE_POINTER: type = "interface"; break;
case VARTYPE_MIN8FLOAT: type = "min8float"; break;
case VARTYPE_MIN10FLOAT: type = "min10float"; break;
case VARTYPE_MIN16FLOAT: type = "min16float"; break;
case VARTYPE_MIN12INT: type = "min12int"; break;
case VARTYPE_MIN16INT: type = "min16int"; break;
case VARTYPE_MIN16UINT: type = "min16uint"; break;
default: RDCERR("Unexpected type in RDEF variable type %d", type);
}
if(desc.varClass == CLASS_OBJECT)
RDCERR("Unexpected object in RDEF variable type");
else if(desc.varClass == CLASS_INTERFACE_CLASS)
RDCERR("Unexpected iface class in RDEF variable type");
else if(desc.varClass == CLASS_INTERFACE_POINTER)
ret = type;
else if(desc.varClass == CLASS_STRUCT)
ret = "<unnamed>";
else
{
char buf[64] = {0};
if(desc.rows > 1)
{
StringFormat::snprintf(buf, 63, "%s%dx%d", type, desc.rows, desc.cols);
if(desc.varClass == CLASS_MATRIX_ROWS)
{
ret = "row_major ";
ret += buf;
}
else
{
ret = buf;
}
}
else if(desc.cols > 1)
{
StringFormat::snprintf(buf, 63, "%s%d", type, desc.cols);
ret = buf;
}
else
{
ret = type;
}
}
return ret;
}
CBufferVariableType DXBCFile::ParseRDEFType(RDEFHeader *h, char *chunkContents, uint32_t typeOffset)
{
if(m_Variables.find(typeOffset) != m_Variables.end())
return m_Variables[typeOffset];
RDEFCBufferType *type = (RDEFCBufferType *)(chunkContents + typeOffset);
CBufferVariableType ret;
ret.descriptor.varClass = (VariableClass)type->varClass;
ret.descriptor.cols = type->cols;
ret.descriptor.elements = type->numElems;
ret.descriptor.members = type->numMembers;
ret.descriptor.rows = type->rows;
ret.descriptor.type = (VariableType)type->varType;
ret.descriptor.name = TypeName(ret.descriptor);
if(ret.descriptor.name == "interface")
{
if(h->targetVersion >= 0x500 && type->nameOffset > 0)
{
ret.descriptor.name += " " + string(chunkContents + type->nameOffset);
}
else
{
char buf[64] = {0};
StringFormat::snprintf(buf, 63, "unnamed_iface_0x%08x", typeOffset);
ret.descriptor.name += " " + string(buf);
}
}
// rename unnamed structs to have valid identifiers as type name
if(ret.descriptor.name.find("<unnamed>") != string::npos)
{
if(h->targetVersion >= 0x500 && type->nameOffset > 0)
{
ret.descriptor.name = chunkContents + type->nameOffset;
}
else
{
char buf[64] = {0};
StringFormat::snprintf(buf, 63, "unnamed_struct_0x%08x", typeOffset);
ret.descriptor.name = buf;
}
}
if(type->memberOffset)
{
RDEFCBufferChildType *members = (RDEFCBufferChildType *)(chunkContents + type->memberOffset);
ret.members.reserve(type->numMembers);
ret.descriptor.bytesize = 0;
for(int32_t j = 0; j < type->numMembers; j++)
{
CBufferVariable v;
v.name = chunkContents + members[j].nameOffset;
v.type = ParseRDEFType(h, chunkContents, members[j].typeOffset);
v.descriptor.offset = members[j].memberOffset;
ret.descriptor.bytesize += v.type.descriptor.bytesize;
// N/A
v.descriptor.flags = 0;
v.descriptor.startTexture = 0;
v.descriptor.numTextures = 0;
v.descriptor.startSampler = 0;
v.descriptor.numSamplers = 0;
v.descriptor.defaultValue.clear();
ret.members.push_back(v);
}
}
else
{
// matrices take up a full vector for each column or row depending which is major, regardless of
// the other dimension
if(ret.descriptor.varClass == CLASS_MATRIX_COLUMNS)
ret.descriptor.bytesize = TypeByteSize(ret.descriptor.type) * ret.descriptor.cols * 4 *
RDCMAX(1U, ret.descriptor.elements);
else if(ret.descriptor.varClass == CLASS_MATRIX_ROWS)
ret.descriptor.bytesize = TypeByteSize(ret.descriptor.type) * ret.descriptor.rows * 4 *
RDCMAX(1U, ret.descriptor.elements);
else
ret.descriptor.bytesize = TypeByteSize(ret.descriptor.type) * ret.descriptor.rows *
ret.descriptor.cols * RDCMAX(1U, ret.descriptor.elements);
}
m_Variables[typeOffset] = ret;
return ret;
}
void DXBCFile::GetHash(uint32_t hash[4], const void *ByteCode, size_t BytecodeLength)
{
if(BytecodeLength < sizeof(FileHeader))
{
memset(hash, 0, sizeof(uint32_t) * 4);
return;
}
FileHeader *header = (FileHeader *)ByteCode;
memcpy(hash, header->hashValue, sizeof(header->hashValue));
}
bool DXBCFile::CheckForDebugInfo(const void *ByteCode, size_t ByteCodeLength)
{
FileHeader *header = (FileHeader *)ByteCode;
char *data = (char *)ByteCode; // just for convenience
if(header->fourcc != FOURCC_DXBC)
return false;
if(header->fileLength != (uint32_t)ByteCodeLength)
return false;
uint32_t *chunkOffsets = (uint32_t *)(header + 1); // right after the header
for(uint32_t chunkIdx = 0; chunkIdx < header->numChunks; chunkIdx++)
{
uint32_t *fourcc = (uint32_t *)(data + chunkOffsets[chunkIdx]);
if(*fourcc == FOURCC_SDBG)
{
return true;
}
else if(*fourcc == FOURCC_SPDB)
{
return true;
}
}
return false;
}
string DXBCFile::GetDebugBinaryPath(const void *ByteCode, size_t ByteCodeLength)
{
string debugPath;
FileHeader *header = (FileHeader *)ByteCode;
char *data = (char *)ByteCode; // just for convenience
if(header->fourcc != FOURCC_DXBC)
return debugPath;
if(header->fileLength != (uint32_t)ByteCodeLength)
return debugPath;
uint32_t *chunkOffsets = (uint32_t *)(header + 1); // right after the header
for(uint32_t chunkIdx = 0; chunkIdx < header->numChunks; chunkIdx++)
{
uint32_t *fourcc = (uint32_t *)(data + chunkOffsets[chunkIdx]);
if(*fourcc == FOURCC_PRIV)
{
PRIVHeader *privHeader = (PRIVHeader *)fourcc;
if(privHeader->debugInfoGUID == PRIVHeader::RENDERDOC_ShaderDebugMagicValue)
{
const char *pathData = (char *)&privHeader->data;
size_t pathLength = strnlen(pathData, privHeader->chunkLength);
if(privHeader->chunkLength == (sizeof(GUID) + pathLength + 1))
{
debugPath.append(pathData, pathData + pathLength);
return debugPath;
}
}
}
}
return debugPath;
}
DXBCFile::DXBCFile(const void *ByteCode, size_t ByteCodeLength)
{
m_DebugInfo = NULL;
m_Disassembled = false;
m_Type = D3D11_ShaderType_Vertex;
m_Version.Major = 5;
m_Version.Minor = 0;
m_GuessedResources = true;
RDCASSERT(ByteCodeLength < UINT32_MAX);
RDCEraseEl(m_ShaderStats);
m_ShaderBlob.resize(ByteCodeLength);
memcpy(&m_ShaderBlob[0], ByteCode, m_ShaderBlob.size());
char *data = (char *)&m_ShaderBlob[0]; // just for convenience
FileHeader *header = (FileHeader *)&m_ShaderBlob[0];
if(header->fourcc != FOURCC_DXBC)
return;
if(header->fileLength != (uint32_t)ByteCodeLength)
return;
// default to vertex shader to support blobs without RDEF chunks (e.g. used with
// input layouts if they're super stripped down)
m_Type = D3D11_ShaderType_Vertex;
bool rdefFound = false;
uint32_t *chunkOffsets = (uint32_t *)(header + 1); // right after the header
for(uint32_t chunkIdx = 0; chunkIdx < header->numChunks; chunkIdx++)
{
uint32_t *fourcc = (uint32_t *)(data + chunkOffsets[chunkIdx]);
uint32_t *chunkSize = (uint32_t *)(data + chunkOffsets[chunkIdx] + sizeof(uint32_t));
char *chunkContents = (char *)(data + chunkOffsets[chunkIdx] + sizeof(uint32_t) * 2);
if(*fourcc == FOURCC_RDEF)
{
RDEFHeader *h = (RDEFHeader *)fourcc;
rdefFound = true;
// for target version 0x500, unknown[0] is FOURCC_RD11.
// for 0x501 it's "\x13\x13\D%"
if(h->targetShaderStage == 0xffff)
m_Type = D3D11_ShaderType_Pixel;
else if(h->targetShaderStage == 0xfffe)
m_Type = D3D11_ShaderType_Vertex;
else if(h->targetShaderStage == 0x4753) // 'GS'
m_Type = D3D11_ShaderType_Geometry;
else if(h->targetShaderStage == 0x4853) // 'HS'
m_Type = D3D11_ShaderType_Hull;
else if(h->targetShaderStage == 0x4453) // 'DS'
m_Type = D3D11_ShaderType_Domain;
else if(h->targetShaderStage == 0x4353) // 'CS'
m_Type = D3D11_ShaderType_Compute;
m_SRVs.reserve(h->resources.count);
m_UAVs.reserve(h->resources.count);
m_Samplers.reserve(h->resources.count);
struct CBufferBind
{
uint32_t reg, space, bindCount;
};
map<string, CBufferBind> cbufferbinds;
uint32_t resourceStride = sizeof(RDEFResource);
// versions before 5.1 don't have the space and ID
if(h->targetVersion < 0x501)
{
resourceStride -= sizeof(RDEFResource) - offsetof(RDEFResource, space);
}
for(int32_t i = 0; i < h->resources.count; i++)
{
RDEFResource *res =
(RDEFResource *)(chunkContents + h->resources.offset + i * resourceStride);
ShaderInputBind desc;
desc.name = chunkContents + res->nameOffset;
desc.type = (ShaderInputBind::InputType)res->type;
desc.space = h->targetVersion >= 0x501 ? res->space : 0;
desc.reg = res->bindPoint;
desc.bindCount = res->bindCount;
desc.flags = res->flags;
desc.retType = (ResourceRetType)res->retType;
desc.dimension = (ShaderInputBind::Dimension)res->dimension;
desc.numSamples = res->sampleCount;
if(desc.numSamples == ~0 && desc.retType != RETURN_TYPE_MIXED &&
desc.retType != RETURN_TYPE_UNKNOWN && desc.retType != RETURN_TYPE_CONTINUED)
{
// uint, uint2, uint3, uint4 seem to be in these bits of flags.
desc.numSamples = 1 + ((desc.flags & 0xC) >> 2);
}
// for cbuffers the names can be duplicated, so handle this by assuming
// the order will match between binding declaration and cbuffer declaration
// and append _s onto each subsequent buffer name
if(desc.IsCBuffer())
{
string cname = desc.name;
while(cbufferbinds.find(cname) != cbufferbinds.end())
cname += "_";
CBufferBind cb;
cb.space = desc.space;
cb.reg = desc.reg;
cb.bindCount = desc.bindCount;
cbufferbinds[cname] = cb;
}
else if(desc.IsSampler())
{
m_Samplers.push_back(desc);
}
else if(desc.IsSRV())
{
m_SRVs.push_back(desc);
}
else if(desc.IsUAV())
{
m_UAVs.push_back(desc);
}
else
{
RDCERR("Unexpected type of resource: %u", desc.type);
}
}
// Expand out any array resources. We deliberately place these at the end of the resources
// array, so that any non-array resources can be picked up first before any arrays.
//
// The reason for this is that an array element could refer to an un-used alias in a bind
// point, and an individual non-array resoruce will always refer to the used alias (an
// un-used individual resource will be omitted entirely from the reflection
//
// Note we preserve the arrays in SM5.1
if(h->targetVersion < 0x501)
{
for(vector<ShaderInputBind> *arr : {&m_SRVs, &m_UAVs, &m_Samplers})
{
vector<ShaderInputBind> &resArray = *arr;
for(size_t i = 0; i < resArray.size();)
{
if(resArray[i].bindCount > 1)
{
ShaderInputBind desc = resArray[i];
resArray.erase(resArray.begin() + i);
string rname = desc.name;
uint32_t arraySize = desc.bindCount;
desc.bindCount = 1;
for(uint32_t a = 0; a < arraySize; a++)
{
desc.name = StringFormat::Fmt("%s[%u]", rname.c_str(), a);
resArray.push_back(desc);
desc.reg++;
}
// continue from the i'th element again since
// we just removed it.
continue;
}
i++;
}
}
}
std::set<std::string> cbuffernames;
for(int32_t i = 0; i < h->cbuffers.count; i++)
{
RDEFCBuffer *cbuf =
(RDEFCBuffer *)(chunkContents + h->cbuffers.offset + i * sizeof(RDEFCBuffer));
CBuffer cb;
// I have no real justification for this, it seems some cbuffers are included that are
// empty and have nameOffset = 0, fxc seems to skip them so I'll do the same.
// See github issue #122
if(cbuf->nameOffset == 0)
continue;
cb.name = chunkContents + cbuf->nameOffset;
cb.descriptor.name = chunkContents + cbuf->nameOffset;
cb.descriptor.byteSize = cbuf->size;
cb.descriptor.type = (CBuffer::Descriptor::Type)cbuf->type;
cb.descriptor.flags = cbuf->flags;
cb.descriptor.numVars = cbuf->variables.count;
cb.variables.reserve(cbuf->variables.count);
size_t varStride = sizeof(RDEFCBufferVariable);
if(h->targetVersion < 0x500)
{
size_t extraData = sizeof(RDEFCBufferVariable) - offsetof(RDEFCBufferVariable, unknown);
varStride -= extraData;
// it seems in rare circumstances, this data is present even for targetVersion < 0x500.
// use a heuristic to check if the lower stride would cause invalid-looking data
// for variables. See github issue #122
if(cbuf->variables.count > 1)
{
RDEFCBufferVariable *var =
(RDEFCBufferVariable *)(chunkContents + cbuf->variables.offset + varStride);
if(var->nameOffset > ByteCodeLength)
{
varStride += extraData;
}
}
}
for(int32_t vi = 0; vi < cbuf->variables.count; vi++)
{
RDEFCBufferVariable *var =
(RDEFCBufferVariable *)(chunkContents + cbuf->variables.offset + vi * varStride);
RDCASSERT(var->nameOffset < ByteCodeLength);
CBufferVariable v;
v.name = chunkContents + var->nameOffset;
v.descriptor.defaultValue.resize(var->size);
if(var->defaultValueOffset && var->defaultValueOffset != ~0U)
{
memcpy(&v.descriptor.defaultValue[0], chunkContents + var->defaultValueOffset, var->size);
}
v.descriptor.name = v.name;
// v.descriptor.bytesize = var->size; // size with cbuffer padding
v.descriptor.offset = var->startOffset;
v.descriptor.flags = var->flags;
v.descriptor.startTexture = (uint32_t)-1;
v.descriptor.startSampler = (uint32_t)-1;
v.descriptor.numSamplers = 0;
v.descriptor.numTextures = 0;
v.type = ParseRDEFType(h, chunkContents, var->typeOffset);
cb.variables.push_back(v);
}
string cname = cb.name;
while(cbuffernames.find(cname) != cbuffernames.end())
cname += "_";
cbuffernames.insert(cname);
cb.space = cbufferbinds[cname].space;
cb.reg = cbufferbinds[cname].reg;
cb.bindCount = cbufferbinds[cname].bindCount;
if(cb.descriptor.type == CBuffer::Descriptor::TYPE_CBUFFER)
{
m_CBuffers.push_back(cb);
}
else if(cb.descriptor.type == CBuffer::Descriptor::TYPE_RESOURCE_BIND_INFO)
{
RDCASSERT(cb.variables.size() == 1 && cb.variables[0].name == "$Element");
m_ResourceBinds[cb.name] = cb.variables[0].type;
}
else if(cb.descriptor.type == CBuffer::Descriptor::TYPE_INTERFACE_POINTERS)
{
m_Interfaces = cb;
}
else
{
RDCDEBUG("Unused information, buffer %d: %s", cb.descriptor.type,
cb.descriptor.name.c_str());
}
}
}
else if(*fourcc == FOURCC_STAT)
{
if(*chunkSize == STATSizeDX10)
{
memcpy(&m_ShaderStats, chunkContents, STATSizeDX10);
m_ShaderStats.version = ShaderStatistics::STATS_DX10;
}
else if(*chunkSize == STATSizeDX11)
{
memcpy(&m_ShaderStats, chunkContents, STATSizeDX11);
m_ShaderStats.version = ShaderStatistics::STATS_DX11;
}
else
{
RDCERR("Unexpected Unexpected STAT chunk version");
}
}
else if(*fourcc == FOURCC_SHEX || *fourcc == FOURCC_SHDR)
{
m_HexDump.resize(*chunkSize / sizeof(uint32_t));
memcpy((uint32_t *)&m_HexDump[0], chunkContents, *chunkSize);
}
}
// get type/version that's used regularly and cheap to fetch
FetchTypeVersion();
m_GuessedResources = false;
// didn't find an rdef means reflection information was stripped.
// Attempt to reverse engineer basic info from declarations
if(!rdefFound)
{
// need to disassemble now to guess resources
DisassembleHexDump();
GuessResources();
m_GuessedResources = true;
}
// make sure to fetch the dispatch threads dimension from disassembly
if(!m_Disassembled && m_Type == D3D11_ShaderType_Compute)
{
FetchThreadDim();
}
for(uint32_t chunkIdx = 0; chunkIdx < header->numChunks; chunkIdx++)
{
uint32_t *fourcc = (uint32_t *)(data + chunkOffsets[chunkIdx]);
// uint32_t *chunkSize = (uint32_t *)(data + chunkOffsets[chunkIdx] + sizeof(uint32_t));
char *chunkContents = (char *)(data + chunkOffsets[chunkIdx] + sizeof(uint32_t) * 2);
if(*fourcc == FOURCC_ISGN || *fourcc == FOURCC_OSGN || *fourcc == FOURCC_ISG1 ||
*fourcc == FOURCC_OSG1 || *fourcc == FOURCC_OSG5 || *fourcc == FOURCC_PCSG)
{
SIGNHeader *sign = (SIGNHeader *)fourcc;
vector<SigParameter> *sig = NULL;
bool input = false;
bool output = false;
bool patch = false;
if(*fourcc == FOURCC_ISGN || *fourcc == FOURCC_ISG1)
{
sig = &m_InputSig;
input = true;
}
if(*fourcc == FOURCC_OSGN || *fourcc == FOURCC_OSG1 || *fourcc == FOURCC_OSG5)
{
sig = &m_OutputSig;
output = true;
}
if(*fourcc == FOURCC_PCSG)
{
sig = &m_PatchConstantSig;
patch = true;
}
RDCASSERT(sig && sig->empty());
SIGNElement *el0 = (SIGNElement *)(sign + 1);
SIGNElement7 *el7 = (SIGNElement7 *)el0;
SIGNElement1 *el1 = (SIGNElement1 *)el0;
for(uint32_t signIdx = 0; signIdx < sign->numElems; signIdx++)
{
SigParameter desc;
const SIGNElement *el = el0;
if(*fourcc == FOURCC_ISG1 || *fourcc == FOURCC_OSG1)
{
desc.stream = el1->stream;
// discard el1->precision as we don't use it and don't want to pollute the common API
// structures
el = &el1->elem;
}
if(*fourcc == FOURCC_OSG5)
{
desc.stream = el7->stream;
el = &el7->elem;
}
ComponentType compType = (ComponentType)el->componentType;
desc.compType = CompType::Float;
if(compType == COMPONENT_TYPE_UINT32)
desc.compType = CompType::UInt;
else if(compType == COMPONENT_TYPE_SINT32)
desc.compType = CompType::SInt;
else if(compType != COMPONENT_TYPE_FLOAT32)
RDCERR("Unexpected component type in signature");
desc.regChannelMask = (uint8_t)(el->mask & 0xff);
desc.channelUsedMask = (uint8_t)(el->rwMask & 0xff);
desc.regIndex = el->registerNum;
desc.semanticIndex = el->semanticIdx;
desc.semanticName = chunkContents + el->nameOffset;
desc.systemValue = GetSystemValue(el->systemType);
desc.compCount = (desc.regChannelMask & 0x1 ? 1 : 0) + (desc.regChannelMask & 0x2 ? 1 : 0) +
(desc.regChannelMask & 0x4 ? 1 : 0) + (desc.regChannelMask & 0x8 ? 1 : 0);
RDCASSERT(m_Type != (D3D11_ShaderType)-1);
// pixel shader outputs with registers are always targets
if(m_Type == D3D11_ShaderType_Pixel && output &&
desc.systemValue == ShaderBuiltin::Undefined && desc.regIndex >= 0 && desc.regIndex <= 16)
desc.systemValue = ShaderBuiltin::ColorOutput;
// check system value semantics
if(desc.systemValue == ShaderBuiltin::Undefined)
{
if(!_stricmp(desc.semanticName.c_str(), "SV_Position"))
desc.systemValue = ShaderBuiltin::Position;
if(!_stricmp(desc.semanticName.c_str(), "SV_ClipDistance"))
desc.systemValue = ShaderBuiltin::ClipDistance;
if(!_stricmp(desc.semanticName.c_str(), "SV_CullDistance"))
desc.systemValue = ShaderBuiltin::CullDistance;
if(!_stricmp(desc.semanticName.c_str(), "SV_RenderTargetArrayIndex"))
desc.systemValue = ShaderBuiltin::RTIndex;
if(!_stricmp(desc.semanticName.c_str(), "SV_ViewportArrayIndex"))
desc.systemValue = ShaderBuiltin::ViewportIndex;
if(!_stricmp(desc.semanticName.c_str(), "SV_VertexID"))
desc.systemValue = ShaderBuiltin::VertexIndex;
if(!_stricmp(desc.semanticName.c_str(), "SV_PrimitiveID"))
desc.systemValue = ShaderBuiltin::PrimitiveIndex;
if(!_stricmp(desc.semanticName.c_str(), "SV_InstanceID"))
desc.systemValue = ShaderBuiltin::InstanceIndex;
if(!_stricmp(desc.semanticName.c_str(), "SV_DispatchThreadID"))
desc.systemValue = ShaderBuiltin::DispatchThreadIndex;
if(!_stricmp(desc.semanticName.c_str(), "SV_GroupID"))
desc.systemValue = ShaderBuiltin::GroupIndex;
if(!_stricmp(desc.semanticName.c_str(), "SV_GroupIndex"))
desc.systemValue = ShaderBuiltin::GroupFlatIndex;
if(!_stricmp(desc.semanticName.c_str(), "SV_GroupThreadID"))
desc.systemValue = ShaderBuiltin::GroupThreadIndex;
if(!_stricmp(desc.semanticName.c_str(), "SV_GSInstanceID"))
desc.systemValue = ShaderBuiltin::GSInstanceIndex;
if(!_stricmp(desc.semanticName.c_str(), "SV_OutputControlPointID"))
desc.systemValue = ShaderBuiltin::OutputControlPointIndex;
if(!_stricmp(desc.semanticName.c_str(), "SV_DomainLocation"))
desc.systemValue = ShaderBuiltin::DomainLocation;
if(!_stricmp(desc.semanticName.c_str(), "SV_IsFrontFace"))
desc.systemValue = ShaderBuiltin::IsFrontFace;
if(!_stricmp(desc.semanticName.c_str(), "SV_SampleIndex"))
desc.systemValue = ShaderBuiltin::MSAASampleIndex;
if(!_stricmp(desc.semanticName.c_str(), "SV_TessFactor"))
desc.systemValue = ShaderBuiltin::OuterTessFactor;
if(!_stricmp(desc.semanticName.c_str(), "SV_InsideTessFactor"))
desc.systemValue = ShaderBuiltin::InsideTessFactor;
if(!_stricmp(desc.semanticName.c_str(), "SV_Target"))
desc.systemValue = ShaderBuiltin::ColorOutput;
if(!_stricmp(desc.semanticName.c_str(), "SV_Depth"))
desc.systemValue = ShaderBuiltin::DepthOutput;
if(!_stricmp(desc.semanticName.c_str(), "SV_Coverage"))
desc.systemValue = ShaderBuiltin::MSAACoverage;
if(!_stricmp(desc.semanticName.c_str(), "SV_DepthGreaterEqual"))
desc.systemValue = ShaderBuiltin::DepthOutputGreaterEqual;
if(!_stricmp(desc.semanticName.c_str(), "SV_DepthLessEqual"))
desc.systemValue = ShaderBuiltin::DepthOutputLessEqual;
}
RDCASSERT(desc.systemValue != ShaderBuiltin::Undefined || desc.regIndex >= 0);
sig->push_back(desc);
el0++;
el1++;
el7++;
}
for(uint32_t i = 0; i < sign->numElems; i++)
{
SigParameter &a = (*sig)[i];
for(uint32_t j = 0; j < sign->numElems; j++)
{
SigParameter &b = (*sig)[j];
if(i != j && a.semanticName == b.semanticName)
{
a.needSemanticIndex = true;
break;
}
}
std::string semanticIdxName = a.semanticName;
if(a.needSemanticIndex)
semanticIdxName += ToStr(a.semanticIndex);
a.semanticIdxName = semanticIdxName;
}
}
else if(*fourcc == FOURCC_Aon9) // 10Level9 most likely
{
char *c = (char *)fourcc;
RDCWARN("Unknown chunk: %c%c%c%c", c[0], c[1], c[2], c[3]);
}
else if(*fourcc == FOURCC_SDBG)
{
m_DebugInfo = new SDBGChunk(fourcc);
}
else if(*fourcc == FOURCC_SPDB)
{
m_DebugInfo = new SPDBChunk(fourcc);
}
}
}
void DXBCFile::GuessResources()
{
char buf[64] = {0};
for(size_t i = 0; i < m_Declarations.size(); i++)
{
ASMDecl &dcl = m_Declarations[i];
switch(dcl.declaration)
{
case OPCODE_DCL_SAMPLER:
{
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;
StringFormat::snprintf(buf, 63, "sampler%u", idx);
desc.name = buf;
desc.type = ShaderInputBind::TYPE_SAMPLER;
desc.space = dcl.space;
desc.reg = idx;
desc.bindCount = 1;
desc.flags = dcl.samplerMode == SAMPLER_MODE_COMPARISON ? 2 : 0;
desc.retType = RETURN_TYPE_UNKNOWN;
desc.dimension = ShaderInputBind::DIM_UNKNOWN;
desc.numSamples = 0;
if(dcl.operand.indices.size() == 3)
{
desc.bindCount = uint32_t(dcl.operand.indices[2].index - dcl.operand.indices[1].index);
if(dcl.operand.indices[2].index == 0xffffffff)
desc.bindCount = 0;
}
m_Samplers.push_back(desc);
break;
}
case OPCODE_DCL_RESOURCE:
{
ShaderInputBind desc;
RDCASSERT(dcl.operand.type == TYPE_RESOURCE);
RDCASSERT(dcl.operand.indices.size() == 1);
RDCASSERT(dcl.operand.indices[0].absolute);
uint32_t idx = (uint32_t)dcl.operand.indices[0].index;
StringFormat::snprintf(buf, 63, "texture%u", idx);
desc.name = buf;
desc.type = ShaderInputBind::TYPE_TEXTURE;
desc.space = dcl.space;
desc.reg = idx;
desc.bindCount = 1;
desc.flags = 0;
desc.retType = dcl.resType[0];
switch(dcl.dim)
{
case RESOURCE_DIMENSION_BUFFER: desc.dimension = ShaderInputBind::DIM_BUFFER; break;
case RESOURCE_DIMENSION_TEXTURE1D: desc.dimension = ShaderInputBind::DIM_TEXTURE1D; break;
case RESOURCE_DIMENSION_TEXTURE2D: desc.dimension = ShaderInputBind::DIM_TEXTURE2D; break;
case RESOURCE_DIMENSION_TEXTURE3D: desc.dimension = ShaderInputBind::DIM_TEXTURE3D; break;
case RESOURCE_DIMENSION_TEXTURECUBE:
desc.dimension = ShaderInputBind::DIM_TEXTURECUBE;
break;
case RESOURCE_DIMENSION_TEXTURE1DARRAY:
desc.dimension = ShaderInputBind::DIM_TEXTURE1DARRAY;
break;
case RESOURCE_DIMENSION_TEXTURE2DARRAY:
desc.dimension = ShaderInputBind::DIM_TEXTURE2DARRAY;
break;
case RESOURCE_DIMENSION_TEXTURECUBEARRAY:
desc.dimension = ShaderInputBind::DIM_TEXTURECUBEARRAY;
break;
case RESOURCE_DIMENSION_TEXTURE2DMS:
desc.dimension = ShaderInputBind::DIM_TEXTURE2DMS;
break;
case RESOURCE_DIMENSION_TEXTURE2DMSARRAY:
desc.dimension = ShaderInputBind::DIM_TEXTURE2DMSARRAY;
break;
default: desc.dimension = ShaderInputBind::DIM_UNKNOWN; break;
}
desc.numSamples = dcl.sampleCount;
// can't tell, fxc seems to default to 4
if(desc.dimension == ShaderInputBind::DIM_BUFFER)
desc.numSamples = 4;
RDCASSERT(desc.dimension != ShaderInputBind::DIM_UNKNOWN);
if(dcl.operand.indices.size() == 3)
{
desc.bindCount = uint32_t(dcl.operand.indices[2].index - dcl.operand.indices[1].index);
if(dcl.operand.indices[2].index == 0xffffffff)
desc.bindCount = 0;
}
m_SRVs.push_back(desc);
break;
}
case OPCODE_DCL_UNORDERED_ACCESS_VIEW_RAW:
case OPCODE_DCL_RESOURCE_RAW:
{
ShaderInputBind desc;
RDCASSERT(dcl.operand.type == TYPE_RESOURCE ||
dcl.operand.type == TYPE_UNORDERED_ACCESS_VIEW);
RDCASSERT(dcl.operand.indices.size() == 1);
RDCASSERT(dcl.operand.indices[0].absolute);
uint32_t idx = (uint32_t)dcl.operand.indices[0].index;
StringFormat::snprintf(buf, 63, "%sbytebuffer%u",
dcl.operand.type != TYPE_RESOURCE ? "rw" : "", idx);
desc.name = buf;
desc.type = dcl.operand.type == TYPE_RESOURCE ? ShaderInputBind::TYPE_BYTEADDRESS
: ShaderInputBind::TYPE_UAV_RWBYTEADDRESS;
desc.space = dcl.space;
desc.reg = idx;
desc.bindCount = 1;
desc.flags = 0;
desc.retType = RETURN_TYPE_MIXED;
desc.dimension = ShaderInputBind::DIM_BUFFER;
desc.numSamples = 0;
if(dcl.operand.indices.size() == 3)
{
desc.bindCount = uint32_t(dcl.operand.indices[2].index - dcl.operand.indices[1].index);
if(dcl.operand.indices[2].index == 0xffffffff)
desc.bindCount = 0;
}
if(dcl.operand.type == TYPE_RESOURCE)
m_SRVs.push_back(desc);
else
m_UAVs.push_back(desc);
break;
}
case OPCODE_DCL_RESOURCE_STRUCTURED:
{
ShaderInputBind desc;
RDCASSERT(dcl.operand.type == TYPE_RESOURCE);
RDCASSERT(dcl.operand.indices.size() == 1);
RDCASSERT(dcl.operand.indices[0].absolute);
uint32_t idx = (uint32_t)dcl.operand.indices[0].index;
StringFormat::snprintf(buf, 63, "structuredbuffer%u", idx);
desc.name = buf;
desc.type = ShaderInputBind::TYPE_STRUCTURED;
desc.space = dcl.space;
desc.reg = idx;
desc.bindCount = 1;
desc.flags = 0;
desc.retType = RETURN_TYPE_MIXED;
desc.dimension = ShaderInputBind::DIM_BUFFER;
desc.numSamples = dcl.stride;
if(dcl.operand.indices.size() == 3)
{
desc.bindCount = uint32_t(dcl.operand.indices[2].index - dcl.operand.indices[1].index);
if(dcl.operand.indices[2].index == 0xffffffff)
desc.bindCount = 0;
}
m_SRVs.push_back(desc);
break;
}
case OPCODE_DCL_UNORDERED_ACCESS_VIEW_STRUCTURED:
{
ShaderInputBind desc;
RDCASSERT(dcl.operand.type == TYPE_UNORDERED_ACCESS_VIEW);
RDCASSERT(dcl.operand.indices.size() == 1);
RDCASSERT(dcl.operand.indices[0].absolute);
uint32_t idx = (uint32_t)dcl.operand.indices[0].index;
StringFormat::snprintf(buf, 63, "uav%u", idx);
desc.name = buf;
desc.type = ShaderInputBind::TYPE_UAV_RWSTRUCTURED; // doesn't seem to be anything that
// determines append vs consume vs
// rwstructured
if(dcl.hasCounter)
desc.type = ShaderInputBind::TYPE_UAV_RWSTRUCTURED_WITH_COUNTER;
desc.space = dcl.space;
desc.reg = idx;
desc.bindCount = 1;
desc.flags = 0;
desc.retType = RETURN_TYPE_MIXED;
desc.dimension = ShaderInputBind::DIM_BUFFER;
desc.numSamples = dcl.stride;
if(dcl.operand.indices.size() == 3)
{
desc.bindCount = uint32_t(dcl.operand.indices[2].index - dcl.operand.indices[1].index);
if(dcl.operand.indices[2].index == 0xffffffff)
desc.bindCount = 0;
}
m_UAVs.push_back(desc);
break;
}
case OPCODE_DCL_UNORDERED_ACCESS_VIEW_TYPED:
{
ShaderInputBind desc;
RDCASSERT(dcl.operand.type == TYPE_UNORDERED_ACCESS_VIEW);
RDCASSERT(dcl.operand.indices.size() == 1);
RDCASSERT(dcl.operand.indices[0].absolute);
uint32_t idx = (uint32_t)dcl.operand.indices[0].index;
StringFormat::snprintf(buf, 63, "uav%u", idx);
desc.name = buf;
desc.type = ShaderInputBind::TYPE_UAV_RWTYPED;
desc.space = dcl.space;
desc.reg = idx;
desc.bindCount = 1;
desc.flags = 0;
desc.retType = dcl.resType[0];
switch(dcl.dim)
{
case RESOURCE_DIMENSION_BUFFER: desc.dimension = ShaderInputBind::DIM_BUFFER; break;
case RESOURCE_DIMENSION_TEXTURE1D: desc.dimension = ShaderInputBind::DIM_TEXTURE1D; break;
case RESOURCE_DIMENSION_TEXTURE2D: desc.dimension = ShaderInputBind::DIM_TEXTURE2D; break;
case RESOURCE_DIMENSION_TEXTURE3D: desc.dimension = ShaderInputBind::DIM_TEXTURE3D; break;
case RESOURCE_DIMENSION_TEXTURECUBE:
desc.dimension = ShaderInputBind::DIM_TEXTURECUBE;
break;
case RESOURCE_DIMENSION_TEXTURE1DARRAY:
desc.dimension = ShaderInputBind::DIM_TEXTURE1DARRAY;
break;
case RESOURCE_DIMENSION_TEXTURE2DARRAY:
desc.dimension = ShaderInputBind::DIM_TEXTURE2DARRAY;
break;
case RESOURCE_DIMENSION_TEXTURECUBEARRAY:
desc.dimension = ShaderInputBind::DIM_TEXTURECUBEARRAY;
break;
case RESOURCE_DIMENSION_TEXTURE2DMS:
desc.dimension = ShaderInputBind::DIM_TEXTURE2DMS;
break;
case RESOURCE_DIMENSION_TEXTURE2DMSARRAY:
desc.dimension = ShaderInputBind::DIM_TEXTURE2DMSARRAY;
break;
default: desc.dimension = ShaderInputBind::DIM_UNKNOWN; break;
}
desc.numSamples = (uint32_t)-1;
if(dcl.operand.indices.size() == 3)
{
desc.bindCount = uint32_t(dcl.operand.indices[2].index - dcl.operand.indices[1].index);
if(dcl.operand.indices[2].index == 0xffffffff)
desc.bindCount = 0;
}
m_UAVs.push_back(desc);
break;
}
case OPCODE_DCL_CONSTANT_BUFFER:
{
ShaderInputBind desc;
RDCASSERT(dcl.operand.type == TYPE_CONSTANT_BUFFER);
RDCASSERT(dcl.operand.indices.size() == 2);
RDCASSERT(dcl.operand.indices[0].absolute && dcl.operand.indices[1].absolute);
uint32_t idx = (uint32_t)dcl.operand.indices[0].index;
uint32_t numVecs = (uint32_t)dcl.operand.indices[1].index;
StringFormat::snprintf(buf, 63, "cbuffer%u", idx);
desc.name = buf;
desc.type = ShaderInputBind::TYPE_CBUFFER;
desc.space = dcl.space;
desc.reg = idx;
desc.bindCount = 1;
desc.flags = 1;
desc.retType = RETURN_TYPE_UNKNOWN;
desc.dimension = ShaderInputBind::DIM_UNKNOWN;
desc.numSamples = 0;
if(dcl.operand.indices.size() == 3)
{
desc.bindCount = uint32_t(dcl.operand.indices[2].index - dcl.operand.indices[1].index);
if(dcl.operand.indices[2].index == 0xffffffff)
desc.bindCount = 0;
}
CBuffer cb;
cb.name = desc.name;
cb.space = dcl.space;
cb.reg = idx;
cb.bindCount = desc.bindCount;
cb.descriptor.name = cb.name;
cb.descriptor.byteSize = numVecs * 4 * sizeof(float);
cb.descriptor.type = CBuffer::Descriptor::TYPE_CBUFFER;
cb.descriptor.flags = 0;
cb.descriptor.numVars = numVecs;
cb.variables.reserve(numVecs);
for(uint32_t v = 0; v < numVecs; v++)
{
CBufferVariable var;
if(desc.space > 0)
StringFormat::snprintf(buf, 63, "cb%u_%u_v%u", desc.space, desc.reg, v);
else
StringFormat::snprintf(buf, 63, "cb%u_v%u", desc.reg, v);
var.name = buf;
var.descriptor.defaultValue.resize(4 * sizeof(float));
var.descriptor.name = var.name;
var.descriptor.offset = 4 * sizeof(float) * v;
var.descriptor.flags = 0;
var.descriptor.startTexture = (uint32_t)-1;
var.descriptor.startSampler = (uint32_t)-1;
var.descriptor.numSamplers = 0;
var.descriptor.numTextures = 0;
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.members = 0;
var.type.descriptor.type = VARTYPE_FLOAT;
var.type.descriptor.varClass = CLASS_VECTOR;
var.type.descriptor.name = TypeName(var.type.descriptor);
cb.variables.push_back(var);
}
m_CBuffers.push_back(cb);
break;
}
}
}
}
uint32_t DecodeFlags(const ShaderCompileFlags &compileFlags)
{
uint32_t ret = 0;
if(!compileFlags.flags.empty() && compileFlags.flags[0].Name == "compileFlags")
ret = atoi(compileFlags.flags[0].Value.c_str());
return ret;
}
ShaderCompileFlags EncodeFlags(const uint32_t flags)
{
ShaderCompileFlags ret;
ret.flags = {{"compileFlags", StringFormat::Fmt("%u", flags)}};
return ret;
}
ShaderCompileFlags EncodeFlags(const DXBCDebugChunk *dbg)
{
return EncodeFlags(dbg ? dbg->GetShaderCompileFlags() : 0);
}
SDBGChunk::SDBGChunk(void *data)
{
m_HasDebugInfo = false;
m_ShaderFlags = 0;
RDCEraseEl(m_Header);
{
uint32_t *raw = (uint32_t *)data;
if(raw[0] != FOURCC_SDBG)
return;
m_RawData.resize(raw[1]);
memcpy(&m_RawData[0], &raw[2], m_RawData.size());
}
char *dbgData = (char *)&m_RawData[0];
m_Header = *((SDBGHeader *)dbgData);
m_ShaderFlags = m_Header.shaderFlags;
char *dbgPostHeader = dbgData + sizeof(SDBGHeader);
SDBGFileHeader *FileHeaders = (SDBGFileHeader *)(dbgPostHeader + m_Header.files.offset);
SDBGAsmInstruction *Instructions =
(SDBGAsmInstruction *)(dbgPostHeader + m_Header.instructions.offset);
SDBGVariable *Variables = (SDBGVariable *)(dbgPostHeader + m_Header.variables.offset);
SDBGInputRegister *Inputs = (SDBGInputRegister *)(dbgPostHeader + m_Header.inputRegisters.offset);
SDBGSymbol *SymbolTable = (SDBGSymbol *)(dbgPostHeader + m_Header.symbolTable.offset);
SDBGScope *Scopes = (SDBGScope *)(dbgPostHeader + m_Header.scopes.offset);
SDBGType *Types = (SDBGType *)(dbgPostHeader + m_Header.types.offset);
int32_t *Int32DB = (int32_t *)(dbgPostHeader + m_Header.int32DBOffset);
m_FileHeaders = vector<SDBGFileHeader>(FileHeaders, FileHeaders + m_Header.files.count);
m_Instructions =
vector<SDBGAsmInstruction>(Instructions, Instructions + m_Header.instructions.count);
m_Variables = vector<SDBGVariable>(Variables, Variables + m_Header.variables.count);
m_Inputs = vector<SDBGInputRegister>(Inputs, Inputs + m_Header.inputRegisters.count);
m_SymbolTable = vector<SDBGSymbol>(SymbolTable, SymbolTable + m_Header.symbolTable.count);
m_Scopes = vector<SDBGScope>(Scopes, Scopes + m_Header.scopes.count);
m_Types = vector<SDBGType>(Types, Types + m_Header.types.count);
m_Int32Database = vector<int32_t>(
Int32DB, Int32DB + (m_Header.asciiDBOffset - m_Header.int32DBOffset) / sizeof(int32_t));
char *asciiDatabase = dbgPostHeader + m_Header.asciiDBOffset;
m_CompilerSig = asciiDatabase + m_Header.compilerSigOffset;
m_Profile = asciiDatabase + m_Header.profileOffset;
m_Entry = asciiDatabase + m_Header.entryFuncOffset;
for(size_t i = 0; i < m_FileHeaders.size(); i++)
{
string filename =
string(asciiDatabase + m_FileHeaders[i].filenameOffset, m_FileHeaders[i].filenameLen);
string source = string(asciiDatabase + m_FileHeaders[i].sourceOffset, m_FileHeaders[i].sourceLen);
this->Files.push_back(make_pair(filename, source));
}
// successful grab of info
m_HasDebugInfo = true;
}
void SDBGChunk::GetFileLine(size_t instruction, uintptr_t offset, int32_t &fileIdx,
int32_t &lineNum) const
{
if(instruction < m_Instructions.size())
{
int32_t symID = m_Instructions[instruction].symbol;
if(symID > 0 && symID < (int32_t)m_SymbolTable.size())
{
const SDBGSymbol &sym = m_SymbolTable[symID];
fileIdx = sym.fileID;
lineNum = sym.lineNum - 1;
}
}
}
string SDBGChunk::GetSymbolName(int symbolID)
{
RDCASSERT(symbolID >= 0 && symbolID < (int)m_SymbolTable.size());
SDBGSymbol &sym = m_SymbolTable[symbolID];
return GetSymbolName(sym.symbol.offset, sym.symbol.count);
}
string SDBGChunk::GetSymbolName(int32_t symbolOffset, int32_t symbolLength)
{
RDCASSERT(symbolOffset < m_Header.compilerSigOffset);
RDCASSERT(symbolOffset + symbolLength <= m_Header.compilerSigOffset);
int32_t offset = sizeof(m_Header) + m_Header.asciiDBOffset + symbolOffset;
return string(&m_RawData[offset], symbolLength);
}
uint32_t ReadVarLenUInt(byte *&s)
{
// check top two bits. 0b00 (or 0b01) means we just return the byte value.
// 0b10 means we take this byte as high-end byte and next as the low-end
// in a word. (with top two bits masked off)
// 0b11 similar, but for a dword.
if((*s & 0xC0) == 0x00 || (*s & 0xC0) == 0x40)
{
return (uint32_t) * (s++);
}
else if((*s & 0xC0) == 0x80)
{
byte first = *(s++);
first &= 0x7f;
byte second = *(s++);
return (uint32_t(first) << 8) | uint32_t(second);
}
else if((*s & 0xC0) == 0xC0)
{
byte first = *(s++);
first &= 0x3f;
byte second = *(s++);
byte third = *(s++);
byte fourth = *(s++);
return (uint32_t(first) << 24) | (uint32_t(second) << 16) | (uint32_t(third) << 8) |
uint32_t(fourth);
}
else
{
// impossible
return 0;
}
}
SPDBChunk::SPDBChunk(void *chunk)
{
m_HasDebugInfo = false;
uint32_t firstInstructionOffset = 0;
byte *data = NULL;
m_ShaderFlags = 0;
uint32_t spdblength;
{
uint32_t *raw = (uint32_t *)chunk;
if(raw[0] != FOURCC_SPDB)
return;
spdblength = raw[1];
data = (byte *)&raw[2];
}
FileHeaderPage *header = (FileHeaderPage *)data;
if(memcmp(header->identifier, "Microsoft C/C++ MSF 7.00\r\n\032DS\0\0", sizeof(header->identifier)))
{
RDCWARN("Unexpected SPDB type");
return;
}
RDCASSERT(header->PageCount * header->PageSize == spdblength);
const byte **pages = new const byte *[header->PageCount];
for(uint32_t i = 0; i < header->PageCount; i++)
pages[i] = &data[i * header->PageSize];
uint32_t rootdirCount = header->PagesForByteSize(header->RootDirSize);
uint32_t rootDirIndicesCount = header->PagesForByteSize(rootdirCount * sizeof(uint32_t));
PageMapping rootdirIndicesMapping(pages, header->PageSize, header->RootDirectory,
rootDirIndicesCount);
const byte *rootdirIndices = rootdirIndicesMapping.Data();
PageMapping directoryMapping(pages, header->PageSize, (uint32_t *)rootdirIndices, rootdirCount);
const uint32_t *dirContents = (const uint32_t *)directoryMapping.Data();
vector<PDBStream> streams;
streams.resize(*dirContents);
dirContents++;
for(size_t i = 0; i < streams.size(); i++)
{
streams[i].byteLength = *dirContents;
dirContents++;
}
for(size_t i = 0; i < streams.size(); i++)
{
if(streams[i].byteLength == 0)
continue;
for(uint32_t p = 0; p < header->PagesForByteSize(streams[i].byteLength); p++)
{
streams[i].pageIndices.push_back(*dirContents);
dirContents++;
}
}
RDCASSERT(streams.size() > 1);
// stream 1: GUID + stream names
PageMapping guidMapping(pages, header->PageSize, &streams[1].pageIndices[0],
(uint32_t)streams[1].pageIndices.size());
GuidPageHeader *guid = (GuidPageHeader *)guidMapping.Data();
uint32_t *hashtable = (uint32_t *)(guid->Strings + guid->StringBytes);
uint32_t numSetBits = hashtable[0];
hashtable++;
uint32_t maxBit = hashtable[0];
hashtable++;
uint32_t setBitsetWords = hashtable[0];
hashtable++;
uint32_t *setBitset = hashtable;
hashtable += setBitsetWords;
RDCASSERT(hashtable[0] == 0);
hashtable++;
map<string, uint32_t> StreamNames;
uint32_t numset = 0;
for(uint32_t i = 0; i < maxBit; i++)
{
if((setBitset[(i / 32)] & (1 << (i % 32))) != 0)
{
uint32_t strOffs = hashtable[0];
hashtable++;
uint32_t stream = hashtable[0];
hashtable++;
char *streamName = guid->Strings + strOffs;
StreamNames[streamName] = stream;
numset++;
}
}
RDCASSERT(numset == numSetBits);
for(auto it = StreamNames.begin(); it != StreamNames.end(); ++it)
{
if(!strncmp(it->first.c_str(), "/src/files/", 11))
{
PDBStream &s = streams[it->second];
PageMapping fileContents(pages, header->PageSize, &s.pageIndices[0],
(uint32_t)s.pageIndices.size());
const char *filename = (const char *)it->first.c_str();
filename += sizeof("/src/files/") - 1;
if(filename[0] == 0)
filename = "shader";
Files.push_back(make_pair(filename, (char *)fileContents.Data()));
}
}
vector<Function> functions;
if(streams.size() >= 5)
{
PDBStream &s = streams[4];
PageMapping fileContents(pages, header->PageSize, &s.pageIndices[0],
(uint32_t)s.pageIndices.size());
byte *bytes = (byte *)fileContents.Data();
byte *end = bytes + s.byteLength;
// seems to be accurate, but we'll just iterate to end
// uint32_t *u32 = (uint32_t *)bytes;
// uint32_t numFuncs = u32[6];
// skip header
bytes += 57;
while(bytes < end)
{
Function f;
memcpy(&f, bytes, 11);
bytes += 11;
f.funcName = (const char *)bytes;
bytes += 1 + f.funcName.length();
while(*bytes)
{
f.things.push_back(*(int8_t *)bytes);
bytes++;
}
functions.push_back(f);
}
}
{
Function mainFunc;
mainFunc.funcName = "entrypoint";
functions.push_back(mainFunc);
}
map<uint32_t, string> Names;
if(StreamNames.find("/names") != StreamNames.end())
{
PDBStream &s = streams[StreamNames["/names"]];
PageMapping namesMapping(pages, header->PageSize, &s.pageIndices[0],
(uint32_t)s.pageIndices.size());
const uint32_t *contents = (const uint32_t *)namesMapping.Data();
RDCASSERT(contents[0] == 0xeffeeffe && contents[1] == 1);
uint32_t StringBytes = contents[2];
char *Strings = (char *)&contents[3];
contents += 3;
contents = (uint32_t *)((byte *)contents + StringBytes);
uint32_t numHashes = contents[0];
contents++;
for(uint32_t i = 0; i < numHashes; i++)
{
uint32_t idx = contents[0];
contents++;
if(idx != 0)
Names[idx] = Strings + idx;
}
}
vector<DBIModule> modules;
{
PageMapping dbiMapping(pages, header->PageSize, &streams[3].pageIndices[0],
(uint32_t)streams[3].pageIndices.size());
DBIHeader *dbi = (DBIHeader *)dbiMapping.Data();
RDCASSERT(dbi->sig == 0xffffffff);
RDCASSERT(dbi->ver == 19990903);
byte *cur = (byte *)(dbi + 1);
byte *end = cur + dbi->gpmodiSize;
while(cur < end)
{
DBIModule *mod = (DBIModule *)cur;
cur += sizeof(DBIModule) - sizeof(string) * 2;
char *moduleName = (char *)cur;
cur += strlen(moduleName) + 1;
char *objectName = (char *)cur;
cur += strlen(objectName) + 1;
// align up to DWORD boundary
while((uintptr_t)cur & 0x3)
cur++;
DBIModule m;
memcpy(&m, mod, sizeof(DBIModule) - sizeof(string) * 2);
m.moduleName = moduleName;
m.objectName = objectName;
modules.push_back(m);
}
RDCASSERT(cur == end);
}
vector<FuncCallLineNumbers> funcCalls;
map<uint32_t, int32_t> FileMapping; // mapping from hash chunk to index in Files[], or -1
for(size_t m = 0; m < modules.size(); m++)
{
if(modules[m].stream == -1)
continue;
PDBStream &s = streams[modules[m].stream];
PageMapping modMapping(pages, header->PageSize, &s.pageIndices[0],
(uint32_t)s.pageIndices.size());
uint32_t *moduledata = (uint32_t *)modMapping.Data();
RDCASSERT(moduledata[0] == 4);
byte *cur = (byte *)&moduledata[1];
byte *end = (byte *)moduledata + modules[m].cbSyms;
while(cur < end)
{
uint16_t *sym = (uint16_t *)cur;
uint16_t len = sym[0];
uint16_t type = sym[1];
len -= sizeof(uint16_t); // len includes type uint16, subtract for ease of use
cur += sizeof(uint16_t) * 2;
byte *contents = cur;
if(type == 0x1110)
{
ProcHeader *proc = (ProcHeader *)contents;
// char *name = (char *)(proc + 1);
firstInstructionOffset = proc->Offset;
// RDCDEBUG("Got global procedure start %s %x -> %x", name, proc->Offset,
// proc->Offset+proc->Length);
}
else if(type == 0x113c)
{
CompilandDetails *details = (CompilandDetails *)contents;
char *compilerString = (char *)&details->CompilerSig;
memcpy(&m_CompilandDetails, details, sizeof(CompilandDetails) - sizeof(string));
m_CompilandDetails.CompilerSig = compilerString;
/*
RDCDEBUG("CompilandDetails: %s (%d.%d.%d.%d)", compilerString,
details->FrontendVersion.Major, details->FrontendVersion.Minor,
details->FrontendVersion.Build, details->FrontendVersion.QFE);*/
// for hlsl/fxc
// RDCASSERT(details->Language == 16 && details->Platform == 256);
}
else if(type == 0x113d)
{
// for hlsl/fxc?
// RDCASSERT(contents[0] == 0x1);
char *key = (char *)contents + 1;
while(key[0])
{
char *value = key + strlen(key) + 1;
// RDCDEBUG("CompilandEnv: %s = \"%s\"", key, value);
if(!strcmp(key, "hlslEntry"))
{
m_Entry = value;
}
else if(!strcmp(key, "hlslTarget"))
{
m_Profile = value;
}
else if(!strcmp(key, "hlslFlags"))
{
if(value[0] == '0' && value[1] == 'x')
{
int i = 2;
m_ShaderFlags = 0;
while(value[i] != 0)
{
uint32_t digit = 0;
if(value[i] >= '0' && value[i] <= '9')
digit = (uint32_t)(value[i] - '0');
if(value[i] >= 'a' && value[i] <= 'f')
digit = 0xa + (uint32_t)(value[i] - 'a');
if(value[i] >= 'A' && value[i] <= 'F')
digit = 0xa + (uint32_t)(value[i] - 'A');
m_ShaderFlags <<= 4;
m_ShaderFlags |= digit;
i++;
}
}
}
else if(!strcmp(key, "hlslDefines"))
{
string cmdlineDefines = "// Command line defines:\n\n";
char *c = value;
while(*c)
{
// skip whitespace
while(*c && (*c == ' ' || *c == '\t' || *c == '\n'))
c++;
if(*c == 0)
break;
// start of a definition
if(c[0] == '/' && c[1] == 'D')
{
c += 2;
// skip whitespace
while(*c && (*c == ' ' || *c == '\t' || *c == '\n'))
c++;
if(*c == 0)
break;
char *defstart = c;
char *defend = strchr(c, '=');
if(defend == 0)
break;
c = defend + 1;
char *valstart = c;
// skip to end or next whitespace
while(*c && *c != ' ' && *c != '\t' && *c != '\n')
c++;
char *valend = c;
cmdlineDefines += "#define ";
cmdlineDefines += string(defstart, defend) + " " + string(valstart, valend);
cmdlineDefines += "\n";
}
}
Files.push_back(make_pair("@cmdline", cmdlineDefines));
}
key = value + strlen(value) + 1;
}
}
else if(type == 0x114D)
{
// RDCDEBUG("0x%04x, %d bytes", uint32_t(type), uint32_t(len));
FuncCallLineNumbers func;
func.fileOffs = 0;
func.baseLineNum = 0;
byte *iterator = (byte *)contents;
byte *callend = contents + len;
// uint32_t *adsf = (uint32_t *)iterator;
// RDCDEBUG("funcdef for %s (%x) flags??=0x%x offset/length??=0x%x",
// functions[adsf[2]&0xfff].funcName.c_str(), adsf[2], adsf[0], adsf[1]);
iterator += 3 * sizeof(uint32_t);
bool working = true;
uint32_t currentBytes = firstInstructionOffset;
uint32_t currentLine = 0;
uint32_t currentColStart = 1;
uint32_t currentColEnd = 100000;
while(iterator < callend)
{
FuncCallBytestreamOpcodes opcode = (FuncCallBytestreamOpcodes)*iterator;
iterator++;
if(opcode == PrologueEnd || opcode == EpilogueBegin)
{
// uint32_t value = ReadVarLenUInt(iterator);
// RDCDEBUG("type %02x: unk=%02x", opcode, value);
if(opcode == EpilogueBegin)
{
// RDCDEBUG(" (endloc: %04x - %u [%u,%u] )", currentBytes,
// currentLine, currentColStart, currentColEnd);
working = false;
}
}
else if(opcode == FunctionEndNoAdvance)
{
uint32_t value = ReadVarLenUInt(iterator);
// RDCDEBUG(" type %02x: %02x: adjust line by 4(?!) & bytes by %x",
// opcode, value, value);
if(working)
{
InstructionLocation loc;
loc.offset = currentBytes;
loc.line = currentLine;
loc.colStart = currentColStart;
loc.colEnd = currentColEnd;
func.locations.push_back(loc);
loc.offset = currentBytes + value;
loc.funcEnd = true;
func.locations.push_back(loc);
// RDCDEBUG(" (loc: %04x - %u [%u,%u] )", currentBytes,
// currentLine, currentColStart, currentColEnd);
}
currentBytes += value;
}
else if(opcode == AdvanceBytesAndLines)
{
uint32_t value = (uint32_t)*iterator;
iterator++;
uint32_t byteMod = (value & 0xf);
uint32_t lineMod = (value >> 4);
currentBytes += byteMod;
currentLine += lineMod / 2;
// RDCDEBUG(" type %02x: %02x: adjust line by %u & bytes by %x",
// type, value, lineMod/2, byteMod);
}
else if(opcode == EndOfFunction)
{
// RDCDEBUG("type %02x:", opcode);
uint32_t retlen = ReadVarLenUInt(iterator);
uint32_t byteAdv = ReadVarLenUInt(iterator);
// RDCDEBUG(" retlen=%x, byteAdv=%x", retlen, byteAdv);
currentBytes += byteAdv;
if(working)
{
InstructionLocation loc;
loc.offset = currentBytes;
loc.line = currentLine;
loc.colStart = currentColStart;
loc.colEnd = currentColEnd;
func.locations.push_back(loc);
loc.offset = currentBytes + retlen;
loc.funcEnd = true;
func.locations.push_back(loc);
}
currentBytes += retlen;
}
else if(opcode == SetByteOffset)
{
currentBytes = ReadVarLenUInt(iterator);
// RDCDEBUG(" type %02x: start at byte offset %x", opcode,
// currentBytes);
}
else if(opcode == AdvanceBytes)
{
uint32_t offs = ReadVarLenUInt(iterator);
currentBytes += offs;
// RDCDEBUG(" type %02x: advance %x bytes", opcode, offs);
if(working)
{
InstructionLocation loc;
loc.offset = currentBytes;
loc.line = currentLine;
loc.colStart = currentColStart;
loc.colEnd = currentColEnd;
func.locations.push_back(loc);
// RDCDEBUG(" (loc: %04x - %u [%u,%u] )", currentBytes,
// currentLine, currentColStart, currentColEnd);
}
}
else if(opcode == AdvanceLines)
{
uint32_t linesAdv = ReadVarLenUInt(iterator);
if(linesAdv & 0x1)
currentLine -= (linesAdv / 2);
else
currentLine += (linesAdv / 2);
// RDCDEBUG(" type %02x: advance %u (%u) lines", opcode,
// linesAdv/2, linesAdv);
}
else if(opcode == ColumnStart)
{
currentColStart = ReadVarLenUInt(iterator);
// RDCDEBUG(" type %02x: col < %u", opcode, currentColStart);
}
else if(opcode == ColumnEnd)
{
currentColEnd = ReadVarLenUInt(iterator);
// RDCDEBUG(" type %02x: col > %u", opcode, currentColEnd);
}
else if(opcode == EndStream)
{
while(*iterator == 0 && iterator < callend)
iterator++;
// RDCASSERT(iterator == callend); // seems like this isn't always true
}
else
{
RDCDEBUG("Unrecognised: %02x", opcode);
break;
}
}
if(func.locations.size() == 1)
{
// not sure what this means, but it seems to just be intended to match
// one instruction offset and we don't have an 0xc to 'cap' things off.
// just insert a dummy location at the same file line but with a slightly
// higher offset so we have a valid range to match against
auto loc = func.locations[0];
loc.offset++;
func.locations.push_back(loc);
}
RDCASSERT(func.locations.size() > 1);
funcCalls.push_back(func);
// RDCDEBUG("Lost %d bytes after we stopped processing", callend - iterator);
}
else if(type == 0x113E)
{
// not currently used
/*
//RDCDEBUG("0x%04x, %d bytes", uint32_t(type), uint32_t(len));
RegisterVariableAssign *var = (RegisterVariableAssign *)contents;
string funcName = "undefined";
if((size_t)(var->func&0xfff) < functions.size())
funcName = functions[var->func&0xfff].funcName;
//RDCDEBUG(" in %s (%x) flags??=%04x, %s:", funcName.c_str(), var->func,
var->unkflags, var->name);
byte *afterName = (byte *)var->name + (strlen(var->name) + 1);
byte *end = contents + len;
// seems to always be 0s
while(afterName < end)
{
RDCASSERT(*afterName == 0);
afterName++;
}
*/
}
else if(type == 0x1150)
{
// not currently used
/*
RDCASSERT(len %4 == 0);
//RDCDEBUG("0x%04x, %d bytes", uint32_t(type), uint32_t(len));
RegisterVariableAssignComponent *comp = (RegisterVariableAssignComponent *)contents;
OperandType t = comp->Type();
const char *type = "";
switch(t)
{
case TYPE_TEMP: type = "r"; break;
case TYPE_INPUT: type = "v"; break;
case TYPE_OUTPUT: type = "o"; break;
case TYPE_INDEXABLE_TEMP: type = "x"; break;
case TYPE_INPUT_THREAD_ID: type = "globalIdx"; break;
case TYPE_INPUT_THREAD_ID_IN_GROUP: type = "localIdx"; break;
case TYPE_INPUT_THREAD_GROUP_ID: type = "groupIdx"; break;
default: break;
}
uint16_t destComp = comp->destComp;
if(len == 24)
destComp = comp->unkE;
if(len > 24)
{
uint16_t *end = (uint16_t *)(contents + len);
destComp = end[-1];
}
char comps[] = "xyzw";
//RDCDEBUG("%s%d.%c (%x, %x) <- <above>.%c @ 0x%x", type, (destComp)>>4,
comps[(destComp&0xf)>>2], comp->destComp, comp->unkE, comps[(comp->srcComp&0xf)>>2],
comp->instrOffset);
//RDCDEBUG(" A:%04x B:%04x C:%04x D:%04x", comp->unkA, comp->unkB, comp->unkC,
comp->unkD);
//RDCDEBUG(" E(d):%04x", comp->unkE);
uint32_t *extra = (uint32_t *)(comp+1);
for(uint16_t l=20; l < len; l+=4)
{
//RDCDEBUG(" %08x", extra[0]);
extra++;
}
*/
}
else if(type == 0x114E)
{
RDCASSERT(len == 0);
// RDCDEBUG("0x%04x, %d bytes", uint32_t(type), uint32_t(len));
}
else if(type == 0x0006)
{
// RDCDEBUG("end");
}
else
{
// RDCDEBUG("(unexpected?) 0x%04x", uint32_t(type));
}
cur += len;
}
RDCASSERT(cur == end);
end = cur + modules[m].cbLines;
while(cur < end)
{
uint16_t *type = (uint16_t *)cur;
if(*type == 0xF4) // hash
{
uint32_t *len = (uint32_t *)(type + 2);
cur = (byte *)(len + 1);
byte *start = cur;
while(cur < start + *len)
{
uint32_t *hashData = (uint32_t *)cur;
cur += sizeof(uint32_t);
uint16_t *unknown = (uint16_t *)cur;
cur += sizeof(uint16_t);
uint32_t chunkOffs = uint32_t((byte *)hashData - start);
uint32_t nameoffset = hashData[0];
// if this is 0, we don't have a hash
if(*unknown)
{
byte hash[16];
memcpy(hash, cur, sizeof(hash));
cur += sizeof(hash);
int32_t fileIdx = -1;
for(size_t i = 0; i < Files.size(); i++)
{
if(!_stricmp(Files[i].first.c_str(), Names[nameoffset].c_str()))
{
fileIdx = (int32_t)i;
break;
}
}
FileMapping[chunkOffs] = fileIdx;
}
else
{
// this is a 'virtual' file. Create a source file that we can map lines to just for
// something,
// as we won't be able to reliably get the real source lines back. The PDB lies
// convincingly about the
// source according to #line
if(Names.find(nameoffset) != Names.end())
{
string name = Names[nameoffset];
Files.push_back(make_pair(name, ""));
FileMapping[chunkOffs] = (int32_t)Files.size() - 1;
}
else
{
RDCERR(
"Processing SPDB chunk, encountered nameoffset %d that doesn't correspond to any "
"name.",
nameoffset);
FileMapping[chunkOffs] = -1;
}
}
unknown = (uint16_t *)cur;
cur += sizeof(uint16_t);
// another unknown
}
RDCASSERT(cur == start + *len);
}
else if(*type == 0xF2)
{
uint32_t *len = (uint32_t *)(type + 2);
cur = (byte *)(len + 1);
byte *start = cur;
LineNumbersHeader *hdr = (LineNumbersHeader *)cur;
cur = (byte *)(hdr + 1);
bool hasExtra = (hdr->flags & 0x1);
while(cur < start + *len)
{
FileLineNumbers *file = (FileLineNumbers *)cur;
cur = (byte *)(file + 1);
uint32_t *linedata = (uint32_t *)cur;
cur += (sizeof(uint32_t) + sizeof(uint32_t)) * file->numLines;
if(hasExtra)
cur += (sizeof(uint16_t) + sizeof(uint16_t)) * file->numLines;
int32_t fileIdx = -1;
if(FileMapping.find(file->fileIdx) == FileMapping.end())
{
RDCERR(
"SPDB chunk - line numbers file references index %u not encountered in file "
"mapping",
file->fileIdx);
}
else
{
fileIdx = FileMapping[file->fileIdx];
}
for(uint32_t l = 0; l < file->numLines; l++)
{
uint32_t offs = linedata[0];
uint32_t lineNum = linedata[1] & 0x00fffff;
// uint32_t unknown = linedata[1]>>24;
linedata += 2;
m_LineNumbers[offs] = make_pair(fileIdx, lineNum);
// RDCDEBUG("Offset %x is line %d", offs, lineNum);
}
uint16_t *extraData = (uint16_t *)linedata;
for(uint32_t l = 0; l < file->numLines; l++)
{
// uint16_t unkA = extraData[0];
// uint16_t unkB = extraData[1];
extraData += 2;
}
RDCASSERT((byte *)extraData == cur);
}
RDCASSERT(cur == start + *len);
}
else if(*type == 0xF6)
{
uint32_t *len = (uint32_t *)(type + 2);
cur = (byte *)(len + 1);
uint32_t *calls = (uint32_t *)cur;
uint32_t *callsend = (uint32_t *)(cur + *len);
// 0 seems to indicate no files, 1 indicates files but we don't need
// to care as we can just handle this below.
// RDCDEBUG("start: %x", calls[0]);
calls++;
int idx = 0;
while(calls < callsend)
{
// some kind of control bytes? they have n file mappings following but I'm not sure what
// they mean
if(calls[0] <= 0xfff)
{
calls += 1 + calls[0];
}
else
{
// function call - 3 uint32s: (function idx | 0x1000, FileMapping idx, line # of start
// of function)
// RDCDEBUG("Call to %s(%x) - file %x, line %d",
// functions[calls[0]&0xfff].funcName.c_str(), calls[0], calls[1], calls[2]);
funcCalls[idx].fileOffs = calls[1];
funcCalls[idx].baseLineNum = calls[2];
idx++;
calls += 3;
}
}
cur += *len;
}
else
{
break;
}
}
}
for(size_t i = 0; i < funcCalls.size(); i++)
{
RDCASSERT(funcCalls[i].locations.size() > 1);
if(funcCalls[i].locations.empty() || funcCalls[i].locations.size() == 1)
{
RDCWARN("Skipping patching function call with %d locations", funcCalls[i].locations.size());
continue;
}
RDCASSERT(FileMapping.find(funcCalls[i].fileOffs) != FileMapping.end());
if(FileMapping.find(funcCalls[i].fileOffs) == FileMapping.end())
{
RDCWARN("Got function call patch with fileoffs %x - skipping", funcCalls[i].fileOffs);
continue;
}
// RDCDEBUG("Function call %d", i);
for(size_t j = 0; j < funcCalls[i].locations.size() - 1; j++)
{
auto &loc = funcCalls[i].locations[j];
auto &locNext = funcCalls[i].locations[j + 1];
// don't apply between function end and next section (if there is one)
if(loc.funcEnd)
continue;
int nPatched = 0;
for(auto it = m_LineNumbers.begin(); it != m_LineNumbers.end(); ++it)
{
if(it->first >= loc.offset && it->first < locNext.offset)
{
int32_t fileIdx = FileMapping[funcCalls[i].fileOffs];
/*
RDCDEBUG("Patching offset %x between [%x,%x] from (%d,%u) to (%d,%u [%u->%u])",
it->first, loc.offset, locNext.offset,
it->second.first, it->second.second,
fileIdx, loc.line+funcCalls[i].baseLineNum,
loc.colStart, loc.colEnd);
*/
it->second.first = fileIdx;
it->second.second = loc.line + funcCalls[i].baseLineNum;
nPatched++;
}
}
/*
if(nPatched == 0)
RDCDEBUG("Can't find anything between offsets %x,%x as desired", loc.offset,
locNext.offset);*/
}
}
delete[] pages;
m_HasDebugInfo = true;
}
void SPDBChunk::GetFileLine(size_t instruction, uintptr_t offset, int32_t &fileIdx,
int32_t &lineNum) const
{
for(auto it = m_LineNumbers.begin(); it != m_LineNumbers.end(); ++it)
{
if((uintptr_t)it->first <= offset)
{
fileIdx = it->second.first;
lineNum = it->second.second - 1; // 0-indexed
}
else
{
return;
}
}
}
}; // namespace DXBC