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