Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
stenzek
GitHub Repository: stenzek/duckstation
Path: blob/master/src/util/d3d11_device.cpp
7382 views
1
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <[email protected]>
2
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
3
4
#include "d3d11_device.h"
5
#include "d3d11_pipeline.h"
6
#include "d3d11_texture.h"
7
#include "d3d_common.h"
8
9
#include "core/core.h"
10
11
#include "common/align.h"
12
#include "common/assert.h"
13
#include "common/bitutils.h"
14
#include "common/error.h"
15
#include "common/file_system.h"
16
#include "common/log.h"
17
#include "common/path.h"
18
#include "common/string_util.h"
19
20
#include "fmt/format.h"
21
22
#include <array>
23
#include <d3dcompiler.h>
24
#include <dxgi1_5.h>
25
26
LOG_CHANNEL(GPUDevice);
27
28
// We need to synchronize instance creation because of adapter enumeration from the UI thread.
29
static std::mutex s_instance_mutex;
30
31
static constexpr std::array<float, 4> s_clear_color = {};
32
static constexpr GPUTextureFormat s_swap_chain_format = GPUTextureFormat::RGBA8;
33
34
void SetD3DDebugObjectName(ID3D11DeviceChild* obj, std::string_view name)
35
{
36
#ifdef ENABLE_GPU_OBJECT_NAMES
37
// WKPDID_D3DDebugObjectName
38
static constexpr GUID guid = {0x429b8c22, 0x9188, 0x4b0c, {0x87, 0x42, 0xac, 0xb0, 0xbf, 0x85, 0xc2, 0x00}};
39
40
UINT existing_data_size;
41
HRESULT hr = obj->GetPrivateData(guid, &existing_data_size, nullptr);
42
if (SUCCEEDED(hr) && existing_data_size > 0)
43
return;
44
45
obj->SetPrivateData(guid, static_cast<UINT>(name.length()), name.data());
46
#endif
47
}
48
49
D3D11Device::D3D11Device()
50
{
51
m_render_api = RenderAPI::D3D11;
52
m_features.exclusive_fullscreen = true; // set so the caller can pass a mode to CreateDeviceAndSwapChain()
53
}
54
55
D3D11Device::~D3D11Device()
56
{
57
// Should all be torn down by now.
58
Assert(!m_device);
59
}
60
61
bool D3D11Device::CreateDeviceAndMainSwapChain(std::string_view adapter, CreateFlags create_flags, const WindowInfo& wi,
62
GPUVSyncMode vsync_mode,
63
const ExclusiveFullscreenMode* exclusive_fullscreen_mode,
64
std::optional<bool> exclusive_fullscreen_control, Error* error)
65
{
66
std::unique_lock lock(s_instance_mutex);
67
68
UINT d3d_create_flags = 0;
69
if (m_debug_device)
70
d3d_create_flags |= D3D11_CREATE_DEVICE_DEBUG;
71
72
m_dxgi_factory = D3DCommon::CreateFactory(m_debug_device, error);
73
if (!m_dxgi_factory)
74
return false;
75
76
ComPtr<IDXGIAdapter1> dxgi_adapter = D3DCommon::GetAdapterByName(m_dxgi_factory.Get(), adapter);
77
m_max_feature_level = D3DCommon::GetDeviceMaxFeatureLevel(dxgi_adapter.Get());
78
79
static constexpr std::array<D3D_FEATURE_LEVEL, 4> requested_feature_levels = {
80
{D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0}};
81
82
ComPtr<ID3D11Device> temp_device;
83
ComPtr<ID3D11DeviceContext> temp_context;
84
HRESULT hr;
85
if (!D3DCommon::CreateD3D11Device(dxgi_adapter.Get(), d3d_create_flags, requested_feature_levels.data(),
86
static_cast<UINT>(requested_feature_levels.size()), &temp_device, nullptr,
87
&temp_context, error))
88
{
89
return false;
90
}
91
else if (FAILED(hr = temp_device.As(&m_device)) || FAILED(hr = temp_context.As(&m_context)))
92
{
93
Error::SetHResult(error, "Failed to get D3D11.1 device: ", hr);
94
return false;
95
}
96
97
// just in case the max query failed, apparently happens for some people...
98
m_max_feature_level = std::max(m_max_feature_level, m_device->GetFeatureLevel());
99
100
// we re-grab these later, see below
101
dxgi_adapter.Reset();
102
temp_context.Reset();
103
temp_device.Reset();
104
105
if (m_debug_device && IsDebuggerPresent())
106
{
107
ComPtr<ID3D11InfoQueue> info;
108
hr = m_device.As(&info);
109
if (SUCCEEDED(hr))
110
{
111
info->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_ERROR, TRUE);
112
info->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_WARNING, TRUE);
113
}
114
}
115
116
#ifdef ENABLE_GPU_OBJECT_NAMES
117
if (m_debug_device)
118
m_context.As(&m_annotation);
119
#endif
120
121
ComPtr<IDXGIDevice> dxgi_device;
122
GPUDriverType driver_type = GPUDriverType::Unknown;
123
if (SUCCEEDED(m_device.As(&dxgi_device)) &&
124
SUCCEEDED(dxgi_device->GetParent(IID_PPV_ARGS(dxgi_adapter.GetAddressOf()))))
125
INFO_LOG("D3D Adapter: {}", D3DCommon::GetAdapterName(dxgi_adapter.Get(), &driver_type));
126
else
127
ERROR_LOG("Failed to obtain D3D adapter name.");
128
INFO_LOG("Max device feature level: {}",
129
D3DCommon::GetFeatureLevelString(D3DCommon::GetRenderAPIVersionForFeatureLevel(m_max_feature_level)));
130
131
SetDriverType(driver_type);
132
SetFeatures(create_flags);
133
134
if (!wi.IsSurfaceless())
135
{
136
m_main_swap_chain = CreateSwapChain(wi, vsync_mode, exclusive_fullscreen_mode, exclusive_fullscreen_control, error);
137
if (!m_main_swap_chain)
138
return false;
139
}
140
141
if (!CreateBuffers(error))
142
return false;
143
144
return true;
145
}
146
147
void D3D11Device::DestroyDevice()
148
{
149
std::unique_lock lock(s_instance_mutex);
150
151
DestroyBuffers();
152
m_main_swap_chain.reset();
153
m_context.Reset();
154
m_device.Reset();
155
}
156
157
void D3D11Device::SetFeatures(CreateFlags create_flags)
158
{
159
const D3D_FEATURE_LEVEL feature_level = m_device->GetFeatureLevel();
160
161
m_render_api_version = D3DCommon::GetRenderAPIVersionForFeatureLevel(feature_level);
162
m_max_texture_size = D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION;
163
m_max_multisamples = 1;
164
for (u32 multisamples = 2; multisamples < D3D11_MAX_MULTISAMPLE_SAMPLE_COUNT; multisamples++)
165
{
166
UINT num_quality_levels;
167
if (SUCCEEDED(
168
m_device->CheckMultisampleQualityLevels(DXGI_FORMAT_R8G8B8A8_UNORM, multisamples, &num_quality_levels)) &&
169
num_quality_levels > 0)
170
{
171
m_max_multisamples = static_cast<u16>(multisamples);
172
}
173
}
174
175
m_features.dual_source_blend = !HasCreateFlag(create_flags, CreateFlags::DisableDualSourceBlend);
176
m_features.framebuffer_fetch = false;
177
m_features.per_sample_shading = (feature_level >= D3D_FEATURE_LEVEL_10_1);
178
m_features.noperspective_interpolation = true;
179
m_features.texture_copy_to_self = false;
180
m_features.texture_buffers = !HasCreateFlag(create_flags, CreateFlags::DisableTextureBuffers);
181
m_features.texture_buffers_emulated_with_ssbo = false;
182
m_features.feedback_loops = false;
183
m_features.geometry_shaders = !HasCreateFlag(create_flags, CreateFlags::DisableGeometryShaders);
184
m_features.compute_shaders =
185
(!HasCreateFlag(create_flags, CreateFlags::DisableComputeShaders) && feature_level >= D3D_FEATURE_LEVEL_11_0);
186
m_features.partial_msaa_resolve = false;
187
m_features.memory_import = false;
188
m_features.exclusive_fullscreen = true;
189
m_features.explicit_present = false;
190
m_features.timed_present = false;
191
m_features.gpu_timing = true;
192
m_features.shader_cache = true;
193
m_features.pipeline_cache = false;
194
m_features.prefer_unused_textures = false;
195
m_features.raster_order_views = false;
196
if (!HasCreateFlag(create_flags, CreateFlags::DisableRasterOrderViews))
197
{
198
D3D11_FEATURE_DATA_D3D11_OPTIONS2 data = {};
199
m_features.raster_order_views =
200
(SUCCEEDED(m_device->CheckFeatureSupport(D3D11_FEATURE_D3D11_OPTIONS2, &data, sizeof(data))) &&
201
data.ROVsSupported);
202
}
203
204
m_features.dxt_textures =
205
(!HasCreateFlag(create_flags, CreateFlags::DisableCompressedTextures) &&
206
(SupportsTextureFormat(GPUTextureFormat::BC1) && SupportsTextureFormat(GPUTextureFormat::BC2) &&
207
SupportsTextureFormat(GPUTextureFormat::BC3)));
208
m_features.bptc_textures = (!HasCreateFlag(create_flags, CreateFlags::DisableCompressedTextures) &&
209
SupportsTextureFormat(GPUTextureFormat::BC7));
210
}
211
212
D3D11SwapChain::D3D11SwapChain(const WindowInfo& wi, GPUVSyncMode vsync_mode,
213
const GPUDevice::ExclusiveFullscreenMode* fullscreen_mode)
214
: GPUSwapChain(wi, vsync_mode)
215
{
216
if (fullscreen_mode)
217
InitializeExclusiveFullscreenMode(fullscreen_mode);
218
}
219
220
D3D11SwapChain::~D3D11SwapChain()
221
{
222
m_swap_chain_rtv.Reset();
223
DestroySwapChain();
224
}
225
226
bool D3D11SwapChain::InitializeExclusiveFullscreenMode(const GPUDevice::ExclusiveFullscreenMode* mode)
227
{
228
const D3DCommon::DXGIFormatMapping& fm = D3DCommon::GetFormatMapping(s_swap_chain_format);
229
230
const HWND window_hwnd = reinterpret_cast<HWND>(m_window_info.window_handle);
231
RECT client_rc{};
232
GetClientRect(window_hwnd, &client_rc);
233
234
// Little bit messy...
235
HRESULT hr;
236
ComPtr<IDXGIDevice> dxgi_dev;
237
if (FAILED((hr = D3D11Device::GetD3DDevice()->QueryInterface(IID_PPV_ARGS(dxgi_dev.GetAddressOf())))))
238
{
239
ERROR_LOG("Failed to get DXGIDevice from D3D device: {:08X}", static_cast<unsigned>(hr));
240
return false;
241
}
242
ComPtr<IDXGIAdapter> dxgi_adapter;
243
if (FAILED((hr = dxgi_dev->GetAdapter(dxgi_adapter.GetAddressOf()))))
244
{
245
ERROR_LOG("Failed to get DXGIAdapter from DXGIDevice: {:08X}", static_cast<unsigned>(hr));
246
return false;
247
}
248
249
m_fullscreen_mode = D3DCommon::GetRequestedExclusiveFullscreenModeDesc(
250
dxgi_adapter.Get(), client_rc, mode, fm.resource_format, m_fullscreen_output.GetAddressOf());
251
return m_fullscreen_mode.has_value();
252
}
253
254
u32 D3D11SwapChain::GetNewBufferCount(GPUVSyncMode vsync_mode)
255
{
256
// With vsync off, we only need two buffers. Same for blocking vsync.
257
// With triple buffering, we need three.
258
return (vsync_mode == GPUVSyncMode::Mailbox) ? 3 : 2;
259
}
260
261
bool D3D11SwapChain::CreateSwapChain(Error* error)
262
{
263
const D3DCommon::DXGIFormatMapping& fm = D3DCommon::GetFormatMapping(s_swap_chain_format);
264
265
const HWND window_hwnd = reinterpret_cast<HWND>(m_window_info.window_handle);
266
RECT client_rc{};
267
GetClientRect(window_hwnd, &client_rc);
268
269
// Using mailbox-style no-allow-tearing causes tearing in exclusive fullscreen.
270
if (IsExclusiveFullscreen() && m_vsync_mode == GPUVSyncMode::Mailbox)
271
{
272
WARNING_LOG("Using FIFO instead of Mailbox vsync due to exclusive fullscreen.");
273
m_vsync_mode = GPUVSyncMode::FIFO;
274
}
275
276
m_using_flip_model_swap_chain =
277
!Core::GetBoolSettingValue("Display", "UseBlitSwapChain", false) || IsExclusiveFullscreen();
278
279
IDXGIFactory5* const dxgi_factory = D3D11Device::GetDXGIFactory();
280
ID3D11Device1* const d3d_device = D3D11Device::GetD3DDevice();
281
282
DXGI_SWAP_CHAIN_DESC1 swap_chain_desc = {};
283
swap_chain_desc.Width = static_cast<u32>(client_rc.right - client_rc.left);
284
swap_chain_desc.Height = static_cast<u32>(client_rc.bottom - client_rc.top);
285
swap_chain_desc.Format = fm.resource_format;
286
swap_chain_desc.SampleDesc.Count = 1;
287
swap_chain_desc.BufferCount = GetNewBufferCount(m_vsync_mode);
288
swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
289
swap_chain_desc.SwapEffect = m_using_flip_model_swap_chain ? DXGI_SWAP_EFFECT_FLIP_DISCARD : DXGI_SWAP_EFFECT_DISCARD;
290
291
HRESULT hr = S_OK;
292
293
if (IsExclusiveFullscreen())
294
{
295
DXGI_SWAP_CHAIN_DESC1 fs_sd_desc = swap_chain_desc;
296
DXGI_SWAP_CHAIN_FULLSCREEN_DESC fs_desc = {};
297
298
fs_sd_desc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
299
fs_sd_desc.Width = m_fullscreen_mode->Width;
300
fs_sd_desc.Height = m_fullscreen_mode->Height;
301
fs_desc.RefreshRate = m_fullscreen_mode->RefreshRate;
302
fs_desc.ScanlineOrdering = m_fullscreen_mode->ScanlineOrdering;
303
fs_desc.Scaling = m_fullscreen_mode->Scaling;
304
fs_desc.Windowed = FALSE;
305
306
VERBOSE_LOG("Creating a {}x{} exclusive fullscreen swap chain", fs_sd_desc.Width, fs_sd_desc.Height);
307
hr = dxgi_factory->CreateSwapChainForHwnd(d3d_device, window_hwnd, &fs_sd_desc, &fs_desc, m_fullscreen_output.Get(),
308
m_swap_chain.ReleaseAndGetAddressOf());
309
if (FAILED(hr))
310
{
311
WARNING_LOG("Failed to create fullscreen swap chain, trying windowed.");
312
m_fullscreen_output.Reset();
313
m_fullscreen_mode.reset();
314
m_using_allow_tearing = (m_using_flip_model_swap_chain && D3DCommon::SupportsAllowTearing(dxgi_factory));
315
}
316
}
317
318
if (!IsExclusiveFullscreen())
319
{
320
VERBOSE_LOG("Creating a {}x{} {} windowed swap chain", swap_chain_desc.Width, swap_chain_desc.Height,
321
m_using_flip_model_swap_chain ? "flip-discard" : "discard");
322
m_using_allow_tearing = (m_using_flip_model_swap_chain && !IsExclusiveFullscreen() &&
323
D3DCommon::SupportsAllowTearing(D3D11Device::GetDXGIFactory()));
324
if (m_using_allow_tearing)
325
swap_chain_desc.Flags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
326
hr = dxgi_factory->CreateSwapChainForHwnd(d3d_device, window_hwnd, &swap_chain_desc, nullptr, nullptr,
327
m_swap_chain.ReleaseAndGetAddressOf());
328
}
329
330
if (FAILED(hr) && m_using_flip_model_swap_chain)
331
{
332
WARNING_LOG("Failed to create a flip-discard swap chain, trying discard.");
333
swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
334
swap_chain_desc.Flags = 0;
335
m_using_flip_model_swap_chain = false;
336
m_using_allow_tearing = false;
337
338
hr = dxgi_factory->CreateSwapChainForHwnd(d3d_device, window_hwnd, &swap_chain_desc, nullptr, nullptr,
339
m_swap_chain.ReleaseAndGetAddressOf());
340
if (FAILED(hr)) [[unlikely]]
341
{
342
Error::SetHResult(error, "CreateSwapChainForHwnd() failed: ", hr);
343
return false;
344
}
345
}
346
347
// we need the specific factory for the device, otherwise MakeWindowAssociation() is flaky.
348
ComPtr<IDXGIFactory> parent_factory;
349
if (FAILED(m_swap_chain->GetParent(IID_PPV_ARGS(parent_factory.GetAddressOf()))) ||
350
FAILED(parent_factory->MakeWindowAssociation(window_hwnd, DXGI_MWA_NO_WINDOW_CHANGES)))
351
{
352
WARNING_LOG("MakeWindowAssociation() to disable ALT+ENTER failed");
353
}
354
355
return true;
356
}
357
358
void D3D11SwapChain::DestroySwapChain()
359
{
360
if (!m_swap_chain)
361
return;
362
363
// switch out of fullscreen before destroying
364
BOOL is_fullscreen;
365
if (SUCCEEDED(m_swap_chain->GetFullscreenState(&is_fullscreen, nullptr)) && is_fullscreen)
366
m_swap_chain->SetFullscreenState(FALSE, nullptr);
367
368
m_swap_chain.Reset();
369
}
370
371
bool D3D11SwapChain::CreateRTV(Error* error)
372
{
373
ComPtr<ID3D11Texture2D> backbuffer;
374
HRESULT hr = m_swap_chain->GetBuffer(0, IID_PPV_ARGS(backbuffer.GetAddressOf()));
375
if (FAILED(hr)) [[unlikely]]
376
{
377
Error::SetHResult(error, "GetBuffer() failed: ", hr);
378
return false;
379
}
380
381
D3D11_TEXTURE2D_DESC backbuffer_desc;
382
backbuffer->GetDesc(&backbuffer_desc);
383
384
CD3D11_RENDER_TARGET_VIEW_DESC rtv_desc(D3D11_RTV_DIMENSION_TEXTURE2D, backbuffer_desc.Format, 0, 0,
385
backbuffer_desc.ArraySize);
386
hr = D3D11Device::GetD3DDevice()->CreateRenderTargetView(backbuffer.Get(), &rtv_desc,
387
m_swap_chain_rtv.ReleaseAndGetAddressOf());
388
if (FAILED(hr)) [[unlikely]]
389
{
390
Error::SetHResult(error, "CreateRenderTargetView(): ", hr);
391
m_swap_chain_rtv.Reset();
392
return false;
393
}
394
395
m_window_info.surface_width = static_cast<u16>(backbuffer_desc.Width);
396
m_window_info.surface_height = static_cast<u16>(backbuffer_desc.Height);
397
m_window_info.surface_format = s_swap_chain_format;
398
VERBOSE_LOG("Swap chain buffer size: {}x{}", m_window_info.surface_width, m_window_info.surface_height);
399
400
if (m_window_info.type == WindowInfoType::Win32)
401
{
402
BOOL fullscreen = FALSE;
403
DXGI_SWAP_CHAIN_DESC desc;
404
if (SUCCEEDED(m_swap_chain->GetFullscreenState(&fullscreen, nullptr)) && fullscreen &&
405
SUCCEEDED(m_swap_chain->GetDesc(&desc)))
406
{
407
m_window_info.surface_refresh_rate = static_cast<float>(desc.BufferDesc.RefreshRate.Numerator) /
408
static_cast<float>(desc.BufferDesc.RefreshRate.Denominator);
409
}
410
}
411
412
return true;
413
}
414
415
bool D3D11SwapChain::ResizeBuffers(u32 new_width, u32 new_height, Error* error)
416
{
417
if (m_window_info.surface_width == new_width && m_window_info.surface_height == new_height)
418
return true;
419
420
m_swap_chain_rtv.Reset();
421
422
HRESULT hr = m_swap_chain->ResizeBuffers(0, 0, 0, DXGI_FORMAT_UNKNOWN,
423
m_using_allow_tearing ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0);
424
if (FAILED(hr)) [[unlikely]]
425
{
426
Error::SetHResult(error, "ResizeBuffers() failed: ", hr);
427
return false;
428
}
429
430
return CreateRTV(error);
431
}
432
433
bool D3D11SwapChain::SetVSyncMode(GPUVSyncMode mode, Error* error)
434
{
435
// Using mailbox-style no-allow-tearing causes tearing in exclusive fullscreen.
436
if (mode == GPUVSyncMode::Mailbox && IsExclusiveFullscreen())
437
{
438
WARNING_LOG("Using FIFO instead of Mailbox vsync due to exclusive fullscreen.");
439
mode = GPUVSyncMode::FIFO;
440
}
441
442
if (m_vsync_mode == mode)
443
return true;
444
445
const u32 old_buffer_count = GetNewBufferCount(m_vsync_mode);
446
const u32 new_buffer_count = GetNewBufferCount(mode);
447
m_vsync_mode = mode;
448
if (old_buffer_count == new_buffer_count)
449
return true;
450
451
// Buffer count change => needs recreation.
452
m_swap_chain_rtv.Reset();
453
DestroySwapChain();
454
return CreateSwapChain(error) && CreateRTV(error);
455
}
456
457
bool D3D11SwapChain::IsExclusiveFullscreen() const
458
{
459
return m_fullscreen_mode.has_value();
460
}
461
462
std::unique_ptr<GPUSwapChain> D3D11Device::CreateSwapChain(const WindowInfo& wi, GPUVSyncMode vsync_mode,
463
const ExclusiveFullscreenMode* exclusive_fullscreen_mode,
464
std::optional<bool> exclusive_fullscreen_control,
465
Error* error)
466
{
467
std::unique_ptr<D3D11SwapChain> ret;
468
if (wi.type != WindowInfoType::Win32)
469
{
470
Error::SetStringView(error, "Cannot create a swap chain on non-win32 window.");
471
return ret;
472
}
473
474
ret = std::make_unique<D3D11SwapChain>(wi, vsync_mode, exclusive_fullscreen_mode);
475
if (ret->CreateSwapChain(error) && ret->CreateRTV(error))
476
{
477
// Render a frame as soon as possible to clear out whatever was previously being displayed.
478
m_context->ClearRenderTargetView(ret->GetRTV(), s_clear_color.data());
479
ret->GetSwapChain()->Present(0, ret->IsUsingAllowTearing() ? DXGI_PRESENT_ALLOW_TEARING : 0);
480
}
481
else
482
{
483
ret.reset();
484
}
485
486
return ret;
487
}
488
489
std::string D3D11Device::GetDriverInfo() const
490
{
491
std::string ret = fmt::format("{} (Shader Model {})\n", D3DCommon::GetFeatureLevelString(m_render_api_version),
492
D3DCommon::GetShaderModelForFeatureLevelNumber(m_render_api_version));
493
494
ComPtr<IDXGIDevice> dxgi_dev;
495
if (SUCCEEDED(m_device.As(&dxgi_dev)))
496
{
497
ComPtr<IDXGIAdapter> dxgi_adapter;
498
if (SUCCEEDED(dxgi_dev->GetAdapter(dxgi_adapter.GetAddressOf())))
499
{
500
DXGI_ADAPTER_DESC desc;
501
if (SUCCEEDED(dxgi_adapter->GetDesc(&desc)))
502
{
503
fmt::format_to(std::back_inserter(ret), "VID: 0x{:04X} PID: 0x{:04X}\n", desc.VendorId, desc.DeviceId);
504
ret += StringUtil::WideStringToUTF8String(desc.Description);
505
ret += "\n";
506
507
const std::string driver_version(D3DCommon::GetDriverVersionFromLUID(desc.AdapterLuid));
508
if (!driver_version.empty())
509
{
510
ret += "Driver Version: ";
511
ret += driver_version;
512
}
513
}
514
}
515
}
516
517
return ret;
518
}
519
520
void D3D11Device::FlushCommands()
521
{
522
m_context->Flush();
523
EndTimestampQuery();
524
TrimTexturePool();
525
}
526
527
void D3D11Device::WaitForGPUIdle()
528
{
529
m_context->Flush();
530
EndTimestampQuery();
531
TrimTexturePool();
532
}
533
534
bool D3D11Device::CreateBuffers(Error* error)
535
{
536
if (!m_vertex_buffer.Create(D3D11_BIND_VERTEX_BUFFER, VERTEX_BUFFER_SIZE, VERTEX_BUFFER_SIZE, error) ||
537
!m_index_buffer.Create(D3D11_BIND_INDEX_BUFFER, INDEX_BUFFER_SIZE, INDEX_BUFFER_SIZE, error) ||
538
!m_uniform_buffer.Create(D3D11_BIND_CONSTANT_BUFFER, MIN_UNIFORM_BUFFER_SIZE, MAX_UNIFORM_BUFFER_SIZE, error))
539
{
540
ERROR_LOG("Failed to create vertex/index/uniform buffers.");
541
return false;
542
}
543
544
const CD3D11_BUFFER_DESC pc_desc(PUSH_CONSTANT_BUFFER_SIZE, D3D11_BIND_CONSTANT_BUFFER, D3D11_USAGE_DYNAMIC,
545
D3D11_CPU_ACCESS_WRITE);
546
if (const HRESULT hr = m_device->CreateBuffer(&pc_desc, nullptr, m_push_constant_buffer.GetAddressOf()); FAILED(hr))
547
{
548
Error::SetHResult(error, "Failed to create push constant buffer: ", hr);
549
return false;
550
}
551
552
// Index buffer never changes :)
553
m_context->IASetIndexBuffer(m_index_buffer.GetD3DBuffer(), DXGI_FORMAT_R16_UINT, 0);
554
m_context->VSSetConstantBuffers(1, 1, m_push_constant_buffer.GetAddressOf());
555
m_context->PSSetConstantBuffers(1, 1, m_push_constant_buffer.GetAddressOf());
556
if (m_features.compute_shaders)
557
m_context->CSSetConstantBuffers(1, 1, m_push_constant_buffer.GetAddressOf());
558
559
return true;
560
}
561
562
void D3D11Device::DestroyBuffers()
563
{
564
m_push_constant_buffer.Reset();
565
m_uniform_buffer.Destroy();
566
m_vertex_buffer.Destroy();
567
m_index_buffer.Destroy();
568
}
569
570
void D3D11Device::CopyTextureRegion(GPUTexture* dst, u32 dst_x, u32 dst_y, u32 dst_layer, u32 dst_level,
571
GPUTexture* src, u32 src_x, u32 src_y, u32 src_layer, u32 src_level, u32 width,
572
u32 height)
573
{
574
DebugAssert(src_level < src->GetLevels() && src_layer < src->GetLayers());
575
DebugAssert((src_x + width) <= src->GetMipWidth(src_level));
576
DebugAssert((src_y + height) <= src->GetMipHeight(src_level));
577
DebugAssert(dst_level < dst->GetLevels() && dst_layer < dst->GetLayers());
578
DebugAssert((dst_x + width) <= dst->GetMipWidth(dst_level));
579
DebugAssert((dst_y + height) <= dst->GetMipHeight(dst_level));
580
581
D3D11Texture* dst11 = static_cast<D3D11Texture*>(dst);
582
D3D11Texture* src11 = static_cast<D3D11Texture*>(src);
583
584
if (dst11->IsRenderTargetOrDepthStencil())
585
{
586
if (src11->GetState() == GPUTexture::State::Cleared)
587
{
588
if (src11->GetWidth() == dst11->GetWidth() && src11->GetHeight() == dst11->GetHeight())
589
{
590
// pass clear through
591
dst11->m_state = src11->m_state;
592
dst11->m_clear_value = src11->m_clear_value;
593
return;
594
}
595
}
596
else if (dst_x == 0 && dst_y == 0 && width == dst11->GetMipWidth(dst_level) &&
597
height == dst11->GetMipHeight(dst_level))
598
{
599
m_context->DiscardView(dst11->GetRTVOrDSV());
600
dst11->SetState(GPUTexture::State::Dirty);
601
}
602
603
dst11->CommitClear(m_context.Get());
604
}
605
606
src11->CommitClear(m_context.Get());
607
608
s_stats.num_copies++;
609
610
const CD3D11_BOX src_box(static_cast<LONG>(src_x), static_cast<LONG>(src_y), 0, static_cast<LONG>(src_x + width),
611
static_cast<LONG>(src_y + height), 1);
612
m_context->CopySubresourceRegion(dst11->GetD3DTexture(), D3D11CalcSubresource(dst_level, dst_layer, dst->GetLevels()),
613
dst_x, dst_y, 0, src11->GetD3DTexture(),
614
D3D11CalcSubresource(src_level, src_layer, src->GetLevels()), &src_box);
615
}
616
617
void D3D11Device::ResolveTextureRegion(GPUTexture* dst, u32 dst_x, u32 dst_y, u32 dst_layer, u32 dst_level,
618
GPUTexture* src, u32 src_x, u32 src_y, u32 width, u32 height)
619
{
620
DebugAssert((src_x + width) <= src->GetWidth());
621
DebugAssert((src_y + height) <= src->GetHeight());
622
DebugAssert(src->IsMultisampled());
623
DebugAssert(dst_level < dst->GetLevels() && dst_layer < dst->GetLayers());
624
DebugAssert((dst_x + width) <= dst->GetMipWidth(dst_level));
625
DebugAssert((dst_y + height) <= dst->GetMipHeight(dst_level));
626
DebugAssert(!dst->IsMultisampled() && src->IsMultisampled());
627
628
s_stats.num_copies++;
629
630
// DX11 can't resolve partial rects.
631
Assert(src_x == 0 && src_y == 0 && width == src->GetWidth() && height == src->GetHeight() && dst_x == 0 &&
632
dst_y == 0 && width == dst->GetMipWidth(dst_level) && height == dst->GetMipHeight(dst_level));
633
634
D3D11Texture* dst11 = static_cast<D3D11Texture*>(dst);
635
D3D11Texture* src11 = static_cast<D3D11Texture*>(src);
636
637
src11->CommitClear(m_context.Get());
638
dst11->CommitClear(m_context.Get());
639
640
m_context->ResolveSubresource(dst11->GetD3DTexture(), D3D11CalcSubresource(dst_level, dst_layer, dst->GetLevels()),
641
src11->GetD3DTexture(), 0, dst11->GetDXGIFormat());
642
}
643
644
bool D3D11Device::IsRenderTargetBound(const D3D11Texture* tex) const
645
{
646
if (tex->IsRenderTarget() || tex->HasFlag(GPUTexture::Flags::AllowBindAsImage))
647
{
648
for (u32 i = 0; i < m_num_current_render_targets; i++)
649
{
650
if (m_current_render_targets[i] == tex)
651
return true;
652
}
653
}
654
655
return false;
656
}
657
658
void D3D11Device::ClearRenderTarget(GPUTexture* t, u32 c)
659
{
660
D3D11Texture* const T = static_cast<D3D11Texture*>(t);
661
GPUDevice::ClearRenderTarget(T, c);
662
if (IsRenderTargetBound(T))
663
T->CommitClear(m_context.Get());
664
}
665
666
void D3D11Device::ClearDepth(GPUTexture* t, float d)
667
{
668
D3D11Texture* const T = static_cast<D3D11Texture*>(t);
669
GPUDevice::ClearDepth(T, d);
670
if (T == m_current_depth_target)
671
T->CommitClear(m_context.Get());
672
}
673
674
void D3D11Device::InvalidateRenderTarget(GPUTexture* t)
675
{
676
D3D11Texture* const T = static_cast<D3D11Texture*>(t);
677
GPUDevice::InvalidateRenderTarget(T);
678
if (T->IsDepthStencil() ? (m_current_depth_target == T) : IsRenderTargetBound(T))
679
T->CommitClear(m_context.Get());
680
}
681
682
GPUDevice::PresentResult D3D11Device::BeginPresent(GPUSwapChain* swap_chain, u32 clear_color)
683
{
684
D3D11SwapChain* const SC = static_cast<D3D11SwapChain*>(swap_chain);
685
686
// Check if we lost exclusive fullscreen. If so, notify the host, so it can switch to windowed mode.
687
// This might get called repeatedly if it takes a while to switch back, that's the host's problem.
688
BOOL is_fullscreen;
689
if (SC->IsExclusiveFullscreen() &&
690
(FAILED(SC->GetSwapChain()->GetFullscreenState(&is_fullscreen, nullptr)) || !is_fullscreen))
691
{
692
TrimTexturePool();
693
return PresentResult::ExclusiveFullscreenLost;
694
}
695
696
// The time here seems to include the time for the buffer to become available.
697
// This blows our our GPU usage number considerably, so read the timestamp before the final blit
698
// in this configuration. It does reduce accuracy a little, but better than seeing 100% all of
699
// the time, when it's more like a couple of percent.
700
if (SC == m_main_swap_chain.get() && m_gpu_timing_enabled)
701
{
702
PopTimestampQuery();
703
EndTimestampQuery();
704
}
705
706
m_context->ClearRenderTargetView(SC->GetRTV(), GSVector4::unorm8(clear_color).F32);
707
708
// Ugh, have to clear out any UAV bindings...
709
if (m_current_render_pass_flags & GPUPipeline::BindRenderTargetsAsImages && !m_current_compute_shader)
710
m_context->OMSetRenderTargetsAndUnorderedAccessViews(1, SC->GetRTVArray(), nullptr, 0, 0, nullptr, nullptr);
711
else
712
m_context->OMSetRenderTargets(1, SC->GetRTVArray(), nullptr);
713
if (m_current_compute_shader)
714
UnbindComputePipeline();
715
s_stats.num_render_passes++;
716
m_num_current_render_targets = 0;
717
m_current_render_pass_flags = GPUPipeline::NoRenderPassFlags;
718
std::memset(m_current_render_targets.data(), 0, sizeof(m_current_render_targets));
719
m_current_depth_target = nullptr;
720
return PresentResult::OK;
721
}
722
723
void D3D11Device::EndPresent(GPUSwapChain* swap_chain, bool explicit_present, u64 present_time)
724
{
725
D3D11SwapChain* const SC = static_cast<D3D11SwapChain*>(swap_chain);
726
DebugAssert(!explicit_present && present_time == 0);
727
DebugAssert(m_num_current_render_targets == 0 && !m_current_depth_target);
728
729
const UINT sync_interval = static_cast<UINT>(SC->GetVSyncMode() == GPUVSyncMode::FIFO);
730
const UINT flags =
731
(SC->GetVSyncMode() == GPUVSyncMode::Disabled && SC->IsUsingAllowTearing()) ? DXGI_PRESENT_ALLOW_TEARING : 0;
732
SC->GetSwapChain()->Present(sync_interval, flags);
733
734
if (m_gpu_timing_enabled)
735
StartTimestampQuery();
736
737
TrimTexturePool();
738
}
739
740
void D3D11Device::SubmitPresent(GPUSwapChain* swap_chain)
741
{
742
Panic("Not supported by this API.");
743
}
744
745
bool D3D11Device::CreateTimestampQueries()
746
{
747
for (u32 i = 0; i < NUM_TIMESTAMP_QUERIES; i++)
748
{
749
for (u32 j = 0; j < 3; j++)
750
{
751
const CD3D11_QUERY_DESC qdesc((j == 0) ? D3D11_QUERY_TIMESTAMP_DISJOINT : D3D11_QUERY_TIMESTAMP);
752
const HRESULT hr = m_device->CreateQuery(&qdesc, m_timestamp_queries[i][j].ReleaseAndGetAddressOf());
753
if (FAILED(hr))
754
{
755
m_timestamp_queries = {};
756
return false;
757
}
758
}
759
}
760
761
StartTimestampQuery();
762
return true;
763
}
764
765
void D3D11Device::DestroyTimestampQueries()
766
{
767
if (!m_timestamp_queries[0][0])
768
return;
769
770
if (m_timestamp_query_started)
771
m_context->End(m_timestamp_queries[m_write_timestamp_query][1].Get());
772
773
m_timestamp_queries = {};
774
m_read_timestamp_query = 0;
775
m_write_timestamp_query = 0;
776
m_waiting_timestamp_queries = 0;
777
m_timestamp_query_started = false;
778
}
779
780
void D3D11Device::PopTimestampQuery()
781
{
782
while (m_waiting_timestamp_queries > 0)
783
{
784
D3D11_QUERY_DATA_TIMESTAMP_DISJOINT disjoint;
785
const HRESULT disjoint_hr = m_context->GetData(m_timestamp_queries[m_read_timestamp_query][0].Get(), &disjoint,
786
sizeof(disjoint), D3D11_ASYNC_GETDATA_DONOTFLUSH);
787
if (disjoint_hr != S_OK)
788
break;
789
790
if (disjoint.Disjoint)
791
{
792
VERBOSE_LOG("GPU timing disjoint, resetting.");
793
m_read_timestamp_query = 0;
794
m_write_timestamp_query = 0;
795
m_waiting_timestamp_queries = 0;
796
m_timestamp_query_started = false;
797
}
798
else
799
{
800
u64 start = 0, end = 0;
801
const HRESULT start_hr = m_context->GetData(m_timestamp_queries[m_read_timestamp_query][1].Get(), &start,
802
sizeof(start), D3D11_ASYNC_GETDATA_DONOTFLUSH);
803
const HRESULT end_hr = m_context->GetData(m_timestamp_queries[m_read_timestamp_query][2].Get(), &end, sizeof(end),
804
D3D11_ASYNC_GETDATA_DONOTFLUSH);
805
if (start_hr == S_OK && end_hr == S_OK)
806
{
807
const float delta =
808
static_cast<float>(static_cast<double>(end - start) / (static_cast<double>(disjoint.Frequency) / 1000.0));
809
m_accumulated_gpu_time += delta;
810
m_read_timestamp_query = (m_read_timestamp_query + 1) % NUM_TIMESTAMP_QUERIES;
811
m_waiting_timestamp_queries--;
812
}
813
else
814
{
815
// Data not ready yet.
816
break;
817
}
818
}
819
}
820
}
821
822
void D3D11Device::EndTimestampQuery()
823
{
824
if (m_timestamp_query_started)
825
{
826
m_context->End(m_timestamp_queries[m_write_timestamp_query][2].Get());
827
m_context->End(m_timestamp_queries[m_write_timestamp_query][0].Get());
828
m_write_timestamp_query = (m_write_timestamp_query + 1) % NUM_TIMESTAMP_QUERIES;
829
m_timestamp_query_started = false;
830
m_waiting_timestamp_queries++;
831
}
832
}
833
834
void D3D11Device::StartTimestampQuery()
835
{
836
if (m_timestamp_query_started || !m_timestamp_queries[0][0] || m_waiting_timestamp_queries == NUM_TIMESTAMP_QUERIES)
837
return;
838
839
m_context->Begin(m_timestamp_queries[m_write_timestamp_query][0].Get());
840
m_context->End(m_timestamp_queries[m_write_timestamp_query][1].Get());
841
m_timestamp_query_started = true;
842
}
843
844
bool D3D11Device::SetGPUTimingEnabled(bool enabled)
845
{
846
if (m_gpu_timing_enabled == enabled)
847
return true;
848
849
m_gpu_timing_enabled = enabled;
850
if (m_gpu_timing_enabled)
851
{
852
if (!CreateTimestampQueries())
853
return false;
854
855
StartTimestampQuery();
856
return true;
857
}
858
else
859
{
860
DestroyTimestampQueries();
861
return true;
862
}
863
}
864
865
float D3D11Device::GetAndResetAccumulatedGPUTime()
866
{
867
const float value = m_accumulated_gpu_time;
868
m_accumulated_gpu_time = 0.0f;
869
return value;
870
}
871
872
#ifdef ENABLE_GPU_OBJECT_NAMES
873
874
void D3D11Device::PushDebugGroup(const char* name)
875
{
876
if (!m_annotation)
877
return;
878
879
m_annotation->BeginEvent(StringUtil::UTF8StringToWideString(name).c_str());
880
}
881
882
void D3D11Device::PopDebugGroup()
883
{
884
if (!m_annotation)
885
return;
886
887
m_annotation->EndEvent();
888
}
889
890
void D3D11Device::InsertDebugMessage(const char* msg)
891
{
892
if (!m_annotation)
893
return;
894
895
m_annotation->SetMarker(StringUtil::UTF8StringToWideString(msg).c_str());
896
}
897
898
#endif
899
900
void D3D11Device::MapVertexBuffer(u32 vertex_size, u32 vertex_count, void** map_ptr, u32* map_space,
901
u32* map_base_vertex)
902
{
903
const auto res = m_vertex_buffer.Map(m_context.Get(), vertex_size, vertex_size * vertex_count);
904
*map_ptr = res.pointer;
905
*map_space = res.space_aligned;
906
*map_base_vertex = res.index_aligned;
907
}
908
909
void D3D11Device::UnmapVertexBuffer(u32 vertex_size, u32 vertex_count)
910
{
911
const u32 upload_size = vertex_size * vertex_count;
912
s_stats.buffer_streamed += upload_size;
913
m_vertex_buffer.Unmap(m_context.Get(), upload_size);
914
}
915
916
void D3D11Device::MapIndexBuffer(u32 index_count, DrawIndex** map_ptr, u32* map_space, u32* map_base_index)
917
{
918
const auto res = m_index_buffer.Map(m_context.Get(), sizeof(DrawIndex), sizeof(DrawIndex) * index_count);
919
*map_ptr = static_cast<DrawIndex*>(res.pointer);
920
*map_space = res.space_aligned;
921
*map_base_index = res.index_aligned;
922
}
923
924
void D3D11Device::UnmapIndexBuffer(u32 used_index_count)
925
{
926
s_stats.buffer_streamed += sizeof(DrawIndex) * used_index_count;
927
m_index_buffer.Unmap(m_context.Get(), sizeof(DrawIndex) * used_index_count);
928
}
929
930
void D3D11Device::PushUniformBuffer(const void* data, u32 data_size)
931
{
932
DebugAssert(data_size <= PUSH_CONSTANT_BUFFER_SIZE);
933
934
D3D11_MAPPED_SUBRESOURCE mapped;
935
if (const HRESULT hr = m_context->Map(m_push_constant_buffer.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped);
936
FAILED(hr))
937
{
938
ERROR_LOG("Failed to map push constant buffer: {:08X}", static_cast<unsigned>(hr));
939
return;
940
}
941
942
std::memcpy(mapped.pData, data, data_size);
943
m_context->Unmap(m_push_constant_buffer.Get(), 0);
944
s_stats.buffer_streamed += data_size;
945
}
946
947
void* D3D11Device::MapUniformBuffer(u32 size)
948
{
949
const u32 req_align =
950
m_uniform_buffer.IsUsingMapNoOverwrite() ? UNIFORM_BUFFER_ALIGNMENT : UNIFORM_BUFFER_ALIGNMENT_DISCARD;
951
const u32 req_size = Common::AlignUpPow2(size, req_align);
952
const auto res = m_uniform_buffer.Map(m_context.Get(), req_align, req_size);
953
return res.pointer;
954
}
955
956
void D3D11Device::UnmapUniformBuffer(u32 size)
957
{
958
const u32 pos = m_uniform_buffer.GetPosition();
959
const u32 req_align =
960
m_uniform_buffer.IsUsingMapNoOverwrite() ? UNIFORM_BUFFER_ALIGNMENT : UNIFORM_BUFFER_ALIGNMENT_DISCARD;
961
const u32 req_size = Common::AlignUpPow2(size, req_align);
962
963
m_uniform_buffer.Unmap(m_context.Get(), req_size);
964
s_stats.buffer_streamed += size;
965
966
BindUniformBuffer(pos, req_size);
967
}
968
969
void D3D11Device::BindUniformBuffer(u32 offset, u32 size)
970
{
971
if (m_uniform_buffer.IsUsingMapNoOverwrite())
972
{
973
const UINT first_constant = offset / 16u;
974
const UINT num_constants = size / 16u;
975
if (m_current_compute_shader)
976
{
977
m_context->CSSetConstantBuffers1(0, 1, m_uniform_buffer.GetD3DBufferArray(), &first_constant, &num_constants);
978
}
979
else
980
{
981
m_context->VSSetConstantBuffers1(0, 1, m_uniform_buffer.GetD3DBufferArray(), &first_constant, &num_constants);
982
m_context->PSSetConstantBuffers1(0, 1, m_uniform_buffer.GetD3DBufferArray(), &first_constant, &num_constants);
983
}
984
}
985
else
986
{
987
DebugAssert(offset == 0);
988
if (m_current_compute_shader)
989
{
990
m_context->CSSetConstantBuffers(0, 1, m_uniform_buffer.GetD3DBufferArray());
991
}
992
else
993
{
994
m_context->VSSetConstantBuffers(0, 1, m_uniform_buffer.GetD3DBufferArray());
995
m_context->PSSetConstantBuffers(0, 1, m_uniform_buffer.GetD3DBufferArray());
996
}
997
}
998
}
999
1000
void D3D11Device::SetRenderTargets(GPUTexture* const* rts, u32 num_rts, GPUTexture* ds,
1001
GPUPipeline::RenderPassFlag flags)
1002
{
1003
DebugAssert(
1004
!(flags & (GPUPipeline::RenderPassFlag::ColorFeedbackLoop | GPUPipeline::RenderPassFlag::SampleDepthBuffer)));
1005
1006
// Make sure DSV isn't bound.
1007
D3D11Texture* DS = static_cast<D3D11Texture*>(ds);
1008
if (DS)
1009
DS->CommitClear(m_context.Get());
1010
1011
bool changed =
1012
(m_num_current_render_targets != num_rts || m_current_depth_target != DS || m_current_render_pass_flags != flags);
1013
m_current_render_pass_flags = flags;
1014
m_current_depth_target = DS;
1015
if (ds)
1016
{
1017
const ID3D11ShaderResourceView* srv = static_cast<D3D11Texture*>(ds)->GetD3DSRV();
1018
for (u32 i = 0; i < MAX_TEXTURE_SAMPLERS; i++)
1019
{
1020
if (m_current_textures[i] && m_current_textures[i] == srv)
1021
{
1022
m_current_textures[i] = nullptr;
1023
m_context->PSSetShaderResources(i, 1, &m_current_textures[i]);
1024
}
1025
}
1026
}
1027
1028
for (u32 i = 0; i < num_rts; i++)
1029
{
1030
D3D11Texture* const RT = static_cast<D3D11Texture*>(rts[i]);
1031
changed |= m_current_render_targets[i] != RT;
1032
m_current_render_targets[i] = RT;
1033
RT->CommitClear(m_context.Get());
1034
1035
const ID3D11ShaderResourceView* srv = RT->GetD3DSRV();
1036
for (u32 j = 0; j < MAX_TEXTURE_SAMPLERS; j++)
1037
{
1038
if (m_current_textures[j] && m_current_textures[j] == srv)
1039
{
1040
m_current_textures[j] = nullptr;
1041
m_context->PSSetShaderResources(j, 1, &m_current_textures[j]);
1042
}
1043
}
1044
}
1045
for (u32 i = num_rts; i < m_num_current_render_targets; i++)
1046
m_current_render_targets[i] = nullptr;
1047
m_num_current_render_targets = num_rts;
1048
if (!changed)
1049
return;
1050
1051
s_stats.num_render_passes++;
1052
1053
if (m_current_render_pass_flags & GPUPipeline::BindRenderTargetsAsImages)
1054
{
1055
std::array<ID3D11UnorderedAccessView*, MAX_RENDER_TARGETS> uavs;
1056
for (u32 i = 0; i < m_num_current_render_targets; i++)
1057
uavs[i] = m_current_render_targets[i]->GetD3DUAV();
1058
1059
if (!m_current_compute_shader)
1060
{
1061
m_context->OMSetRenderTargetsAndUnorderedAccessViews(
1062
0, nullptr, m_current_depth_target ? m_current_depth_target->GetD3DDSV() : nullptr, 0,
1063
m_num_current_render_targets, uavs.data(), nullptr);
1064
}
1065
else
1066
{
1067
m_context->CSSetUnorderedAccessViews(0, m_num_current_render_targets, uavs.data(), nullptr);
1068
}
1069
}
1070
else
1071
{
1072
std::array<ID3D11RenderTargetView*, MAX_RENDER_TARGETS> rtvs;
1073
for (u32 i = 0; i < m_num_current_render_targets; i++)
1074
rtvs[i] = m_current_render_targets[i]->GetD3DRTV();
1075
1076
m_context->OMSetRenderTargets(m_num_current_render_targets,
1077
(m_num_current_render_targets > 0) ? rtvs.data() : nullptr,
1078
m_current_depth_target ? m_current_depth_target->GetD3DDSV() : nullptr);
1079
}
1080
}
1081
1082
void D3D11Device::SetTextureSampler(u32 slot, GPUTexture* texture, GPUSampler* sampler)
1083
{
1084
ID3D11ShaderResourceView* T;
1085
if (texture)
1086
{
1087
static_cast<D3D11Texture*>(texture)->CommitClear(m_context.Get());
1088
T = static_cast<D3D11Texture*>(texture)->GetD3DSRV();
1089
}
1090
else
1091
{
1092
T = nullptr;
1093
}
1094
1095
ID3D11SamplerState* S = sampler ? static_cast<D3D11Sampler*>(sampler)->GetSamplerState() : nullptr;
1096
1097
// Runtime will null these if we don't...
1098
DebugAssert(!texture ||
1099
!((texture->IsRenderTarget() || texture->HasFlag(GPUTexture::Flags::AllowBindAsImage)) &&
1100
IsRenderTargetBound(static_cast<D3D11Texture*>(texture))) ||
1101
!(texture->IsDepthStencil() &&
1102
(!m_current_depth_target || m_current_depth_target != static_cast<D3D11Texture*>(texture))));
1103
1104
if (m_current_textures[slot] != T)
1105
{
1106
m_current_textures[slot] = T;
1107
m_context->PSSetShaderResources(slot, 1, &T);
1108
if (m_current_compute_shader)
1109
m_context->CSSetShaderResources(slot, 1, &T);
1110
}
1111
if (m_current_samplers[slot] != S)
1112
{
1113
m_current_samplers[slot] = S;
1114
m_context->PSSetSamplers(slot, 1, &S);
1115
if (m_current_compute_shader)
1116
m_context->CSSetSamplers(slot, 1, &S);
1117
}
1118
}
1119
1120
void D3D11Device::SetTextureBuffer(u32 slot, GPUTextureBuffer* buffer)
1121
{
1122
ID3D11ShaderResourceView* B = buffer ? static_cast<D3D11TextureBuffer*>(buffer)->GetSRV() : nullptr;
1123
if (m_current_textures[slot] != B)
1124
{
1125
m_current_textures[slot] = B;
1126
1127
// Compute doesn't support texture buffers, yet...
1128
m_context->PSSetShaderResources(slot, 1, &B);
1129
}
1130
}
1131
1132
void D3D11Device::UnbindTexture(D3D11Texture* tex)
1133
{
1134
if (const ID3D11ShaderResourceView* srv = tex->GetD3DSRV(); srv)
1135
{
1136
for (u32 i = 0; i < MAX_TEXTURE_SAMPLERS; i++)
1137
{
1138
if (m_current_textures[i] == srv)
1139
{
1140
m_current_textures[i] = nullptr;
1141
m_context->PSSetShaderResources(i, 1, &m_current_textures[i]);
1142
}
1143
}
1144
}
1145
1146
if (tex->IsRenderTarget() || tex->HasFlag(GPUTexture::Flags::AllowBindAsImage))
1147
{
1148
for (u32 i = 0; i < m_num_current_render_targets; i++)
1149
{
1150
if (m_current_render_targets[i] == tex)
1151
{
1152
DEV_LOG("Unbinding current RT");
1153
SetRenderTargets(nullptr, 0, m_current_depth_target);
1154
break;
1155
}
1156
}
1157
}
1158
else if (tex->IsDepthStencil() && m_current_depth_target == tex)
1159
{
1160
DEV_LOG("Unbinding current DS");
1161
SetRenderTargets(nullptr, 0, nullptr);
1162
}
1163
}
1164
1165
void D3D11Device::SetViewport(const GSVector4i rc)
1166
{
1167
const CD3D11_VIEWPORT vp(static_cast<float>(rc.left), static_cast<float>(rc.top), static_cast<float>(rc.width()),
1168
static_cast<float>(rc.height()), 0.0f, 1.0f);
1169
m_context->RSSetViewports(1, &vp);
1170
}
1171
1172
void D3D11Device::SetScissor(const GSVector4i rc)
1173
{
1174
alignas(16) D3D11_RECT drc;
1175
GSVector4i::store<true>(&drc, rc);
1176
m_context->RSSetScissorRects(1, &drc);
1177
}
1178
1179
void D3D11Device::Draw(u32 vertex_count, u32 base_vertex)
1180
{
1181
DebugAssert(!m_vertex_buffer.IsMapped() && !m_index_buffer.IsMapped() && !m_current_compute_shader);
1182
s_stats.num_draws++;
1183
m_context->Draw(vertex_count, base_vertex);
1184
}
1185
1186
void D3D11Device::DrawWithPushConstants(u32 vertex_count, u32 base_vertex, const void* push_constants,
1187
u32 push_constants_size)
1188
{
1189
PushUniformBuffer(push_constants, push_constants_size);
1190
Draw(vertex_count, base_vertex);
1191
}
1192
1193
void D3D11Device::DrawIndexed(u32 index_count, u32 base_index, u32 base_vertex)
1194
{
1195
DebugAssert(!m_vertex_buffer.IsMapped() && !m_index_buffer.IsMapped() && !m_current_compute_shader);
1196
s_stats.num_draws++;
1197
m_context->DrawIndexed(index_count, base_index, base_vertex);
1198
}
1199
1200
void D3D11Device::DrawIndexedWithPushConstants(u32 index_count, u32 base_index, u32 base_vertex,
1201
const void* push_constants, u32 push_constants_size)
1202
{
1203
PushUniformBuffer(push_constants, push_constants_size);
1204
DrawIndexed(index_count, base_index, base_vertex);
1205
}
1206
1207
void D3D11Device::Dispatch(u32 threads_x, u32 threads_y, u32 threads_z, u32 group_size_x, u32 group_size_y,
1208
u32 group_size_z)
1209
{
1210
DebugAssert(m_current_compute_shader);
1211
s_stats.num_draws++;
1212
1213
const u32 groups_x = threads_x / group_size_x;
1214
const u32 groups_y = threads_y / group_size_y;
1215
const u32 groups_z = threads_z / group_size_z;
1216
m_context->Dispatch(groups_x, groups_y, groups_z);
1217
}
1218
1219
void D3D11Device::DispatchWithPushConstants(u32 threads_x, u32 threads_y, u32 threads_z, u32 group_size_x,
1220
u32 group_size_y, u32 group_size_z, const void* push_constants,
1221
u32 push_constants_size)
1222
{
1223
PushUniformBuffer(push_constants, push_constants_size);
1224
Dispatch(threads_x, threads_y, threads_z, group_size_x, group_size_y, group_size_z);
1225
}
1226
1227