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