Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
stenzek
GitHub Repository: stenzek/duckstation
Path: blob/master/src/util/d3d12_pipeline.cpp
7373 views
1
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <[email protected]>
2
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
3
4
#include "d3d12_pipeline.h"
5
#include "d3d12_builders.h"
6
#include "d3d12_device.h"
7
#include "d3d_common.h"
8
9
#include "common/assert.h"
10
#include "common/bitutils.h"
11
#include "common/log.h"
12
#include "common/sha1_digest.h"
13
#include "common/string_util.h"
14
15
#include <d3dcompiler.h>
16
17
LOG_CHANNEL(GPUDevice);
18
19
D3D12Shader::D3D12Shader(GPUShaderStage stage, Bytecode bytecode) : GPUShader(stage), m_bytecode(std::move(bytecode))
20
{
21
}
22
23
D3D12Shader::~D3D12Shader() = default;
24
25
#ifdef ENABLE_GPU_OBJECT_NAMES
26
27
void D3D12Shader::SetDebugName(std::string_view name)
28
{
29
}
30
31
#endif
32
33
std::unique_ptr<GPUShader> D3D12Device::CreateShaderFromBinary(GPUShaderStage stage, std::span<const u8> data,
34
Error* error)
35
{
36
// Can't do much at this point.
37
std::vector bytecode(data.begin(), data.end());
38
return std::unique_ptr<GPUShader>(new D3D12Shader(stage, std::move(bytecode)));
39
}
40
41
std::unique_ptr<GPUShader> D3D12Device::CreateShaderFromSource(GPUShaderStage stage, GPUShaderLanguage language,
42
std::string_view source, const char* entry_point,
43
DynamicHeapArray<u8>* out_binary, Error* error)
44
{
45
const u32 shader_model = D3DCommon::GetShaderModelForFeatureLevelNumber(m_render_api_version);
46
if (language != GPUShaderLanguage::HLSL)
47
{
48
return TranspileAndCreateShaderFromSource(stage, language, source, entry_point, GPUShaderLanguage::HLSL,
49
shader_model, out_binary, error);
50
}
51
52
std::optional<DynamicHeapArray<u8>> bytecode =
53
D3DCommon::CompileShader(shader_model, m_debug_device, stage, source, entry_point, error);
54
if (!bytecode.has_value())
55
return {};
56
57
std::unique_ptr<GPUShader> ret = CreateShaderFromBinary(stage, bytecode.value(), error);
58
if (ret && out_binary)
59
*out_binary = std::move(bytecode.value());
60
61
return ret;
62
}
63
64
//////////////////////////////////////////////////////////////////////////
65
66
D3D12Pipeline::D3D12Pipeline(Microsoft::WRL::ComPtr<ID3D12PipelineState> pipeline, Layout layout,
67
D3D12_PRIMITIVE_TOPOLOGY topology, u32 vertex_stride, u32 blend_constants)
68
: GPUPipeline(), m_pipeline(std::move(pipeline)), m_layout(layout), m_topology(topology),
69
m_vertex_stride(vertex_stride), m_blend_constants(blend_constants),
70
m_blend_constants_f(GPUDevice::RGBA8ToFloat(blend_constants))
71
{
72
}
73
74
D3D12Pipeline::~D3D12Pipeline()
75
{
76
D3D12Device& dev = D3D12Device::GetInstance();
77
dev.UnbindPipeline(this);
78
dev.DeferObjectDestruction(std::move(m_pipeline));
79
}
80
81
#ifdef ENABLE_GPU_OBJECT_NAMES
82
83
void D3D12Pipeline::SetDebugName(std::string_view name)
84
{
85
D3D12::SetObjectName(m_pipeline.Get(), name);
86
}
87
88
#endif
89
90
std::string D3D12Pipeline::GetPipelineName(const GraphicsConfig& config)
91
{
92
SHA1Digest hash;
93
hash.Update(&config.layout, sizeof(config.layout));
94
hash.Update(&config.primitive, sizeof(config.primitive));
95
if (!config.input_layout.vertex_attributes.empty())
96
{
97
hash.Update(config.input_layout.vertex_attributes.data(),
98
sizeof(VertexAttribute) * static_cast<u32>(config.input_layout.vertex_attributes.size()));
99
hash.Update(&config.input_layout.vertex_stride, sizeof(config.input_layout.vertex_stride));
100
}
101
hash.Update(&config.rasterization.key, sizeof(config.rasterization.key));
102
hash.Update(&config.depth.key, sizeof(config.depth.key));
103
hash.Update(&config.blend.key, sizeof(config.blend.key));
104
if (const D3D12Shader* shader = static_cast<const D3D12Shader*>(config.vertex_shader))
105
hash.Update(shader->GetBytecodeData(), shader->GetBytecodeSize());
106
if (const D3D12Shader* shader = static_cast<const D3D12Shader*>(config.fragment_shader))
107
hash.Update(shader->GetBytecodeData(), shader->GetBytecodeSize());
108
if (const D3D12Shader* shader = static_cast<const D3D12Shader*>(config.geometry_shader))
109
hash.Update(shader->GetBytecodeData(), shader->GetBytecodeSize());
110
hash.Update(&config.color_formats, sizeof(config.color_formats));
111
hash.Update(&config.depth_format, sizeof(config.depth_format));
112
hash.Update(&config.render_pass_flags, sizeof(config.render_pass_flags));
113
114
u8 digest[SHA1Digest::DIGEST_SIZE];
115
hash.Final(digest);
116
return SHA1Digest::DigestToString(digest);
117
}
118
119
std::string D3D12Pipeline::GetPipelineName(const ComputeConfig& config)
120
{
121
SHA1Digest hash;
122
hash.Update(&config.layout, sizeof(config.layout));
123
if (const D3D12Shader* shader = static_cast<const D3D12Shader*>(config.compute_shader))
124
hash.Update(shader->GetBytecodeData(), shader->GetBytecodeSize());
125
126
u8 digest[SHA1Digest::DIGEST_SIZE];
127
hash.Final(digest);
128
return SHA1Digest::DigestToString(digest);
129
}
130
131
std::unique_ptr<GPUPipeline> D3D12Device::CreatePipeline(const GPUPipeline::GraphicsConfig& config, Error* error)
132
{
133
static constexpr std::array<D3D12_PRIMITIVE_TOPOLOGY, static_cast<u32>(GPUPipeline::Primitive::MaxCount)> primitives =
134
{{
135
D3D_PRIMITIVE_TOPOLOGY_POINTLIST, // Points
136
D3D_PRIMITIVE_TOPOLOGY_LINELIST, // Lines
137
D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST, // Triangles
138
D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, // TriangleStrips
139
}};
140
static constexpr std::array<D3D12_PRIMITIVE_TOPOLOGY_TYPE, static_cast<u32>(GPUPipeline::Primitive::MaxCount)>
141
primitive_types = {{
142
D3D12_PRIMITIVE_TOPOLOGY_TYPE_POINT, // Points
143
D3D12_PRIMITIVE_TOPOLOGY_TYPE_LINE, // Lines
144
D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE, // Triangles
145
D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE, // TriangleStrips
146
}};
147
148
static constexpr u32 MAX_COMPONENTS = 4;
149
static constexpr const DXGI_FORMAT
150
format_mapping[static_cast<u8>(GPUPipeline::VertexAttribute::Type::MaxCount)][MAX_COMPONENTS] = {
151
{DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_R32G32_FLOAT, DXGI_FORMAT_R32G32B32_FLOAT,
152
DXGI_FORMAT_R32G32B32A32_FLOAT}, // Float
153
{DXGI_FORMAT_R8_UINT, DXGI_FORMAT_R8G8_UINT, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R8G8B8A8_UINT}, // UInt8
154
{DXGI_FORMAT_R8_SINT, DXGI_FORMAT_R8G8_SINT, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R8G8B8A8_SINT}, // SInt8
155
{DXGI_FORMAT_R8_UNORM, DXGI_FORMAT_R8G8_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R8G8B8A8_UNORM}, // UNorm8
156
{DXGI_FORMAT_R16_UINT, DXGI_FORMAT_R16G16_UINT, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R16G16B16A16_UINT}, // UInt16
157
{DXGI_FORMAT_R16_SINT, DXGI_FORMAT_R16G16_SINT, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R16G16B16A16_SINT}, // SInt16
158
{DXGI_FORMAT_R16_UNORM, DXGI_FORMAT_R16G16_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R16G16B16A16_UNORM}, // UNorm16
159
{DXGI_FORMAT_R32_UINT, DXGI_FORMAT_R32G32_UINT, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R32G32B32A32_UINT}, // UInt32
160
{DXGI_FORMAT_R32_SINT, DXGI_FORMAT_R32G32_SINT, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R32G32B32A32_SINT}, // SInt32
161
};
162
163
static constexpr std::array<D3D12_CULL_MODE, static_cast<u32>(GPUPipeline::CullMode::MaxCount)> cull_mapping = {{
164
D3D12_CULL_MODE_NONE, // None
165
D3D12_CULL_MODE_FRONT, // Front
166
D3D12_CULL_MODE_BACK, // Back
167
}};
168
169
static constexpr std::array<D3D12_COMPARISON_FUNC, static_cast<u32>(GPUPipeline::DepthFunc::MaxCount)>
170
compare_mapping = {{
171
D3D12_COMPARISON_FUNC_NEVER, // Never
172
D3D12_COMPARISON_FUNC_ALWAYS, // Always
173
D3D12_COMPARISON_FUNC_LESS, // Less
174
D3D12_COMPARISON_FUNC_LESS_EQUAL, // LessEqual
175
D3D12_COMPARISON_FUNC_GREATER, // Greater
176
D3D12_COMPARISON_FUNC_GREATER_EQUAL, // GreaterEqual
177
D3D12_COMPARISON_FUNC_EQUAL, // Equal
178
}};
179
180
static constexpr std::array<D3D12_BLEND, static_cast<u32>(GPUPipeline::BlendFunc::MaxCount)> blend_mapping = {{
181
D3D12_BLEND_ZERO, // Zero
182
D3D12_BLEND_ONE, // One
183
D3D12_BLEND_SRC_COLOR, // SrcColor
184
D3D12_BLEND_INV_SRC_COLOR, // InvSrcColor
185
D3D12_BLEND_DEST_COLOR, // DstColor
186
D3D12_BLEND_INV_DEST_COLOR, // InvDstColor
187
D3D12_BLEND_SRC_ALPHA, // SrcAlpha
188
D3D12_BLEND_INV_SRC_ALPHA, // InvSrcAlpha
189
D3D12_BLEND_SRC1_ALPHA, // SrcAlpha1
190
D3D12_BLEND_INV_SRC1_ALPHA, // InvSrcAlpha1
191
D3D12_BLEND_DEST_ALPHA, // DstAlpha
192
D3D12_BLEND_INV_DEST_ALPHA, // InvDstAlpha
193
D3D12_BLEND_BLEND_FACTOR, // ConstantColor
194
D3D12_BLEND_INV_BLEND_FACTOR, // InvConstantColor
195
}};
196
197
static constexpr std::array<D3D12_BLEND_OP, static_cast<u32>(GPUPipeline::BlendOp::MaxCount)> op_mapping = {{
198
D3D12_BLEND_OP_ADD, // Add
199
D3D12_BLEND_OP_SUBTRACT, // Subtract
200
D3D12_BLEND_OP_REV_SUBTRACT, // ReverseSubtract
201
D3D12_BLEND_OP_MIN, // Min
202
D3D12_BLEND_OP_MAX, // Max
203
}};
204
205
if (config.render_pass_flags & GPUPipeline::BindRenderTargetsAsImages && !m_features.raster_order_views)
206
{
207
ERROR_LOG("Attempting to create ROV pipeline without ROV feature.");
208
return {};
209
}
210
211
D3D12::GraphicsPipelineBuilder gpb;
212
gpb.SetRootSignature(m_root_signatures[BoolToUInt8(
213
(config.render_pass_flags & GPUPipeline::BindRenderTargetsAsImages))][static_cast<u8>(config.layout)]
214
.Get());
215
gpb.SetVertexShader(static_cast<const D3D12Shader*>(config.vertex_shader)->GetBytecodeData(),
216
static_cast<const D3D12Shader*>(config.vertex_shader)->GetBytecodeSize());
217
gpb.SetPixelShader(static_cast<const D3D12Shader*>(config.fragment_shader)->GetBytecodeData(),
218
static_cast<const D3D12Shader*>(config.fragment_shader)->GetBytecodeSize());
219
if (config.geometry_shader)
220
{
221
gpb.SetGeometryShader(static_cast<const D3D12Shader*>(config.geometry_shader)->GetBytecodeData(),
222
static_cast<const D3D12Shader*>(config.geometry_shader)->GetBytecodeSize());
223
}
224
gpb.SetPrimitiveTopologyType(primitive_types[static_cast<u8>(config.primitive)]);
225
226
if (!config.input_layout.vertex_attributes.empty())
227
{
228
for (u32 i = 0; i < static_cast<u32>(config.input_layout.vertex_attributes.size()); i++)
229
{
230
const GPUPipeline::VertexAttribute& va = config.input_layout.vertex_attributes[i];
231
DebugAssert(va.components > 0 && va.components <= MAX_COMPONENTS);
232
gpb.AddVertexAttribute(
233
"ATTR", i, format_mapping[static_cast<u8>(va.type.GetValue())][static_cast<u8>(va.components.GetValue() - 1)],
234
0, va.offset);
235
}
236
}
237
238
gpb.SetRasterizationState(D3D12_FILL_MODE_SOLID,
239
cull_mapping[static_cast<u8>(config.rasterization.cull_mode.GetValue())], false);
240
if (config.rasterization.multisamples > 1)
241
gpb.SetMultisamples(config.rasterization.multisamples);
242
gpb.SetDepthState(config.depth.depth_test != GPUPipeline::DepthFunc::Always || config.depth.depth_write,
243
config.depth.depth_write, compare_mapping[static_cast<u8>(config.depth.depth_test.GetValue())]);
244
gpb.SetNoStencilState();
245
246
gpb.SetBlendState(0, config.blend.enable, blend_mapping[static_cast<u8>(config.blend.src_blend.GetValue())],
247
blend_mapping[static_cast<u8>(config.blend.dst_blend.GetValue())],
248
op_mapping[static_cast<u8>(config.blend.blend_op.GetValue())],
249
blend_mapping[static_cast<u8>(config.blend.src_alpha_blend.GetValue())],
250
blend_mapping[static_cast<u8>(config.blend.dst_alpha_blend.GetValue())],
251
op_mapping[static_cast<u8>(config.blend.alpha_blend_op.GetValue())], config.blend.write_mask);
252
253
for (u32 i = 0; i < MAX_RENDER_TARGETS; i++)
254
{
255
if (config.color_formats[i] != GPUTextureFormat::Unknown)
256
gpb.SetRenderTarget(i, D3DCommon::GetFormatMapping(config.color_formats[i]).rtv_format);
257
}
258
259
if (config.depth_format != GPUTextureFormat::Unknown)
260
gpb.SetDepthStencilFormat(D3DCommon::GetFormatMapping(config.depth_format).dsv_format);
261
262
ComPtr<ID3D12PipelineState> pipeline;
263
if (m_pipeline_library)
264
{
265
const std::wstring name = StringUtil::UTF8StringToWideString(D3D12Pipeline::GetPipelineName(config));
266
HRESULT hr =
267
m_pipeline_library->LoadGraphicsPipeline(name.c_str(), gpb.GetDesc(), IID_PPV_ARGS(pipeline.GetAddressOf()));
268
if (FAILED(hr))
269
{
270
// E_INVALIDARG = not found.
271
if (hr != E_INVALIDARG)
272
ERROR_LOG("LoadGraphicsPipeline() failed with HRESULT {:08X}", static_cast<unsigned>(hr));
273
274
// Need to create it normally.
275
pipeline = gpb.Create(m_device.Get(), error, false);
276
277
// Store if it wasn't an OOM or something else.
278
if (pipeline && hr == E_INVALIDARG)
279
{
280
hr = m_pipeline_library->StorePipeline(name.c_str(), pipeline.Get());
281
if (FAILED(hr))
282
ERROR_LOG("StorePipeline() failed with HRESULT {:08X}", static_cast<unsigned>(hr));
283
}
284
}
285
}
286
else
287
{
288
pipeline = gpb.Create(m_device.Get(), error, false);
289
}
290
291
if (!pipeline)
292
return {};
293
294
return std::unique_ptr<GPUPipeline>(new D3D12Pipeline(
295
pipeline, config.layout, primitives[static_cast<u8>(config.primitive)],
296
config.input_layout.vertex_attributes.empty() ? 0 : config.input_layout.vertex_stride, config.blend.constant));
297
}
298
299
std::unique_ptr<GPUPipeline> D3D12Device::CreatePipeline(const GPUPipeline::ComputeConfig& config, Error* error)
300
{
301
D3D12::ComputePipelineBuilder cpb;
302
cpb.SetRootSignature(m_root_signatures[0][static_cast<u8>(config.layout)].Get());
303
cpb.SetShader(static_cast<const D3D12Shader*>(config.compute_shader)->GetBytecodeData(),
304
static_cast<const D3D12Shader*>(config.compute_shader)->GetBytecodeSize());
305
306
ComPtr<ID3D12PipelineState> pipeline;
307
if (m_pipeline_library)
308
{
309
const std::wstring name = StringUtil::UTF8StringToWideString(D3D12Pipeline::GetPipelineName(config));
310
HRESULT hr =
311
m_pipeline_library->LoadComputePipeline(name.c_str(), cpb.GetDesc(), IID_PPV_ARGS(pipeline.GetAddressOf()));
312
if (FAILED(hr))
313
{
314
// E_INVALIDARG = not found.
315
if (hr != E_INVALIDARG)
316
ERROR_LOG("LoadComputePipeline() failed with HRESULT {:08X}", static_cast<unsigned>(hr));
317
318
// Need to create it normally.
319
pipeline = cpb.Create(m_device.Get(), error, false);
320
321
// Store if it wasn't an OOM or something else.
322
if (pipeline && hr == E_INVALIDARG)
323
{
324
hr = m_pipeline_library->StorePipeline(name.c_str(), pipeline.Get());
325
if (FAILED(hr))
326
ERROR_LOG("StorePipeline() failed with HRESULT {:08X}", static_cast<unsigned>(hr));
327
}
328
}
329
}
330
else
331
{
332
pipeline = cpb.Create(m_device.Get(), error, false);
333
}
334
335
if (!pipeline)
336
return {};
337
338
return std::unique_ptr<GPUPipeline>(
339
new D3D12Pipeline(pipeline, config.layout, D3D_PRIMITIVE_TOPOLOGY_UNDEFINED, 0, 0));
340
}
341
342