mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-05-06 01:50:38 +00:00
Store shader tool command-line from compile flags
* For DXBC files, we decode the D3DCOMPILE flags back into fxc parameters, and vice-versa. * For SPIR-V, glslang will embed command line parameters as OpModuleProcessed strings or opcodes. * This is stored as a "@cmdline" shader compile flag.
This commit is contained in:
@@ -1648,12 +1648,72 @@ void DXBCFile::GuessResources()
|
||||
}
|
||||
}
|
||||
|
||||
struct FxcArg
|
||||
{
|
||||
uint32_t bit;
|
||||
const char *arg;
|
||||
} fxc_flags[] = {
|
||||
{D3DCOMPILE_DEBUG, " /Zi "},
|
||||
{D3DCOMPILE_SKIP_VALIDATION, " /Vd "},
|
||||
{D3DCOMPILE_SKIP_OPTIMIZATION, " /Od "},
|
||||
{D3DCOMPILE_PACK_MATRIX_ROW_MAJOR, " /Zpr "},
|
||||
{D3DCOMPILE_PACK_MATRIX_COLUMN_MAJOR, " /Zpc "},
|
||||
{D3DCOMPILE_PARTIAL_PRECISION, " /Gpp "},
|
||||
//{D3DCOMPILE_FORCE_VS_SOFTWARE_NO_OPT, " /XX "},
|
||||
//{D3DCOMPILE_FORCE_PS_SOFTWARE_NO_OPT, " /XX "},
|
||||
{D3DCOMPILE_NO_PRESHADER, " /Op "},
|
||||
{D3DCOMPILE_AVOID_FLOW_CONTROL, " /Gfa "},
|
||||
{D3DCOMPILE_PREFER_FLOW_CONTROL, " /Gfp "},
|
||||
{D3DCOMPILE_ENABLE_STRICTNESS, " /Ges "},
|
||||
{D3DCOMPILE_ENABLE_BACKWARDS_COMPATIBILITY, " /Gec "},
|
||||
{D3DCOMPILE_IEEE_STRICTNESS, " /Gis "},
|
||||
{D3DCOMPILE_WARNINGS_ARE_ERRORS, " /WX "},
|
||||
{D3DCOMPILE_RESOURCES_MAY_ALIAS, " /res_may_alias "},
|
||||
{D3DCOMPILE_ENABLE_UNBOUNDED_DESCRIPTOR_TABLES, " /enable_unbounded_descriptor_tables "},
|
||||
{D3DCOMPILE_ALL_RESOURCES_BOUND, " /all_resources_bound "},
|
||||
{D3DCOMPILE_DEBUG_NAME_FOR_SOURCE, " /Zss "},
|
||||
{D3DCOMPILE_DEBUG_NAME_FOR_BINARY, " /Zsb "},
|
||||
};
|
||||
|
||||
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());
|
||||
for(const ShaderCompileFlag flag : compileFlags.flags)
|
||||
{
|
||||
if(flag.name == "@cmdline")
|
||||
{
|
||||
std::string cmdline = flag.value;
|
||||
|
||||
// ensure cmdline is surrounded by spaces and all whitespace is spaces. This means we can
|
||||
// search for our flags surrounded by space and ensure we get exact matches.
|
||||
for(char &c : cmdline)
|
||||
if(isspace(c))
|
||||
c = ' ';
|
||||
|
||||
cmdline = " " + cmdline + " ";
|
||||
|
||||
for(const FxcArg &arg : fxc_flags)
|
||||
{
|
||||
if(strstr(cmdline.c_str(), arg.arg))
|
||||
ret |= arg.bit;
|
||||
}
|
||||
|
||||
// check optimisation special case
|
||||
if(strstr(cmdline.c_str(), " /O0 "))
|
||||
ret |= D3DCOMPILE_OPTIMIZATION_LEVEL0;
|
||||
else if(strstr(cmdline.c_str(), " /O1 "))
|
||||
ret |= D3DCOMPILE_OPTIMIZATION_LEVEL1;
|
||||
else if(strstr(cmdline.c_str(), " /O2 "))
|
||||
ret |= D3DCOMPILE_OPTIMIZATION_LEVEL2;
|
||||
else if(strstr(cmdline.c_str(), " /O3 "))
|
||||
ret |= D3DCOMPILE_OPTIMIZATION_LEVEL3;
|
||||
|
||||
// ignore any other flags we might not understand
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -1662,7 +1722,32 @@ ShaderCompileFlags EncodeFlags(const uint32_t flags)
|
||||
{
|
||||
ShaderCompileFlags ret;
|
||||
|
||||
ret.flags = {{"compileFlags", StringFormat::Fmt("%u", flags)}};
|
||||
std::string cmdline;
|
||||
|
||||
for(const FxcArg &arg : fxc_flags)
|
||||
{
|
||||
if(flags & arg.bit)
|
||||
cmdline += arg.arg;
|
||||
}
|
||||
|
||||
// optimization flags are a special case.
|
||||
//
|
||||
// D3DCOMPILE_OPTIMIZATION_LEVEL0 = (1 << 14)
|
||||
// D3DCOMPILE_OPTIMIZATION_LEVEL1 = 0
|
||||
// D3DCOMPILE_OPTIMIZATION_LEVEL2 = ((1 << 14) | (1 << 15))
|
||||
// D3DCOMPILE_OPTIMIZATION_LEVEL3 = (1 << 15)
|
||||
|
||||
uint32_t opt = (flags & D3DCOMPILE_OPTIMIZATION_LEVEL2);
|
||||
if(opt == D3DCOMPILE_OPTIMIZATION_LEVEL0)
|
||||
cmdline += " /O0";
|
||||
else if(opt == D3DCOMPILE_OPTIMIZATION_LEVEL1)
|
||||
cmdline += " /O1";
|
||||
else if(opt == D3DCOMPILE_OPTIMIZATION_LEVEL2)
|
||||
cmdline += " /O2";
|
||||
else if(opt == D3DCOMPILE_OPTIMIZATION_LEVEL3)
|
||||
cmdline += " /O3";
|
||||
|
||||
ret.flags = {{"@cmdline", cmdline}};
|
||||
|
||||
// If D3DCOMPILE_SKIP_OPTIMIZATION is set, then prefer source-level debugging as it should be
|
||||
// accurate enough to work with.
|
||||
@@ -1677,4 +1762,101 @@ ShaderCompileFlags EncodeFlags(const DXBCDebugChunk *dbg)
|
||||
return EncodeFlags(dbg ? dbg->GetShaderCompileFlags() : 0);
|
||||
}
|
||||
|
||||
}; // namespace DXBC
|
||||
}; // namespace DXBC
|
||||
|
||||
#if ENABLED(ENABLE_UNIT_TESTS)
|
||||
|
||||
#include "3rdparty/catch/catch.hpp"
|
||||
|
||||
TEST_CASE("Check DXBC flags are non-overlapping", "[dxbc]")
|
||||
{
|
||||
for(const DXBC::FxcArg &a : DXBC::fxc_flags)
|
||||
{
|
||||
for(const DXBC::FxcArg &b : DXBC::fxc_flags)
|
||||
{
|
||||
if(a.arg == b.arg)
|
||||
continue;
|
||||
|
||||
// no argument should be a subset of another argument
|
||||
std::string arga = trim(a.arg);
|
||||
std::string argb = trim(b.arg);
|
||||
INFO("a: '" << arga << "' b: '" << argb << "'");
|
||||
CHECK(strstr(arga.c_str(), argb.c_str()) == NULL);
|
||||
CHECK(strstr(argb.c_str(), arga.c_str()) == NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Check DXBC flag encoding/decoding", "[dxbc]")
|
||||
{
|
||||
SECTION("encode/decode identity")
|
||||
{
|
||||
uint32_t flags = D3DCOMPILE_PARTIAL_PRECISION | D3DCOMPILE_SKIP_OPTIMIZATION |
|
||||
D3DCOMPILE_ALL_RESOURCES_BOUND | D3DCOMPILE_OPTIMIZATION_LEVEL2;
|
||||
uint32_t flags2 = DXBC::DecodeFlags(DXBC::EncodeFlags(flags));
|
||||
|
||||
CHECK(flags == flags2);
|
||||
|
||||
flags = 0;
|
||||
flags2 = DXBC::DecodeFlags(DXBC::EncodeFlags(flags));
|
||||
|
||||
CHECK(flags == flags2);
|
||||
|
||||
flags = D3DCOMPILE_OPTIMIZATION_LEVEL3 | D3DCOMPILE_DEBUG;
|
||||
flags2 = DXBC::DecodeFlags(DXBC::EncodeFlags(flags));
|
||||
|
||||
CHECK(flags == flags2);
|
||||
};
|
||||
|
||||
SECTION("encode/decode discards unrecognised parameters")
|
||||
{
|
||||
uint32_t flags = D3DCOMPILE_PARTIAL_PRECISION | (1 << 30);
|
||||
uint32_t flags2 = DXBC::DecodeFlags(DXBC::EncodeFlags(flags));
|
||||
|
||||
CHECK(flags2 == D3DCOMPILE_PARTIAL_PRECISION);
|
||||
|
||||
ShaderCompileFlags compileflags;
|
||||
|
||||
compileflags.flags = {
|
||||
{"@cmdline", "/Zi /Z8 /JJ /WX /K other words embed/Odparam /DFoo=\"bar\""}};
|
||||
|
||||
flags2 = DXBC::DecodeFlags(compileflags);
|
||||
|
||||
CHECK(flags2 == (D3DCOMPILE_DEBUG | D3DCOMPILE_WARNINGS_ARE_ERRORS));
|
||||
|
||||
flags = ~0U;
|
||||
flags2 = DXBC::DecodeFlags(DXBC::EncodeFlags(flags));
|
||||
|
||||
uint32_t allflags = 0;
|
||||
for(const DXBC::FxcArg &a : DXBC::fxc_flags)
|
||||
allflags |= a.bit;
|
||||
|
||||
allflags |= D3DCOMPILE_OPTIMIZATION_LEVEL2;
|
||||
|
||||
CHECK(flags2 == allflags);
|
||||
};
|
||||
|
||||
SECTION("optimisation flags are properly decoded and encoded")
|
||||
{
|
||||
uint32_t flags = D3DCOMPILE_DEBUG | D3DCOMPILE_OPTIMIZATION_LEVEL0;
|
||||
uint32_t flags2 = DXBC::DecodeFlags(DXBC::EncodeFlags(flags));
|
||||
|
||||
CHECK(flags == flags2);
|
||||
|
||||
flags = D3DCOMPILE_DEBUG | D3DCOMPILE_OPTIMIZATION_LEVEL1;
|
||||
flags2 = DXBC::DecodeFlags(DXBC::EncodeFlags(flags));
|
||||
|
||||
CHECK(flags == flags2);
|
||||
|
||||
flags = D3DCOMPILE_DEBUG | D3DCOMPILE_OPTIMIZATION_LEVEL2;
|
||||
flags2 = DXBC::DecodeFlags(DXBC::EncodeFlags(flags));
|
||||
|
||||
CHECK(flags == flags2);
|
||||
|
||||
flags = D3DCOMPILE_DEBUG | D3DCOMPILE_OPTIMIZATION_LEVEL3;
|
||||
flags2 = DXBC::DecodeFlags(DXBC::EncodeFlags(flags));
|
||||
|
||||
CHECK(flags == flags2);
|
||||
};
|
||||
}
|
||||
#endif
|
||||
@@ -113,6 +113,7 @@ struct SPVModule
|
||||
spv::SourceLanguage sourceLang;
|
||||
uint32_t sourceVer;
|
||||
|
||||
std::string cmdline;
|
||||
vector<std::pair<string, string>> sourceFiles;
|
||||
|
||||
vector<string> extensions;
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "common/common.h"
|
||||
#include "maths/formatpacking.h"
|
||||
#include "serialise/serialiser.h"
|
||||
#include "strings/string_utils.h"
|
||||
#include "spirv_common.h"
|
||||
|
||||
using std::pair;
|
||||
@@ -3981,6 +3982,9 @@ void SPVModule::MakeReflection(ShaderStage stage, const string &entryPoint,
|
||||
}
|
||||
}
|
||||
|
||||
if(!cmdline.empty())
|
||||
reflection.debugInfo.compileFlags.flags = {{"@cmdline", cmdline}};
|
||||
|
||||
// TODO need to fetch these
|
||||
reflection.dispatchThreadsDimension[0] = 0;
|
||||
reflection.dispatchThreadsDimension[1] = 0;
|
||||
@@ -4649,6 +4653,15 @@ void ParseSPIRV(uint32_t *spirv, size_t spirvLength, SPVModule &module)
|
||||
|
||||
module.generator = spirv[2];
|
||||
|
||||
bool isglslang = false;
|
||||
|
||||
{
|
||||
uint32_t toolid = (module.generator & 0xffff0000) >> 16;
|
||||
|
||||
if(toolid == 8)
|
||||
isglslang = true;
|
||||
}
|
||||
|
||||
uint32_t idbound = spirv[3];
|
||||
module.ids.resize(idbound);
|
||||
|
||||
@@ -4686,7 +4699,50 @@ void ParseSPIRV(uint32_t *spirv, size_t spirvLength, SPVModule &module)
|
||||
RDCASSERT(filenameInst);
|
||||
|
||||
sourceFile.first = filenameInst->str;
|
||||
sourceFile.second = (const char *)&spirv[it + 4];
|
||||
|
||||
std::string source = (const char *)&spirv[it + 4];
|
||||
|
||||
// glslang outputs command-line arguments as OpModuleProcessed - before SPIR-V 1.1 where
|
||||
// it was an actual op, it was output as comments in the source.
|
||||
if(isglslang)
|
||||
{
|
||||
const char compileFlagPrefix[] = "// OpModuleProcessed ";
|
||||
const char endMarker[] = "#line 1\n";
|
||||
if(source.find(compileFlagPrefix) == 0)
|
||||
{
|
||||
// process compile flags
|
||||
size_t nextLine = source.find('\n');
|
||||
while(nextLine != std::string::npos)
|
||||
{
|
||||
bool finished = false;
|
||||
if(source.find(compileFlagPrefix) == 0)
|
||||
{
|
||||
size_t offs = sizeof(compileFlagPrefix) - 1;
|
||||
module.cmdline += " --" + source.substr(offs, nextLine - offs);
|
||||
}
|
||||
else if(source.find(endMarker) == 0)
|
||||
{
|
||||
finished = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
RDCERR("Unexpected preamble line with OpModuleProcessed: %s",
|
||||
std::string(source.begin(), source.begin() + nextLine));
|
||||
break;
|
||||
}
|
||||
|
||||
// erase this line
|
||||
source.erase(0, nextLine + 1);
|
||||
|
||||
nextLine = source.find('\n');
|
||||
|
||||
if(finished)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sourceFile.second = source;
|
||||
|
||||
module.sourceFiles.push_back(sourceFile);
|
||||
}
|
||||
@@ -4717,6 +4773,16 @@ void ParseSPIRV(uint32_t *spirv, size_t spirvLength, SPVModule &module)
|
||||
module.sourceexts.push_back(&op);
|
||||
break;
|
||||
}
|
||||
case spv::OpModuleProcessed:
|
||||
{
|
||||
// glslang outputs command-line arguments as OpModuleProcessed
|
||||
if(isglslang)
|
||||
{
|
||||
module.cmdline += " --";
|
||||
module.cmdline += (const char *)&spirv[it + 1];
|
||||
}
|
||||
break;
|
||||
}
|
||||
case spv::OpExtension:
|
||||
{
|
||||
string ext = (const char *)&spirv[it + 1];
|
||||
|
||||
Reference in New Issue
Block a user