Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
stenzek
GitHub Repository: stenzek/duckstation
Path: blob/master/src/util/d3d_common.cpp
7407 views
1
// SPDX-FileCopyrightText: 2019-2026 Connor McLaughlin <[email protected]>
2
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
3
4
#include "d3d_common.h"
5
#include "gpu_device.h"
6
7
#include "common/assert.h"
8
#include "common/dynamic_library.h"
9
#include "common/error.h"
10
#include "common/file_system.h"
11
#include "common/gsvector.h"
12
#include "common/log.h"
13
#include "common/string_util.h"
14
15
#include "fmt/format.h"
16
17
#include <algorithm>
18
#include <d3d11.h>
19
#include <d3d12.h>
20
#include <d3dcompiler.h>
21
#include <dxcapi.h>
22
#include <dxgi1_5.h>
23
#include <mutex>
24
25
LOG_CHANNEL(GPUDevice);
26
27
namespace D3DCommon {
28
namespace {
29
struct FeatureLevelTableEntry
30
{
31
D3D_FEATURE_LEVEL d3d_feature_level;
32
u16 render_api_version;
33
u16 shader_model_number;
34
const char* feature_level_str;
35
};
36
37
struct Libs
38
{
39
std::mutex load_mutex;
40
DynamicLibrary dxgi_library;
41
decltype(&CreateDXGIFactory2) CreateDXGIFactory2;
42
DynamicLibrary d3d11_library;
43
PFN_D3D11_CREATE_DEVICE D3D11CreateDevice;
44
DynamicLibrary d3d12_library;
45
PFN_D3D12_CREATE_DEVICE D3D12CreateDevice;
46
PFN_D3D12_GET_DEBUG_INTERFACE D3D12GetDebugInterface;
47
PFN_D3D12_SERIALIZE_ROOT_SIGNATURE D3D12SerializeRootSignature;
48
DynamicLibrary d3dcompiler_library;
49
pD3DCompile D3DCompile;
50
DynamicLibrary dxcompiler_library;
51
DxcCreateInstanceProc DxcCreateInstance;
52
};
53
54
} // namespace
55
56
static std::optional<DynamicHeapArray<u8>> CompileShaderWithFXC(u32 shader_model, bool debug_device,
57
GPUShaderStage stage, std::string_view source,
58
const char* entry_point, Error* error);
59
static std::optional<DynamicHeapArray<u8>> CompileShaderWithDXC(u32 shader_model, bool debug_device,
60
GPUShaderStage stage, std::string_view source,
61
const char* entry_point, Error* error);
62
static bool LoadD3D12Library(Error* error);
63
static bool LoadD3DCompilerLibrary(Error* error);
64
static bool LoadDXCompilerLibrary(Error* error);
65
66
static constexpr std::array<FeatureLevelTableEntry, 11> s_feature_levels = {{
67
{D3D_FEATURE_LEVEL_1_0_CORE, 100, 40, "D3D_FEATURE_LEVEL_1_0_CORE"},
68
{D3D_FEATURE_LEVEL_9_1, 910, 40, "D3D_FEATURE_LEVEL_9_1"},
69
{D3D_FEATURE_LEVEL_9_2, 920, 40, "D3D_FEATURE_LEVEL_9_2"},
70
{D3D_FEATURE_LEVEL_9_3, 930, 40, "D3D_FEATURE_LEVEL_9_3"},
71
{D3D_FEATURE_LEVEL_10_0, 1000, 40, "D3D_FEATURE_LEVEL_10_0"},
72
{D3D_FEATURE_LEVEL_10_1, 1010, 41, "D3D_FEATURE_LEVEL_10_1"},
73
{D3D_FEATURE_LEVEL_11_0, 1100, 50, "D3D_FEATURE_LEVEL_11_0"},
74
{D3D_FEATURE_LEVEL_11_1, 1110, 50, "D3D_FEATURE_LEVEL_11_1"},
75
{D3D_FEATURE_LEVEL_12_0, 1200, 60, "D3D_FEATURE_LEVEL_12_0"},
76
{D3D_FEATURE_LEVEL_12_1, 1210, 60, "D3D_FEATURE_LEVEL_12_1"},
77
{D3D_FEATURE_LEVEL_12_2, 1220, 60, "D3D_FEATURE_LEVEL_12_2"},
78
}};
79
80
static Libs s_libs;
81
} // namespace D3DCommon
82
83
const char* D3DCommon::GetFeatureLevelString(u32 render_api_version)
84
{
85
const auto iter =
86
std::find_if(s_feature_levels.begin(), s_feature_levels.end(),
87
[&render_api_version](const auto& it) { return it.render_api_version == render_api_version; });
88
return (iter != s_feature_levels.end()) ? iter->feature_level_str : "D3D_FEATURE_LEVEL_UNKNOWN";
89
}
90
91
u32 D3DCommon::GetRenderAPIVersionForFeatureLevel(D3D_FEATURE_LEVEL feature_level)
92
{
93
const FeatureLevelTableEntry* highest_entry = nullptr;
94
for (const FeatureLevelTableEntry& entry : s_feature_levels)
95
{
96
if (feature_level >= entry.d3d_feature_level)
97
highest_entry = &entry;
98
}
99
return highest_entry ? highest_entry->render_api_version : 0;
100
}
101
102
D3D_FEATURE_LEVEL D3DCommon::GetFeatureLevelForNumber(u32 render_api_version)
103
{
104
const auto iter =
105
std::find_if(s_feature_levels.begin(), s_feature_levels.end(),
106
[&render_api_version](const auto& it) { return it.render_api_version == render_api_version; });
107
return (iter != s_feature_levels.end()) ? iter->d3d_feature_level : D3D_FEATURE_LEVEL_1_0_CORE;
108
}
109
110
u32 D3DCommon::GetShaderModelForFeatureLevelNumber(u32 render_api_version)
111
{
112
const auto iter =
113
std::find_if(s_feature_levels.begin(), s_feature_levels.end(),
114
[&render_api_version](const auto& it) { return it.render_api_version == render_api_version; });
115
return (iter != s_feature_levels.end()) ? iter->shader_model_number : 40;
116
}
117
118
D3D_FEATURE_LEVEL D3DCommon::GetDeviceMaxFeatureLevel(IDXGIAdapter1* adapter)
119
{
120
static constexpr std::array requested_feature_levels = {
121
D3D_FEATURE_LEVEL_12_2, D3D_FEATURE_LEVEL_12_1, D3D_FEATURE_LEVEL_12_0, D3D_FEATURE_LEVEL_11_1,
122
D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0};
123
124
D3D_FEATURE_LEVEL max_supported_level;
125
Error error;
126
if (!CreateD3D11Device(adapter, 0, requested_feature_levels.data(),
127
static_cast<UINT>(requested_feature_levels.size()), nullptr, &max_supported_level, nullptr,
128
&error))
129
{
130
WARNING_LOG("D3D11CreateDevice() for getting max feature level failed: {}", error.GetDescription());
131
max_supported_level = requested_feature_levels.back();
132
}
133
134
return max_supported_level;
135
}
136
137
Microsoft::WRL::ComPtr<IDXGIFactory5> D3DCommon::CreateFactory(bool debug, Error* error)
138
{
139
if (!s_libs.dxgi_library.IsOpen())
140
{
141
// another thread may have opened it
142
const std::unique_lock lock(s_libs.load_mutex);
143
if (!s_libs.d3d11_library.IsOpen())
144
{
145
if (!s_libs.dxgi_library.Open("dxgi.dll", error))
146
return {};
147
148
if (!s_libs.dxgi_library.GetSymbol("CreateDXGIFactory2", &s_libs.CreateDXGIFactory2))
149
{
150
Error::SetStringView(error, "Failed to load CreateDXGIFactory2 from dxgi.dll");
151
s_libs.dxgi_library.Close();
152
return {};
153
}
154
}
155
}
156
157
UINT flags = 0;
158
if (debug)
159
flags |= DXGI_CREATE_FACTORY_DEBUG;
160
161
Microsoft::WRL::ComPtr<IDXGIFactory5> factory;
162
const HRESULT hr = s_libs.CreateDXGIFactory2(flags, IID_PPV_ARGS(factory.GetAddressOf()));
163
if (FAILED(hr))
164
Error::SetHResult(error, "Failed to create DXGI factory: ", hr);
165
166
return factory;
167
}
168
169
bool D3DCommon::SupportsAllowTearing(IDXGIFactory5* factory)
170
{
171
BOOL allow_tearing_supported = false;
172
HRESULT hr = factory->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &allow_tearing_supported,
173
sizeof(allow_tearing_supported));
174
return (SUCCEEDED(hr) && allow_tearing_supported == TRUE);
175
}
176
177
bool D3DCommon::CreateD3D11Device(IDXGIAdapter* adapter, UINT create_flags, const D3D_FEATURE_LEVEL* feature_levels,
178
UINT num_feature_levels, Microsoft::WRL::ComPtr<ID3D11Device>* device,
179
D3D_FEATURE_LEVEL* out_feature_level,
180
Microsoft::WRL::ComPtr<ID3D11DeviceContext>* immediate_context, Error* error)
181
{
182
if (!s_libs.d3d11_library.IsOpen())
183
{
184
// another thread may have opened it
185
const std::unique_lock lock(s_libs.load_mutex);
186
if (!s_libs.d3d11_library.IsOpen())
187
{
188
if (!s_libs.d3d11_library.Open("d3d11.dll", error))
189
return false;
190
191
if (!s_libs.d3d11_library.GetSymbol("D3D11CreateDevice", &s_libs.D3D11CreateDevice))
192
{
193
Error::SetStringView(error, "Failed to load D3D11CreateDevice from d3d11.dll");
194
s_libs.d3dcompiler_library.Close();
195
return false;
196
}
197
}
198
}
199
200
const HRESULT hr = s_libs.D3D11CreateDevice(
201
adapter, adapter ? D3D_DRIVER_TYPE_UNKNOWN : D3D_DRIVER_TYPE_HARDWARE, nullptr, create_flags, feature_levels,
202
num_feature_levels, D3D11_SDK_VERSION, device ? device->ReleaseAndGetAddressOf() : nullptr, out_feature_level,
203
immediate_context ? immediate_context->ReleaseAndGetAddressOf() : nullptr);
204
if (SUCCEEDED(hr))
205
return true;
206
207
Error::SetHResult(error, "D3D11CreateDevice() failed: ", hr);
208
return true;
209
}
210
211
bool D3DCommon::LoadD3D12Library(Error* error)
212
{
213
if (s_libs.d3d12_library.IsOpen())
214
return true;
215
216
// double check, another thread may have opened it
217
const std::unique_lock lock(s_libs.load_mutex);
218
if (s_libs.d3d12_library.IsOpen())
219
return true;
220
221
if (!s_libs.d3d12_library.Open("d3d12.dll", error))
222
return false;
223
224
if (!s_libs.d3d12_library.GetSymbol("D3D12CreateDevice", &s_libs.D3D12CreateDevice) ||
225
!s_libs.d3d12_library.GetSymbol("D3D12GetDebugInterface", &s_libs.D3D12GetDebugInterface) ||
226
!s_libs.d3d12_library.GetSymbol("D3D12SerializeRootSignature", &s_libs.D3D12SerializeRootSignature))
227
{
228
Error::SetStringView(error, "Failed to load one or more required functions from d3d12.dll");
229
s_libs.d3d12_library.Close();
230
return false;
231
}
232
233
return true;
234
}
235
236
bool D3DCommon::GetD3D12DebugInterface(Microsoft::WRL::ComPtr<ID3D12Debug>* debug, Error* error)
237
{
238
if (!LoadD3D12Library(error))
239
return false;
240
241
const HRESULT hr = s_libs.D3D12GetDebugInterface(IID_PPV_ARGS(debug->ReleaseAndGetAddressOf()));
242
if (FAILED(hr))
243
{
244
Error::SetHResult(error, "D3D12GetDebugInterface() failed: ", hr);
245
return false;
246
}
247
248
return true;
249
}
250
251
bool D3DCommon::CreateD3D12Device(IDXGIAdapter* adapter, D3D_FEATURE_LEVEL feature_level,
252
Microsoft::WRL::ComPtr<ID3D12Device1>* device, Error* error)
253
{
254
if (!LoadD3D12Library(error))
255
return false;
256
257
const HRESULT hr = s_libs.D3D12CreateDevice(adapter, feature_level, IID_PPV_ARGS(device->ReleaseAndGetAddressOf()));
258
if (FAILED(hr))
259
{
260
Error::SetHResult(error, "D3D12CreateDevice() failed: ", hr);
261
return false;
262
}
263
264
return true;
265
}
266
267
Microsoft::WRL::ComPtr<ID3DBlob> D3DCommon::SerializeRootSignature(const D3D12_ROOT_SIGNATURE_DESC* desc, Error* error)
268
{
269
Microsoft::WRL::ComPtr<ID3DBlob> blob;
270
Microsoft::WRL::ComPtr<ID3DBlob> error_blob;
271
const HRESULT hr = s_libs.D3D12SerializeRootSignature(desc, D3D_ROOT_SIGNATURE_VERSION_1, blob.GetAddressOf(),
272
error_blob.GetAddressOf());
273
if (FAILED(hr)) [[unlikely]]
274
{
275
Error::SetHResult(error, "D3D12SerializeRootSignature() failed: ", hr);
276
if (error_blob)
277
ERROR_LOG(static_cast<const char*>(error_blob->GetBufferPointer()));
278
279
return {};
280
}
281
282
return blob;
283
}
284
285
static std::string FixupDuplicateAdapterNames(const GPUDevice::AdapterInfoList& adapter_names, std::string adapter_name)
286
{
287
if (std::any_of(adapter_names.begin(), adapter_names.end(),
288
[&adapter_name](const GPUDevice::AdapterInfo& other) { return (adapter_name == other.name); }))
289
{
290
std::string original_adapter_name = std::move(adapter_name);
291
292
u32 current_extra = 2;
293
do
294
{
295
adapter_name = fmt::format("{} ({})", original_adapter_name.c_str(), current_extra);
296
current_extra++;
297
} while (
298
std::any_of(adapter_names.begin(), adapter_names.end(),
299
[&adapter_name](const GPUDevice::AdapterInfo& other) { return (adapter_name == other.name); }));
300
}
301
302
return adapter_name;
303
}
304
305
std::optional<GPUDevice::AdapterInfoList> D3DCommon::GetAdapterInfoList(Error* error)
306
{
307
std::optional<GPUDevice::AdapterInfoList> ret;
308
309
Microsoft::WRL::ComPtr<IDXGIFactory5> factory = CreateFactory(false, error);
310
if (!factory)
311
return ret;
312
313
Microsoft::WRL::ComPtr<IDXGIAdapter1> adapter;
314
for (u32 index = 0;; index++)
315
{
316
HRESULT hr = factory->EnumAdapters1(index, adapter.ReleaseAndGetAddressOf());
317
if (hr == DXGI_ERROR_NOT_FOUND)
318
break;
319
320
if (FAILED(hr))
321
{
322
ERROR_LOG("IDXGIFactory2::EnumAdapters() returned {:08X}", static_cast<unsigned>(hr));
323
continue;
324
}
325
326
if (!ret.has_value())
327
ret.emplace();
328
329
// Unfortunately we can't get any properties such as feature level without creating the device.
330
// So just assume a max of the D3D11 max across the board.
331
GPUDevice::AdapterInfo ai;
332
ai.name = FixupDuplicateAdapterNames(ret.value(), GetAdapterName(adapter.Get(), &ai.driver_type));
333
ai.max_texture_size = D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION;
334
ai.max_multisamples = 8;
335
ai.supports_sample_shading = true;
336
337
Microsoft::WRL::ComPtr<IDXGIOutput> output;
338
if (SUCCEEDED(hr = adapter->EnumOutputs(0, output.ReleaseAndGetAddressOf())))
339
{
340
UINT num_modes = 0;
341
if (SUCCEEDED(hr = output->GetDisplayModeList(DXGI_FORMAT_R8G8B8A8_UNORM, 0, &num_modes, nullptr)))
342
{
343
std::vector<DXGI_MODE_DESC> dmodes(num_modes);
344
if (SUCCEEDED(hr = output->GetDisplayModeList(DXGI_FORMAT_R8G8B8A8_UNORM, 0, &num_modes, dmodes.data())))
345
{
346
for (const DXGI_MODE_DESC& mode : dmodes)
347
{
348
const GPUDevice::ExclusiveFullscreenMode efm{.width = mode.Width,
349
.height = mode.Height,
350
.refresh_rate =
351
static_cast<float>(mode.RefreshRate.Numerator) /
352
static_cast<float>(mode.RefreshRate.Denominator)};
353
if (std::ranges::find(ai.fullscreen_modes, efm) == ai.fullscreen_modes.end())
354
ai.fullscreen_modes.push_back(efm);
355
}
356
357
std::sort(ai.fullscreen_modes.begin(), ai.fullscreen_modes.end());
358
}
359
else
360
{
361
ERROR_LOG("GetDisplayModeList() (2) failed: {:08X}", static_cast<unsigned>(hr));
362
}
363
}
364
else
365
{
366
ERROR_LOG("GetDisplayModeList() failed: {:08X}", static_cast<unsigned>(hr));
367
}
368
}
369
else
370
{
371
// Adapter may not have any outputs, don't spam the error log in this case.
372
if (hr != DXGI_ERROR_NOT_FOUND)
373
ERROR_LOG("EnumOutputs() failed: {:08X}", static_cast<unsigned>(hr));
374
}
375
376
ret->push_back(std::move(ai));
377
}
378
379
if (!ret.has_value())
380
{
381
Error::SetStringView(error, "No DXGI adapters found.");
382
return ret;
383
}
384
385
return ret;
386
}
387
388
std::optional<DXGI_MODE_DESC>
389
D3DCommon::GetRequestedExclusiveFullscreenModeDesc(IDXGIAdapter* adapter, const RECT& window_rect,
390
const GPUDevice::ExclusiveFullscreenMode* requested_fullscreen_mode,
391
DXGI_FORMAT format, IDXGIOutput** output)
392
{
393
std::optional<DXGI_MODE_DESC> ret;
394
395
// We need to find which monitor the window is located on.
396
// The adapter must match, you cannot restrict the output to a monitor that is not connected to the device.
397
const GSVector4i client_rc_vec(window_rect.left, window_rect.top, window_rect.right, window_rect.bottom);
398
399
// The window might be on a different adapter to which we are rendering.. so we have to enumerate them all.
400
HRESULT hr;
401
Microsoft::WRL::ComPtr<IDXGIOutput> first_output, intersecting_output;
402
for (u32 output_index = 0;; output_index++)
403
{
404
Microsoft::WRL::ComPtr<IDXGIOutput> this_output;
405
DXGI_OUTPUT_DESC output_desc;
406
hr = adapter->EnumOutputs(output_index, this_output.GetAddressOf());
407
if (hr == DXGI_ERROR_NOT_FOUND)
408
break;
409
else if (FAILED(hr) || FAILED(this_output->GetDesc(&output_desc)))
410
continue;
411
412
const GSVector4i output_rc(output_desc.DesktopCoordinates.left, output_desc.DesktopCoordinates.top,
413
output_desc.DesktopCoordinates.right, output_desc.DesktopCoordinates.bottom);
414
if (!client_rc_vec.rintersects(output_rc))
415
{
416
intersecting_output = std::move(this_output);
417
break;
418
}
419
420
// Fallback to the first monitor.
421
if (!first_output)
422
first_output = std::move(this_output);
423
}
424
425
if (!intersecting_output)
426
{
427
if (!first_output)
428
{
429
ERROR_LOG("No DXGI output found. Can't use exclusive fullscreen.");
430
return ret;
431
}
432
433
WARNING_LOG("No DXGI output found for window, using first.");
434
intersecting_output = std::move(first_output);
435
}
436
437
DXGI_MODE_DESC request_mode = {};
438
request_mode.Width = requested_fullscreen_mode->width;
439
request_mode.Height = requested_fullscreen_mode->height;
440
request_mode.Format = format;
441
request_mode.RefreshRate.Numerator = static_cast<UINT>(std::floor(requested_fullscreen_mode->refresh_rate * 1000.0f));
442
request_mode.RefreshRate.Denominator = 1000u;
443
444
ret = DXGI_MODE_DESC();
445
446
if (FAILED(hr = intersecting_output->FindClosestMatchingMode(&request_mode, &ret.value(), nullptr)) ||
447
request_mode.Format != format)
448
{
449
ERROR_LOG("Failed to find closest matching mode, hr={:08X}", static_cast<unsigned>(hr));
450
ret.reset();
451
return ret;
452
}
453
454
*output = intersecting_output.Get();
455
intersecting_output->AddRef();
456
return ret;
457
}
458
459
Microsoft::WRL::ComPtr<IDXGIAdapter1> D3DCommon::GetAdapterByName(IDXGIFactory5* factory, std::string_view name)
460
{
461
if (name.empty())
462
return {};
463
464
// This might seem a bit odd to cache the names.. but there's a method to the madness.
465
// We might have two GPUs with the same name... :)
466
GPUDevice::AdapterInfoList adapters;
467
468
Microsoft::WRL::ComPtr<IDXGIAdapter1> adapter;
469
for (u32 index = 0;; index++)
470
{
471
const HRESULT hr = factory->EnumAdapters1(index, adapter.ReleaseAndGetAddressOf());
472
if (hr == DXGI_ERROR_NOT_FOUND)
473
break;
474
475
if (FAILED(hr))
476
{
477
ERROR_LOG("IDXGIFactory2::EnumAdapters() returned {:08X}", static_cast<unsigned>(hr));
478
continue;
479
}
480
481
std::string adapter_name = FixupDuplicateAdapterNames(adapters, GetAdapterName(adapter.Get()));
482
if (adapter_name == name)
483
{
484
VERBOSE_LOG("Found adapter '{}'", adapter_name);
485
return adapter;
486
}
487
488
GPUDevice::AdapterInfo ai;
489
ai.name = std::move(adapter_name);
490
adapters.push_back(std::move(ai));
491
}
492
493
ERROR_LOG("Adapter '{}' not found.", name);
494
return {};
495
}
496
497
Microsoft::WRL::ComPtr<IDXGIAdapter1> D3DCommon::GetFirstAdapter(IDXGIFactory5* factory)
498
{
499
Microsoft::WRL::ComPtr<IDXGIAdapter1> adapter;
500
HRESULT hr = factory->EnumAdapters1(0, adapter.GetAddressOf());
501
if (FAILED(hr))
502
ERROR_LOG("IDXGIFactory2::EnumAdapters() for first adapter returned {:08X}", static_cast<unsigned>(hr));
503
504
return adapter;
505
}
506
507
Microsoft::WRL::ComPtr<IDXGIAdapter1> D3DCommon::GetChosenOrFirstAdapter(IDXGIFactory5* factory, std::string_view name)
508
{
509
Microsoft::WRL::ComPtr<IDXGIAdapter1> adapter = GetAdapterByName(factory, name);
510
if (!adapter)
511
adapter = GetFirstAdapter(factory);
512
513
return adapter;
514
}
515
516
std::string D3DCommon::GetAdapterName(IDXGIAdapter1* adapter, GPUDriverType* out_driver_type)
517
{
518
std::string ret;
519
520
DXGI_ADAPTER_DESC1 desc;
521
HRESULT hr = adapter->GetDesc1(&desc);
522
if (SUCCEEDED(hr))
523
{
524
ret = StringUtil::WideStringToUTF8String(desc.Description);
525
if (out_driver_type)
526
{
527
// Handle WARP here.
528
if (desc.VendorId == 0x1414)
529
*out_driver_type = GPUDriverType::WARP;
530
else
531
*out_driver_type = GPUDevice::GuessDriverType(desc.VendorId, {}, ret);
532
}
533
}
534
else
535
{
536
ERROR_LOG("IDXGIAdapter1::GetDesc() returned {:08X}", static_cast<unsigned>(hr));
537
if (out_driver_type)
538
*out_driver_type = GPUDriverType::Unknown;
539
}
540
541
if (ret.empty())
542
ret = "(Unknown)";
543
544
return ret;
545
}
546
547
std::string D3DCommon::GetDriverVersionFromLUID(const LUID& luid)
548
{
549
std::string ret;
550
551
HKEY hKey;
552
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\DirectX", 0, KEY_READ, &hKey) == ERROR_SUCCESS)
553
{
554
DWORD max_key_len = 0, adapter_count = 0;
555
if (RegQueryInfoKeyW(hKey, nullptr, nullptr, nullptr, &adapter_count, &max_key_len, nullptr, nullptr, nullptr,
556
nullptr, nullptr, nullptr) == ERROR_SUCCESS)
557
{
558
std::vector<WCHAR> current_name(max_key_len + 1);
559
for (DWORD i = 0; i < adapter_count; ++i)
560
{
561
DWORD subKeyLength = static_cast<DWORD>(current_name.size());
562
if (RegEnumKeyExW(hKey, i, current_name.data(), &subKeyLength, nullptr, nullptr, nullptr, nullptr) ==
563
ERROR_SUCCESS)
564
{
565
LUID current_luid = {};
566
DWORD current_luid_size = sizeof(uint64_t);
567
if (RegGetValueW(hKey, current_name.data(), L"AdapterLuid", RRF_RT_QWORD, nullptr, &current_luid,
568
&current_luid_size) == ERROR_SUCCESS &&
569
current_luid.HighPart == luid.HighPart && current_luid.LowPart == luid.LowPart)
570
{
571
LARGE_INTEGER driver_version = {};
572
DWORD driver_version_size = sizeof(driver_version);
573
if (RegGetValueW(hKey, current_name.data(), L"DriverVersion", RRF_RT_QWORD, nullptr, &driver_version,
574
&driver_version_size) == ERROR_SUCCESS)
575
{
576
WORD nProduct = HIWORD(driver_version.HighPart);
577
WORD nVersion = LOWORD(driver_version.HighPart);
578
WORD nSubVersion = HIWORD(driver_version.LowPart);
579
WORD nBuild = LOWORD(driver_version.LowPart);
580
ret = fmt::format("{}.{}.{}.{}", nProduct, nVersion, nSubVersion, nBuild);
581
}
582
}
583
}
584
}
585
}
586
587
RegCloseKey(hKey);
588
}
589
590
return ret;
591
}
592
593
std::optional<DynamicHeapArray<u8>> D3DCommon::CompileShader(u32 shader_model, bool debug_device, GPUShaderStage stage,
594
std::string_view source, const char* entry_point,
595
Error* error)
596
{
597
if (shader_model >= 60)
598
return CompileShaderWithDXC(shader_model, debug_device, stage, source, entry_point, error);
599
else
600
return CompileShaderWithFXC(shader_model, debug_device, stage, source, entry_point, error);
601
}
602
603
std::optional<DynamicHeapArray<u8>> D3DCommon::CompileShaderWithFXC(u32 shader_model, bool debug_device,
604
GPUShaderStage stage, std::string_view source,
605
const char* entry_point, Error* error)
606
{
607
if (!LoadD3DCompilerLibrary(error))
608
return {};
609
610
const char* target;
611
switch (shader_model)
612
{
613
case 40:
614
{
615
static constexpr std::array<const char*, static_cast<u32>(GPUShaderStage::MaxCount)> targets = {
616
{"vs_4_0", "ps_4_0", "gs_4_0", "cs_4_0"}};
617
target = targets[static_cast<int>(stage)];
618
}
619
break;
620
621
case 41:
622
{
623
static constexpr std::array<const char*, static_cast<u32>(GPUShaderStage::MaxCount)> targets = {
624
{"vs_4_1", "ps_4_1", "gs_4_0", "cs_4_1"}};
625
target = targets[static_cast<int>(stage)];
626
}
627
break;
628
629
case 50:
630
{
631
static constexpr std::array<const char*, static_cast<u32>(GPUShaderStage::MaxCount)> targets = {
632
{"vs_5_0", "ps_5_0", "gs_5_0", "cs_5_0"}};
633
target = targets[static_cast<int>(stage)];
634
}
635
break;
636
637
default:
638
Error::SetStringFmt(error, "Unknown shader model: {}", shader_model);
639
return {};
640
}
641
642
static constexpr UINT flags_non_debug = D3DCOMPILE_PACK_MATRIX_ROW_MAJOR | D3DCOMPILE_OPTIMIZATION_LEVEL3;
643
static constexpr UINT flags_debug =
644
D3DCOMPILE_PACK_MATRIX_ROW_MAJOR | D3DCOMPILE_SKIP_OPTIMIZATION | D3DCOMPILE_DEBUG;
645
646
Microsoft::WRL::ComPtr<ID3DBlob> blob;
647
Microsoft::WRL::ComPtr<ID3DBlob> error_blob;
648
const HRESULT hr =
649
s_libs.D3DCompile(source.data(), source.size(), "0", nullptr, nullptr, entry_point, target,
650
debug_device ? flags_debug : flags_non_debug, 0, blob.GetAddressOf(), error_blob.GetAddressOf());
651
652
std::string_view error_string;
653
if (error_blob)
654
{
655
error_string =
656
std::string_view(static_cast<const char*>(error_blob->GetBufferPointer()), error_blob->GetBufferSize());
657
}
658
659
if (FAILED(hr))
660
{
661
ERROR_LOG("Failed to compile '{}':\n{}", target, error_string);
662
GPUDevice::DumpBadShader(source, error_string);
663
Error::SetHResult(error, "D3DCompile() failed: ", hr);
664
return {};
665
}
666
667
if (!error_string.empty())
668
WARNING_LOG("'{}' compiled with warnings:\n{}", target, error_string);
669
670
error_blob.Reset();
671
672
return DynamicHeapArray<u8>(static_cast<const u8*>(blob->GetBufferPointer()), blob->GetBufferSize());
673
}
674
675
bool D3DCommon::LoadD3DCompilerLibrary(Error* error)
676
{
677
if (s_libs.d3dcompiler_library.IsOpen())
678
return true;
679
680
// double check, another thread may have opened it
681
const std::unique_lock lock(s_libs.load_mutex);
682
if (s_libs.d3dcompiler_library.IsOpen())
683
return true;
684
685
if (!s_libs.d3dcompiler_library.Open(D3DCOMPILER_DLL_A, error))
686
return false;
687
688
if (!s_libs.d3dcompiler_library.GetSymbol("D3DCompile", &s_libs.D3DCompile))
689
{
690
Error::SetStringView(error, "Failed to load D3DCompile from d3dcompiler.dll");
691
s_libs.d3dcompiler_library.Close();
692
return false;
693
}
694
695
return true;
696
}
697
698
std::optional<DynamicHeapArray<u8>> D3DCommon::CompileShaderWithDXC(u32 shader_model, bool debug_device,
699
GPUShaderStage stage, std::string_view source,
700
const char* entry_point, Error* error)
701
{
702
if (!LoadDXCompilerLibrary(error))
703
return {};
704
705
HRESULT hr;
706
Microsoft::WRL::ComPtr<IDxcUtils> utils;
707
if (FAILED(hr = s_libs.DxcCreateInstance(CLSID_DxcUtils, IID_PPV_ARGS(utils.GetAddressOf())))) [[unlikely]]
708
{
709
Error::SetHResult(error, "DxcCreateInstance(CLSID_DxcUtils) failed: ", hr);
710
return {};
711
}
712
713
Microsoft::WRL::ComPtr<IDxcBlobEncoding> source_blob;
714
if (FAILED(hr = utils->CreateBlob(source.data(), static_cast<u32>(source.size()), DXC_CP_UTF8,
715
source_blob.GetAddressOf()))) [[unlikely]]
716
{
717
Error::SetHResult(error, "CreateBlob() failed: ", hr);
718
return {};
719
}
720
721
Microsoft::WRL::ComPtr<IDxcCompiler> compiler;
722
if (FAILED(hr = s_libs.DxcCreateInstance(CLSID_DxcCompiler, IID_PPV_ARGS(compiler.GetAddressOf())))) [[unlikely]]
723
{
724
Error::SetHResult(error, "DxcCreateInstance(CLSID_DxcCompiler) failed: ", hr);
725
return {};
726
}
727
728
const wchar_t* target;
729
switch (shader_model)
730
{
731
case 60:
732
{
733
static constexpr std::array<const wchar_t*, static_cast<u32>(GPUShaderStage::MaxCount)> targets = {
734
{L"vs_6_0", L"ps_6_0", L"gs_6_0", L"cs_6_0"}};
735
target = targets[static_cast<int>(stage)];
736
}
737
break;
738
739
default:
740
Error::SetStringFmt(error, "Unknown shader model: {}", shader_model);
741
return {};
742
}
743
744
static constexpr const wchar_t* nondebug_arguments[] = {
745
L"-Qstrip_reflect",
746
L"-Qstrip_debug",
747
DXC_ARG_PACK_MATRIX_ROW_MAJOR,
748
DXC_ARG_OPTIMIZATION_LEVEL3,
749
};
750
static constexpr const wchar_t* debug_arguments[] = {
751
L"-Qstrip_reflect", DXC_ARG_DEBUG, L"-Qembed_debug", DXC_ARG_PACK_MATRIX_ROW_MAJOR, DXC_ARG_SKIP_OPTIMIZATIONS,
752
};
753
const wchar_t* const* arguments = debug_device ? debug_arguments : nondebug_arguments;
754
const size_t arguments_size = debug_device ? std::size(debug_arguments) : std::size(nondebug_arguments);
755
756
Microsoft::WRL::ComPtr<IDxcOperationResult> result;
757
Microsoft::WRL::ComPtr<IDxcResult> compile_result;
758
Microsoft::WRL::ComPtr<IDxcBlobUtf8> error_output;
759
std::string_view error_output_sv;
760
761
hr = compiler->Compile(source_blob.Get(), L"source", StringUtil::UTF8StringToWideString(entry_point).c_str(), target,
762
const_cast<LPCWSTR*>(arguments), static_cast<u32>(arguments_size), nullptr, 0, nullptr,
763
result.GetAddressOf());
764
765
if (SUCCEEDED(result.As(&compile_result)) && compile_result->HasOutput(DXC_OUT_ERRORS) &&
766
SUCCEEDED(compile_result->GetOutput(DXC_OUT_ERRORS, IID_PPV_ARGS(error_output.GetAddressOf()), nullptr)))
767
{
768
error_output_sv =
769
std::string_view(static_cast<const char*>(error_output->GetBufferPointer()), error_output->GetBufferSize());
770
}
771
772
if (FAILED(hr) || (FAILED(result->GetStatus(&hr)) || FAILED(hr)))
773
{
774
Error::SetHResult(error, "Compile() failed: ", hr);
775
776
ERROR_LOG("Failed to compile {} {}:\n{}", GPUShader::GetStageName(stage), shader_model, error_output_sv);
777
GPUDevice::DumpBadShader(source, error_output_sv);
778
return {};
779
}
780
781
if (!error_output_sv.empty())
782
WARNING_LOG("{} {} compiled with warnings:\n{}", GPUShader::GetStageName(stage), shader_model, error_output_sv);
783
784
Microsoft::WRL::ComPtr<IDxcBlob> object_blob;
785
if (!compile_result->HasOutput(DXC_OUT_OBJECT) ||
786
FAILED(hr = compile_result->GetOutput(DXC_OUT_OBJECT, IID_PPV_ARGS(object_blob.GetAddressOf()), nullptr)))
787
{
788
Error::SetHResult(error, "GetOutput(DXC_OUT_OBJECT) failed: ", hr);
789
return {};
790
}
791
792
return DynamicHeapArray<u8>(static_cast<const u8*>(object_blob->GetBufferPointer()), object_blob->GetBufferSize());
793
}
794
795
bool D3DCommon::LoadDXCompilerLibrary(Error* error)
796
{
797
if (s_libs.dxcompiler_library.IsOpen())
798
return true;
799
800
// double check, another thread may have opened it
801
const std::unique_lock lock(s_libs.load_mutex);
802
if (s_libs.dxcompiler_library.IsOpen())
803
return true;
804
805
if (!s_libs.dxcompiler_library.Open("dxcompiler.dll", error))
806
return false;
807
808
if (!s_libs.dxcompiler_library.GetSymbol("DxcCreateInstance", &s_libs.DxcCreateInstance))
809
{
810
Error::SetStringView(error, "Failed to load DxcCreateInstance from dxcompiler.dll");
811
s_libs.dxcompiler_library.Close();
812
return false;
813
}
814
815
return true;
816
}
817
818
static constexpr std::array<D3DCommon::DXGIFormatMapping, static_cast<int>(GPUTextureFormat::MaxCount)>
819
s_format_mapping = {{
820
// clang-format off
821
// d3d_format srv_format rtv_format dsv_format
822
{DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN }, // Unknown
823
{DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_UNKNOWN }, // RGBA8
824
{DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_UNKNOWN }, // BGRA8
825
{DXGI_FORMAT_B5G6R5_UNORM, DXGI_FORMAT_B5G6R5_UNORM, DXGI_FORMAT_B5G6R5_UNORM, DXGI_FORMAT_UNKNOWN }, // RGB565
826
{DXGI_FORMAT_B5G5R5A1_UNORM, DXGI_FORMAT_B5G5R5A1_UNORM, DXGI_FORMAT_B5G5R5A1_UNORM, DXGI_FORMAT_UNKNOWN }, // RGB5A1
827
{DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN }, // A1BGR5
828
{DXGI_FORMAT_R8_UNORM, DXGI_FORMAT_R8_UNORM, DXGI_FORMAT_R8_UNORM, DXGI_FORMAT_UNKNOWN }, // R8
829
{DXGI_FORMAT_R16_TYPELESS, DXGI_FORMAT_R16_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_D16_UNORM }, // D16
830
{DXGI_FORMAT_R24G8_TYPELESS, DXGI_FORMAT_R24_UNORM_X8_TYPELESS, DXGI_FORMAT_R24_UNORM_X8_TYPELESS, DXGI_FORMAT_D24_UNORM_S8_UINT }, // D24S8
831
{DXGI_FORMAT_R32_TYPELESS, DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_D32_FLOAT }, // D32F
832
{DXGI_FORMAT_R32G8X24_TYPELESS, DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS,DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS, DXGI_FORMAT_D32_FLOAT_S8X24_UINT }, // D32FS8
833
{DXGI_FORMAT_R16_UNORM, DXGI_FORMAT_R16_UNORM, DXGI_FORMAT_R16_UNORM, DXGI_FORMAT_UNKNOWN }, // R16
834
{DXGI_FORMAT_R16_SINT, DXGI_FORMAT_R16_SINT, DXGI_FORMAT_R16_SINT, DXGI_FORMAT_UNKNOWN }, // R16I
835
{DXGI_FORMAT_R16_UINT, DXGI_FORMAT_R16_UINT, DXGI_FORMAT_R16_UINT, DXGI_FORMAT_UNKNOWN }, // R16U
836
{DXGI_FORMAT_R16_FLOAT, DXGI_FORMAT_R16_FLOAT, DXGI_FORMAT_R16_FLOAT, DXGI_FORMAT_UNKNOWN }, // R16F
837
{DXGI_FORMAT_R32_SINT, DXGI_FORMAT_R32_SINT, DXGI_FORMAT_R32_SINT, DXGI_FORMAT_UNKNOWN }, // R32I
838
{DXGI_FORMAT_R32_UINT, DXGI_FORMAT_R32_UINT, DXGI_FORMAT_R32_UINT, DXGI_FORMAT_UNKNOWN }, // R32U
839
{DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_UNKNOWN }, // R32F
840
{DXGI_FORMAT_R8G8_UNORM, DXGI_FORMAT_R8G8_UNORM, DXGI_FORMAT_R8G8_UNORM, DXGI_FORMAT_UNKNOWN }, // RG8
841
{DXGI_FORMAT_R16G16_UNORM, DXGI_FORMAT_R16G16_UNORM, DXGI_FORMAT_R16G16_UNORM, DXGI_FORMAT_UNKNOWN }, // RG16
842
{DXGI_FORMAT_R16G16_FLOAT, DXGI_FORMAT_R16G16_FLOAT, DXGI_FORMAT_R16G16_FLOAT, DXGI_FORMAT_UNKNOWN }, // RG16F
843
{DXGI_FORMAT_R32G32_FLOAT, DXGI_FORMAT_R32G32_FLOAT, DXGI_FORMAT_R32G32_FLOAT, DXGI_FORMAT_UNKNOWN }, // RG32F
844
{DXGI_FORMAT_R16G16B16A16_UNORM, DXGI_FORMAT_R16G16B16A16_UNORM, DXGI_FORMAT_R16G16B16A16_UNORM, DXGI_FORMAT_UNKNOWN }, // RGBA16
845
{DXGI_FORMAT_R16G16B16A16_FLOAT, DXGI_FORMAT_R16G16B16A16_FLOAT, DXGI_FORMAT_R16G16B16A16_FLOAT, DXGI_FORMAT_UNKNOWN }, // RGBA16F
846
{DXGI_FORMAT_R32G32B32A32_FLOAT, DXGI_FORMAT_R32G32B32A32_FLOAT, DXGI_FORMAT_R32G32B32A32_FLOAT, DXGI_FORMAT_UNKNOWN }, // RGBA32F
847
{DXGI_FORMAT_R10G10B10A2_UNORM, DXGI_FORMAT_R10G10B10A2_UNORM, DXGI_FORMAT_R10G10B10A2_UNORM, DXGI_FORMAT_UNKNOWN }, // RGB10A2
848
{DXGI_FORMAT_R8G8B8A8_UNORM_SRGB,DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, DXGI_FORMAT_UNKNOWN }, // SRGBA8
849
{DXGI_FORMAT_BC1_UNORM, DXGI_FORMAT_BC1_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN }, // BC1
850
{DXGI_FORMAT_BC2_UNORM, DXGI_FORMAT_BC2_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN }, // BC2
851
{DXGI_FORMAT_BC3_UNORM, DXGI_FORMAT_BC3_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN }, // BC3
852
{DXGI_FORMAT_BC7_UNORM, DXGI_FORMAT_BC7_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN }, // BC7
853
// clang-format on
854
}};
855
856
const D3DCommon::DXGIFormatMapping& D3DCommon::GetFormatMapping(GPUTextureFormat format)
857
{
858
DebugAssert(static_cast<u8>(format) < s_format_mapping.size());
859
return s_format_mapping[static_cast<u8>(format)];
860
}
861
862
GPUTextureFormat D3DCommon::GetFormatForDXGIFormat(DXGI_FORMAT format)
863
{
864
for (u32 i = 0; i < static_cast<u32>(GPUTextureFormat::MaxCount); i++)
865
{
866
if (s_format_mapping[i].resource_format == format)
867
return static_cast<GPUTextureFormat>(i);
868
}
869
870
return GPUTextureFormat::Unknown;
871
}
872
873