Skip to content

Commit 4039b0f

Browse files
committed
Process undocumented SRCI chunk in DXBC files containing source code
* When building to separate PDBs source code gets stripped out of the debug module and put into a separate chunk, it's all kind of a mess.
1 parent 7115c46 commit 4039b0f

File tree

4 files changed

+335
-2
lines changed

4 files changed

+335
-2
lines changed

renderdoc/driver/shaders/dxbc/dxbc_container.cpp

+322-1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include "driver/shaders/dxil/dxil_bytecode.h"
3333
#include "lz4/lz4.h"
3434
#include "md5/md5.h"
35+
#include "miniz/miniz.h"
3536
#include "replay/replay_driver.h"
3637
#include "serialise/serialiser.h"
3738
#include "dxbc_bytecode.h"
@@ -46,6 +47,7 @@ namespace
4647

4748
// lookup from plain filename -> absolute path of first result in search paths
4849
std::unordered_map<rdcstr, rdcstr> cachedDebugFilesLookup;
50+
int32_t cachedDebugFilesLookupInit = 0;
4951

5052
void CacheSearchDirDebugPaths(rdcstr dir)
5153
{
@@ -70,7 +72,7 @@ void CacheSearchDirDebugPaths(rdcstr dir)
7072

7173
void CacheSearchDirDebugPaths()
7274
{
73-
if(!cachedDebugFilesLookup.empty())
75+
if(Atomic::CmpExch32(&cachedDebugFilesLookupInit, 0, 1) != 0)
7476
return;
7577

7678
if(!RenderDoc::Inst().IsReplayApp())
@@ -92,6 +94,7 @@ namespace DXBC
9294
void ResetSearchDirsCache()
9395
{
9496
cachedDebugFilesLookup.clear();
97+
cachedDebugFilesLookupInit = 0;
9598
}
9699

97100
rdcstr BasicDemangle(const rdcstr &possiblyMangledName)
@@ -2329,6 +2332,33 @@ DXBCContainer::DXBCContainer(const bytebuf &ByteCode, const rdcstr &debugInfoPat
23292332
PreprocessLineDirectives(m_DebugInfo->Files);
23302333
}
23312334

2335+
// if we have DXIL bytecode, check for separate source-info chunks and layer them over the top
2336+
if(m_DXILByteCode)
2337+
{
2338+
// check debug header first, but we don't expect to see these in a non-debug header
2339+
for(uint32_t chunkIdx = 0; debugHeader && chunkIdx < debugHeader->numChunks; chunkIdx++)
2340+
{
2341+
const uint32_t *fourcc = (const uint32_t *)(debugData + debugChunkOffsets[chunkIdx]);
2342+
const uint32_t *chunkSize = (const uint32_t *)(fourcc + 1);
2343+
2344+
const byte *chunkContents = (const byte *)(chunkSize + 1);
2345+
2346+
if(*fourcc == FOURCC_SRCI)
2347+
ProcessSourceInfo(chunkContents, *chunkSize);
2348+
}
2349+
2350+
for(uint32_t chunkIdx = 0; header && chunkIdx < header->numChunks; chunkIdx++)
2351+
{
2352+
const uint32_t *fourcc = (const uint32_t *)(data + chunkOffsets[chunkIdx]);
2353+
const uint32_t *chunkSize = (const uint32_t *)(fourcc + 1);
2354+
2355+
const byte *chunkContents = (const byte *)(chunkSize + 1);
2356+
2357+
if(*fourcc == FOURCC_SRCI)
2358+
ProcessSourceInfo(chunkContents, *chunkSize);
2359+
}
2360+
}
2361+
23322362
// if we had bytecode in this container, ensure we had reflection. If it's a blob with only an
23332363
// input signature then we can do without reflection.
23342364
if(m_DXBCByteCode || m_DXILByteCode)
@@ -2372,6 +2402,297 @@ DXBCContainer::~DXBCContainer()
23722402
SAFE_DELETE(m_Reflection);
23732403
}
23742404

2405+
struct SRCIHeader
2406+
{
2407+
uint32_t size; // utterly redundant?
2408+
uint16_t flags;
2409+
uint16_t numSections;
2410+
};
2411+
2412+
enum class SRCISectionType : uint16_t
2413+
{
2414+
FileContents = 0,
2415+
Filenames,
2416+
Args,
2417+
};
2418+
2419+
struct SRCISection
2420+
{
2421+
uint32_t sectionSize;
2422+
uint16_t flags;
2423+
SRCISectionType type;
2424+
};
2425+
2426+
struct SRCIArgsSection
2427+
{
2428+
uint32_t flags;
2429+
uint32_t dataSize;
2430+
uint32_t numArgs;
2431+
};
2432+
2433+
struct SRCIFileContentsSection
2434+
{
2435+
uint32_t sectionSize; // NOTE For this section only, this is the size of the data *with* the
2436+
// header. Who knows why?
2437+
uint16_t flags;
2438+
uint16_t zLibCompressed;
2439+
uint32_t dataSize;
2440+
uint32_t uncompressedDataSize;
2441+
uint32_t numFiles;
2442+
};
2443+
2444+
struct SRCIFileContentsEntry
2445+
{
2446+
uint32_t entrySize;
2447+
uint32_t flags;
2448+
uint32_t fileSize;
2449+
};
2450+
2451+
struct SRCIFilenamesSection
2452+
{
2453+
uint32_t flags;
2454+
uint32_t numFiles;
2455+
uint16_t dataSize;
2456+
// NOTE: NO PADDING HERE BECAUSE OF COURSE NOT
2457+
byte beginningOfData;
2458+
2459+
static constexpr size_t unpaddedSize() { return offsetof(SRCIFilenamesSection, beginningOfData); }
2460+
};
2461+
2462+
struct SRCIFilenameEntry
2463+
{
2464+
uint32_t entrySize;
2465+
uint32_t flags;
2466+
uint32_t nameSize;
2467+
uint32_t fileSize;
2468+
};
2469+
2470+
void DXBCContainer::ProcessSourceInfo(const byte *chunkContents, uint32_t chunkSize)
2471+
{
2472+
const SRCIHeader *srci = (const SRCIHeader *)chunkContents;
2473+
chunkContents += sizeof(SRCIHeader);
2474+
2475+
// redundant size? this should always be equal
2476+
RDCASSERTEQUAL(srci->size, chunkSize);
2477+
RDCASSERTEQUAL(srci->flags, 0);
2478+
2479+
rdcarray<ShaderSourceFile> &sourceFiles = m_DXILByteCode->Files;
2480+
2481+
if(!sourceFiles.empty())
2482+
{
2483+
// if we have source files, check that they're all at least empty
2484+
bool allEmpty = true;
2485+
for(const ShaderSourceFile &f : sourceFiles)
2486+
allEmpty &= f.contents.empty();
2487+
2488+
if(!allEmpty)
2489+
RDCERR("Some shader source files have contents being overridden by SRCI");
2490+
sourceFiles.clear();
2491+
}
2492+
2493+
for(uint32_t sec = 0; sec < srci->numSections; sec++)
2494+
{
2495+
const SRCISection *section = (const SRCISection *)chunkContents;
2496+
chunkContents += section->sectionSize;
2497+
RDCASSERTEQUAL(section->flags, 0);
2498+
2499+
const byte *sectionContents = (const byte *)(section + 1);
2500+
switch(section->type)
2501+
{
2502+
case SRCISectionType::FileContents:
2503+
{
2504+
const SRCIFileContentsSection *contents = (const SRCIFileContentsSection *)(section + 1);
2505+
2506+
// flags on flags on flags
2507+
RDCASSERTEQUAL(contents->flags, 0);
2508+
// not only would this be pointless if it contains the section size, but dxc seems to set it
2509+
// to 0 because of course it does.
2510+
RDCASSERTEQUAL(contents->sectionSize, 0);
2511+
2512+
if(!sourceFiles.empty() && sourceFiles.size() != contents->numFiles)
2513+
{
2514+
RDCERR(
2515+
"Unexpected number of source files in contents section %u when we have %zu already",
2516+
contents->numFiles, sourceFiles.size());
2517+
continue;
2518+
}
2519+
2520+
sourceFiles.resize(contents->numFiles);
2521+
2522+
bytebuf decompressedData;
2523+
const byte *contentsData = (const byte *)(contents + 1);
2524+
2525+
if(contents->zLibCompressed == 1)
2526+
{
2527+
decompressedData.resize(contents->uncompressedDataSize);
2528+
2529+
mz_stream stream = {};
2530+
2531+
stream.next_in = contentsData;
2532+
stream.avail_in = contents->dataSize;
2533+
stream.next_out = decompressedData.data();
2534+
stream.avail_out = contents->uncompressedDataSize;
2535+
2536+
int status = mz_inflateInit(&stream);
2537+
if(status != MZ_OK)
2538+
{
2539+
RDCERR("Couldn't initialise zlib decompressor");
2540+
continue;
2541+
}
2542+
2543+
status = mz_inflate(&stream, MZ_FINISH);
2544+
if(status != MZ_STREAM_END)
2545+
{
2546+
mz_inflateEnd(&stream);
2547+
RDCERR("zlib decompression failed");
2548+
continue;
2549+
}
2550+
RDCASSERTEQUAL((uint32_t)stream.total_out, contents->uncompressedDataSize);
2551+
decompressedData.resize(stream.total_out);
2552+
2553+
status = mz_inflateEnd(&stream);
2554+
if(status != MZ_OK)
2555+
{
2556+
RDCERR("Failed shutting down zlib decompressor");
2557+
continue;
2558+
}
2559+
2560+
contentsData = decompressedData.data();
2561+
}
2562+
else
2563+
{
2564+
RDCASSERTEQUAL(contents->zLibCompressed, 0);
2565+
}
2566+
2567+
for(uint32_t fileIdx = 0; fileIdx < contents->numFiles; fileIdx++)
2568+
{
2569+
const SRCIFileContentsEntry *contentsEntry = (const SRCIFileContentsEntry *)contentsData;
2570+
const char *fileContents = (const char *)(contentsEntry + 1);
2571+
2572+
// should be null terminated but don't take any chances because who knows if that will change
2573+
sourceFiles[fileIdx].contents.assign(fileContents, contentsEntry->fileSize);
2574+
2575+
RDCASSERTEQUAL(fileContents[contentsEntry->fileSize], 0);
2576+
2577+
// flags on flags on flags
2578+
RDCASSERTEQUAL(contentsEntry->flags, 0);
2579+
2580+
contentsData += contentsEntry->entrySize;
2581+
}
2582+
break;
2583+
}
2584+
case SRCISectionType::Filenames:
2585+
{
2586+
const SRCIFilenamesSection *names = (const SRCIFilenamesSection *)(section + 1);
2587+
2588+
// flags on flags on flags
2589+
RDCASSERTEQUAL(names->flags, 0);
2590+
2591+
RDCASSERTEQUAL(AlignUp4(names->dataSize + SRCIFilenamesSection::unpaddedSize()),
2592+
AlignUp4(section->sectionSize - sizeof(SRCISection)));
2593+
2594+
if(!sourceFiles.empty() && sourceFiles.size() != names->numFiles)
2595+
{
2596+
RDCERR(
2597+
"Unexpected number of source files in filenames section %u when we have %zu already",
2598+
names->numFiles, sourceFiles.size());
2599+
continue;
2600+
}
2601+
2602+
sourceFiles.resize(names->numFiles);
2603+
2604+
const byte *nameContents = (const byte *)names + SRCIFilenamesSection::unpaddedSize();
2605+
for(uint32_t fileIdx = 0; fileIdx < names->numFiles; fileIdx++)
2606+
{
2607+
const SRCIFilenameEntry *filenameEntry = (const SRCIFilenameEntry *)nameContents;
2608+
const char *filename = (const char *)(filenameEntry + 1);
2609+
2610+
sourceFiles[fileIdx].filename.assign(filename, filenameEntry->nameSize);
2611+
2612+
RDCASSERTEQUAL(filename[filenameEntry->nameSize], 0);
2613+
2614+
nameContents += filenameEntry->entrySize;
2615+
2616+
// I have no idea what you're expected to do with this filesize. I guess pre-allocate, but why?
2617+
RDCASSERT(sourceFiles[fileIdx].contents.empty() ||
2618+
sourceFiles[fileIdx].contents.size() == filenameEntry->fileSize,
2619+
sourceFiles[fileIdx].contents.count(), filenameEntry->fileSize);
2620+
}
2621+
2622+
break;
2623+
}
2624+
case SRCISectionType::Args:
2625+
{
2626+
const SRCIArgsSection *args = (const SRCIArgsSection *)sectionContents;
2627+
2628+
// flags on flags on flags
2629+
RDCASSERTEQUAL(args->flags, 0);
2630+
RDCASSERTEQUAL(AlignUp4(args->dataSize + sizeof(SRCIArgsSection)),
2631+
AlignUp4(section->sectionSize - sizeof(SRCISection)));
2632+
2633+
ShaderCompileFlags flags = m_DXILByteCode->GetShaderCompileFlags();
2634+
2635+
size_t cmdlineIdx = flags.flags.size();
2636+
for(size_t i = 0; i < flags.flags.size(); i++)
2637+
{
2638+
if(flags.flags[i].name == "preferSourceDebug")
2639+
continue;
2640+
2641+
if(flags.flags[i].name == "@cmdline")
2642+
{
2643+
cmdlineIdx = i;
2644+
2645+
// print an error if we're not just overriding default data, but continue to override
2646+
// assuming the SRCI is 'better' data
2647+
if(flags.flags[i].value != m_DXILByteCode->GetDefaultCommandLine())
2648+
{
2649+
RDCERR(
2650+
"Unexpected non-default command line in existing DXIL data, will be "
2651+
"overridden by SRCI information: %s",
2652+
flags.flags[i].value.c_str());
2653+
}
2654+
2655+
continue;
2656+
}
2657+
}
2658+
2659+
// if we didn't find a @cmdline (we expect to always do that, even with no original debug
2660+
// source info), add one here
2661+
if(cmdlineIdx == flags.flags.size())
2662+
flags.flags.push_back({"@cmdline", ""});
2663+
2664+
flags.flags[cmdlineIdx].value.clear();
2665+
2666+
// NULL-terminated pairs of strings
2667+
const char *strData = (const char *)(args + 1);
2668+
for(uint32_t arg = 0; arg < args->numArgs; arg++)
2669+
{
2670+
const char *name = strData;
2671+
strData += strlen(name) + 1;
2672+
const char *value = strData;
2673+
strData += strlen(value) + 1;
2674+
2675+
if(arg > 0)
2676+
flags.flags[cmdlineIdx].value += " ";
2677+
2678+
flags.flags[cmdlineIdx].value += "-";
2679+
flags.flags[cmdlineIdx].value += name;
2680+
if(value[0] != 0)
2681+
{
2682+
flags.flags[cmdlineIdx].value += "=";
2683+
flags.flags[cmdlineIdx].value += value;
2684+
}
2685+
}
2686+
2687+
m_DXILByteCode->SetShaderCompileFlags(flags);
2688+
2689+
break;
2690+
}
2691+
default: RDCERR("Unexpected SRCI section type %u", section->type); break;
2692+
}
2693+
}
2694+
}
2695+
23752696
struct DxcArg
23762697
{
23772698
uint32_t bit;

renderdoc/driver/shaders/dxbc/dxbc_container.h

+2
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ static const uint32_t FOURCC_PSV0 = MAKE_FOURCC('P', 'S', 'V', '0');
176176
static const uint32_t FOURCC_RTS0 = MAKE_FOURCC('R', 'T', 'S', '0');
177177
static const uint32_t FOURCC_RDAT = MAKE_FOURCC('R', 'D', 'A', 'T');
178178
static const uint32_t FOURCC_VERS = MAKE_FOURCC('V', 'E', 'R', 'S');
179+
static const uint32_t FOURCC_SRCI = MAKE_FOURCC('S', 'R', 'C', 'I');
179180

180181
struct RDEFHeader;
181182

@@ -275,6 +276,7 @@ class DXBCContainer
275276

276277
private:
277278
void TryFetchSeparateDebugInfo(bytebuf &byteCode, const rdcstr &debugInfoPath);
279+
void ProcessSourceInfo(const byte *chunkContents, uint32_t size);
278280

279281
bytebuf m_DebugShaderBlob;
280282
bytebuf m_ShaderBlob;

0 commit comments

Comments
 (0)