mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-05-06 01:50:38 +00:00
Handle debug search paths on D3D12 correctly
* We also need to unwrap the needless PDB wrapper on MS's separate debug info setup.
This commit is contained in:
@@ -25,7 +25,6 @@
|
||||
|
||||
#include "d3d11_device.h"
|
||||
#include "core/core.h"
|
||||
#include "core/settings.h"
|
||||
#include "driver/dxgi/dxgi_wrapped.h"
|
||||
#include "jpeg-compressor/jpge.h"
|
||||
#include "maths/formatpacking.h"
|
||||
@@ -39,9 +38,6 @@
|
||||
#include "d3d11_resources.h"
|
||||
#include "d3d11_shader_cache.h"
|
||||
|
||||
RDOC_CONFIG(rdcarray<rdcstr>, DXBC_Debug_SearchDirPaths, {},
|
||||
"Paths to search for separated shader debug PDBs.");
|
||||
|
||||
WRAPPED_POOL_INST(WrappedID3D11Device);
|
||||
|
||||
WrappedID3D11Device *WrappedID3D11Device::m_pCurrentWrappedDevice = NULL;
|
||||
@@ -790,11 +786,6 @@ rdcstr WrappedID3D11Device::GetChunkName(uint32_t idx)
|
||||
return ToStr((D3D11Chunk)idx);
|
||||
}
|
||||
|
||||
const rdcarray<rdcstr> &WrappedID3D11Device::GetShaderDebugInfoSearchPaths()
|
||||
{
|
||||
return DXBC_Debug_SearchDirPaths();
|
||||
}
|
||||
|
||||
void WrappedID3D11Device::AddDebugMessage(MessageCategory c, MessageSeverity sv, MessageSource src,
|
||||
rdcstr d)
|
||||
{
|
||||
|
||||
@@ -454,7 +454,6 @@ public:
|
||||
return m_LayoutDescs[layout];
|
||||
}
|
||||
|
||||
const rdcarray<rdcstr> &GetShaderDebugInfoSearchPaths();
|
||||
template <typename SerialiserType>
|
||||
bool Serialise_CaptureScope(SerialiserType &ser);
|
||||
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
#include "api/app/renderdoc_app.h"
|
||||
#include "driver/dxgi/dxgi_wrapped.h"
|
||||
#include "driver/shaders/dxbc/dxbc_reflect.h"
|
||||
#include "lz4/lz4.h"
|
||||
#include "d3d11_context.h"
|
||||
#include "d3d11_renderstate.h"
|
||||
|
||||
@@ -72,134 +71,6 @@ Threading::CriticalSection WrappedID3DDeviceContextState::m_Lock;
|
||||
|
||||
const GUID RENDERDOC_ID3D11ShaderGUID_ShaderDebugMagicValue = RENDERDOC_ShaderDebugMagicValue_struct;
|
||||
|
||||
void WrappedShader::ShaderEntry::TryReplaceOriginalByteCode()
|
||||
{
|
||||
if(!DXBC::DXBCContainer::CheckForDebugInfo((const void *)&m_Bytecode[0], m_Bytecode.size()))
|
||||
{
|
||||
rdcstr originalPath = m_DebugInfoPath;
|
||||
|
||||
if(originalPath.empty())
|
||||
originalPath =
|
||||
DXBC::DXBCContainer::GetDebugBinaryPath((const void *)&m_Bytecode[0], m_Bytecode.size());
|
||||
|
||||
if(!originalPath.empty())
|
||||
{
|
||||
bool lz4 = false;
|
||||
|
||||
if(!strncmp(originalPath.c_str(), "lz4#", 4))
|
||||
{
|
||||
originalPath = originalPath.substr(4);
|
||||
lz4 = true;
|
||||
}
|
||||
// could support more if we're willing to compile in the decompressor
|
||||
|
||||
FILE *originalShaderFile = NULL;
|
||||
|
||||
size_t numSearchPaths = m_DebugInfoSearchPaths ? m_DebugInfoSearchPaths->size() : 0;
|
||||
|
||||
rdcstr foundPath;
|
||||
|
||||
// keep searching until we've exhausted all possible path options, or we've found a file that
|
||||
// opens
|
||||
while(originalShaderFile == NULL && !originalPath.empty())
|
||||
{
|
||||
// while we haven't found a file, keep trying through the search paths. For i==0
|
||||
// check the path on its own, in case it's an absolute path.
|
||||
for(size_t i = 0; originalShaderFile == NULL && i <= numSearchPaths; i++)
|
||||
{
|
||||
if(i == 0)
|
||||
{
|
||||
originalShaderFile = FileIO::fopen(originalPath.c_str(), "rb");
|
||||
foundPath = originalPath;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
const rdcstr &searchPath = (*m_DebugInfoSearchPaths)[i - 1];
|
||||
foundPath = searchPath + "/" + originalPath;
|
||||
originalShaderFile = FileIO::fopen(foundPath.c_str(), "rb");
|
||||
}
|
||||
}
|
||||
|
||||
if(originalShaderFile == NULL)
|
||||
{
|
||||
// the "documented" behaviour for D3D debug info names is that when presented with a
|
||||
// relative path containing subfolders like foo/bar/blah.pdb then we should first try to
|
||||
// append it to all search paths as-is, then strip off the top-level subdirectory to get
|
||||
// bar/blah.pdb and try that in all search directories, and keep going. So if we got here
|
||||
// and didn't open a file, try to strip off the the top directory and continue.
|
||||
int32_t offs = originalPath.find_first_of("\\/");
|
||||
|
||||
// if we couldn't find a directory separator there's nothing to do, stop looking
|
||||
if(offs == -1)
|
||||
break;
|
||||
|
||||
// otherwise strip up to there and keep going
|
||||
originalPath.erase(0, offs + 1);
|
||||
}
|
||||
}
|
||||
|
||||
if(originalShaderFile == NULL)
|
||||
return;
|
||||
|
||||
FileIO::fseek64(originalShaderFile, 0L, SEEK_END);
|
||||
uint64_t originalShaderSize = FileIO::ftell64(originalShaderFile);
|
||||
FileIO::fseek64(originalShaderFile, 0, SEEK_SET);
|
||||
|
||||
if(lz4 || originalShaderSize >= m_Bytecode.size())
|
||||
{
|
||||
rdcarray<byte> originalBytecode;
|
||||
|
||||
originalBytecode.resize((size_t)originalShaderSize);
|
||||
FileIO::fread(&originalBytecode[0], sizeof(byte), (size_t)originalShaderSize,
|
||||
originalShaderFile);
|
||||
|
||||
if(lz4)
|
||||
{
|
||||
rdcarray<byte> decompressed;
|
||||
|
||||
// first try decompressing to 1MB flat
|
||||
decompressed.resize(100 * 1024);
|
||||
|
||||
int ret = LZ4_decompress_safe((const char *)&originalBytecode[0], (char *)&decompressed[0],
|
||||
(int)originalBytecode.size(), (int)decompressed.size());
|
||||
|
||||
if(ret < 0)
|
||||
{
|
||||
// if it failed, either source is corrupt or we didn't allocate enough space.
|
||||
// Just allocate 255x compressed size since it can't need any more than that.
|
||||
decompressed.resize(255 * originalBytecode.size());
|
||||
|
||||
ret = LZ4_decompress_safe((const char *)&originalBytecode[0], (char *)&decompressed[0],
|
||||
(int)originalBytecode.size(), (int)decompressed.size());
|
||||
|
||||
if(ret < 0)
|
||||
{
|
||||
RDCERR("Failed to decompress LZ4 data from %s", foundPath.c_str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
RDCASSERT(ret > 0, ret);
|
||||
|
||||
// we resize and memcpy instead of just doing .swap() because that would
|
||||
// transfer over the over-large pessimistic capacity needed for decompression
|
||||
originalBytecode.resize(ret);
|
||||
memcpy(&originalBytecode[0], &decompressed[0], originalBytecode.size());
|
||||
}
|
||||
|
||||
if(DXBC::DXBCContainer::CheckForDebugInfo((const void *)&originalBytecode[0],
|
||||
originalBytecode.size()))
|
||||
{
|
||||
m_Bytecode.swap(originalBytecode);
|
||||
}
|
||||
}
|
||||
|
||||
FileIO::fclose(originalShaderFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WrappedShader::ShaderEntry::BuildReflection()
|
||||
{
|
||||
RDCCOMPILE_ASSERT(
|
||||
|
||||
@@ -911,12 +911,11 @@ public:
|
||||
class ShaderEntry
|
||||
{
|
||||
public:
|
||||
ShaderEntry() : m_DebugInfoSearchPaths(NULL), m_DXBCFile(NULL) {}
|
||||
ShaderEntry() : m_DXBCFile(NULL) {}
|
||||
ShaderEntry(WrappedID3D11Device *device, ResourceId id, const byte *code, size_t codeLen)
|
||||
{
|
||||
m_ID = id;
|
||||
m_Bytecode.assign(code, codeLen);
|
||||
m_DebugInfoSearchPaths = &device->GetShaderDebugInfoSearchPaths();
|
||||
m_DXBCFile = NULL;
|
||||
}
|
||||
~ShaderEntry()
|
||||
@@ -930,8 +929,8 @@ public:
|
||||
{
|
||||
if(m_DXBCFile == NULL && !m_Bytecode.empty())
|
||||
{
|
||||
TryReplaceOriginalByteCode();
|
||||
m_DXBCFile = new DXBC::DXBCContainer((const void *)&m_Bytecode[0], m_Bytecode.size());
|
||||
m_DXBCFile = new DXBC::DXBCContainer(m_Bytecode, m_DebugInfoPath);
|
||||
m_Bytecode.clear();
|
||||
}
|
||||
return m_DXBCFile;
|
||||
}
|
||||
@@ -962,9 +961,8 @@ public:
|
||||
ResourceId m_ID;
|
||||
|
||||
rdcstr m_DebugInfoPath;
|
||||
const rdcarray<rdcstr> *m_DebugInfoSearchPaths;
|
||||
|
||||
rdcarray<byte> m_Bytecode;
|
||||
bytebuf m_Bytecode;
|
||||
|
||||
bool m_Built = false;
|
||||
DXBC::DXBCContainer *m_DXBCFile;
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
|
||||
#include "d3d12_resources.h"
|
||||
#include "driver/shaders/dxbc/dxbc_reflect.h"
|
||||
#include "lz4/lz4.h"
|
||||
#include "d3d12_command_list.h"
|
||||
#include "d3d12_command_queue.h"
|
||||
|
||||
@@ -34,134 +33,6 @@ bool WrappedID3D12Shader::m_InternalResources = false;
|
||||
|
||||
const GUID RENDERDOC_ID3D12ShaderGUID_ShaderDebugMagicValue = RENDERDOC_ShaderDebugMagicValue_struct;
|
||||
|
||||
void WrappedID3D12Shader::TryReplaceOriginalByteCode()
|
||||
{
|
||||
if(!DXBC::DXBCContainer::CheckForDebugInfo((const void *)&m_Bytecode[0], m_Bytecode.size()))
|
||||
{
|
||||
rdcstr originalPath = m_DebugInfoPath;
|
||||
|
||||
if(originalPath.empty())
|
||||
originalPath =
|
||||
DXBC::DXBCContainer::GetDebugBinaryPath((const void *)&m_Bytecode[0], m_Bytecode.size());
|
||||
|
||||
if(!originalPath.empty())
|
||||
{
|
||||
bool lz4 = false;
|
||||
|
||||
if(!strncmp(originalPath.c_str(), "lz4#", 4))
|
||||
{
|
||||
originalPath = originalPath.substr(4);
|
||||
lz4 = true;
|
||||
}
|
||||
// could support more if we're willing to compile in the decompressor
|
||||
|
||||
FILE *originalShaderFile = NULL;
|
||||
|
||||
size_t numSearchPaths = m_DebugInfoSearchPaths ? m_DebugInfoSearchPaths->size() : 0;
|
||||
|
||||
rdcstr foundPath;
|
||||
|
||||
// keep searching until we've exhausted all possible path options, or we've found a file that
|
||||
// opens
|
||||
while(originalShaderFile == NULL && !originalPath.empty())
|
||||
{
|
||||
// while we haven't found a file, keep trying through the search paths. For i==0
|
||||
// check the path on its own, in case it's an absolute path.
|
||||
for(size_t i = 0; originalShaderFile == NULL && i <= numSearchPaths; i++)
|
||||
{
|
||||
if(i == 0)
|
||||
{
|
||||
originalShaderFile = FileIO::fopen(originalPath.c_str(), "rb");
|
||||
foundPath = originalPath;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
const rdcstr &searchPath = (*m_DebugInfoSearchPaths)[i - 1];
|
||||
foundPath = searchPath + "/" + originalPath;
|
||||
originalShaderFile = FileIO::fopen(foundPath.c_str(), "rb");
|
||||
}
|
||||
}
|
||||
|
||||
if(originalShaderFile == NULL)
|
||||
{
|
||||
// the "documented" behaviour for D3D debug info names is that when presented with a
|
||||
// relative path containing subfolders like foo/bar/blah.pdb then we should first try to
|
||||
// append it to all search paths as-is, then strip off the top-level subdirectory to get
|
||||
// bar/blah.pdb and try that in all search directories, and keep going. So if we got here
|
||||
// and didn't open a file, try to strip off the the top directory and continue.
|
||||
int32_t offs = originalPath.find_first_of("\\/");
|
||||
|
||||
// if we couldn't find a directory separator there's nothing to do, stop looking
|
||||
if(offs == -1)
|
||||
break;
|
||||
|
||||
// otherwise strip up to there and keep going
|
||||
originalPath.erase(0, offs + 1);
|
||||
}
|
||||
}
|
||||
|
||||
if(originalShaderFile == NULL)
|
||||
return;
|
||||
|
||||
FileIO::fseek64(originalShaderFile, 0L, SEEK_END);
|
||||
uint64_t originalShaderSize = FileIO::ftell64(originalShaderFile);
|
||||
FileIO::fseek64(originalShaderFile, 0, SEEK_SET);
|
||||
|
||||
if(lz4 || originalShaderSize >= m_Bytecode.size())
|
||||
{
|
||||
rdcarray<byte> originalBytecode;
|
||||
|
||||
originalBytecode.resize((size_t)originalShaderSize);
|
||||
FileIO::fread(&originalBytecode[0], sizeof(byte), (size_t)originalShaderSize,
|
||||
originalShaderFile);
|
||||
|
||||
if(lz4)
|
||||
{
|
||||
rdcarray<byte> decompressed;
|
||||
|
||||
// first try decompressing to 1MB flat
|
||||
decompressed.resize(100 * 1024);
|
||||
|
||||
int ret = LZ4_decompress_safe((const char *)&originalBytecode[0], (char *)&decompressed[0],
|
||||
(int)originalBytecode.size(), (int)decompressed.size());
|
||||
|
||||
if(ret < 0)
|
||||
{
|
||||
// if it failed, either source is corrupt or we didn't allocate enough space.
|
||||
// Just allocate 255x compressed size since it can't need any more than that.
|
||||
decompressed.resize(255 * originalBytecode.size());
|
||||
|
||||
ret = LZ4_decompress_safe((const char *)&originalBytecode[0], (char *)&decompressed[0],
|
||||
(int)originalBytecode.size(), (int)decompressed.size());
|
||||
|
||||
if(ret < 0)
|
||||
{
|
||||
RDCERR("Failed to decompress LZ4 data from %s", foundPath.c_str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
RDCASSERT(ret > 0, ret);
|
||||
|
||||
// we resize and memcpy instead of just doing .swap() because that would
|
||||
// transfer over the over-large pessimistic capacity needed for decompression
|
||||
originalBytecode.resize(ret);
|
||||
memcpy(&originalBytecode[0], &decompressed[0], originalBytecode.size());
|
||||
}
|
||||
|
||||
if(DXBC::DXBCContainer::CheckForDebugInfo((const void *)&originalBytecode[0],
|
||||
originalBytecode.size()))
|
||||
{
|
||||
m_Bytecode.swap(originalBytecode);
|
||||
}
|
||||
}
|
||||
|
||||
FileIO::fclose(originalShaderFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#undef D3D12_TYPE_MACRO
|
||||
#define D3D12_TYPE_MACRO(iface) WRAPPED_POOL_INST(CONCAT(Wrapped, iface));
|
||||
|
||||
|
||||
@@ -681,7 +681,6 @@ public:
|
||||
: WrappedDeviceChild12(NULL, device), m_Key(byteCode)
|
||||
{
|
||||
m_Bytecode.assign((const byte *)byteCode.pShaderBytecode, byteCode.BytecodeLength);
|
||||
m_DebugInfoSearchPaths = NULL;
|
||||
m_DXBCFile = NULL;
|
||||
|
||||
device->GetResourceManager()->AddLiveResource(GetResourceID(), this);
|
||||
@@ -733,12 +732,6 @@ public:
|
||||
}
|
||||
|
||||
DXBCKey GetKey() { return m_Key; }
|
||||
void SetDebugInfoPath(rdcarray<rdcstr> *searchPaths, const rdcstr &path)
|
||||
{
|
||||
m_DebugInfoSearchPaths = searchPaths;
|
||||
m_DebugInfoPath = path;
|
||||
}
|
||||
|
||||
D3D12_SHADER_BYTECODE GetDesc()
|
||||
{
|
||||
D3D12_SHADER_BYTECODE ret;
|
||||
@@ -751,8 +744,7 @@ public:
|
||||
{
|
||||
if(m_DXBCFile == NULL && !m_Bytecode.empty())
|
||||
{
|
||||
TryReplaceOriginalByteCode();
|
||||
m_DXBCFile = new DXBC::DXBCContainer((const void *)&m_Bytecode[0], m_Bytecode.size());
|
||||
m_DXBCFile = new DXBC::DXBCContainer(m_Bytecode, rdcstr());
|
||||
}
|
||||
return m_DXBCFile;
|
||||
}
|
||||
@@ -781,10 +773,7 @@ public:
|
||||
|
||||
DXBCKey m_Key;
|
||||
|
||||
rdcstr m_DebugInfoPath;
|
||||
rdcarray<rdcstr> *m_DebugInfoSearchPaths;
|
||||
|
||||
rdcarray<byte> m_Bytecode;
|
||||
bytebuf m_Bytecode;
|
||||
|
||||
bool m_Built;
|
||||
DXBC::DXBCContainer *m_DXBCFile;
|
||||
|
||||
@@ -27,13 +27,18 @@
|
||||
#include <algorithm>
|
||||
#include "api/app/renderdoc_app.h"
|
||||
#include "common/common.h"
|
||||
#include "core/settings.h"
|
||||
#include "driver/shaders/dxil/dxil_bytecode.h"
|
||||
#include "lz4/lz4.h"
|
||||
#include "serialise/serialiser.h"
|
||||
#include "strings/string_utils.h"
|
||||
#include "dxbc_bytecode.h"
|
||||
|
||||
#include "driver/dx/official/d3dcompiler.h"
|
||||
|
||||
RDOC_CONFIG(rdcarray<rdcstr>, DXBC_Debug_SearchDirPaths, {},
|
||||
"Paths to search for separated shader debug PDBs.");
|
||||
|
||||
namespace DXBC
|
||||
{
|
||||
struct RDEFCBufferVariable
|
||||
@@ -774,25 +779,166 @@ rdcstr DXBCContainer::GetDebugBinaryPath(const void *ByteCode, size_t ByteCodeLe
|
||||
return debugPath;
|
||||
}
|
||||
|
||||
DXBCContainer::DXBCContainer(const void *ByteCode, size_t ByteCodeLength)
|
||||
void DXBCContainer::TryFetchSeparateDebugInfo(bytebuf &byteCode, const rdcstr &debugInfoPath)
|
||||
{
|
||||
RDCASSERT(ByteCodeLength < UINT32_MAX);
|
||||
if(!CheckForDebugInfo((const void *)&byteCode[0], byteCode.size()))
|
||||
{
|
||||
rdcstr originalPath = debugInfoPath;
|
||||
|
||||
if(originalPath.empty())
|
||||
originalPath = GetDebugBinaryPath((const void *)&byteCode[0], byteCode.size());
|
||||
|
||||
if(!originalPath.empty())
|
||||
{
|
||||
bool lz4 = false;
|
||||
|
||||
if(!strncmp(originalPath.c_str(), "lz4#", 4))
|
||||
{
|
||||
originalPath = originalPath.substr(4);
|
||||
lz4 = true;
|
||||
}
|
||||
// could support more if we're willing to compile in the decompressor
|
||||
|
||||
FILE *originalShaderFile = NULL;
|
||||
|
||||
const rdcarray<rdcstr> &searchPaths = DXBC_Debug_SearchDirPaths();
|
||||
|
||||
size_t numSearchPaths = searchPaths.size();
|
||||
|
||||
rdcstr foundPath;
|
||||
|
||||
// keep searching until we've exhausted all possible path options, or we've found a file that
|
||||
// opens
|
||||
while(originalShaderFile == NULL && !originalPath.empty())
|
||||
{
|
||||
// while we haven't found a file, keep trying through the search paths. For i==0
|
||||
// check the path on its own, in case it's an absolute path.
|
||||
for(size_t i = 0; originalShaderFile == NULL && i <= numSearchPaths; i++)
|
||||
{
|
||||
if(i == 0)
|
||||
{
|
||||
originalShaderFile = FileIO::fopen(originalPath.c_str(), "rb");
|
||||
foundPath = originalPath;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
const rdcstr &searchPath = searchPaths[i - 1];
|
||||
foundPath = searchPath + "/" + originalPath;
|
||||
originalShaderFile = FileIO::fopen(foundPath.c_str(), "rb");
|
||||
}
|
||||
}
|
||||
|
||||
if(originalShaderFile == NULL)
|
||||
{
|
||||
// the "documented" behaviour for D3D debug info names is that when presented with a
|
||||
// relative path containing subfolders like foo/bar/blah.pdb then we should first try to
|
||||
// append it to all search paths as-is, then strip off the top-level subdirectory to get
|
||||
// bar/blah.pdb and try that in all search directories, and keep going. So if we got here
|
||||
// and didn't open a file, try to strip off the the top directory and continue.
|
||||
int32_t offs = originalPath.find_first_of("\\/");
|
||||
|
||||
// if we couldn't find a directory separator there's nothing to do, stop looking
|
||||
if(offs == -1)
|
||||
break;
|
||||
|
||||
// otherwise strip up to there and keep going
|
||||
originalPath.erase(0, offs + 1);
|
||||
}
|
||||
}
|
||||
|
||||
if(originalShaderFile == NULL)
|
||||
return;
|
||||
|
||||
FileIO::fseek64(originalShaderFile, 0L, SEEK_END);
|
||||
uint64_t originalShaderSize = FileIO::ftell64(originalShaderFile);
|
||||
FileIO::fseek64(originalShaderFile, 0, SEEK_SET);
|
||||
|
||||
if(lz4 || originalShaderSize >= byteCode.size())
|
||||
{
|
||||
bytebuf debugBytecode;
|
||||
|
||||
debugBytecode.resize((size_t)originalShaderSize);
|
||||
FileIO::fread(&debugBytecode[0], sizeof(byte), (size_t)originalShaderSize,
|
||||
originalShaderFile);
|
||||
|
||||
if(lz4)
|
||||
{
|
||||
rdcarray<byte> decompressed;
|
||||
|
||||
// first try decompressing to 1MB flat
|
||||
decompressed.resize(100 * 1024);
|
||||
|
||||
int ret = LZ4_decompress_safe((const char *)&debugBytecode[0], (char *)&decompressed[0],
|
||||
(int)debugBytecode.size(), (int)decompressed.size());
|
||||
|
||||
if(ret < 0)
|
||||
{
|
||||
// if it failed, either source is corrupt or we didn't allocate enough space.
|
||||
// Just allocate 255x compressed size since it can't need any more than that.
|
||||
decompressed.resize(255 * debugBytecode.size());
|
||||
|
||||
ret = LZ4_decompress_safe((const char *)&debugBytecode[0], (char *)&decompressed[0],
|
||||
(int)debugBytecode.size(), (int)decompressed.size());
|
||||
|
||||
if(ret < 0)
|
||||
{
|
||||
RDCERR("Failed to decompress LZ4 data from %s", foundPath.c_str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
RDCASSERT(ret > 0, ret);
|
||||
|
||||
// we resize and memcpy instead of just doing .swap() because that would
|
||||
// transfer over the over-large pessimistic capacity needed for decompression
|
||||
debugBytecode.resize(ret);
|
||||
memcpy(&debugBytecode[0], &decompressed[0], debugBytecode.size());
|
||||
}
|
||||
|
||||
if(IsPDBFile(&debugBytecode[0], debugBytecode.size()))
|
||||
{
|
||||
UnwrapEmbeddedPDBData(debugBytecode);
|
||||
m_DebugShaderBlob = debugBytecode;
|
||||
}
|
||||
else if(CheckForDebugInfo((const void *)&debugBytecode[0], debugBytecode.size()))
|
||||
{
|
||||
byteCode.swap(debugBytecode);
|
||||
}
|
||||
}
|
||||
|
||||
FileIO::fclose(originalShaderFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DXBCContainer::DXBCContainer(bytebuf &ByteCode, const rdcstr &debugInfoPath)
|
||||
{
|
||||
RDCEraseEl(m_ShaderStats);
|
||||
|
||||
m_ShaderBlob.resize(ByteCodeLength);
|
||||
memcpy(&m_ShaderBlob[0], ByteCode, m_ShaderBlob.size());
|
||||
TryFetchSeparateDebugInfo(ByteCode, debugInfoPath);
|
||||
|
||||
char *data = (char *)&m_ShaderBlob[0]; // just for convenience
|
||||
m_ShaderBlob = ByteCode;
|
||||
|
||||
FileHeader *header = (FileHeader *)&m_ShaderBlob[0];
|
||||
// just for convenience
|
||||
char *data = (char *)m_ShaderBlob.data();
|
||||
char *debugData = (char *)m_DebugShaderBlob.data();
|
||||
|
||||
FileHeader *header = (FileHeader *)data;
|
||||
FileHeader *debugHeader = (FileHeader *)debugData;
|
||||
|
||||
if(header->fourcc != FOURCC_DXBC)
|
||||
return;
|
||||
|
||||
if(header->fileLength != (uint32_t)ByteCodeLength)
|
||||
if(header->fileLength != (uint32_t)ByteCode.size())
|
||||
return;
|
||||
|
||||
if(debugHeader && debugHeader->fourcc != FOURCC_DXBC)
|
||||
debugHeader = NULL;
|
||||
|
||||
if(debugHeader && debugHeader->fileLength != m_DebugShaderBlob.size())
|
||||
debugHeader = NULL;
|
||||
|
||||
memcpy(m_Hash, header->hashValue, sizeof(m_Hash));
|
||||
|
||||
// default to vertex shader to support blobs without RDEF chunks (e.g. used with
|
||||
@@ -800,6 +946,7 @@ DXBCContainer::DXBCContainer(const void *ByteCode, size_t ByteCodeLength)
|
||||
m_Type = DXBC::ShaderType::Vertex;
|
||||
|
||||
uint32_t *chunkOffsets = (uint32_t *)(header + 1); // right after the header
|
||||
uint32_t *debugChunkOffsets = debugHeader ? (uint32_t *)(debugHeader + 1) : NULL;
|
||||
|
||||
for(uint32_t chunkIdx = 0; chunkIdx < header->numChunks; chunkIdx++)
|
||||
{
|
||||
@@ -989,7 +1136,7 @@ DXBCContainer::DXBCContainer(const void *ByteCode, size_t ByteCodeLength)
|
||||
RDEFCBufferVariable *var =
|
||||
(RDEFCBufferVariable *)(chunkContents + cbuf->variables.offset + varStride);
|
||||
|
||||
if(var->nameOffset > ByteCodeLength)
|
||||
if(var->nameOffset > ByteCode.size())
|
||||
{
|
||||
varStride += extraData;
|
||||
}
|
||||
@@ -1001,7 +1148,7 @@ DXBCContainer::DXBCContainer(const void *ByteCode, size_t ByteCodeLength)
|
||||
RDEFCBufferVariable *var =
|
||||
(RDEFCBufferVariable *)(chunkContents + cbuf->variables.offset + vi * varStride);
|
||||
|
||||
RDCASSERT(var->nameOffset < ByteCodeLength);
|
||||
RDCASSERT(var->nameOffset < ByteCode.size());
|
||||
|
||||
CBufferVariable v;
|
||||
|
||||
@@ -1131,6 +1278,19 @@ DXBCContainer::DXBCContainer(const void *ByteCode, size_t ByteCodeLength)
|
||||
}
|
||||
}
|
||||
|
||||
// next search the debug file if it exists
|
||||
for(uint32_t chunkIdx = 0;
|
||||
debugHeader && m_DXILByteCode == NULL && chunkIdx < debugHeader->numChunks; chunkIdx++)
|
||||
{
|
||||
uint32_t *fourcc = (uint32_t *)(debugData + debugChunkOffsets[chunkIdx]);
|
||||
uint32_t *chunkSize = (uint32_t *)(debugData + debugChunkOffsets[chunkIdx] + sizeof(uint32_t));
|
||||
|
||||
char *chunkContents = (char *)(debugData + debugChunkOffsets[chunkIdx] + sizeof(uint32_t) * 2);
|
||||
|
||||
if(*fourcc == FOURCC_ILDB)
|
||||
m_DXILByteCode = new DXIL::Program((const byte *)chunkContents, *chunkSize);
|
||||
}
|
||||
|
||||
// if we didn't find ILDB then we have to get the bytecode from DXIL. However we look for the
|
||||
// STAT chunk and if we find it get reflection from there, since it will have better
|
||||
// information. What a mess.
|
||||
@@ -1390,6 +1550,18 @@ DXBCContainer::DXBCContainer(const void *ByteCode, size_t ByteCodeLength)
|
||||
}
|
||||
}
|
||||
|
||||
// try to find SPDB in the separate debug info pdb now
|
||||
for(uint32_t chunkIdx = 0;
|
||||
debugHeader && m_DebugInfo == NULL && chunkIdx < debugHeader->numChunks; chunkIdx++)
|
||||
{
|
||||
uint32_t *fourcc = (uint32_t *)(debugData + debugChunkOffsets[chunkIdx]);
|
||||
|
||||
if(*fourcc == FOURCC_SPDB)
|
||||
{
|
||||
m_DebugInfo = MakeSPDBChunk(fourcc);
|
||||
}
|
||||
}
|
||||
|
||||
if(m_DXILByteCode)
|
||||
m_DebugInfo = m_DXILByteCode;
|
||||
|
||||
|
||||
@@ -40,6 +40,8 @@ class IDebugInfo;
|
||||
struct Reflection;
|
||||
IDebugInfo *MakeSDBGChunk(void *data);
|
||||
IDebugInfo *MakeSPDBChunk(void *data);
|
||||
bool IsPDBFile(void *data, size_t length);
|
||||
void UnwrapEmbeddedPDBData(bytebuf &bytes);
|
||||
};
|
||||
|
||||
// many thanks to winehq for information of format of RDEF, STAT and SIGN chunks:
|
||||
@@ -140,7 +142,7 @@ ShaderCompileFlags EncodeFlags(const uint32_t flags, const rdcstr &profile);
|
||||
class DXBCContainer
|
||||
{
|
||||
public:
|
||||
DXBCContainer(const void *ByteCode, size_t ByteCodeLength);
|
||||
DXBCContainer(bytebuf &ByteCode, const rdcstr &debugInfoPath);
|
||||
~DXBCContainer();
|
||||
DXBC::ShaderType m_Type = DXBC::ShaderType::Max;
|
||||
struct
|
||||
@@ -171,6 +173,10 @@ private:
|
||||
DXBCContainer(const DXBCContainer &o);
|
||||
DXBCContainer &operator=(const DXBCContainer &o);
|
||||
|
||||
void TryFetchSeparateDebugInfo(bytebuf &byteCode, const rdcstr &debugInfoPath);
|
||||
|
||||
bytebuf m_DebugShaderBlob;
|
||||
|
||||
rdcstr m_Disassembly;
|
||||
|
||||
D3D_PRIMITIVE_TOPOLOGY m_OutputTopology = D3D_PRIMITIVE_TOPOLOGY_UNDEFINED;
|
||||
|
||||
@@ -42,6 +42,20 @@ namespace DXBC
|
||||
{
|
||||
static const uint32_t FOURCC_SPDB = MAKE_FOURCC('S', 'P', 'D', 'B');
|
||||
|
||||
bool IsPDBFile(void *data, size_t length)
|
||||
{
|
||||
FileHeaderPage *header = (FileHeaderPage *)data;
|
||||
|
||||
if(length < sizeof(FileHeaderPage))
|
||||
return false;
|
||||
|
||||
if(memcmp(header->identifier, "Microsoft C/C++ MSF 7.00\r\n\032DS\0\0",
|
||||
sizeof(header->identifier)) != 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
SPDBChunk::SPDBChunk(void *chunk)
|
||||
{
|
||||
m_HasDebugInfo = false;
|
||||
@@ -64,8 +78,7 @@ SPDBChunk::SPDBChunk(void *chunk)
|
||||
|
||||
FileHeaderPage *header = (FileHeaderPage *)data;
|
||||
|
||||
if(memcmp(header->identifier, "Microsoft C/C++ MSF 7.00\r\n\032DS\0\0",
|
||||
sizeof(header->identifier)) != 0)
|
||||
if(!IsPDBFile(data, spdblength))
|
||||
{
|
||||
RDCWARN("Unexpected SPDB type");
|
||||
return;
|
||||
@@ -1822,4 +1835,77 @@ IDebugInfo *MakeSPDBChunk(void *data)
|
||||
return new SPDBChunk(data);
|
||||
}
|
||||
|
||||
void UnwrapEmbeddedPDBData(bytebuf &bytes)
|
||||
{
|
||||
if(!IsPDBFile(bytes.data(), bytes.size()))
|
||||
return;
|
||||
|
||||
FileHeaderPage *header = (FileHeaderPage *)bytes.data();
|
||||
|
||||
uint32_t pageCount = header->PageCount;
|
||||
|
||||
if(pageCount * header->PageSize != bytes.size())
|
||||
{
|
||||
RDCWARN("Corrupt header/pdb. %u pages of %u size doesn't match %zu file size", pageCount,
|
||||
header->PageSize, bytes.size());
|
||||
|
||||
// some DXC versions write the wrong page count, just count ourselves from the file size.
|
||||
if((bytes.size() % header->PageSize) == 0)
|
||||
{
|
||||
header->PageCount = (uint32_t)bytes.size() / header->PageSize;
|
||||
RDCWARN("Correcting page count to %u by dividing file size %zu by page size %u.",
|
||||
header->PageCount, bytes.size(), header->PageSize);
|
||||
}
|
||||
}
|
||||
|
||||
const byte **pages = new const byte *[header->PageCount];
|
||||
for(uint32_t i = 0; i < header->PageCount; i++)
|
||||
pages[i] = &bytes[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();
|
||||
|
||||
rdcarray<PDBStream> streams;
|
||||
|
||||
streams.resize(*dirContents);
|
||||
dirContents++;
|
||||
|
||||
SPDBLOG("SPDB contains %zu streams", streams.size());
|
||||
|
||||
for(size_t i = 0; i < streams.size(); i++)
|
||||
{
|
||||
streams[i].byteLength = *dirContents;
|
||||
SPDBLOG("Stream[%zu] is %u bytes", i, streams[i].byteLength);
|
||||
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++;
|
||||
}
|
||||
}
|
||||
|
||||
if(streams.size() < 5)
|
||||
return;
|
||||
|
||||
// stream 5 is expected to contain the embedded data
|
||||
PageMapping embeddedData(pages, header->PageSize, &streams[5].pageIndices[0],
|
||||
(uint32_t)streams[5].pageIndices.size());
|
||||
|
||||
bytes.assign(embeddedData.Data(), streams[5].byteLength);
|
||||
}
|
||||
|
||||
}; // namespace DXBC
|
||||
|
||||
Reference in New Issue
Block a user