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

d3d_common.cpp (20367B)


      1 // SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
      2 // SPDX-License-Identifier: (GPL-3.0 OR PolyForm-Strict-1.0.0)
      3 
      4 #include "d3d_common.h"
      5 #include "gpu_device.h"
      6 
      7 #include "common/assert.h"
      8 #include "common/error.h"
      9 #include "common/file_system.h"
     10 #include "common/gsvector.h"
     11 #include "common/log.h"
     12 #include "common/string_util.h"
     13 
     14 #include "fmt/format.h"
     15 
     16 #include <d3d11.h>
     17 #include <d3dcompiler.h>
     18 #include <dxgi1_5.h>
     19 
     20 Log_SetChannel(D3DCommon);
     21 
     22 const char* D3DCommon::GetFeatureLevelString(D3D_FEATURE_LEVEL feature_level)
     23 {
     24   static constexpr std::array<std::tuple<D3D_FEATURE_LEVEL, const char*>, 11> feature_level_names = {{
     25     {D3D_FEATURE_LEVEL_1_0_CORE, "D3D_FEATURE_LEVEL_1_0_CORE"},
     26     {D3D_FEATURE_LEVEL_9_1, "D3D_FEATURE_LEVEL_9_1"},
     27     {D3D_FEATURE_LEVEL_9_2, "D3D_FEATURE_LEVEL_9_2"},
     28     {D3D_FEATURE_LEVEL_9_3, "D3D_FEATURE_LEVEL_9_3"},
     29     {D3D_FEATURE_LEVEL_10_0, "D3D_FEATURE_LEVEL_10_0"},
     30     {D3D_FEATURE_LEVEL_10_1, "D3D_FEATURE_LEVEL_10_1"},
     31     {D3D_FEATURE_LEVEL_11_0, "D3D_FEATURE_LEVEL_11_0"},
     32     {D3D_FEATURE_LEVEL_11_1, "D3D_FEATURE_LEVEL_11_1"},
     33     {D3D_FEATURE_LEVEL_12_0, "D3D_FEATURE_LEVEL_12_0"},
     34     {D3D_FEATURE_LEVEL_12_1, "D3D_FEATURE_LEVEL_12_1"},
     35     {D3D_FEATURE_LEVEL_12_2, "D3D_FEATURE_LEVEL_12_2"},
     36   }};
     37 
     38   for (const auto& [fl, name] : feature_level_names)
     39   {
     40     if (fl == feature_level)
     41       return name;
     42   }
     43 
     44   return "D3D_FEATURE_LEVEL_UNKNOWN";
     45 }
     46 
     47 const char* D3DCommon::GetFeatureLevelShaderModelString(D3D_FEATURE_LEVEL feature_level)
     48 {
     49   static constexpr std::array<std::tuple<D3D_FEATURE_LEVEL, const char*>, 4> feature_level_names = {{
     50     {D3D_FEATURE_LEVEL_10_0, "sm40"},
     51     {D3D_FEATURE_LEVEL_10_1, "sm41"},
     52     {D3D_FEATURE_LEVEL_11_0, "sm50"},
     53     {D3D_FEATURE_LEVEL_11_1, "sm50"},
     54   }};
     55 
     56   for (const auto& [fl, name] : feature_level_names)
     57   {
     58     if (fl == feature_level)
     59       return name;
     60   }
     61 
     62   return "unk";
     63 }
     64 
     65 D3D_FEATURE_LEVEL D3DCommon::GetDeviceMaxFeatureLevel(IDXGIAdapter1* adapter)
     66 {
     67   static constexpr std::array requested_feature_levels = {
     68     D3D_FEATURE_LEVEL_12_2, D3D_FEATURE_LEVEL_12_1, D3D_FEATURE_LEVEL_12_0, D3D_FEATURE_LEVEL_11_1,
     69     D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0};
     70 
     71   D3D_FEATURE_LEVEL max_supported_level = requested_feature_levels.back();
     72   HRESULT hr = D3D11CreateDevice(adapter, adapter ? D3D_DRIVER_TYPE_UNKNOWN : D3D_DRIVER_TYPE_HARDWARE, nullptr, 0,
     73                                  requested_feature_levels.data(), static_cast<UINT>(requested_feature_levels.size()),
     74                                  D3D11_SDK_VERSION, nullptr, &max_supported_level, nullptr);
     75   if (FAILED(hr))
     76     WARNING_LOG("D3D11CreateDevice() for getting max feature level failed: 0x{:08X}", static_cast<unsigned>(hr));
     77 
     78   return max_supported_level;
     79 }
     80 
     81 Microsoft::WRL::ComPtr<IDXGIFactory5> D3DCommon::CreateFactory(bool debug, Error* error)
     82 {
     83   UINT flags = 0;
     84   if (debug)
     85     flags |= DXGI_CREATE_FACTORY_DEBUG;
     86 
     87   Microsoft::WRL::ComPtr<IDXGIFactory5> factory;
     88   const HRESULT hr = CreateDXGIFactory2(flags, IID_PPV_ARGS(factory.GetAddressOf()));
     89   if (FAILED(hr))
     90     Error::SetHResult(error, "Failed to create DXGI factory: ", hr);
     91 
     92   return factory;
     93 }
     94 
     95 static std::string FixupDuplicateAdapterNames(const GPUDevice::AdapterInfoList& adapter_names, std::string adapter_name)
     96 {
     97   if (std::any_of(adapter_names.begin(), adapter_names.end(),
     98                   [&adapter_name](const GPUDevice::AdapterInfo& other) { return (adapter_name == other.name); }))
     99   {
    100     std::string original_adapter_name = std::move(adapter_name);
    101 
    102     u32 current_extra = 2;
    103     do
    104     {
    105       adapter_name = fmt::format("{} ({})", original_adapter_name.c_str(), current_extra);
    106       current_extra++;
    107     } while (
    108       std::any_of(adapter_names.begin(), adapter_names.end(),
    109                   [&adapter_name](const GPUDevice::AdapterInfo& other) { return (adapter_name == other.name); }));
    110   }
    111 
    112   return adapter_name;
    113 }
    114 
    115 GPUDevice::AdapterInfoList D3DCommon::GetAdapterInfoList()
    116 {
    117   GPUDevice::AdapterInfoList adapters;
    118 
    119   Microsoft::WRL::ComPtr<IDXGIFactory5> factory = CreateFactory(false, nullptr);
    120   if (!factory)
    121     return adapters;
    122 
    123   Microsoft::WRL::ComPtr<IDXGIAdapter1> adapter;
    124   for (u32 index = 0;; index++)
    125   {
    126     HRESULT hr = factory->EnumAdapters1(index, adapter.ReleaseAndGetAddressOf());
    127     if (hr == DXGI_ERROR_NOT_FOUND)
    128       break;
    129 
    130     if (FAILED(hr))
    131     {
    132       ERROR_LOG("IDXGIFactory2::EnumAdapters() returned {:08X}", static_cast<unsigned>(hr));
    133       continue;
    134     }
    135 
    136     // Unfortunately we can't get any properties such as feature level without creating the device.
    137     // So just assume a max of the D3D11 max across the board.
    138     GPUDevice::AdapterInfo ai;
    139     ai.name = FixupDuplicateAdapterNames(adapters, GetAdapterName(adapter.Get()));
    140     ai.max_texture_size = D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION;
    141     ai.max_multisamples = 8;
    142     ai.supports_sample_shading = true;
    143 
    144     Microsoft::WRL::ComPtr<IDXGIOutput> output;
    145     if (SUCCEEDED(hr = adapter->EnumOutputs(0, output.ReleaseAndGetAddressOf())))
    146     {
    147       UINT num_modes = 0;
    148       if (SUCCEEDED(hr = output->GetDisplayModeList(DXGI_FORMAT_R8G8B8A8_UNORM, 0, &num_modes, nullptr)))
    149       {
    150         std::vector<DXGI_MODE_DESC> dmodes(num_modes);
    151         if (SUCCEEDED(hr = output->GetDisplayModeList(DXGI_FORMAT_R8G8B8A8_UNORM, 0, &num_modes, dmodes.data())))
    152         {
    153           for (const DXGI_MODE_DESC& mode : dmodes)
    154           {
    155             ai.fullscreen_modes.push_back(GPUDevice::GetFullscreenModeString(
    156               mode.Width, mode.Height,
    157               static_cast<float>(mode.RefreshRate.Numerator) / static_cast<float>(mode.RefreshRate.Denominator)));
    158           }
    159         }
    160         else
    161         {
    162           ERROR_LOG("GetDisplayModeList() (2) failed: {:08X}", static_cast<unsigned>(hr));
    163         }
    164       }
    165       else
    166       {
    167         ERROR_LOG("GetDisplayModeList() failed: {:08X}", static_cast<unsigned>(hr));
    168       }
    169     }
    170     else
    171     {
    172       // Adapter may not have any outputs, don't spam the error log in this case.
    173       if (hr != DXGI_ERROR_NOT_FOUND)
    174         ERROR_LOG("EnumOutputs() failed: {:08X}", static_cast<unsigned>(hr));
    175     }
    176 
    177     adapters.push_back(std::move(ai));
    178   }
    179 
    180   return adapters;
    181 }
    182 
    183 bool D3DCommon::GetRequestedExclusiveFullscreenModeDesc(IDXGIFactory5* factory, const RECT& window_rect, u32 width,
    184                                                         u32 height, float refresh_rate, DXGI_FORMAT format,
    185                                                         DXGI_MODE_DESC* fullscreen_mode, IDXGIOutput** output)
    186 {
    187   // We need to find which monitor the window is located on.
    188   const GSVector4i client_rc_vec(window_rect.left, window_rect.top, window_rect.right, window_rect.bottom);
    189 
    190   // The window might be on a different adapter to which we are rendering.. so we have to enumerate them all.
    191   HRESULT hr;
    192   Microsoft::WRL::ComPtr<IDXGIOutput> first_output, intersecting_output;
    193 
    194   for (u32 adapter_index = 0; !intersecting_output; adapter_index++)
    195   {
    196     Microsoft::WRL::ComPtr<IDXGIAdapter1> adapter;
    197     hr = factory->EnumAdapters1(adapter_index, adapter.GetAddressOf());
    198     if (hr == DXGI_ERROR_NOT_FOUND)
    199       break;
    200     else if (FAILED(hr))
    201       continue;
    202 
    203     for (u32 output_index = 0;; output_index++)
    204     {
    205       Microsoft::WRL::ComPtr<IDXGIOutput> this_output;
    206       DXGI_OUTPUT_DESC output_desc;
    207       hr = adapter->EnumOutputs(output_index, this_output.GetAddressOf());
    208       if (hr == DXGI_ERROR_NOT_FOUND)
    209         break;
    210       else if (FAILED(hr) || FAILED(this_output->GetDesc(&output_desc)))
    211         continue;
    212 
    213       const GSVector4i output_rc(output_desc.DesktopCoordinates.left, output_desc.DesktopCoordinates.top,
    214                                  output_desc.DesktopCoordinates.right, output_desc.DesktopCoordinates.bottom);
    215       if (!client_rc_vec.rintersects(output_rc))
    216       {
    217         intersecting_output = std::move(this_output);
    218         break;
    219       }
    220 
    221       // Fallback to the first monitor.
    222       if (!first_output)
    223         first_output = std::move(this_output);
    224     }
    225   }
    226 
    227   if (!intersecting_output)
    228   {
    229     if (!first_output)
    230     {
    231       ERROR_LOG("No DXGI output found. Can't use exclusive fullscreen.");
    232       return false;
    233     }
    234 
    235     WARNING_LOG("No DXGI output found for window, using first.");
    236     intersecting_output = std::move(first_output);
    237   }
    238 
    239   DXGI_MODE_DESC request_mode = {};
    240   request_mode.Width = width;
    241   request_mode.Height = height;
    242   request_mode.Format = format;
    243   request_mode.RefreshRate.Numerator = static_cast<UINT>(std::floor(refresh_rate * 1000.0f));
    244   request_mode.RefreshRate.Denominator = 1000u;
    245 
    246   if (FAILED(hr = intersecting_output->FindClosestMatchingMode(&request_mode, fullscreen_mode, nullptr)) ||
    247       request_mode.Format != format)
    248   {
    249     ERROR_LOG("Failed to find closest matching mode, hr={:08X}", static_cast<unsigned>(hr));
    250     return false;
    251   }
    252 
    253   *output = intersecting_output.Get();
    254   intersecting_output->AddRef();
    255   return true;
    256 }
    257 
    258 Microsoft::WRL::ComPtr<IDXGIAdapter1> D3DCommon::GetAdapterByName(IDXGIFactory5* factory, std::string_view name)
    259 {
    260   if (name.empty())
    261     return {};
    262 
    263   // This might seem a bit odd to cache the names.. but there's a method to the madness.
    264   // We might have two GPUs with the same name... :)
    265   GPUDevice::AdapterInfoList adapters;
    266 
    267   Microsoft::WRL::ComPtr<IDXGIAdapter1> adapter;
    268   for (u32 index = 0;; index++)
    269   {
    270     const HRESULT hr = factory->EnumAdapters1(index, adapter.ReleaseAndGetAddressOf());
    271     if (hr == DXGI_ERROR_NOT_FOUND)
    272       break;
    273 
    274     if (FAILED(hr))
    275     {
    276       ERROR_LOG("IDXGIFactory2::EnumAdapters() returned {:08X}", static_cast<unsigned>(hr));
    277       continue;
    278     }
    279 
    280     std::string adapter_name = FixupDuplicateAdapterNames(adapters, GetAdapterName(adapter.Get()));
    281     if (adapter_name == name)
    282     {
    283       VERBOSE_LOG("Found adapter '{}'", adapter_name);
    284       return adapter;
    285     }
    286 
    287     GPUDevice::AdapterInfo ai;
    288     ai.name = std::move(adapter_name);
    289     adapters.push_back(std::move(ai));
    290   }
    291 
    292   ERROR_LOG("Adapter '{}' not found.", name);
    293   return {};
    294 }
    295 
    296 Microsoft::WRL::ComPtr<IDXGIAdapter1> D3DCommon::GetFirstAdapter(IDXGIFactory5* factory)
    297 {
    298   Microsoft::WRL::ComPtr<IDXGIAdapter1> adapter;
    299   HRESULT hr = factory->EnumAdapters1(0, adapter.GetAddressOf());
    300   if (FAILED(hr))
    301     ERROR_LOG("IDXGIFactory2::EnumAdapters() for first adapter returned {:08X}", static_cast<unsigned>(hr));
    302 
    303   return adapter;
    304 }
    305 
    306 Microsoft::WRL::ComPtr<IDXGIAdapter1> D3DCommon::GetChosenOrFirstAdapter(IDXGIFactory5* factory, std::string_view name)
    307 {
    308   Microsoft::WRL::ComPtr<IDXGIAdapter1> adapter = GetAdapterByName(factory, name);
    309   if (!adapter)
    310     adapter = GetFirstAdapter(factory);
    311 
    312   return adapter;
    313 }
    314 
    315 std::string D3DCommon::GetAdapterName(IDXGIAdapter1* adapter)
    316 {
    317   std::string ret;
    318 
    319   DXGI_ADAPTER_DESC1 desc;
    320   HRESULT hr = adapter->GetDesc1(&desc);
    321   if (SUCCEEDED(hr))
    322   {
    323     ret = StringUtil::WideStringToUTF8String(desc.Description);
    324   }
    325   else
    326   {
    327     ERROR_LOG("IDXGIAdapter1::GetDesc() returned {:08X}", static_cast<unsigned>(hr));
    328   }
    329 
    330   if (ret.empty())
    331     ret = "(Unknown)";
    332 
    333   return ret;
    334 }
    335 
    336 std::string D3DCommon::GetDriverVersionFromLUID(const LUID& luid)
    337 {
    338   std::string ret;
    339 
    340   HKEY hKey;
    341   if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\DirectX", 0, KEY_READ, &hKey) == ERROR_SUCCESS)
    342   {
    343     DWORD max_key_len = 0, adapter_count = 0;
    344     if (RegQueryInfoKeyW(hKey, nullptr, nullptr, nullptr, &adapter_count, &max_key_len, nullptr, nullptr, nullptr,
    345                          nullptr, nullptr, nullptr) == ERROR_SUCCESS)
    346     {
    347       std::vector<WCHAR> current_name(max_key_len + 1);
    348       for (DWORD i = 0; i < adapter_count; ++i)
    349       {
    350         DWORD subKeyLength = static_cast<DWORD>(current_name.size());
    351         if (RegEnumKeyExW(hKey, i, current_name.data(), &subKeyLength, nullptr, nullptr, nullptr, nullptr) ==
    352             ERROR_SUCCESS)
    353         {
    354           LUID current_luid = {};
    355           DWORD current_luid_size = sizeof(uint64_t);
    356           if (RegGetValueW(hKey, current_name.data(), L"AdapterLuid", RRF_RT_QWORD, nullptr, &current_luid,
    357                            &current_luid_size) == ERROR_SUCCESS &&
    358               current_luid.HighPart == luid.HighPart && current_luid.LowPart == luid.LowPart)
    359           {
    360             LARGE_INTEGER driver_version = {};
    361             DWORD driver_version_size = sizeof(driver_version);
    362             if (RegGetValueW(hKey, current_name.data(), L"DriverVersion", RRF_RT_QWORD, nullptr, &driver_version,
    363                              &driver_version_size) == ERROR_SUCCESS)
    364             {
    365               WORD nProduct = HIWORD(driver_version.HighPart);
    366               WORD nVersion = LOWORD(driver_version.HighPart);
    367               WORD nSubVersion = HIWORD(driver_version.LowPart);
    368               WORD nBuild = LOWORD(driver_version.LowPart);
    369               ret = fmt::format("{}.{}.{}.{}", nProduct, nVersion, nSubVersion, nBuild);
    370             }
    371           }
    372         }
    373       }
    374     }
    375 
    376     RegCloseKey(hKey);
    377   }
    378 
    379   return ret;
    380 }
    381 
    382 u32 D3DCommon::GetShaderModelForFeatureLevel(D3D_FEATURE_LEVEL feature_level)
    383 {
    384   switch (feature_level)
    385   {
    386     case D3D_FEATURE_LEVEL_10_0:
    387       return 40;
    388 
    389     case D3D_FEATURE_LEVEL_10_1:
    390       return 41;
    391 
    392     case D3D_FEATURE_LEVEL_11_0:
    393     case D3D_FEATURE_LEVEL_11_1:
    394     default:
    395       return 50;
    396   }
    397 }
    398 
    399 std::optional<DynamicHeapArray<u8>> D3DCommon::CompileShader(u32 shader_model, bool debug_device, GPUShaderStage stage,
    400                                                              std::string_view source, const char* entry_point,
    401                                                              Error* error)
    402 {
    403   const char* target;
    404   switch (shader_model)
    405   {
    406     case 40:
    407     {
    408       static constexpr std::array<const char*, static_cast<u32>(GPUShaderStage::MaxCount)> targets = {
    409         {"vs_4_0", "ps_4_0", "gs_4_0", "cs_4_0"}};
    410       target = targets[static_cast<int>(stage)];
    411     }
    412     break;
    413 
    414     case 41:
    415     {
    416       static constexpr std::array<const char*, static_cast<u32>(GPUShaderStage::MaxCount)> targets = {
    417         {"vs_4_1", "ps_4_1", "gs_4_0", "cs_4_1"}};
    418       target = targets[static_cast<int>(stage)];
    419     }
    420     break;
    421 
    422     case 50:
    423     {
    424       static constexpr std::array<const char*, static_cast<u32>(GPUShaderStage::MaxCount)> targets = {
    425         {"vs_5_0", "ps_5_0", "gs_5_0", "cs_5_0"}};
    426       target = targets[static_cast<int>(stage)];
    427     }
    428     break;
    429 
    430     default:
    431       Error::SetStringFmt(error, "Unknown shader model: {}", shader_model);
    432       return {};
    433   }
    434 
    435   static constexpr UINT flags_non_debug = D3DCOMPILE_OPTIMIZATION_LEVEL3;
    436   static constexpr UINT flags_debug = D3DCOMPILE_SKIP_OPTIMIZATION | D3DCOMPILE_DEBUG;
    437 
    438   Microsoft::WRL::ComPtr<ID3DBlob> blob;
    439   Microsoft::WRL::ComPtr<ID3DBlob> error_blob;
    440   const HRESULT hr =
    441     D3DCompile(source.data(), source.size(), "0", nullptr, nullptr, entry_point, target,
    442                debug_device ? flags_debug : flags_non_debug, 0, blob.GetAddressOf(), error_blob.GetAddressOf());
    443 
    444   std::string_view error_string;
    445   if (error_blob)
    446   {
    447     error_string =
    448       std::string_view(static_cast<const char*>(error_blob->GetBufferPointer()), error_blob->GetBufferSize());
    449   }
    450 
    451   if (FAILED(hr))
    452   {
    453     ERROR_LOG("Failed to compile '{}':\n{}", target, error_string);
    454     GPUDevice::DumpBadShader(source, error_string);
    455     Error::SetHResult(error, "D3DCompile() failed: ", hr);
    456     return {};
    457   }
    458 
    459   if (!error_string.empty())
    460     WARNING_LOG("'{}' compiled with warnings:\n{}", target, error_string);
    461 
    462   error_blob.Reset();
    463 
    464   return DynamicHeapArray<u8>(static_cast<const u8*>(blob->GetBufferPointer()), blob->GetBufferSize());
    465 }
    466 
    467 static constexpr std::array<D3DCommon::DXGIFormatMapping, static_cast<int>(GPUTexture::Format::MaxCount)>
    468   s_format_mapping = {{
    469     // clang-format off
    470   // d3d_format                    srv_format                           rtv_format                      dsv_format
    471   {DXGI_FORMAT_UNKNOWN,            DXGI_FORMAT_UNKNOWN,                 DXGI_FORMAT_UNKNOWN,                  DXGI_FORMAT_UNKNOWN               }, // Unknown
    472   {DXGI_FORMAT_R8G8B8A8_UNORM,     DXGI_FORMAT_R8G8B8A8_UNORM,          DXGI_FORMAT_R8G8B8A8_UNORM,           DXGI_FORMAT_UNKNOWN               }, // RGBA8
    473   {DXGI_FORMAT_B8G8R8A8_UNORM,     DXGI_FORMAT_B8G8R8A8_UNORM,          DXGI_FORMAT_B8G8R8A8_UNORM,           DXGI_FORMAT_UNKNOWN               }, // BGRA8
    474   {DXGI_FORMAT_B5G6R5_UNORM,       DXGI_FORMAT_B5G6R5_UNORM,            DXGI_FORMAT_B5G6R5_UNORM,             DXGI_FORMAT_UNKNOWN               }, // RGB565
    475   {DXGI_FORMAT_B5G5R5A1_UNORM,     DXGI_FORMAT_B5G5R5A1_UNORM,          DXGI_FORMAT_B5G5R5A1_UNORM,           DXGI_FORMAT_UNKNOWN               }, // RGBA5551
    476   {DXGI_FORMAT_R8_UNORM,           DXGI_FORMAT_R8_UNORM,                DXGI_FORMAT_R8_UNORM,                 DXGI_FORMAT_UNKNOWN               }, // R8
    477   {DXGI_FORMAT_R16_TYPELESS,       DXGI_FORMAT_R16_UNORM,               DXGI_FORMAT_UNKNOWN,                  DXGI_FORMAT_D16_UNORM             }, // D16
    478   {DXGI_FORMAT_R24G8_TYPELESS,     DXGI_FORMAT_R24_UNORM_X8_TYPELESS,   DXGI_FORMAT_R24_UNORM_X8_TYPELESS,    DXGI_FORMAT_D24_UNORM_S8_UINT     }, // D24S8
    479   {DXGI_FORMAT_R32_TYPELESS,       DXGI_FORMAT_R32_FLOAT,               DXGI_FORMAT_R32_FLOAT,                DXGI_FORMAT_D32_FLOAT             }, // D32F
    480   {DXGI_FORMAT_R32G8X24_TYPELESS,  DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS,DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS, DXGI_FORMAT_D32_FLOAT_S8X24_UINT  }, // D32FS8
    481   {DXGI_FORMAT_R16_UNORM,          DXGI_FORMAT_R16_UNORM,               DXGI_FORMAT_R16_UNORM,                DXGI_FORMAT_UNKNOWN               }, // R16
    482   {DXGI_FORMAT_R16_SINT,           DXGI_FORMAT_R16_SINT,                DXGI_FORMAT_R16_SINT,                 DXGI_FORMAT_UNKNOWN               }, // R16I
    483   {DXGI_FORMAT_R16_UINT,           DXGI_FORMAT_R16_UINT,                DXGI_FORMAT_R16_UINT,                 DXGI_FORMAT_UNKNOWN               }, // R16U
    484   {DXGI_FORMAT_R16_FLOAT,          DXGI_FORMAT_R16_FLOAT,               DXGI_FORMAT_R16_FLOAT,                DXGI_FORMAT_UNKNOWN               }, // R16F
    485   {DXGI_FORMAT_R32_SINT,           DXGI_FORMAT_R32_SINT,                DXGI_FORMAT_R32_SINT,                 DXGI_FORMAT_UNKNOWN               }, // R32I
    486   {DXGI_FORMAT_R32_UINT,           DXGI_FORMAT_R32_UINT,                DXGI_FORMAT_R32_UINT,                 DXGI_FORMAT_UNKNOWN               }, // R32U
    487   {DXGI_FORMAT_R32_FLOAT,          DXGI_FORMAT_R32_FLOAT,               DXGI_FORMAT_R32_FLOAT,                DXGI_FORMAT_UNKNOWN               }, // R32F
    488   {DXGI_FORMAT_R8G8_UNORM,         DXGI_FORMAT_R8G8_UNORM,              DXGI_FORMAT_R8G8_UNORM,               DXGI_FORMAT_UNKNOWN               }, // RG8
    489   {DXGI_FORMAT_R16G16_UNORM,       DXGI_FORMAT_R16G16_UNORM,            DXGI_FORMAT_R16G16_UNORM,             DXGI_FORMAT_UNKNOWN               }, // RG16
    490   {DXGI_FORMAT_R16G16_FLOAT,       DXGI_FORMAT_R16G16_FLOAT,            DXGI_FORMAT_R16G16_FLOAT,             DXGI_FORMAT_UNKNOWN               }, // RG16F
    491   {DXGI_FORMAT_R32G32_FLOAT,       DXGI_FORMAT_R32G32_FLOAT,            DXGI_FORMAT_R32G32_FLOAT,             DXGI_FORMAT_UNKNOWN               }, // RG32F
    492   {DXGI_FORMAT_R16G16B16A16_UNORM, DXGI_FORMAT_R16G16B16A16_UNORM,      DXGI_FORMAT_R16G16B16A16_UNORM,       DXGI_FORMAT_UNKNOWN               }, // RGBA16
    493   {DXGI_FORMAT_R16G16B16A16_FLOAT, DXGI_FORMAT_R16G16B16A16_FLOAT,      DXGI_FORMAT_R16G16B16A16_FLOAT,       DXGI_FORMAT_UNKNOWN               }, // RGBA16F
    494   {DXGI_FORMAT_R32G32B32A32_FLOAT, DXGI_FORMAT_R32G32B32A32_FLOAT,      DXGI_FORMAT_R32G32B32A32_FLOAT,       DXGI_FORMAT_UNKNOWN               }, // RGBA32F
    495   {DXGI_FORMAT_R10G10B10A2_UNORM,  DXGI_FORMAT_R10G10B10A2_UNORM,       DXGI_FORMAT_R10G10B10A2_UNORM,        DXGI_FORMAT_UNKNOWN               }, // RGB10A2
    496     // clang-format on
    497   }};
    498 
    499 const D3DCommon::DXGIFormatMapping& D3DCommon::GetFormatMapping(GPUTexture::Format format)
    500 {
    501   DebugAssert(static_cast<u8>(format) < s_format_mapping.size());
    502   return s_format_mapping[static_cast<u8>(format)];
    503 }
    504 
    505 GPUTexture::Format D3DCommon::GetFormatForDXGIFormat(DXGI_FORMAT format)
    506 {
    507   for (u32 i = 0; i < static_cast<u32>(GPUTexture::Format::MaxCount); i++)
    508   {
    509     if (s_format_mapping[i].resource_format == format)
    510       return static_cast<GPUTexture::Format>(i);
    511   }
    512 
    513   return GPUTexture::Format::Unknown;
    514 }