diff --git a/renderdoc/driver/d3d11/d3d11_device.cpp b/renderdoc/driver/d3d11/d3d11_device.cpp index e1b1309bd..4121ca380 100644 --- a/renderdoc/driver/d3d11/d3d11_device.cpp +++ b/renderdoc/driver/d3d11/d3d11_device.cpp @@ -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, 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 &WrappedID3D11Device::GetShaderDebugInfoSearchPaths() -{ - return DXBC_Debug_SearchDirPaths(); -} - void WrappedID3D11Device::AddDebugMessage(MessageCategory c, MessageSeverity sv, MessageSource src, rdcstr d) { diff --git a/renderdoc/driver/d3d11/d3d11_device.h b/renderdoc/driver/d3d11/d3d11_device.h index 6be154fde..7202421e3 100644 --- a/renderdoc/driver/d3d11/d3d11_device.h +++ b/renderdoc/driver/d3d11/d3d11_device.h @@ -454,7 +454,6 @@ public: return m_LayoutDescs[layout]; } - const rdcarray &GetShaderDebugInfoSearchPaths(); template bool Serialise_CaptureScope(SerialiserType &ser); diff --git a/renderdoc/driver/d3d11/d3d11_resources.cpp b/renderdoc/driver/d3d11/d3d11_resources.cpp index 60cadb7d1..3d297d9f5 100644 --- a/renderdoc/driver/d3d11/d3d11_resources.cpp +++ b/renderdoc/driver/d3d11/d3d11_resources.cpp @@ -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 originalBytecode; - - originalBytecode.resize((size_t)originalShaderSize); - FileIO::fread(&originalBytecode[0], sizeof(byte), (size_t)originalShaderSize, - originalShaderFile); - - if(lz4) - { - rdcarray 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( diff --git a/renderdoc/driver/d3d11/d3d11_resources.h b/renderdoc/driver/d3d11/d3d11_resources.h index dbe69234f..091d667b9 100644 --- a/renderdoc/driver/d3d11/d3d11_resources.h +++ b/renderdoc/driver/d3d11/d3d11_resources.h @@ -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 *m_DebugInfoSearchPaths; - rdcarray m_Bytecode; + bytebuf m_Bytecode; bool m_Built = false; DXBC::DXBCContainer *m_DXBCFile; diff --git a/renderdoc/driver/d3d12/d3d12_resources.cpp b/renderdoc/driver/d3d12/d3d12_resources.cpp index a269377ee..deb1bcd85 100644 --- a/renderdoc/driver/d3d12/d3d12_resources.cpp +++ b/renderdoc/driver/d3d12/d3d12_resources.cpp @@ -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 originalBytecode; - - originalBytecode.resize((size_t)originalShaderSize); - FileIO::fread(&originalBytecode[0], sizeof(byte), (size_t)originalShaderSize, - originalShaderFile); - - if(lz4) - { - rdcarray 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)); diff --git a/renderdoc/driver/d3d12/d3d12_resources.h b/renderdoc/driver/d3d12/d3d12_resources.h index b03ab85dd..9c3a1c087 100644 --- a/renderdoc/driver/d3d12/d3d12_resources.h +++ b/renderdoc/driver/d3d12/d3d12_resources.h @@ -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 *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 *m_DebugInfoSearchPaths; - - rdcarray m_Bytecode; + bytebuf m_Bytecode; bool m_Built; DXBC::DXBCContainer *m_DXBCFile; diff --git a/renderdoc/driver/shaders/dxbc/dxbc_container.cpp b/renderdoc/driver/shaders/dxbc/dxbc_container.cpp index f654e6ad5..a1b7d127f 100644 --- a/renderdoc/driver/shaders/dxbc/dxbc_container.cpp +++ b/renderdoc/driver/shaders/dxbc/dxbc_container.cpp @@ -27,13 +27,18 @@ #include #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, 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 &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 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; diff --git a/renderdoc/driver/shaders/dxbc/dxbc_container.h b/renderdoc/driver/shaders/dxbc/dxbc_container.h index 3d7a2f93a..3420553f9 100644 --- a/renderdoc/driver/shaders/dxbc/dxbc_container.h +++ b/renderdoc/driver/shaders/dxbc/dxbc_container.h @@ -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; diff --git a/renderdoc/driver/shaders/dxbc/dxbc_spdb.cpp b/renderdoc/driver/shaders/dxbc/dxbc_spdb.cpp index 5700ec656..5456ee582 100644 --- a/renderdoc/driver/shaders/dxbc/dxbc_spdb.cpp +++ b/renderdoc/driver/shaders/dxbc/dxbc_spdb.cpp @@ -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 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