duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.git
Log | Files | Refs | README | LICENSE

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 }