Skip to content

Commit e413e64

Browse files
zehao-jing-armbradgrantham-lunarg
authored andcommitted
Support DX shader replacement
1. Add support for DX shader replacement 2. Refine DX shader extraction 3. Update shader extraction file format Change-Id: I63010ad3951c65aca3ec9250066f7b296543688a
1 parent b21fe07 commit e413e64

9 files changed

Lines changed: 650 additions & 165 deletions

File tree

USAGE_desktop_D3D12.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,11 @@ Optional arguments:
278278
unspecified screenshots will use the swapchain images
279279
dimensions. If --screenshot-scale is also specified then
280280
this option is ignored.
281+
--replace-shaders <dir>
282+
Replace the shader code in each Create*Pipeline/CreateStateObject/AddToStateObject call
283+
with the contents of the file <dir>/sh<handle_id> if found, where
284+
<handle_id> is the handle id of the Create*Pipeline/CreateStateObject/AddToStateObject call.
285+
See gfxrecon-extract.
281286
--validate Enables the Khronos Vulkan validation layer when replaying a
282287
Vulkan capture or the Direct3D debug layer when replaying a
283288
Direct3D 12 capture.

framework/decode/dx12_replay_consumer_base.cpp

Lines changed: 285 additions & 10 deletions
Large diffs are not rendered by default.

framework/decode/dx_replay_options.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ struct DxReplayOptions : public ReplayOptions
4949
bool override_object_names{ false };
5050
bool ags_inject_markers{ false };
5151
int32_t memory_usage{ kDefaultBatchingMemoryUsage };
52+
std::string replace_shader_dir;
5253
};
5354

5455
GFXRECON_END_NAMESPACE(decode)

framework/graphics/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ target_sources(gfxrecon_graphics
4040
$<$<BOOL:${D3D12_SUPPORT}>:${CMAKE_CURRENT_LIST_DIR}/dx12_shader_id_map.cpp>
4141
$<$<BOOL:${D3D12_SUPPORT}>:${CMAKE_CURRENT_LIST_DIR}/dx12_image_renderer.h>
4242
$<$<BOOL:${D3D12_SUPPORT}>:${CMAKE_CURRENT_LIST_DIR}/dx12_image_renderer.cpp>
43+
$<$<BOOL:${D3D12_SUPPORT}>:${CMAKE_CURRENT_LIST_DIR}/dx12_shader_tool.h>
44+
$<$<BOOL:${D3D12_SUPPORT}>:${CMAKE_CURRENT_LIST_DIR}/dx12_shader_tool.cpp>
4345
${CMAKE_CURRENT_LIST_DIR}/fps_info.h
4446
${CMAKE_CURRENT_LIST_DIR}/fps_info.cpp
4547
${CMAKE_CURRENT_LIST_DIR}/vulkan_check_buffer_references.h
Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
/*
2+
** Copyright (c) 2025 LunarG, Inc.
3+
** Copyright (c) 2025-2026 Arm Limited and/or its affiliates <open-source-office@arm.com>
4+
**
5+
** Permission is hereby granted, free of charge, to any person obtaining a
6+
** copy of this software and associated documentation files (the "Software"),
7+
** to deal in the Software without restriction, including without limitation
8+
** the rights to use, copy, modify, merge, publish, distribute, sublicense,
9+
** and/or sell copies of the Software, and to permit persons to whom the
10+
** Software is furnished to do so, subject to the following conditions:
11+
**
12+
** The above copyright notice and this permission notice shall be included in
13+
** all copies or substantial portions of the Software.
14+
**
15+
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20+
** FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21+
** DEALINGS IN THE SOFTWARE.
22+
*/
23+
24+
#include "graphics/dx12_shader_tool.h"
25+
26+
#include "util/file_path.h"
27+
#include "util/logging.h"
28+
#include "util/platform.h"
29+
30+
GFXRECON_BEGIN_NAMESPACE(gfxrecon)
31+
GFXRECON_BEGIN_NAMESPACE(graphics)
32+
33+
const char* Dx12ShaderTool::ShaderTypeToString(ShaderType type)
34+
{
35+
switch (type)
36+
{
37+
case ShaderType::kVertex:
38+
return "vertex";
39+
case ShaderType::kDomain:
40+
return "domain";
41+
case ShaderType::kHull:
42+
return "hull";
43+
case ShaderType::kGeometry:
44+
return "geometry";
45+
case ShaderType::kPixel:
46+
return "pixel";
47+
case ShaderType::kCompute:
48+
return "compute";
49+
case ShaderType::kStateObjectDxilLibrary:
50+
return "dxil_library";
51+
default:
52+
return "unknown";
53+
}
54+
}
55+
56+
std::string Dx12ShaderTool::MakePipelineShaderFileName(uint64_t handle_id, ShaderType type)
57+
{
58+
std::string suffix;
59+
switch (type)
60+
{
61+
case ShaderType::kVertex:
62+
suffix = ".vs.cso";
63+
break;
64+
case ShaderType::kPixel:
65+
suffix = ".ps.cso";
66+
break;
67+
case ShaderType::kDomain:
68+
suffix = ".ds.cso";
69+
break;
70+
case ShaderType::kHull:
71+
suffix = ".hs.cso";
72+
break;
73+
case ShaderType::kGeometry:
74+
suffix = ".gs.cso";
75+
break;
76+
case ShaderType::kCompute:
77+
suffix = ".cs.cso";
78+
break;
79+
default:
80+
suffix = ".cso";
81+
break;
82+
}
83+
84+
return "sh" + std::to_string(handle_id) + suffix;
85+
}
86+
87+
std::string Dx12ShaderTool::MakeStateObjectDxilLibraryFileName(uint64_t handle_id, uint32_t subobject_index)
88+
{
89+
return "sh" + std::to_string(handle_id) + "_" + std::to_string(subobject_index) + ".cso";
90+
}
91+
92+
bool Dx12ShaderTool::ExtractShaderToDir(const std::string& extract_dir,
93+
const std::string& file_name,
94+
const void* code,
95+
size_t code_size)
96+
{
97+
if ((code == nullptr) || (code_size == 0))
98+
{
99+
return false;
100+
}
101+
102+
std::string file_path = util::filepath::Join(extract_dir, file_name);
103+
104+
FILE* fp = nullptr;
105+
auto result = util::platform::FileOpen(&fp, file_path.c_str(), "wb");
106+
if ((result == 0) && (fp != nullptr))
107+
{
108+
if (!util::platform::FileWrite(code, code_size, fp))
109+
{
110+
GFXRECON_WRITE_CONSOLE("Error while writing file %s: Could not complete", file_name.c_str());
111+
util::platform::FileClose(fp);
112+
return false;
113+
}
114+
util::platform::FileClose(fp);
115+
return true;
116+
}
117+
118+
GFXRECON_WRITE_CONSOLE("Error while writing file %s: Could not open", file_name.c_str());
119+
return false;
120+
}
121+
122+
bool Dx12ShaderTool::ExtractPipelineShaderToDir(
123+
const std::string& extract_dir, uint64_t handle_id, ShaderType type, const void* code, size_t code_size)
124+
{
125+
return ExtractShaderToDir(extract_dir, MakePipelineShaderFileName(handle_id, type), code, code_size);
126+
}
127+
128+
bool Dx12ShaderTool::ExtractStateObjectDxilLibraryToDir(const std::string& extract_dir,
129+
uint64_t state_object_handle_id,
130+
uint32_t subobject_index,
131+
const void* code,
132+
size_t code_size)
133+
{
134+
return ExtractShaderToDir(
135+
extract_dir, MakeStateObjectDxilLibraryFileName(state_object_handle_id, subobject_index), code, code_size);
136+
}
137+
138+
bool Dx12ShaderTool::LoadReplacementShaderFromDir(const std::string& replace_shader_dir,
139+
const std::string& file_name,
140+
std::unique_ptr<char[]>& out_code,
141+
size_t& out_size)
142+
{
143+
out_code.reset();
144+
out_size = 0;
145+
146+
if (replace_shader_dir.empty())
147+
{
148+
return false;
149+
}
150+
151+
std::string file_path = util::filepath::Join(replace_shader_dir, file_name);
152+
FILE* fp = nullptr;
153+
int32_t result = util::platform::FileOpen(&fp, file_path.c_str(), "rb");
154+
if ((result != 0) || (fp == nullptr))
155+
{
156+
return false;
157+
}
158+
159+
util::platform::FileSeek(fp, 0L, util::platform::FileSeekEnd);
160+
size_t file_size = static_cast<size_t>(util::platform::FileTell(fp));
161+
util::platform::FileSeek(fp, 0L, util::platform::FileSeekSet);
162+
if (file_size == 0)
163+
{
164+
util::platform::FileClose(fp);
165+
return false;
166+
}
167+
168+
auto buffer = std::make_unique<char[]>(file_size);
169+
util::platform::FileRead(buffer.get(), file_size, fp);
170+
util::platform::FileClose(fp);
171+
172+
out_code = std::move(buffer);
173+
out_size = file_size;
174+
return true;
175+
}
176+
177+
bool Dx12ShaderTool::LoadReplacementPipelineShaderFromDir(const std::string& replace_shader_dir,
178+
uint64_t handle_id,
179+
ShaderType type,
180+
std::unique_ptr<char[]>& out_code,
181+
size_t& out_size)
182+
{
183+
return LoadReplacementShaderFromDir(
184+
replace_shader_dir, MakePipelineShaderFileName(handle_id, type), out_code, out_size);
185+
}
186+
187+
bool Dx12ShaderTool::LoadReplacementStateObjectDxilLibraryFromDir(const std::string& replace_shader_dir,
188+
uint64_t handle_id,
189+
uint32_t subobject_index,
190+
std::unique_ptr<char[]>& out_code,
191+
size_t& out_size)
192+
{
193+
return LoadReplacementShaderFromDir(
194+
replace_shader_dir, MakeStateObjectDxilLibraryFileName(handle_id, subobject_index), out_code, out_size);
195+
}
196+
197+
GFXRECON_END_NAMESPACE(graphics)
198+
GFXRECON_END_NAMESPACE(gfxrecon)
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
/*
2+
** Copyright (c) 2025 LunarG, Inc.
3+
** Copyright (c) 2025-2026 Arm Limited and/or its affiliates <open-source-office@arm.com>
4+
**
5+
** Permission is hereby granted, free of charge, to any person obtaining a
6+
** copy of this software and associated documentation files (the "Software"),
7+
** to deal in the Software without restriction, including without limitation
8+
** the rights to use, copy, modify, merge, publish, distribute, sublicense,
9+
** and/or sell copies of the Software, and to permit persons to whom the
10+
** Software is furnished to do so, subject to the following conditions:
11+
**
12+
** The above copyright notice and this permission notice shall be included in
13+
** all copies or substantial portions of the Software.
14+
**
15+
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20+
** FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21+
** DEALINGS IN THE SOFTWARE.
22+
*/
23+
24+
#ifndef GFXRECON_GRAPHICS_SHADER_TOOL_H
25+
#define GFXRECON_GRAPHICS_SHADER_TOOL_H
26+
27+
#include "util/defines.h"
28+
#include <cstddef>
29+
#include <cstdint>
30+
#include <memory>
31+
#include <string>
32+
33+
GFXRECON_BEGIN_NAMESPACE(gfxrecon)
34+
GFXRECON_BEGIN_NAMESPACE(graphics)
35+
36+
class Dx12ShaderTool
37+
{
38+
public:
39+
enum class ShaderType
40+
{
41+
kVertex,
42+
kDomain,
43+
kHull,
44+
kGeometry,
45+
kPixel,
46+
kCompute,
47+
// StateObject subobject type D3D12_STATE_SUBOBJECT_TYPE_DXIL_LIBRARY
48+
kStateObjectDxilLibrary,
49+
kUnknown
50+
};
51+
52+
static const char* ShaderTypeToString(ShaderType type);
53+
54+
// File naming convention matches tools/extract and replay --replace-shaders logic.
55+
// Graphics/compute pipeline stage shaders:
56+
// sh<handle_id>.<stage>.cso
57+
// StateObject DXIL libraries:
58+
// sh<handle_id>_<subobject_index>.cso
59+
static std::string MakePipelineShaderFileName(uint64_t handle_id, ShaderType type);
60+
static std::string MakeStateObjectDxilLibraryFileName(uint64_t handle_id, uint32_t subobject_index);
61+
62+
static bool ExtractShaderToDir(const std::string& extract_dir,
63+
const std::string& file_name,
64+
const void* code,
65+
size_t code_size);
66+
67+
static bool ExtractPipelineShaderToDir(
68+
const std::string& extract_dir, uint64_t handle_id, ShaderType type, const void* code, size_t code_size);
69+
70+
static bool ExtractStateObjectDxilLibraryToDir(const std::string& extract_dir,
71+
uint64_t state_object_handle_id,
72+
uint32_t subobject_index,
73+
const void* code,
74+
size_t code_size);
75+
76+
// Replacement helpers (read shader bytecode from replace_shader_dir)
77+
static bool LoadReplacementShaderFromDir(const std::string& replace_shader_dir,
78+
const std::string& file_name,
79+
std::unique_ptr<char[]>& out_code,
80+
size_t& out_size);
81+
82+
static bool LoadReplacementPipelineShaderFromDir(const std::string& replace_shader_dir,
83+
uint64_t handle_id,
84+
ShaderType type,
85+
std::unique_ptr<char[]>& out_code,
86+
size_t& out_size);
87+
88+
static bool LoadReplacementStateObjectDxilLibraryFromDir(const std::string& replace_shader_dir,
89+
uint64_t handle_id,
90+
uint32_t subobject_index,
91+
std::unique_ptr<char[]>& out_code,
92+
size_t& out_size);
93+
};
94+
95+
GFXRECON_END_NAMESPACE(graphics)
96+
GFXRECON_END_NAMESPACE(gfxrecon)
97+
98+
#endif // GFXRECON_GRAPHICS_SHADER_TOOL_H

0 commit comments

Comments
 (0)