mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-10-31 19:16:17 +00:00
Add tests for separate debug info on D3D + Vulkan, and embedding
This commit is contained in:
32 files changed
+696
-113
No files matched your search
@@ -166,6 +166,7 @@ set(VULKAN_SRC
|
||||
vk/vk_simple_triangle.cpp
|
||||
vk/vk_spec_constants.cpp
|
||||
vk/vk_spirv_13_shaders.cpp
|
||||
vk/vk_stripped_shaders.cpp
|
||||
vk/vk_subgroup_zoo.cpp
|
||||
vk/vk_structured_buffer_nested.cpp
|
||||
vk/vk_sync2.cpp
|
||||
|
||||
@@ -272,8 +272,9 @@ void main(uint3 threadID : SV_DispatchThreadID)
|
||||
|
||||
ID3DBlobPtr vsblob = Compile(D3DDefaultVertex, "main", "vs_4_0");
|
||||
// can't skip optimising and still have the extensions work, sadly
|
||||
ID3DBlobPtr psblob = Compile(ags_header + BaryCentricPixel, "main", "ps_5_0", false);
|
||||
ID3DBlobPtr csblob = Compile(ags_header + MaxCompute, "main", "cs_5_0", false);
|
||||
ID3DBlobPtr psblob =
|
||||
Compile(ags_header + BaryCentricPixel, "main", "ps_5_0", CompileOptionFlags::None);
|
||||
ID3DBlobPtr csblob = Compile(ags_header + MaxCompute, "main", "cs_5_0", CompileOptionFlags::None);
|
||||
|
||||
CreateDefaultInputLayout(vsblob);
|
||||
|
||||
|
||||
@@ -163,7 +163,7 @@ void main(uint3 gid : SV_GroupThreadID)
|
||||
ID3D11UnorderedAccessViewPtr inUAV = MakeUAV(inBuf);
|
||||
ID3D11UnorderedAccessViewPtr outUAV = MakeUAV(outBuf);
|
||||
|
||||
ID3D11ComputeShaderPtr shad = CreateCS(Compile(comp, "main", "cs_5_0", true));
|
||||
ID3D11ComputeShaderPtr shad = CreateCS(Compile(comp, "main", "cs_5_0"));
|
||||
|
||||
int cbufferdata[4];
|
||||
memset(cbufferdata, 0, sizeof(cbufferdata));
|
||||
|
||||
@@ -1072,8 +1072,9 @@ float4 main(v2f IN, uint samp : SV_SampleIndex) : SV_Target0
|
||||
vsblob->GetBufferSize(), &layout));
|
||||
|
||||
ID3D11VertexShaderPtr vs = CreateVS(vsblob);
|
||||
ID3D11PixelShaderPtr ps = CreatePS(Compile(common + pixel, "main", "ps_5_0", true));
|
||||
ID3D11PixelShaderPtr psopt = CreatePS(Compile(common + pixel, "main", "ps_5_0", false));
|
||||
ID3D11PixelShaderPtr ps = CreatePS(Compile(common + pixel, "main", "ps_5_0"));
|
||||
ID3D11PixelShaderPtr psopt =
|
||||
CreatePS(Compile(common + pixel, "main", "ps_5_0", CompileOptionFlags::None));
|
||||
ID3D11PixelShaderPtr flowps = CreatePS(Compile(common + flowPixel, "main", "ps_5_0"));
|
||||
|
||||
static const uint32_t texDim = AlignUp(numTests, 64U) * 4;
|
||||
|
||||
@@ -35,23 +35,45 @@ RD_TEST(D3D11_Stripped_Shaders, D3D11GraphicsTest)
|
||||
if(!Init())
|
||||
return 3;
|
||||
|
||||
ID3DBlobPtr vsblob = Compile(D3DDefaultVertex, "main", "vs_5_0");
|
||||
ID3DBlobPtr psblob = Compile(D3DDefaultPixel, "main", "ps_5_0");
|
||||
MakeDir((GetCWD() + "/tmp/D3D11_Stripped_Shaders").c_str());
|
||||
|
||||
WriteBlob(GetCWD() + "tmp/D3D11_Stripped_Shaders/shader_debug.vs", vsblob, false);
|
||||
WriteBlob(GetCWD() + "tmp/D3D11_Stripped_Shaders/shader_debug.ps", psblob, true);
|
||||
// several variables:
|
||||
//
|
||||
// legacy whole unstripped blob, or dxc-style just pdb
|
||||
// (for dxc-style) manual named or auto-named
|
||||
// (for legacy-style) lz4 compressed or not
|
||||
// (for legacy-style) PRIV pointed or SetPrivateData
|
||||
// absolute or relative path
|
||||
//
|
||||
// we don't test all possible combinations but check at least one of each
|
||||
|
||||
Strip(vsblob);
|
||||
Strip(psblob);
|
||||
|
||||
SetBlobPath(GetCWD() + "tmp/D3D11_Stripped_Shaders/shader_debug.vs", vsblob);
|
||||
// PRIV will be set with absolute path
|
||||
ID3DBlobPtr vsblob =
|
||||
Compile(D3DDefaultVertex + "\n// shader source // vertex", "main", "vs_5_0",
|
||||
CompileOptionFlags::SeparateDebugLegacy | CompileOptionFlags::SeparateDebugSetPRIV);
|
||||
// we'll set relative SetPrivateData path below
|
||||
ID3DBlobPtr psblob =
|
||||
Compile(D3DDefaultPixel + "\n// shader source // pixel", "main", "ps_5_0",
|
||||
CompileOptionFlags::SeparateDebugLegacy | CompileOptionFlags::ManualDebugName |
|
||||
CompileOptionFlags::LZ4Compress);
|
||||
|
||||
CreateDefaultInputLayout(vsblob);
|
||||
|
||||
ID3D11VertexShaderPtr vs = CreateVS(vsblob);
|
||||
ID3D11PixelShaderPtr ps = CreatePS(psblob);
|
||||
|
||||
SetBlobPath("lz4#tmp/D3D11_Stripped_Shaders/shader_debug.ps", ps);
|
||||
SetBlobPath("lz4#shader_ps.dbg", ps);
|
||||
|
||||
// manual name will be set absolute in a manual ILDN
|
||||
ID3DBlobPtr vsblob2 =
|
||||
Compile(D3DDefaultVertex + "\n// shader source 2 // vertex", "main", "vs_5_0",
|
||||
CompileOptionFlags::SeparateDebug | CompileOptionFlags::ManualDebugName);
|
||||
// the fxc-generated ILDN will be relative
|
||||
ID3DBlobPtr psblob2 = Compile(D3DDefaultPixel + "\n// shader source 2 // pixel", "main",
|
||||
"ps_5_0", CompileOptionFlags::SeparateDebug);
|
||||
|
||||
ID3D11VertexShaderPtr vs2 = CreateVS(vsblob2);
|
||||
ID3D11PixelShaderPtr ps2 = CreatePS(psblob2);
|
||||
|
||||
ID3D11BufferPtr vb = MakeBuffer().Vertex().Data(DefaultTri);
|
||||
|
||||
@@ -70,6 +92,17 @@ RD_TEST(D3D11_Stripped_Shaders, D3D11GraphicsTest)
|
||||
|
||||
ctx->OMSetRenderTargets(1, &bbRTV.GetInterfacePtr(), NULL);
|
||||
|
||||
setMarker("test: shader source");
|
||||
setMarker("vertex auto absolute");
|
||||
setMarker("pixel manual relative");
|
||||
ctx->Draw(3, 0);
|
||||
|
||||
ctx->VSSetShader(vs2, NULL, 0);
|
||||
ctx->PSSetShader(ps2, NULL, 0);
|
||||
|
||||
setMarker("test: shader source 2");
|
||||
setMarker("vertex manual absolute");
|
||||
setMarker("pixel auto relative");
|
||||
ctx->Draw(3, 0);
|
||||
|
||||
Present();
|
||||
|
||||
@@ -51,6 +51,7 @@ struct Capabilities
|
||||
pD3DCompile dyn_D3DCompile = NULL;
|
||||
pD3DStripShader dyn_D3DStripShader = NULL;
|
||||
pD3DSetBlobPart dyn_D3DSetBlobPart = NULL;
|
||||
pD3DGetBlobPart dyn_D3DGetBlobPart = NULL;
|
||||
|
||||
PFN_D3D11_CREATE_DEVICE dyn_D3D11CreateDevice = NULL;
|
||||
PFN_D3D11_CREATE_DEVICE_AND_SWAP_CHAIN dyn_D3D11CreateDeviceAndSwapChain = NULL;
|
||||
@@ -94,6 +95,7 @@ void D3D11GraphicsTest::Prepare(int argc, char **argv)
|
||||
dyn_D3DCompile = (pD3DCompile)GetProcAddress(d3dcompiler, "D3DCompile");
|
||||
dyn_D3DStripShader = (pD3DStripShader)GetProcAddress(d3dcompiler, "D3DStripShader");
|
||||
dyn_D3DSetBlobPart = (pD3DSetBlobPart)GetProcAddress(d3dcompiler, "D3DSetBlobPart");
|
||||
dyn_D3DGetBlobPart = (pD3DGetBlobPart)GetProcAddress(d3dcompiler, "D3DGetBlobPart");
|
||||
}
|
||||
|
||||
HRESULT hr = S_OK;
|
||||
@@ -136,7 +138,7 @@ void D3D11GraphicsTest::Prepare(int argc, char **argv)
|
||||
else if(!factory)
|
||||
Avail = "Couldn't create DXGI factory";
|
||||
else if(!dyn_D3D11CreateDevice || !dyn_D3D11CreateDeviceAndSwapChain || !dyn_D3DCompile ||
|
||||
!dyn_D3DStripShader || !dyn_D3DSetBlobPart)
|
||||
!dyn_D3DStripShader || !dyn_D3DSetBlobPart || !dyn_D3DGetBlobPart)
|
||||
Avail = "Missing required entry point";
|
||||
}
|
||||
|
||||
@@ -643,13 +645,24 @@ void D3D11GraphicsTest::SetStencilRef(UINT ref)
|
||||
}
|
||||
|
||||
ID3DBlobPtr D3D11GraphicsTest::Compile(std::string src, std::string entry, std::string profile,
|
||||
bool skipoptimise)
|
||||
uint32_t compileOptions)
|
||||
{
|
||||
const bool skipoptimise = ((compileOptions & CompileOptionFlags::SkipOptimise) != 0);
|
||||
const bool separateDebugLegacy = ((compileOptions & CompileOptionFlags::SeparateDebugLegacy) != 0);
|
||||
const bool separateDebugSetPRIV =
|
||||
((compileOptions & CompileOptionFlags::SeparateDebugSetPRIV) != 0);
|
||||
const bool separateDebug = ((compileOptions & CompileOptionFlags::SeparateDebug) != 0);
|
||||
const bool manualDebugName = ((compileOptions & CompileOptionFlags::ManualDebugName) != 0);
|
||||
const bool lz4Compress = ((compileOptions & CompileOptionFlags::LZ4Compress) != 0);
|
||||
|
||||
ID3DBlobPtr blob = NULL;
|
||||
ID3DBlobPtr error = NULL;
|
||||
|
||||
UINT flags = D3DCOMPILE_WARNINGS_ARE_ERRORS | D3DCOMPILE_DEBUG;
|
||||
|
||||
if((separateDebugLegacy || separateDebug) && !manualDebugName)
|
||||
flags |= D3DCOMPILE_DEBUG_NAME_FOR_SOURCE;
|
||||
|
||||
if(skipoptimise)
|
||||
flags |= D3DCOMPILE_SKIP_OPTIMIZATION | D3DCOMPILE_OPTIMIZATION_LEVEL0;
|
||||
else
|
||||
@@ -668,6 +681,76 @@ ID3DBlobPtr D3D11GraphicsTest::Compile(std::string src, std::string entry, std::
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(separateDebugLegacy || separateDebug)
|
||||
{
|
||||
std::string debugName;
|
||||
if(manualDebugName)
|
||||
{
|
||||
debugName = "shader_" + profile.substr(0, 2) + ".dbg";
|
||||
}
|
||||
else
|
||||
{
|
||||
ID3DBlobPtr nameBlob;
|
||||
LPVOID bufferPointer = blob->GetBufferPointer();
|
||||
hr = dyn_D3DGetBlobPart(bufferPointer, blob->GetBufferSize(), D3D_BLOB_DEBUG_NAME, 0,
|
||||
&nameBlob);
|
||||
|
||||
if(SUCCEEDED(hr))
|
||||
{
|
||||
const uint16_t *flags_len = (const uint16_t *)nameBlob->GetBufferPointer();
|
||||
|
||||
const char *name_str = (const char *)(flags_len + 2);
|
||||
|
||||
debugName.assign(name_str, flags_len[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
TEST_ERROR("Couldn't get automatic name");
|
||||
debugName = "shader_" + profile.substr(0, 2) + ".dbg";
|
||||
}
|
||||
}
|
||||
|
||||
std::string path = tmpPath + "/" + debugName;
|
||||
|
||||
if(separateDebug)
|
||||
{
|
||||
ID3DBlobPtr debugBlob;
|
||||
dyn_D3DGetBlobPart(blob->GetBufferPointer(), blob->GetBufferSize(), D3D_BLOB_PDB, 0,
|
||||
&debugBlob);
|
||||
|
||||
WriteBlob(path, debugBlob, lz4Compress);
|
||||
|
||||
if(manualDebugName)
|
||||
{
|
||||
std::string nameBlob = path;
|
||||
// pad the blob to multiple of 4
|
||||
nameBlob.resize((path.size() + 0x3) & ~0x3);
|
||||
|
||||
nameBlob.insert(0, 4, '\0');
|
||||
nameBlob[2] += path.size() & 0xff;
|
||||
|
||||
if(path.size() > 255)
|
||||
TEST_ERROR("tmp path is too long!");
|
||||
|
||||
ID3DBlobPtr newBlob;
|
||||
|
||||
dyn_D3DSetBlobPart(blob->GetBufferPointer(), blob->GetBufferSize(), D3D_BLOB_DEBUG_NAME, 0,
|
||||
nameBlob.c_str(), nameBlob.size(), &newBlob);
|
||||
|
||||
blob = newBlob;
|
||||
}
|
||||
}
|
||||
else if(separateDebugLegacy)
|
||||
{
|
||||
WriteBlob(path, blob, lz4Compress);
|
||||
|
||||
if(separateDebugSetPRIV)
|
||||
SetBlobPath(path, blob);
|
||||
}
|
||||
|
||||
Strip(blob);
|
||||
}
|
||||
|
||||
return blob;
|
||||
}
|
||||
|
||||
|
||||
@@ -29,16 +29,6 @@
|
||||
#include "dx/official/dxgi.h"
|
||||
#include "d3d11_helpers.h"
|
||||
|
||||
#include "dx/official/d3dcompiler.h"
|
||||
|
||||
typedef HRESULT(WINAPI *pD3DStripShader)(_In_reads_bytes_(BytecodeLength) LPCVOID pShaderBytecode,
|
||||
_In_ SIZE_T BytecodeLength, _In_ UINT uStripFlags,
|
||||
_Out_ ID3DBlob **ppStrippedBlob);
|
||||
typedef HRESULT(WINAPI *pD3DSetBlobPart)(_In_reads_bytes_(SrcDataSize) LPCVOID pSrcData,
|
||||
_In_ SIZE_T SrcDataSize, _In_ D3D_BLOB_PART Part,
|
||||
_In_ UINT Flags, _In_reads_bytes_(PartSize) LPCVOID pPart,
|
||||
_In_ SIZE_T PartSize, _Out_ ID3DBlob **ppNewShader);
|
||||
|
||||
struct Win32Window;
|
||||
|
||||
struct D3D11GraphicsTest : public GraphicsTest
|
||||
@@ -74,8 +64,25 @@ struct D3D11GraphicsTest : public GraphicsTest
|
||||
BufUAVType = 0xf00,
|
||||
};
|
||||
|
||||
enum CompileOptionFlags
|
||||
{
|
||||
None = 0,
|
||||
SkipOptimise = 1 << 0,
|
||||
// the legacy path, where we write the unstripped blob somewhere and set a path either via PRIV
|
||||
// name or SetPrivateData
|
||||
SeparateDebugLegacy = 1 << 1,
|
||||
SeparateDebugSetPRIV = 1 << 2,
|
||||
// the 'new' path which is still manual but matches dxc's behaviour, where we pull out the PDB and set an ILDN name
|
||||
SeparateDebug = 1 << 3,
|
||||
// don't use the auto-generated name for a debug file, name it custom shader_Xs.dbg (so only one
|
||||
// per stage per demo allowed)
|
||||
ManualDebugName = 1 << 4,
|
||||
// only possible for SeparateDebugLegacy and ManualDebugName, lz4 compress the resulting blob
|
||||
LZ4Compress = 1 << 5,
|
||||
};
|
||||
|
||||
ID3DBlobPtr Compile(std::string src, std::string entry, std::string profile,
|
||||
bool skipoptimise = true);
|
||||
uint32_t compileOptions = CompileOptionFlags::SkipOptimise);
|
||||
void Strip(ID3DBlobPtr &ptr);
|
||||
void WriteBlob(std::string name, ID3DBlobPtr blob, bool compress);
|
||||
|
||||
|
||||
@@ -198,8 +198,8 @@ void main(uint3 inGTid : SV_GroupThreadID)
|
||||
compsizes[i].x, compsizes[i].y, compsizes[i].z);
|
||||
comppipe_name[i] = fmt::format("{}x{}x{}", compsizes[i].x, compsizes[i].y, compsizes[i].z);
|
||||
|
||||
testShaders[i] = CreateCS(Compile(defines + sizedefine + testShader, "main", "cs_5_0", true));
|
||||
perfShaders[i] = CreateCS(Compile(defines + sizedefine + perfShader, "main", "cs_5_0", true));
|
||||
testShaders[i] = CreateCS(Compile(defines + sizedefine + testShader, "main", "cs_5_0"));
|
||||
perfShaders[i] = CreateCS(Compile(defines + sizedefine + perfShader, "main", "cs_5_0"));
|
||||
}
|
||||
|
||||
while(Running())
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
/******************************************************************************
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2023-2026 Baldur Karlsson
|
||||
*
|
||||
* 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 "d3d12_test.h"
|
||||
|
||||
RD_TEST(D3D12_Stripped_Shaders, D3D12GraphicsTest)
|
||||
{
|
||||
static constexpr const char *Description =
|
||||
"Tests shaders with their debug/reflection info stripped out and stored in separate blobs.";
|
||||
|
||||
void Prepare(int argc, char **argv)
|
||||
{
|
||||
D3D12GraphicsTest::Prepare(argc, argv);
|
||||
|
||||
if(!Avail.empty())
|
||||
return;
|
||||
|
||||
if(!m_DXILSupport)
|
||||
Avail = "DXIL can't be compiled";
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
// initialise, create window, create device, etc
|
||||
if(!Init())
|
||||
return 3;
|
||||
|
||||
MakeDir((GetCWD() + "/tmp/D3D12_Stripped_Shaders").c_str());
|
||||
|
||||
// only a couple of variables:
|
||||
//
|
||||
// auto-named or manual named
|
||||
// absolute or relative path
|
||||
|
||||
// manual debug name will be absolute
|
||||
ID3DBlobPtr vsblob =
|
||||
Compile(D3DDefaultVertex + "\n// shader source // vertex", "main", "vs_6_0",
|
||||
CompileOptionFlags::SeparateDebug | CompileOptionFlags::ManualDebugName);
|
||||
// auto name will be relative
|
||||
ID3DBlobPtr psblob = Compile(D3DDefaultPixel + "\n// shader source // pixel", "main", "ps_6_0",
|
||||
CompileOptionFlags::SeparateDebug);
|
||||
|
||||
ID3D12PipelineStatePtr pso = MakePSO().RootSig(DefaultTriSig).InputLayout().VS(vsblob).PS(psblob);
|
||||
|
||||
while(Running())
|
||||
{
|
||||
ID3D12GraphicsCommandListPtr cmd = GetCommandBuffer();
|
||||
|
||||
Reset(cmd);
|
||||
|
||||
ID3D12ResourcePtr bb = StartUsingBackbuffer(cmd, D3D12_RESOURCE_STATE_RENDER_TARGET);
|
||||
|
||||
ClearRenderTargetView(cmd, BBRTV, {0.2f, 0.2f, 0.2f, 1.0f});
|
||||
|
||||
cmd->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
|
||||
|
||||
IASetVertexBuffer(cmd, DefaultTriVB, sizeof(DefaultA2V), 0);
|
||||
cmd->SetPipelineState(pso);
|
||||
cmd->SetGraphicsRootSignature(DefaultTriSig);
|
||||
|
||||
SetMainWindowViewScissor(cmd);
|
||||
|
||||
OMSetRenderTargets(cmd, {BBRTV}, {});
|
||||
|
||||
setMarker(cmd, "test: shader source");
|
||||
setMarker(cmd, "vertex manual absolute");
|
||||
setMarker(cmd, "pixel auto relative");
|
||||
cmd->DrawInstanced(3, 1, 0, 0);
|
||||
|
||||
FinishUsingBackbuffer(cmd, D3D12_RESOURCE_STATE_RENDER_TARGET);
|
||||
|
||||
cmd->Close();
|
||||
|
||||
SubmitAndPresent({cmd});
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
REGISTER_TEST();
|
||||
@@ -52,6 +52,7 @@ bool d3d12on7 = false;
|
||||
pD3DCompile dyn_D3DCompile = NULL;
|
||||
pD3DStripShader dyn_D3DStripShader = NULL;
|
||||
pD3DSetBlobPart dyn_D3DSetBlobPart = NULL;
|
||||
pD3DGetBlobPart dyn_D3DGetBlobPart = NULL;
|
||||
pD3DCreateBlob dyn_CreateBlob = NULL;
|
||||
|
||||
PFN_D3D12_CREATE_DEVICE dyn_D3D12CreateDevice = NULL;
|
||||
@@ -382,6 +383,7 @@ void D3D12GraphicsTest::Prepare(int argc, char **argv)
|
||||
dyn_D3DCompile = (pD3DCompile)GetProcAddress(d3dcompiler, "D3DCompile");
|
||||
dyn_D3DStripShader = (pD3DStripShader)GetProcAddress(d3dcompiler, "D3DStripShader");
|
||||
dyn_D3DSetBlobPart = (pD3DSetBlobPart)GetProcAddress(d3dcompiler, "D3DSetBlobPart");
|
||||
dyn_D3DGetBlobPart = (pD3DGetBlobPart)GetProcAddress(d3dcompiler, "D3DGetBlobPart");
|
||||
dyn_CreateBlob = (pD3DCreateBlob)GetProcAddress(d3dcompiler, "D3DCreateBlob");
|
||||
}
|
||||
|
||||
@@ -441,7 +443,7 @@ void D3D12GraphicsTest::Prepare(int argc, char **argv)
|
||||
else if(!factory)
|
||||
Avail = "Couldn't create DXGI factory";
|
||||
else if(!dyn_D3D12CreateDevice || !dyn_D3DCompile || !dyn_D3DStripShader || !dyn_D3DSetBlobPart ||
|
||||
!dyn_CreateBlob)
|
||||
!dyn_D3DGetBlobPart || !dyn_CreateBlob)
|
||||
Avail = "Missing required entry point";
|
||||
else if(!dyn_serializeRootSig && !dyn_serializeRootSigOld)
|
||||
Avail = "Missing required root signature serialize entry point";
|
||||
@@ -1441,9 +1443,10 @@ ID3DBlobPtr D3D12GraphicsTest::Compile(std::string src, std::string entry, std::
|
||||
uint32_t compileOptions)
|
||||
{
|
||||
ID3DBlobPtr blob = NULL;
|
||||
bool skipoptimise = ((compileOptions & CompileOptionFlags::SkipOptimise) != 0);
|
||||
bool enable16BitTypes = ((compileOptions & CompileOptionFlags::Enable16BitTypes) != 0);
|
||||
bool separateDebug = ((compileOptions & CompileOptionFlags::SeparateDebug) != 0);
|
||||
const bool skipoptimise = ((compileOptions & CompileOptionFlags::SkipOptimise) != 0);
|
||||
const bool enable16BitTypes = ((compileOptions & CompileOptionFlags::Enable16BitTypes) != 0);
|
||||
const bool separateDebug = ((compileOptions & CompileOptionFlags::SeparateDebug) != 0);
|
||||
const bool manualDebugName = ((compileOptions & CompileOptionFlags::ManualDebugName) != 0);
|
||||
|
||||
if(profile[3] >= '6')
|
||||
{
|
||||
@@ -1516,6 +1519,10 @@ ID3DBlobPtr D3D12GraphicsTest::Compile(std::string src, std::string entry, std::
|
||||
argStorage.push_back(L"-select-validator internal");
|
||||
}
|
||||
|
||||
// use source for debug name so we can have just a comment and get different debug info if we want
|
||||
if(!manualDebugName)
|
||||
argStorage.push_back(L"-Zss");
|
||||
|
||||
// Must be the final option
|
||||
argStorage.push_back(L"-Qembed_debug");
|
||||
|
||||
@@ -1529,6 +1536,8 @@ ID3DBlobPtr D3D12GraphicsTest::Compile(std::string src, std::string entry, std::
|
||||
IDxcOperationResultPtr result;
|
||||
HRESULT hrStatus = E_NOINTERFACE;
|
||||
|
||||
std::string debugBlobPath;
|
||||
|
||||
if(separateDebug)
|
||||
{
|
||||
IDxcCompiler2Ptr compiler2 = compiler;
|
||||
@@ -1554,20 +1563,16 @@ ID3DBlobPtr D3D12GraphicsTest::Compile(std::string src, std::string entry, std::
|
||||
CoTaskMemFree(debugBlobWideName);
|
||||
}
|
||||
|
||||
if(manualDebugName)
|
||||
{
|
||||
debugBlobName = "shader_" + profile.substr(0, 2) + ".dbg";
|
||||
}
|
||||
|
||||
if(debugBlob)
|
||||
{
|
||||
std::string path = GetExecutableName();
|
||||
path.erase(path.find_last_of("/\\"));
|
||||
path += "/tmp/";
|
||||
MakeDir(path.c_str());
|
||||
path += demoName;
|
||||
path += "/";
|
||||
MakeDir(path.c_str());
|
||||
path += "dxcDebugBlobs/";
|
||||
MakeDir(path.c_str());
|
||||
debugBlobPath = tmpPath + "/" + debugBlobName;
|
||||
|
||||
WriteBlob(path + debugBlobName, debugBlob->GetBufferPointer(), debugBlob->GetBufferSize(),
|
||||
false);
|
||||
WriteBlob(debugBlobPath, debugBlob->GetBufferPointer(), debugBlob->GetBufferSize(), false);
|
||||
}
|
||||
|
||||
if(result)
|
||||
@@ -1629,6 +1634,26 @@ ID3DBlobPtr D3D12GraphicsTest::Compile(std::string src, std::string entry, std::
|
||||
TEST_ERROR("No compilation result found from DXC compile: %x", hr);
|
||||
}
|
||||
}
|
||||
|
||||
if(manualDebugName)
|
||||
{
|
||||
std::string nameBlob = debugBlobPath;
|
||||
// pad the blob to multiple of 4
|
||||
nameBlob.resize((debugBlobPath.size() + 0x3) & ~0x3);
|
||||
|
||||
nameBlob.insert(0, 4, '\0');
|
||||
nameBlob[2] += debugBlobPath.size() & 0xff;
|
||||
|
||||
if(debugBlobPath.size() > 255)
|
||||
TEST_ERROR("tmp path is too long!");
|
||||
|
||||
ID3DBlobPtr newBlob;
|
||||
|
||||
hr = dyn_D3DSetBlobPart(blob->GetBufferPointer(), blob->GetBufferSize(), D3D_BLOB_DEBUG_NAME,
|
||||
0, nameBlob.c_str(), nameBlob.size(), &newBlob);
|
||||
|
||||
blob = newBlob;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -30,17 +30,6 @@
|
||||
#include "dx/official/dxgi.h"
|
||||
#include "d3d12_helpers.h"
|
||||
|
||||
#include "dx/official/d3dcompiler.h"
|
||||
|
||||
typedef HRESULT(WINAPI *pD3DStripShader)(_In_reads_bytes_(BytecodeLength) LPCVOID pShaderBytecode,
|
||||
_In_ SIZE_T BytecodeLength, _In_ UINT uStripFlags,
|
||||
_Out_ ID3DBlob **ppStrippedBlob);
|
||||
typedef HRESULT(WINAPI *pD3DSetBlobPart)(_In_reads_bytes_(SrcDataSize) LPCVOID pSrcData,
|
||||
_In_ SIZE_T SrcDataSize, _In_ D3D_BLOB_PART Part,
|
||||
_In_ UINT Flags, _In_reads_bytes_(PartSize) LPCVOID pPart,
|
||||
_In_ SIZE_T PartSize, _Out_ ID3DBlob **ppNewShader);
|
||||
typedef HRESULT(WINAPI *pD3DCreateBlob)(SIZE_T Size, ID3DBlob **ppBlob);
|
||||
|
||||
struct Win32Window;
|
||||
|
||||
struct D3D12GraphicsTest : public GraphicsTest
|
||||
@@ -83,6 +72,7 @@ struct D3D12GraphicsTest : public GraphicsTest
|
||||
SkipOptimise = 1 << 0,
|
||||
Enable16BitTypes = 1 << 1,
|
||||
SeparateDebug = 1 << 2,
|
||||
ManualDebugName = 1 << 3,
|
||||
};
|
||||
|
||||
ID3DBlobPtr Compile(std::string src, std::string entry, std::string profile,
|
||||
|
||||
@@ -214,6 +214,7 @@
|
||||
<ClCompile Include="d3d12\d3d12_sharing.cpp" />
|
||||
<ClCompile Include="d3d12\d3d12_simple_dispatch.cpp" />
|
||||
<ClCompile Include="d3d12\d3d12_simple_triangle.cpp" />
|
||||
<ClCompile Include="d3d12\d3d12_stripped_shaders.cpp" />
|
||||
<ClCompile Include="d3d12\d3d12_subgroup_zoo.cpp" />
|
||||
<ClCompile Include="d3d12\d3d12_swapchain_zoo.cpp" />
|
||||
<ClCompile Include="d3d12\d3d12_template.cpp" />
|
||||
@@ -348,6 +349,7 @@
|
||||
<ClCompile Include="vk\vk_shader_printf.cpp" />
|
||||
<ClCompile Include="vk\vk_spec_constants.cpp" />
|
||||
<ClCompile Include="vk\vk_spirv_13_shaders.cpp" />
|
||||
<ClCompile Include="vk\vk_stripped_shaders.cpp" />
|
||||
<ClCompile Include="vk\vk_subgroup_zoo.cpp" />
|
||||
<ClCompile Include="vk\vk_sync2.cpp" />
|
||||
<ClCompile Include="vk\vk_template.cpp" />
|
||||
|
||||
@@ -769,6 +769,12 @@
|
||||
<ClCompile Include="vk\vk_device_address_commands.cpp">
|
||||
<Filter>Vulkan\demos</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="d3d12\d3d12_stripped_shaders.cpp">
|
||||
<Filter>D3D12\demos</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="vk\vk_stripped_shaders.cpp">
|
||||
<Filter>Vulkan\demos</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Filter Include="D3D11">
|
||||
|
||||
@@ -29,10 +29,24 @@
|
||||
#include <vector>
|
||||
#include "dx/official/dxgi1_4.h"
|
||||
|
||||
#include "dx/official/d3dcompiler.h"
|
||||
|
||||
extern std::string D3DFullscreenQuadVertex;
|
||||
extern std::string D3DDefaultVertex;
|
||||
extern std::string D3DDefaultPixel;
|
||||
|
||||
typedef HRESULT(WINAPI *pD3DStripShader)(_In_reads_bytes_(BytecodeLength) LPCVOID pShaderBytecode,
|
||||
_In_ SIZE_T BytecodeLength, _In_ UINT uStripFlags,
|
||||
_Out_ ID3DBlob **ppStrippedBlob);
|
||||
typedef HRESULT(WINAPI *pD3DSetBlobPart)(_In_reads_bytes_(SrcDataSize) LPCVOID pSrcData,
|
||||
_In_ SIZE_T SrcDataSize, _In_ D3D_BLOB_PART Part,
|
||||
_In_ UINT Flags, _In_reads_bytes_(PartSize) LPCVOID pPart,
|
||||
_In_ SIZE_T PartSize, _Out_ ID3DBlob **ppNewShader);
|
||||
typedef HRESULT(WINAPI *pD3DGetBlobPart)(_In_reads_bytes_(SrcDataSize) LPCVOID pSrcData,
|
||||
_In_ SIZE_T SrcDataSize, _In_ D3D_BLOB_PART Part,
|
||||
_In_ UINT Flags, _Out_ ID3DBlob **ppPart);
|
||||
typedef HRESULT(WINAPI *pD3DCreateBlob)(SIZE_T Size, ID3DBlob **ppBlob);
|
||||
|
||||
#define COM_SMARTPTR(classname) _COM_SMARTPTR_TYPEDEF(classname, __uuidof(classname))
|
||||
|
||||
COM_SMARTPTR(IDXGISwapChain);
|
||||
|
||||
@@ -691,8 +691,8 @@ Usage: %s Test_Name [test_options]
|
||||
if(testchoice == name)
|
||||
{
|
||||
TEST_LOG("Running '%s'", test.Name);
|
||||
test.test->Prepare(argc, argv);
|
||||
test.test->SetName(test.Name);
|
||||
test.test->Prepare(argc, argv);
|
||||
|
||||
if(!test.IsAvailable())
|
||||
{
|
||||
|
||||
@@ -284,7 +284,8 @@ bool SpvCompilationSupported()
|
||||
std::vector<uint32_t> CompileShaderToSpv(const char *demo_name, const std::string &source_text,
|
||||
SPIRVTarget target, ShaderLang lang, ShaderStage stage,
|
||||
const char *entry_point,
|
||||
const std::map<std::string, std::string> ¯os)
|
||||
const std::map<std::string, std::string> ¯os,
|
||||
bool debug_info)
|
||||
{
|
||||
std::vector<uint32_t> ret;
|
||||
|
||||
@@ -303,7 +304,9 @@ std::vector<uint32_t> CompileShaderToSpv(const char *demo_name, const std::strin
|
||||
|
||||
if(externalCompiler == "glslc")
|
||||
{
|
||||
command_line = "glslc" EXECUTABLE_SUFFIX " -g -O0";
|
||||
command_line = "glslc" EXECUTABLE_SUFFIX;
|
||||
if(debug_info)
|
||||
command_line += " -g -O0";
|
||||
command_line += " -fentry-point=";
|
||||
command_line += entry_point;
|
||||
|
||||
@@ -349,7 +352,9 @@ std::vector<uint32_t> CompileShaderToSpv(const char *demo_name, const std::strin
|
||||
}
|
||||
else if(externalCompiler == "glslangValidator")
|
||||
{
|
||||
command_line = "glslangValidator" EXECUTABLE_SUFFIX " -g ";
|
||||
command_line = "glslangValidator" EXECUTABLE_SUFFIX;
|
||||
if(debug_info)
|
||||
command_line += " -g ";
|
||||
command_line += " --entry-point ";
|
||||
command_line += entry_point;
|
||||
|
||||
@@ -458,6 +463,11 @@ void GraphicsTest::Prepare(int argc, char **argv)
|
||||
if(dataRoot.empty())
|
||||
dataRoot = GetCWD() + "/data/demos/";
|
||||
|
||||
tmpPath = GetExecutableName();
|
||||
tmpPath.erase(tmpPath.find_last_of("/\\"));
|
||||
tmpPath += "/tmp";
|
||||
MakeDir(tmpPath.c_str());
|
||||
|
||||
// parse parameters
|
||||
for(int i = 0; i < argc; i++)
|
||||
{
|
||||
@@ -477,6 +487,11 @@ void GraphicsTest::Prepare(int argc, char **argv)
|
||||
logFile = fopen(argv[i + 1], "w");
|
||||
}
|
||||
|
||||
if(i + 1 < argc && !strcmp(argv[i], "--tmp"))
|
||||
{
|
||||
tmpPath = argv[i + 1];
|
||||
}
|
||||
|
||||
if(i + 1 < argc && (!strcmp(argv[i], "--width") || !strcmp(argv[i], "-w")))
|
||||
{
|
||||
screenWidth = atoi(argv[i + 1]);
|
||||
@@ -506,6 +521,10 @@ void GraphicsTest::Prepare(int argc, char **argv)
|
||||
}
|
||||
}
|
||||
|
||||
tmpPath += "/";
|
||||
tmpPath += demoName;
|
||||
MakeDir(tmpPath.c_str());
|
||||
|
||||
pRENDERDOC_GetAPI RENDERDOC_GetAPI = NULL;
|
||||
|
||||
#if defined(WIN32)
|
||||
|
||||
@@ -87,7 +87,8 @@ bool SpvCompilationSupported();
|
||||
std::vector<uint32_t> CompileShaderToSpv(const char *demo_name, const std::string &source_text,
|
||||
SPIRVTarget target, ShaderLang lang, ShaderStage stage,
|
||||
const char *entry_point,
|
||||
const std::map<std::string, std::string> ¯os = {});
|
||||
const std::map<std::string, std::string> ¯os = {},
|
||||
bool debug_info = true);
|
||||
|
||||
struct Vec2f
|
||||
{
|
||||
@@ -224,6 +225,8 @@ struct GraphicsTest
|
||||
|
||||
RENDERDOC_API_1_7_0 *rdoc = NULL;
|
||||
|
||||
std::string tmpPath;
|
||||
|
||||
// shared parameters
|
||||
static int maxFrameCount;
|
||||
static std::string dataRoot;
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
/******************************************************************************
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2023-2026 Baldur Karlsson
|
||||
*
|
||||
* 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 "vk_test.h"
|
||||
|
||||
RD_TEST(VK_Stripped_Shaders, VulkanGraphicsTest)
|
||||
{
|
||||
static constexpr const char *Description =
|
||||
"Tests shaders with their debug/reflection info stripped out and stored in separate blobs.";
|
||||
|
||||
int main()
|
||||
{
|
||||
// initialise, create window, create context, etc
|
||||
if(!Init())
|
||||
return 3;
|
||||
|
||||
vkh::GraphicsPipelineCreateInfo pipeCreateInfo;
|
||||
|
||||
VkPipelineLayout layout = createPipelineLayout(vkh::PipelineLayoutCreateInfo());
|
||||
|
||||
pipeCreateInfo.layout = layout;
|
||||
pipeCreateInfo.renderPass = mainWindow->rp;
|
||||
|
||||
pipeCreateInfo.vertexInputState.vertexBindingDescriptions = {vkh::vertexBind(0, DefaultA2V)};
|
||||
pipeCreateInfo.vertexInputState.vertexAttributeDescriptions = {
|
||||
vkh::vertexAttr(0, 0, DefaultA2V, pos),
|
||||
vkh::vertexAttr(1, 0, DefaultA2V, col),
|
||||
vkh::vertexAttr(2, 0, DefaultA2V, uv),
|
||||
};
|
||||
|
||||
pipeCreateInfo.stages = {
|
||||
CompileShaderModule(VKDefaultVertex + "\n// shader source // vertex", ShaderLang::glsl,
|
||||
ShaderStage::vert, "main", {}, SPIRVTarget::vulkan,
|
||||
CompileOptionFlags::SeparateDebug),
|
||||
CompileShaderModule(VKDefaultPixel + "\n// shader source // pixel", ShaderLang::glsl,
|
||||
ShaderStage::frag, "main", {}, SPIRVTarget::vulkan,
|
||||
CompileOptionFlags::SeparateDebug | CompileOptionFlags::RelativeDebugName),
|
||||
};
|
||||
|
||||
VkPipeline pipe = createGraphicsPipeline(pipeCreateInfo);
|
||||
|
||||
while(Running())
|
||||
{
|
||||
VkCommandBuffer cmd = GetCommandBuffer();
|
||||
|
||||
vkBeginCommandBuffer(cmd, vkh::CommandBufferBeginInfo());
|
||||
|
||||
VkImage swapimg = StartUsingBackbuffer(cmd);
|
||||
|
||||
vkh::cmdClearImage(cmd, swapimg, vkh::ClearColorValue(0.2f, 0.2f, 0.2f, 1.0f));
|
||||
|
||||
vkCmdBeginRenderPass(cmd, mainWindow->beginRP(), VK_SUBPASS_CONTENTS_INLINE);
|
||||
|
||||
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipe);
|
||||
mainWindow->setViewScissor(cmd);
|
||||
vkh::cmdBindVertexBuffers(cmd, {DefaultTriVB.buffer});
|
||||
|
||||
setMarker(cmd, "test: shader source");
|
||||
setMarker(cmd, "vertex manual absolute");
|
||||
setMarker(cmd, "pixel manual relative");
|
||||
vkCmdDraw(cmd, 3, 1, 0, 0);
|
||||
|
||||
vkCmdEndRenderPass(cmd);
|
||||
|
||||
FinishUsingBackbuffer(cmd);
|
||||
|
||||
vkEndCommandBuffer(cmd);
|
||||
|
||||
SubmitAndPresent({cmd});
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
REGISTER_TEST();
|
||||
@@ -1009,18 +1009,63 @@ VkPipelineShaderStageCreateInfo VulkanGraphicsTest::LoadShaderModule(const std::
|
||||
|
||||
VkPipelineShaderStageCreateInfo VulkanGraphicsTest::CompileShaderModule(
|
||||
const std::string &source_text, ShaderLang lang, ShaderStage stage, const char *entry_point,
|
||||
const std::map<std::string, std::string> ¯os, SPIRVTarget target)
|
||||
const std::map<std::string, std::string> ¯os, SPIRVTarget target, uint32_t compileOptions)
|
||||
{
|
||||
VkShaderModule ret = VK_NULL_HANDLE;
|
||||
|
||||
const bool separateDebug = ((compileOptions & CompileOptionFlags::SeparateDebug) != 0);
|
||||
const bool relativeDebugName = ((compileOptions & CompileOptionFlags::RelativeDebugName) != 0);
|
||||
|
||||
std::vector<uint32_t> spirv =
|
||||
::CompileShaderToSpv(demoName, source_text, target, lang, stage, entry_point, macros);
|
||||
|
||||
if(spirv.empty())
|
||||
return {};
|
||||
|
||||
std::string path;
|
||||
if(separateDebug)
|
||||
{
|
||||
path = tmpPath + "/";
|
||||
|
||||
std::string filenames[] = {
|
||||
"shader_vs.dbg", "shader_hs.dbg", "shader_ds.dbg", "shader_gs.dbg",
|
||||
"shader_ps.dbg", "shader_cs.dbg", "shader_ms.dbg", "shader_as.dbg",
|
||||
};
|
||||
|
||||
path += filenames[(int)stage];
|
||||
|
||||
FILE *f = fopen(path.c_str(), "wb");
|
||||
if(f)
|
||||
{
|
||||
fwrite(spirv.data(), 1, spirv.size() * sizeof(uint32_t), f);
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
spirv =
|
||||
::CompileShaderToSpv(demoName, source_text, target, lang, stage, entry_point, macros, false);
|
||||
}
|
||||
|
||||
CHECK_VKR(vkCreateShaderModule(device, vkh::ShaderModuleCreateInfo(spirv), NULL, &ret));
|
||||
|
||||
if(relativeDebugName)
|
||||
{
|
||||
path.erase(0, path.find_last_of("/\\") + 1);
|
||||
// keep the demo name so we don't have to set up tons of search paths
|
||||
path = std::string(demoName) + "/" + path;
|
||||
}
|
||||
|
||||
if(vkSetDebugUtilsObjectTagEXT)
|
||||
{
|
||||
VkDebugUtilsObjectTagInfoEXT info = {};
|
||||
info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_TAG_INFO_EXT;
|
||||
info.objectType = VK_OBJECT_TYPE_SHADER_MODULE;
|
||||
info.objectHandle = (uint64_t)ret;
|
||||
info.pTag = path.c_str();
|
||||
info.tagSize = path.size();
|
||||
info.tagName = RENDERDOC_ShaderDebugMagicValue_truncated;
|
||||
vkSetDebugUtilsObjectTagEXT(device, &info);
|
||||
}
|
||||
|
||||
shaders.push_back(ret);
|
||||
|
||||
VkShaderStageFlagBits vkstage[] = {
|
||||
|
||||
@@ -194,6 +194,13 @@ struct VulkanGraphicsTest : public GraphicsTest
|
||||
{
|
||||
static const TestAPI API = TestAPI::Vulkan;
|
||||
|
||||
enum CompileOptionFlags
|
||||
{
|
||||
None = 0,
|
||||
SeparateDebug = 1 << 0,
|
||||
RelativeDebugName = 1 << 1,
|
||||
};
|
||||
|
||||
VulkanGraphicsTest();
|
||||
|
||||
void Prepare(int argc, char **argv);
|
||||
@@ -222,7 +229,7 @@ struct VulkanGraphicsTest : public GraphicsTest
|
||||
VkPipelineShaderStageCreateInfo CompileShaderModule(
|
||||
const std::string &source_text, ShaderLang lang, ShaderStage stage,
|
||||
const char *entry_point = "main", const std::map<std::string, std::string> ¯os = {},
|
||||
SPIRVTarget target = SPIRVTarget::vulkan);
|
||||
SPIRVTarget target = SPIRVTarget::vulkan, uint32_t compileOptions = CompileOptionFlags::None);
|
||||
VkCommandBuffer GetCommandBuffer(VkCommandBufferLevel level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
|
||||
VulkanWindow *window = NULL);
|
||||
|
||||
|
||||
@@ -14,3 +14,4 @@ from .shared.Pixel_History import *
|
||||
from .shared.Workgroup_Zoo import *
|
||||
from .shared.Groupshared import *
|
||||
from .shared.Annotations import *
|
||||
from .shared.Stripped_Shaders import *
|
||||
+15
-22
@@ -6,7 +6,7 @@ from . import util
|
||||
|
||||
|
||||
def open_capture(
|
||||
filename="", cap: rd.CaptureFile | None = None, opts: rd.ReplayOptions | None = None
|
||||
filename="", opts: rd.ReplayOptions | None = None
|
||||
):
|
||||
"""
|
||||
Opens a capture file and begins a replay.
|
||||
@@ -21,32 +21,28 @@ def open_capture(
|
||||
if opts is None:
|
||||
opts = rd.ReplayOptions()
|
||||
|
||||
# Open a capture file handle
|
||||
own_cap = False
|
||||
cap = None
|
||||
api = "Unknown"
|
||||
|
||||
result = None
|
||||
controller = None
|
||||
if util.get_remote_server() is None:
|
||||
if cap is None:
|
||||
own_cap = True
|
||||
cap = rd.OpenCaptureFile()
|
||||
|
||||
cap = rd.OpenCaptureFile()
|
||||
# Open a particular file
|
||||
result = cap.OpenFile(filename, '', None)
|
||||
|
||||
# Open a particular file
|
||||
result = cap.OpenFile(filename, '', None)
|
||||
# Make sure the file opened successfully
|
||||
if not result:
|
||||
cap.Shutdown()
|
||||
raise RuntimeError(f"Couldn't open '{filename}': {result!s}")
|
||||
|
||||
# Make sure the file opened successfully
|
||||
if not result:
|
||||
cap.Shutdown()
|
||||
raise RuntimeError(f"Couldn't open '{filename}': {result!s}")
|
||||
api = cap.DriverName()
|
||||
|
||||
api = cap.DriverName()
|
||||
|
||||
# Make sure we can replay
|
||||
if not cap.LocalReplaySupport():
|
||||
cap.Shutdown()
|
||||
raise RuntimeError(f"{api} capture cannot be replayed")
|
||||
# Make sure we can replay
|
||||
if not cap.LocalReplaySupport():
|
||||
cap.Shutdown()
|
||||
raise RuntimeError(f"{api} capture cannot be replayed")
|
||||
|
||||
result, controller = cap.OpenCapture(opts, None)
|
||||
else:
|
||||
@@ -58,13 +54,10 @@ def open_capture(
|
||||
if result:
|
||||
api = util.get_remote_server().remote.DriverName()
|
||||
|
||||
if own_cap:
|
||||
cap.Shutdown()
|
||||
|
||||
if not result:
|
||||
raise RuntimeError(f"Couldn't initialise replay for {api}: {result!s}")
|
||||
|
||||
return controller
|
||||
return cap, controller
|
||||
|
||||
|
||||
def fetch_indices(controller: rd.ReplayController, action: rd.ActionDescription, mesh: rd.MeshFormat, index_offset: int, first_index: int, num_indices: int) -> List[int | None]:
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
import rdtest
|
||||
import shutil
|
||||
import renderdoc as rd
|
||||
import re
|
||||
|
||||
|
||||
# Not a real test, re-used by API-specific tests
|
||||
class Stripped_Shaders(rdtest.TestCase):
|
||||
internal = True
|
||||
demos_test_name = None
|
||||
|
||||
def get_capture_options(self):
|
||||
# We don't actually need to set any capture-time options but we'll use this to change the search dir paths
|
||||
paths = rd.SetConfigSetting("DXBC.Debug.SearchDirPaths")
|
||||
|
||||
paths.DeleteChildren()
|
||||
paths.AddChild(rd.makeSDString("path", rdtest.get_tmp_dir()))
|
||||
|
||||
# this won't automatically save and we won't call SaveConfigSettings()
|
||||
|
||||
return rd.CaptureOptions()
|
||||
|
||||
def check_capture(self):
|
||||
self.check_debug_symbols(False)
|
||||
|
||||
filename = self.retrieve_capture()
|
||||
embed_path = rdtest.get_tmp_path("embedded.rdc")
|
||||
|
||||
shutil.copy(filename, embed_path)
|
||||
|
||||
# shut down the controller that is for the old file, to keep it the same
|
||||
self.controller.Shutdown()
|
||||
self.controller = None
|
||||
if self.cap is not None:
|
||||
self.cap.Shutdown()
|
||||
self.cap = None
|
||||
|
||||
# open the version we're going to embed into
|
||||
self.cap, self.controller = rdtest.open_capture(embed_path)
|
||||
|
||||
assert self.cap is not None
|
||||
assert len(self.cap.GetPendingDependenciesNicknames()) > 0
|
||||
|
||||
# embed the dependencies
|
||||
assert self.cap.EmbedDependenciesIntoCapture().OK()
|
||||
|
||||
self.controller.Shutdown()
|
||||
self.cap.Shutdown()
|
||||
|
||||
# now clear the search paths, this should make at least one fail
|
||||
rd.SetConfigSetting("DXBC.Debug.SearchDirPaths").DeleteChildren()
|
||||
|
||||
self.cap, self.controller = rdtest.open_capture(embed_path)
|
||||
self.sdfile = self.controller.GetStructuredFile()
|
||||
|
||||
# check everything is still OK
|
||||
self.check_debug_symbols(True)
|
||||
|
||||
def check_debug_symbols(self, embedded: bool):
|
||||
test = self.find_action("test:", 0)
|
||||
|
||||
while test is not None:
|
||||
pix_cfg = self.get_action(test.eventId + 2)
|
||||
assert pix_cfg is not None
|
||||
draw = self.get_action(test.eventId + 3)
|
||||
assert draw is not None
|
||||
|
||||
search_text = test.customName.replace("test: ", "")
|
||||
assert "pixel" in pix_cfg.customName
|
||||
|
||||
self.set_event(draw.eventId, False)
|
||||
|
||||
pipe = self.controller.GetPipelineState()
|
||||
|
||||
stages = [
|
||||
(1, rd.ShaderStage.Vertex, "vertex", "shader_vs.dbg"),
|
||||
(2, rd.ShaderStage.Pixel, "pixel", "shader_ps.dbg"),
|
||||
]
|
||||
|
||||
for offs, stage, name, manual_filename in stages:
|
||||
cfg = self.get_action(test.eventId + offs)
|
||||
assert cfg is not None
|
||||
|
||||
assert name in cfg.customName
|
||||
|
||||
refl = pipe.GetShaderReflection(stage)
|
||||
|
||||
assert len(refl.debugInfo.files) == 1
|
||||
|
||||
src = refl.debugInfo.files[0].contents
|
||||
|
||||
assert f"// {search_text} // {name}" in src
|
||||
|
||||
log = refl.debugInfo.debugInfoLoadingLog
|
||||
|
||||
assert re.match("Found ", log, re.IGNORECASE)
|
||||
|
||||
if "auto" in cfg.customName:
|
||||
assert re.search("[a-f0-9]{32}.pdb", log) is not None
|
||||
elif "manual" in cfg.customName:
|
||||
assert f"{manual_filename}'" in log
|
||||
else:
|
||||
raise rdtest.TestFailureException(
|
||||
f"Unexpected {name} config {cfg.customName}"
|
||||
)
|
||||
|
||||
filepath = re.search("Shader filepath '([^']*)'", log)
|
||||
assert filepath is not None
|
||||
|
||||
filepath = filepath[1].replace("\\", "/")
|
||||
tmp_dir = rdtest.get_tmp_dir().replace("\\", "/")
|
||||
|
||||
if "absolute" in cfg.customName:
|
||||
assert tmp_dir in filepath
|
||||
assert "File found using filepath" in log
|
||||
assert "File found in search directory" not in log
|
||||
assert "File found in recursive directory search" not in log
|
||||
assert "embedded in the capture using nickname" not in log
|
||||
elif "relative" in cfg.customName:
|
||||
assert tmp_dir not in filepath
|
||||
assert "File found using filepath" not in log
|
||||
if embedded:
|
||||
assert "embedded in the capture using nickname" in log
|
||||
assert "File found in search directory" not in log
|
||||
assert "File found in recursive directory search" not in log
|
||||
else:
|
||||
assert (
|
||||
"File found in search directory" in log
|
||||
or "File found in recursive directory search" in log
|
||||
)
|
||||
else:
|
||||
raise rdtest.TestFailureException(
|
||||
f"Unexpected {name} config {cfg.customName}"
|
||||
)
|
||||
|
||||
rdtest.log.success(f"{name} shader debug info and loading log looks OK")
|
||||
|
||||
test = self.find_action("test:", draw.eventId)
|
||||
|
||||
rdtest.log.success("All draws had proper shader debug info")
|
||||
@@ -263,6 +263,7 @@ class TestCase:
|
||||
self.capture_filename = ""
|
||||
self.worker_thread = 0
|
||||
self.controller: rd.ReplayController | None = None
|
||||
self.cap: rd.CaptureFile | None = None
|
||||
self.sdfile: rd.SDFile | None = None
|
||||
self.contexts: List[ScopedContext] = []
|
||||
self.cur_event = 0
|
||||
@@ -319,7 +320,7 @@ class TestCase:
|
||||
if timeout is None:
|
||||
timeout = util.get_demos_timeout()
|
||||
return capture.run_and_capture(exe,
|
||||
self.demos_test_name + " --log " + remote_logfile,
|
||||
f"{self.demos_test_name} --log {remote_logfile} --tmp {util.get_tmp_dir()}",
|
||||
self.demos_frame_cap, frame_count=self.demos_frame_count,
|
||||
captures_expected=self.demos_captures_expected, logfile=logfile,
|
||||
opts=self.get_capture_options(), timeout=timeout)
|
||||
@@ -761,7 +762,7 @@ class TestCase:
|
||||
|
||||
log.print("Loading capture")
|
||||
|
||||
self.controller = analyse.open_capture(self.capture_filename, opts=self.get_replay_options())
|
||||
self.cap, self.controller = analyse.open_capture(self.capture_filename, opts=self.get_replay_options())
|
||||
self.sdfile = self.controller.GetStructuredFile()
|
||||
|
||||
if not self.validate_eventids(self.controller):
|
||||
@@ -782,6 +783,9 @@ class TestCase:
|
||||
else:
|
||||
self.controller.Shutdown()
|
||||
|
||||
if self.cap is not None:
|
||||
self.cap.Shutdown()
|
||||
|
||||
def invoketest(self, debugMode: bool):
|
||||
start_time = self.get_time()
|
||||
self.run()
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
import rdtest
|
||||
|
||||
|
||||
class D3D11_Stripped_Shaders(rdtest.Stripped_Shaders):
|
||||
demos_test_name = 'D3D11_Stripped_Shaders'
|
||||
internal = False
|
||||
@@ -0,0 +1,6 @@
|
||||
import rdtest
|
||||
|
||||
|
||||
class D3D12_Stripped_Shaders(rdtest.Stripped_Shaders):
|
||||
demos_test_name = 'D3D12_Stripped_Shaders'
|
||||
internal = False
|
||||
@@ -30,19 +30,12 @@ class GL_Buffer_Updates(rdtest.TestCase):
|
||||
|
||||
rdtest.log.success("Draws are all green")
|
||||
|
||||
# Open the capture and grab the thumbnail, check that it is all green too (dirty way of verifying we didn't
|
||||
# grab the thumbnail, check that it is all green too (dirty way of verifying we didn't
|
||||
# break in-app updates but somehow end up with the right data)
|
||||
cap = rd.OpenCaptureFile()
|
||||
if self.cap is None:
|
||||
raise rdtest.TestFailureException(f"Don't have open capture {self.capture_filename}")
|
||||
|
||||
# Open a particular file
|
||||
result = cap.OpenFile(self.capture_filename, '', None)
|
||||
|
||||
# Make sure the file opened successfully
|
||||
if not result:
|
||||
cap.Shutdown()
|
||||
raise rdtest.TestFailureException(f"Couldn't open '{self.capture_filename}': {result!s}")
|
||||
|
||||
thumb = cap.GetThumbnail(rd.FileType.PNG, 0)
|
||||
thumb = self.cap.GetThumbnail(rd.FileType.PNG, 0)
|
||||
|
||||
tmp_path = rdtest.get_tmp_path('thumbnail.png')
|
||||
|
||||
|
||||
@@ -21,21 +21,13 @@ class GL_Callstacks(rdtest.TestCase):
|
||||
return ret
|
||||
|
||||
def check_capture(self):
|
||||
# Need capture access. Rather than trying to keep the original around, we just open a new one
|
||||
cap = rd.OpenCaptureFile()
|
||||
if self.cap is None:
|
||||
raise rdtest.TestFailureException(f"Don't have open capture {self.capture_filename}")
|
||||
|
||||
# Open a particular file
|
||||
result = cap.OpenFile(self.capture_filename, '', None)
|
||||
|
||||
# Make sure the file opened successfully
|
||||
if not result:
|
||||
cap.Shutdown()
|
||||
raise rdtest.TestFailureException(f"Couldn't open capture {self.capture_filename} for access: {result!s}")
|
||||
|
||||
if not cap.HasCallstacks():
|
||||
if not self.cap.HasCallstacks():
|
||||
raise rdtest.TestFailureException("Capture does not report having callstacks")
|
||||
|
||||
if not cap.InitResolver(False, resolve_progress):
|
||||
if not self.cap.InitResolver(False, resolve_progress):
|
||||
raise rdtest.TestFailureException("Failed to initialise callstack resolver")
|
||||
|
||||
action = self.find_action("Draw")
|
||||
@@ -59,7 +51,7 @@ class GL_Callstacks(rdtest.TestCase):
|
||||
|
||||
chunk = sdfile.chunks[event.chunkIndex]
|
||||
|
||||
callstack = cap.GetResolve(list(chunk.metadata.callstack))
|
||||
callstack = self.cap.GetResolve(list(chunk.metadata.callstack))
|
||||
|
||||
if len(callstack) < len(expected_funcs):
|
||||
raise rdtest.TestFailureException(f"Resolved callstack isn't long enough ({len(callstack)} stack frames), expected at least {len(expected_funcs)}")
|
||||
|
||||
@@ -436,7 +436,7 @@ class Iter_Test(rdtest.TestCase):
|
||||
rdtest.log.print(f"Opening '{file.name}'.")
|
||||
|
||||
try:
|
||||
self.controller = rdtest.open_capture(file.path)
|
||||
self.cap, self.controller = rdtest.open_capture(file.path)
|
||||
except RuntimeError as err:
|
||||
rdtest.log.print(f"Skipping. Can't open {file.path}: {err}")
|
||||
continue
|
||||
@@ -450,6 +450,8 @@ class Iter_Test(rdtest.TestCase):
|
||||
rdtest.log.end_section(section_name)
|
||||
|
||||
self.controller.Shutdown()
|
||||
if self.cap is not None:
|
||||
self.cap.Shutdown()
|
||||
|
||||
rdtest.log.success("Iterated all files")
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ class Repeat_Load(rdtest.TestCase):
|
||||
rdtest.log.print(f"Loading for iteration {i + 1}")
|
||||
|
||||
try:
|
||||
controller = rdtest.open_capture(path)
|
||||
cap, controller = rdtest.open_capture(path)
|
||||
except RuntimeError as err:
|
||||
rdtest.log.print(f"Skipping. Can't open {path}: {err}")
|
||||
return
|
||||
@@ -31,6 +31,8 @@ class Repeat_Load(rdtest.TestCase):
|
||||
memory_baseline = memory_usage
|
||||
|
||||
controller.Shutdown()
|
||||
if cap is not None:
|
||||
cap.Shutdown()
|
||||
|
||||
pct_over = 'N/A'
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ class VK_Large_Descriptor_Sets(rdtest.TestCase):
|
||||
memory_before = rd.GetCurrentProcessMemoryUsage()
|
||||
start_time = self.get_time()
|
||||
|
||||
self.controller = rdtest.open_capture(self.capture_filename, opts=self.get_replay_options())
|
||||
self.cap, self.controller = rdtest.open_capture(self.capture_filename, opts=self.get_replay_options())
|
||||
|
||||
duration = self.get_time() - start_time
|
||||
memory_after = rd.GetCurrentProcessMemoryUsage()
|
||||
@@ -37,3 +37,5 @@ class VK_Large_Descriptor_Sets(rdtest.TestCase):
|
||||
rdtest.log.print("Not checking time to load in non-release build")
|
||||
|
||||
self.controller.Shutdown()
|
||||
if self.cap is not None:
|
||||
self.cap.Shutdown()
|
||||
@@ -0,0 +1,6 @@
|
||||
import rdtest
|
||||
|
||||
|
||||
class VK_Stripped_Shaders(rdtest.Stripped_Shaders):
|
||||
demos_test_name = 'VK_Stripped_Shaders'
|
||||
internal = False
|
||||
Reference in new issue
Block a user