From dda2c858599761e524605be3cc1fd3ca2c56287d Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sun, 13 Aug 2023 01:16:00 +1000 Subject: [PATCH] D3D12 --- src/common/common.props | 2 +- src/common/http_downloader_winhttp.cpp | 2 - src/common/win32_progress_callback.cpp | 1 - src/core/core.props | 8 +- src/core/core.vcxproj | 35 +- src/core/core.vcxproj.filters | 104 +- src/core/gpu/d3d11_device.cpp | 398 +--- src/core/gpu/d3d11_device.h | 7 +- src/core/gpu/d3d12/context.cpp | 556 ----- src/core/gpu/d3d12/context.h | 156 -- .../gpu/d3d12/descriptor_heap_manager.cpp | 116 - src/core/gpu/d3d12/descriptor_heap_manager.h | 84 - src/core/gpu/d3d12/shader_cache.cpp | 520 ----- src/core/gpu/d3d12/shader_cache.h | 122 - src/core/gpu/d3d12/staging_texture.cpp | 239 -- src/core/gpu/d3d12/staging_texture.h | 64 - src/core/gpu/d3d12/texture.cpp | 466 ---- src/core/gpu/d3d12/texture.h | 86 - src/core/gpu/d3d12/util.cpp | 392 ---- src/core/gpu/d3d12_builders.cpp | 352 +++ .../gpu/{d3d12/util.h => d3d12_builders.h} | 86 +- .../gpu/d3d12_descriptor_heap_manager.cpp | 154 ++ src/core/gpu/d3d12_descriptor_heap_manager.h | 244 ++ src/core/gpu/d3d12_device.cpp | 2028 +++++++++++++++++ src/core/gpu/d3d12_device.h | 343 +++ src/core/gpu/d3d12_gpu_device.cpp | 1031 --------- src/core/gpu/d3d12_gpu_device.h | 107 - src/core/gpu/d3d12_pipeline.cpp | 199 ++ src/core/gpu/d3d12_pipeline.h | 63 + ...eam_buffer.cpp => d3d12_stream_buffer.cpp} | 71 +- .../stream_buffer.h => d3d12_stream_buffer.h} | 16 +- src/core/gpu/d3d12_texture.cpp | 941 ++++++++ src/core/gpu/d3d12_texture.h | 166 ++ src/core/gpu/d3d_common.cpp | 388 ++++ src/core/gpu/d3d_common.h | 55 + src/core/gpu/d3d_shaders.h | 930 -------- src/core/gpu/gl/context_egl.cpp | 30 +- src/core/gpu/gl/context_wgl.cpp | 4 +- src/core/gpu/gpu_device.cpp | 6 +- src/core/gpu/imgui_impl_dx12.cpp | 545 ----- src/core/gpu/imgui_impl_dx12.h | 14 - src/core/gpu/postprocessing_shadergen.cpp | 4 +- src/core/gpu/vulkan_device.cpp | 12 +- src/core/gpu/vulkan_texture.cpp | 68 +- src/core/gpu/window_info.cpp | 2 - src/core/gpu/window_info.h | 2 + src/core/gpu_hw.cpp | 7 + src/core/gpu_hw_d3d12.cpp | 1171 ---------- src/core/gpu_hw_d3d12.h | 107 - src/core/gpu_hw_shadergen.cpp | 2 +- src/duckstation-qt/displaysettingswidget.cpp | 4 +- src/frontend-common/cubeb_audio_stream.cpp | 1 - 52 files changed, 5223 insertions(+), 7288 deletions(-) delete mode 100644 src/core/gpu/d3d12/context.cpp delete mode 100644 src/core/gpu/d3d12/context.h delete mode 100644 src/core/gpu/d3d12/descriptor_heap_manager.cpp delete mode 100644 src/core/gpu/d3d12/descriptor_heap_manager.h delete mode 100644 src/core/gpu/d3d12/shader_cache.cpp delete mode 100644 src/core/gpu/d3d12/shader_cache.h delete mode 100644 src/core/gpu/d3d12/staging_texture.cpp delete mode 100644 src/core/gpu/d3d12/staging_texture.h delete mode 100644 src/core/gpu/d3d12/texture.cpp delete mode 100644 src/core/gpu/d3d12/texture.h delete mode 100644 src/core/gpu/d3d12/util.cpp create mode 100644 src/core/gpu/d3d12_builders.cpp rename src/core/gpu/{d3d12/util.h => d3d12_builders.h} (68%) create mode 100644 src/core/gpu/d3d12_descriptor_heap_manager.cpp create mode 100644 src/core/gpu/d3d12_descriptor_heap_manager.h create mode 100644 src/core/gpu/d3d12_device.cpp create mode 100644 src/core/gpu/d3d12_device.h delete mode 100644 src/core/gpu/d3d12_gpu_device.cpp delete mode 100644 src/core/gpu/d3d12_gpu_device.h create mode 100644 src/core/gpu/d3d12_pipeline.cpp create mode 100644 src/core/gpu/d3d12_pipeline.h rename src/core/gpu/{d3d12/stream_buffer.cpp => d3d12_stream_buffer.cpp} (80%) rename src/core/gpu/{d3d12/stream_buffer.h => d3d12_stream_buffer.h} (91%) create mode 100644 src/core/gpu/d3d12_texture.cpp create mode 100644 src/core/gpu/d3d12_texture.h create mode 100644 src/core/gpu/d3d_common.cpp create mode 100644 src/core/gpu/d3d_common.h delete mode 100644 src/core/gpu/d3d_shaders.h delete mode 100644 src/core/gpu/imgui_impl_dx12.cpp delete mode 100644 src/core/gpu/imgui_impl_dx12.h delete mode 100644 src/core/gpu_hw_d3d12.cpp delete mode 100644 src/core/gpu_hw_d3d12.h diff --git a/src/common/common.props b/src/common/common.props index 8c15b81fa..c76be2c33 100644 --- a/src/common/common.props +++ b/src/common/common.props @@ -11,7 +11,7 @@ $(RootBuildDir)glad\glad.lib;$(RootBuildDir)glslang\glslang.lib;%(AdditionalDependencies) - $(RootBuildDir)zstd\zstd.lib;$(RootBuildDir)fmt\fmt.lib;$(RootBuildDir)zlib\zlib.lib;$(RootBuildDir)minizip\minizip.lib;$(RootBuildDir)lzma\lzma.lib;d3dcompiler.lib;d3d11.lib;%(AdditionalDependencies) + $(RootBuildDir)zstd\zstd.lib;$(RootBuildDir)fmt\fmt.lib;$(RootBuildDir)zlib\zlib.lib;$(RootBuildDir)minizip\minizip.lib;$(RootBuildDir)lzma\lzma.lib;Comctl32.lib;winhttp.lib;%(AdditionalDependencies) diff --git a/src/common/http_downloader_winhttp.cpp b/src/common/http_downloader_winhttp.cpp index b15992e49..f746daedf 100644 --- a/src/common/http_downloader_winhttp.cpp +++ b/src/common/http_downloader_winhttp.cpp @@ -10,8 +10,6 @@ #include Log_SetChannel(HTTPDownloaderWinHttp); -#pragma comment(lib, "winhttp.lib") - namespace Common { HTTPDownloaderWinHttp::HTTPDownloaderWinHttp() : HTTPDownloader() {} diff --git a/src/common/win32_progress_callback.cpp b/src/common/win32_progress_callback.cpp index 264da5334..cd61d22d7 100644 --- a/src/common/win32_progress_callback.cpp +++ b/src/common/win32_progress_callback.cpp @@ -4,7 +4,6 @@ #include "win32_progress_callback.h" #include "common/log.h" #include -#pragma comment(lib, "Comctl32.lib") Log_SetChannel(Win32ProgressCallback); Win32ProgressCallback::Win32ProgressCallback() : BaseProgressCallback() diff --git a/src/core/core.props b/src/core/core.props index 7f3b8c7d7..d590ed9b8 100644 --- a/src/core/core.props +++ b/src/core/core.props @@ -9,7 +9,7 @@ WITH_RECOMPILER=1;%(PreprocessorDefinitions) WITH_MMAP_FASTMEM=1;%(PreprocessorDefinitions) - $(SolutionDir)dep\tinyxml2\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xxhash\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\rcheevos\include;$(SolutionDir)dep\rapidjson\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) + $(SolutionDir)dep\tinyxml2\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xxhash\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\d3d12ma\include;$(SolutionDir)dep\rcheevos\include;$(SolutionDir)dep\rapidjson\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) $(SolutionDir)dep\rainterface;%(AdditionalIncludeDirectories) $(SolutionDir)dep\xbyak\xbyak;%(AdditionalIncludeDirectories) @@ -19,9 +19,11 @@ - $(RootBuildDir)tinyxml2\tinyxml2.lib;$(RootBuildDir)rcheevos\rcheevos.lib;$(RootBuildDir)imgui\imgui.lib;$(RootBuildDir)stb\stb.lib;$(RootBuildDir)xxhash\xxhash.lib;$(RootBuildDir)zlib\zlib.lib;$(RootBuildDir)util\util.lib;$(RootBuildDir)common\common.lib;%(AdditionalDependencies) - $(RootBuildDir)rainterface\rainterface.lib;%(AdditionalDependencies) + $(RootBuildDir)tinyxml2\tinyxml2.lib;$(RootBuildDir)rcheevos\rcheevos.lib;$(RootBuildDir)imgui\imgui.lib;$(RootBuildDir)stb\stb.lib;$(RootBuildDir)xxhash\xxhash.lib;$(RootBuildDir)zlib\zlib.lib;$(RootBuildDir)d3d12ma\d3d12ma.lib;$(RootBuildDir)util\util.lib;$(RootBuildDir)common\common.lib;d3d11.lib;d3d12.lib;d3dcompiler.lib;dxgi.lib;Dwmapi.lib;%(AdditionalDependencies) + $(RootBuildDir)rainterface\rainterface.lib;opengl32.lib;%(AdditionalDependencies) $(RootBuildDir)vixl\vixl.lib;%(AdditionalDependencies) + + diff --git a/src/core/core.vcxproj b/src/core/core.vcxproj index 6086d6b2f..3cdc15def 100644 --- a/src/core/core.vcxproj +++ b/src/core/core.vcxproj @@ -34,14 +34,13 @@ - - - - - - - - + + + + + + + true @@ -61,7 +60,6 @@ - @@ -81,7 +79,6 @@ - @@ -144,15 +141,13 @@ - - - - - - - - - + + + + + + + true @@ -175,7 +170,6 @@ - @@ -196,7 +190,6 @@ - diff --git a/src/core/core.vcxproj.filters b/src/core/core.vcxproj.filters index 68d13d157..d000e2518 100644 --- a/src/core/core.vcxproj.filters +++ b/src/core/core.vcxproj.filters @@ -52,40 +52,18 @@ - gpu - - gpu - gpu gpu - - gpu\d3d12 - - - gpu\d3d12 - - - gpu\d3d12 - - - gpu\d3d12 - - - gpu\d3d12 - - - gpu\d3d12 - gpu\gl @@ -116,9 +94,6 @@ gpu - - gpu - gpu @@ -134,9 +109,6 @@ gpu - - gpu\d3d12 - gpu @@ -164,6 +136,27 @@ gpu + + gpu + + + gpu + + + gpu + + + gpu + + + gpu + + + gpu + + + gpu + @@ -219,7 +212,6 @@ - @@ -230,33 +222,12 @@ gpu - - gpu - gpu gpu - - gpu\d3d12 - - - gpu\d3d12 - - - gpu\d3d12 - - - gpu\d3d12 - - - gpu\d3d12 - - - gpu\d3d12 - gpu\gl @@ -290,15 +261,9 @@ gpu - - gpu - gpu - - gpu - gpu @@ -314,9 +279,6 @@ gpu - - gpu\d3d12 - gpu @@ -347,14 +309,32 @@ gpu + + gpu + + + gpu + + + gpu + + + gpu + + + gpu + + + gpu + + + gpu + {7b18fac1-ee2b-4816-8537-eb8f32d40ada} - - {17c2f89b-5cf4-4d3a-9343-a03c1c668b85} - {e2aeacc6-65aa-4110-bef9-5f3e1fc0470e} diff --git a/src/core/gpu/d3d11_device.cpp b/src/core/gpu/d3d11_device.cpp index d90ea6a59..3fe34f791 100644 --- a/src/core/gpu/d3d11_device.cpp +++ b/src/core/gpu/d3d11_device.cpp @@ -4,6 +4,7 @@ #include "d3d11_device.h" #include "../host_settings.h" #include "../shader_cache_version.h" +#include "d3d_common.h" #include "postprocessing_chain.h" // TODO: Remove me #include "common/align.h" @@ -22,16 +23,15 @@ Log_SetChannel(D3D11Device); -#pragma comment(lib, "d3d11.lib") -#pragma comment(lib, "dxgi.lib") +// We need to synchronize instance creation because of adapter enumeration from the UI thread. +static std::mutex s_instance_mutex; static constexpr std::array(GPUTexture::Format::MaxCount)> s_dxgi_mapping = { {DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_B5G6R5_UNORM, DXGI_FORMAT_B5G5R5A1_UNORM, DXGI_FORMAT_R8_UNORM, DXGI_FORMAT_D16_UNORM}}; static constexpr std::array s_clear_color = {}; - -static unsigned s_next_bad_shader_id = 1; +static constexpr GPUTexture::Format s_swap_chain_format = GPUTexture::Format::RGBA8; static void SetD3DDebugObjectName(ID3D11DeviceChild* obj, const std::string_view& name) { @@ -367,70 +367,44 @@ void D3D11Device::SetVSync(bool enabled) bool D3D11Device::CreateDevice(const std::string_view& adapter) { + std::unique_lock lock(s_instance_mutex); + UINT create_flags = 0; if (m_debug_device) create_flags |= D3D11_CREATE_DEVICE_DEBUG; - ComPtr temp_dxgi_factory; - HRESULT hr = CreateDXGIFactory(IID_PPV_ARGS(temp_dxgi_factory.GetAddressOf())); - if (FAILED(hr)) - { - Log_ErrorPrintf("Failed to create DXGI factory: 0x%08X", hr); + m_dxgi_factory = D3DCommon::CreateFactory(m_debug_device); + if (!m_dxgi_factory) return false; - } - u32 adapter_index; - if (!adapter.empty()) - { - AdapterAndModeList adapter_info(GetAdapterAndModeList(temp_dxgi_factory.Get())); - for (adapter_index = 0; adapter_index < static_cast(adapter_info.adapter_names.size()); adapter_index++) - { - if (adapter == adapter_info.adapter_names[adapter_index]) - break; - } - if (adapter_index == static_cast(adapter_info.adapter_names.size())) - { - // TODO: Log_Fmt - Log_WarningPrintf( - fmt::format("Could not find adapter '{}', using first ({})", adapter, adapter_info.adapter_names[0]).c_str()); - adapter_index = 0; - } - } - else - { - Log_InfoPrintf("No adapter selected, using first."); - adapter_index = 0; - } - - ComPtr dxgi_adapter; - hr = temp_dxgi_factory->EnumAdapters(adapter_index, dxgi_adapter.GetAddressOf()); - if (FAILED(hr)) - Log_WarningPrintf("Failed to enumerate adapter %u, using default", adapter_index); + ComPtr dxgi_adapter = D3DCommon::GetAdapterByName(m_dxgi_factory.Get(), adapter); static constexpr std::array requested_feature_levels = { {D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0}}; - ComPtr device; - ComPtr context; - hr = + ComPtr temp_device; + ComPtr temp_context; + HRESULT hr = D3D11CreateDevice(dxgi_adapter.Get(), dxgi_adapter ? D3D_DRIVER_TYPE_UNKNOWN : D3D_DRIVER_TYPE_HARDWARE, nullptr, create_flags, requested_feature_levels.data(), static_cast(requested_feature_levels.size()), - D3D11_SDK_VERSION, device.GetAddressOf(), nullptr, context.GetAddressOf()); - // we re-grab these later, see below - dxgi_adapter.Reset(); - temp_dxgi_factory.Reset(); + D3D11_SDK_VERSION, temp_device.GetAddressOf(), nullptr, temp_context.GetAddressOf()); if (FAILED(hr)) { Log_ErrorPrintf("Failed to create D3D device: 0x%08X", hr); return false; } - else if (FAILED(hr = device.As(&m_device)) || FAILED(hr = context.As(&m_context))) + else if (FAILED(hr = temp_device.As(&m_device)) || FAILED(hr = temp_context.As(&m_context))) { Log_ErrorPrintf("Failed to get D3D11.1 device: 0x%08X", hr); return false; } + // we re-grab these later, see below + dxgi_adapter.Reset(); + temp_context.Reset(); + temp_device.Reset(); + if (m_debug_device && IsDebuggerPresent()) { ComPtr info; @@ -447,31 +421,17 @@ bool D3D11Device::CreateDevice(const std::string_view& adapter) m_context.As(&m_annotation); #endif - // we need the specific factory for the device, otherwise MakeWindowAssociation() is flaky. ComPtr dxgi_device; - if (FAILED(m_device.As(&dxgi_device)) || FAILED(dxgi_device->GetParent(IID_PPV_ARGS(dxgi_adapter.GetAddressOf()))) || - FAILED(dxgi_adapter->GetParent(IID_PPV_ARGS(m_dxgi_factory.GetAddressOf())))) - { - Log_WarningPrint("Failed to get parent adapter/device/factory"); - return false; - } - ComPtr dxgi_device1; - if (SUCCEEDED(dxgi_device.As(&dxgi_device1))) - dxgi_device1->SetMaximumFrameLatency(1); + if (SUCCEEDED(m_device.As(&dxgi_device)) && + SUCCEEDED(dxgi_device->GetParent(IID_PPV_ARGS(dxgi_adapter.GetAddressOf())))) + Log_InfoPrintf("D3D Adapter: %s", D3DCommon::GetAdapterName(dxgi_adapter.Get()).c_str()); + else + Log_ErrorPrint("Failed to obtain D3D adapter name."); - DXGI_ADAPTER_DESC adapter_desc; - if (SUCCEEDED(dxgi_adapter->GetDesc(&adapter_desc))) - { - char adapter_name_buffer[128]; - const int name_length = - WideCharToMultiByte(CP_UTF8, 0, adapter_desc.Description, static_cast(std::wcslen(adapter_desc.Description)), - adapter_name_buffer, countof(adapter_name_buffer), 0, nullptr); - if (name_length >= 0) - { - adapter_name_buffer[name_length] = 0; - Log_InfoPrintf("D3D Adapter: %s", adapter_name_buffer); - } - } + BOOL allow_tearing_supported = false; + hr = m_dxgi_factory->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &allow_tearing_supported, + sizeof(allow_tearing_supported)); + m_allow_tearing_supported = (SUCCEEDED(hr) && allow_tearing_supported == TRUE); SetFeatures(); @@ -486,6 +446,8 @@ bool D3D11Device::CreateDevice(const std::string_view& adapter) void D3D11Device::DestroyDevice() { + std::unique_lock lock(s_instance_mutex); + DestroyStagingBuffer(); DestroyBuffers(); m_context.Reset(); @@ -515,103 +477,15 @@ void D3D11Device::SetFeatures() m_features.supports_texture_buffers = true; m_features.texture_buffers_emulated_with_ssbo = false; m_features.gpu_timing = true; - - m_allow_tearing_supported = false; - ComPtr dxgi_factory5; - HRESULT hr = m_dxgi_factory.As(&dxgi_factory5); - if (SUCCEEDED(hr)) - { - BOOL allow_tearing_supported = false; - hr = dxgi_factory5->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &allow_tearing_supported, - sizeof(allow_tearing_supported)); - if (SUCCEEDED(hr)) - m_allow_tearing_supported = (allow_tearing_supported == TRUE); - } -} - -bool D3D11Device::GetRequestedExclusiveFullscreenModeDesc(IDXGIFactory5* factory, const RECT& window_rect, u32 width, - u32 height, float refresh_rate, DXGI_FORMAT format, - DXGI_MODE_DESC* fullscreen_mode, IDXGIOutput** output) -{ - // We need to find which monitor the window is located on. - const Common::Rectangle client_rc_vec(window_rect.left, window_rect.top, window_rect.right, window_rect.bottom); - - // The window might be on a different adapter to which we are rendering.. so we have to enumerate them all. - HRESULT hr; - ComPtr first_output, intersecting_output; - - for (u32 adapter_index = 0; !intersecting_output; adapter_index++) - { - ComPtr adapter; - hr = factory->EnumAdapters1(adapter_index, adapter.GetAddressOf()); - if (hr == DXGI_ERROR_NOT_FOUND) - break; - else if (FAILED(hr)) - continue; - - for (u32 output_index = 0;; output_index++) - { - ComPtr this_output; - DXGI_OUTPUT_DESC output_desc; - hr = adapter->EnumOutputs(output_index, this_output.GetAddressOf()); - if (hr == DXGI_ERROR_NOT_FOUND) - break; - else if (FAILED(hr) || FAILED(this_output->GetDesc(&output_desc))) - continue; - - const Common::Rectangle output_rc(output_desc.DesktopCoordinates.left, output_desc.DesktopCoordinates.top, - output_desc.DesktopCoordinates.right, - output_desc.DesktopCoordinates.bottom); - if (!client_rc_vec.Intersects(output_rc)) - { - intersecting_output = std::move(this_output); - break; - } - - // Fallback to the first monitor. - if (!first_output) - first_output = std::move(this_output); - } - } - - if (!intersecting_output) - { - if (!first_output) - { - Log_ErrorPrintf("No DXGI output found. Can't use exclusive fullscreen."); - return false; - } - - Log_WarningPrint("No DXGI output found for window, using first."); - intersecting_output = std::move(first_output); - } - - DXGI_MODE_DESC request_mode = {}; - request_mode.Width = width; - request_mode.Height = height; - request_mode.Format = format; - request_mode.RefreshRate.Numerator = static_cast(std::floor(refresh_rate * 1000.0f)); - request_mode.RefreshRate.Denominator = 1000u; - - if (FAILED(hr = intersecting_output->FindClosestMatchingMode(&request_mode, fullscreen_mode, nullptr)) || - request_mode.Format != format) - { - Log_ErrorPrintf("Failed to find closest matching mode, hr=%08X", hr); - return false; - } - - *output = intersecting_output.Get(); - intersecting_output->AddRef(); - return true; } bool D3D11Device::CreateSwapChain() { - constexpr DXGI_FORMAT swap_chain_format = DXGI_FORMAT_R8G8B8A8_UNORM; - if (m_window_info.type != WindowInfo::Type::Win32) return false; + const DXGI_FORMAT dxgi_format = s_dxgi_mapping[static_cast(s_swap_chain_format)]; + const HWND window_hwnd = reinterpret_cast(m_window_info.window_handle); RECT client_rc{}; GetClientRect(window_hwnd, &client_rc); @@ -624,9 +498,9 @@ bool D3D11Device::CreateSwapChain() float fullscreen_refresh_rate; m_is_exclusive_fullscreen = GetRequestedExclusiveFullscreenMode(&fullscreen_width, &fullscreen_height, &fullscreen_refresh_rate) && - GetRequestedExclusiveFullscreenModeDesc(m_dxgi_factory.Get(), client_rc, fullscreen_width, fullscreen_height, - fullscreen_refresh_rate, swap_chain_format, &fullscreen_mode, - fullscreen_output.GetAddressOf()); + D3DCommon::GetRequestedExclusiveFullscreenModeDesc(m_dxgi_factory.Get(), client_rc, fullscreen_width, + fullscreen_height, fullscreen_refresh_rate, dxgi_format, + &fullscreen_mode, fullscreen_output.GetAddressOf()); } else { @@ -639,7 +513,7 @@ bool D3D11Device::CreateSwapChain() DXGI_SWAP_CHAIN_DESC1 swap_chain_desc = {}; swap_chain_desc.Width = static_cast(client_rc.right - client_rc.left); swap_chain_desc.Height = static_cast(client_rc.bottom - client_rc.top); - swap_chain_desc.Format = swap_chain_format; + swap_chain_desc.Format = dxgi_format; swap_chain_desc.SampleDesc.Count = 1; swap_chain_desc.BufferCount = 3; swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; @@ -741,6 +615,7 @@ bool D3D11Device::CreateSwapChainRTV() m_window_info.surface_width = backbuffer_desc.Width; m_window_info.surface_height = backbuffer_desc.Height; + m_window_info.surface_format = s_swap_chain_format; Log_VerbosePrintf("Swap chain buffer size: %ux%u", m_window_info.surface_width, m_window_info.surface_height); if (m_window_info.type == WindowInfo::Type::Win32) @@ -912,85 +787,35 @@ void D3D11Device::EndPresent() GPUDevice::AdapterAndModeList D3D11Device::StaticGetAdapterAndModeList() { - ComPtr dxgi_factory; - HRESULT hr = CreateDXGIFactory(IID_PPV_ARGS(dxgi_factory.GetAddressOf())); - if (FAILED(hr)) - return {}; + AdapterAndModeList ret; + std::unique_lock lock(s_instance_mutex); - return GetAdapterAndModeList(dxgi_factory.Get()); -} - -GPUDevice::AdapterAndModeList D3D11Device::GetAdapterAndModeList(IDXGIFactory* dxgi_factory) -{ - AdapterAndModeList adapter_info; - ComPtr current_adapter; - while (SUCCEEDED(dxgi_factory->EnumAdapters(static_cast(adapter_info.adapter_names.size()), - current_adapter.ReleaseAndGetAddressOf()))) + // Device shouldn't be torn down since we have the lock. + if (g_gpu_device && g_gpu_device->GetRenderAPI() == RenderAPI::D3D12) { - DXGI_ADAPTER_DESC adapter_desc; - std::string adapter_name; - if (SUCCEEDED(current_adapter->GetDesc(&adapter_desc))) - { - char adapter_name_buffer[128]; - const int name_length = WideCharToMultiByte(CP_UTF8, 0, adapter_desc.Description, - static_cast(std::wcslen(adapter_desc.Description)), - adapter_name_buffer, countof(adapter_name_buffer), 0, nullptr); - if (name_length >= 0) - adapter_name.assign(adapter_name_buffer, static_cast(name_length)); - else - adapter_name.assign("(Unknown)"); - } - else - { - adapter_name.assign("(Unknown)"); - } - - if (adapter_info.fullscreen_modes.empty()) - { - ComPtr output; - if (SUCCEEDED(current_adapter->EnumOutputs(0, &output))) - { - UINT num_modes = 0; - if (SUCCEEDED(output->GetDisplayModeList(DXGI_FORMAT_R8G8B8A8_UNORM, 0, &num_modes, nullptr))) - { - std::vector modes(num_modes); - if (SUCCEEDED(output->GetDisplayModeList(DXGI_FORMAT_R8G8B8A8_UNORM, 0, &num_modes, modes.data()))) - { - for (const DXGI_MODE_DESC& mode : modes) - { - adapter_info.fullscreen_modes.push_back(GetFullscreenModeString( - mode.Width, mode.Height, - static_cast(mode.RefreshRate.Numerator) / static_cast(mode.RefreshRate.Denominator))); - } - } - } - } - } - - // handle duplicate adapter names - if (std::any_of(adapter_info.adapter_names.begin(), adapter_info.adapter_names.end(), - [&adapter_name](const std::string& other) { return (adapter_name == other); })) - { - std::string original_adapter_name = std::move(adapter_name); - - u32 current_extra = 2; - do - { - adapter_name = StringUtil::StdStringFromFormat("%s (%u)", original_adapter_name.c_str(), current_extra); - current_extra++; - } while (std::any_of(adapter_info.adapter_names.begin(), adapter_info.adapter_names.end(), - [&adapter_name](const std::string& other) { return (adapter_name == other); })); - } - - adapter_info.adapter_names.push_back(std::move(adapter_name)); + GetAdapterAndModeList(&ret, D3D11Device::GetInstance().m_dxgi_factory.Get()); + } + else + { + ComPtr factory = D3DCommon::CreateFactory(false); + if (factory) + GetAdapterAndModeList(&ret, factory.Get()); } - return adapter_info; + return ret; +} + +void D3D11Device::GetAdapterAndModeList(AdapterAndModeList* ret, IDXGIFactory5* factory) +{ + ret->adapter_names = D3DCommon::GetAdapterNames(factory); + ret->fullscreen_modes = D3DCommon::GetFullscreenModes(factory, {}); } GPUDevice::AdapterAndModeList D3D11Device::GetAdapterAndModeList() { - return GetAdapterAndModeList(m_dxgi_factory.Get()); + AdapterAndModeList ret; + GetAdapterAndModeList(&ret, m_dxgi_factory.Get()); + return ret; } bool D3D11Device::CreateTimestampQueries() @@ -1197,15 +1022,22 @@ std::unique_ptr D3D11Device::CreateSampler(const GPUSampler::Config& D3D11_TEXTURE_ADDRESS_CLAMP, // ClampToEdge D3D11_TEXTURE_ADDRESS_BORDER, // ClampToBorder }}; + static constexpr u8 filter_count = static_cast(GPUSampler::Filter::MaxCount); + static constexpr D3D11_FILTER filters[filter_count][filter_count][filter_count] = { + { + {D3D11_FILTER_MIN_MAG_MIP_POINT, D3D11_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT}, + {D3D11_FILTER_MIN_LINEAR_MAG_MIP_POINT, D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT}, + }, + { + {D3D11_FILTER_MIN_MAG_POINT_MIP_LINEAR, D3D11_FILTER_MIN_POINT_MAG_MIP_LINEAR}, + {D3D11_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR, D3D11_FILTER_MIN_MAG_MIP_LINEAR}, + }}; D3D11_SAMPLER_DESC desc = {}; desc.AddressU = ta[static_cast(config.address_u.GetValue())]; desc.AddressV = ta[static_cast(config.address_v.GetValue())]; desc.AddressW = ta[static_cast(config.address_w.GetValue())]; - desc.BorderColor[0] = static_cast(config.border_color & 0xFF) / 255.0f; - desc.BorderColor[1] = static_cast((config.border_color >> 8) & 0xFF) / 255.0f; - desc.BorderColor[2] = static_cast((config.border_color >> 16) & 0xFF) / 255.0f; - desc.BorderColor[3] = static_cast((config.border_color >> 24) & 0xFF) / 255.0f; + std::memcpy(desc.BorderColor, RGBA8ToFloat(config.border_color).data(), sizeof(desc.BorderColor)); desc.MinLOD = static_cast(config.min_lod); desc.MaxLOD = static_cast(config.max_lod); @@ -1216,17 +1048,6 @@ std::unique_ptr D3D11Device::CreateSampler(const GPUSampler::Config& } else { - static constexpr u8 filter_count = static_cast(GPUSampler::Filter::MaxCount); - static constexpr D3D11_FILTER filters[filter_count][filter_count][filter_count] = { - { - {D3D11_FILTER_MIN_MAG_MIP_POINT, D3D11_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT}, - {D3D11_FILTER_MIN_LINEAR_MAG_MIP_POINT, D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT}, - }, - { - {D3D11_FILTER_MIN_MAG_POINT_MIP_LINEAR, D3D11_FILTER_MIN_POINT_MAG_MIP_LINEAR}, - {D3D11_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR, D3D11_FILTER_MIN_MAG_MIP_LINEAR}, - }}; - desc.Filter = filters[static_cast(config.mip_filter.GetValue())][static_cast(config.min_filter.GetValue())] [static_cast(config.mag_filter.GetValue())]; desc.MaxAnisotropy = 1; @@ -1313,83 +1134,16 @@ std::unique_ptr D3D11Device::CreateShaderFromBinary(GPUShaderStage st std::unique_ptr D3D11Device::CreateShaderFromSource(GPUShaderStage stage, const std::string_view& source, std::vector* out_binary /* = nullptr */) { - const char* target; - switch (m_device->GetFeatureLevel()) - { - case D3D_FEATURE_LEVEL_10_0: - { - static constexpr std::array targets = {{"vs_4_0", "ps_4_0", "cs_4_0"}}; - target = targets[static_cast(stage)]; - } - break; - - case D3D_FEATURE_LEVEL_10_1: - { - static constexpr std::array targets = {{"vs_4_1", "ps_4_1", "cs_4_1"}}; - target = targets[static_cast(stage)]; - } - break; - - case D3D_FEATURE_LEVEL_11_0: - { - static constexpr std::array targets = {{"vs_5_0", "ps_5_0", "cs_5_0"}}; - target = targets[static_cast(stage)]; - } - break; - - case D3D_FEATURE_LEVEL_11_1: - default: - { - static constexpr std::array targets = {{"vs_5_1", "ps_5_1", "cs_5_1"}}; - target = targets[static_cast(stage)]; - } - break; - } - - static constexpr UINT flags_non_debug = D3DCOMPILE_OPTIMIZATION_LEVEL3; - static constexpr UINT flags_debug = D3DCOMPILE_SKIP_OPTIMIZATION | D3DCOMPILE_DEBUG; - - ComPtr blob; - ComPtr error_blob; - const HRESULT hr = - D3DCompile(source.data(), source.size(), "0", nullptr, nullptr, "main", target, - m_debug_device ? flags_debug : flags_non_debug, 0, blob.GetAddressOf(), error_blob.GetAddressOf()); - - std::string error_string; - if (error_blob) - { - error_string.append(static_cast(error_blob->GetBufferPointer()), error_blob->GetBufferSize()); - error_blob.Reset(); - } - - if (FAILED(hr)) - { - Log_ErrorPrintf("Failed to compile '%s':\n%s", target, error_string.c_str()); - - auto fp = FileSystem::OpenManagedCFile( - GetShaderDumpPath(fmt::format("bad_shader_{}.txt", s_next_bad_shader_id++)).c_str(), "wb"); - if (fp) - { - std::fwrite(source.data(), source.size(), 1, fp.get()); - std::fprintf(fp.get(), "\n\nCompile as %s failed: %08X\n", target, hr); - std::fwrite(error_string.c_str(), error_string.size(), 1, fp.get()); - } - + std::optional> bytecode = + D3DCommon::CompileShader(m_device->GetFeatureLevel(), m_debug_device, stage, source); + if (!bytecode.has_value()) return {}; - } - if (!error_string.empty()) - Log_WarningPrintf("'%s' compiled with warnings:\n%s", target, error_string.c_str()); + std::unique_ptr ret = CreateShaderFromBinary(stage, bytecode.value()); + if (ret && out_binary) + *out_binary = std::move(bytecode.value()); - if (out_binary) - { - const size_t size = blob->GetBufferSize(); - out_binary->resize(size); - std::memcpy(out_binary->data(), blob->GetBufferPointer(), size); - } - - return CreateShaderFromBinary( - stage, gsl::span(static_cast(blob->GetBufferPointer()), blob->GetBufferSize())); + return ret; } D3D11Pipeline::D3D11Pipeline(ComPtr rs, ComPtr ds, diff --git a/src/core/gpu/d3d11_device.h b/src/core/gpu/d3d11_device.h index 6119f1fe3..10b423f37 100644 --- a/src/core/gpu/d3d11_device.h +++ b/src/core/gpu/d3d11_device.h @@ -268,11 +268,6 @@ public: ALWAYS_INLINE static ID3D11Device* GetD3DDevice() { return GetInstance().m_device.Get(); } ALWAYS_INLINE static ID3D11DeviceContext1* GetD3DContext() { return GetInstance().m_context.Get(); } - // returns the fullscreen mode to use for the specified dimensions - static bool GetRequestedExclusiveFullscreenModeDesc(IDXGIFactory5* factory, const RECT& window_rect, u32 width, - u32 height, float refresh_rate, DXGI_FORMAT format, - DXGI_MODE_DESC* fullscreen_mode, IDXGIOutput** output); - RenderAPI GetRenderAPI() const override; bool HasSurface() const override; @@ -364,7 +359,7 @@ private: static constexpr u32 UNIFORM_BUFFER_ALIGNMENT = 256; static constexpr u8 NUM_TIMESTAMP_QUERIES = 3; - static AdapterAndModeList GetAdapterAndModeList(IDXGIFactory* dxgi_factory); + static void GetAdapterAndModeList(AdapterAndModeList* ret, IDXGIFactory5* factory); void SetFeatures(); diff --git a/src/core/gpu/d3d12/context.cpp b/src/core/gpu/d3d12/context.cpp deleted file mode 100644 index 1d4514ba7..000000000 --- a/src/core/gpu/d3d12/context.cpp +++ /dev/null @@ -1,556 +0,0 @@ -// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin -// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) -// Parts originally from Dolphin Emulator, also written by myself. - -#include "context.h" -#include "common/assert.h" -#include "common/log.h" -#include "common/scoped_guard.h" -#include -#include -#include -#include -#include -Log_SetChannel(D3D12::Context); - -std::unique_ptr g_d3d12_context; - -namespace D3D12 { - -#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) - -// Private D3D12 state -static HMODULE s_d3d12_library; -static PFN_D3D12_CREATE_DEVICE s_d3d12_create_device; -static PFN_D3D12_GET_DEBUG_INTERFACE s_d3d12_get_debug_interface; -static PFN_D3D12_SERIALIZE_ROOT_SIGNATURE s_d3d12_serialize_root_signature; - -static bool LoadD3D12Library() -{ - if ((s_d3d12_library = LoadLibrary("d3d12.dll")) == nullptr || - (s_d3d12_create_device = - reinterpret_cast(GetProcAddress(s_d3d12_library, "D3D12CreateDevice"))) == nullptr || - (s_d3d12_get_debug_interface = reinterpret_cast( - GetProcAddress(s_d3d12_library, "D3D12GetDebugInterface"))) == nullptr || - (s_d3d12_serialize_root_signature = reinterpret_cast( - GetProcAddress(s_d3d12_library, "D3D12SerializeRootSignature"))) == nullptr) - { - Log_ErrorPrintf("d3d12.dll could not be loaded."); - s_d3d12_create_device = nullptr; - s_d3d12_get_debug_interface = nullptr; - s_d3d12_serialize_root_signature = nullptr; - if (s_d3d12_library) - FreeLibrary(s_d3d12_library); - s_d3d12_library = nullptr; - return false; - } - - return true; -} - -static void UnloadD3D12Library() -{ - s_d3d12_serialize_root_signature = nullptr; - s_d3d12_get_debug_interface = nullptr; - s_d3d12_create_device = nullptr; - if (s_d3d12_library) - { - FreeLibrary(s_d3d12_library); - s_d3d12_library = nullptr; - } -} - -#else - -static const PFN_D3D12_CREATE_DEVICE s_d3d12_create_device = D3D12CreateDevice; -static const PFN_D3D12_GET_DEBUG_INTERFACE s_d3d12_get_debug_interface = D3D12GetDebugInterface; -static const PFN_D3D12_SERIALIZE_ROOT_SIGNATURE s_d3d12_serialize_root_signature = D3D12SerializeRootSignature; - -static bool LoadD3D12Library() -{ - return true; -} - -static void UnloadD3D12Library() {} - -#endif - -Context::Context() = default; - -Context::~Context() -{ - DestroyResources(); -} - -Context::ComPtr Context::SerializeRootSignature(const D3D12_ROOT_SIGNATURE_DESC* desc) -{ - ComPtr blob; - ComPtr error_blob; - const HRESULT hr = s_d3d12_serialize_root_signature(desc, D3D_ROOT_SIGNATURE_VERSION_1, blob.GetAddressOf(), - error_blob.GetAddressOf()); - if (FAILED(hr)) - { - Log_ErrorPrintf("D3D12SerializeRootSignature() failed: %08X", hr); - if (error_blob) - Log_ErrorPrintf("%s", error_blob->GetBufferPointer()); - - return {}; - } - - return blob; -} - -D3D12::Context::ComPtr Context::CreateRootSignature(const D3D12_ROOT_SIGNATURE_DESC* desc) -{ - ComPtr blob = SerializeRootSignature(desc); - if (!blob) - return {}; - - ComPtr rs; - const HRESULT hr = - m_device->CreateRootSignature(0, blob->GetBufferPointer(), blob->GetBufferSize(), IID_PPV_ARGS(rs.GetAddressOf())); - if (FAILED(hr)) - { - Log_ErrorPrintf("CreateRootSignature() failed: %08X", hr); - return {}; - } - - return rs; -} - -bool Context::SupportsTextureFormat(DXGI_FORMAT format) -{ - constexpr u32 required = D3D12_FORMAT_SUPPORT1_TEXTURE2D | D3D12_FORMAT_SUPPORT1_SHADER_SAMPLE; - - D3D12_FEATURE_DATA_FORMAT_SUPPORT support = {format}; - return SUCCEEDED(m_device->CheckFeatureSupport(D3D12_FEATURE_FORMAT_SUPPORT, &support, sizeof(support))) && - (support.Support1 & required) == required; -} - -bool Context::Create(IDXGIFactory* dxgi_factory, u32 adapter_index, bool enable_debug_layer) -{ - Assert(!g_d3d12_context); - - if (!LoadD3D12Library()) - return false; - - g_d3d12_context.reset(new Context()); - if (!g_d3d12_context->CreateDevice(dxgi_factory, adapter_index, enable_debug_layer) || - !g_d3d12_context->CreateCommandQueue() || !g_d3d12_context->CreateFence() || - !g_d3d12_context->CreateDescriptorHeaps() || !g_d3d12_context->CreateCommandLists() || - !g_d3d12_context->CreateTimestampQuery() || !g_d3d12_context->CreateTextureStreamBuffer()) - { - Destroy(); - return false; - } - - return true; -} - -void Context::Destroy() -{ - if (g_d3d12_context) - g_d3d12_context.reset(); - - UnloadD3D12Library(); -} - -bool Context::CreateDevice(IDXGIFactory* dxgi_factory, u32 adapter_index, bool enable_debug_layer) -{ - ComPtr adapter; - HRESULT hr = dxgi_factory->EnumAdapters(adapter_index, &adapter); - if (FAILED(hr)) - { - Log_ErrorPrintf("Adapter %u not found, using default", adapter_index); - adapter = nullptr; - } - else - { - DXGI_ADAPTER_DESC adapter_desc; - if (SUCCEEDED(adapter->GetDesc(&adapter_desc))) - { - char adapter_name_buffer[128]; - const int name_length = WideCharToMultiByte(CP_UTF8, 0, adapter_desc.Description, - static_cast(std::wcslen(adapter_desc.Description)), - adapter_name_buffer, countof(adapter_name_buffer), 0, nullptr); - if (name_length >= 0) - { - adapter_name_buffer[name_length] = 0; - Log_InfoPrintf("D3D Adapter: %s", adapter_name_buffer); - } - } - } - - // Enabling the debug layer will fail if the Graphics Tools feature is not installed. - if (enable_debug_layer) - { - hr = s_d3d12_get_debug_interface(IID_PPV_ARGS(&m_debug_interface)); - if (SUCCEEDED(hr)) - { - m_debug_interface->EnableDebugLayer(); - } - else - { - Log_ErrorPrintf("Debug layer requested but not available."); - enable_debug_layer = false; - } - } - - // Create the actual device. - hr = s_d3d12_create_device(adapter.Get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&m_device)); - AssertMsg(SUCCEEDED(hr), "Create D3D12 device"); - if (FAILED(hr)) - return false; - - if (enable_debug_layer) - { - ComPtr info_queue; - if (SUCCEEDED(m_device.As(&info_queue))) - { - info_queue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_ERROR, TRUE); - info_queue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_WARNING, TRUE); - - D3D12_INFO_QUEUE_FILTER filter = {}; - std::array id_list{ - D3D12_MESSAGE_ID_CLEARRENDERTARGETVIEW_MISMATCHINGCLEARVALUE, - D3D12_MESSAGE_ID_CLEARDEPTHSTENCILVIEW_MISMATCHINGCLEARVALUE, - D3D12_MESSAGE_ID_CREATEGRAPHICSPIPELINESTATE_RENDERTARGETVIEW_NOT_SET, - D3D12_MESSAGE_ID_CREATEINPUTLAYOUT_TYPE_MISMATCH, - D3D12_MESSAGE_ID_DRAW_EMPTY_SCISSOR_RECTANGLE, - }; - filter.DenyList.NumIDs = static_cast(id_list.size()); - filter.DenyList.pIDList = id_list.data(); - info_queue->PushStorageFilter(&filter); - } - } - - return true; -} - -bool Context::CreateCommandQueue() -{ - const D3D12_COMMAND_QUEUE_DESC queue_desc = {D3D12_COMMAND_LIST_TYPE_DIRECT, D3D12_COMMAND_QUEUE_PRIORITY_NORMAL, - D3D12_COMMAND_QUEUE_FLAG_NONE}; - HRESULT hr = m_device->CreateCommandQueue(&queue_desc, IID_PPV_ARGS(&m_command_queue)); - AssertMsg(SUCCEEDED(hr), "Create command queue"); - return SUCCEEDED(hr); -} - -bool Context::CreateFence() -{ - HRESULT hr = m_device->CreateFence(m_completed_fence_value, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&m_fence)); - AssertMsg(SUCCEEDED(hr), "Create fence"); - if (FAILED(hr)) - return false; - - m_fence_event = CreateEvent(nullptr, FALSE, FALSE, nullptr); - AssertMsg(m_fence_event != NULL, "Create fence event"); - if (!m_fence_event) - return false; - - return true; -} - -bool Context::CreateDescriptorHeaps() -{ - static constexpr size_t MAX_SRVS = 16384; - static constexpr size_t MAX_RTVS = 8192; - static constexpr size_t MAX_DSVS = 128; - static constexpr size_t MAX_SAMPLERS = 128; - - if (!m_descriptor_heap_manager.Create(m_device.Get(), D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, MAX_SRVS, true) || - !m_rtv_heap_manager.Create(m_device.Get(), D3D12_DESCRIPTOR_HEAP_TYPE_RTV, MAX_RTVS, false) || - !m_dsv_heap_manager.Create(m_device.Get(), D3D12_DESCRIPTOR_HEAP_TYPE_DSV, MAX_DSVS, false) || - !m_sampler_heap_manager.Create(m_device.Get(), D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, MAX_SAMPLERS, true)) - { - return false; - } - - m_gpu_descriptor_heaps[0] = m_descriptor_heap_manager.GetDescriptorHeap(); - m_gpu_descriptor_heaps[1] = m_sampler_heap_manager.GetDescriptorHeap(); - - // Allocate null SRV descriptor for unbound textures. - constexpr D3D12_SHADER_RESOURCE_VIEW_DESC null_srv_desc = {DXGI_FORMAT_R8G8B8A8_UNORM, D3D12_SRV_DIMENSION_TEXTURE2D, - D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING}; - - if (!m_descriptor_heap_manager.Allocate(&m_null_srv_descriptor)) - { - Panic("Failed to allocate null descriptor"); - return false; - } - - m_device->CreateShaderResourceView(nullptr, &null_srv_desc, m_null_srv_descriptor.cpu_handle); - return true; -} - -bool Context::CreateCommandLists() -{ - for (u32 i = 0; i < NUM_COMMAND_LISTS; i++) - { - CommandListResources& res = m_command_lists[i]; - HRESULT hr = m_device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, - IID_PPV_ARGS(res.command_allocator.GetAddressOf())); - AssertMsg(SUCCEEDED(hr), "Create command allocator"); - if (FAILED(hr)) - return false; - - hr = m_device->CreateCommandList(1, D3D12_COMMAND_LIST_TYPE_DIRECT, res.command_allocator.Get(), nullptr, - IID_PPV_ARGS(res.command_list.GetAddressOf())); - if (FAILED(hr)) - { - Log_ErrorPrintf("Failed to create command list: %08X", hr); - return false; - } - - // Close the command list, since the first thing we do is reset them. - hr = res.command_list->Close(); - AssertMsg(SUCCEEDED(hr), "Closing new command list failed"); - if (FAILED(hr)) - return false; - } - - MoveToNextCommandList(); - return true; -} - -bool Context::CreateTextureStreamBuffer() -{ - return m_texture_stream_buffer.Create(TEXTURE_UPLOAD_BUFFER_SIZE); -} - -void Context::MoveToNextCommandList() -{ - m_current_command_list = (m_current_command_list + 1) % NUM_COMMAND_LISTS; - m_current_fence_value++; - - // We may have to wait if this command list hasn't finished on the GPU. - CommandListResources& res = m_command_lists[m_current_command_list]; - WaitForFence(res.ready_fence_value); - res.ready_fence_value = m_current_fence_value; - - // Begin command list. - res.command_allocator->Reset(); - res.command_list->Reset(res.command_allocator.Get(), nullptr); - - if (res.has_timestamp_query) - { - // readback timestamp from the last time this cmdlist was used. - // we don't need to worry about disjoint in dx12, the frequency is reliable within a single cmdlist. - const u32 offset = (m_current_command_list * (sizeof(u64) * NUM_TIMESTAMP_QUERIES_PER_CMDLIST)); - const D3D12_RANGE read_range = {offset, offset + (sizeof(u64) * NUM_TIMESTAMP_QUERIES_PER_CMDLIST)}; - void* map; - HRESULT hr = m_timestamp_query_buffer->Map(0, &read_range, &map); - if (SUCCEEDED(hr)) - { - u64 timestamps[2]; - std::memcpy(timestamps, static_cast(map) + offset, sizeof(timestamps)); - m_accumulated_gpu_time += - static_cast(static_cast(timestamps[1] - timestamps[0]) / m_timestamp_frequency); - - const D3D12_RANGE write_range = {}; - m_timestamp_query_buffer->Unmap(0, &write_range); - } - else - { - Log_WarningPrintf("Map() for timestamp query failed: %08X", hr); - } - } - - res.has_timestamp_query = m_gpu_timing_enabled; - if (m_gpu_timing_enabled) - { - res.command_list->EndQuery(m_timestamp_query_heap.Get(), D3D12_QUERY_TYPE_TIMESTAMP, - m_current_command_list * NUM_TIMESTAMP_QUERIES_PER_CMDLIST); - } - - res.command_list->SetDescriptorHeaps(static_cast(m_gpu_descriptor_heaps.size()), m_gpu_descriptor_heaps.data()); -} - -void Context::ExecuteCommandList(bool wait_for_completion) -{ - CommandListResources& res = m_command_lists[m_current_command_list]; - HRESULT hr; - - if (res.has_timestamp_query) - { - // write the timestamp back at the end of the cmdlist - res.command_list->EndQuery(m_timestamp_query_heap.Get(), D3D12_QUERY_TYPE_TIMESTAMP, - (m_current_command_list * NUM_TIMESTAMP_QUERIES_PER_CMDLIST) + 1); - res.command_list->ResolveQueryData(m_timestamp_query_heap.Get(), D3D12_QUERY_TYPE_TIMESTAMP, - m_current_command_list * NUM_TIMESTAMP_QUERIES_PER_CMDLIST, - NUM_TIMESTAMP_QUERIES_PER_CMDLIST, m_timestamp_query_buffer.Get(), - m_current_command_list * (sizeof(u64) * NUM_TIMESTAMP_QUERIES_PER_CMDLIST)); - } - - // Close and queue command list. - hr = res.command_list->Close(); - AssertMsg(SUCCEEDED(hr), "Close command list"); - const std::array execute_lists{res.command_list.Get()}; - m_command_queue->ExecuteCommandLists(static_cast(execute_lists.size()), execute_lists.data()); - - // Update fence when GPU has completed. - hr = m_command_queue->Signal(m_fence.Get(), m_current_fence_value); - AssertMsg(SUCCEEDED(hr), "Signal fence"); - - MoveToNextCommandList(); - if (wait_for_completion) - WaitForFence(res.ready_fence_value); -} - -void Context::DeferResourceDestruction(ID3D12Resource* resource) -{ - if (!resource) - return; - - resource->AddRef(); - m_command_lists[m_current_command_list].pending_resources.push_back(resource); -} - -void Context::DeferDescriptorDestruction(DescriptorHeapManager& manager, u32 index) -{ - m_command_lists[m_current_command_list].pending_descriptors.emplace_back(manager, index); -} - -void Context::DeferDescriptorDestruction(DescriptorHeapManager& manager, DescriptorHandle* handle) -{ - if (handle->index == DescriptorHandle::INVALID_INDEX) - return; - - m_command_lists[m_current_command_list].pending_descriptors.emplace_back(manager, handle->index); - handle->Clear(); -} - -void Context::DestroyPendingResources(CommandListResources& cmdlist) -{ - for (const auto& dd : cmdlist.pending_descriptors) - dd.first.Free(dd.second); - cmdlist.pending_descriptors.clear(); - - for (ID3D12Resource* res : cmdlist.pending_resources) - res->Release(); - cmdlist.pending_resources.clear(); -} - -void Context::DestroyResources() -{ - ExecuteCommandList(true); - - m_timestamp_query_buffer.Reset(); - m_timestamp_query_heap.Reset(); - m_texture_stream_buffer.Destroy(false); - m_descriptor_heap_manager.Free(&m_null_srv_descriptor); - m_sampler_heap_manager.Destroy(); - m_dsv_heap_manager.Destroy(); - m_rtv_heap_manager.Destroy(); - m_descriptor_heap_manager.Destroy(); - m_command_lists = {}; - m_current_command_list = 0; - m_completed_fence_value = 0; - m_current_fence_value = 0; - if (m_fence_event) - { - CloseHandle(m_fence_event); - m_fence_event = {}; - } - - m_command_queue.Reset(); - m_debug_interface.Reset(); - m_device.Reset(); -} - -void Context::WaitForFence(u64 fence) -{ - if (m_completed_fence_value >= fence) - return; - - // Try non-blocking check. - m_completed_fence_value = m_fence->GetCompletedValue(); - if (m_completed_fence_value < fence) - { - // Fall back to event. - HRESULT hr = m_fence->SetEventOnCompletion(fence, m_fence_event); - AssertMsg(SUCCEEDED(hr), "Set fence event on completion"); - WaitForSingleObject(m_fence_event, INFINITE); - m_completed_fence_value = m_fence->GetCompletedValue(); - } - - // Release resources for as many command lists which have completed. - u32 index = (m_current_command_list + 1) % NUM_COMMAND_LISTS; - for (u32 i = 0; i < NUM_COMMAND_LISTS; i++) - { - CommandListResources& res = m_command_lists[index]; - if (m_completed_fence_value < res.ready_fence_value) - break; - - DestroyPendingResources(res); - index = (index + 1) % NUM_COMMAND_LISTS; - } -} - -void Context::WaitForGPUIdle() -{ - u32 index = (m_current_command_list + 1) % NUM_COMMAND_LISTS; - for (u32 i = 0; i < (NUM_COMMAND_LISTS - 1); i++) - { - WaitForFence(m_command_lists[index].ready_fence_value); - index = (index + 1) % NUM_COMMAND_LISTS; - } -} - -bool Context::CreateTimestampQuery() -{ - constexpr u32 QUERY_COUNT = NUM_TIMESTAMP_QUERIES_PER_CMDLIST * NUM_COMMAND_LISTS; - constexpr u32 BUFFER_SIZE = sizeof(u64) * QUERY_COUNT; - - const D3D12_QUERY_HEAP_DESC desc = {D3D12_QUERY_HEAP_TYPE_TIMESTAMP, QUERY_COUNT}; - HRESULT hr = m_device->CreateQueryHeap(&desc, IID_PPV_ARGS(m_timestamp_query_heap.ReleaseAndGetAddressOf())); - if (FAILED(hr)) - { - Log_ErrorPrintf("CreateQueryHeap() for timestamp failed with %08X", hr); - return false; - } - - const D3D12_HEAP_PROPERTIES heap_properties = {D3D12_HEAP_TYPE_READBACK}; - const D3D12_RESOURCE_DESC resource_desc = {D3D12_RESOURCE_DIMENSION_BUFFER, - 0, - BUFFER_SIZE, - 1, - 1, - 1, - DXGI_FORMAT_UNKNOWN, - {1, 0}, - D3D12_TEXTURE_LAYOUT_ROW_MAJOR, - D3D12_RESOURCE_FLAG_NONE}; - hr = m_device->CreateCommittedResource(&heap_properties, D3D12_HEAP_FLAG_NONE, &resource_desc, - D3D12_RESOURCE_STATE_COPY_DEST, nullptr, - IID_PPV_ARGS(m_timestamp_query_buffer.ReleaseAndGetAddressOf())); - if (FAILED(hr)) - { - Log_ErrorPrintf("CreateResource() for timestamp failed with %08X", hr); - return false; - } - - u64 frequency; - hr = m_command_queue->GetTimestampFrequency(&frequency); - if (FAILED(hr)) - { - Log_ErrorPrintf("GetTimestampFrequency() failed: %08X", hr); - return false; - } - - m_timestamp_frequency = static_cast(frequency) / 1000.0; - return true; -} - -float Context::GetAndResetAccumulatedGPUTime() -{ - const float time = m_accumulated_gpu_time; - m_accumulated_gpu_time = 0.0f; - return time; -} - -void Context::SetEnableGPUTiming(bool enabled) -{ - m_gpu_timing_enabled = enabled; -} -} // namespace D3D12 diff --git a/src/core/gpu/d3d12/context.h b/src/core/gpu/d3d12/context.h deleted file mode 100644 index dcad6d1c3..000000000 --- a/src/core/gpu/d3d12/context.h +++ /dev/null @@ -1,156 +0,0 @@ -// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin -// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) -// Parts originally from Dolphin Emulator, also written by myself. - -#pragma once - -#include "common/types.h" -#include "common/windows_headers.h" -#include "descriptor_heap_manager.h" -#include "stream_buffer.h" -#include -#include -#include -#include -#include - -struct IDXGIFactory; - -namespace D3D12 { - -class Context -{ -public: - template - using ComPtr = Microsoft::WRL::ComPtr; - - enum : u32 - { - // Number of command lists. One is being built while the other(s) are executed. - NUM_COMMAND_LISTS = 3, - - // Textures that don't fit into this buffer will be uploaded with a staging buffer. - TEXTURE_UPLOAD_BUFFER_SIZE = 16 * 1024 * 1024, - - /// Start/End timestamp queries. - NUM_TIMESTAMP_QUERIES_PER_CMDLIST = 2, - }; - - ~Context(); - - // Creates new device and context. - static bool Create(IDXGIFactory* dxgi_factory, u32 adapter_index, bool enable_debug_layer); - - // Destroys active context. - static void Destroy(); - - ID3D12Device* GetDevice() const { return m_device.Get(); } - ID3D12CommandQueue* GetCommandQueue() const { return m_command_queue.Get(); } - - // Returns the current command list, commands can be recorded directly. - ID3D12GraphicsCommandList* GetCommandList() const - { - return m_command_lists[m_current_command_list].command_list.Get(); - } - - // Descriptor manager access. - DescriptorHeapManager& GetDescriptorHeapManager() { return m_descriptor_heap_manager; } - DescriptorHeapManager& GetRTVHeapManager() { return m_rtv_heap_manager; } - DescriptorHeapManager& GetDSVHeapManager() { return m_dsv_heap_manager; } - DescriptorHeapManager& GetSamplerHeapManager() { return m_sampler_heap_manager; } - ID3D12DescriptorHeap* const* GetGPUDescriptorHeaps() const { return m_gpu_descriptor_heaps.data(); } - u32 GetGPUDescriptorHeapCount() const { return static_cast(m_gpu_descriptor_heaps.size()); } - const DescriptorHandle& GetNullSRVDescriptor() const { return m_null_srv_descriptor; } - StreamBuffer& GetTextureStreamBuffer() { return m_texture_stream_buffer; } - - // Root signature access. - ComPtr SerializeRootSignature(const D3D12_ROOT_SIGNATURE_DESC* desc); - ComPtr CreateRootSignature(const D3D12_ROOT_SIGNATURE_DESC* desc); - - // Fence value for current command list. - u64 GetCurrentFenceValue() const { return m_current_fence_value; } - - // Last "completed" fence. - u64 GetCompletedFenceValue() const { return m_completed_fence_value; } - - // Feature level to use when compiling shaders. - D3D_FEATURE_LEVEL GetFeatureLevel() const { return m_feature_level; } - - // Test for support for the specified texture format. - bool SupportsTextureFormat(DXGI_FORMAT format); - - // Executes the current command list. - void ExecuteCommandList(bool wait_for_completion); - - // Waits for a specific fence. - void WaitForFence(u64 fence); - - // Waits for any in-flight command buffers to complete. - void WaitForGPUIdle(); - - // Defers destruction of a D3D resource (associates it with the current list). - void DeferResourceDestruction(ID3D12Resource* resource); - - // Defers destruction of a descriptor handle (associates it with the current list). - void DeferDescriptorDestruction(DescriptorHeapManager& manager, u32 index); - void DeferDescriptorDestruction(DescriptorHeapManager& manager, DescriptorHandle* handle); - - float GetAndResetAccumulatedGPUTime(); - void SetEnableGPUTiming(bool enabled); - -private: - struct CommandListResources - { - ComPtr command_allocator; - ComPtr command_list; - std::vector pending_resources; - std::vector> pending_descriptors; - u64 ready_fence_value = 0; - bool has_timestamp_query = false; - }; - - Context(); - - bool CreateDevice(IDXGIFactory* dxgi_factory, u32 adapter_index, bool enable_debug_layer); - bool CreateCommandQueue(); - bool CreateFence(); - bool CreateDescriptorHeaps(); - bool CreateCommandLists(); - bool CreateTextureStreamBuffer(); - bool CreateTimestampQuery(); - void MoveToNextCommandList(); - void DestroyPendingResources(CommandListResources& cmdlist); - void DestroyResources(); - - ComPtr m_debug_interface; - ComPtr m_device; - ComPtr m_command_queue; - - ComPtr m_fence = nullptr; - HANDLE m_fence_event = {}; - u32 m_current_fence_value = 0; - u64 m_completed_fence_value = 0; - - std::array m_command_lists; - u32 m_current_command_list = NUM_COMMAND_LISTS - 1; - - ComPtr m_timestamp_query_heap; - ComPtr m_timestamp_query_buffer; - double m_timestamp_frequency = 0.0; - float m_accumulated_gpu_time = 0.0f; - bool m_gpu_timing_enabled = false; - - DescriptorHeapManager m_descriptor_heap_manager; - DescriptorHeapManager m_rtv_heap_manager; - DescriptorHeapManager m_dsv_heap_manager; - DescriptorHeapManager m_sampler_heap_manager; - std::array m_gpu_descriptor_heaps = {}; - DescriptorHandle m_null_srv_descriptor; - StreamBuffer m_texture_stream_buffer; - - D3D_FEATURE_LEVEL m_feature_level = D3D_FEATURE_LEVEL_11_0; -}; - -} // namespace D3D12 - -extern std::unique_ptr g_d3d12_context; diff --git a/src/core/gpu/d3d12/descriptor_heap_manager.cpp b/src/core/gpu/d3d12/descriptor_heap_manager.cpp deleted file mode 100644 index 20956a9f4..000000000 --- a/src/core/gpu/d3d12/descriptor_heap_manager.cpp +++ /dev/null @@ -1,116 +0,0 @@ -// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin -// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) -// Parts originally from Dolphin Emulator, also written by myself. - -#include "descriptor_heap_manager.h" -#include "common/assert.h" -#include "common/log.h" -#include "context.h" -Log_SetChannel(DescriptorHeapManager); - -namespace D3D12 { -DescriptorHeapManager::DescriptorHeapManager() = default; -DescriptorHeapManager::~DescriptorHeapManager() = default; - -bool DescriptorHeapManager::Create(ID3D12Device* device, D3D12_DESCRIPTOR_HEAP_TYPE type, u32 num_descriptors, - bool shader_visible) -{ - D3D12_DESCRIPTOR_HEAP_DESC desc = {type, static_cast(num_descriptors), - shader_visible ? D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE : - D3D12_DESCRIPTOR_HEAP_FLAG_NONE}; - - HRESULT hr = device->CreateDescriptorHeap(&desc, IID_PPV_ARGS(&m_descriptor_heap)); - AssertMsg(SUCCEEDED(hr), "Create descriptor heap"); - if (FAILED(hr)) - return false; - - m_heap_base_cpu = m_descriptor_heap->GetCPUDescriptorHandleForHeapStart(); - if (shader_visible) - m_heap_base_gpu = m_descriptor_heap->GetGPUDescriptorHandleForHeapStart(); - m_num_descriptors = num_descriptors; - m_descriptor_increment_size = device->GetDescriptorHandleIncrementSize(type); - - // Set all slots to unallocated (1) - const u32 bitset_count = num_descriptors / BITSET_SIZE + (((num_descriptors % BITSET_SIZE) != 0) ? 1 : 0); - m_free_slots.resize(bitset_count); - for (BitSetType& bs : m_free_slots) - bs.flip(); - - return true; -} - -void DescriptorHeapManager::Destroy() -{ - for (BitSetType& bs : m_free_slots) - Assert(bs.all()); - - m_num_descriptors = 0; - m_descriptor_increment_size = 0; - m_heap_base_cpu = {}; - m_heap_base_gpu = {}; - m_descriptor_heap.Reset(); - m_free_slots.clear(); -} - -bool DescriptorHeapManager::Allocate(DescriptorHandle* handle, u32 count /* = 1 */) -{ - // Start past the temporary slots, no point in searching those. - for (u32 group = 0; group < m_free_slots.size(); group++) - { - BitSetType& bs = m_free_slots[group]; - if (bs.none()) - continue; - - u32 bit = 0; - for (; bit < BITSET_SIZE; bit++) - { - if (bs[bit]) - { - u32 offset; - for (offset = 0; offset < count; offset++) - { - if (!bs[bit + offset]) - break; - } - - if (offset == count) - break; - } - } - - u32 index = group * BITSET_SIZE + bit; - for (u32 offset = 0; offset < count; offset++) - bs[bit + offset] = false; - - handle->index = index; - handle->cpu_handle.ptr = m_heap_base_cpu.ptr + index * m_descriptor_increment_size; - handle->gpu_handle.ptr = m_heap_base_gpu.ptr + index * m_descriptor_increment_size; - return true; - } - - Panic("Out of fixed descriptors"); - return false; -} - -void DescriptorHeapManager::Free(u32 index, u32 count /* = 1 */) -{ - Assert(index < m_num_descriptors); - - for (u32 i = 0; i < count; i++, index++) - { - u32 group = index / BITSET_SIZE; - u32 bit = index % BITSET_SIZE; - m_free_slots[group][bit] = true; - } -} - -void DescriptorHeapManager::Free(DescriptorHandle* handle, u32 count /* = 1 */) -{ - if (handle->index == DescriptorHandle::INVALID_INDEX) - return; - - Free(handle->index, count); - handle->Clear(); -} - -} // namespace D3D12 diff --git a/src/core/gpu/d3d12/descriptor_heap_manager.h b/src/core/gpu/d3d12/descriptor_heap_manager.h deleted file mode 100644 index b05034ae4..000000000 --- a/src/core/gpu/d3d12/descriptor_heap_manager.h +++ /dev/null @@ -1,84 +0,0 @@ -// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin -// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) -// Parts originally from Dolphin Emulator, also written by myself. - -#pragma once - -#include "common/types.h" -#include "common/windows_headers.h" -#include -#include -#include -#include -#include - -namespace D3D12 { -// This class provides an abstraction for D3D12 descriptor heaps. -struct DescriptorHandle final -{ - enum : u32 - { - INVALID_INDEX = 0xFFFFFFFF - }; - - D3D12_CPU_DESCRIPTOR_HANDLE cpu_handle{}; - D3D12_GPU_DESCRIPTOR_HANDLE gpu_handle{}; - u32 index = INVALID_INDEX; - - ALWAYS_INLINE operator bool() const { return index != INVALID_INDEX; } - - ALWAYS_INLINE operator D3D12_CPU_DESCRIPTOR_HANDLE() const { return cpu_handle; } - ALWAYS_INLINE operator D3D12_GPU_DESCRIPTOR_HANDLE() const { return gpu_handle; } - - ALWAYS_INLINE void Clear() - { - cpu_handle = {}; - gpu_handle = {}; - index = INVALID_INDEX; - } -}; - -class DescriptorHeapManager final -{ -public: - DescriptorHeapManager(); - ~DescriptorHeapManager(); - - ALWAYS_INLINE ID3D12DescriptorHeap* GetDescriptorHeap() const { return m_descriptor_heap.Get(); } - ALWAYS_INLINE u32 GetDescriptorIncrementSize() const { return m_descriptor_increment_size; } - - ALWAYS_INLINE D3D12_CPU_DESCRIPTOR_HANDLE OffsetCPUHandle(D3D12_CPU_DESCRIPTOR_HANDLE handle, u32 count) const - { - D3D12_CPU_DESCRIPTOR_HANDLE ret; - ret.ptr = handle.ptr + m_descriptor_increment_size * count; - return ret; - } - - ALWAYS_INLINE D3D12_GPU_DESCRIPTOR_HANDLE OffsetGPUHandle(D3D12_GPU_DESCRIPTOR_HANDLE handle, u32 count) const - { - D3D12_GPU_DESCRIPTOR_HANDLE ret; - ret.ptr = handle.ptr + m_descriptor_increment_size * count; - return ret; - } - - bool Create(ID3D12Device* device, D3D12_DESCRIPTOR_HEAP_TYPE type, u32 num_descriptors, bool shader_visible); - void Destroy(); - - bool Allocate(DescriptorHandle* handle, u32 count = 1); - void Free(DescriptorHandle* handle, u32 count = 1); - void Free(u32 index, u32 count = 1); - -private: - Microsoft::WRL::ComPtr m_descriptor_heap; - u32 m_num_descriptors = 0; - u32 m_descriptor_increment_size = 0; - - D3D12_CPU_DESCRIPTOR_HANDLE m_heap_base_cpu = {}; - D3D12_GPU_DESCRIPTOR_HANDLE m_heap_base_gpu = {}; - - static constexpr u32 BITSET_SIZE = 1024; - using BitSetType = std::bitset; - std::vector m_free_slots = {}; -}; - -} // namespace D3D12 diff --git a/src/core/gpu/d3d12/shader_cache.cpp b/src/core/gpu/d3d12/shader_cache.cpp deleted file mode 100644 index d98458dc1..000000000 --- a/src/core/gpu/d3d12/shader_cache.cpp +++ /dev/null @@ -1,520 +0,0 @@ -// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin -// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) - -#include "shader_cache.h" -#include "common/file_system.h" -#include "common/log.h" -#include "common/md5_digest.h" -#include "common/string_util.h" -#include -#include -#include -Log_SetChannel(D3D12::ShaderCache); - -namespace D3D12 { - -#pragma pack(push, 1) -struct CacheIndexEntry -{ - u64 source_hash_low; - u64 source_hash_high; - u32 source_length; - u32 shader_type; - u32 file_offset; - u32 blob_size; -}; -#pragma pack(pop) - -ShaderCache::ShaderCache() = default; - -ShaderCache::~ShaderCache() -{ - if (m_pipeline_index_file) - std::fclose(m_pipeline_index_file); - if (m_pipeline_blob_file) - std::fclose(m_pipeline_blob_file); - if (m_shader_index_file) - std::fclose(m_shader_index_file); - if (m_shader_blob_file) - std::fclose(m_shader_blob_file); -} - -bool ShaderCache::CacheIndexKey::operator==(const CacheIndexKey& key) const -{ - return (source_hash_low == key.source_hash_low && source_hash_high == key.source_hash_high && - source_length == key.source_length && type == key.type); -} - -bool ShaderCache::CacheIndexKey::operator!=(const CacheIndexKey& key) const -{ - return (source_hash_low != key.source_hash_low || source_hash_high != key.source_hash_high || - source_length != key.source_length || type != key.type); -} - -void ShaderCache::Open(std::string_view base_path, D3D_FEATURE_LEVEL feature_level, bool debug) -{ - m_base_path = base_path; - m_feature_level = feature_level; - m_debug = debug; - - if (!base_path.empty()) - { - const std::string base_shader_filename = GetCacheBaseFileName(base_path, "shaders", feature_level, debug); - const std::string shader_index_filename = base_shader_filename + ".idx"; - const std::string shader_blob_filename = base_shader_filename + ".bin"; - - if (!ReadExisting(shader_index_filename, shader_blob_filename, m_shader_index_file, m_shader_blob_file, - m_shader_index)) - { - CreateNew(shader_index_filename, shader_blob_filename, m_shader_index_file, m_shader_blob_file); - } - - const std::string base_pipelines_filename = GetCacheBaseFileName(base_path, "pipelines", feature_level, debug); - const std::string pipelines_index_filename = base_pipelines_filename + ".idx"; - const std::string pipelines_blob_filename = base_pipelines_filename + ".bin"; - - if (!ReadExisting(pipelines_index_filename, pipelines_blob_filename, m_pipeline_index_file, m_pipeline_blob_file, - m_pipeline_index)) - { - CreateNew(pipelines_index_filename, pipelines_blob_filename, m_pipeline_index_file, m_pipeline_blob_file); - } - } -} - -void ShaderCache::InvalidatePipelineCache() -{ - m_pipeline_index.clear(); - if (m_pipeline_blob_file) - { - std::fclose(m_pipeline_blob_file); - m_pipeline_blob_file = nullptr; - } - - if (m_pipeline_index_file) - { - std::fclose(m_pipeline_index_file); - m_pipeline_index_file = nullptr; - } - - const std::string base_pipelines_filename = GetCacheBaseFileName(m_base_path, "pipelines", m_feature_level, m_debug); - const std::string pipelines_index_filename = base_pipelines_filename + ".idx"; - const std::string pipelines_blob_filename = base_pipelines_filename + ".bin"; - CreateNew(pipelines_index_filename, pipelines_blob_filename, m_pipeline_index_file, m_pipeline_blob_file); -} - -bool ShaderCache::CreateNew(const std::string& index_filename, const std::string& blob_filename, std::FILE*& index_file, - std::FILE*& blob_file) -{ - if (FileSystem::FileExists(index_filename.c_str())) - { - Log_WarningPrintf("Removing existing index file '%s'", index_filename.c_str()); - FileSystem::DeleteFile(index_filename.c_str()); - } - if (FileSystem::FileExists(blob_filename.c_str())) - { - Log_WarningPrintf("Removing existing blob file '%s'", blob_filename.c_str()); - FileSystem::DeleteFile(blob_filename.c_str()); - } - - index_file = FileSystem::OpenCFile(index_filename.c_str(), "wb"); - if (!index_file) - { - Log_ErrorPrintf("Failed to open index file '%s' for writing", index_filename.c_str()); - return false; - } - - const u32 index_version = FILE_VERSION; - if (std::fwrite(&index_version, sizeof(index_version), 1, index_file) != 1) - { - Log_ErrorPrintf("Failed to write version to index file '%s'", index_filename.c_str()); - std::fclose(index_file); - index_file = nullptr; - FileSystem::DeleteFile(index_filename.c_str()); - return false; - } - - blob_file = FileSystem::OpenCFile(blob_filename.c_str(), "w+b"); - if (!blob_file) - { - Log_ErrorPrintf("Failed to open blob file '%s' for writing", blob_filename.c_str()); - std::fclose(blob_file); - blob_file = nullptr; - FileSystem::DeleteFile(index_filename.c_str()); - return false; - } - - return true; -} - -bool ShaderCache::ReadExisting(const std::string& index_filename, const std::string& blob_filename, - std::FILE*& index_file, std::FILE*& blob_file, CacheIndex& index) -{ - index_file = FileSystem::OpenCFile(index_filename.c_str(), "r+b"); - if (!index_file) - return false; - - u32 file_version; - if (std::fread(&file_version, sizeof(file_version), 1, index_file) != 1 || file_version != FILE_VERSION) - { - Log_ErrorPrintf("Bad file version in '%s'", index_filename.c_str()); - std::fclose(index_file); - index_file = nullptr; - return false; - } - - blob_file = FileSystem::OpenCFile(blob_filename.c_str(), "a+b"); - if (!blob_file) - { - Log_ErrorPrintf("Blob file '%s' is missing", blob_filename.c_str()); - std::fclose(index_file); - index_file = nullptr; - return false; - } - - std::fseek(blob_file, 0, SEEK_END); - const u32 blob_file_size = static_cast(std::ftell(blob_file)); - - for (;;) - { - CacheIndexEntry entry; - if (std::fread(&entry, sizeof(entry), 1, index_file) != 1 || (entry.file_offset + entry.blob_size) > blob_file_size) - { - if (std::feof(index_file)) - break; - - Log_ErrorPrintf("Failed to read entry from '%s', corrupt file?", index_filename.c_str()); - index.clear(); - std::fclose(blob_file); - blob_file = nullptr; - std::fclose(index_file); - index_file = nullptr; - return false; - } - - const CacheIndexKey key{entry.source_hash_low, entry.source_hash_high, entry.source_length, - static_cast(entry.shader_type)}; - const CacheIndexData data{entry.file_offset, entry.blob_size}; - index.emplace(key, data); - } - - // ensure we don't write before seeking - std::fseek(index_file, 0, SEEK_END); - - Log_InfoPrintf("Read %zu entries from '%s'", index.size(), index_filename.c_str()); - return true; -} - -std::string ShaderCache::GetCacheBaseFileName(const std::string_view& base_path, const std::string_view& type, - D3D_FEATURE_LEVEL feature_level, bool debug) -{ - std::string base_filename(base_path); - base_filename += FS_OSPATH_SEPARATOR_STR "d3d12_"; - base_filename += type; - base_filename += "_"; - - switch (feature_level) - { - case D3D_FEATURE_LEVEL_10_0: - base_filename += "sm40"; - break; - case D3D_FEATURE_LEVEL_10_1: - base_filename += "sm41"; - break; - case D3D_FEATURE_LEVEL_11_0: - base_filename += "sm50"; - break; - default: - base_filename += "unk"; - break; - } - - if (debug) - base_filename += "_debug"; - - return base_filename; -} - -union MD5Hash -{ - struct - { - u64 low; - u64 high; - }; - u8 hash[16]; -}; - -ShaderCache::CacheIndexKey ShaderCache::GetShaderCacheKey(EntryType type, const std::string_view& shader_code) -{ - MD5Hash h; - MD5Digest digest; - digest.Update(shader_code.data(), static_cast(shader_code.length())); - digest.Final(h.hash); - - return CacheIndexKey{h.low, h.high, static_cast(shader_code.length()), type}; -} - -ShaderCache::CacheIndexKey ShaderCache::GetPipelineCacheKey(const D3D12_GRAPHICS_PIPELINE_STATE_DESC& gpdesc) -{ - MD5Digest digest; - u32 length = sizeof(D3D12_GRAPHICS_PIPELINE_STATE_DESC); - - if (gpdesc.VS.BytecodeLength > 0) - { - digest.Update(gpdesc.VS.pShaderBytecode, static_cast(gpdesc.VS.BytecodeLength)); - length += static_cast(gpdesc.VS.BytecodeLength); - } - if (gpdesc.GS.BytecodeLength > 0) - { - digest.Update(gpdesc.GS.pShaderBytecode, static_cast(gpdesc.GS.BytecodeLength)); - length += static_cast(gpdesc.GS.BytecodeLength); - } - if (gpdesc.PS.BytecodeLength > 0) - { - digest.Update(gpdesc.PS.pShaderBytecode, static_cast(gpdesc.PS.BytecodeLength)); - length += static_cast(gpdesc.PS.BytecodeLength); - } - - digest.Update(&gpdesc.BlendState, sizeof(gpdesc.BlendState)); - digest.Update(&gpdesc.SampleMask, sizeof(gpdesc.SampleMask)); - digest.Update(&gpdesc.RasterizerState, sizeof(gpdesc.RasterizerState)); - digest.Update(&gpdesc.DepthStencilState, sizeof(gpdesc.DepthStencilState)); - - for (u32 i = 0; i < gpdesc.InputLayout.NumElements; i++) - { - const D3D12_INPUT_ELEMENT_DESC& ie = gpdesc.InputLayout.pInputElementDescs[i]; - digest.Update(ie.SemanticName, static_cast(std::strlen(ie.SemanticName))); - digest.Update(&ie.SemanticIndex, sizeof(ie.SemanticIndex)); - digest.Update(&ie.Format, sizeof(ie.Format)); - digest.Update(&ie.InputSlot, sizeof(ie.InputSlot)); - digest.Update(&ie.AlignedByteOffset, sizeof(ie.AlignedByteOffset)); - digest.Update(&ie.InputSlotClass, sizeof(ie.InputSlotClass)); - digest.Update(&ie.InstanceDataStepRate, sizeof(ie.InstanceDataStepRate)); - length += sizeof(D3D12_INPUT_ELEMENT_DESC); - } - - digest.Update(&gpdesc.IBStripCutValue, sizeof(gpdesc.IBStripCutValue)); - digest.Update(&gpdesc.PrimitiveTopologyType, sizeof(gpdesc.PrimitiveTopologyType)); - digest.Update(&gpdesc.NumRenderTargets, sizeof(gpdesc.NumRenderTargets)); - digest.Update(gpdesc.RTVFormats, sizeof(gpdesc.RTVFormats)); - digest.Update(&gpdesc.DSVFormat, sizeof(gpdesc.DSVFormat)); - digest.Update(&gpdesc.SampleDesc, sizeof(gpdesc.SampleDesc)); - digest.Update(&gpdesc.Flags, sizeof(gpdesc.Flags)); - - MD5Hash h; - digest.Final(h.hash); - - return CacheIndexKey{h.low, h.high, length, EntryType::GraphicsPipeline}; -} - -ShaderCache::ComPtr ShaderCache::GetShaderBlob(EntryType type, std::string_view shader_code) -{ - const auto key = GetShaderCacheKey(type, shader_code); - auto iter = m_shader_index.find(key); - if (iter == m_shader_index.end()) - return CompileAndAddShaderBlob(key, shader_code); - - ComPtr blob; - HRESULT hr = D3DCreateBlob(iter->second.blob_size, blob.GetAddressOf()); - if (FAILED(hr) || std::fseek(m_shader_blob_file, iter->second.file_offset, SEEK_SET) != 0 || - std::fread(blob->GetBufferPointer(), 1, iter->second.blob_size, m_shader_blob_file) != iter->second.blob_size) - { - Log_ErrorPrintf("Read blob from file failed"); - return {}; - } - - return blob; -} - -ShaderCache::ComPtr ShaderCache::GetPipelineState(ID3D12Device* device, - const D3D12_GRAPHICS_PIPELINE_STATE_DESC& desc) -{ - const auto key = GetPipelineCacheKey(desc); - - auto iter = m_pipeline_index.find(key); - if (iter == m_pipeline_index.end()) - return CompileAndAddPipeline(device, key, desc); - - ComPtr blob; - HRESULT hr = D3DCreateBlob(iter->second.blob_size, blob.GetAddressOf()); - if (FAILED(hr) || std::fseek(m_pipeline_blob_file, iter->second.file_offset, SEEK_SET) != 0 || - std::fread(blob->GetBufferPointer(), 1, iter->second.blob_size, m_pipeline_blob_file) != iter->second.blob_size) - { - Log_ErrorPrintf("Read blob from file failed"); - return {}; - } - - D3D12_GRAPHICS_PIPELINE_STATE_DESC desc_with_blob(desc); - desc_with_blob.CachedPSO.pCachedBlob = blob->GetBufferPointer(); - desc_with_blob.CachedPSO.CachedBlobSizeInBytes = blob->GetBufferSize(); - - ComPtr pso; - hr = device->CreateGraphicsPipelineState(&desc_with_blob, IID_PPV_ARGS(pso.GetAddressOf())); - if (FAILED(hr)) - { - Log_WarningPrintf("Creating cached PSO failed: %08X. Invalidating cache.", hr); - InvalidatePipelineCache(); - pso = CompileAndAddPipeline(device, key, desc); - } - - return pso; -} - -static unsigned s_next_bad_shader_id = 1; - -ShaderCache::ComPtr ShaderCache::CompileShader(EntryType type, D3D_FEATURE_LEVEL feature_level, std::string_view code, bool debug) -{ - const char* target; - switch (feature_level) - { - case D3D_FEATURE_LEVEL_10_0: - { - static constexpr std::array targets = {{"vs_4_0", "gs_4_0", "ps_4_0", "cs_4_0"}}; - target = targets[static_cast(type)]; - } - break; - - case D3D_FEATURE_LEVEL_10_1: - { - static constexpr std::array targets = {{"vs_4_1", "gs_4_1", "ps_4_1", "cs_4_1"}}; - target = targets[static_cast(type)]; - } - break; - - case D3D_FEATURE_LEVEL_11_0: - { - static constexpr std::array targets = {{"vs_5_0", "gs_5_0", "ps_5_0", "cs_5_0"}}; - target = targets[static_cast(type)]; - } - break; - - case D3D_FEATURE_LEVEL_11_1: - default: - { - static constexpr std::array targets = {{"vs_5_1", "gs_5_1", "ps_5_1", "cs_5_1"}}; - target = targets[static_cast(type)]; - } - break; - } - - static constexpr UINT flags_non_debug = D3DCOMPILE_OPTIMIZATION_LEVEL3; - static constexpr UINT flags_debug = D3DCOMPILE_SKIP_OPTIMIZATION | D3DCOMPILE_DEBUG; - - ComPtr blob; - ComPtr error_blob; - const HRESULT hr = - D3DCompile(code.data(), code.size(), "0", nullptr, nullptr, "main", target, debug ? flags_debug : flags_non_debug, - 0, blob.GetAddressOf(), error_blob.GetAddressOf()); - - std::string error_string; - if (error_blob) - { - error_string.append(static_cast(error_blob->GetBufferPointer()), error_blob->GetBufferSize()); - error_blob.Reset(); - } - - if (FAILED(hr)) - { - Log_ErrorPrintf("Failed to compile '%s':\n%s", target, error_string.c_str()); - - std::ofstream ofs(StringUtil::StdStringFromFormat("bad_shader_%u.txt", s_next_bad_shader_id++).c_str(), - std::ofstream::out | std::ofstream::binary); - if (ofs.is_open()) - { - ofs << code; - ofs << "\n\nCompile as " << target << " failed: " << hr << "\n"; - ofs.write(error_string.c_str(), error_string.size()); - ofs.close(); - } - - return {}; - } - - if (!error_string.empty()) - Log_WarningPrintf("'%s' compiled with warnings:\n%s", target, error_string.c_str()); - - return blob; -} - -ShaderCache::ComPtr ShaderCache::CompileAndAddShaderBlob(const CacheIndexKey& key, - std::string_view shader_code) -{ - ComPtr blob = CompileShader(key.type, m_feature_level, shader_code, m_debug); - if (!blob) - return {}; - - if (!m_shader_blob_file || std::fseek(m_shader_blob_file, 0, SEEK_END) != 0) - return blob; - - CacheIndexData data; - data.file_offset = static_cast(std::ftell(m_shader_blob_file)); - data.blob_size = static_cast(blob->GetBufferSize()); - - CacheIndexEntry entry = {}; - entry.source_hash_low = key.source_hash_low; - entry.source_hash_high = key.source_hash_high; - entry.source_length = key.source_length; - entry.shader_type = static_cast(key.type); - entry.blob_size = data.blob_size; - entry.file_offset = data.file_offset; - - if (std::fwrite(blob->GetBufferPointer(), 1, entry.blob_size, m_shader_blob_file) != entry.blob_size || - std::fflush(m_shader_blob_file) != 0 || std::fwrite(&entry, sizeof(entry), 1, m_shader_index_file) != 1 || - std::fflush(m_shader_index_file) != 0) - { - Log_ErrorPrintf("Failed to write shader blob to file"); - return blob; - } - - m_shader_index.emplace(key, data); - return blob; -} - -ShaderCache::ComPtr -ShaderCache::CompileAndAddPipeline(ID3D12Device* device, const CacheIndexKey& key, - const D3D12_GRAPHICS_PIPELINE_STATE_DESC& gpdesc) -{ - ComPtr pso; - HRESULT hr = device->CreateGraphicsPipelineState(&gpdesc, IID_PPV_ARGS(pso.GetAddressOf())); - if (FAILED(hr)) - { - Log_ErrorPrintf("Creating cached PSO failed: %08X", hr); - return {}; - } - - if (!m_pipeline_blob_file || std::fseek(m_pipeline_blob_file, 0, SEEK_END) != 0) - return pso; - - ComPtr blob; - hr = pso->GetCachedBlob(blob.GetAddressOf()); - if (FAILED(hr)) - { - Log_WarningPrintf("Failed to get cached PSO data: %08X", hr); - return pso; - } - - CacheIndexData data; - data.file_offset = static_cast(std::ftell(m_pipeline_blob_file)); - data.blob_size = static_cast(blob->GetBufferSize()); - - CacheIndexEntry entry = {}; - entry.source_hash_low = key.source_hash_low; - entry.source_hash_high = key.source_hash_high; - entry.source_length = key.source_length; - entry.shader_type = static_cast(key.type); - entry.blob_size = data.blob_size; - entry.file_offset = data.file_offset; - - if (std::fwrite(blob->GetBufferPointer(), 1, entry.blob_size, m_pipeline_blob_file) != entry.blob_size || - std::fflush(m_pipeline_blob_file) != 0 || std::fwrite(&entry, sizeof(entry), 1, m_pipeline_index_file) != 1 || - std::fflush(m_pipeline_index_file) != 0) - { - Log_ErrorPrintf("Failed to write pipeline blob to file"); - return pso; - } - - m_shader_index.emplace(key, data); - return pso; -} - -} // namespace D3D12 diff --git a/src/core/gpu/d3d12/shader_cache.h b/src/core/gpu/d3d12/shader_cache.h deleted file mode 100644 index 1f1cfa855..000000000 --- a/src/core/gpu/d3d12/shader_cache.h +++ /dev/null @@ -1,122 +0,0 @@ -// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin -// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) - -#pragma once -#include "common/hash_combine.h" -#include "common/types.h" -#include "common/windows_headers.h" -#include -#include -#include -#include -#include -#include - -namespace D3D12 { - -class ShaderCache -{ -public: - template - using ComPtr = Microsoft::WRL::ComPtr; - - enum class EntryType - { - VertexShader, - GeometryShader, - PixelShader, - ComputeShader, - GraphicsPipeline, - }; - - ShaderCache(); - ~ShaderCache(); - - void Open(std::string_view base_path, D3D_FEATURE_LEVEL feature_level, bool debug); - - ALWAYS_INLINE ComPtr GetVertexShader(std::string_view shader_code) - { - return GetShaderBlob(EntryType::VertexShader, shader_code); - } - ALWAYS_INLINE ComPtr GetGeometryShader(std::string_view shader_code) - { - return GetShaderBlob(EntryType::GeometryShader, shader_code); - } - ALWAYS_INLINE ComPtr GetPixelShader(std::string_view shader_code) - { - return GetShaderBlob(EntryType::PixelShader, shader_code); - } - ALWAYS_INLINE ComPtr GetComputeShader(std::string_view shader_code) - { - return GetShaderBlob(EntryType::ComputeShader, shader_code); - } - - ComPtr GetShaderBlob(EntryType type, std::string_view shader_code); - - ComPtr GetPipelineState(ID3D12Device* device, const D3D12_GRAPHICS_PIPELINE_STATE_DESC& desc); - -private: - static constexpr u32 FILE_VERSION = 1; - - struct CacheIndexKey - { - u64 source_hash_low; - u64 source_hash_high; - u32 source_length; - EntryType type; - - bool operator==(const CacheIndexKey& key) const; - bool operator!=(const CacheIndexKey& key) const; - }; - - struct CacheIndexEntryHasher - { - std::size_t operator()(const CacheIndexKey& e) const noexcept - { - std::size_t h = 0; - hash_combine(h, e.source_hash_low, e.source_hash_high, e.source_length, e.type); - return h; - } - }; - - struct CacheIndexData - { - u32 file_offset; - u32 blob_size; - }; - - using CacheIndex = std::unordered_map; - - static ComPtr CompileShader(EntryType type, D3D_FEATURE_LEVEL feature_level, std::string_view code, bool debug); - - static std::string GetCacheBaseFileName(const std::string_view& base_path, const std::string_view& type, - D3D_FEATURE_LEVEL feature_level, bool debug); - static CacheIndexKey GetShaderCacheKey(EntryType type, const std::string_view& shader_code); - static CacheIndexKey GetPipelineCacheKey(const D3D12_GRAPHICS_PIPELINE_STATE_DESC& gpdesc); - - bool CreateNew(const std::string& index_filename, const std::string& blob_filename, std::FILE*& index_file, - std::FILE*& blob_file); - bool ReadExisting(const std::string& index_filename, const std::string& blob_filename, std::FILE*& index_file, - std::FILE*& blob_file, CacheIndex& index); - void InvalidatePipelineCache(); - void Close(); - - ComPtr CompileAndAddShaderBlob(const CacheIndexKey& key, std::string_view shader_code); - ComPtr CompileAndAddPipeline(ID3D12Device* device, const CacheIndexKey& key, - const D3D12_GRAPHICS_PIPELINE_STATE_DESC& gpdesc); - - std::string m_base_path; - - std::FILE* m_shader_index_file = nullptr; - std::FILE* m_shader_blob_file = nullptr; - CacheIndex m_shader_index; - - std::FILE* m_pipeline_index_file = nullptr; - std::FILE* m_pipeline_blob_file = nullptr; - CacheIndex m_pipeline_index; - - D3D_FEATURE_LEVEL m_feature_level = D3D_FEATURE_LEVEL_11_0; - bool m_debug = false; -}; - -} // namespace D3D12 diff --git a/src/core/gpu/d3d12/staging_texture.cpp b/src/core/gpu/d3d12/staging_texture.cpp deleted file mode 100644 index 14744a4f2..000000000 --- a/src/core/gpu/d3d12/staging_texture.cpp +++ /dev/null @@ -1,239 +0,0 @@ -// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin -// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) - -#include "staging_texture.h" -#include "common/align.h" -#include "common/assert.h" -#include "common/log.h" -#include "context.h" -#include "util.h" -Log_SetChannel(D3D12); - -namespace D3D12 { - -StagingTexture::StagingTexture() : m_width(0), m_height(0) {} - -StagingTexture::~StagingTexture() -{ - Destroy(); -} - -bool StagingTexture::Create(u32 width, u32 height, DXGI_FORMAT format, bool for_uploading) -{ - const u32 texel_size = GetTexelSize(format); - const u32 row_pitch = Common::AlignUpPow2(width * texel_size, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT); - const u32 buffer_size = height * row_pitch; - - const D3D12_HEAP_PROPERTIES heap_properties = {for_uploading ? D3D12_HEAP_TYPE_UPLOAD : D3D12_HEAP_TYPE_READBACK}; - - D3D12_RESOURCE_DESC desc = {}; - desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; - desc.Width = buffer_size; - desc.Height = 1; - desc.DepthOrArraySize = 1; - desc.MipLevels = 1; - desc.Format = DXGI_FORMAT_UNKNOWN; - desc.SampleDesc.Count = 1; - desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; - desc.Flags = D3D12_RESOURCE_FLAG_NONE; - - D3D12_RESOURCE_STATES state = for_uploading ? D3D12_RESOURCE_STATE_GENERIC_READ : D3D12_RESOURCE_STATE_COPY_DEST; - - ComPtr resource; - HRESULT hr = g_d3d12_context->GetDevice()->CreateCommittedResource( - &heap_properties, D3D12_HEAP_FLAG_NONE, &desc, state, nullptr, IID_PPV_ARGS(resource.GetAddressOf())); - if (FAILED(hr)) - { - Log_ErrorPrintf("Create buffer failed: 0x%08X", hr); - return false; - } - - Destroy(true); - - m_resource = std::move(resource); - m_width = width; - m_height = height; - m_format = format; - m_buffer_size = buffer_size; - m_row_pitch = row_pitch; - m_texel_size = texel_size; - return true; -} - -void StagingTexture::Destroy(bool defer) -{ - if (IsMapped()) - Unmap(); - - if (m_resource && defer) - g_d3d12_context->DeferResourceDestruction(m_resource.Get()); - m_resource.Reset(); - m_width = 0; - m_height = 0; - m_format = DXGI_FORMAT_UNKNOWN; - m_buffer_size = 0; - m_row_pitch = 0; - m_texel_size = 0; -} - -bool StagingTexture::Map(bool writing) -{ - D3D12_RANGE range{0u, m_buffer_size}; - - Assert(!IsMapped()); - const HRESULT hr = m_resource->Map(0, writing ? nullptr : &range, &m_mapped_pointer); - if (FAILED(hr)) - { - Log_ErrorPrintf("Map staging buffer failed: 0x%08X", hr); - return false; - } - - m_mapped_for_write = writing; - return true; -} - -void StagingTexture::Unmap() -{ - Assert(IsMapped()); - - D3D12_RANGE range{0u, m_buffer_size}; - m_resource->Unmap(0, m_mapped_for_write ? &range : nullptr); - m_mapped_pointer = nullptr; - m_mapped_for_write = false; -} - -void StagingTexture::Flush() -{ - if (!m_needs_flush) - return; - - m_needs_flush = false; - - // If the completed fence is the same as the current command buffer fence, we need to execute - // the current list and wait for it to complete. This is the slowest path. Otherwise, if the - // command list with the copy has been submitted, we only need to wait for the fence. - if (m_completed_fence == g_d3d12_context->GetCurrentFenceValue()) - g_d3d12_context->ExecuteCommandList(true); - else - g_d3d12_context->WaitForFence(m_completed_fence); -} - -void StagingTexture::CopyToTexture(u32 src_x, u32 src_y, ID3D12Resource* dst_texture, u32 dst_subresource, u32 dst_x, - u32 dst_y, u32 width, u32 height) -{ - DebugAssert((src_x + width) <= m_width && (src_y + height) <= m_height); - - D3D12_TEXTURE_COPY_LOCATION dst; - dst.pResource = dst_texture; - dst.SubresourceIndex = 0; - dst.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; - - D3D12_TEXTURE_COPY_LOCATION src; - src.pResource = m_resource.Get(); - src.SubresourceIndex = 0; - src.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; - src.PlacedFootprint.Offset = 0; - src.PlacedFootprint.Footprint.Width = m_width; - src.PlacedFootprint.Footprint.Height = m_height; - src.PlacedFootprint.Footprint.Depth = 1; - src.PlacedFootprint.Footprint.Format = m_format; - src.PlacedFootprint.Footprint.RowPitch = m_row_pitch; - - const D3D12_BOX src_box{src_x, src_y, 0u, src_x + width, src_y + height, 1u}; - g_d3d12_context->GetCommandList()->CopyTextureRegion(&dst, dst_x, dst_y, 0, &src, &src_box); -} - -void StagingTexture::CopyFromTexture(ID3D12Resource* src_texture, u32 src_subresource, u32 src_x, u32 src_y, u32 dst_x, - u32 dst_y, u32 width, u32 height) -{ - DebugAssert((dst_x + width) <= m_width && (dst_y + height) <= m_height); - - D3D12_TEXTURE_COPY_LOCATION src; - src.pResource = src_texture; - src.SubresourceIndex = 0; - src.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; - - D3D12_TEXTURE_COPY_LOCATION dst; - dst.pResource = m_resource.Get(); - dst.SubresourceIndex = 0; - dst.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; - dst.PlacedFootprint.Offset = 0; - dst.PlacedFootprint.Footprint.Width = m_width; - dst.PlacedFootprint.Footprint.Height = m_height; - dst.PlacedFootprint.Footprint.Depth = 1; - dst.PlacedFootprint.Footprint.Format = m_format; - dst.PlacedFootprint.Footprint.RowPitch = m_row_pitch; - - const D3D12_BOX src_box{src_x, src_y, 0u, src_x + width, src_y + height, 1u}; - g_d3d12_context->GetCommandList()->CopyTextureRegion(&dst, dst_x, dst_y, 0, &src, &src_box); - m_completed_fence = g_d3d12_context->GetCurrentFenceValue(); - m_needs_flush = true; -} - -bool StagingTexture::ReadPixels(u32 x, u32 y, u32 width, u32 height, void* data, u32 row_pitch) -{ - if (m_needs_flush) - Flush(); - - const bool was_mapped = IsMapped(); - if (!was_mapped && !Map(false)) - return false; - - const u8* src_ptr = static_cast(m_mapped_pointer) + (y * m_row_pitch) + (x * m_texel_size); - u8* dst_ptr = reinterpret_cast(data); - if (m_row_pitch != row_pitch || width != m_width || x != 0) - { - const u32 copy_size = m_texel_size * width; - for (u32 row = 0; row < height; row++) - { - std::memcpy(dst_ptr, src_ptr, copy_size); - src_ptr += m_row_pitch; - dst_ptr += row_pitch; - } - } - else - { - std::memcpy(dst_ptr, src_ptr, row_pitch * height); - } - - return true; -} - -bool StagingTexture::WritePixels(u32 x, u32 y, u32 width, u32 height, const void* data, u32 row_pitch) -{ - const bool was_mapped = IsMapped(); - if (!was_mapped && !Map(true)) - return false; - - const u8* src_ptr = reinterpret_cast(data); - u8* dst_ptr = static_cast(m_mapped_pointer) + (y * m_row_pitch) + (x * m_texel_size); - if (m_row_pitch != row_pitch || width != m_width || x != 0) - { - const u32 copy_size = m_texel_size * width; - for (u32 row = 0; row < height; row++) - { - std::memcpy(dst_ptr, src_ptr, copy_size); - src_ptr += row_pitch; - dst_ptr += m_row_pitch; - } - } - else - { - std::memcpy(dst_ptr, src_ptr, m_row_pitch * height); - } - - if (!was_mapped) - Unmap(); - - return true; -} - -bool StagingTexture::EnsureSize(u32 width, u32 height, DXGI_FORMAT format, bool for_uploading) -{ - if (m_resource && m_width >= width && m_height >= height && m_format == format) - return true; - - return Create(width, height, format, for_uploading); -} - -} // namespace D3D12 \ No newline at end of file diff --git a/src/core/gpu/d3d12/staging_texture.h b/src/core/gpu/d3d12/staging_texture.h deleted file mode 100644 index 11e95e335..000000000 --- a/src/core/gpu/d3d12/staging_texture.h +++ /dev/null @@ -1,64 +0,0 @@ -// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin -// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) - -#pragma once -#include "common/types.h" -#include "common/windows_headers.h" -#include -#include -#include - -namespace D3D12 { -class StagingTexture -{ -public: - template - using ComPtr = Microsoft::WRL::ComPtr; - - StagingTexture(); - ~StagingTexture(); - - ALWAYS_INLINE ID3D12Resource* GetD3DResource() const { return m_resource.Get(); } - - ALWAYS_INLINE u32 GetWidth() const { return m_width; } - ALWAYS_INLINE u32 GetHeight() const { return m_height; } - ALWAYS_INLINE DXGI_FORMAT GetFormat() const { return m_format; } - ALWAYS_INLINE bool IsMapped() const { return m_mapped_pointer != nullptr; } - ALWAYS_INLINE const void* GetMapPointer() const { return m_mapped_pointer; } - - ALWAYS_INLINE operator bool() const { return static_cast(m_resource); } - - bool Create(u32 width, u32 height, DXGI_FORMAT format, bool for_uploading); - void Destroy(bool defer = true); - - bool Map(bool writing); - void Unmap(); - void Flush(); - - void CopyToTexture(u32 src_x, u32 src_y, ID3D12Resource* dst_texture, u32 dst_subresource, u32 dst_x, u32 dst_y, - u32 width, u32 height); - void CopyFromTexture(ID3D12Resource* src_texture, u32 src_subresource, u32 src_x, u32 src_y, u32 dst_x, u32 dst_y, - u32 width, u32 height); - - bool ReadPixels(u32 x, u32 y, u32 width, u32 height, void* data, u32 row_pitch); - - bool WritePixels(u32 x, u32 y, u32 width, u32 height, const void* data, u32 row_pitch); - - bool EnsureSize(u32 width, u32 height, DXGI_FORMAT format, bool for_uploading); - -protected: - ComPtr m_resource; - u32 m_width; - u32 m_height; - DXGI_FORMAT m_format; - u32 m_texel_size; - u32 m_row_pitch; - u32 m_buffer_size; - - void* m_mapped_pointer = nullptr; - u64 m_completed_fence = 0; - bool m_mapped_for_write = false; - bool m_needs_flush = false; -}; - -} // namespace D3D12 \ No newline at end of file diff --git a/src/core/gpu/d3d12/texture.cpp b/src/core/gpu/d3d12/texture.cpp deleted file mode 100644 index 28add3ec1..000000000 --- a/src/core/gpu/d3d12/texture.cpp +++ /dev/null @@ -1,466 +0,0 @@ -// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin -// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) - -#include "texture.h" -#include "common/align.h" -#include "common/assert.h" -#include "common/log.h" -#include "context.h" -#include "staging_texture.h" -#include "stream_buffer.h" -#include "util.h" -Log_SetChannel(D3D12); - -static constexpr std::array(GPUTexture::Format::MaxCount)> s_dxgi_mapping = { - {DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_B5G6R5_UNORM, - DXGI_FORMAT_B5G5R5A1_UNORM, DXGI_FORMAT_R8_UNORM, DXGI_FORMAT_D16_UNORM}}; - -D3D12::Texture::Texture() = default; - -D3D12::Texture::Texture(ID3D12Resource* resource, D3D12_RESOURCE_STATES state) : m_resource(std::move(resource)) -{ - const D3D12_RESOURCE_DESC desc = GetDesc(); - m_width = static_cast(desc.Width); - m_height = static_cast(desc.Height); - m_layers = static_cast(desc.DepthOrArraySize); - m_levels = static_cast(desc.MipLevels); - m_samples = static_cast(desc.SampleDesc.Count); - m_format = LookupBaseFormat(desc.Format); -} - -D3D12::Texture::Texture(Texture&& texture) - : m_resource(std::move(texture.m_resource)), m_srv_descriptor(texture.m_srv_descriptor), - m_rtv_or_dsv_descriptor(texture.m_rtv_or_dsv_descriptor), m_is_depth_view(texture.m_is_depth_view) -{ - m_width = texture.m_width; - m_height = texture.m_height; - m_layers = texture.m_layers; - m_levels = texture.m_levels; - m_samples = texture.m_samples; - texture.m_srv_descriptor = {}; - texture.m_rtv_or_dsv_descriptor = {}; - texture.m_state = D3D12_RESOURCE_STATE_COMMON; - texture.m_is_depth_view = false; - texture.ClearBaseProperties(); -} - -DXGI_FORMAT D3D12::Texture::GetDXGIFormat(Format format) -{ - return s_dxgi_mapping[static_cast(format)]; -} - -GPUTexture::Format D3D12::Texture::LookupBaseFormat(DXGI_FORMAT dformat) -{ - for (u32 i = 0; i < static_cast(s_dxgi_mapping.size()); i++) - { - if (s_dxgi_mapping[i] == dformat) - return static_cast(i); - } - return GPUTexture::Format::Unknown; -} - -D3D12::Texture::~Texture() -{ - Destroy(); -} - -D3D12::Texture& D3D12::Texture::operator=(Texture&& texture) -{ - Destroy(); - - m_width = texture.m_width; - m_height = texture.m_height; - m_layers = texture.m_layers; - m_levels = texture.m_levels; - m_samples = texture.m_samples; - - m_resource = std::move(texture.m_resource); - m_srv_descriptor = texture.m_srv_descriptor; - m_rtv_or_dsv_descriptor = texture.m_rtv_or_dsv_descriptor; - m_state = texture.m_state; - m_is_depth_view = texture.m_is_depth_view; - - texture.ClearBaseProperties(); - texture.m_srv_descriptor = {}; - texture.m_rtv_or_dsv_descriptor = {}; - texture.m_state = D3D12_RESOURCE_STATE_COMMON; - texture.m_is_depth_view = false; - return *this; -} - -D3D12_RESOURCE_DESC D3D12::Texture::GetDesc() const -{ - return m_resource->GetDesc(); -} - -bool D3D12::Texture::IsValid() const -{ - return static_cast(m_resource); -} - -bool D3D12::Texture::Update(u32 x, u32 y, u32 width, u32 height, const void* data, u32 pitch, u32 layer /*= 0*/, - u32 level /*= 0*/) -{ - UnreachableCode(); - return false; -} - -bool D3D12::Texture::Map(void** map, u32* map_stride, u32 x, u32 y, u32 width, u32 height, u32 layer /*= 0*/, - u32 level /*= 0*/) -{ - UnreachableCode(); - return false; -} - -void D3D12::Texture::Unmap() -{ - UnreachableCode(); -} - -bool D3D12::Texture::Create(u32 width, u32 height, u32 layers, u32 levels, u32 samples, DXGI_FORMAT format, - DXGI_FORMAT srv_format, DXGI_FORMAT rtv_format, DXGI_FORMAT dsv_format, - D3D12_RESOURCE_FLAGS flags) -{ - constexpr D3D12_HEAP_PROPERTIES heap_properties = {D3D12_HEAP_TYPE_DEFAULT}; - - if (width > MAX_WIDTH || height > MAX_HEIGHT || layers > MAX_LAYERS || levels > MAX_LEVELS || samples > MAX_SAMPLES) - { - Log_ErrorPrintf("Invalid dimensions: %ux%ux%u %u %u", width, height, layers, levels, samples); - return false; - } - - D3D12_RESOURCE_DESC desc = {}; - desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; - desc.Width = width; - desc.Height = static_cast(height); - desc.DepthOrArraySize = static_cast(layers); - desc.MipLevels = static_cast(levels); - desc.Format = format; - desc.SampleDesc.Count = samples; - desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; - desc.Flags = flags; - - D3D12_CLEAR_VALUE optimized_clear_value = {}; - D3D12_RESOURCE_STATES state; - if (rtv_format != DXGI_FORMAT_UNKNOWN) - { - optimized_clear_value.Format = rtv_format; - state = D3D12_RESOURCE_STATE_RENDER_TARGET; - } - else if (dsv_format != DXGI_FORMAT_UNKNOWN) - { - optimized_clear_value.Format = dsv_format; - state = D3D12_RESOURCE_STATE_DEPTH_WRITE; - } - else - { - state = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; - } - - ComPtr resource; - HRESULT hr = g_d3d12_context->GetDevice()->CreateCommittedResource( - &heap_properties, D3D12_HEAP_FLAG_NONE, &desc, state, - (rtv_format != DXGI_FORMAT_UNKNOWN || dsv_format != DXGI_FORMAT_UNKNOWN) ? &optimized_clear_value : nullptr, - IID_PPV_ARGS(resource.GetAddressOf())); - if (FAILED(hr)) - { - Log_ErrorPrintf("Create texture failed: 0x%08X", hr); - return false; - } - - DescriptorHandle srv_descriptor, rtv_descriptor; - bool is_depth_view = false; - if (srv_format != DXGI_FORMAT_UNKNOWN) - { - if (!CreateSRVDescriptor(resource.Get(), srv_format, samples > 1, &srv_descriptor)) - return false; - } - - if (rtv_format != DXGI_FORMAT_UNKNOWN) - { - Assert(dsv_format == DXGI_FORMAT_UNKNOWN); - if (!CreateRTVDescriptor(resource.Get(), rtv_format, samples > 1, &rtv_descriptor)) - { - g_d3d12_context->GetDescriptorHeapManager().Free(&srv_descriptor); - return false; - } - } - else if (dsv_format != DXGI_FORMAT_UNKNOWN) - { - if (!CreateDSVDescriptor(resource.Get(), dsv_format, samples > 1, &rtv_descriptor)) - { - g_d3d12_context->GetDescriptorHeapManager().Free(&srv_descriptor); - return false; - } - - is_depth_view = true; - } - - Destroy(true); - - m_resource = std::move(resource); - m_srv_descriptor = std::move(srv_descriptor); - m_rtv_or_dsv_descriptor = std::move(rtv_descriptor); - m_width = static_cast(width); - m_height = static_cast(height); - m_layers = static_cast(layers); - m_levels = static_cast(levels); - m_samples = static_cast(samples); - m_format = LookupBaseFormat(format); - m_state = state; - m_is_depth_view = is_depth_view; - return true; -} - -bool D3D12::Texture::Adopt(ComPtr texture, DXGI_FORMAT srv_format, DXGI_FORMAT rtv_format, - DXGI_FORMAT dsv_format, D3D12_RESOURCE_STATES state) -{ - const D3D12_RESOURCE_DESC desc(texture->GetDesc()); - - DescriptorHandle srv_descriptor, rtv_descriptor; - if (srv_format != DXGI_FORMAT_UNKNOWN) - { - if (!CreateSRVDescriptor(texture.Get(), srv_format, desc.SampleDesc.Count > 1, &srv_descriptor)) - return false; - } - - m_is_depth_view = false; - - if (rtv_format != DXGI_FORMAT_UNKNOWN) - { - Assert(dsv_format == DXGI_FORMAT_UNKNOWN); - if (!CreateRTVDescriptor(texture.Get(), rtv_format, desc.SampleDesc.Count > 1, &rtv_descriptor)) - { - g_d3d12_context->GetDescriptorHeapManager().Free(&srv_descriptor); - return false; - } - } - else if (dsv_format != DXGI_FORMAT_UNKNOWN) - { - if (!CreateDSVDescriptor(texture.Get(), dsv_format, desc.SampleDesc.Count > 1, &rtv_descriptor)) - { - g_d3d12_context->GetDescriptorHeapManager().Free(&srv_descriptor); - return false; - } - - m_is_depth_view = true; - } - - m_resource = std::move(texture); - m_srv_descriptor = std::move(srv_descriptor); - m_rtv_or_dsv_descriptor = std::move(rtv_descriptor); - m_width = static_cast(desc.Width); - m_height = static_cast(desc.Height); - m_layers = static_cast(desc.DepthOrArraySize); - m_levels = static_cast(desc.MipLevels); - m_samples = static_cast(desc.SampleDesc.Count); - m_format = LookupBaseFormat(desc.Format); - m_state = state; - return true; -} - -void D3D12::Texture::Destroy(bool defer /* = true */) -{ - if (defer) - { - g_d3d12_context->DeferDescriptorDestruction(g_d3d12_context->GetDescriptorHeapManager(), &m_srv_descriptor); - if (m_is_depth_view) - g_d3d12_context->DeferDescriptorDestruction(g_d3d12_context->GetDSVHeapManager(), &m_rtv_or_dsv_descriptor); - else - g_d3d12_context->DeferDescriptorDestruction(g_d3d12_context->GetRTVHeapManager(), &m_rtv_or_dsv_descriptor); - g_d3d12_context->DeferResourceDestruction(m_resource.Get()); - m_resource.Reset(); - } - else - { - g_d3d12_context->GetDescriptorHeapManager().Free(&m_srv_descriptor); - if (m_is_depth_view) - g_d3d12_context->GetDSVHeapManager().Free(&m_rtv_or_dsv_descriptor); - else - g_d3d12_context->GetRTVHeapManager().Free(&m_rtv_or_dsv_descriptor); - - m_resource.Reset(); - } - - ClearBaseProperties(); - m_is_depth_view = false; -} - -void D3D12::Texture::TransitionToState(D3D12_RESOURCE_STATES state) const -{ - if (m_state == state) - return; - - ResourceBarrier(g_d3d12_context->GetCommandList(), m_resource.Get(), m_state, state); - m_state = state; -} - -bool D3D12::Texture::BeginStreamUpdate(u32 x, u32 y, u32 width, u32 height, void** out_data, u32* out_data_pitch) -{ - const u32 copy_pitch = Common::AlignUpPow2(width * GetPixelSize(), D3D12_TEXTURE_DATA_PITCH_ALIGNMENT); - const u32 upload_size = copy_pitch * height; - - if (!g_d3d12_context->GetTextureStreamBuffer().ReserveMemory(upload_size, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT)) - { - Log_PerfPrintf("Executing command buffer while waiting for %u bytes (%ux%u) in upload buffer", upload_size, width, - height); - g_d3d12_context->ExecuteCommandList(false); - if (!g_d3d12_context->GetTextureStreamBuffer().ReserveMemory(upload_size, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT)) - { - Log_ErrorPrintf("Failed to reserve %u bytes for %ux%u upload", upload_size, width, height); - return false; - } - } - - *out_data = g_d3d12_context->GetTextureStreamBuffer().GetCurrentHostPointer(); - *out_data_pitch = copy_pitch; - return true; -} - -void D3D12::Texture::EndStreamUpdate(u32 x, u32 y, u32 width, u32 height) -{ - const u32 copy_pitch = Common::AlignUpPow2(width * GetPixelSize(), D3D12_TEXTURE_DATA_PITCH_ALIGNMENT); - const u32 upload_size = copy_pitch * height; - - StreamBuffer& sb = g_d3d12_context->GetTextureStreamBuffer(); - const u32 sb_offset = sb.GetCurrentOffset(); - sb.CommitMemory(upload_size); - - CopyFromBuffer(x, y, width, height, copy_pitch, sb.GetBuffer(), sb_offset); -} - -void D3D12::Texture::CopyFromBuffer(u32 x, u32 y, u32 width, u32 height, u32 pitch, ID3D12Resource* buffer, - u32 buffer_offset) -{ - D3D12_TEXTURE_COPY_LOCATION src; - src.pResource = buffer; - src.SubresourceIndex = 0; - src.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; - src.PlacedFootprint.Offset = buffer_offset; - src.PlacedFootprint.Footprint.Width = width; - src.PlacedFootprint.Footprint.Height = height; - src.PlacedFootprint.Footprint.Depth = 1; - src.PlacedFootprint.Footprint.RowPitch = pitch; - src.PlacedFootprint.Footprint.Format = GetDXGIFormat(); - - D3D12_TEXTURE_COPY_LOCATION dst; - dst.pResource = m_resource.Get(); - dst.SubresourceIndex = 0; - dst.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; - - const D3D12_BOX src_box{0u, 0u, 0u, width, height, 1u}; - const D3D12_RESOURCE_STATES old_state = m_state; - TransitionToState(D3D12_RESOURCE_STATE_COPY_DEST); - g_d3d12_context->GetCommandList()->CopyTextureRegion(&dst, x, y, 0, &src, &src_box); - TransitionToState(old_state); -} - -void D3D12::Texture::SetDebugName(const std::string_view& name) -{ - UnreachableCode(); -} - -void D3D12::Texture::MakeReadyForSampling() -{ - UnreachableCode(); -} - -bool D3D12::Texture::LoadData(u32 x, u32 y, u32 width, u32 height, const void* data, u32 pitch) -{ - const u32 texel_size = GetPixelSize(); - const u32 upload_pitch = Common::AlignUpPow2(width * texel_size, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT); - const u32 upload_size = upload_pitch * height; - if (upload_size >= g_d3d12_context->GetTextureStreamBuffer().GetSize()) - { - StagingTexture st; - if (!st.Create(width, height, GetDXGIFormat(), true) || !st.WritePixels(0, 0, width, height, data, pitch)) - return false; - - D3D12_RESOURCE_STATES old_state = m_state; - TransitionToState(D3D12_RESOURCE_STATE_COPY_DEST); - st.CopyToTexture(0, 0, m_resource.Get(), 0, x, y, width, height); - st.Destroy(true); - TransitionToState(old_state); - return true; - } - - void* write_ptr; - u32 write_pitch; - if (!BeginStreamUpdate(x, y, width, height, &write_ptr, &write_pitch)) - return false; - - CopyToUploadBuffer(data, pitch, height, write_ptr, write_pitch); - EndStreamUpdate(x, y, width, height); - return true; -} - -void D3D12::Texture::CopyToUploadBuffer(const void* src_data, u32 src_pitch, u32 height, void* dst_data, u32 dst_pitch) -{ - const u8* src_ptr = static_cast(src_data); - u8* dst_ptr = static_cast(dst_data); - if (src_pitch == dst_pitch) - { - std::memcpy(dst_ptr, src_ptr, dst_pitch * height); - } - else - { - const u32 copy_size = std::min(src_pitch, dst_pitch); - for (u32 row = 0; row < height; row++) - { - std::memcpy(dst_ptr, src_ptr, copy_size); - src_ptr += src_pitch; - dst_ptr += dst_pitch; - } - } -} - -bool D3D12::Texture::CreateSRVDescriptor(ID3D12Resource* resource, DXGI_FORMAT format, bool multisampled, - DescriptorHandle* dh) -{ - if (!g_d3d12_context->GetDescriptorHeapManager().Allocate(dh)) - { - Log_ErrorPrintf("Failed to allocate SRV descriptor"); - return false; - } - - D3D12_SHADER_RESOURCE_VIEW_DESC desc = { - format, multisampled ? D3D12_SRV_DIMENSION_TEXTURE2DMS : D3D12_SRV_DIMENSION_TEXTURE2D, - D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING}; - if (!multisampled) - desc.Texture2D.MipLevels = 1; - - g_d3d12_context->GetDevice()->CreateShaderResourceView(resource, &desc, dh->cpu_handle); - return true; -} - -bool D3D12::Texture::CreateRTVDescriptor(ID3D12Resource* resource, DXGI_FORMAT format, bool multisampled, - DescriptorHandle* dh) -{ - if (!g_d3d12_context->GetRTVHeapManager().Allocate(dh)) - { - Log_ErrorPrintf("Failed to allocate SRV descriptor"); - return false; - } - - D3D12_RENDER_TARGET_VIEW_DESC desc = {format, - multisampled ? D3D12_RTV_DIMENSION_TEXTURE2DMS : D3D12_RTV_DIMENSION_TEXTURE2D}; - - g_d3d12_context->GetDevice()->CreateRenderTargetView(resource, &desc, dh->cpu_handle); - return true; -} - -bool D3D12::Texture::CreateDSVDescriptor(ID3D12Resource* resource, DXGI_FORMAT format, bool multisampled, - DescriptorHandle* dh) -{ - if (!g_d3d12_context->GetDSVHeapManager().Allocate(dh)) - { - Log_ErrorPrintf("Failed to allocate SRV descriptor"); - return false; - } - - D3D12_DEPTH_STENCIL_VIEW_DESC desc = { - format, multisampled ? D3D12_DSV_DIMENSION_TEXTURE2DMS : D3D12_DSV_DIMENSION_TEXTURE2D, D3D12_DSV_FLAG_NONE}; - - g_d3d12_context->GetDevice()->CreateDepthStencilView(resource, &desc, dh->cpu_handle); - return true; -} diff --git a/src/core/gpu/d3d12/texture.h b/src/core/gpu/d3d12/texture.h deleted file mode 100644 index 1e1a1ff05..000000000 --- a/src/core/gpu/d3d12/texture.h +++ /dev/null @@ -1,86 +0,0 @@ -// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin -// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) - -#pragma once -#include "../gpu_texture.h" -#include "common/windows_headers.h" -#include "descriptor_heap_manager.h" -#include -#include - -namespace D3D12 { - -class StreamBuffer; - -class Texture final : public GPUTexture -{ -public: - template - using ComPtr = Microsoft::WRL::ComPtr; - - Texture(); - Texture(ID3D12Resource* resource, D3D12_RESOURCE_STATES state); - Texture(Texture&& texture); - Texture(const Texture&) = delete; - ~Texture(); - - static DXGI_FORMAT GetDXGIFormat(Format format); - static Format LookupBaseFormat(DXGI_FORMAT dformat); - - ALWAYS_INLINE ID3D12Resource* GetResource() const { return m_resource.Get(); } - ALWAYS_INLINE const DescriptorHandle& GetSRVDescriptor() const { return m_srv_descriptor; } - ALWAYS_INLINE const DescriptorHandle& GetRTVOrDSVDescriptor() const { return m_rtv_or_dsv_descriptor; } - ALWAYS_INLINE D3D12_RESOURCE_STATES GetState() const { return m_state; } - ALWAYS_INLINE DXGI_FORMAT GetDXGIFormat() const { return GetDXGIFormat(m_format); } - - ALWAYS_INLINE operator ID3D12Resource*() const { return m_resource.Get(); } - ALWAYS_INLINE operator bool() const { return static_cast(m_resource); } - - bool IsValid() const override; - bool Update(u32 x, u32 y, u32 width, u32 height, const void* data, u32 pitch, u32 layer = 0, u32 level = 0) override; - bool Map(void** map, u32* map_stride, u32 x, u32 y, u32 width, u32 height, u32 layer = 0, u32 level = 0) override; - void Unmap() override; - - bool Create(u32 width, u32 height, u32 layers, u32 levels, u32 samples, DXGI_FORMAT format, DXGI_FORMAT srv_format, DXGI_FORMAT rtv_format, - DXGI_FORMAT dsv_format, D3D12_RESOURCE_FLAGS flags); - bool Adopt(ComPtr texture, DXGI_FORMAT srv_format, DXGI_FORMAT rtv_format, DXGI_FORMAT dsv_format, - D3D12_RESOURCE_STATES state); - - D3D12_RESOURCE_DESC GetDesc() const; - - void Destroy(bool defer = true); - - void TransitionToState(D3D12_RESOURCE_STATES state) const; - - Texture& operator=(const Texture&) = delete; - Texture& operator=(Texture&& texture); - - bool BeginStreamUpdate(u32 x, u32 y, u32 width, u32 height, void** out_data, u32* out_data_pitch); - void EndStreamUpdate(u32 x, u32 y, u32 width, u32 height); - - bool LoadData(u32 x, u32 y, u32 width, u32 height, const void* data, u32 pitch); - - static void CopyToUploadBuffer(const void* src_data, u32 src_pitch, u32 height, void* dst_data, u32 dst_pitch); - void CopyFromBuffer(u32 x, u32 y, u32 width, u32 height, u32 pitch, ID3D12Resource* buffer, u32 buffer_offset); - - void SetDebugName(const std::string_view& name) override; - void MakeReadyForSampling() override; - -private: - static bool CreateSRVDescriptor(ID3D12Resource* resource, DXGI_FORMAT format, bool multisampled, - DescriptorHandle* dh); - static bool CreateRTVDescriptor(ID3D12Resource* resource, DXGI_FORMAT format, bool multisampled, - DescriptorHandle* dh); - static bool CreateDSVDescriptor(ID3D12Resource* resource, DXGI_FORMAT format, bool multisampled, - DescriptorHandle* dh); - - ComPtr m_resource; - DescriptorHandle m_srv_descriptor = {}; - DescriptorHandle m_rtv_or_dsv_descriptor = {}; - - mutable D3D12_RESOURCE_STATES m_state = D3D12_RESOURCE_STATE_COMMON; - - bool m_is_depth_view = false; -}; - -} // namespace D3D12 \ No newline at end of file diff --git a/src/core/gpu/d3d12/util.cpp b/src/core/gpu/d3d12/util.cpp deleted file mode 100644 index 726430025..000000000 --- a/src/core/gpu/d3d12/util.cpp +++ /dev/null @@ -1,392 +0,0 @@ -// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin -// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) - -#include "util.h" -#include "common/assert.h" -#include "common/log.h" -#include "common/string.h" -#include "common/string_util.h" -#include "context.h" -#include "shader_cache.h" -#include -#include -Log_SetChannel(D3D12); - -namespace D3D12 { - -void ResourceBarrier(ID3D12GraphicsCommandList* cmdlist, ID3D12Resource* resource, D3D12_RESOURCE_STATES from_state, - D3D12_RESOURCE_STATES to_state) -{ - const D3D12_RESOURCE_BARRIER barrier = {D3D12_RESOURCE_BARRIER_TYPE_TRANSITION, - D3D12_RESOURCE_BARRIER_FLAG_NONE, - {{resource, D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, from_state, to_state}}}; - cmdlist->ResourceBarrier(1, &barrier); -} - -void SetViewport(ID3D12GraphicsCommandList* cmdlist, int x, int y, int width, int height, float min_depth /*= 0.0f*/, - float max_depth /*= 1.0f*/) -{ - const D3D12_VIEWPORT vp{static_cast(x), - static_cast(y), - static_cast(width), - static_cast(height), - min_depth, - max_depth}; - cmdlist->RSSetViewports(1, &vp); -} - -void SetScissor(ID3D12GraphicsCommandList* cmdlist, int x, int y, int width, int height) -{ - const D3D12_RECT r{x, y, x + width, y + height}; - cmdlist->RSSetScissorRects(1, &r); -} - -void SetViewportAndScissor(ID3D12GraphicsCommandList* cmdlist, int x, int y, int width, int height, - float min_depth /*= 0.0f*/, float max_depth /*= 1.0f*/) -{ - SetViewport(cmdlist, x, y, width, height, min_depth, max_depth); - SetScissor(cmdlist, x, y, width, height); -} - -void SetViewportAndClampScissor(ID3D12GraphicsCommandList* cmdlist, int x, int y, int width, int height, - float min_depth /*= 0.0f*/, float max_depth /*= 1.0f*/) -{ - SetViewport(cmdlist, x, y, width, height, min_depth, max_depth); - - const int cx = std::max(x, 0); - const int cy = std::max(y, 0); - const int cwidth = width - (cx - x); - const int cheight = height - (cy - y); - SetScissor(cmdlist, cx, cy, cwidth, cheight); -} - -u32 GetTexelSize(DXGI_FORMAT format) -{ - switch (format) - { - case DXGI_FORMAT_R8G8B8A8_UNORM: - case DXGI_FORMAT_R8G8B8A8_SNORM: - case DXGI_FORMAT_R8G8B8A8_TYPELESS: - case DXGI_FORMAT_B8G8R8A8_UNORM: - case DXGI_FORMAT_B8G8R8A8_TYPELESS: - return 4; - - case DXGI_FORMAT_B5G5R5A1_UNORM: - case DXGI_FORMAT_B5G6R5_UNORM: - return 2; - - default: - Panic("Unknown format"); - return 1; - } -} - -void SetDefaultSampler(D3D12_SAMPLER_DESC* desc) -{ - desc->Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR; - desc->AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; - desc->AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; - desc->AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; - desc->MipLODBias = 0; - desc->MaxAnisotropy = 1; - desc->ComparisonFunc = D3D12_COMPARISON_FUNC_NEVER; - desc->BorderColor[0] = 1.0f; - desc->BorderColor[1] = 1.0f; - desc->BorderColor[2] = 1.0f; - desc->BorderColor[3] = 1.0f; - desc->MinLOD = -3.402823466e+38F; // -FLT_MAX - desc->MaxLOD = 3.402823466e+38F; // FLT_MAX -} - -#ifdef _DEBUG - -void SetObjectName(ID3D12Object* object, const char* name) -{ - object->SetName(StringUtil::UTF8StringToWideString(name).c_str()); -} - -void SetObjectNameFormatted(ID3D12Object* object, const char* format, ...) -{ - std::va_list ap; - va_start(ap, format); - - SmallString str; - str.FormatVA(format, ap); - - SetObjectName(object, str); - va_end(ap); -} - -#endif - -GraphicsPipelineBuilder::GraphicsPipelineBuilder() -{ - Clear(); -} - -void GraphicsPipelineBuilder::Clear() -{ - std::memset(&m_desc, 0, sizeof(m_desc)); - std::memset(m_input_elements.data(), 0, sizeof(D3D12_INPUT_ELEMENT_DESC) * m_input_elements.size()); - m_desc.NodeMask = 1; - m_desc.SampleMask = 0xFFFFFFFF; - m_desc.SampleDesc.Count = 1; -} - -Microsoft::WRL::ComPtr GraphicsPipelineBuilder::Create(ID3D12Device* device, bool clear /*= true*/) -{ - Microsoft::WRL::ComPtr ps; - HRESULT hr = device->CreateGraphicsPipelineState(&m_desc, IID_PPV_ARGS(ps.GetAddressOf())); - if (FAILED(hr)) - { - Log_ErrorPrintf("CreateGraphicsPipelineState() failed: %08X", hr); - return {}; - } - - if (clear) - Clear(); - - return ps; -} - -Microsoft::WRL::ComPtr GraphicsPipelineBuilder::Create(ID3D12Device* device, ShaderCache& cache, - bool clear /*= true*/) -{ - Microsoft::WRL::ComPtr pso = cache.GetPipelineState(device, m_desc); - if (!pso) - return {}; - - if (clear) - Clear(); - - return pso; -} - -void GraphicsPipelineBuilder::SetRootSignature(ID3D12RootSignature* rs) -{ - m_desc.pRootSignature = rs; -} - -void GraphicsPipelineBuilder::SetVertexShader(ID3DBlob* blob) -{ - SetVertexShader(blob->GetBufferPointer(), static_cast(blob->GetBufferSize())); -} - -void GraphicsPipelineBuilder::SetVertexShader(const void* data, u32 data_size) -{ - m_desc.VS.pShaderBytecode = data; - m_desc.VS.BytecodeLength = data_size; -} - -void GraphicsPipelineBuilder::SetGeometryShader(ID3DBlob* blob) -{ - SetGeometryShader(blob->GetBufferPointer(), static_cast(blob->GetBufferSize())); -} - -void GraphicsPipelineBuilder::SetGeometryShader(const void* data, u32 data_size) -{ - m_desc.GS.pShaderBytecode = data; - m_desc.GS.BytecodeLength = data_size; -} - -void GraphicsPipelineBuilder::SetPixelShader(ID3DBlob* blob) -{ - SetPixelShader(blob->GetBufferPointer(), static_cast(blob->GetBufferSize())); -} - -void GraphicsPipelineBuilder::SetPixelShader(const void* data, u32 data_size) -{ - m_desc.PS.pShaderBytecode = data; - m_desc.PS.BytecodeLength = data_size; -} - -void GraphicsPipelineBuilder::AddVertexAttribute(const char* semantic_name, u32 semantic_index, DXGI_FORMAT format, - u32 buffer, u32 offset) -{ - const u32 index = m_desc.InputLayout.NumElements; - m_input_elements[index].SemanticIndex = semantic_index; - m_input_elements[index].SemanticName = semantic_name; - m_input_elements[index].Format = format; - m_input_elements[index].AlignedByteOffset = offset; - m_input_elements[index].InputSlot = buffer; - m_input_elements[index].InputSlotClass = D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA; - m_input_elements[index].InstanceDataStepRate = 0; - - m_desc.InputLayout.pInputElementDescs = m_input_elements.data(); - m_desc.InputLayout.NumElements++; -} - -void GraphicsPipelineBuilder::SetPrimitiveTopologyType(D3D12_PRIMITIVE_TOPOLOGY_TYPE type) -{ - m_desc.PrimitiveTopologyType = type; -} - -void GraphicsPipelineBuilder::SetRasterizationState(D3D12_FILL_MODE polygon_mode, D3D12_CULL_MODE cull_mode, - bool front_face_ccw) -{ - m_desc.RasterizerState.FillMode = polygon_mode; - m_desc.RasterizerState.CullMode = cull_mode; - m_desc.RasterizerState.FrontCounterClockwise = front_face_ccw; -} - -void GraphicsPipelineBuilder::SetMultisamples(u32 multisamples) -{ - m_desc.RasterizerState.MultisampleEnable = multisamples > 1; - m_desc.SampleDesc.Count = multisamples; -} - -void GraphicsPipelineBuilder::SetNoCullRasterizationState() -{ - SetRasterizationState(D3D12_FILL_MODE_SOLID, D3D12_CULL_MODE_NONE, false); -} - -void GraphicsPipelineBuilder::SetDepthState(bool depth_test, bool depth_write, D3D12_COMPARISON_FUNC compare_op) -{ - m_desc.DepthStencilState.DepthEnable = depth_test; - m_desc.DepthStencilState.DepthWriteMask = depth_write ? D3D12_DEPTH_WRITE_MASK_ALL : D3D12_DEPTH_WRITE_MASK_ZERO; - m_desc.DepthStencilState.DepthFunc = compare_op; -} - -void GraphicsPipelineBuilder::SetNoDepthTestState() -{ - SetDepthState(false, false, D3D12_COMPARISON_FUNC_ALWAYS); -} - -void GraphicsPipelineBuilder::SetBlendState(u32 rt, bool blend_enable, D3D12_BLEND src_factor, D3D12_BLEND dst_factor, - D3D12_BLEND_OP op, D3D12_BLEND alpha_src_factor, - D3D12_BLEND alpha_dst_factor, D3D12_BLEND_OP alpha_op, - u8 write_mask /*= 0xFF*/) -{ - m_desc.BlendState.RenderTarget[rt].BlendEnable = blend_enable; - m_desc.BlendState.RenderTarget[rt].SrcBlend = src_factor; - m_desc.BlendState.RenderTarget[rt].DestBlend = dst_factor; - m_desc.BlendState.RenderTarget[rt].BlendOp = op; - m_desc.BlendState.RenderTarget[rt].SrcBlendAlpha = alpha_src_factor; - m_desc.BlendState.RenderTarget[rt].DestBlendAlpha = alpha_dst_factor; - m_desc.BlendState.RenderTarget[rt].BlendOpAlpha = alpha_op; - m_desc.BlendState.RenderTarget[rt].RenderTargetWriteMask = write_mask; - - if (rt > 0) - m_desc.BlendState.IndependentBlendEnable = TRUE; -} - -void GraphicsPipelineBuilder::SetNoBlendingState() -{ - SetBlendState(0, false, D3D12_BLEND_ONE, D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD, D3D12_BLEND_ONE, D3D12_BLEND_ZERO, - D3D12_BLEND_OP_ADD, D3D12_COLOR_WRITE_ENABLE_ALL); - m_desc.BlendState.IndependentBlendEnable = FALSE; -} - -void GraphicsPipelineBuilder::ClearRenderTargets() -{ - m_desc.NumRenderTargets = 0; - for (u32 i = 0; i < sizeof(m_desc.RTVFormats) / sizeof(m_desc.RTVFormats[0]); i++) - m_desc.RTVFormats[i] = DXGI_FORMAT_UNKNOWN; -} - -void GraphicsPipelineBuilder::SetRenderTarget(u32 rt, DXGI_FORMAT format) -{ - m_desc.RTVFormats[rt] = format; - if (rt >= m_desc.NumRenderTargets) - m_desc.NumRenderTargets = rt + 1; -} - -void GraphicsPipelineBuilder::ClearDepthStencilFormat() -{ - m_desc.DSVFormat = DXGI_FORMAT_UNKNOWN; -} - -void GraphicsPipelineBuilder::SetDepthStencilFormat(DXGI_FORMAT format) -{ - m_desc.DSVFormat = format; -} - -RootSignatureBuilder::RootSignatureBuilder() -{ - Clear(); -} - -void RootSignatureBuilder::Clear() -{ - m_desc = {}; - m_desc.pParameters = m_params.data(); - m_params = {}; - m_descriptor_ranges = {}; - m_num_descriptor_ranges = 0; -} - -Microsoft::WRL::ComPtr RootSignatureBuilder::Create(bool clear /*= true*/) -{ - Microsoft::WRL::ComPtr rs = g_d3d12_context->CreateRootSignature(&m_desc); - if (!rs) - return {}; - - if (clear) - Clear(); - - return rs; -} - -void RootSignatureBuilder::SetInputAssemblerFlag() -{ - m_desc.Flags |= D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT; -} - -u32 RootSignatureBuilder::Add32BitConstants(u32 shader_reg, u32 num_values, D3D12_SHADER_VISIBILITY visibility) -{ - const u32 index = m_desc.NumParameters++; - - m_params[index].ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS; - m_params[index].ShaderVisibility = visibility; - m_params[index].Constants.ShaderRegister = shader_reg; - m_params[index].Constants.RegisterSpace = 0; - m_params[index].Constants.Num32BitValues = num_values; - - return index; -} - -u32 RootSignatureBuilder::AddCBVParameter(u32 shader_reg, D3D12_SHADER_VISIBILITY visibility) -{ - const u32 index = m_desc.NumParameters++; - - m_params[index].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV; - m_params[index].ShaderVisibility = visibility; - m_params[index].Descriptor.ShaderRegister = shader_reg; - m_params[index].Descriptor.RegisterSpace = 0; - - return index; -} - -u32 RootSignatureBuilder::AddSRVParameter(u32 shader_reg, D3D12_SHADER_VISIBILITY visibility) -{ - const u32 index = m_desc.NumParameters++; - - m_params[index].ParameterType = D3D12_ROOT_PARAMETER_TYPE_SRV; - m_params[index].ShaderVisibility = visibility; - m_params[index].Descriptor.ShaderRegister = shader_reg; - m_params[index].Descriptor.RegisterSpace = 0; - - return index; -} - -u32 RootSignatureBuilder::AddDescriptorTable(D3D12_DESCRIPTOR_RANGE_TYPE rt, u32 start_shader_reg, u32 num_shader_regs, - D3D12_SHADER_VISIBILITY visibility) -{ - const u32 index = m_desc.NumParameters++; - const u32 dr_index = m_num_descriptor_ranges++; - - m_descriptor_ranges[dr_index].RangeType = rt; - m_descriptor_ranges[dr_index].NumDescriptors = num_shader_regs; - m_descriptor_ranges[dr_index].BaseShaderRegister = start_shader_reg; - m_descriptor_ranges[dr_index].RegisterSpace = 0; - m_descriptor_ranges[dr_index].OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; - - m_params[index].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; - m_params[index].DescriptorTable.pDescriptorRanges = &m_descriptor_ranges[dr_index]; - m_params[index].DescriptorTable.NumDescriptorRanges = 1; - m_params[index].ShaderVisibility = visibility; - - return index; -} - -} // namespace D3D12 \ No newline at end of file diff --git a/src/core/gpu/d3d12_builders.cpp b/src/core/gpu/d3d12_builders.cpp new file mode 100644 index 000000000..94ce24ddc --- /dev/null +++ b/src/core/gpu/d3d12_builders.cpp @@ -0,0 +1,352 @@ +// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin +// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) + +#include "d3d12_builders.h" +#include "d3d12_device.h" + +#include "common/assert.h" +#include "common/log.h" +#include "common/string_util.h" + +#include +#include + +Log_SetChannel(D3D12Device); + +D3D12::GraphicsPipelineBuilder::GraphicsPipelineBuilder() +{ + Clear(); +} + +void D3D12::GraphicsPipelineBuilder::Clear() +{ + std::memset(&m_desc, 0, sizeof(m_desc)); + std::memset(m_input_elements.data(), 0, sizeof(D3D12_INPUT_ELEMENT_DESC) * m_input_elements.size()); + m_desc.NodeMask = 1; + m_desc.SampleMask = 0xFFFFFFFF; + m_desc.SampleDesc.Count = 1; +} + +Microsoft::WRL::ComPtr D3D12::GraphicsPipelineBuilder::Create(ID3D12Device* device, + bool clear /*= true*/) +{ + Microsoft::WRL::ComPtr ps; + HRESULT hr = device->CreateGraphicsPipelineState(&m_desc, IID_PPV_ARGS(ps.GetAddressOf())); + if (FAILED(hr)) + { + Log_ErrorPrintf("CreateGraphicsPipelineState() failed: %08X", hr); + return {}; + } + + if (clear) + Clear(); + + return ps; +} + +void D3D12::GraphicsPipelineBuilder::SetRootSignature(ID3D12RootSignature* rs) +{ + m_desc.pRootSignature = rs; +} + +void D3D12::GraphicsPipelineBuilder::SetVertexShader(const ID3DBlob* blob) +{ + SetVertexShader(const_cast(blob)->GetBufferPointer(), + static_cast(const_cast(blob)->GetBufferSize())); +} + +void D3D12::GraphicsPipelineBuilder::SetVertexShader(const void* data, u32 data_size) +{ + m_desc.VS.pShaderBytecode = data; + m_desc.VS.BytecodeLength = data_size; +} + +void D3D12::GraphicsPipelineBuilder::SetGeometryShader(const ID3DBlob* blob) +{ + SetGeometryShader(const_cast(blob)->GetBufferPointer(), + static_cast(const_cast(blob)->GetBufferSize())); +} + +void D3D12::GraphicsPipelineBuilder::SetGeometryShader(const void* data, u32 data_size) +{ + m_desc.GS.pShaderBytecode = data; + m_desc.GS.BytecodeLength = data_size; +} + +void D3D12::GraphicsPipelineBuilder::SetPixelShader(const ID3DBlob* blob) +{ + SetPixelShader(const_cast(blob)->GetBufferPointer(), + static_cast(const_cast(blob)->GetBufferSize())); +} + +void D3D12::GraphicsPipelineBuilder::SetPixelShader(const void* data, u32 data_size) +{ + m_desc.PS.pShaderBytecode = data; + m_desc.PS.BytecodeLength = data_size; +} + +void D3D12::GraphicsPipelineBuilder::AddVertexAttribute(const char* semantic_name, u32 semantic_index, + DXGI_FORMAT format, u32 buffer, u32 offset) +{ + const u32 index = m_desc.InputLayout.NumElements; + m_input_elements[index].SemanticIndex = semantic_index; + m_input_elements[index].SemanticName = semantic_name; + m_input_elements[index].Format = format; + m_input_elements[index].AlignedByteOffset = offset; + m_input_elements[index].InputSlot = buffer; + m_input_elements[index].InputSlotClass = D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA; + m_input_elements[index].InstanceDataStepRate = 0; + + m_desc.InputLayout.pInputElementDescs = m_input_elements.data(); + m_desc.InputLayout.NumElements++; +} + +void D3D12::GraphicsPipelineBuilder::SetPrimitiveTopologyType(D3D12_PRIMITIVE_TOPOLOGY_TYPE type) +{ + m_desc.PrimitiveTopologyType = type; +} + +void D3D12::GraphicsPipelineBuilder::SetRasterizationState(D3D12_FILL_MODE polygon_mode, D3D12_CULL_MODE cull_mode, + bool front_face_ccw) +{ + m_desc.RasterizerState.FillMode = polygon_mode; + m_desc.RasterizerState.CullMode = cull_mode; + m_desc.RasterizerState.FrontCounterClockwise = front_face_ccw; +} + +void D3D12::GraphicsPipelineBuilder::SetMultisamples(u32 multisamples) +{ + m_desc.RasterizerState.MultisampleEnable = multisamples > 1; + m_desc.SampleDesc.Count = multisamples; +} + +void D3D12::GraphicsPipelineBuilder::SetNoCullRasterizationState() +{ + SetRasterizationState(D3D12_FILL_MODE_SOLID, D3D12_CULL_MODE_NONE, false); +} + +void D3D12::GraphicsPipelineBuilder::SetDepthState(bool depth_test, bool depth_write, D3D12_COMPARISON_FUNC compare_op) +{ + m_desc.DepthStencilState.DepthEnable = depth_test; + m_desc.DepthStencilState.DepthWriteMask = depth_write ? D3D12_DEPTH_WRITE_MASK_ALL : D3D12_DEPTH_WRITE_MASK_ZERO; + m_desc.DepthStencilState.DepthFunc = compare_op; +} + +void D3D12::GraphicsPipelineBuilder::SetStencilState(bool stencil_test, u8 read_mask, u8 write_mask, + const D3D12_DEPTH_STENCILOP_DESC& front, + const D3D12_DEPTH_STENCILOP_DESC& back) +{ + m_desc.DepthStencilState.StencilEnable = stencil_test; + m_desc.DepthStencilState.StencilReadMask = read_mask; + m_desc.DepthStencilState.StencilWriteMask = write_mask; + m_desc.DepthStencilState.FrontFace = front; + m_desc.DepthStencilState.BackFace = back; +} + +void D3D12::GraphicsPipelineBuilder::SetNoDepthTestState() +{ + SetDepthState(false, false, D3D12_COMPARISON_FUNC_ALWAYS); +} + +void D3D12::GraphicsPipelineBuilder::SetNoStencilState() +{ + D3D12_DEPTH_STENCILOP_DESC empty = {}; + SetStencilState(false, 0, 0, empty, empty); +} + +void D3D12::GraphicsPipelineBuilder::SetBlendState(u32 rt, bool blend_enable, D3D12_BLEND src_factor, + D3D12_BLEND dst_factor, D3D12_BLEND_OP op, + D3D12_BLEND alpha_src_factor, D3D12_BLEND alpha_dst_factor, + D3D12_BLEND_OP alpha_op, u8 write_mask /*= 0xFF*/) +{ + m_desc.BlendState.RenderTarget[rt].BlendEnable = blend_enable; + m_desc.BlendState.RenderTarget[rt].SrcBlend = src_factor; + m_desc.BlendState.RenderTarget[rt].DestBlend = dst_factor; + m_desc.BlendState.RenderTarget[rt].BlendOp = op; + m_desc.BlendState.RenderTarget[rt].SrcBlendAlpha = alpha_src_factor; + m_desc.BlendState.RenderTarget[rt].DestBlendAlpha = alpha_dst_factor; + m_desc.BlendState.RenderTarget[rt].BlendOpAlpha = alpha_op; + m_desc.BlendState.RenderTarget[rt].RenderTargetWriteMask = write_mask; + + if (rt > 0) + m_desc.BlendState.IndependentBlendEnable = TRUE; +} + +void D3D12::GraphicsPipelineBuilder::SetColorWriteMask(u32 rt, u8 write_mask /* = D3D12_COLOR_WRITE_ENABLE_ALL */) +{ + m_desc.BlendState.RenderTarget[rt].RenderTargetWriteMask = write_mask; +} + +void D3D12::GraphicsPipelineBuilder::SetNoBlendingState() +{ + SetBlendState(0, false, D3D12_BLEND_ONE, D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD, D3D12_BLEND_ONE, D3D12_BLEND_ZERO, + D3D12_BLEND_OP_ADD, D3D12_COLOR_WRITE_ENABLE_ALL); + m_desc.BlendState.IndependentBlendEnable = FALSE; +} + +void D3D12::GraphicsPipelineBuilder::ClearRenderTargets() +{ + m_desc.NumRenderTargets = 0; + for (u32 i = 0; i < sizeof(m_desc.RTVFormats) / sizeof(m_desc.RTVFormats[0]); i++) + m_desc.RTVFormats[i] = DXGI_FORMAT_UNKNOWN; +} + +void D3D12::GraphicsPipelineBuilder::SetRenderTarget(u32 rt, DXGI_FORMAT format) +{ + m_desc.RTVFormats[rt] = format; + if (rt >= m_desc.NumRenderTargets) + m_desc.NumRenderTargets = rt + 1; +} + +void D3D12::GraphicsPipelineBuilder::ClearDepthStencilFormat() +{ + m_desc.DSVFormat = DXGI_FORMAT_UNKNOWN; +} + +void D3D12::GraphicsPipelineBuilder::SetDepthStencilFormat(DXGI_FORMAT format) +{ + m_desc.DSVFormat = format; +} + +D3D12::ComputePipelineBuilder::ComputePipelineBuilder() +{ + Clear(); +} + +void D3D12::ComputePipelineBuilder::Clear() +{ + std::memset(&m_desc, 0, sizeof(m_desc)); +} + +Microsoft::WRL::ComPtr D3D12::ComputePipelineBuilder::Create(ID3D12Device* device, + bool clear /*= true*/) +{ + Microsoft::WRL::ComPtr ps; + HRESULT hr = device->CreateComputePipelineState(&m_desc, IID_PPV_ARGS(ps.GetAddressOf())); + if (FAILED(hr)) + { + Log_ErrorPrintf("CreateComputePipelineState() failed: %08X", hr); + return {}; + } + + if (clear) + Clear(); + + return ps; +} + +void D3D12::ComputePipelineBuilder::SetRootSignature(ID3D12RootSignature* rs) +{ + m_desc.pRootSignature = rs; +} + +void D3D12::ComputePipelineBuilder::SetShader(const void* data, u32 data_size) +{ + m_desc.CS.pShaderBytecode = data; + m_desc.CS.BytecodeLength = data_size; +} + +D3D12::RootSignatureBuilder::RootSignatureBuilder() +{ + Clear(); +} + +void D3D12::RootSignatureBuilder::Clear() +{ + m_desc = {}; + m_desc.pParameters = m_params.data(); + m_params = {}; + m_descriptor_ranges = {}; + m_num_descriptor_ranges = 0; +} + +Microsoft::WRL::ComPtr D3D12::RootSignatureBuilder::Create(bool clear /*= true*/) +{ + Microsoft::WRL::ComPtr rs = D3D12Device::GetInstance().CreateRootSignature(&m_desc); + if (!rs) + return {}; + + if (clear) + Clear(); + + return rs; +} + +void D3D12::RootSignatureBuilder::SetInputAssemblerFlag() +{ + m_desc.Flags |= D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT; +} + +u32 D3D12::RootSignatureBuilder::Add32BitConstants(u32 shader_reg, u32 num_values, D3D12_SHADER_VISIBILITY visibility) +{ + const u32 index = m_desc.NumParameters++; + + m_params[index].ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS; + m_params[index].ShaderVisibility = visibility; + m_params[index].Constants.ShaderRegister = shader_reg; + m_params[index].Constants.RegisterSpace = 0; + m_params[index].Constants.Num32BitValues = num_values; + + return index; +} + +u32 D3D12::RootSignatureBuilder::AddCBVParameter(u32 shader_reg, D3D12_SHADER_VISIBILITY visibility) +{ + const u32 index = m_desc.NumParameters++; + + m_params[index].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV; + m_params[index].ShaderVisibility = visibility; + m_params[index].Descriptor.ShaderRegister = shader_reg; + m_params[index].Descriptor.RegisterSpace = 0; + + return index; +} + +u32 D3D12::RootSignatureBuilder::AddSRVParameter(u32 shader_reg, D3D12_SHADER_VISIBILITY visibility) +{ + const u32 index = m_desc.NumParameters++; + + m_params[index].ParameterType = D3D12_ROOT_PARAMETER_TYPE_SRV; + m_params[index].ShaderVisibility = visibility; + m_params[index].Descriptor.ShaderRegister = shader_reg; + m_params[index].Descriptor.RegisterSpace = 0; + + return index; +} + +u32 D3D12::RootSignatureBuilder::AddDescriptorTable(D3D12_DESCRIPTOR_RANGE_TYPE rt, u32 start_shader_reg, + u32 num_shader_regs, D3D12_SHADER_VISIBILITY visibility) +{ + const u32 index = m_desc.NumParameters++; + const u32 dr_index = m_num_descriptor_ranges++; + + m_descriptor_ranges[dr_index].RangeType = rt; + m_descriptor_ranges[dr_index].NumDescriptors = num_shader_regs; + m_descriptor_ranges[dr_index].BaseShaderRegister = start_shader_reg; + m_descriptor_ranges[dr_index].RegisterSpace = 0; + m_descriptor_ranges[dr_index].OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; + + m_params[index].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; + m_params[index].DescriptorTable.pDescriptorRanges = &m_descriptor_ranges[dr_index]; + m_params[index].DescriptorTable.NumDescriptorRanges = 1; + m_params[index].ShaderVisibility = visibility; + + return index; +} + +#ifdef _DEBUG + +void D3D12::SetObjectName(ID3D12Object* object, const std::string_view& name) +{ + object->SetName(StringUtil::UTF8StringToWideString(name).c_str()); +} + +void D3D12::SetObjectNameFormatted(ID3D12Object* object, const char* format, ...) +{ + std::va_list ap; + va_start(ap, format); + SetObjectName(object, StringUtil::StdStringFromFormatV(format, ap).c_str()); + va_end(ap); +} + +#endif diff --git a/src/core/gpu/d3d12/util.h b/src/core/gpu/d3d12_builders.h similarity index 68% rename from src/core/gpu/d3d12/util.h rename to src/core/gpu/d3d12_builders.h index cafbe8341..b9957fbd1 100644 --- a/src/core/gpu/d3d12/util.h +++ b/src/core/gpu/d3d12_builders.h @@ -1,47 +1,17 @@ -// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin +// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) #pragma once + #include "common/types.h" #include "common/windows_headers.h" + #include #include +#include #include namespace D3D12 { - -class ShaderCache; - -void ResourceBarrier(ID3D12GraphicsCommandList* cmdlist, ID3D12Resource* resource, D3D12_RESOURCE_STATES from_state, - D3D12_RESOURCE_STATES to_state); - -void SetViewport(ID3D12GraphicsCommandList* cmdlist, int x, int y, int width, int height, float min_depth = 0.0f, - float max_depth = 1.0f); - -void SetScissor(ID3D12GraphicsCommandList* cmdlist, int x, int y, int width, int height); - -void SetViewportAndScissor(ID3D12GraphicsCommandList* cmdlist, int x, int y, int width, int height, - float min_depth = 0.0f, float max_depth = 1.0f); - -void SetViewportAndClampScissor(ID3D12GraphicsCommandList* cmdlist, int x, int y, int width, int height, - float min_depth = 0.0f, float max_depth = 1.0f); - -u32 GetTexelSize(DXGI_FORMAT format); - -void SetDefaultSampler(D3D12_SAMPLER_DESC* desc); - -#ifdef _DEBUG - -void SetObjectName(ID3D12Object* object, const char* name); -void SetObjectNameFormatted(ID3D12Object* object, const char* format, ...); - -#else - -static inline void SetObjectName(ID3D12Object* object, const char* name) {} -static inline void SetObjectNameFormatted(ID3D12Object* object, const char* format, ...) {} - -#endif - class RootSignatureBuilder { public: @@ -86,8 +56,8 @@ public: void Clear(); + // TODO: Pipeline Cache Microsoft::WRL::ComPtr Create(ID3D12Device* device, bool clear = true); - Microsoft::WRL::ComPtr Create(ID3D12Device* device, ShaderCache& cache, bool clear = true); void SetRootSignature(ID3D12RootSignature* rs); @@ -95,9 +65,9 @@ public: void SetGeometryShader(const void* data, u32 data_size); void SetPixelShader(const void* data, u32 data_size); - void SetVertexShader(ID3DBlob* blob); - void SetGeometryShader(ID3DBlob* blob); - void SetPixelShader(ID3DBlob* blob); + void SetVertexShader(const ID3DBlob* blob); + void SetGeometryShader(const ID3DBlob* blob); + void SetPixelShader(const ID3DBlob* blob); void AddVertexAttribute(const char* semantic_name, u32 semantic_index, DXGI_FORMAT format, u32 buffer, u32 offset); @@ -110,12 +80,16 @@ public: void SetNoCullRasterizationState(); void SetDepthState(bool depth_test, bool depth_write, D3D12_COMPARISON_FUNC compare_op); + void SetStencilState(bool stencil_test, u8 read_mask, u8 write_mask, const D3D12_DEPTH_STENCILOP_DESC& front, + const D3D12_DEPTH_STENCILOP_DESC& back); void SetNoDepthTestState(); + void SetNoStencilState(); void SetBlendState(u32 rt, bool blend_enable, D3D12_BLEND src_factor, D3D12_BLEND dst_factor, D3D12_BLEND_OP op, D3D12_BLEND alpha_src_factor, D3D12_BLEND alpha_dst_factor, D3D12_BLEND_OP alpha_op, u8 write_mask = D3D12_COLOR_WRITE_ENABLE_ALL); + void SetColorWriteMask(u32 rt, u8 write_mask = D3D12_COLOR_WRITE_ENABLE_ALL); void SetNoBlendingState(); @@ -128,8 +102,38 @@ public: void SetDepthStencilFormat(DXGI_FORMAT format); private: - D3D12_GRAPHICS_PIPELINE_STATE_DESC m_desc{}; - std::array m_input_elements{}; + D3D12_GRAPHICS_PIPELINE_STATE_DESC m_desc; + std::array m_input_elements; }; -} // namespace D3D12 \ No newline at end of file +class ComputePipelineBuilder +{ +public: + ComputePipelineBuilder(); + ~ComputePipelineBuilder() = default; + + void Clear(); + + // TODO: Pipeline Library + Microsoft::WRL::ComPtr Create(ID3D12Device* device, bool clear = true); + + void SetRootSignature(ID3D12RootSignature* rs); + + void SetShader(const void* data, u32 data_size); + +private: + D3D12_COMPUTE_PIPELINE_STATE_DESC m_desc; +}; + +#ifdef _DEBUG +void SetObjectName(ID3D12Object* object, const std::string_view& name); +void SetObjectNameFormatted(ID3D12Object* object, const char* format, ...); +#else +static inline void SetObjectName(ID3D12Object* object, const std::string_view& name) +{ +} +static inline void SetObjectNameFormatted(ID3D12Object* object, const char* format, ...) +{ +} +#endif +} // namespace D3D12 diff --git a/src/core/gpu/d3d12_descriptor_heap_manager.cpp b/src/core/gpu/d3d12_descriptor_heap_manager.cpp new file mode 100644 index 000000000..8ebb3ed05 --- /dev/null +++ b/src/core/gpu/d3d12_descriptor_heap_manager.cpp @@ -0,0 +1,154 @@ +// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin +// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) + +#include "d3d12_descriptor_heap_manager.h" + +#include "common/assert.h" +#include "common/log.h" + +Log_SetChannel(D3D12Device); + +D3D12DescriptorHeapManager::D3D12DescriptorHeapManager() = default; +D3D12DescriptorHeapManager::~D3D12DescriptorHeapManager() = default; + +bool D3D12DescriptorHeapManager::Create(ID3D12Device* device, D3D12_DESCRIPTOR_HEAP_TYPE type, u32 num_descriptors, + bool shader_visible) +{ + D3D12_DESCRIPTOR_HEAP_DESC desc = {type, static_cast(num_descriptors), + shader_visible ? D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE : + D3D12_DESCRIPTOR_HEAP_FLAG_NONE}; + + HRESULT hr = device->CreateDescriptorHeap(&desc, IID_PPV_ARGS(m_descriptor_heap.ReleaseAndGetAddressOf())); + if (FAILED(hr)) + { + Log_ErrorPrintf("CreateDescriptorHeap() failed: %08X", hr); + return false; + } + + m_heap_base_cpu = m_descriptor_heap->GetCPUDescriptorHandleForHeapStart(); + if (shader_visible) + m_heap_base_gpu = m_descriptor_heap->GetGPUDescriptorHandleForHeapStart(); + + m_num_descriptors = num_descriptors; + m_descriptor_increment_size = device->GetDescriptorHandleIncrementSize(type); + m_shader_visible = shader_visible; + + // Set all slots to unallocated (1) + const u32 bitset_count = num_descriptors / BITSET_SIZE + (((num_descriptors % BITSET_SIZE) != 0) ? 1 : 0); + m_free_slots.resize(bitset_count); + for (BitSetType& bs : m_free_slots) + bs.flip(); + + return true; +} + +void D3D12DescriptorHeapManager::Destroy() +{ + for (BitSetType& bs : m_free_slots) + { + DebugAssert(bs.all()); + } + + m_shader_visible = false; + m_num_descriptors = 0; + m_descriptor_increment_size = 0; + m_heap_base_cpu = {}; + m_heap_base_gpu = {}; + m_descriptor_heap.Reset(); + m_free_slots.clear(); +} + +bool D3D12DescriptorHeapManager::Allocate(D3D12DescriptorHandle* handle) +{ + // Start past the temporary slots, no point in searching those. + for (u32 group = 0; group < m_free_slots.size(); group++) + { + BitSetType& bs = m_free_slots[group]; + if (bs.none()) + continue; + + u32 bit = 0; + for (; bit < BITSET_SIZE; bit++) + { + if (bs[bit]) + break; + } + + u32 index = group * BITSET_SIZE + bit; + bs[bit] = false; + + handle->index = index; + handle->cpu_handle.ptr = m_heap_base_cpu.ptr + index * m_descriptor_increment_size; + handle->gpu_handle.ptr = m_shader_visible ? (m_heap_base_gpu.ptr + index * m_descriptor_increment_size) : 0; + return true; + } + + Panic("Out of fixed descriptors"); + return false; +} + +void D3D12DescriptorHeapManager::Free(u32 index) +{ + DebugAssert(index < m_num_descriptors); + + u32 group = index / BITSET_SIZE; + u32 bit = index % BITSET_SIZE; + m_free_slots[group][bit] = true; +} + +void D3D12DescriptorHeapManager::Free(D3D12DescriptorHandle* handle) +{ + if (handle->index == D3D12DescriptorHandle::INVALID_INDEX) + return; + + Free(handle->index); + handle->Clear(); +} + +D3D12DescriptorAllocator::D3D12DescriptorAllocator() = default; +D3D12DescriptorAllocator::~D3D12DescriptorAllocator() = default; + +bool D3D12DescriptorAllocator::Create(ID3D12Device* device, D3D12_DESCRIPTOR_HEAP_TYPE type, u32 num_descriptors) +{ + const D3D12_DESCRIPTOR_HEAP_DESC desc = {type, static_cast(num_descriptors), + D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE}; + const HRESULT hr = device->CreateDescriptorHeap(&desc, IID_PPV_ARGS(m_descriptor_heap.ReleaseAndGetAddressOf())); + if (FAILED(hr)) + { + Log_ErrorPrintf("CreateDescriptorHeap() failed: %08X", hr); + return false; + } + + m_num_descriptors = num_descriptors; + m_descriptor_increment_size = device->GetDescriptorHandleIncrementSize(type); + m_heap_base_cpu = m_descriptor_heap->GetCPUDescriptorHandleForHeapStart(); + m_heap_base_gpu = m_descriptor_heap->GetGPUDescriptorHandleForHeapStart(); + return true; +} + +void D3D12DescriptorAllocator::Destroy() +{ + m_descriptor_heap.Reset(); + m_descriptor_increment_size = 0; + m_num_descriptors = 0; + m_current_offset = 0; + m_heap_base_cpu = {}; + m_heap_base_gpu = {}; +} + +bool D3D12DescriptorAllocator::Allocate(u32 num_handles, D3D12DescriptorHandle* out_base_handle) +{ + if ((m_current_offset + num_handles) > m_num_descriptors) + return false; + + out_base_handle->index = m_current_offset; + out_base_handle->cpu_handle.ptr = m_heap_base_cpu.ptr + m_current_offset * m_descriptor_increment_size; + out_base_handle->gpu_handle.ptr = m_heap_base_gpu.ptr + m_current_offset * m_descriptor_increment_size; + m_current_offset += num_handles; + return true; +} + +void D3D12DescriptorAllocator::Reset() +{ + m_current_offset = 0; +} diff --git a/src/core/gpu/d3d12_descriptor_heap_manager.h b/src/core/gpu/d3d12_descriptor_heap_manager.h new file mode 100644 index 000000000..28e54d09c --- /dev/null +++ b/src/core/gpu/d3d12_descriptor_heap_manager.h @@ -0,0 +1,244 @@ +// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin +// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) + +#pragma once + +#include "common/hash_combine.h" +#include "common/types.h" +#include "common/windows_headers.h" + +#include +#include +#include +#include +#include +#include + +// This class provides an abstraction for D3D12 descriptor heaps. +struct D3D12DescriptorHandle final +{ + enum : u32 + { + INVALID_INDEX = 0xFFFFFFFF + }; + + D3D12_CPU_DESCRIPTOR_HANDLE cpu_handle{}; + D3D12_GPU_DESCRIPTOR_HANDLE gpu_handle{}; + u32 index = INVALID_INDEX; + + ALWAYS_INLINE operator bool() const { return index != INVALID_INDEX; } + + ALWAYS_INLINE operator D3D12_CPU_DESCRIPTOR_HANDLE() const { return cpu_handle; } + ALWAYS_INLINE operator D3D12_GPU_DESCRIPTOR_HANDLE() const { return gpu_handle; } + + ALWAYS_INLINE bool operator==(const D3D12DescriptorHandle& rhs) const { return (index == rhs.index); } + ALWAYS_INLINE bool operator!=(const D3D12DescriptorHandle& rhs) const { return (index != rhs.index); } + ALWAYS_INLINE bool operator<(const D3D12DescriptorHandle& rhs) const { return (index < rhs.index); } + ALWAYS_INLINE bool operator<=(const D3D12DescriptorHandle& rhs) const { return (index <= rhs.index); } + ALWAYS_INLINE bool operator>(const D3D12DescriptorHandle& rhs) const { return (index > rhs.index); } + ALWAYS_INLINE bool operator>=(const D3D12DescriptorHandle& rhs) const { return (index >= rhs.index); } + + ALWAYS_INLINE void Clear() + { + cpu_handle = {}; + gpu_handle = {}; + index = INVALID_INDEX; + } +}; + +class D3D12DescriptorHeapManager final +{ +public: + D3D12DescriptorHeapManager(); + ~D3D12DescriptorHeapManager(); + + ID3D12DescriptorHeap* GetDescriptorHeap() const { return m_descriptor_heap.Get(); } + u32 GetDescriptorIncrementSize() const { return m_descriptor_increment_size; } + + bool Create(ID3D12Device* device, D3D12_DESCRIPTOR_HEAP_TYPE type, u32 num_descriptors, bool shader_visible); + void Destroy(); + + bool Allocate(D3D12DescriptorHandle* handle); + void Free(D3D12DescriptorHandle* handle); + void Free(u32 index); + +private: + Microsoft::WRL::ComPtr m_descriptor_heap; + u32 m_num_descriptors = 0; + u32 m_descriptor_increment_size = 0; + bool m_shader_visible = false; + + D3D12_CPU_DESCRIPTOR_HANDLE m_heap_base_cpu = {}; + D3D12_GPU_DESCRIPTOR_HANDLE m_heap_base_gpu = {}; + + static constexpr u32 BITSET_SIZE = 1024; + using BitSetType = std::bitset; + std::vector m_free_slots = {}; +}; + +class D3D12DescriptorAllocator +{ +public: + D3D12DescriptorAllocator(); + ~D3D12DescriptorAllocator(); + + ALWAYS_INLINE ID3D12DescriptorHeap* GetDescriptorHeap() const { return m_descriptor_heap.Get(); } + ALWAYS_INLINE u32 GetDescriptorIncrementSize() const { return m_descriptor_increment_size; } + + bool Create(ID3D12Device* device, D3D12_DESCRIPTOR_HEAP_TYPE type, u32 num_descriptors); + void Destroy(); + + bool Allocate(u32 num_handles, D3D12DescriptorHandle* out_base_handle); + void Reset(); + +private: + Microsoft::WRL::ComPtr m_descriptor_heap; + u32 m_descriptor_increment_size = 0; + u32 m_num_descriptors = 0; + u32 m_current_offset = 0; + + D3D12_CPU_DESCRIPTOR_HANDLE m_heap_base_cpu = {}; + D3D12_GPU_DESCRIPTOR_HANDLE m_heap_base_gpu = {}; +}; + +template +class D3D12GroupedSamplerAllocator : private D3D12DescriptorAllocator +{ + struct Key + { + u32 idx[NumSamplers]; + + ALWAYS_INLINE bool operator==(const Key& rhs) const { return (std::memcmp(idx, rhs.idx, sizeof(idx)) == 0); } + ALWAYS_INLINE bool operator!=(const Key& rhs) const { return (std::memcmp(idx, rhs.idx, sizeof(idx)) != 0); } + }; + + struct KeyHash + { + ALWAYS_INLINE std::size_t operator()(const Key& key) const + { + size_t seed = 0; + for (u32 i : key.idx) + hash_combine(seed, i); + return seed; + } + }; + +public: + D3D12GroupedSamplerAllocator(); + ~D3D12GroupedSamplerAllocator(); + + using D3D12DescriptorAllocator::GetDescriptorHeap; + using D3D12DescriptorAllocator::GetDescriptorIncrementSize; + + bool Create(ID3D12Device* device, u32 num_descriptors); + void Destroy(); + + bool LookupSingle(ID3D12Device* device, D3D12DescriptorHandle* gpu_handle, const D3D12DescriptorHandle& cpu_handle); + bool LookupGroup(ID3D12Device* device, D3D12DescriptorHandle* gpu_handle, const D3D12DescriptorHandle* cpu_handles); + + // Clears cache but doesn't reset allocator. + void InvalidateCache(); + + void Reset(); + bool ShouldReset() const; + +private: + std::unordered_map m_groups; +}; + +template +D3D12GroupedSamplerAllocator::D3D12GroupedSamplerAllocator() = default; + +template +D3D12GroupedSamplerAllocator::~D3D12GroupedSamplerAllocator() = default; + +template +bool D3D12GroupedSamplerAllocator::Create(ID3D12Device* device, u32 num_descriptors) +{ + return D3D12DescriptorAllocator::Create(device, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, num_descriptors); +} + +template +void D3D12GroupedSamplerAllocator::Destroy() +{ + D3D12DescriptorAllocator::Destroy(); +} + +template +void D3D12GroupedSamplerAllocator::Reset() +{ + m_groups.clear(); + D3D12DescriptorAllocator::Reset(); +} + +template +void D3D12GroupedSamplerAllocator::InvalidateCache() +{ + m_groups.clear(); +} + +template +bool D3D12GroupedSamplerAllocator::LookupSingle(ID3D12Device* device, D3D12DescriptorHandle* gpu_handle, + const D3D12DescriptorHandle& cpu_handle) +{ + Key key; + key.idx[0] = cpu_handle.index; + for (u32 i = 1; i < NumSamplers; i++) + key.idx[i] = 0; + + auto it = m_groups.find(key); + if (it != m_groups.end()) + { + *gpu_handle = it->second; + return true; + } + + if (!Allocate(1, gpu_handle)) + return false; + + device->CopyDescriptorsSimple(1, *gpu_handle, cpu_handle, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER); + m_groups.emplace(key, *gpu_handle); + return true; +} + +template +bool D3D12GroupedSamplerAllocator::LookupGroup(ID3D12Device* device, D3D12DescriptorHandle* gpu_handle, + const D3D12DescriptorHandle* cpu_handles) +{ + Key key; + for (u32 i = 0; i < NumSamplers; i++) + key.idx[i] = cpu_handles[i].index; + + auto it = m_groups.find(key); + if (it != m_groups.end()) + { + *gpu_handle = it->second; + return true; + } + + if (!Allocate(NumSamplers, gpu_handle)) + return false; + + D3D12_CPU_DESCRIPTOR_HANDLE dst_handle = *gpu_handle; + UINT dst_size = NumSamplers; + D3D12_CPU_DESCRIPTOR_HANDLE src_handles[NumSamplers]; + UINT src_sizes[NumSamplers]; + for (u32 i = 0; i < NumSamplers; i++) + { + src_handles[i] = cpu_handles[i]; + src_sizes[i] = 1; + } + device->CopyDescriptors(1, &dst_handle, &dst_size, NumSamplers, src_handles, src_sizes, + D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER); + + m_groups.emplace(key, *gpu_handle); + return true; +} + +template +bool D3D12GroupedSamplerAllocator::ShouldReset() const +{ + // We only reset the sampler heap if more than half of the descriptors are used. + // This saves descriptor copying when there isn't a large number of sampler configs per frame. + return m_groups.size() >= (D3D12_MAX_SHADER_VISIBLE_SAMPLER_HEAP_SIZE / 2); +} diff --git a/src/core/gpu/d3d12_device.cpp b/src/core/gpu/d3d12_device.cpp new file mode 100644 index 000000000..63292c707 --- /dev/null +++ b/src/core/gpu/d3d12_device.cpp @@ -0,0 +1,2028 @@ +// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin +// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) + +#include "d3d12_device.h" +#include "d3d12_builders.h" +#include "d3d12_pipeline.h" +#include "d3d12_stream_buffer.h" +#include "d3d12_texture.h" +#include "d3d_common.h" +#include "postprocessing_chain.h" // TODO: Remove me + +#include "../host.h" + +#include "common/align.h" +#include "common/assert.h" +#include "common/bitutils.h" +#include "common/log.h" +#include "common/path.h" +#include "common/scoped_guard.h" +#include "common/string.h" +#include "common/string_util.h" + +#include "D3D12MemAlloc.h" +#include "fmt/format.h" + +#include +#include + +Log_SetChannel(D3D12Device); + +// Tweakables +enum : u32 +{ + MIN_TEXEL_BUFFER_ELEMENTS = 1024 * 512, + + MAX_DRAW_CALLS_PER_FRAME = 2048, + MAX_DESCRIPTORS_PER_FRAME = 32768, + MAX_SAMPLERS_PER_FRAME = D3D12_MAX_SHADER_VISIBLE_SAMPLER_HEAP_SIZE, + MAX_DESCRIPTOR_SETS_PER_FRAME = MAX_DRAW_CALLS_PER_FRAME, + + MAX_PERSISTENT_DESCRIPTORS = 2048, + MAX_PERSISTENT_RTVS = 512, + MAX_PERSISTENT_DSVS = 128, + MAX_PERSISTENT_SAMPLERS = 512, + + VERTEX_BUFFER_SIZE = 32 * 1024 * 1024, + INDEX_BUFFER_SIZE = 16 * 1024 * 1024, + VERTEX_UNIFORM_BUFFER_SIZE = 8 * 1024 * 1024, + FRAGMENT_UNIFORM_BUFFER_SIZE = 8 * 1024 * 1024, + TEXTURE_BUFFER_SIZE = 64 * 1024 * 1024, + + // UNIFORM_PUSH_CONSTANTS_STAGES = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, + UNIFORM_PUSH_CONSTANTS_SIZE = 128, + + MAX_UNIFORM_BUFFER_SIZE = 1024, +}; + +// TODO: FIXME +namespace Host { +extern bool IsFullscreen(); +extern void SetFullscreen(bool enabled); +} // namespace Host + +// We need to synchronize instance creation because of adapter enumeration from the UI thread. +static std::mutex s_instance_mutex; + +static constexpr D3D12_CLEAR_VALUE s_present_clear_color = {DXGI_FORMAT_R8G8B8A8_UNORM, {0.0f, 0.0f, 0.0f, 1.0f}}; +static constexpr GPUTexture::Format s_swap_chain_format = GPUTexture::Format::RGBA8; + +#ifdef _DEBUG +#include "WinPixEventRuntime/pix3.h" +static u32 s_debug_scope_depth = 0; +#endif + +D3D12Device::D3D12Device() +{ +#ifdef _DEBUG + s_debug_scope_depth = 0; +#endif +} + +D3D12Device::~D3D12Device() +{ + Assert(!m_device); +} + +static constexpr u32 GetActiveTexturesForLayout(GPUPipeline::Layout layout) +{ + constexpr std::array(GPUPipeline::Layout::MaxCount)> counts = { + 1, // SingleTextureAndUBO + 1, // SingleTextureAndPushConstants + 0, // SingleTextureBufferAndPushConstants + GPUDevice::MAX_TEXTURE_SAMPLERS, // MultiTextureAndUBO + }; + + return counts[static_cast(layout)]; +} + +D3D12Device::ComPtr D3D12Device::SerializeRootSignature(const D3D12_ROOT_SIGNATURE_DESC* desc) +{ + ComPtr blob; + ComPtr error_blob; + const HRESULT hr = + D3D12SerializeRootSignature(desc, D3D_ROOT_SIGNATURE_VERSION_1, blob.GetAddressOf(), error_blob.GetAddressOf()); + if (FAILED(hr)) + { + Log_ErrorPrintf("D3D12SerializeRootSignature() failed: %08X", hr); + if (error_blob) + Log_ErrorPrintf("%s", error_blob->GetBufferPointer()); + + return {}; + } + + return blob; +} + +D3D12Device::ComPtr D3D12Device::CreateRootSignature(const D3D12_ROOT_SIGNATURE_DESC* desc) +{ + ComPtr blob = SerializeRootSignature(desc); + if (!blob) + return {}; + + ComPtr rs; + const HRESULT hr = + m_device->CreateRootSignature(0, blob->GetBufferPointer(), blob->GetBufferSize(), IID_PPV_ARGS(rs.GetAddressOf())); + if (FAILED(hr)) + { + Log_ErrorPrintf("CreateRootSignature() failed: %08X", hr); + return {}; + } + + return rs; +} + +bool D3D12Device::CreateDevice(const std::string_view& adapter) +{ + std::unique_lock lock(s_instance_mutex); + + m_dxgi_factory = D3DCommon::CreateFactory(m_debug_device); + if (!m_dxgi_factory) + return false; + + m_adapter = D3DCommon::GetAdapterByName(m_dxgi_factory.Get(), adapter); + + HRESULT hr; + + // Enabling the debug layer will fail if the Graphics Tools feature is not installed. + if (m_debug_device) + { + ComPtr debug12; + hr = D3D12GetDebugInterface(IID_PPV_ARGS(debug12.GetAddressOf())); + if (SUCCEEDED(hr)) + { + debug12->EnableDebugLayer(); + } + else + { + Log_ErrorPrintf("Debug layer requested but not available."); + m_debug_device = false; + } + } + + // Create the actual device. + m_feature_level = D3D_FEATURE_LEVEL_11_0; + hr = D3D12CreateDevice(m_adapter.Get(), m_feature_level, IID_PPV_ARGS(&m_device)); + if (FAILED(hr)) + { + Log_ErrorPrintf("Failed to create D3D12 device: %08X", hr); + return false; + } + + if (!m_adapter) + { + const LUID luid(m_device->GetAdapterLuid()); + if (FAILED(m_dxgi_factory->EnumAdapterByLuid(luid, IID_PPV_ARGS(m_adapter.GetAddressOf())))) + Log_ErrorPrintf("Failed to get lookup adapter by device LUID"); + } + + if (m_debug_device) + { + ComPtr info_queue; + if (SUCCEEDED(m_device.As(&info_queue))) + { + if (IsDebuggerPresent()) + { + info_queue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_ERROR, TRUE); + info_queue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_WARNING, TRUE); + } + + D3D12_INFO_QUEUE_FILTER filter = {}; + std::array id_list{ + D3D12_MESSAGE_ID_CLEARRENDERTARGETVIEW_MISMATCHINGCLEARVALUE, + D3D12_MESSAGE_ID_CLEARDEPTHSTENCILVIEW_MISMATCHINGCLEARVALUE, + D3D12_MESSAGE_ID_CREATEGRAPHICSPIPELINESTATE_RENDERTARGETVIEW_NOT_SET, + D3D12_MESSAGE_ID_CREATEINPUTLAYOUT_TYPE_MISMATCH, + D3D12_MESSAGE_ID_DRAW_EMPTY_SCISSOR_RECTANGLE, + }; + filter.DenyList.NumIDs = static_cast(id_list.size()); + filter.DenyList.pIDList = id_list.data(); + info_queue->PushStorageFilter(&filter); + } + } + + const D3D12_COMMAND_QUEUE_DESC queue_desc = {D3D12_COMMAND_LIST_TYPE_DIRECT, D3D12_COMMAND_QUEUE_PRIORITY_NORMAL, + D3D12_COMMAND_QUEUE_FLAG_NONE}; + hr = m_device->CreateCommandQueue(&queue_desc, IID_PPV_ARGS(&m_command_queue)); + if (FAILED(hr)) + { + Log_ErrorPrintf("Failed to create command queue: %08X", hr); + return false; + } + + D3D12MA::ALLOCATOR_DESC allocatorDesc = {}; + allocatorDesc.pDevice = m_device.Get(); + allocatorDesc.pAdapter = m_adapter.Get(); + allocatorDesc.Flags = + D3D12MA::ALLOCATOR_FLAG_SINGLETHREADED | + D3D12MA::ALLOCATOR_FLAG_DEFAULT_POOLS_NOT_ZEROED /* | D3D12MA::ALLOCATOR_FLAG_ALWAYS_COMMITTED*/; + + hr = D3D12MA::CreateAllocator(&allocatorDesc, m_allocator.GetAddressOf()); + if (FAILED(hr)) + { + Log_ErrorPrintf("D3D12MA::CreateAllocator() failed with HRESULT %08X", hr); + return false; + } + + hr = m_device->CreateFence(m_completed_fence_value, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&m_fence)); + if (FAILED(hr)) + { + Log_ErrorPrintf("Failed to create fence: %08X", hr); + return false; + } + + m_fence_event = CreateEvent(nullptr, FALSE, FALSE, nullptr); + if (m_fence_event == NULL) + { + Log_ErrorPrintf("Failed to create fence event: %08X", GetLastError()); + return false; + } + + SetFeatures(); + + if (!CreateCommandLists() || !CreateDescriptorHeaps()) + return false; + + if (!m_window_info.IsSurfaceless() && !CreateSwapChain()) + return false; + + if (!CreateRootSignatures() || !CreateBuffers()) + return false; + + CreateTimestampQuery(); + return true; +} + +void D3D12Device::DestroyDevice() +{ + std::unique_lock lock(s_instance_mutex); + + // Toss command list if we're recording... + if (InRenderPass()) + EndRenderPass(); + + WaitForGPUIdle(); + + DestroyDeferredObjects(m_current_fence_value); + DestroyDownloadBuffer(); + DestroySamplers(); + DestroyTimestampQuery(); + DestroyBuffers(); + DestroyDescriptorHeaps(); + DestroyRootSignatures(); + DestroySwapChain(); + DestroyCommandLists(); + + m_fence.Reset(); + if (m_fence_event != NULL) + CloseHandle(m_fence_event); + + m_allocator.Reset(); + m_command_queue.Reset(); + m_device.Reset(); + m_adapter.Reset(); + m_dxgi_factory.Reset(); +} + +bool D3D12Device::CreateCommandLists() +{ + for (u32 i = 0; i < NUM_COMMAND_LISTS; i++) + { + CommandList& res = m_command_lists[i]; + HRESULT hr; + + for (u32 j = 0; j < 2; j++) + { + hr = m_device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, + IID_PPV_ARGS(res.command_allocators[j].GetAddressOf())); + if (FAILED(hr)) + { + Log_ErrorPrintf("CreateCommandAllocator() failed: %08X", hr); + return false; + } + + hr = m_device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, res.command_allocators[j].Get(), nullptr, + IID_PPV_ARGS(res.command_lists[j].GetAddressOf())); + if (FAILED(hr)) + { + Log_ErrorPrintf("CreateCommandList() failed: %08X", hr); + return false; + } + + // Close the command lists, since the first thing we do is reset them. + hr = res.command_lists[j]->Close(); + if (FAILED(hr)) + { + Log_ErrorPrintf("Close() failed: %08X", hr); + return false; + } + } + + if (!res.descriptor_allocator.Create(m_device.Get(), D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, + MAX_DESCRIPTORS_PER_FRAME)) + { + Log_ErrorPrintf("Failed to create per frame descriptor allocator"); + return false; + } + + if (!res.sampler_allocator.Create(m_device.Get(), MAX_SAMPLERS_PER_FRAME)) + { + Log_ErrorPrintf("Failed to create per frame sampler allocator"); + return false; + } + } + + MoveToNextCommandList(); + return true; +} + +void D3D12Device::MoveToNextCommandList() +{ + m_current_command_list = (m_current_command_list + 1) % NUM_COMMAND_LISTS; + m_current_fence_value++; + + // We may have to wait if this command list hasn't finished on the GPU. + CommandList& res = m_command_lists[m_current_command_list]; + WaitForFence(res.fence_counter); + res.fence_counter = m_current_fence_value; + res.init_list_used = false; + + // Begin command list. + res.command_allocators[1]->Reset(); + res.command_lists[1]->Reset(res.command_allocators[1].Get(), nullptr); + res.descriptor_allocator.Reset(); + if (res.sampler_allocator.ShouldReset()) + res.sampler_allocator.Reset(); + + if (res.has_timestamp_query) + { + // readback timestamp from the last time this cmdlist was used. + // we don't need to worry about disjoint in dx12, the frequency is reliable within a single cmdlist. + const u32 offset = (m_current_command_list * (sizeof(u64) * NUM_TIMESTAMP_QUERIES_PER_CMDLIST)); + const D3D12_RANGE read_range = {offset, offset + (sizeof(u64) * NUM_TIMESTAMP_QUERIES_PER_CMDLIST)}; + void* map; + HRESULT hr = m_timestamp_query_buffer->Map(0, &read_range, &map); + if (SUCCEEDED(hr)) + { + u64 timestamps[2]; + std::memcpy(timestamps, static_cast(map) + offset, sizeof(timestamps)); + m_accumulated_gpu_time += + static_cast(static_cast(timestamps[1] - timestamps[0]) / m_timestamp_frequency); + + const D3D12_RANGE write_range = {}; + m_timestamp_query_buffer->Unmap(0, &write_range); + } + else + { + Log_WarningPrintf("Map() for timestamp query failed: %08X", hr); + } + } + + res.has_timestamp_query = m_gpu_timing_enabled; + if (m_gpu_timing_enabled) + { + res.command_lists[1]->EndQuery(m_timestamp_query_heap.Get(), D3D12_QUERY_TYPE_TIMESTAMP, + m_current_command_list * NUM_TIMESTAMP_QUERIES_PER_CMDLIST); + } + + ID3D12DescriptorHeap* heaps[2] = {res.descriptor_allocator.GetDescriptorHeap(), + res.sampler_allocator.GetDescriptorHeap()}; + res.command_lists[1]->SetDescriptorHeaps(static_cast(std::size(heaps)), heaps); + + m_allocator->SetCurrentFrameIndex(static_cast(m_current_fence_value)); + InvalidateCachedState(); +} + +void D3D12Device::DestroyCommandLists() +{ + for (CommandList& resources : m_command_lists) + { + resources.descriptor_allocator.Destroy(); + resources.sampler_allocator.Destroy(); + for (u32 i = 0; i < 2; i++) + { + resources.command_lists[i].Reset(); + resources.command_allocators[i].Reset(); + } + } +} + +bool D3D12Device::CreateDescriptorHeaps() +{ + if (!m_descriptor_heap_manager.Create(m_device.Get(), D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, + MAX_PERSISTENT_DESCRIPTORS, false) || + !m_rtv_heap_manager.Create(m_device.Get(), D3D12_DESCRIPTOR_HEAP_TYPE_RTV, MAX_PERSISTENT_RTVS, false) || + !m_dsv_heap_manager.Create(m_device.Get(), D3D12_DESCRIPTOR_HEAP_TYPE_DSV, MAX_PERSISTENT_DSVS, false) || + !m_sampler_heap_manager.Create(m_device.Get(), D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, MAX_PERSISTENT_SAMPLERS, + false)) + { + return false; + } + + // Allocate null SRV descriptor for unbound textures. + constexpr D3D12_SHADER_RESOURCE_VIEW_DESC null_srv_desc = {DXGI_FORMAT_R8G8B8A8_UNORM, D3D12_SRV_DIMENSION_TEXTURE2D, + D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING}; + + if (!m_descriptor_heap_manager.Allocate(&m_null_srv_descriptor)) + { + Log_ErrorPrint("Failed to allocate null descriptor"); + return false; + } + + m_device->CreateShaderResourceView(nullptr, &null_srv_desc, m_null_srv_descriptor.cpu_handle); + + // Same for samplers. + m_point_sampler = GetSampler(GPUSampler::GetNearestConfig()); + for (u32 i = 0; i < MAX_TEXTURE_SAMPLERS; i++) + m_current_samplers[i] = m_point_sampler; + return true; +} + +void D3D12Device::DestroyDescriptorHeaps() +{ + if (m_null_srv_descriptor) + m_descriptor_heap_manager.Free(&m_null_srv_descriptor); + m_sampler_heap_manager.Destroy(); + m_dsv_heap_manager.Destroy(); + m_rtv_heap_manager.Destroy(); + m_descriptor_heap_manager.Destroy(); +} + +ID3D12GraphicsCommandList4* D3D12Device::GetInitCommandList() +{ + CommandList& res = m_command_lists[m_current_command_list]; + if (!res.init_list_used) + { + HRESULT hr = res.command_allocators[0]->Reset(); + DebugAssertMsg(SUCCEEDED(hr), "Reset init command allocator failed"); + + res.command_lists[0]->Reset(res.command_allocators[0].Get(), nullptr); + DebugAssertMsg(SUCCEEDED(hr), "Reset init command list failed"); + res.init_list_used = true; + } + + return res.command_lists[0].Get(); +} + +void D3D12Device::SubmitCommandList(bool wait_for_completion) +{ + CommandList& res = m_command_lists[m_current_command_list]; + HRESULT hr; + + if (res.has_timestamp_query) + { + // write the timestamp back at the end of the cmdlist + res.command_lists[1]->EndQuery(m_timestamp_query_heap.Get(), D3D12_QUERY_TYPE_TIMESTAMP, + (m_current_command_list * NUM_TIMESTAMP_QUERIES_PER_CMDLIST) + 1); + res.command_lists[1]->ResolveQueryData(m_timestamp_query_heap.Get(), D3D12_QUERY_TYPE_TIMESTAMP, + m_current_command_list * NUM_TIMESTAMP_QUERIES_PER_CMDLIST, + NUM_TIMESTAMP_QUERIES_PER_CMDLIST, m_timestamp_query_buffer.Get(), + m_current_command_list * (sizeof(u64) * NUM_TIMESTAMP_QUERIES_PER_CMDLIST)); + } + + // TODO: error handling + if (res.init_list_used) + { + hr = res.command_lists[0]->Close(); + if (FAILED(hr)) + { + Log_ErrorPrintf("Closing init command list failed with HRESULT %08X", hr); + Panic("TODO cannot continue"); + } + } + + // Close and queue command list. + hr = res.command_lists[1]->Close(); + if (FAILED(hr)) + { + Log_ErrorPrintf("Closing main command list failed with HRESULT %08X", hr); + Panic("TODO cannot continue"); + } + + if (res.init_list_used) + { + const std::array execute_lists{res.command_lists[0].Get(), res.command_lists[1].Get()}; + m_command_queue->ExecuteCommandLists(static_cast(execute_lists.size()), execute_lists.data()); + } + else + { + const std::array execute_lists{res.command_lists[1].Get()}; + m_command_queue->ExecuteCommandLists(static_cast(execute_lists.size()), execute_lists.data()); + } + + // Update fence when GPU has completed. + hr = m_command_queue->Signal(m_fence.Get(), res.fence_counter); + DebugAssertMsg(SUCCEEDED(hr), "Signal fence"); + + MoveToNextCommandList(); + + if (wait_for_completion) + WaitForFence(res.fence_counter); +} + +void D3D12Device::SubmitCommandList(bool wait_for_completion, const char* reason, ...) +{ + std::va_list ap; + va_start(ap, reason); + const std::string reason_str(StringUtil::StdStringFromFormatV(reason, ap)); + va_end(ap); + + Log_WarningPrintf("Executing command buffer due to '%s'", reason_str.c_str()); + SubmitCommandList(wait_for_completion); +} + +void D3D12Device::SubmitCommandListAndRestartRenderPass(const char* reason) +{ + if (InRenderPass()) + EndRenderPass(); + + D3D12Framebuffer* fb = m_current_framebuffer; + D3D12Pipeline* pl = m_current_pipeline; + SubmitCommandList(false, "%s", reason); + + if (fb) + SetFramebuffer(fb); + SetPipeline(pl); + BeginRenderPass(); +} + +void D3D12Device::WaitForFence(u64 fence) +{ + if (m_completed_fence_value >= fence) + return; + + // Try non-blocking check. + m_completed_fence_value = m_fence->GetCompletedValue(); + if (m_completed_fence_value < fence) + { + // Fall back to event. + HRESULT hr = m_fence->SetEventOnCompletion(fence, m_fence_event); + DebugAssertMsg(SUCCEEDED(hr), "Set fence event on completion"); + WaitForSingleObject(m_fence_event, INFINITE); + m_completed_fence_value = m_fence->GetCompletedValue(); + } + + // Release resources for as many command lists which have completed. + DestroyDeferredObjects(m_completed_fence_value); +} + +void D3D12Device::WaitForGPUIdle() +{ + u32 index = (m_current_command_list + 1) % NUM_COMMAND_LISTS; + for (u32 i = 0; i < (NUM_COMMAND_LISTS - 1); i++) + { + WaitForFence(m_command_lists[index].fence_counter); + index = (index + 1) % NUM_COMMAND_LISTS; + } +} + +bool D3D12Device::CreateTimestampQuery() +{ + constexpr u32 QUERY_COUNT = NUM_TIMESTAMP_QUERIES_PER_CMDLIST * NUM_COMMAND_LISTS; + constexpr u32 BUFFER_SIZE = sizeof(u64) * QUERY_COUNT; + + const D3D12_QUERY_HEAP_DESC desc = {D3D12_QUERY_HEAP_TYPE_TIMESTAMP, QUERY_COUNT}; + HRESULT hr = m_device->CreateQueryHeap(&desc, IID_PPV_ARGS(m_timestamp_query_heap.GetAddressOf())); + if (FAILED(hr)) + { + Log_ErrorPrintf("CreateQueryHeap() for timestamp failed with %08X", hr); + m_features.gpu_timing = false; + return false; + } + + const D3D12MA::ALLOCATION_DESC allocation_desc = {D3D12MA::ALLOCATION_FLAG_NONE, D3D12_HEAP_TYPE_READBACK}; + const D3D12_RESOURCE_DESC resource_desc = {D3D12_RESOURCE_DIMENSION_BUFFER, + 0, + BUFFER_SIZE, + 1, + 1, + 1, + DXGI_FORMAT_UNKNOWN, + {1, 0}, + D3D12_TEXTURE_LAYOUT_ROW_MAJOR, + D3D12_RESOURCE_FLAG_NONE}; + hr = m_allocator->CreateResource(&allocation_desc, &resource_desc, D3D12_RESOURCE_STATE_COPY_DEST, nullptr, + m_timestamp_query_allocation.GetAddressOf(), + IID_PPV_ARGS(m_timestamp_query_buffer.GetAddressOf())); + if (FAILED(hr)) + { + Log_ErrorPrintf("CreateResource() for timestamp failed with %08X", hr); + m_features.gpu_timing = false; + return false; + } + + u64 frequency; + hr = m_command_queue->GetTimestampFrequency(&frequency); + if (FAILED(hr)) + { + Log_ErrorPrintf("GetTimestampFrequency() failed: %08X", hr); + m_features.gpu_timing = false; + return false; + } + + m_timestamp_frequency = static_cast(frequency) / 1000.0; + return true; +} + +void D3D12Device::DestroyTimestampQuery() +{ + m_timestamp_query_buffer.Reset(); + m_timestamp_query_allocation.Reset(); + m_timestamp_query_heap.Reset(); +} + +float D3D12Device::GetAndResetAccumulatedGPUTime() +{ + const float time = m_accumulated_gpu_time; + m_accumulated_gpu_time = 0.0f; + return time; +} + +bool D3D12Device::SetGPUTimingEnabled(bool enabled) +{ + m_gpu_timing_enabled = enabled && m_features.gpu_timing; + return (enabled == m_gpu_timing_enabled); +} + +void D3D12Device::DeferObjectDestruction(ComPtr resource) +{ + DebugAssert(resource); + m_cleanup_resources.emplace_back(GetCurrentFenceValue(), + std::pair(nullptr, resource.Detach())); +} + +void D3D12Device::DeferResourceDestruction(ComPtr allocation, ComPtr resource) +{ + DebugAssert(allocation && resource); + m_cleanup_resources.emplace_back( + GetCurrentFenceValue(), std::pair(allocation.Detach(), resource.Detach())); +} + +void D3D12Device::DeferDescriptorDestruction(D3D12DescriptorHeapManager& heap, D3D12DescriptorHandle* descriptor) +{ + DebugAssert(descriptor->index != D3D12DescriptorHandle::INVALID_INDEX); + m_cleanup_descriptors.emplace_back(GetCurrentFenceValue(), + std::pair(&heap, *descriptor)); + descriptor->Clear(); +} + +void D3D12Device::DestroyDeferredObjects(u64 fence_value) +{ + while (!m_cleanup_descriptors.empty()) + { + auto& it = m_cleanup_descriptors.front(); + if (it.first > fence_value) + break; + + it.second.first->Free(it.second.second.index); + m_cleanup_descriptors.pop_front(); + } + + while (!m_cleanup_resources.empty()) + { + auto& it = m_cleanup_resources.front(); + if (it.first > fence_value) + break; + + it.second.second->Release(); + if (it.second.first) + it.second.first->Release(); + m_cleanup_resources.pop_front(); + } +} + +void D3D12Device::GetAdapterAndModeList(AdapterAndModeList* ret, IDXGIFactory5* factory) +{ + ret->adapter_names = D3DCommon::GetAdapterNames(factory); + ret->fullscreen_modes = D3DCommon::GetFullscreenModes(factory, {}); +} + +GPUDevice::AdapterAndModeList D3D12Device::StaticGetAdapterAndModeList() +{ + AdapterAndModeList ret; + std::unique_lock lock(s_instance_mutex); + + // Device shouldn't be torn down since we have the lock. + if (g_gpu_device && g_gpu_device->GetRenderAPI() == RenderAPI::D3D12) + { + GetAdapterAndModeList(&ret, D3D12Device::GetInstance().m_dxgi_factory.Get()); + } + else + { + ComPtr factory = D3DCommon::CreateFactory(false); + if (factory) + GetAdapterAndModeList(&ret, factory.Get()); + } + + return ret; +} + +GPUDevice::AdapterAndModeList D3D12Device::GetAdapterAndModeList() +{ + AdapterAndModeList ret; + GetAdapterAndModeList(&ret, m_dxgi_factory.Get()); + return ret; +} + +RenderAPI D3D12Device::GetRenderAPI() const +{ + return RenderAPI::D3D12; +} + +bool D3D12Device::HasSurface() const +{ + return static_cast(m_swap_chain); +} + +bool D3D12Device::CreateSwapChain() +{ + if (m_window_info.type != WindowInfo::Type::Win32) + return false; + + DXGI_FORMAT dxgi_format; + LookupNativeFormat(s_swap_chain_format, &dxgi_format, nullptr, nullptr, nullptr); + + const HWND window_hwnd = reinterpret_cast(m_window_info.window_handle); + RECT client_rc{}; + GetClientRect(window_hwnd, &client_rc); + + DXGI_MODE_DESC fullscreen_mode = {}; + ComPtr fullscreen_output; + if (Host::IsFullscreen()) + { + u32 fullscreen_width, fullscreen_height; + float fullscreen_refresh_rate; + m_is_exclusive_fullscreen = + GetRequestedExclusiveFullscreenMode(&fullscreen_width, &fullscreen_height, &fullscreen_refresh_rate) && + D3DCommon::GetRequestedExclusiveFullscreenModeDesc(m_dxgi_factory.Get(), client_rc, fullscreen_width, + fullscreen_height, fullscreen_refresh_rate, dxgi_format, + &fullscreen_mode, fullscreen_output.GetAddressOf()); + } + else + { + m_is_exclusive_fullscreen = false; + } + + DXGI_SWAP_CHAIN_DESC1 swap_chain_desc = {}; + swap_chain_desc.Width = static_cast(client_rc.right - client_rc.left); + swap_chain_desc.Height = static_cast(client_rc.bottom - client_rc.top); + swap_chain_desc.Format = dxgi_format; + swap_chain_desc.SampleDesc.Count = 1; + swap_chain_desc.BufferCount = 3; + swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; + + m_using_allow_tearing = (m_allow_tearing_supported && !m_is_exclusive_fullscreen); + if (m_using_allow_tearing) + swap_chain_desc.Flags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING; + + HRESULT hr = S_OK; + + if (m_is_exclusive_fullscreen) + { + DXGI_SWAP_CHAIN_DESC1 fs_sd_desc = swap_chain_desc; + DXGI_SWAP_CHAIN_FULLSCREEN_DESC fs_desc = {}; + + fs_sd_desc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH; + fs_sd_desc.Width = fullscreen_mode.Width; + fs_sd_desc.Height = fullscreen_mode.Height; + fs_desc.RefreshRate = fullscreen_mode.RefreshRate; + fs_desc.ScanlineOrdering = fullscreen_mode.ScanlineOrdering; + fs_desc.Scaling = fullscreen_mode.Scaling; + fs_desc.Windowed = FALSE; + + Log_VerbosePrintf("Creating a %dx%d exclusive fullscreen swap chain", fs_sd_desc.Width, fs_sd_desc.Height); + hr = m_dxgi_factory->CreateSwapChainForHwnd(m_command_queue.Get(), window_hwnd, &fs_sd_desc, &fs_desc, + fullscreen_output.Get(), m_swap_chain.ReleaseAndGetAddressOf()); + if (FAILED(hr)) + { + Log_WarningPrint("Failed to create fullscreen swap chain, trying windowed."); + m_is_exclusive_fullscreen = false; + m_using_allow_tearing = m_allow_tearing_supported; + } + } + + if (!m_is_exclusive_fullscreen) + { + Log_VerbosePrintf("Creating a %dx%d windowed swap chain", swap_chain_desc.Width, swap_chain_desc.Height); + hr = m_dxgi_factory->CreateSwapChainForHwnd(m_command_queue.Get(), window_hwnd, &swap_chain_desc, nullptr, nullptr, + m_swap_chain.ReleaseAndGetAddressOf()); + } + + hr = m_dxgi_factory->MakeWindowAssociation(window_hwnd, DXGI_MWA_NO_WINDOW_CHANGES); + if (FAILED(hr)) + Log_WarningPrint("MakeWindowAssociation() to disable ALT+ENTER failed"); + + if (!CreateSwapChainRTV()) + { + DestroySwapChain(); + return false; + } + + // Render a frame as soon as possible to clear out whatever was previously being displayed. + RenderBlankFrame(); + return true; +} + +bool D3D12Device::CreateSwapChainRTV() +{ + DXGI_SWAP_CHAIN_DESC swap_chain_desc; + HRESULT hr = m_swap_chain->GetDesc(&swap_chain_desc); + if (FAILED(hr)) + return false; + + const D3D12_RENDER_TARGET_VIEW_DESC rtv_desc = {swap_chain_desc.BufferDesc.Format, D3D12_RTV_DIMENSION_TEXTURE2D}; + + for (u32 i = 0; i < swap_chain_desc.BufferCount; i++) + { + ComPtr backbuffer; + hr = m_swap_chain->GetBuffer(i, IID_PPV_ARGS(backbuffer.GetAddressOf())); + if (FAILED(hr)) + { + Log_ErrorPrintf("GetBuffer for RTV failed: 0x%08X", hr); + DestroySwapChainRTVs(); + return false; + } + + D3D12::SetObjectNameFormatted(backbuffer.Get(), "Swap Chain Buffer #%u", i); + + D3D12DescriptorHandle rtv; + if (!m_rtv_heap_manager.Allocate(&rtv)) + { + Log_ErrorPrintf("Failed to allocate RTV handle"); + DestroySwapChainRTVs(); + return false; + } + + m_device->CreateRenderTargetView(backbuffer.Get(), &rtv_desc, rtv); + m_swap_chain_buffers.emplace_back(std::move(backbuffer), rtv); + } + + m_window_info.surface_width = swap_chain_desc.BufferDesc.Width; + m_window_info.surface_height = swap_chain_desc.BufferDesc.Height; + m_window_info.surface_format = s_swap_chain_format; + Log_VerbosePrintf("Swap chain buffer size: %ux%u", m_window_info.surface_width, m_window_info.surface_height); + + if (m_window_info.type == WindowInfo::Type::Win32) + { + BOOL fullscreen = FALSE; + DXGI_SWAP_CHAIN_DESC desc; + if (SUCCEEDED(m_swap_chain->GetFullscreenState(&fullscreen, nullptr)) && fullscreen && + SUCCEEDED(m_swap_chain->GetDesc(&desc))) + { + m_window_info.surface_refresh_rate = static_cast(desc.BufferDesc.RefreshRate.Numerator) / + static_cast(desc.BufferDesc.RefreshRate.Denominator); + } + else + { + m_window_info.surface_refresh_rate = 0.0f; + } + } + + m_current_swap_chain_buffer = 0; + return true; +} + +void D3D12Device::DestroySwapChainRTVs() +{ + // Runtime gets cranky if we don't submit the current buffer... + if (InRenderPass()) + EndRenderPass(); + SubmitCommandList(true); + + for (auto it = m_swap_chain_buffers.rbegin(); it != m_swap_chain_buffers.rend(); ++it) + { + m_rtv_heap_manager.Free(it->second.index); + it->first.Reset(); + } + m_swap_chain_buffers.clear(); + m_current_swap_chain_buffer = 0; +} + +void D3D12Device::DestroySwapChain() +{ + if (!m_swap_chain) + return; + + DestroySwapChainRTVs(); + + // switch out of fullscreen before destroying + BOOL is_fullscreen; + if (SUCCEEDED(m_swap_chain->GetFullscreenState(&is_fullscreen, nullptr)) && is_fullscreen) + m_swap_chain->SetFullscreenState(FALSE, nullptr); + + m_swap_chain.Reset(); + m_is_exclusive_fullscreen = false; +} + +void D3D12Device::RenderBlankFrame() +{ + if (InRenderPass()) + EndRenderPass(); + + auto& swap_chain_buf = m_swap_chain_buffers[m_current_swap_chain_buffer]; + ID3D12GraphicsCommandList4* cmdlist = GetCommandList(); + m_current_swap_chain_buffer = ((m_current_swap_chain_buffer + 1) % static_cast(m_swap_chain_buffers.size())); + D3D12Texture::TransitionSubresourceToState(cmdlist, swap_chain_buf.first.Get(), 0, D3D12_RESOURCE_STATE_COMMON, + D3D12_RESOURCE_STATE_RENDER_TARGET); + cmdlist->ClearRenderTargetView(swap_chain_buf.second, s_present_clear_color.Color, 0, nullptr); + D3D12Texture::TransitionSubresourceToState(cmdlist, swap_chain_buf.first.Get(), 0, D3D12_RESOURCE_STATE_RENDER_TARGET, + D3D12_RESOURCE_STATE_PRESENT); + SubmitCommandList(false); + m_swap_chain->Present(0, m_using_allow_tearing ? DXGI_PRESENT_ALLOW_TEARING : 0); +} + +bool D3D12Device::UpdateWindow() +{ + WaitForGPUIdle(); + DestroySwapChain(); + + if (!AcquireWindow(false)) + return false; + + if (m_window_info.IsSurfaceless()) + return true; + + if (!CreateSwapChain()) + { + Log_ErrorPrintf("Failed to create swap chain on updated window"); + return false; + } + + RenderBlankFrame(); + return true; +} + +void D3D12Device::ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale) +{ + if (!m_swap_chain) + return; + + m_window_info.surface_scale = new_window_scale; + + if (m_window_info.surface_width == static_cast(new_window_width) && + m_window_info.surface_height == static_cast(new_window_height)) + { + return; + } + + DestroySwapChainRTVs(); + + HRESULT hr = m_swap_chain->ResizeBuffers(0, 0, 0, DXGI_FORMAT_UNKNOWN, + m_using_allow_tearing ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0); + if (FAILED(hr)) + Log_ErrorPrintf("ResizeBuffers() failed: 0x%08X", hr); + + if (!CreateSwapChainRTV()) + Panic("Failed to recreate swap chain RTV after resize"); +} + +void D3D12Device::DestroySurface() +{ + DestroySwapChainRTVs(); + DestroySwapChain(); +} + +std::string D3D12Device::GetShaderCacheBaseName(const std::string_view& type) const +{ + return fmt::format("d3d12_{}{}", type, m_debug_device ? "_debug" : ""); +} + +bool D3D12Device::SupportsTextureFormat(GPUTexture::Format format) const +{ + constexpr u32 required = D3D12_FORMAT_SUPPORT1_TEXTURE2D | D3D12_FORMAT_SUPPORT1_SHADER_SAMPLE; + + DXGI_FORMAT dxgi_format; + LookupNativeFormat(format, &dxgi_format, nullptr, nullptr, nullptr); + + D3D12_FEATURE_DATA_FORMAT_SUPPORT support = {dxgi_format}; + return SUCCEEDED(m_device->CheckFeatureSupport(D3D12_FEATURE_FORMAT_SUPPORT, &support, sizeof(support))) && + (support.Support1 & required) == required; +} + +#if 0 +// TODO: Enable me +std::string D3D12Device::GetDriverInfo() const +{ + std::string ret = "Unknown Feature Level"; + + static constexpr std::array, 4> feature_level_names = { { + {D3D_FEATURE_LEVEL_11_0, "D3D_FEATURE_LEVEL_11_0"}, + {D3D_FEATURE_LEVEL_11_1, "D3D_FEATURE_LEVEL_11_1"}, + } }; + + for (size_t i = 0; i < std::size(feature_level_names); i++) + { + if (m_feature_level == std::get<0>(feature_level_names[i])) + { + ret = std::get<1>(feature_level_names[i]); + break; + } + } + + ret += "\n"; + + DXGI_ADAPTER_DESC desc; + if (m_adapter && SUCCEEDED(m_adapter->GetDesc(&desc))) + { + ret += StringUtil::StdStringFromFormat("VID: 0x%04X PID: 0x%04X\n", desc.VendorId, desc.DeviceId); + ret += StringUtil::WideStringToUTF8String(desc.Description); + ret += "\n"; + + const std::string driver_version(D3D::GetDriverVersionFromLUID(desc.AdapterLuid)); + if (!driver_version.empty()) + { + ret += "Driver Version: "; + ret += driver_version; + } + } + + return ret; +} +#endif + +void D3D12Device::SetVSync(bool enabled) +{ + m_vsync_enabled = enabled; +} + +bool D3D12Device::BeginPresent(bool frame_skip) +{ + if (InRenderPass()) + EndRenderPass(); + + if (frame_skip) + return false; + + // If we're running surfaceless, kick the command buffer so we don't run out of descriptors. + if (!m_swap_chain) + { + SubmitCommandList(false); + return false; + } + + // TODO: Check if the device was lost. + + // Check if we lost exclusive fullscreen. If so, notify the host, so it can switch to windowed mode. + // This might get called repeatedly if it takes a while to switch back, that's the host's problem. + BOOL is_fullscreen; + if (m_is_exclusive_fullscreen && + (FAILED(m_swap_chain->GetFullscreenState(&is_fullscreen, nullptr)) || !is_fullscreen)) + { + Host::RunOnCPUThread([]() { Host::SetFullscreen(false); }); + return false; + } + + BeginSwapChainRenderPass(); + return true; +} + +void D3D12Device::EndPresent() +{ + DebugAssert(InRenderPass() && !m_current_framebuffer); + EndRenderPass(); + + const auto& swap_chain_buf = m_swap_chain_buffers[m_current_swap_chain_buffer]; + m_current_swap_chain_buffer = ((m_current_swap_chain_buffer + 1) % static_cast(m_swap_chain_buffers.size())); + + ID3D12GraphicsCommandList* cmdlist = GetCommandList(); + D3D12Texture::TransitionSubresourceToState(cmdlist, swap_chain_buf.first.Get(), 0, D3D12_RESOURCE_STATE_RENDER_TARGET, + D3D12_RESOURCE_STATE_PRESENT); + + SubmitCommandList(false); + + if (!m_vsync_enabled && m_using_allow_tearing) + m_swap_chain->Present(0, DXGI_PRESENT_ALLOW_TEARING); + else + m_swap_chain->Present(static_cast(m_vsync_enabled), 0); +} + +#ifdef _DEBUG +static UINT64 Palette(float phase, const std::array& a, const std::array& b, + const std::array& c, const std::array& d) +{ + std::array result; + result[0] = a[0] + b[0] * std::cos(6.28318f * (c[0] * phase + d[0])); + result[1] = a[1] + b[1] * std::cos(6.28318f * (c[1] * phase + d[1])); + result[2] = a[2] + b[2] * std::cos(6.28318f * (c[2] * phase + d[2])); + + return PIX_COLOR(static_cast(result[0]), static_cast(result[1]), static_cast(result[2])); +} +#endif + +void D3D12Device::PushDebugGroup(const char* fmt, ...) +{ +#ifdef _DEBUG + if (!m_debug_device) + return; + + std::va_list ap; + va_start(ap, fmt); + const std::string buf(StringUtil::StdStringFromFormatV(fmt, ap)); + va_end(ap); + + const UINT64 color = Palette(static_cast(++s_debug_scope_depth), {0.5f, 0.5f, 0.5f}, {0.5f, 0.5f, 0.5f}, + {1.0f, 1.0f, 0.5f}, {0.8f, 0.90f, 0.30f}); + PIXBeginEvent(GetCommandList(), color, "%s", buf.c_str()); +#endif +} + +void D3D12Device::PopDebugGroup() +{ +#ifdef _DEBUG + if (!m_debug_device) + return; + + s_debug_scope_depth = (s_debug_scope_depth == 0) ? 0 : (s_debug_scope_depth - 1u); + PIXEndEvent(GetCommandList()); +#endif +} + +void D3D12Device::InsertDebugMessage(const char* fmt, ...) +{ +#ifdef _DEBUG + if (!m_debug_device) + return; + + std::va_list ap; + va_start(ap, fmt); + const std::string buf(StringUtil::StdStringFromFormatV(fmt, ap)); + va_end(ap); + + if (buf.empty()) + return; + + PIXSetMarker(GetCommandList(), PIX_COLOR(0, 0, 0), "%s", buf.c_str()); +#endif +} + +void D3D12Device::SetFeatures() +{ + m_max_texture_size = D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION; + m_max_multisamples = 1; + for (u32 multisamples = 2; multisamples < D3D12_MAX_MULTISAMPLE_SAMPLE_COUNT; multisamples++) + { + D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS fd = {DXGI_FORMAT_R8G8B8A8_UNORM, static_cast(multisamples)}; + + if (SUCCEEDED(m_device->CheckFeatureSupport(D3D12_FEATURE_MULTISAMPLE_QUALITY_LEVELS, &fd, sizeof(fd))) && + fd.NumQualityLevels > 0) + { + m_max_multisamples = multisamples; + } + } + + m_features.dual_source_blend = true; + m_features.noperspective_interpolation = true; + m_features.per_sample_shading = true; + m_features.supports_texture_buffers = true; + m_features.texture_buffers_emulated_with_ssbo = false; + m_features.gpu_timing = true; + + BOOL allow_tearing_supported = false; + HRESULT hr = m_dxgi_factory->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &allow_tearing_supported, + sizeof(allow_tearing_supported)); + m_allow_tearing_supported = (SUCCEEDED(hr) && allow_tearing_supported == TRUE); +} + +void D3D12Device::CopyTextureRegion(GPUTexture* dst, u32 dst_x, u32 dst_y, u32 dst_layer, u32 dst_level, + GPUTexture* src, u32 src_x, u32 src_y, u32 src_layer, u32 src_level, u32 width, + u32 height) +{ + D3D12Texture* const S = static_cast(src); + D3D12Texture* const D = static_cast(dst); + + if (S->GetState() == GPUTexture::State::Cleared) + { + // source is cleared. if destination is a render target, we can carry the clear forward + if (D->IsRenderTargetOrDepthStencil()) + { + if (dst_level == 0 && dst_x == 0 && dst_y == 0 && width == D->GetWidth() && height == D->GetHeight()) + { + // pass it forward if we're clearing the whole thing + if (S->IsDepthStencil()) + D->SetClearDepth(S->GetClearDepth()); + else + D->SetClearColor(S->GetClearColor()); + + return; + } + + if (D->GetState() == GPUTexture::State::Cleared) + { + // destination is cleared, if it's the same colour and rect, we can just avoid this entirely + if (D->IsDepthStencil()) + { + if (D->GetClearDepth() == S->GetClearDepth()) + return; + } + else + { + if (D->GetClearColor() == S->GetClearColor()) + return; + } + } + + // TODO: Could use attachment clear here.. + } + + // commit the clear to the source first, then do normal copy + S->CommitClear(); + } + + // if the destination has been cleared, and we're not overwriting the whole thing, commit the clear first + // (the area outside of where we're copying to) + if (D->GetState() == GPUTexture::State::Cleared && + (dst_level != 0 || dst_x != 0 || dst_y != 0 || width != D->GetWidth() || height != D->GetHeight())) + { + D->CommitClear(); + } + + // *now* we can do a normal image copy. + if (InRenderPass()) + EndRenderPass(); + + S->TransitionToState(D3D12_RESOURCE_STATE_COPY_SOURCE); + S->SetUseFenceValue(GetCurrentFenceValue()); + + D->TransitionToState(D3D12_RESOURCE_STATE_COPY_DEST); + D->SetUseFenceValue(GetCurrentFenceValue()); + + D3D12_TEXTURE_COPY_LOCATION srcloc; + srcloc.pResource = S->GetResource(); + srcloc.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; + srcloc.SubresourceIndex = S->CalculateSubresource(src_layer, src_level); + + D3D12_TEXTURE_COPY_LOCATION dstloc; + dstloc.pResource = D->GetResource(); + dstloc.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; + dstloc.SubresourceIndex = D->CalculateSubresource(dst_layer, dst_level); + + const D3D12_BOX srcbox{static_cast(src_x), static_cast(src_y), 0u, + static_cast(src_x + width), static_cast(src_y + height), 1u}; + GetCommandList()->CopyTextureRegion(&dstloc, dst_x, dst_y, 0, &srcloc, &srcbox); + + D->SetState(GPUTexture::State::Dirty); +} + +void D3D12Device::ResolveTextureRegion(GPUTexture* dst, u32 dst_x, u32 dst_y, u32 dst_layer, u32 dst_level, + GPUTexture* src, u32 src_x, u32 src_y, u32 src_layer, u32 src_level, u32 width, + u32 height) +{ + Panic("Fixme"); +} + +void D3D12Device::ClearRenderTarget(GPUTexture* t, u32 c) +{ + GPUDevice::ClearRenderTarget(t, c); + if (InRenderPass() && m_current_framebuffer && m_current_framebuffer->GetRT() == t) + EndRenderPass(); +} + +void D3D12Device::ClearDepth(GPUTexture* t, float d) +{ + GPUDevice::ClearDepth(t, d); + if (InRenderPass() && m_current_framebuffer && m_current_framebuffer->GetDS() == t) + EndRenderPass(); +} + +void D3D12Device::InvalidateRenderTarget(GPUTexture* t) +{ + GPUDevice::InvalidateRenderTarget(t); + if (InRenderPass() && m_current_framebuffer && + (m_current_framebuffer->GetRT() == t || m_current_framebuffer->GetDS() == t)) + { + EndRenderPass(); + } +} + +bool D3D12Device::CreateBuffers() +{ + if (!m_vertex_buffer.Create(VERTEX_BUFFER_SIZE)) + { + Log_ErrorPrint("Failed to allocate vertex buffer"); + return false; + } + + if (!m_index_buffer.Create(INDEX_BUFFER_SIZE)) + { + Log_ErrorPrint("Failed to allocate index buffer"); + return false; + } + + if (!m_uniform_buffer.Create(VERTEX_UNIFORM_BUFFER_SIZE)) + { + Log_ErrorPrint("Failed to allocate uniform buffer"); + return false; + } + + if (!m_texture_upload_buffer.Create(TEXTURE_BUFFER_SIZE)) + { + Log_ErrorPrint("Failed to allocate texture upload buffer"); + return false; + } + + return true; +} + +void D3D12Device::DestroyBuffers() +{ + m_texture_upload_buffer.Destroy(false); + m_uniform_buffer.Destroy(false); + m_index_buffer.Destroy(false); + m_vertex_buffer.Destroy(false); +} + +void D3D12Device::MapVertexBuffer(u32 vertex_size, u32 vertex_count, void** map_ptr, u32* map_space, + u32* map_base_vertex) +{ + const u32 req_size = vertex_size * vertex_count; + if (!m_vertex_buffer.ReserveMemory(req_size, vertex_size)) + { + SubmitCommandListAndRestartRenderPass("out of vertex space"); + if (!m_vertex_buffer.ReserveMemory(req_size, vertex_size)) + Panic("Failed to allocate vertex space"); + } + + *map_ptr = m_vertex_buffer.GetCurrentHostPointer(); + *map_space = m_vertex_buffer.GetCurrentSpace() / vertex_size; + *map_base_vertex = m_vertex_buffer.GetCurrentOffset() / vertex_size; +} + +void D3D12Device::UnmapVertexBuffer(u32 vertex_size, u32 vertex_count) +{ + m_vertex_buffer.CommitMemory(vertex_size * vertex_count); +} + +void D3D12Device::MapIndexBuffer(u32 index_count, DrawIndex** map_ptr, u32* map_space, u32* map_base_index) +{ + const u32 req_size = sizeof(DrawIndex) * index_count; + if (!m_index_buffer.ReserveMemory(req_size, sizeof(DrawIndex))) + { + SubmitCommandListAndRestartRenderPass("out of index space"); + if (!m_index_buffer.ReserveMemory(req_size, sizeof(DrawIndex))) + Panic("Failed to allocate index space"); + } + + *map_ptr = reinterpret_cast(m_index_buffer.GetCurrentHostPointer()); + *map_space = m_index_buffer.GetCurrentSpace() / sizeof(DrawIndex); + *map_base_index = m_index_buffer.GetCurrentOffset() / sizeof(DrawIndex); +} + +void D3D12Device::UnmapIndexBuffer(u32 used_index_count) +{ + m_index_buffer.CommitMemory(sizeof(DrawIndex) * used_index_count); +} + +void D3D12Device::PushUniformBuffer(const void* data, u32 data_size) +{ + static constexpr std::array(GPUPipeline::Layout::MaxCount)> push_parameter = { + 0, // SingleTextureAndUBO + 2, // SingleTextureAndPushConstants + 1, // SingleTextureBufferAndPushConstants + 0, // MultiTextureAndUBO + }; + + DebugAssert(data_size < UNIFORM_PUSH_CONSTANTS_SIZE); + if (m_dirty_flags & DIRTY_FLAG_PIPELINE_LAYOUT) + { + m_dirty_flags &= ~DIRTY_FLAG_PIPELINE_LAYOUT; + UpdateRootSignature(); + } + + GetCommandList()->SetGraphicsRoot32BitConstants(push_parameter[static_cast(m_current_pipeline_layout)], + data_size / 4u, data, 0); +} + +void* D3D12Device::MapUniformBuffer(u32 size) +{ + const u32 used_space = Common::AlignUpPow2(size, D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT); + if (!m_uniform_buffer.ReserveMemory(used_space + MAX_UNIFORM_BUFFER_SIZE, + D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT)) + { + SubmitCommandListAndRestartRenderPass("out of uniform space"); + if (!m_uniform_buffer.ReserveMemory(used_space + MAX_UNIFORM_BUFFER_SIZE, + D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT)) + Panic("Failed to allocate uniform space."); + } + + return m_uniform_buffer.GetCurrentHostPointer(); +} + +void D3D12Device::UnmapUniformBuffer(u32 size) +{ + m_uniform_buffer_position = m_uniform_buffer.GetCurrentOffset(); + m_uniform_buffer.CommitMemory(size); + m_dirty_flags |= DIRTY_FLAG_CONSTANT_BUFFER; +} + +bool D3D12Device::CreateRootSignatures() +{ + D3D12::RootSignatureBuilder rsb; + + { + auto& rs = m_root_signatures[static_cast(GPUPipeline::Layout::SingleTextureAndUBO)]; + + rsb.SetInputAssemblerFlag(); + rsb.AddDescriptorTable(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 0, 1, D3D12_SHADER_VISIBILITY_PIXEL); + rsb.AddDescriptorTable(D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER, 0, 1, D3D12_SHADER_VISIBILITY_PIXEL); + rsb.AddCBVParameter(0, D3D12_SHADER_VISIBILITY_ALL); + if (!(rs = rsb.Create())) + return false; + D3D12::SetObjectName(rs.Get(), "Single Texture + UBO Pipeline Layout"); + } + + { + auto& rs = m_root_signatures[static_cast(GPUPipeline::Layout::SingleTextureAndPushConstants)]; + + rsb.SetInputAssemblerFlag(); + rsb.AddDescriptorTable(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 0, 1, D3D12_SHADER_VISIBILITY_PIXEL); + rsb.AddDescriptorTable(D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER, 0, 1, D3D12_SHADER_VISIBILITY_PIXEL); + rsb.Add32BitConstants(0, UNIFORM_PUSH_CONSTANTS_SIZE / sizeof(u32), D3D12_SHADER_VISIBILITY_ALL); + if (!(rs = rsb.Create())) + return false; + D3D12::SetObjectName(rs.Get(), "Single Texture Pipeline Layout"); + } + + { + auto& rs = m_root_signatures[static_cast(GPUPipeline::Layout::SingleTextureBufferAndPushConstants)]; + + rsb.SetInputAssemblerFlag(); + rsb.AddDescriptorTable(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 0, 1, D3D12_SHADER_VISIBILITY_PIXEL); + rsb.Add32BitConstants(0, UNIFORM_PUSH_CONSTANTS_SIZE / sizeof(u32), D3D12_SHADER_VISIBILITY_ALL); + if (!(rs = rsb.Create())) + return false; + D3D12::SetObjectName(rs.Get(), "Single Texture Buffer + UBO Pipeline Layout"); + } + + { + auto& rs = m_root_signatures[static_cast(GPUPipeline::Layout::MultiTextureAndUBO)]; + + rsb.SetInputAssemblerFlag(); + rsb.AddDescriptorTable(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 0, MAX_TEXTURE_SAMPLERS, D3D12_SHADER_VISIBILITY_PIXEL); + rsb.AddDescriptorTable(D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER, 0, MAX_TEXTURE_SAMPLERS, D3D12_SHADER_VISIBILITY_PIXEL); + rsb.AddCBVParameter(0, D3D12_SHADER_VISIBILITY_ALL); + if (!(rs = rsb.Create())) + return false; + D3D12::SetObjectName(rs.Get(), "Multi Texture Pipeline Layout"); + } + + return true; +} + +void D3D12Device::DestroyRootSignatures() +{ + for (auto it = m_root_signatures.rbegin(); it != m_root_signatures.rend(); ++it) + it->Reset(); +} + +void D3D12Device::SetFramebuffer(GPUFramebuffer* fb) +{ + if (m_current_framebuffer == fb) + return; + + if (InRenderPass()) + EndRenderPass(); + + m_current_framebuffer = static_cast(fb); +} + +void D3D12Device::BeginRenderPass() +{ + DebugAssert(!InRenderPass()); + + D3D12_RENDER_PASS_RENDER_TARGET_DESC rt_desc; + D3D12_RENDER_PASS_DEPTH_STENCIL_DESC ds_desc; + const D3D12_RENDER_PASS_RENDER_TARGET_DESC* rt_desc_p = nullptr; + const D3D12_RENDER_PASS_DEPTH_STENCIL_DESC* ds_desc_p = nullptr; + + ID3D12GraphicsCommandList4* cmdlist = GetCommandList(); + + if (LIKELY(m_current_framebuffer)) + { + D3D12Texture* rt = static_cast(m_current_framebuffer->GetRT()); + if (rt) + { + rt->TransitionToState(cmdlist, D3D12_RESOURCE_STATE_RENDER_TARGET); + rt->SetUseFenceValue(GetCurrentFenceValue()); + rt_desc_p = &rt_desc; + rt_desc.cpuDescriptor = rt->GetWriteDescriptor(); + rt_desc.EndingAccess.Type = D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE; + + switch (rt->GetState()) + { + case GPUTexture::State::Cleared: + { + rt_desc.BeginningAccess.Type = D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_CLEAR; + std::memcpy(rt_desc.BeginningAccess.Clear.ClearValue.Color, rt->GetUNormClearColor().data(), + sizeof(rt_desc.BeginningAccess.Clear.ClearValue.Color)); + rt->SetState(GPUTexture::State::Dirty); + } + break; + + case GPUTexture::State::Invalidated: + { + rt_desc.BeginningAccess.Type = D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_DISCARD; + rt->SetState(GPUTexture::State::Dirty); + } + break; + + case GPUTexture::State::Dirty: + { + rt_desc.BeginningAccess.Type = D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_PRESERVE; + } + break; + + default: + UnreachableCode(); + break; + } + } + + D3D12Texture* ds = static_cast(m_current_framebuffer->GetDS()); + if (ds) + { + ds->TransitionToState(cmdlist, D3D12_RESOURCE_STATE_DEPTH_WRITE); + ds->SetUseFenceValue(GetCurrentFenceValue()); + ds_desc_p = &ds_desc; + ds_desc.cpuDescriptor = ds->GetWriteDescriptor(); + ds_desc.DepthEndingAccess.Type = D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE; + ds_desc.StencilBeginningAccess = {}; + ds_desc.StencilEndingAccess = {}; + + switch (ds->GetState()) + { + case GPUTexture::State::Cleared: + { + ds_desc.DepthBeginningAccess.Type = D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_CLEAR; + ds_desc.DepthBeginningAccess.Clear.ClearValue.DepthStencil.Depth = ds->GetClearDepth(); + ds->SetState(GPUTexture::State::Dirty); + } + break; + + case GPUTexture::State::Invalidated: + { + ds_desc.DepthBeginningAccess.Type = D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_DISCARD; + ds->SetState(GPUTexture::State::Dirty); + } + break; + + case GPUTexture::State::Dirty: + { + ds_desc.DepthBeginningAccess.Type = D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_PRESERVE; + } + break; + + default: + UnreachableCode(); + break; + } + } + } + else + { + // Re-rendering to swap chain. + const auto& swap_chain_buf = m_swap_chain_buffers[m_current_swap_chain_buffer]; + rt_desc = {swap_chain_buf.second, + {D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_PRESERVE}, + {D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE}}; + rt_desc_p = &rt_desc; + } + + // All textures should be in shader read only optimal already, but just in case.. + const u32 num_textures = GetActiveTexturesForLayout(m_current_pipeline_layout); + for (u32 i = 0; i < num_textures; i++) + { + if (m_current_textures[i]) + m_current_textures[i]->TransitionToState(cmdlist, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); + } + + DebugAssert(rt_desc_p || ds_desc_p); + cmdlist->BeginRenderPass(rt_desc_p ? 1 : 0, rt_desc_p, ds_desc_p, D3D12_RENDER_PASS_FLAG_NONE); + + // TODO: Stats + m_in_render_pass = true; + + // If this is a new command buffer, bind the pipeline and such. + if (m_dirty_flags & DIRTY_FLAG_INITIAL) + SetInitialPipelineState(); +} + +void D3D12Device::BeginSwapChainRenderPass() +{ + DebugAssert(!InRenderPass()); + + ID3D12GraphicsCommandList4* const cmdlist = GetCommandList(); + const auto& swap_chain_buf = m_swap_chain_buffers[m_current_swap_chain_buffer]; + + D3D12Texture::TransitionSubresourceToState(cmdlist, swap_chain_buf.first.Get(), 0, D3D12_RESOURCE_STATE_COMMON, + D3D12_RESOURCE_STATE_RENDER_TARGET); + + // All textures should be in shader read only optimal already, but just in case.. + const u32 num_textures = GetActiveTexturesForLayout(m_current_pipeline_layout); + for (u32 i = 0; i < num_textures; i++) + { + if (m_current_textures[i]) + m_current_textures[i]->TransitionToState(cmdlist, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); + } + + const D3D12_RENDER_PASS_RENDER_TARGET_DESC rt_desc = { + swap_chain_buf.second, + {D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_CLEAR, {s_present_clear_color}}, + {D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE}}; + cmdlist->BeginRenderPass(1, &rt_desc, nullptr, D3D12_RENDER_PASS_FLAG_NONE); + + m_current_framebuffer = nullptr; + m_in_render_pass = true; + + // Clear pipeline, it's likely incompatible. + m_current_pipeline = nullptr; +} + +bool D3D12Device::InRenderPass() +{ + return m_in_render_pass; +} + +void D3D12Device::EndRenderPass() +{ + DebugAssert(m_in_render_pass); + + // TODO: stats + m_in_render_pass = false; + + GetCommandList()->EndRenderPass(); +} + +void D3D12Device::UnbindFramebuffer(D3D12Framebuffer* fb) +{ + if (m_current_framebuffer != fb) + return; + + if (InRenderPass()) + EndRenderPass(); + m_current_framebuffer = nullptr; +} + +void D3D12Device::UnbindFramebuffer(D3D12Texture* tex) +{ + if (!m_current_framebuffer) + return; + + if (m_current_framebuffer->GetRT() != tex && m_current_framebuffer->GetDS() != tex) + return; + + if (InRenderPass()) + EndRenderPass(); + m_current_framebuffer = nullptr; +} + +void D3D12Device::SetPipeline(GPUPipeline* pipeline) +{ + // First draw? Bind everything. + if (m_dirty_flags & DIRTY_FLAG_INITIAL) + { + m_current_pipeline = static_cast(pipeline); + if (!m_current_pipeline) + return; + + SetInitialPipelineState(); + return; + } + else if (m_current_pipeline == pipeline) + { + return; + } + + m_current_pipeline = static_cast(pipeline); + + ID3D12GraphicsCommandList4* cmdlist = GetCommandList(); + cmdlist->SetPipelineState(m_current_pipeline->GetPipeline()); + + if (D3D12_PRIMITIVE_TOPOLOGY topology = m_current_pipeline->GetTopology(); topology != m_current_topology) + { + m_current_topology = topology; + cmdlist->IASetPrimitiveTopology(topology); + } + + if (u32 vertex_stride = m_current_pipeline->GetVertexStride(); + vertex_stride > 0 && m_current_vertex_stride != vertex_stride) + { + m_current_vertex_stride = vertex_stride; + SetVertexBuffer(cmdlist); + } + + // TODO: we don't need to change the blend constant if blending isn't on. + if (u32 blend_constants = m_current_pipeline->GetBlendConstants(); m_current_blend_constant != blend_constants) + { + m_current_blend_constant = blend_constants; + cmdlist->OMSetBlendFactor(m_current_pipeline->GetBlendConstantsF().data()); + } + + if (GPUPipeline::Layout layout = m_current_pipeline->GetLayout(); m_current_pipeline_layout != layout) + { + m_current_pipeline_layout = layout; + m_dirty_flags |= + DIRTY_FLAG_PIPELINE_LAYOUT | DIRTY_FLAG_CONSTANT_BUFFER | DIRTY_FLAG_TEXTURES | DIRTY_FLAG_SAMPLERS; + } +} + +void D3D12Device::UnbindPipeline(D3D12Pipeline* pl) +{ + if (m_current_pipeline != pl) + return; + + m_current_pipeline = nullptr; +} + +void D3D12Device::InvalidateCachedState() +{ + m_dirty_flags = ALL_DIRTY_STATE; + m_in_render_pass = false; + m_current_framebuffer = nullptr; + m_current_pipeline = nullptr; + m_current_vertex_stride = 0; + m_current_blend_constant = 0; + m_current_topology = D3D_PRIMITIVE_TOPOLOGY_UNDEFINED; +} + +void D3D12Device::SetInitialPipelineState() +{ + DebugAssert(m_current_pipeline); + m_dirty_flags &= ~DIRTY_FLAG_INITIAL; + + ID3D12GraphicsCommandList4* cmdlist = GetCommandList(); + + m_current_vertex_stride = m_current_pipeline->GetVertexStride(); + SetVertexBuffer(cmdlist); + const D3D12_INDEX_BUFFER_VIEW ib_view = {m_index_buffer.GetGPUPointer(), m_index_buffer.GetSize(), + DXGI_FORMAT_R16_UINT}; + cmdlist->IASetIndexBuffer(&ib_view); + + cmdlist->SetPipelineState(m_current_pipeline->GetPipeline()); + m_current_pipeline_layout = m_current_pipeline->GetLayout(); + + m_current_topology = m_current_pipeline->GetTopology(); + cmdlist->IASetPrimitiveTopology(m_current_topology); + + m_current_blend_constant = m_current_pipeline->GetBlendConstants(); + cmdlist->OMSetBlendFactor(m_current_pipeline->GetBlendConstantsF().data()); + + SetViewport(cmdlist); + SetScissor(cmdlist); +} + +void D3D12Device::SetVertexBuffer(ID3D12GraphicsCommandList4* cmdlist) +{ + const D3D12_VERTEX_BUFFER_VIEW vb_view = {m_vertex_buffer.GetGPUPointer(), m_vertex_buffer.GetSize(), + m_current_vertex_stride}; + cmdlist->IASetVertexBuffers(0, 1, &vb_view); +} + +void D3D12Device::SetViewport(ID3D12GraphicsCommandList4* cmdlist) +{ + const D3D12_VIEWPORT vp = {static_cast(m_current_viewport.left), + static_cast(m_current_viewport.top), + static_cast(m_current_viewport.GetWidth()), + static_cast(m_current_viewport.GetHeight()), + 0.0f, + 1.0f}; + cmdlist->RSSetViewports(1, &vp); +} + +void D3D12Device::SetScissor(ID3D12GraphicsCommandList4* cmdlist) +{ + const D3D12_RECT rc = {static_cast(m_current_scissor.left), static_cast(m_current_scissor.top), + static_cast(m_current_scissor.right), static_cast(m_current_scissor.bottom)}; + cmdlist->RSSetScissorRects(1, &rc); +} + +void D3D12Device::SetTextureSampler(u32 slot, GPUTexture* texture, GPUSampler* sampler) +{ + D3D12DescriptorHandle null_handle; + + D3D12Texture* T = static_cast(texture); + if (m_current_textures[slot] != T) + { + m_current_textures[slot] = T; + m_dirty_flags |= DIRTY_FLAG_TEXTURES; + + if (T) + { + T->SetUseFenceValue(GetCurrentFenceValue()); + if (T->GetResourceState() != D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE) + { + if (InRenderPass()) + EndRenderPass(); + T->TransitionToState(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); + } + } + } + + const D3D12DescriptorHandle& handle = sampler ? static_cast(sampler)->GetDescriptor() : m_point_sampler; + if (m_current_samplers[slot] != handle) + { + m_current_samplers[slot] = handle; + m_dirty_flags |= DIRTY_FLAG_SAMPLERS; + } +} + +void D3D12Device::SetTextureBuffer(u32 slot, GPUTextureBuffer* buffer) +{ + DebugAssert(slot == 0); + if (m_current_texture_buffer == buffer) + return; + + m_current_texture_buffer = static_cast(buffer); + if (m_current_pipeline_layout == GPUPipeline::Layout::SingleTextureBufferAndPushConstants) + m_dirty_flags |= DIRTY_FLAG_TEXTURES; +} + +void D3D12Device::UnbindTexture(D3D12Texture* tex) +{ + for (u32 i = 0; i < MAX_TEXTURE_SAMPLERS; i++) + { + if (m_current_textures[i] == tex) + { + m_current_textures[i] = nullptr; + m_dirty_flags |= DIRTY_FLAG_TEXTURES; + } + } +} + +void D3D12Device::UnbindTextureBuffer(D3D12TextureBuffer* buf) +{ + if (m_current_texture_buffer != buf) + return; + + m_current_texture_buffer = nullptr; + + if (m_current_pipeline_layout == GPUPipeline::Layout::SingleTextureBufferAndPushConstants) + m_dirty_flags |= DIRTY_FLAG_TEXTURES; +} + +void D3D12Device::SetViewport(s32 x, s32 y, s32 width, s32 height) +{ + const Common::Rectangle rc = Common::Rectangle::FromExtents(x, y, width, height); + if (m_current_viewport == rc) + return; + + m_current_viewport = rc; + + if (m_dirty_flags & DIRTY_FLAG_INITIAL) + return; + + SetViewport(GetCommandList()); +} + +void D3D12Device::SetScissor(s32 x, s32 y, s32 width, s32 height) +{ + const Common::Rectangle rc = Common::Rectangle::FromExtents(x, y, width, height); + if (m_current_scissor == rc) + return; + + m_current_scissor = rc; + + if (m_dirty_flags & DIRTY_FLAG_INITIAL) + return; + + SetScissor(GetCommandList()); +} + +void D3D12Device::PreDrawCheck() +{ + // TODO: Flushing cmdbuffer because of descriptor OOM will lose push constants. + + DebugAssert(!(m_dirty_flags & DIRTY_FLAG_INITIAL)); + const u32 dirty = std::exchange(m_dirty_flags, 0); + if (dirty != 0) + { + if (dirty & DIRTY_FLAG_PIPELINE_LAYOUT) + { + UpdateRootSignature(); + if (!UpdateRootParameters(dirty)) + Panic("TODO handle failure case"); + } + else if (dirty & DIRTY_FLAG_CONSTANT_BUFFER | DIRTY_FLAG_TEXTURES | DIRTY_FLAG_SAMPLERS) + { + if (!UpdateRootParameters(dirty)) + Panic("TODO handle failure case"); + } + } + + if (!InRenderPass()) + BeginRenderPass(); +} + +void D3D12Device::UpdateRootSignature() +{ + GetCommandList()->SetGraphicsRootSignature(m_root_signatures[static_cast(m_current_pipeline_layout)].Get()); +} + +template +bool D3D12Device::UpdateParametersForLayout(u32 dirty) +{ + ID3D12GraphicsCommandList4* cmdlist = GetCommandList(); + + if constexpr (layout == GPUPipeline::Layout::SingleTextureAndUBO || layout == GPUPipeline::Layout::MultiTextureAndUBO) + { + if (dirty & DIRTY_FLAG_CONSTANT_BUFFER) + cmdlist->SetGraphicsRootConstantBufferView(2, m_uniform_buffer.GetGPUPointer() + m_uniform_buffer_position); + } + + constexpr u32 num_textures = GetActiveTexturesForLayout(layout); + if (dirty & DIRTY_FLAG_TEXTURES && num_textures > 0) + { + D3D12DescriptorAllocator& allocator = m_command_lists[m_current_command_list].descriptor_allocator; + D3D12DescriptorHandle gpu_handle; + if (!allocator.Allocate(num_textures, &gpu_handle)) + return false; + + if constexpr (num_textures == 1) + { + m_device->CopyDescriptorsSimple( + 1, gpu_handle, m_current_textures[0] ? m_current_textures[0]->GetSRVDescriptor() : m_null_srv_descriptor, + D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); + } + else + { + D3D12_CPU_DESCRIPTOR_HANDLE src_handles[MAX_TEXTURE_SAMPLERS]; + UINT src_sizes[MAX_TEXTURE_SAMPLERS]; + for (u32 i = 0; i < num_textures; i++) + { + src_handles[i] = m_current_textures[i] ? m_current_textures[i]->GetSRVDescriptor() : m_null_srv_descriptor; + src_sizes[i] = 1; + } + m_device->CopyDescriptors(1, &gpu_handle.cpu_handle, &num_textures, num_textures, src_handles, src_sizes, + D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); + } + + cmdlist->SetGraphicsRootDescriptorTable(0, gpu_handle); + } + + if (dirty & DIRTY_FLAG_SAMPLERS && num_textures > 0) + { + auto& allocator = m_command_lists[m_current_command_list].sampler_allocator; + D3D12DescriptorHandle gpu_handle; + if constexpr (num_textures == 1) + { + if (!allocator.LookupSingle(m_device.Get(), &gpu_handle, m_current_samplers[0])) + return false; + } + else + { + if (!allocator.LookupGroup(m_device.Get(), &gpu_handle, m_current_samplers.data())) + return false; + } + + cmdlist->SetGraphicsRootDescriptorTable(1, gpu_handle); + } + + if (dirty & DIRTY_FLAG_TEXTURES && layout == GPUPipeline::Layout::SingleTextureBufferAndPushConstants) + { + D3D12DescriptorAllocator& allocator = m_command_lists[m_current_command_list].descriptor_allocator; + D3D12DescriptorHandle gpu_handle; + if (!allocator.Allocate(1, &gpu_handle)) + return false; + + m_device->CopyDescriptorsSimple( + 1, gpu_handle, m_current_texture_buffer ? m_current_texture_buffer->GetDescriptor() : m_null_srv_descriptor, + D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); + cmdlist->SetGraphicsRootDescriptorTable(0, gpu_handle); + } + + return true; +} + +bool D3D12Device::UpdateRootParameters(u32 dirty) +{ + switch (m_current_pipeline_layout) + { + case GPUPipeline::Layout::SingleTextureAndUBO: + return UpdateParametersForLayout(dirty); + + case GPUPipeline::Layout::SingleTextureAndPushConstants: + return UpdateParametersForLayout(dirty); + + case GPUPipeline::Layout::SingleTextureBufferAndPushConstants: + return UpdateParametersForLayout(dirty); + + case GPUPipeline::Layout::MultiTextureAndUBO: + return UpdateParametersForLayout(dirty); + + default: + UnreachableCode(); + return false; + } +} + +void D3D12Device::Draw(u32 vertex_count, u32 base_vertex) +{ + PreDrawCheck(); + GetCommandList()->DrawInstanced(vertex_count, 1, base_vertex, 0); +} + +void D3D12Device::DrawIndexed(u32 index_count, u32 base_index, u32 base_vertex) +{ + PreDrawCheck(); + GetCommandList()->DrawIndexedInstanced(index_count, 1, base_index, base_vertex, 0); +} diff --git a/src/core/gpu/d3d12_device.h b/src/core/gpu/d3d12_device.h new file mode 100644 index 000000000..e0488ee29 --- /dev/null +++ b/src/core/gpu/d3d12_device.h @@ -0,0 +1,343 @@ +// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin +// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) + +#pragma once + +#include "d3d12_descriptor_heap_manager.h" +#include "d3d12_stream_buffer.h" +#include "gpu_device.h" +#include "gpu_texture.h" + +#include "common/windows_headers.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class D3D12Framebuffer; +class D3D12Pipeline; +class D3D12SwapChain; +class D3D12Texture; +class D3D12TextureBuffer; + +namespace D3D12MA { +class Allocator; +} + +class D3D12Device final : public GPUDevice +{ +public: + friend D3D12Texture; + + template + using ComPtr = Microsoft::WRL::ComPtr; + + enum : u32 + { + NUM_COMMAND_LISTS = 3, + + /// Start/End timestamp queries. + NUM_TIMESTAMP_QUERIES_PER_CMDLIST = 2, + }; + + static void LookupNativeFormat(GPUTexture::Format format, DXGI_FORMAT* d3d_format, DXGI_FORMAT* srv_format, + DXGI_FORMAT* rtv_format, DXGI_FORMAT* dsv_format); + +public: + D3D12Device(); + ~D3D12Device() override; + + RenderAPI GetRenderAPI() const override; + + bool HasSurface() const override; + + bool UpdateWindow() override; + void ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale) override; + + static AdapterAndModeList StaticGetAdapterAndModeList(); + AdapterAndModeList GetAdapterAndModeList() override; + void DestroySurface() override; + + std::string GetShaderCacheBaseName(const std::string_view& type) const override; + + std::unique_ptr CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples, + GPUTexture::Type type, GPUTexture::Format format, + const void* data = nullptr, u32 data_stride = 0, + bool dynamic = false) override; + std::unique_ptr CreateSampler(const GPUSampler::Config& config) override; + std::unique_ptr CreateTextureBuffer(GPUTextureBuffer::Format format, u32 size_in_elements) override; + + bool DownloadTexture(GPUTexture* texture, u32 x, u32 y, u32 width, u32 height, void* out_data, + u32 out_data_stride) override; + bool SupportsTextureFormat(GPUTexture::Format format) const override; + void CopyTextureRegion(GPUTexture* dst, u32 dst_x, u32 dst_y, u32 dst_layer, u32 dst_level, GPUTexture* src, + u32 src_x, u32 src_y, u32 src_layer, u32 src_level, u32 width, u32 height) override; + void ResolveTextureRegion(GPUTexture* dst, u32 dst_x, u32 dst_y, u32 dst_layer, u32 dst_level, GPUTexture* src, + u32 src_x, u32 src_y, u32 src_layer, u32 src_level, u32 width, u32 height) override; + void ClearRenderTarget(GPUTexture* t, u32 c) override; + void ClearDepth(GPUTexture* t, float d) override; + void InvalidateRenderTarget(GPUTexture* t) override; + + std::unique_ptr CreateFramebuffer(GPUTexture* rt_or_ds, GPUTexture* ds = nullptr) override; + + std::unique_ptr CreateShaderFromBinary(GPUShaderStage stage, gsl::span data) override; + std::unique_ptr CreateShaderFromSource(GPUShaderStage stage, const std::string_view& source, + std::vector* out_binary = nullptr) override; + std::unique_ptr CreatePipeline(const GPUPipeline::GraphicsConfig& config) override; + + void PushDebugGroup(const char* fmt, ...) override; + void PopDebugGroup() override; + void InsertDebugMessage(const char* fmt, ...) override; + + void MapVertexBuffer(u32 vertex_size, u32 vertex_count, void** map_ptr, u32* map_space, + u32* map_base_vertex) override; + void UnmapVertexBuffer(u32 vertex_size, u32 vertex_count) override; + void MapIndexBuffer(u32 index_count, DrawIndex** map_ptr, u32* map_space, u32* map_base_index) override; + void UnmapIndexBuffer(u32 used_index_count) override; + void PushUniformBuffer(const void* data, u32 data_size) override; + void* MapUniformBuffer(u32 size) override; + void UnmapUniformBuffer(u32 size) override; + void SetFramebuffer(GPUFramebuffer* fb) override; + void SetPipeline(GPUPipeline* pipeline) override; + void SetTextureSampler(u32 slot, GPUTexture* texture, GPUSampler* sampler) override; + void SetTextureBuffer(u32 slot, GPUTextureBuffer* buffer) override; + void SetViewport(s32 x, s32 y, s32 width, s32 height) override; + void SetScissor(s32 x, s32 y, s32 width, s32 height) override; + void Draw(u32 vertex_count, u32 base_vertex) override; + void DrawIndexed(u32 index_count, u32 base_index, u32 base_vertex) override; + + bool SetGPUTimingEnabled(bool enabled) override; + float GetAndResetAccumulatedGPUTime() override; + + void SetVSync(bool enabled) override; + + bool BeginPresent(bool skip_present) override; + void EndPresent() override; + + // Global state accessors + ALWAYS_INLINE static D3D12Device& GetInstance() { return *static_cast(g_gpu_device.get()); } + ALWAYS_INLINE IDXGIAdapter1* GetAdapter() const { return m_adapter.Get(); } + ALWAYS_INLINE ID3D12Device1* GetDevice() const { return m_device.Get(); } + ALWAYS_INLINE ID3D12CommandQueue* GetCommandQueue() const { return m_command_queue.Get(); } + ALWAYS_INLINE D3D12MA::Allocator* GetAllocator() const { return m_allocator.Get(); } + + void WaitForGPUIdle(); + + // Descriptor manager access. + D3D12DescriptorHeapManager& GetDescriptorHeapManager() { return m_descriptor_heap_manager; } + D3D12DescriptorHeapManager& GetRTVHeapManager() { return m_rtv_heap_manager; } + D3D12DescriptorHeapManager& GetDSVHeapManager() { return m_dsv_heap_manager; } + D3D12DescriptorHeapManager& GetSamplerHeapManager() { return m_sampler_heap_manager; } + const D3D12DescriptorHandle& GetNullSRVDescriptor() const { return m_null_srv_descriptor; } + + // These command buffers are allocated per-frame. They are valid until the command buffer + // is submitted, after that you should call these functions again. + ALWAYS_INLINE ID3D12GraphicsCommandList4* GetCommandList() const + { + return m_command_lists[m_current_command_list].command_lists[1].Get(); + } + ALWAYS_INLINE D3D12StreamBuffer& GetTextureUploadBuffer() { return m_texture_upload_buffer; } + ID3D12GraphicsCommandList4* GetInitCommandList(); + + // Root signature access. + ComPtr SerializeRootSignature(const D3D12_ROOT_SIGNATURE_DESC* desc); + ComPtr CreateRootSignature(const D3D12_ROOT_SIGNATURE_DESC* desc); + + /// Fence value for current command list. + u64 GetCurrentFenceValue() const { return m_current_fence_value; } + + /// Last "completed" fence. + u64 GetCompletedFenceValue() const { return m_completed_fence_value; } + + // Schedule a d3d12 resource for destruction later on. This will occur when the command buffer + // is next re-used, and the GPU has finished working with the specified resource. + void DeferObjectDestruction(ComPtr resource); + void DeferResourceDestruction(ComPtr allocation, ComPtr resource); + void DeferDescriptorDestruction(D3D12DescriptorHeapManager& heap, D3D12DescriptorHandle* descriptor); + + // Wait for a fence to be completed. + // Also invokes callbacks for completion. + void WaitForFence(u64 fence_counter); + + /// Ends any render pass, executes the command buffer, and invalidates cached state. + void SubmitCommandList(bool wait_for_completion); + void SubmitCommandList(bool wait_for_completion, const char* reason, ...); + void SubmitCommandListAndRestartRenderPass(const char* reason); + + void UnbindFramebuffer(D3D12Framebuffer* fb); + void UnbindFramebuffer(D3D12Texture* tex); + void UnbindPipeline(D3D12Pipeline* pl); + void UnbindTexture(D3D12Texture* tex); + void UnbindTextureBuffer(D3D12TextureBuffer* buf); + +protected: + bool CreateDevice(const std::string_view& adapter) override; + void DestroyDevice() override; + +private: + enum DIRTY_FLAG : u32 + { + DIRTY_FLAG_INITIAL = (1 << 0), + DIRTY_FLAG_PIPELINE_LAYOUT = (1 << 1), + DIRTY_FLAG_CONSTANT_BUFFER = (1 << 2), + DIRTY_FLAG_TEXTURES = (1 << 3), + DIRTY_FLAG_SAMPLERS = (1 << 3), + + ALL_DIRTY_STATE = DIRTY_FLAG_INITIAL | DIRTY_FLAG_PIPELINE_LAYOUT | DIRTY_FLAG_CONSTANT_BUFFER | + DIRTY_FLAG_TEXTURES | DIRTY_FLAG_SAMPLERS, + }; + + struct CommandList + { + // [0] - Init (upload) command buffer, [1] - draw command buffer + std::array, 2> command_allocators; + std::array, 2> command_lists; + D3D12DescriptorAllocator descriptor_allocator; + D3D12GroupedSamplerAllocator sampler_allocator; + u64 fence_counter = 0; + bool init_list_used = false; + bool needs_fence_wait = false; + bool has_timestamp_query = false; + }; + + using SamplerMap = std::unordered_map; + + static void GetAdapterAndModeList(AdapterAndModeList* ret, IDXGIFactory5* factory); + + void SetFeatures(); + + bool CreateSwapChain(); + bool CreateSwapChainRTV(); + void DestroySwapChainRTVs(); + void DestroySwapChain(); + + bool CreateCommandLists(); + void DestroyCommandLists(); + bool CreateRootSignatures(); + void DestroyRootSignatures(); + bool CreateBuffers(); + void DestroyBuffers(); + bool CreateDescriptorHeaps(); + void DestroyDescriptorHeaps(); + bool CreateTimestampQuery(); + void DestroyTimestampQuery(); + D3D12DescriptorHandle GetSampler(const GPUSampler::Config& config); + void DestroySamplers(); + void DestroyDeferredObjects(u64 fence_value); + + void RenderBlankFrame(); + void MoveToNextCommandList(); + + bool CreateSRVDescriptor(ID3D12Resource* resource, u32 levels, DXGI_FORMAT format, D3D12DescriptorHandle* dh); + bool CreateRTVDescriptor(ID3D12Resource* resource, DXGI_FORMAT format, D3D12DescriptorHandle* dh); + bool CreateDSVDescriptor(ID3D12Resource* resource, DXGI_FORMAT format, D3D12DescriptorHandle* dh); + bool CreateUAVDescriptor(ID3D12Resource* resource, DXGI_FORMAT format, D3D12DescriptorHandle* dh); + + bool CheckDownloadBufferSize(u32 required_size); + void DestroyDownloadBuffer(); + + /// Set dirty flags on everything to force re-bind at next draw time. + void InvalidateCachedState(); + void SetVertexBuffer(ID3D12GraphicsCommandList4* cmdlist); + void SetViewport(ID3D12GraphicsCommandList4* cmdlist); + void SetScissor(ID3D12GraphicsCommandList4* cmdlist); + + /// Applies any changed state. + ID3D12RootSignature* GetCurrentRootSignature() const; + void SetInitialPipelineState(); + void PreDrawCheck(); + + void UpdateRootSignature(); + template + bool UpdateParametersForLayout(u32 dirty); + bool UpdateRootParameters(u32 dirty); + + // Ends a render pass if we're currently in one. + // When Bind() is next called, the pass will be restarted. + void BeginRenderPass(); + void BeginSwapChainRenderPass(); + void EndRenderPass(); + bool InRenderPass(); + + ComPtr m_adapter; + ComPtr m_device; + ComPtr m_command_queue; + ComPtr m_allocator; + + ComPtr m_fence; + HANDLE m_fence_event = {}; + u64 m_current_fence_value = 0; + u64 m_completed_fence_value = 0; + + std::array m_command_lists; + u32 m_current_command_list = NUM_COMMAND_LISTS - 1; + D3D_FEATURE_LEVEL m_feature_level = D3D_FEATURE_LEVEL_11_0; + + ComPtr m_dxgi_factory; + ComPtr m_swap_chain; + std::vector, D3D12DescriptorHandle>> m_swap_chain_buffers; + u32 m_current_swap_chain_buffer = 0; + bool m_allow_tearing_supported = false; + bool m_using_allow_tearing = false; + bool m_is_exclusive_fullscreen = false; + + D3D12DescriptorHeapManager m_descriptor_heap_manager; + D3D12DescriptorHeapManager m_rtv_heap_manager; + D3D12DescriptorHeapManager m_dsv_heap_manager; + D3D12DescriptorHeapManager m_sampler_heap_manager; + D3D12DescriptorHandle m_null_srv_descriptor; + D3D12DescriptorHandle m_point_sampler; + + ComPtr m_timestamp_query_heap; + ComPtr m_timestamp_query_buffer; + ComPtr m_timestamp_query_allocation; + double m_timestamp_frequency = 0.0; + float m_accumulated_gpu_time = 0.0f; + + std::deque>> m_cleanup_resources; + std::deque>> m_cleanup_descriptors; + + std::array, static_cast(GPUPipeline::Layout::MaxCount)> m_root_signatures = {}; + + D3D12StreamBuffer m_vertex_buffer; + D3D12StreamBuffer m_index_buffer; + D3D12StreamBuffer m_uniform_buffer; + D3D12StreamBuffer m_texture_upload_buffer; + + u32 m_uniform_buffer_position = 0; + bool m_in_render_pass = false; + + SamplerMap m_sampler_map; + + ComPtr m_download_buffer_allocation; + ComPtr m_download_buffer; + u32 m_download_buffer_size = 0; + + // Which bindings/state has to be updated before the next draw. + u32 m_dirty_flags = ALL_DIRTY_STATE; + + D3D12Framebuffer* m_current_framebuffer = nullptr; + + D3D12Pipeline* m_current_pipeline = nullptr; + D3D12_PRIMITIVE_TOPOLOGY m_current_topology = D3D_PRIMITIVE_TOPOLOGY_UNDEFINED; + u32 m_current_vertex_stride = 0; + u32 m_current_blend_constant = 0; + GPUPipeline::Layout m_current_pipeline_layout = GPUPipeline::Layout::SingleTextureAndPushConstants; + + std::array m_current_textures = {}; + std::array m_current_samplers = {}; + D3D12TextureBuffer* m_current_texture_buffer = nullptr; + Common::Rectangle m_current_viewport{0, 0, 1, 1}; + Common::Rectangle m_current_scissor{0, 0, 1, 1}; +}; diff --git a/src/core/gpu/d3d12_gpu_device.cpp b/src/core/gpu/d3d12_gpu_device.cpp deleted file mode 100644 index 0b9c6ecb5..000000000 --- a/src/core/gpu/d3d12_gpu_device.cpp +++ /dev/null @@ -1,1031 +0,0 @@ -// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin -// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) - -#include "d3d12_gpu_device.h" -#include "../settings.h" -#include "common/assert.h" -#include "common/log.h" -#include "common/string_util.h" -#include "d3d12/context.h" -#include "d3d12/shader_cache.h" -#include "d3d12/util.h" -#include "d3d_shaders.h" -#include "imgui.h" -#include "imgui_impl_dx12.h" -#include "postprocessing_shadergen.h" -#include -#include -Log_SetChannel(D3D12GPUDevice); - -static constexpr const std::array s_clear_color = {0.0f, 0.0f, 0.0f, 1.0f}; - -D3D12GPUDevice::D3D12GPUDevice() = default; - -D3D12GPUDevice::~D3D12GPUDevice() -{ - if (!g_d3d12_context) - return; - - // DestroyRenderSurface() will exec the command list. - DestroySurface(); - //DestroyResources(); - g_d3d12_context->Destroy(); -} - -RenderAPI D3D12GPUDevice::GetRenderAPI() const -{ - return RenderAPI::D3D12; -} - -bool D3D12GPUDevice::HasSurface() const -{ - return static_cast(m_swap_chain); -} - -std::unique_ptr D3D12GPUDevice::CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples, - GPUTexture::Type type, GPUTexture::Format format, - const void* data, u32 data_stride, bool dynamic /* = false */) -{ - const DXGI_FORMAT dformat = D3D12::Texture::GetDXGIFormat(format); - if (dformat == DXGI_FORMAT_UNKNOWN) - return {}; - - std::unique_ptr tex(std::make_unique()); - if (!tex->Create(width, height, layers, levels, samples, dformat, dformat, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, - D3D12_RESOURCE_FLAG_NONE)) - { - return {}; - } - - if (data && !tex->LoadData(0, 0, width, height, data, data_stride)) - return {}; - - return tex; -} - -#if 0 -bool D3D12GPUDevice::BeginTextureUpdate(GPUTexture* texture, u32 width, u32 height, void** out_buffer, u32* out_pitch) -{ - return static_cast(texture)->BeginStreamUpdate(0, 0, width, height, out_buffer, out_pitch); -} - -void D3D12GPUDevice::EndTextureUpdate(GPUTexture* texture, u32 x, u32 y, u32 width, u32 height) -{ - static_cast(texture)->EndStreamUpdate(x, y, width, height); -} - -bool D3D12GPUDevice::UpdateTexture(GPUTexture* texture, u32 x, u32 y, u32 width, u32 height, const void* data, - u32 pitch) -{ - return GPUDevice::UpdateTexture(texture, x, y, width, height, data, pitch); -} -#endif - -bool D3D12GPUDevice::DownloadTexture(GPUTexture* texture, u32 x, u32 y, u32 width, u32 height, void* out_data, - u32 out_data_stride) -{ - const D3D12::Texture* tex = static_cast(texture); - - if (!m_readback_staging_texture.EnsureSize(width, height, tex->GetDXGIFormat(), false)) - return false; - - const D3D12_RESOURCE_STATES old_state = tex->GetState(); - tex->TransitionToState(D3D12_RESOURCE_STATE_COPY_SOURCE); - m_readback_staging_texture.CopyFromTexture(tex->GetResource(), 0, x, y, 0, 0, width, height); - tex->TransitionToState(old_state); - - return m_readback_staging_texture.ReadPixels(0, 0, width, height, out_data, out_data_stride); -} - -bool D3D12GPUDevice::SupportsTextureFormat(GPUTexture::Format format) const -{ - const DXGI_FORMAT dfmt = D3D12::Texture::GetDXGIFormat(format); - if (dfmt == DXGI_FORMAT_UNKNOWN) - return false; - - return g_d3d12_context->SupportsTextureFormat(dfmt); -} - -bool D3D12GPUDevice::GetHostRefreshRate(float* refresh_rate) -{ -#if 0 - if (m_swap_chain && IsFullscreen()) - { - DXGI_SWAP_CHAIN_DESC desc; - if (SUCCEEDED(m_swap_chain->GetDesc(&desc)) && desc.BufferDesc.RefreshRate.Numerator > 0 && - desc.BufferDesc.RefreshRate.Denominator > 0) - { - Log_InfoPrintf("using fs rr: %u %u", desc.BufferDesc.RefreshRate.Numerator, - desc.BufferDesc.RefreshRate.Denominator); - *refresh_rate = static_cast(desc.BufferDesc.RefreshRate.Numerator) / - static_cast(desc.BufferDesc.RefreshRate.Denominator); - return true; - } - } -#endif - - return GPUDevice::GetHostRefreshRate(refresh_rate); -} - -void D3D12GPUDevice::SetVSync(bool enabled) -{ - m_vsync_enabled = enabled; -} - -#if 0 -bool D3D12GPUDevice::CreateDevice(const WindowInfo& wi, bool vsync) -{ - ComPtr temp_dxgi_factory; - HRESULT hr = CreateDXGIFactory(IID_PPV_ARGS(temp_dxgi_factory.GetAddressOf())); - if (FAILED(hr)) - { - Log_ErrorPrintf("Failed to create DXGI factory: 0x%08X", hr); - return false; - } - - u32 adapter_index; - if (!g_settings.gpu_adapter.empty()) - { - AdapterAndModeList adapter_info(GetAdapterAndModeList(temp_dxgi_factory.Get())); - for (adapter_index = 0; adapter_index < static_cast(adapter_info.adapter_names.size()); adapter_index++) - { - if (g_settings.gpu_adapter == adapter_info.adapter_names[adapter_index]) - break; - } - if (adapter_index == static_cast(adapter_info.adapter_names.size())) - { - Log_WarningPrintf("Could not find adapter '%s', using first (%s)", g_settings.gpu_adapter.c_str(), - adapter_info.adapter_names[0].c_str()); - adapter_index = 0; - } - } - else - { - Log_InfoPrintf("No adapter selected, using first."); - adapter_index = 0; - } - - if (!D3D12::Context::Create(temp_dxgi_factory.Get(), adapter_index, g_settings.gpu_use_debug_device)) - return false; - - if (FAILED(hr)) - { - Log_ErrorPrintf("Failed to create D3D device: 0x%08X", hr); - return false; - } - - m_dxgi_factory = std::move(temp_dxgi_factory); - - m_allow_tearing_supported = false; - ComPtr dxgi_factory5; - hr = m_dxgi_factory.As(&dxgi_factory5); - if (SUCCEEDED(hr)) - { - BOOL allow_tearing_supported = false; - hr = dxgi_factory5->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &allow_tearing_supported, - sizeof(allow_tearing_supported)); - if (SUCCEEDED(hr)) - m_allow_tearing_supported = (allow_tearing_supported == TRUE); - } - - m_window_info = wi; - m_vsync_enabled = vsync; - - if (m_window_info.type != WindowInfo::Type::Surfaceless && !CreateSwapChain(nullptr)) - { - m_window_info = {}; - return false; - } - - return true; -} - -bool D3D12GPUDevice::SetupDevice() -{ - if (!CreateResources()) - return false; - - return true; -} - -bool D3D12GPUDevice::MakeCurrent() -{ - return true; -} - -bool D3D12GPUDevice::DoneCurrent() -{ - return true; -} -#endif - -bool D3D12GPUDevice::CreateSwapChain(const DXGI_MODE_DESC* fullscreen_mode) -{ - HRESULT hr; - - if (m_window_info.type != WindowInfo::Type::Win32) - return false; - - const HWND window_hwnd = reinterpret_cast(m_window_info.window_handle); - RECT client_rc{}; - GetClientRect(window_hwnd, &client_rc); - const u32 width = static_cast(client_rc.right - client_rc.left); - const u32 height = static_cast(client_rc.bottom - client_rc.top); - - DXGI_SWAP_CHAIN_DESC swap_chain_desc = {}; - swap_chain_desc.BufferDesc.Width = width; - swap_chain_desc.BufferDesc.Height = height; - swap_chain_desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; - swap_chain_desc.SampleDesc.Count = 1; - swap_chain_desc.BufferCount = 2; - swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; - swap_chain_desc.OutputWindow = window_hwnd; - swap_chain_desc.Windowed = TRUE; - swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; - - m_using_allow_tearing = (m_allow_tearing_supported && !fullscreen_mode); - if (m_using_allow_tearing) - swap_chain_desc.Flags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING; - - if (fullscreen_mode) - { - swap_chain_desc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH; - swap_chain_desc.Windowed = FALSE; - swap_chain_desc.BufferDesc = *fullscreen_mode; - } - - Log_InfoPrintf("Creating a %dx%d %s swap chain", swap_chain_desc.BufferDesc.Width, swap_chain_desc.BufferDesc.Height, - swap_chain_desc.Windowed ? "windowed" : "full-screen"); - - hr = - m_dxgi_factory->CreateSwapChain(g_d3d12_context->GetCommandQueue(), &swap_chain_desc, m_swap_chain.GetAddressOf()); - if (FAILED(hr)) - { - Log_ErrorPrintf("CreateSwapChain failed: 0x%08X", hr); - return false; - } - - hr = m_dxgi_factory->MakeWindowAssociation(swap_chain_desc.OutputWindow, DXGI_MWA_NO_WINDOW_CHANGES); - if (FAILED(hr)) - Log_WarningPrintf("MakeWindowAssociation() to disable ALT+ENTER failed"); - - return CreateSwapChainRTV(); -} - -bool D3D12GPUDevice::CreateSwapChainRTV() -{ - DXGI_SWAP_CHAIN_DESC swap_chain_desc; - HRESULT hr = m_swap_chain->GetDesc(&swap_chain_desc); - if (FAILED(hr)) - return false; - - for (u32 i = 0; i < swap_chain_desc.BufferCount; i++) - { - ComPtr backbuffer; - hr = m_swap_chain->GetBuffer(i, IID_PPV_ARGS(backbuffer.GetAddressOf())); - if (FAILED(hr)) - { - Log_ErrorPrintf("GetBuffer for RTV failed: 0x%08X", hr); - return false; - } - - D3D12::Texture tex; - if (!tex.Adopt(std::move(backbuffer), DXGI_FORMAT_UNKNOWN, swap_chain_desc.BufferDesc.Format, DXGI_FORMAT_UNKNOWN, - D3D12_RESOURCE_STATE_PRESENT)) - { - return false; - } - - m_swap_chain_buffers.push_back(std::move(tex)); - } - - m_window_info.surface_width = swap_chain_desc.BufferDesc.Width; - m_window_info.surface_height = swap_chain_desc.BufferDesc.Height; - Log_InfoPrintf("Swap chain buffer size: %ux%u", m_window_info.surface_width, m_window_info.surface_height); - - if (m_window_info.type == WindowInfo::Type::Win32) - { - BOOL fullscreen = FALSE; - DXGI_SWAP_CHAIN_DESC desc; - if (SUCCEEDED(m_swap_chain->GetFullscreenState(&fullscreen, nullptr)) && fullscreen && - SUCCEEDED(m_swap_chain->GetDesc(&desc))) - { - m_window_info.surface_refresh_rate = static_cast(desc.BufferDesc.RefreshRate.Numerator) / - static_cast(desc.BufferDesc.RefreshRate.Denominator); - } - else - { - m_window_info.surface_refresh_rate = 0.0f; - } - } - - m_current_swap_chain_buffer = 0; - return true; -} - -void D3D12GPUDevice::DestroySwapChainRTVs() -{ - for (D3D12::Texture& buffer : m_swap_chain_buffers) - buffer.Destroy(false); - m_swap_chain_buffers.clear(); - m_current_swap_chain_buffer = 0; -} - -#if 0 - -bool D3D12GPUDevice::ChangeWindow(const WindowInfo& new_wi) -{ - DestroySurface(); - - m_window_info = new_wi; - return CreateSwapChain(nullptr); -} -#endif - -void D3D12GPUDevice::DestroySurface() -{ - m_window_info.SetSurfaceless(); - - // For some reason if we don't execute the command list here, the swap chain is in use.. not sure where. - g_d3d12_context->ExecuteCommandList(true); - -#if 0 - if (IsFullscreen()) - SetFullscreen(false, 0, 0, 0.0f); -#endif - - DestroySwapChainRTVs(); - m_swap_chain.Reset(); -} -#if 0 - -void D3D12GPUDevice::ResizeWindow(s32 new_window_width, s32 new_window_height) -{ - if (!m_swap_chain) - return; - - // For some reason if we don't execute the command list here, the swap chain is in use.. not sure where. - g_d3d12_context->ExecuteCommandList(true); - - DestroySwapChainRTVs(); - - HRESULT hr = m_swap_chain->ResizeBuffers(0, 0, 0, DXGI_FORMAT_UNKNOWN, - m_using_allow_tearing ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0); - if (FAILED(hr)) - Log_ErrorPrintf("ResizeBuffers() failed: 0x%08X", hr); - - if (!CreateSwapChainRTV()) - Panic("Failed to recreate swap chain RTV after resize"); -} - -bool D3D12GPUDevice::SupportsFullscreen() const -{ - return true; -} - -bool D3D12GPUDevice::IsFullscreen() -{ - BOOL is_fullscreen = FALSE; - return (m_swap_chain && SUCCEEDED(m_swap_chain->GetFullscreenState(&is_fullscreen, nullptr)) && is_fullscreen); -} - -bool D3D12GPUDevice::SetFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate) -{ - if (!m_swap_chain) - return false; - - BOOL is_fullscreen = FALSE; - HRESULT hr = m_swap_chain->GetFullscreenState(&is_fullscreen, nullptr); - if (!fullscreen) - { - // leaving fullscreen - if (is_fullscreen) - return SUCCEEDED(m_swap_chain->SetFullscreenState(FALSE, nullptr)); - else - return true; - } - - IDXGIOutput* output; - if (FAILED(hr = m_swap_chain->GetContainingOutput(&output))) - return false; - - DXGI_SWAP_CHAIN_DESC current_desc; - hr = m_swap_chain->GetDesc(¤t_desc); - if (FAILED(hr)) - return false; - - DXGI_MODE_DESC new_mode = current_desc.BufferDesc; - new_mode.Width = width; - new_mode.Height = height; - new_mode.RefreshRate.Numerator = static_cast(std::floor(refresh_rate * 1000.0f)); - new_mode.RefreshRate.Denominator = 1000u; - - DXGI_MODE_DESC closest_mode; - if (FAILED(hr = output->FindClosestMatchingMode(&new_mode, &closest_mode, nullptr)) || - new_mode.Format != current_desc.BufferDesc.Format) - { - Log_ErrorPrintf("Failed to find closest matching mode, hr=%08X", hr); - return false; - } - - if (new_mode.Width == current_desc.BufferDesc.Width && new_mode.Height == current_desc.BufferDesc.Width && - new_mode.RefreshRate.Numerator == current_desc.BufferDesc.RefreshRate.Numerator && - new_mode.RefreshRate.Denominator == current_desc.BufferDesc.RefreshRate.Denominator) - { - Log_InfoPrintf("Fullscreen mode already set"); - return true; - } - - g_d3d12_context->ExecuteCommandList(true); - DestroySwapChainRTVs(); - m_swap_chain.Reset(); - - if (!CreateSwapChain(&closest_mode)) - { - Log_ErrorPrintf("Failed to create a fullscreen swap chain"); - if (!CreateSwapChain(nullptr)) - Panic("Failed to recreate windowed swap chain"); - - return false; - } - - return true; -} -#endif -GPUDevice::AdapterAndModeList D3D12GPUDevice::GetAdapterAndModeList() -{ - return GetAdapterAndModeList(m_dxgi_factory.Get()); -} - -#if 0 - -bool D3D12GPUDevice::CreateResources() -{ - D3D12::RootSignatureBuilder rsbuilder; - rsbuilder.Add32BitConstants(0, 4, D3D12_SHADER_VISIBILITY_VERTEX); - rsbuilder.AddDescriptorTable(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 0, 1, D3D12_SHADER_VISIBILITY_ALL); - rsbuilder.AddDescriptorTable(D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER, 0, 1, D3D12_SHADER_VISIBILITY_ALL); - m_display_root_signature = rsbuilder.Create(); - if (!m_display_root_signature) - return false; - -#if 0 - rsbuilder.SetInputAssemblerFlag(); - rsbuilder.Add32BitConstants(0, FrontendCommon::PostProcessingShader::PUSH_CONSTANT_SIZE_THRESHOLD / sizeof(u32), - D3D12_SHADER_VISIBILITY_ALL); - rsbuilder.AddDescriptorTable(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 0, 1, D3D12_SHADER_VISIBILITY_PIXEL); - rsbuilder.AddDescriptorTable(D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER, 0, 1, D3D12_SHADER_VISIBILITY_PIXEL); - m_post_processing_root_signature = rsbuilder.Create(); - if (!m_post_processing_root_signature) - return false; - - rsbuilder.SetInputAssemblerFlag(); - rsbuilder.AddCBVParameter(0, D3D12_SHADER_VISIBILITY_ALL); - rsbuilder.AddDescriptorTable(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 0, 1, D3D12_SHADER_VISIBILITY_PIXEL); - rsbuilder.AddDescriptorTable(D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER, 0, 1, D3D12_SHADER_VISIBILITY_PIXEL); - m_post_processing_cb_root_signature = rsbuilder.Create(); - if (!m_post_processing_cb_root_signature) - return false; -#endif - - D3D12::GraphicsPipelineBuilder gpbuilder; - gpbuilder.SetPrimitiveTopologyType(D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE); - gpbuilder.SetRootSignature(m_display_root_signature.Get()); - gpbuilder.SetVertexShader(s_display_vs_bytecode, sizeof(s_display_vs_bytecode)); - gpbuilder.SetPixelShader(s_display_ps_bytecode, sizeof(s_display_ps_bytecode)); - gpbuilder.SetNoCullRasterizationState(); - gpbuilder.SetNoDepthTestState(); - gpbuilder.SetNoBlendingState(); - gpbuilder.SetRenderTarget(0, DXGI_FORMAT_R8G8B8A8_UNORM); - m_display_pipeline = gpbuilder.Create(g_d3d12_context->GetDevice(), false); - if (!m_display_pipeline) - return false; - - gpbuilder.SetPixelShader(s_display_ps_alpha_bytecode, sizeof(s_display_ps_alpha_bytecode)); - gpbuilder.SetBlendState(0, true, D3D12_BLEND_SRC_ALPHA, D3D12_BLEND_INV_SRC_ALPHA, D3D12_BLEND_OP_ADD, - D3D12_BLEND_ONE, D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD, D3D12_COLOR_WRITE_ENABLE_ALL); - m_software_cursor_pipeline = gpbuilder.Create(g_d3d12_context->GetDevice(), false); - if (!m_software_cursor_pipeline) - return false; - - D3D12_SAMPLER_DESC desc = {}; - D3D12::SetDefaultSampler(&desc); - desc.AddressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP; - desc.AddressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP; - desc.Filter = D3D12_FILTER_MIN_MAG_MIP_POINT; - - if (!g_d3d12_context->GetSamplerHeapManager().Allocate(&m_point_sampler)) - return false; - - g_d3d12_context->GetDevice()->CreateSampler(&desc, m_point_sampler.cpu_handle); - - desc.Filter = D3D12_FILTER_MIN_MAG_LINEAR_MIP_POINT; - - if (!g_d3d12_context->GetSamplerHeapManager().Allocate(&m_linear_sampler)) - return false; - - g_d3d12_context->GetDevice()->CreateSampler(&desc, m_linear_sampler.cpu_handle); - - if (!g_d3d12_context->GetSamplerHeapManager().Allocate(&m_border_sampler)) - return false; - - desc.AddressU = D3D12_TEXTURE_ADDRESS_MODE_BORDER; - desc.AddressV = D3D12_TEXTURE_ADDRESS_MODE_BORDER; - desc.Filter = D3D12_FILTER_MIN_MAG_MIP_POINT; - desc.BorderColor[0] = 0.0f; - desc.BorderColor[1] = 0.0f; - desc.BorderColor[2] = 0.0f; - desc.BorderColor[3] = 1.0f; - g_d3d12_context->GetDevice()->CreateSampler(&desc, m_border_sampler.cpu_handle); - - return true; -} - -void D3D12GPUDevice::DestroyResources() -{ - GPUDevice::DestroyResources(); - -#if 0 - m_post_processing_cbuffer.Destroy(false); - m_post_processing_chain.ClearStages(); - m_post_processing_input_texture.Destroy(); - m_post_processing_stages.clear(); - m_post_processing_cb_root_signature.Reset(); - m_post_processing_root_signature.Reset(); -#endif - - m_readback_staging_texture.Destroy(false); - g_d3d12_context->GetSamplerHeapManager().Free(&m_border_sampler); - g_d3d12_context->GetSamplerHeapManager().Free(&m_linear_sampler); - g_d3d12_context->GetSamplerHeapManager().Free(&m_point_sampler); - m_software_cursor_pipeline.Reset(); - m_display_pipeline.Reset(); - m_display_root_signature.Reset(); -} -#endif - -#if 0 -bool D3D12GPUDevice::CreateImGuiContext() -{ - ImGui::GetIO().DisplaySize.x = static_cast(m_window_info.surface_width); - ImGui::GetIO().DisplaySize.y = static_cast(m_window_info.surface_height); - - return ImGui_ImplDX12_Init(DXGI_FORMAT_R8G8B8A8_UNORM); -} - -void D3D12GPUDevice::DestroyImGuiContext() -{ - g_d3d12_context->WaitForGPUIdle(); - - ImGui_ImplDX12_Shutdown(); -} - -bool D3D12GPUDevice::UpdateImGuiFontTexture() -{ - return ImGui_ImplDX12_CreateFontsTexture(); -} -#endif - -#if 0 -bool D3D12GPUDevice::Render(bool skip_present) -{ - if (skip_present || !m_swap_chain) - { - if (ImGui::GetCurrentContext()) - ImGui::Render(); - - return false; - } - - D3D12::Texture& swap_chain_buf = m_swap_chain_buffers[m_current_swap_chain_buffer]; - m_current_swap_chain_buffer = ((m_current_swap_chain_buffer + 1) % static_cast(m_swap_chain_buffers.size())); - - ID3D12GraphicsCommandList* cmdlist = g_d3d12_context->GetCommandList(); - RenderDisplay(cmdlist, &swap_chain_buf); - - if (ImGui::GetCurrentContext()) - RenderImGui(cmdlist); - - RenderSoftwareCursor(cmdlist); - - swap_chain_buf.TransitionToState(D3D12_RESOURCE_STATE_PRESENT); - g_d3d12_context->ExecuteCommandList(false); - - if (!m_vsync_enabled && m_using_allow_tearing) - m_swap_chain->Present(0, DXGI_PRESENT_ALLOW_TEARING); - else - m_swap_chain->Present(BoolToUInt32(m_vsync_enabled), 0); - - return true; -} -#endif - -bool D3D12GPUDevice::SetGPUTimingEnabled(bool enabled) -{ - g_d3d12_context->SetEnableGPUTiming(enabled); - m_gpu_timing_enabled = enabled; - return true; -} - -float D3D12GPUDevice::GetAndResetAccumulatedGPUTime() -{ - return g_d3d12_context->GetAndResetAccumulatedGPUTime(); -} - -#if 0 - -void D3D12GPUDevice::RenderImGui(ID3D12GraphicsCommandList* cmdlist) -{ - ImGui::Render(); - ImGui_ImplDX12_RenderDrawData(ImGui::GetDrawData()); -} - -void D3D12GPUDevice::RenderDisplay(ID3D12GraphicsCommandList* cmdlist, D3D12::Texture* swap_chain_buf) -{ - const auto [left, top, width, height] = CalculateDrawRect(GetWindowWidth(), GetWindowHeight()); - -#if 0 - if (HasDisplayTexture() && !m_post_processing_chain.IsEmpty()) - { - ApplyPostProcessingChain(cmdlist, swap_chain_buf, left, top, width, height, - static_cast(m_display_texture), m_display_texture_view_x, - m_display_texture_view_y, m_display_texture_view_width, m_display_texture_view_height, - GetWindowWidth(), GetWindowHeight()); - return; - } -#endif - - swap_chain_buf->TransitionToState(D3D12_RESOURCE_STATE_RENDER_TARGET); - cmdlist->ClearRenderTargetView(swap_chain_buf->GetRTVOrDSVDescriptor(), s_clear_color.data(), 0, nullptr); - cmdlist->OMSetRenderTargets(1, &swap_chain_buf->GetRTVOrDSVDescriptor().cpu_handle, FALSE, nullptr); - - if (!HasDisplayTexture()) - return; - - RenderDisplay(cmdlist, left, top, width, height, static_cast(m_display_texture), - m_display_texture_view_x, m_display_texture_view_y, m_display_texture_view_width, - m_display_texture_view_height, IsUsingLinearFiltering()); -} - -void D3D12GPUDevice::RenderDisplay(ID3D12GraphicsCommandList* cmdlist, s32 left, s32 top, s32 width, s32 height, - D3D12::Texture* texture, s32 texture_view_x, s32 texture_view_y, - s32 texture_view_width, s32 texture_view_height, bool linear_filter) -{ - const float position_adjust = linear_filter ? 0.5f : 0.0f; - const float size_adjust = linear_filter ? 1.0f : 0.0f; - const float uniforms[4] = { - (static_cast(texture_view_x) + position_adjust) / static_cast(texture->GetWidth()), - (static_cast(texture_view_y) + position_adjust) / static_cast(texture->GetHeight()), - (static_cast(texture_view_width) - size_adjust) / static_cast(texture->GetWidth()), - (static_cast(texture_view_height) - size_adjust) / static_cast(texture->GetHeight())}; - - cmdlist->SetGraphicsRootSignature(m_display_root_signature.Get()); - cmdlist->SetPipelineState(m_display_pipeline.Get()); - cmdlist->SetGraphicsRoot32BitConstants(0, static_cast(std::size(uniforms)), uniforms, 0); - cmdlist->SetGraphicsRootDescriptorTable(1, texture->GetSRVDescriptor()); - cmdlist->SetGraphicsRootDescriptorTable(2, linear_filter ? m_linear_sampler : m_point_sampler); - - D3D12::SetViewportAndClampScissor(cmdlist, left, top, width, height); - - cmdlist->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); - cmdlist->DrawInstanced(3, 1, 0, 0); -} - -void D3D12GPUDevice::RenderSoftwareCursor(ID3D12GraphicsCommandList* cmdlist) -{ - if (!HasSoftwareCursor()) - return; - - const auto [left, top, width, height] = CalculateSoftwareCursorDrawRect(); - RenderSoftwareCursor(cmdlist, left, top, width, height, m_cursor_texture.get()); -} - -void D3D12GPUDevice::RenderSoftwareCursor(ID3D12GraphicsCommandList* cmdlist, s32 left, s32 top, s32 width, s32 height, - GPUTexture* texture_handle) -{ - const float uniforms[4] = {0.0f, 0.0f, 1.0f, 1.0f}; - - cmdlist->SetGraphicsRootSignature(m_display_root_signature.Get()); - cmdlist->SetPipelineState(m_software_cursor_pipeline.Get()); - cmdlist->SetGraphicsRoot32BitConstants(0, static_cast(std::size(uniforms)), uniforms, 0); - cmdlist->SetGraphicsRootDescriptorTable(1, static_cast(texture_handle)->GetSRVDescriptor()); - cmdlist->SetGraphicsRootDescriptorTable(2, m_linear_sampler); - - D3D12::SetViewportAndClampScissor(cmdlist, left, top, width, height); - - cmdlist->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); - cmdlist->DrawInstanced(3, 1, 0, 0); -} - -#endif - -GPUDevice::AdapterAndModeList D3D12GPUDevice::StaticGetAdapterAndModeList() -{ - ComPtr dxgi_factory; - HRESULT hr = CreateDXGIFactory(IID_PPV_ARGS(dxgi_factory.GetAddressOf())); - if (FAILED(hr)) - return {}; - - return GetAdapterAndModeList(dxgi_factory.Get()); -} - -GPUDevice::AdapterAndModeList D3D12GPUDevice::GetAdapterAndModeList(IDXGIFactory* dxgi_factory) -{ - AdapterAndModeList adapter_info; - ComPtr current_adapter; - while (SUCCEEDED(dxgi_factory->EnumAdapters(static_cast(adapter_info.adapter_names.size()), - current_adapter.ReleaseAndGetAddressOf()))) - { - DXGI_ADAPTER_DESC adapter_desc; - std::string adapter_name; - if (SUCCEEDED(current_adapter->GetDesc(&adapter_desc))) - { - char adapter_name_buffer[128]; - const int name_length = WideCharToMultiByte(CP_UTF8, 0, adapter_desc.Description, - static_cast(std::wcslen(adapter_desc.Description)), - adapter_name_buffer, countof(adapter_name_buffer), 0, nullptr); - if (name_length >= 0) - adapter_name.assign(adapter_name_buffer, static_cast(name_length)); - else - adapter_name.assign("(Unknown)"); - } - else - { - adapter_name.assign("(Unknown)"); - } - - if (adapter_info.fullscreen_modes.empty()) - { - ComPtr output; - if (SUCCEEDED(current_adapter->EnumOutputs(0, &output))) - { - UINT num_modes = 0; - if (SUCCEEDED(output->GetDisplayModeList(DXGI_FORMAT_R8G8B8A8_UNORM, 0, &num_modes, nullptr))) - { - std::vector modes(num_modes); - if (SUCCEEDED(output->GetDisplayModeList(DXGI_FORMAT_R8G8B8A8_UNORM, 0, &num_modes, modes.data()))) - { - for (const DXGI_MODE_DESC& mode : modes) - { - adapter_info.fullscreen_modes.push_back(StringUtil::StdStringFromFormat( - "%u x %u @ %f hz", mode.Width, mode.Height, - static_cast(mode.RefreshRate.Numerator) / static_cast(mode.RefreshRate.Denominator))); - } - } - } - } - } - - // handle duplicate adapter names - if (std::any_of(adapter_info.adapter_names.begin(), adapter_info.adapter_names.end(), - [&adapter_name](const std::string& other) { return (adapter_name == other); })) - { - std::string original_adapter_name = std::move(adapter_name); - - u32 current_extra = 2; - do - { - adapter_name = StringUtil::StdStringFromFormat("%s (%u)", original_adapter_name.c_str(), current_extra); - current_extra++; - } while (std::any_of(adapter_info.adapter_names.begin(), adapter_info.adapter_names.end(), - [&adapter_name](const std::string& other) { return (adapter_name == other); })); - } - - adapter_info.adapter_names.push_back(std::move(adapter_name)); - } - - return adapter_info; -} - -#if 0 -D3D12GPUDevice::PostProcessingStage::PostProcessingStage(PostProcessingStage&& move) - : pipeline(std::move(move.pipeline)), output_texture(std::move(move.output_texture)), - uniforms_size(move.uniforms_size) -{ - move.uniforms_size = 0; -} - -D3D12GPUDevice::PostProcessingStage::~PostProcessingStage() -{ - output_texture.Destroy(true); -} -#endif - -bool D3D12GPUDevice::SetPostProcessingChain(const std::string_view& config) -{ -#if 0 - g_d3d12_context->ExecuteCommandList(true); - - if (config.empty()) - { - m_post_processing_stages.clear(); - m_post_processing_chain.ClearStages(); - return true; - } - - if (!m_post_processing_chain.CreateFromString(config)) - return false; - - m_post_processing_stages.clear(); - - D3D12::ShaderCache shader_cache; - shader_cache.Open(EmuFolders::Cache, g_d3d12_context->GetFeatureLevel(), g_settings.gpu_use_debug_device); - - FrontendCommon::PostProcessingShaderGen shadergen(RenderAPI::D3D12, false); - bool only_use_push_constants = true; - - for (u32 i = 0; i < m_post_processing_chain.GetStageCount(); i++) - { - const FrontendCommon::PostProcessingShader& shader = m_post_processing_chain.GetShaderStage(i); - const std::string vs = shadergen.GeneratePostProcessingVertexShader(shader); - const std::string ps = shadergen.GeneratePostProcessingFragmentShader(shader); - const bool use_push_constants = shader.UsePushConstants(); - only_use_push_constants &= use_push_constants; - - PostProcessingStage stage; - stage.uniforms_size = shader.GetUniformsSize(); - - ComPtr vs_blob(shader_cache.GetVertexShader(vs)); - ComPtr ps_blob(shader_cache.GetPixelShader(ps)); - if (!vs_blob || !ps_blob) - { - Log_ErrorPrintf("Failed to compile one or more post-processing shaders, disabling."); - m_post_processing_stages.clear(); - m_post_processing_chain.ClearStages(); - return false; - } - - D3D12::GraphicsPipelineBuilder gpbuilder; - gpbuilder.SetVertexShader(vs_blob.Get()); - gpbuilder.SetPixelShader(ps_blob.Get()); - gpbuilder.SetPrimitiveTopologyType(D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE); - gpbuilder.SetNoCullRasterizationState(); - gpbuilder.SetNoDepthTestState(); - gpbuilder.SetNoBlendingState(); - gpbuilder.SetRootSignature(use_push_constants ? m_post_processing_root_signature.Get() : - m_post_processing_cb_root_signature.Get()); - gpbuilder.SetRenderTarget(0, DXGI_FORMAT_R8G8B8A8_UNORM); - - stage.pipeline = gpbuilder.Create(g_d3d12_context->GetDevice(), shader_cache); - if (!stage.pipeline) - { - Log_ErrorPrintf("Failed to compile one or more post-processing pipelines, disabling."); - m_post_processing_stages.clear(); - m_post_processing_chain.ClearStages(); - return false; - } - D3D12::SetObjectNameFormatted(stage.pipeline.Get(), "%s Pipeline", shader.GetName().c_str()); - - m_post_processing_stages.push_back(std::move(stage)); - } - - constexpr u32 UBO_SIZE = 1 * 1024 * 1024; - if (!only_use_push_constants && m_post_processing_cbuffer.GetSize() < UBO_SIZE) - { - if (!m_post_processing_cbuffer.Create(UBO_SIZE)) - { - Log_ErrorPrintf("Failed to allocate %u byte constant buffer for postprocessing", UBO_SIZE); - m_post_processing_stages.clear(); - m_post_processing_chain.ClearStages(); - return false; - } - - D3D12::SetObjectName(m_post_processing_cbuffer.GetBuffer(), "Post Processing Uniform Buffer"); - } - - m_post_processing_timer.Reset(); - return true; -#else - return false; -#endif -} - -#if 0 -bool D3D12GPUDevice::CheckPostProcessingRenderTargets(u32 target_width, u32 target_height) -{ - DebugAssert(!m_post_processing_stages.empty()); - - const DXGI_FORMAT tex_format = DXGI_FORMAT_R8G8B8A8_UNORM; - const DXGI_FORMAT srv_format = DXGI_FORMAT_R8G8B8A8_UNORM; - const DXGI_FORMAT rtv_format = DXGI_FORMAT_R8G8B8A8_UNORM; - - if (m_post_processing_input_texture.GetWidth() != target_width || - m_post_processing_input_texture.GetHeight() != target_height) - { - if (!m_post_processing_input_texture.Create(target_width, target_height, 1, 1, 1, tex_format, srv_format, - rtv_format, DXGI_FORMAT_UNKNOWN, - D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET)) - { - return false; - } - D3D12::SetObjectName(m_post_processing_input_texture.GetResource(), "Post Processing Input Texture"); - } - - const u32 target_count = (static_cast(m_post_processing_stages.size()) - 1); - for (u32 i = 0; i < target_count; i++) - { - PostProcessingStage& pps = m_post_processing_stages[i]; - if (pps.output_texture.GetWidth() != target_width || pps.output_texture.GetHeight() != target_height) - { - if (!pps.output_texture.Create(target_width, target_height, 1, 1, 1, tex_format, srv_format, rtv_format, - DXGI_FORMAT_UNKNOWN, D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET)) - { - return false; - } - D3D12::SetObjectNameFormatted(pps.output_texture.GetResource(), "Post Processing Output Texture %u", i); - } - } - - return true; -} - -void D3D12GPUDevice::ApplyPostProcessingChain(ID3D12GraphicsCommandList* cmdlist, D3D12::Texture* final_target, - s32 final_left, s32 final_top, s32 final_width, s32 final_height, - D3D12::Texture* texture, s32 texture_view_x, s32 texture_view_y, - s32 texture_view_width, s32 texture_view_height, u32 target_width, - u32 target_height) -{ - if (!CheckPostProcessingRenderTargets(target_width, target_height)) - { - final_target->TransitionToState(D3D12_RESOURCE_STATE_RENDER_TARGET); - cmdlist->ClearRenderTargetView(final_target->GetRTVOrDSVDescriptor(), s_clear_color.data(), 0, nullptr); - cmdlist->OMSetRenderTargets(1, &final_target->GetRTVOrDSVDescriptor().cpu_handle, FALSE, nullptr); - - RenderDisplay(cmdlist, final_left, final_top, final_width, final_height, texture, texture_view_x, texture_view_y, - texture_view_width, texture_view_height, IsUsingLinearFiltering()); - return; - } - - // downsample/upsample - use same viewport for remainder - m_post_processing_input_texture.TransitionToState(D3D12_RESOURCE_STATE_RENDER_TARGET); - cmdlist->ClearRenderTargetView(m_post_processing_input_texture.GetRTVOrDSVDescriptor(), s_clear_color.data(), 0, - nullptr); - cmdlist->OMSetRenderTargets(1, &m_post_processing_input_texture.GetRTVOrDSVDescriptor().cpu_handle, FALSE, nullptr); - RenderDisplay(cmdlist, final_left, final_top, final_width, final_height, texture, texture_view_x, texture_view_y, - texture_view_width, texture_view_height, IsUsingLinearFiltering()); - m_post_processing_input_texture.TransitionToState(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); - - const s32 orig_texture_width = texture_view_width; - const s32 orig_texture_height = texture_view_height; - texture = &m_post_processing_input_texture; - texture_view_x = final_left; - texture_view_y = final_top; - texture_view_width = final_width; - texture_view_height = final_height; - - const u32 final_stage = static_cast(m_post_processing_stages.size()) - 1u; - for (u32 i = 0; i < static_cast(m_post_processing_stages.size()); i++) - { - PostProcessingStage& pps = m_post_processing_stages[i]; - - const bool use_push_constants = m_post_processing_chain.GetShaderStage(i).UsePushConstants(); - if (use_push_constants) - { - u8 buffer[FrontendCommon::PostProcessingShader::PUSH_CONSTANT_SIZE_THRESHOLD]; - Assert(pps.uniforms_size <= sizeof(buffer)); - m_post_processing_chain.GetShaderStage(i).FillUniformBuffer( - buffer, texture->GetWidth(), texture->GetHeight(), texture_view_x, texture_view_y, texture_view_width, - texture_view_height, GetWindowWidth(), GetWindowHeight(), orig_texture_width, orig_texture_height, - static_cast(m_post_processing_timer.GetTimeSeconds())); - - cmdlist->SetGraphicsRootSignature(m_post_processing_root_signature.Get()); - cmdlist->SetGraphicsRoot32BitConstants(0, sizeof(buffer) / sizeof(u32), buffer, 0); - } - else - { - if (!m_post_processing_cbuffer.ReserveMemory(pps.uniforms_size, D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT)) - { - Panic("Failed to reserve space in post-processing UBO"); - } - - const u32 offset = m_post_processing_cbuffer.GetCurrentOffset(); - m_post_processing_chain.GetShaderStage(i).FillUniformBuffer( - m_post_processing_cbuffer.GetCurrentHostPointer(), texture->GetWidth(), texture->GetHeight(), texture_view_x, - texture_view_y, texture_view_width, texture_view_height, GetWindowWidth(), GetWindowHeight(), - orig_texture_width, orig_texture_height, static_cast(m_post_processing_timer.GetTimeSeconds())); - m_post_processing_cbuffer.CommitMemory(pps.uniforms_size); - - cmdlist->SetGraphicsRootSignature(m_post_processing_cb_root_signature.Get()); - cmdlist->SetGraphicsRootConstantBufferView(0, m_post_processing_cbuffer.GetGPUPointer() + offset); - } - - D3D12::Texture* rt = (i != final_stage) ? &pps.output_texture : final_target; - rt->TransitionToState(D3D12_RESOURCE_STATE_RENDER_TARGET); - cmdlist->ClearRenderTargetView(rt->GetRTVOrDSVDescriptor(), s_clear_color.data(), 0, nullptr); - cmdlist->OMSetRenderTargets(1, &rt->GetRTVOrDSVDescriptor().cpu_handle, FALSE, nullptr); - - cmdlist->SetPipelineState(pps.pipeline.Get()); - cmdlist->SetGraphicsRootDescriptorTable(1, texture->GetSRVDescriptor()); - cmdlist->SetGraphicsRootDescriptorTable(2, m_border_sampler); - - cmdlist->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); - cmdlist->DrawInstanced(3, 1, 0, 0); - - if (i != final_stage) - { - pps.output_texture.TransitionToState(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); - texture = &pps.output_texture; - } - } -} -#endif \ No newline at end of file diff --git a/src/core/gpu/d3d12_gpu_device.h b/src/core/gpu/d3d12_gpu_device.h deleted file mode 100644 index 1c9edb6e9..000000000 --- a/src/core/gpu/d3d12_gpu_device.h +++ /dev/null @@ -1,107 +0,0 @@ -// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin -// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) - -#pragma once -#include "common/timer.h" -#include "common/windows_headers.h" -#include "d3d12/descriptor_heap_manager.h" -#include "d3d12/staging_texture.h" -#include "d3d12/stream_buffer.h" -#include "d3d12/texture.h" -#include "gpu_device.h" -#include "postprocessing_chain.h" -#include -#include -#include -#include -#include -#include -#include - -class D3D12GPUDevice final : public GPUDevice -{ -public: - template - using ComPtr = Microsoft::WRL::ComPtr; - - D3D12GPUDevice(); - ~D3D12GPUDevice(); - - RenderAPI GetRenderAPI() const override; - - bool HasSurface() const override; - - //bool CreateDevice(const WindowInfo& wi, bool vsync) override; - //bool SetupDevice() override; - - //bool MakeCurrent() override; - //bool DoneCurrent() override; - - //bool ChangeWindow(const WindowInfo& new_wi) override; - //void ResizeWindow(s32 new_window_width, s32 new_window_height) override; - //bool SupportsFullscreen() const override; - //bool IsFullscreen() override; - //bool SetFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate) override; - AdapterAndModeList GetAdapterAndModeList() override; - void DestroySurface() override; - - bool SetPostProcessingChain(const std::string_view& config) override; - - std::unique_ptr CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples, - GPUTexture::Type type, GPUTexture::Format format, const void* data, - u32 data_stride, bool dynamic = false) override; - bool DownloadTexture(GPUTexture* texture, u32 x, u32 y, u32 width, u32 height, void* out_data, - u32 out_data_stride) override; - bool SupportsTextureFormat(GPUTexture::Format format) const override; - - bool GetHostRefreshRate(float* refresh_rate) override; - - void SetVSync(bool enabled) override; - - //bool Render(bool skip_present) override; - - bool SetGPUTimingEnabled(bool enabled) override; - float GetAndResetAccumulatedGPUTime() override; - - static AdapterAndModeList StaticGetAdapterAndModeList(); - -protected: - static AdapterAndModeList GetAdapterAndModeList(IDXGIFactory* dxgi_factory); - - //virtual bool CreateResources() override; - //virtual void DestroyResources() override; - - bool CreateSwapChain(const DXGI_MODE_DESC* fullscreen_mode); - bool CreateSwapChainRTV(); - void DestroySwapChainRTVs(); - -#if 0 - void RenderDisplay(ID3D12GraphicsCommandList* cmdlist, D3D12::Texture* swap_chain_buf); - void RenderSoftwareCursor(ID3D12GraphicsCommandList* cmdlist); - void RenderImGui(ID3D12GraphicsCommandList* cmdlist); - - void RenderDisplay(ID3D12GraphicsCommandList* cmdlist, s32 left, s32 top, s32 width, s32 height, - D3D12::Texture* texture, s32 texture_view_x, s32 texture_view_y, s32 texture_view_width, - s32 texture_view_height, bool linear_filter); - void RenderSoftwareCursor(ID3D12GraphicsCommandList* cmdlist, s32 left, s32 top, s32 width, s32 height, - GPUTexture* texture_handle); -#endif - - ComPtr m_dxgi_factory; - ComPtr m_swap_chain; - std::vector m_swap_chain_buffers; - u32 m_current_swap_chain_buffer = 0; - - ComPtr m_display_root_signature; - ComPtr m_display_pipeline; - ComPtr m_software_cursor_pipeline; - D3D12::DescriptorHandle m_point_sampler; - D3D12::DescriptorHandle m_linear_sampler; - D3D12::DescriptorHandle m_border_sampler; - - D3D12::Texture m_display_pixels_texture; - D3D12::StagingTexture m_readback_staging_texture; - - bool m_allow_tearing_supported = false; - bool m_using_allow_tearing = false; -}; diff --git a/src/core/gpu/d3d12_pipeline.cpp b/src/core/gpu/d3d12_pipeline.cpp new file mode 100644 index 000000000..efd3237d0 --- /dev/null +++ b/src/core/gpu/d3d12_pipeline.cpp @@ -0,0 +1,199 @@ +// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin +// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) + +#include "d3d12_pipeline.h" +#include "d3d12_builders.h" +#include "d3d12_device.h" +#include "d3d_common.h" + +#include "common/assert.h" +#include "common/log.h" + +#include + +Log_SetChannel(D3D12Device); + +static u32 s_next_bad_shader_id = 1; + +D3D12Shader::D3D12Shader(GPUShaderStage stage, Bytecode bytecode) : GPUShader(stage), m_bytecode(std::move(bytecode)) +{ +} + +D3D12Shader::~D3D12Shader() = default; + +void D3D12Shader::SetDebugName(const std::string_view& name) +{ +} + +std::unique_ptr D3D12Device::CreateShaderFromBinary(GPUShaderStage stage, gsl::span data) +{ + // Can't do much at this point. + std::vector bytecode(data.begin(), data.end()); + return std::unique_ptr(new D3D12Shader(stage, std::move(bytecode))); +} + +std::unique_ptr D3D12Device::CreateShaderFromSource(GPUShaderStage stage, const std::string_view& source, + std::vector* out_binary /*= nullptr*/) +{ + std::optional> bytecode = D3DCommon::CompileShader(m_feature_level, m_debug_device, stage, source); + if (!bytecode.has_value()) + return {}; + + std::unique_ptr ret = CreateShaderFromBinary(stage, bytecode.value()); + if (ret && out_binary) + *out_binary = std::move(bytecode.value()); + + return ret; +} + +////////////////////////////////////////////////////////////////////////// + +D3D12Pipeline::D3D12Pipeline(Microsoft::WRL::ComPtr pipeline, Layout layout, + D3D12_PRIMITIVE_TOPOLOGY topology, u32 vertex_stride, u32 blend_constants) + : GPUPipeline(), m_pipeline(std::move(pipeline)), m_layout(layout), m_topology(topology), + m_vertex_stride(vertex_stride), m_blend_constants(blend_constants), + m_blend_constants_f(GPUDevice::RGBA8ToFloat(blend_constants)) +{ +} + +D3D12Pipeline::~D3D12Pipeline() +{ + D3D12Device::GetInstance().DeferObjectDestruction(std::move(m_pipeline)); +} + +void D3D12Pipeline::SetDebugName(const std::string_view& name) +{ + D3D12::SetObjectName(m_pipeline.Get(), name); +} + +std::unique_ptr D3D12Device::CreatePipeline(const GPUPipeline::GraphicsConfig& config) +{ + static constexpr std::array(GPUPipeline::Primitive::MaxCount)> primitives = + {{ + D3D_PRIMITIVE_TOPOLOGY_POINTLIST, // Points + D3D_PRIMITIVE_TOPOLOGY_LINELIST, // Lines + D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST, // Triangles + D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, // TriangleStrips + }}; + static constexpr std::array(GPUPipeline::Primitive::MaxCount)> + primitive_types = {{ + D3D12_PRIMITIVE_TOPOLOGY_TYPE_POINT, // Points + D3D12_PRIMITIVE_TOPOLOGY_TYPE_LINE, // Lines + D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE, // Triangles + D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE, // TriangleStrips + }}; + + static constexpr u32 MAX_COMPONENTS = 4; + static constexpr const DXGI_FORMAT + format_mapping[static_cast(GPUPipeline::VertexAttribute::Type::MaxCount)][MAX_COMPONENTS] = { + {DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_R32G32_FLOAT, DXGI_FORMAT_R32G32B32_FLOAT, + DXGI_FORMAT_R32G32B32A32_FLOAT}, // Float + {DXGI_FORMAT_R8_UINT, DXGI_FORMAT_R8G8_UINT, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R8G8B8A8_UINT}, // UInt8 + {DXGI_FORMAT_R8_SINT, DXGI_FORMAT_R8G8_SINT, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R8G8B8A8_SINT}, // SInt8 + {DXGI_FORMAT_R8_UNORM, DXGI_FORMAT_R8G8_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R8G8B8A8_UNORM}, // UNorm8 + {DXGI_FORMAT_R16_UINT, DXGI_FORMAT_R16G16_UINT, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R16G16B16A16_UINT}, // UInt16 + {DXGI_FORMAT_R16_SINT, DXGI_FORMAT_R16G16_SINT, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R16G16B16A16_SINT}, // SInt16 + {DXGI_FORMAT_R16_UNORM, DXGI_FORMAT_R16G16_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R16G16B16A16_UNORM}, // UNorm16 + {DXGI_FORMAT_R32_UINT, DXGI_FORMAT_R32G32_UINT, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R32G32B32A32_UINT}, // UInt32 + {DXGI_FORMAT_R32_SINT, DXGI_FORMAT_R32G32_SINT, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R32G32B32A32_SINT}, // SInt32 + }; + + static constexpr std::array(GPUPipeline::CullMode::MaxCount)> cull_mapping = {{ + D3D12_CULL_MODE_NONE, // None + D3D12_CULL_MODE_FRONT, // Front + D3D12_CULL_MODE_BACK, // Back + }}; + + static constexpr std::array(GPUPipeline::DepthFunc::MaxCount)> + compare_mapping = {{ + D3D12_COMPARISON_FUNC_NEVER, // Never + D3D12_COMPARISON_FUNC_ALWAYS, // Always + D3D12_COMPARISON_FUNC_LESS, // Less + D3D12_COMPARISON_FUNC_LESS_EQUAL, // LessEqual + D3D12_COMPARISON_FUNC_GREATER, // Greater + D3D12_COMPARISON_FUNC_GREATER_EQUAL, // GreaterEqual + D3D12_COMPARISON_FUNC_EQUAL, // Equal + }}; + + static constexpr std::array(GPUPipeline::BlendFunc::MaxCount)> blend_mapping = {{ + D3D12_BLEND_ZERO, // Zero + D3D12_BLEND_ONE, // One + D3D12_BLEND_SRC_COLOR, // SrcColor + D3D12_BLEND_INV_SRC_COLOR, // InvSrcColor + D3D12_BLEND_DEST_COLOR, // DstColor + D3D12_BLEND_INV_DEST_COLOR, // InvDstColor + D3D12_BLEND_SRC_ALPHA, // SrcAlpha + D3D12_BLEND_INV_SRC_ALPHA, // InvSrcAlpha + D3D12_BLEND_SRC1_ALPHA, // SrcAlpha1 + D3D12_BLEND_INV_SRC1_ALPHA, // InvSrcAlpha1 + D3D12_BLEND_DEST_ALPHA, // DstAlpha + D3D12_BLEND_INV_DEST_ALPHA, // InvDstAlpha + D3D12_BLEND_BLEND_FACTOR, // ConstantColor + D3D12_BLEND_INV_BLEND_FACTOR, // InvConstantColor + }}; + + static constexpr std::array(GPUPipeline::BlendOp::MaxCount)> op_mapping = {{ + D3D12_BLEND_OP_ADD, // Add + D3D12_BLEND_OP_SUBTRACT, // Subtract + D3D12_BLEND_OP_REV_SUBTRACT, // ReverseSubtract + D3D12_BLEND_OP_MIN, // Min + D3D12_BLEND_OP_MAX, // Max + }}; + + D3D12::GraphicsPipelineBuilder gpb; + gpb.SetRootSignature(m_root_signatures[static_cast(config.layout)].Get()); + gpb.SetVertexShader(static_cast(config.vertex_shader)->GetBytecodeData(), + static_cast(config.vertex_shader)->GetBytecodeSize()); + gpb.SetPixelShader(static_cast(config.fragment_shader)->GetBytecodeData(), + static_cast(config.fragment_shader)->GetBytecodeSize()); + gpb.SetPrimitiveTopologyType(primitive_types[static_cast(config.primitive)]); + + if (!config.input_layout.vertex_attributes.empty()) + { + for (u32 i = 0; i < static_cast(config.input_layout.vertex_attributes.size()); i++) + { + const GPUPipeline::VertexAttribute& va = config.input_layout.vertex_attributes[i]; + DebugAssert(va.components > 0 && va.components <= MAX_COMPONENTS); + gpb.AddVertexAttribute( + "ATTR", i, format_mapping[static_cast(va.type.GetValue())][static_cast(va.components.GetValue() - 1)], + 0, va.offset); + } + } + + gpb.SetRasterizationState(D3D12_FILL_MODE_SOLID, + cull_mapping[static_cast(config.rasterization.cull_mode.GetValue())], false); + if (config.samples > 1) + gpb.SetMultisamples(config.samples); + gpb.SetDepthState(config.depth.depth_test != GPUPipeline::DepthFunc::Always || config.depth.depth_write, + config.depth.depth_write, compare_mapping[static_cast(config.depth.depth_test.GetValue())]); + gpb.SetNoStencilState(); + + gpb.SetBlendState(0, config.blend.enable, blend_mapping[static_cast(config.blend.src_blend.GetValue())], + blend_mapping[static_cast(config.blend.dst_blend.GetValue())], + op_mapping[static_cast(config.blend.blend_op.GetValue())], + blend_mapping[static_cast(config.blend.src_alpha_blend.GetValue())], + blend_mapping[static_cast(config.blend.dst_alpha_blend.GetValue())], + op_mapping[static_cast(config.blend.alpha_blend_op.GetValue())], config.blend.write_mask); + + if (config.color_format != GPUTexture::Format::Unknown) + { + DXGI_FORMAT color_format; + LookupNativeFormat(config.color_format, nullptr, nullptr, &color_format, nullptr); + gpb.SetRenderTarget(0, color_format); + } + if (config.depth_format != GPUTexture::Format::Unknown) + { + DXGI_FORMAT depth_format; + LookupNativeFormat(config.depth_format, nullptr, nullptr, nullptr, &depth_format); + gpb.SetDepthStencilFormat(depth_format); + } + + /* TODO: PIPELINE CACHE */ + ComPtr pipeline = gpb.Create(m_device.Get(), false); + if (!pipeline) + return {}; + + return std::unique_ptr(new D3D12Pipeline( + pipeline, config.layout, primitives[static_cast(config.primitive)], + config.input_layout.vertex_attributes.empty() ? 0 : config.input_layout.vertex_stride, config.blend.constant)); +} diff --git a/src/core/gpu/d3d12_pipeline.h b/src/core/gpu/d3d12_pipeline.h new file mode 100644 index 000000000..12c4837ce --- /dev/null +++ b/src/core/gpu/d3d12_pipeline.h @@ -0,0 +1,63 @@ +// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin +// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) + +#include "gpu_device.h" + +#include "common/windows_headers.h" + +#include +#include +#include + +class D3D12Device; + +class D3D12Shader final : public GPUShader +{ + friend D3D12Device; + +public: + using Bytecode = std::vector; + + ~D3D12Shader() override; + + ALWAYS_INLINE const Bytecode& GetBytecode() const { return m_bytecode; } + ALWAYS_INLINE D3D12_SHADER_BYTECODE GetD3DBytecode() const { return {m_bytecode.data(), m_bytecode.size()}; } + ALWAYS_INLINE const u8* GetBytecodeData() const { return m_bytecode.data(); } + ALWAYS_INLINE u32 GetBytecodeSize() const { return static_cast(m_bytecode.size()); } + + void SetDebugName(const std::string_view& name) override; + +private: + D3D12Shader(GPUShaderStage stage, Bytecode bytecode); + + Bytecode m_bytecode; +}; + +class D3D12Pipeline final : public GPUPipeline +{ + friend D3D12Device; + +public: + ~D3D12Pipeline() override; + + ALWAYS_INLINE ID3D12PipelineState* GetPipeline() const { return m_pipeline.Get(); } + ALWAYS_INLINE Layout GetLayout() const { return m_layout; } + ALWAYS_INLINE D3D12_PRIMITIVE_TOPOLOGY GetTopology() const { return m_topology; } + ALWAYS_INLINE u32 GetVertexStride() const { return m_vertex_stride; } + ALWAYS_INLINE u32 GetBlendConstants() const { return m_blend_constants; } + ALWAYS_INLINE const std::array& GetBlendConstantsF() const { return m_blend_constants_f; } + ALWAYS_INLINE bool HasVertexStride() const { return (m_vertex_stride > 0); } + + void SetDebugName(const std::string_view& name) override; + +private: + D3D12Pipeline(Microsoft::WRL::ComPtr pipeline, Layout layout, D3D12_PRIMITIVE_TOPOLOGY topology, + u32 vertex_stride, u32 blend_constants); + + Microsoft::WRL::ComPtr m_pipeline; + Layout m_layout; + D3D12_PRIMITIVE_TOPOLOGY m_topology; + u32 m_vertex_stride; + u32 m_blend_constants; + std::array m_blend_constants_f; +}; diff --git a/src/core/gpu/d3d12/stream_buffer.cpp b/src/core/gpu/d3d12_stream_buffer.cpp similarity index 80% rename from src/core/gpu/d3d12/stream_buffer.cpp rename to src/core/gpu/d3d12_stream_buffer.cpp index 8baa765c3..c2e3f63ee 100644 --- a/src/core/gpu/d3d12/stream_buffer.cpp +++ b/src/core/gpu/d3d12_stream_buffer.cpp @@ -1,57 +1,67 @@ // SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) -// Parts originally from Dolphin Emulator, also written by myself. -#include "stream_buffer.h" +#include "d3d12_stream_buffer.h" +#include "d3d12_device.h" + #include "common/align.h" #include "common/assert.h" #include "common/log.h" -#include "context.h" + +#include "D3D12MemAlloc.h" + #include -#include -Log_SetChannel(D3D12::StreamBuffer); -namespace D3D12 { -StreamBuffer::StreamBuffer() = default; +Log_SetChannel(D3D12StreamBuffer); -StreamBuffer::~StreamBuffer() +D3D12StreamBuffer::D3D12StreamBuffer() = default; + +D3D12StreamBuffer::~D3D12StreamBuffer() { Destroy(); } -bool StreamBuffer::Create(u32 size) +bool D3D12StreamBuffer::Create(u32 size) { - static const D3D12_HEAP_PROPERTIES heap_properties = {D3D12_HEAP_TYPE_UPLOAD}; const D3D12_RESOURCE_DESC resource_desc = { D3D12_RESOURCE_DIMENSION_BUFFER, 0, size, 1, 1, 1, DXGI_FORMAT_UNKNOWN, {1, 0}, D3D12_TEXTURE_LAYOUT_ROW_MAJOR, D3D12_RESOURCE_FLAG_NONE}; - Microsoft::WRL::ComPtr buffer; + D3D12MA::ALLOCATION_DESC allocationDesc = {}; + allocationDesc.Flags = D3D12MA::ALLOCATION_FLAG_COMMITTED; + allocationDesc.HeapType = D3D12_HEAP_TYPE_UPLOAD; - HRESULT hr = g_d3d12_context->GetDevice()->CreateCommittedResource(&heap_properties, D3D12_HEAP_FLAG_NONE, - &resource_desc, D3D12_RESOURCE_STATE_GENERIC_READ, - nullptr, IID_PPV_ARGS(buffer.GetAddressOf())); - AssertMsg(SUCCEEDED(hr), "Allocate buffer"); + Microsoft::WRL::ComPtr buffer; + Microsoft::WRL::ComPtr allocation; + HRESULT hr = D3D12Device::GetInstance().GetAllocator()->CreateResource( + &allocationDesc, &resource_desc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, allocation.ReleaseAndGetAddressOf(), + IID_PPV_ARGS(buffer.GetAddressOf())); if (FAILED(hr)) + { + Log_ErrorPrintf("CreateResource() failed: %08X", hr); return false; + } static const D3D12_RANGE read_range = {}; u8* host_pointer; hr = buffer->Map(0, &read_range, reinterpret_cast(&host_pointer)); - AssertMsg(SUCCEEDED(hr), "Map buffer"); if (FAILED(hr)) + { + Log_ErrorPrintf("Map() failed: %08X", hr); return false; + } Destroy(true); m_buffer = std::move(buffer); + m_allocation = std::move(allocation); m_host_pointer = host_pointer; m_size = size; m_gpu_pointer = m_buffer->GetGPUVirtualAddress(); return true; } -bool StreamBuffer::ReserveMemory(u32 num_bytes, u32 alignment) +bool D3D12StreamBuffer::ReserveMemory(u32 num_bytes, u32 alignment) { const u32 required_bytes = num_bytes + alignment; @@ -120,15 +130,15 @@ bool StreamBuffer::ReserveMemory(u32 num_bytes, u32 alignment) return false; } -void StreamBuffer::CommitMemory(u32 final_num_bytes) +void D3D12StreamBuffer::CommitMemory(u32 final_num_bytes) { - Assert((m_current_offset + final_num_bytes) <= m_size); - Assert(final_num_bytes <= m_current_space); + DebugAssert((m_current_offset + final_num_bytes) <= m_size); + DebugAssert(final_num_bytes <= m_current_space); m_current_offset += final_num_bytes; m_current_space -= final_num_bytes; } -void StreamBuffer::Destroy(bool defer) +void D3D12StreamBuffer::Destroy(bool defer) { if (m_host_pointer) { @@ -138,8 +148,9 @@ void StreamBuffer::Destroy(bool defer) } if (m_buffer && defer) - g_d3d12_context->DeferResourceDestruction(m_buffer.Get()); + D3D12Device::GetInstance().DeferResourceDestruction(std::move(m_allocation), std::move(m_buffer)); m_buffer.Reset(); + m_allocation.Reset(); m_current_offset = 0; m_current_space = 0; @@ -147,14 +158,14 @@ void StreamBuffer::Destroy(bool defer) m_tracked_fences.clear(); } -void StreamBuffer::UpdateCurrentFencePosition() +void D3D12StreamBuffer::UpdateCurrentFencePosition() { // Don't create a tracking entry if the GPU is caught up with the buffer. if (m_current_offset == m_current_gpu_position) return; // Has the offset changed since the last fence? - const u64 fence = g_d3d12_context->GetCurrentFenceValue(); + const u64 fence = D3D12Device::GetInstance().GetCurrentFenceValue(); if (!m_tracked_fences.empty() && m_tracked_fences.back().first == fence) { // Still haven't executed a command buffer, so just update the offset. @@ -166,12 +177,12 @@ void StreamBuffer::UpdateCurrentFencePosition() m_tracked_fences.emplace_back(fence, m_current_offset); } -void StreamBuffer::UpdateGPUPosition() +void D3D12StreamBuffer::UpdateGPUPosition() { auto start = m_tracked_fences.begin(); auto end = start; - const u64 completed_counter = g_d3d12_context->GetCompletedFenceValue(); + const u64 completed_counter = D3D12Device::GetInstance().GetCompletedFenceValue(); while (end != m_tracked_fences.end() && completed_counter >= end->first) { m_current_gpu_position = end->second; @@ -182,7 +193,7 @@ void StreamBuffer::UpdateGPUPosition() m_tracked_fences.erase(start, end); } -bool StreamBuffer::WaitForClearSpace(u32 num_bytes) +bool D3D12StreamBuffer::WaitForClearSpace(u32 num_bytes) { u32 new_offset = 0; u32 new_space = 0; @@ -249,16 +260,14 @@ bool StreamBuffer::WaitForClearSpace(u32 num_bytes) // Did any fences satisfy this condition? // Has the command buffer been executed yet? If not, the caller should execute it. - if (iter == m_tracked_fences.end() || iter->first == g_d3d12_context->GetCurrentFenceValue()) + if (iter == m_tracked_fences.end() || iter->first == D3D12Device::GetInstance().GetCurrentFenceValue()) return false; // Wait until this fence is signaled. This will fire the callback, updating the GPU position. - g_d3d12_context->WaitForFence(iter->first); + D3D12Device::GetInstance().WaitForFence(iter->first); m_tracked_fences.erase(m_tracked_fences.begin(), m_current_offset == iter->second ? m_tracked_fences.end() : ++iter); m_current_offset = new_offset; m_current_space = new_space; m_current_gpu_position = new_gpu_position; return true; } - -} // namespace D3D12 diff --git a/src/core/gpu/d3d12/stream_buffer.h b/src/core/gpu/d3d12_stream_buffer.h similarity index 91% rename from src/core/gpu/d3d12/stream_buffer.h rename to src/core/gpu/d3d12_stream_buffer.h index ca2972791..314cb746c 100644 --- a/src/core/gpu/d3d12/stream_buffer.h +++ b/src/core/gpu/d3d12_stream_buffer.h @@ -1,22 +1,25 @@ // SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) -// Parts originally from Dolphin Emulator, also written by myself. #pragma once #include "common/types.h" #include "common/windows_headers.h" + #include #include #include #include -namespace D3D12 { -class StreamBuffer +namespace D3D12MA { +class Allocation; +} + +class D3D12StreamBuffer { public: - StreamBuffer(); - ~StreamBuffer(); + D3D12StreamBuffer(); + ~D3D12StreamBuffer(); bool Create(u32 size); @@ -48,11 +51,10 @@ private: u32 m_current_gpu_position = 0; Microsoft::WRL::ComPtr m_buffer; + Microsoft::WRL::ComPtr m_allocation; D3D12_GPU_VIRTUAL_ADDRESS m_gpu_pointer = {}; u8* m_host_pointer = nullptr; // List of fences and the corresponding positions in the buffer std::deque> m_tracked_fences; }; - -} // namespace D3D12 diff --git a/src/core/gpu/d3d12_texture.cpp b/src/core/gpu/d3d12_texture.cpp new file mode 100644 index 000000000..b9ff8776e --- /dev/null +++ b/src/core/gpu/d3d12_texture.cpp @@ -0,0 +1,941 @@ +// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin +// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) + +#include "d3d12_texture.h" +#include "d3d12_builders.h" +#include "d3d12_device.h" + +#include "common/align.h" +#include "common/assert.h" +#include "common/bitutils.h" +#include "common/log.h" +#include "common/string_util.h" + +#include "D3D12MemAlloc.h" + +Log_SetChannel(D3D12Device); + +void D3D12Device::LookupNativeFormat(GPUTexture::Format format, DXGI_FORMAT* d3d_format, DXGI_FORMAT* srv_format, + DXGI_FORMAT* rtv_format, DXGI_FORMAT* dsv_format) +{ + static constexpr std::array, static_cast(GPUTexture::Format::MaxCount)> + s_format_mapping = {{ + {DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN}, // Unknown + {DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, + DXGI_FORMAT_UNKNOWN}, // RGBA8 + {DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_B8G8R8A8_UNORM, + DXGI_FORMAT_UNKNOWN}, // BGRA8 + {DXGI_FORMAT_B5G6R5_UNORM, DXGI_FORMAT_B5G6R5_UNORM, DXGI_FORMAT_B5G6R5_UNORM, DXGI_FORMAT_UNKNOWN}, // RGB565 + {DXGI_FORMAT_B5G5R5A1_UNORM, DXGI_FORMAT_B5G5R5A1_UNORM, DXGI_FORMAT_B5G5R5A1_UNORM, + DXGI_FORMAT_UNKNOWN}, // RGBA5551 + {DXGI_FORMAT_R8_UNORM, DXGI_FORMAT_R8_UNORM, DXGI_FORMAT_R8_UNORM, DXGI_FORMAT_UNKNOWN}, // R8 + {DXGI_FORMAT_R16_TYPELESS, DXGI_FORMAT_R16_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_D16_UNORM}, // D16 + }}; + + const auto& mapping = s_format_mapping[static_cast(format)]; + if (d3d_format) + *d3d_format = mapping[0]; + if (srv_format) + *srv_format = mapping[1]; + if (rtv_format) + *rtv_format = mapping[2]; + if (dsv_format) + *dsv_format = mapping[3]; +} + +D3D12Texture::D3D12Texture(u32 width, u32 height, u32 layers, u32 levels, u32 samples, Type type, Format format, + DXGI_FORMAT dxgi_format, ComPtr resource, + ComPtr allocation, const D3D12DescriptorHandle& srv_descriptor, + const D3D12DescriptorHandle& write_descriptor, const D3D12DescriptorHandle& uav_descriptor, + WriteDescriptorType wdtype, D3D12_RESOURCE_STATES resource_state) + : GPUTexture(static_cast(width), static_cast(height), static_cast(layers), static_cast(levels), + static_cast(samples), type, format), + m_resource(std::move(resource)), m_allocation(std::move(allocation)), m_srv_descriptor(srv_descriptor), + m_write_descriptor(write_descriptor), m_uav_descriptor(uav_descriptor), m_dxgi_format(dxgi_format), + m_resource_state(resource_state), m_write_descriptor_type(wdtype) +{ +} + +D3D12Texture::~D3D12Texture() +{ + Destroy(true); +} + +std::unique_ptr D3D12Device::CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples, + GPUTexture::Type type, GPUTexture::Format format, + const void* data /* = nullptr */, u32 data_stride /* = 0 */, + bool dynamic /* = false */) +{ + if (!GPUTexture::ValidateConfig(width, height, layers, levels, samples, type, format)) + return {}; + + DXGI_FORMAT dxgi_format, srv_format, rtv_format, dsv_format; + LookupNativeFormat(format, &dxgi_format, &srv_format, &rtv_format, &dsv_format); + + const DXGI_FORMAT uav_format = (type == GPUTexture::Type::RWTexture) ? dxgi_format : DXGI_FORMAT_UNKNOWN; + + D3D12_RESOURCE_DESC desc = {}; + desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; + desc.Width = width; + desc.Height = height; + desc.DepthOrArraySize = 1; + desc.MipLevels = static_cast(levels); + desc.Format = dxgi_format; + desc.SampleDesc.Count = 1; + desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; + + D3D12MA::ALLOCATION_DESC allocationDesc = {}; + allocationDesc.Flags = D3D12MA::ALLOCATION_FLAG_WITHIN_BUDGET; + allocationDesc.HeapType = D3D12_HEAP_TYPE_DEFAULT; + + D3D12_CLEAR_VALUE optimized_clear_value = {}; + D3D12_RESOURCE_STATES state; + + switch (type) + { + case GPUTexture::Type::Texture: + { + desc.Flags = D3D12_RESOURCE_FLAG_NONE; + state = D3D12_RESOURCE_STATE_COPY_DEST; + } + break; + + case GPUTexture::Type::RenderTarget: + { + // RT's tend to be larger, so we'll keep them committed for speed. + DebugAssert(levels == 1); + allocationDesc.Flags |= D3D12MA::ALLOCATION_FLAG_COMMITTED; + desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET; + optimized_clear_value.Format = rtv_format; + state = D3D12_RESOURCE_STATE_RENDER_TARGET; + } + break; + + case GPUTexture::Type::DepthStencil: + { + DebugAssert(levels == 1); + allocationDesc.Flags |= D3D12MA::ALLOCATION_FLAG_COMMITTED; + desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL; + optimized_clear_value.Format = dsv_format; + state = D3D12_RESOURCE_STATE_DEPTH_WRITE; + } + break; + + case GPUTexture::Type::RWTexture: + { + DebugAssert(levels == 1); + allocationDesc.Flags |= D3D12MA::ALLOCATION_FLAG_COMMITTED; + state = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; + } + break; + + default: + return {}; + } + + if (uav_format != DXGI_FORMAT_UNKNOWN) + desc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS; + + ComPtr resource; + ComPtr allocation; + HRESULT hr = m_allocator->CreateResource( + &allocationDesc, &desc, state, + (type == GPUTexture::Type::RenderTarget || type == GPUTexture::Type::DepthStencil) ? &optimized_clear_value : + nullptr, + allocation.GetAddressOf(), IID_PPV_ARGS(resource.GetAddressOf())); + if (FAILED(hr)) + { + // OOM isn't fatal. + if (hr != E_OUTOFMEMORY) + Log_ErrorPrintf("Create texture failed: 0x%08X", hr); + + return {}; + } + + D3D12DescriptorHandle srv_descriptor, write_descriptor, uav_descriptor; + D3D12Texture::WriteDescriptorType write_descriptor_type = D3D12Texture::WriteDescriptorType::None; + if (srv_format != DXGI_FORMAT_UNKNOWN) + { + if (!CreateSRVDescriptor(resource.Get(), levels, srv_format, &srv_descriptor)) + return {}; + } + + switch (type) + { + case GPUTexture::Type::RenderTarget: + { + write_descriptor_type = D3D12Texture::WriteDescriptorType::RTV; + if (!CreateRTVDescriptor(resource.Get(), rtv_format, &write_descriptor)) + { + m_descriptor_heap_manager.Free(&srv_descriptor); + return {}; + } + } + break; + + case GPUTexture::Type::DepthStencil: + { + write_descriptor_type = D3D12Texture::WriteDescriptorType::DSV; + if (!CreateDSVDescriptor(resource.Get(), dsv_format, &write_descriptor)) + { + m_descriptor_heap_manager.Free(&srv_descriptor); + return {}; + } + } + break; + + default: + break; + } + + if (uav_format != DXGI_FORMAT_UNKNOWN && !CreateUAVDescriptor(resource.Get(), dsv_format, &uav_descriptor)) + { + m_descriptor_heap_manager.Free(&write_descriptor); + m_descriptor_heap_manager.Free(&srv_descriptor); + return {}; + } + + std::unique_ptr tex(new D3D12Texture(width, height, layers, levels, samples, type, format, dxgi_format, + std::move(resource), std::move(allocation), srv_descriptor, + write_descriptor, uav_descriptor, write_descriptor_type, state)); + + if (data) + { + tex->Update(0, 0, width, height, data, data_stride); + tex->TransitionToState(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); + } + + return tex; +} + +bool D3D12Device::CreateSRVDescriptor(ID3D12Resource* resource, u32 levels, DXGI_FORMAT format, + D3D12DescriptorHandle* dh) +{ + if (!m_descriptor_heap_manager.Allocate(dh)) + { + Log_ErrorPrint("Failed to allocate SRV descriptor"); + return false; + } + + D3D12_SHADER_RESOURCE_VIEW_DESC desc = {format, D3D12_SRV_DIMENSION_TEXTURE2D, + D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING}; + desc.Texture2D.MipLevels = levels; + + m_device->CreateShaderResourceView(resource, &desc, dh->cpu_handle); + return true; +} + +bool D3D12Device::CreateRTVDescriptor(ID3D12Resource* resource, DXGI_FORMAT format, D3D12DescriptorHandle* dh) +{ + if (!m_rtv_heap_manager.Allocate(dh)) + { + Log_ErrorPrint("Failed to allocate SRV descriptor"); + return false; + } + + const D3D12_RENDER_TARGET_VIEW_DESC desc = {format, D3D12_RTV_DIMENSION_TEXTURE2D}; + m_device->CreateRenderTargetView(resource, &desc, dh->cpu_handle); + return true; +} + +bool D3D12Device::CreateDSVDescriptor(ID3D12Resource* resource, DXGI_FORMAT format, D3D12DescriptorHandle* dh) +{ + if (!m_dsv_heap_manager.Allocate(dh)) + { + Log_ErrorPrint("Failed to allocate SRV descriptor"); + return false; + } + + const D3D12_DEPTH_STENCIL_VIEW_DESC desc = {format, D3D12_DSV_DIMENSION_TEXTURE2D, D3D12_DSV_FLAG_NONE}; + m_device->CreateDepthStencilView(resource, &desc, dh->cpu_handle); + return true; +} + +bool D3D12Device::CreateUAVDescriptor(ID3D12Resource* resource, DXGI_FORMAT format, D3D12DescriptorHandle* dh) +{ + if (!m_descriptor_heap_manager.Allocate(dh)) + { + Log_ErrorPrint("Failed to allocate UAV descriptor"); + return false; + } + + const D3D12_UNORDERED_ACCESS_VIEW_DESC desc = {format, D3D12_UAV_DIMENSION_TEXTURE2D}; + m_device->CreateUnorderedAccessView(resource, nullptr, &desc, dh->cpu_handle); + return true; +} + +void D3D12Texture::Destroy(bool defer) +{ + D3D12Device& dev = D3D12Device::GetInstance(); + dev.UnbindTexture(this); + + if (defer) + { + dev.DeferDescriptorDestruction(dev.GetDescriptorHeapManager(), &m_srv_descriptor); + + switch (m_write_descriptor_type) + { + case WriteDescriptorType::RTV: + dev.DeferDescriptorDestruction(dev.GetRTVHeapManager(), &m_write_descriptor); + break; + case WriteDescriptorType::DSV: + dev.DeferDescriptorDestruction(dev.GetDSVHeapManager(), &m_write_descriptor); + break; + case WriteDescriptorType::None: + default: + break; + } + + if (m_uav_descriptor) + dev.DeferDescriptorDestruction(dev.GetDescriptorHeapManager(), &m_uav_descriptor); + + dev.DeferResourceDestruction(std::move(m_allocation), std::move(m_resource)); + } + else + { + dev.GetDescriptorHeapManager().Free(&m_srv_descriptor); + + switch (m_write_descriptor_type) + { + case WriteDescriptorType::RTV: + dev.GetRTVHeapManager().Free(&m_write_descriptor); + break; + case WriteDescriptorType::DSV: + dev.GetDSVHeapManager().Free(&m_write_descriptor); + break; + case WriteDescriptorType::None: + default: + break; + } + + if (m_uav_descriptor) + dev.GetDescriptorHeapManager().Free(&m_uav_descriptor); + + m_resource.Reset(); + m_allocation.Reset(); + } + + m_write_descriptor_type = WriteDescriptorType::None; +} + +ID3D12GraphicsCommandList4* D3D12Texture::GetCommandBufferForUpdate() +{ + D3D12Device& dev = D3D12Device::GetInstance(); + if (m_type != Type::Texture || m_use_fence_counter == dev.GetCurrentFenceValue()) + { + // Console.WriteLn("Texture update within frame, can't use do beforehand"); + dev.EndRenderPass(); + return dev.GetCommandList(); + } + + return dev.GetInitCommandList(); +} + +void D3D12Texture::CopyTextureDataForUpload(void* dst, const void* src, u32 width, u32 height, u32 pitch, + u32 upload_pitch) const +{ + StringUtil::StrideMemCpy(dst, upload_pitch, src, pitch, GetPixelSize() * width, height); +} + +ID3D12Resource* D3D12Texture::AllocateUploadStagingBuffer(const void* data, u32 pitch, u32 upload_pitch, u32 width, + u32 height) const +{ + const u32 size = upload_pitch * height; + ComPtr resource; + ComPtr allocation; + + const D3D12MA::ALLOCATION_DESC allocation_desc = {D3D12MA::ALLOCATION_FLAG_NONE, D3D12_HEAP_TYPE_UPLOAD}; + const D3D12_RESOURCE_DESC resource_desc = { + D3D12_RESOURCE_DIMENSION_BUFFER, 0, size, 1, 1, 1, DXGI_FORMAT_UNKNOWN, {1, 0}, D3D12_TEXTURE_LAYOUT_ROW_MAJOR, + D3D12_RESOURCE_FLAG_NONE}; + HRESULT hr = D3D12Device::GetInstance().GetAllocator()->CreateResource( + &allocation_desc, &resource_desc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, allocation.GetAddressOf(), + IID_PPV_ARGS(resource.GetAddressOf())); + if (FAILED(hr)) + { + Log_ErrorPrintf("CreateResource() failed with %08X", hr); + return nullptr; + } + + void* map_ptr; + hr = resource->Map(0, nullptr, &map_ptr); + if (FAILED(hr)) + { + Log_ErrorPrintf("Map() failed with %08X", hr); + return nullptr; + } + + CopyTextureDataForUpload(map_ptr, data, width, height, pitch, upload_pitch); + + const D3D12_RANGE write_range = {0, size}; + resource->Unmap(0, &write_range); + + // Immediately queue it for freeing after the command buffer finishes, since it's only needed for the copy. + // This adds the reference needed to keep the buffer alive. + ID3D12Resource* ret = resource.Get(); + D3D12Device::GetInstance().DeferResourceDestruction(std::move(allocation), std::move(resource)); + return ret; +} + +bool D3D12Texture::Update(u32 x, u32 y, u32 width, u32 height, const void* data, u32 pitch, u32 layer, u32 level) +{ + DebugAssert(layer < m_layers && level < m_levels); + DebugAssert((x + width) <= GetMipWidth(level) && (y + height) <= GetMipHeight(level)); + + D3D12Device& dev = D3D12Device::GetInstance(); + D3D12StreamBuffer& sbuffer = dev.GetTextureUploadBuffer(); + + const u32 upload_pitch = Common::AlignUpPow2(pitch, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT); + const u32 required_size = height * upload_pitch; + + D3D12_TEXTURE_COPY_LOCATION srcloc; + srcloc.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; + srcloc.PlacedFootprint.Footprint.Width = width; + srcloc.PlacedFootprint.Footprint.Height = height; + srcloc.PlacedFootprint.Footprint.Depth = 1; + srcloc.PlacedFootprint.Footprint.Format = m_dxgi_format; + srcloc.PlacedFootprint.Footprint.RowPitch = upload_pitch; + + // If the texture is larger than half our streaming buffer size, use a separate buffer. + // Otherwise allocation will either fail, or require lots of cmdbuffer submissions. + if (required_size > (sbuffer.GetSize() / 2)) + { + srcloc.pResource = AllocateUploadStagingBuffer(data, pitch, upload_pitch, width, height); + if (!srcloc.pResource) + return false; + + srcloc.PlacedFootprint.Offset = 0; + } + else + { + if (!sbuffer.ReserveMemory(required_size, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT)) + { + D3D12Device::GetInstance().SubmitCommandList(false, "While waiting for %u bytes in texture upload buffer", + required_size); + if (!sbuffer.ReserveMemory(required_size, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT)) + { + Log_ErrorPrintf("Failed to reserve texture upload memory (%u bytes).", required_size); + return false; + } + } + + srcloc.pResource = sbuffer.GetBuffer(); + srcloc.PlacedFootprint.Offset = sbuffer.GetCurrentOffset(); + CopyTextureDataForUpload(sbuffer.GetCurrentHostPointer(), data, width, height, pitch, upload_pitch); + sbuffer.CommitMemory(required_size); + } + + ID3D12GraphicsCommandList4* cmdlist = GetCommandBufferForUpdate(); + + // if we're an rt and have been cleared, and the full rect isn't being uploaded, do the clear + if (m_type == Type::RenderTarget) + { + if (x != 0 || y != 0 || width != m_width || height != m_height) + CommitClear(cmdlist); + else + m_state = State::Dirty; + } + + // first time the texture is used? don't leave it undefined + if (m_resource_state == D3D12_RESOURCE_STATE_COMMON) + TransitionToState(cmdlist, D3D12_RESOURCE_STATE_COPY_DEST); + else if (m_resource_state != D3D12_RESOURCE_STATE_COPY_DEST) + TransitionSubresourceToState(cmdlist, layer, level, m_resource_state, D3D12_RESOURCE_STATE_COPY_DEST); + + D3D12_TEXTURE_COPY_LOCATION dstloc; + dstloc.pResource = m_resource.Get(); + dstloc.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; + dstloc.SubresourceIndex = layer; + + const D3D12_BOX srcbox{0u, 0u, 0u, width, height, 1u}; + cmdlist->CopyTextureRegion(&dstloc, x, y, 0, &srcloc, &srcbox); + + if (m_resource_state != D3D12_RESOURCE_STATE_COPY_DEST) + TransitionSubresourceToState(cmdlist, layer, level, D3D12_RESOURCE_STATE_COPY_DEST, m_resource_state); + + return true; +} + +bool D3D12Texture::Map(void** map, u32* map_stride, u32 x, u32 y, u32 width, u32 height, u32 layer, u32 level) +{ + // TODO: linear textures for dynamic? + if ((x + width) > GetMipWidth(level) || (y + height) > GetMipHeight(level) || layer > m_layers || level > m_levels) + { + return false; + } + + D3D12Device& dev = D3D12Device::GetInstance(); + if (m_state == State::Cleared && (x != 0 || y != 0 || width != m_width || height != m_height)) + CommitClear(GetCommandBufferForUpdate()); + + // see note in Update() for the reason why. + const u32 aligned_pitch = Common::AlignUpPow2(width * GetPixelSize(), D3D12_TEXTURE_DATA_PITCH_ALIGNMENT); + const u32 req_size = height * aligned_pitch; + D3D12StreamBuffer& buffer = dev.GetTextureUploadBuffer(); + if (req_size >= (buffer.GetSize() / 2)) + return false; + + if (!buffer.ReserveMemory(req_size, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT)) + { + dev.SubmitCommandList(false, "While waiting for %u bytes in texture upload buffer", req_size); + if (!buffer.ReserveMemory(req_size, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT)) + Panic("Failed to reserve texture upload memory"); + } + + // map for writing + *map = buffer.GetCurrentHostPointer(); + *map_stride = aligned_pitch; + m_map_x = static_cast(x); + m_map_y = static_cast(y); + m_map_width = static_cast(width); + m_map_height = static_cast(height); + m_map_layer = static_cast(layer); + m_map_level = static_cast(level); + m_state = State::Dirty; + return true; +} + +void D3D12Texture::Unmap() +{ + D3D12Device& dev = D3D12Device::GetInstance(); + D3D12StreamBuffer& sb = dev.GetTextureUploadBuffer(); + const u32 aligned_pitch = Common::AlignUpPow2(m_map_width * GetPixelSize(), D3D12_TEXTURE_DATA_PITCH_ALIGNMENT); + const u32 req_size = m_map_height * aligned_pitch; + const u32 offset = sb.GetCurrentOffset(); + sb.CommitMemory(req_size); + + ID3D12GraphicsCommandList4* cmdlist = GetCommandBufferForUpdate(); + + // first time the texture is used? don't leave it undefined + if (m_resource_state == D3D12_RESOURCE_STATE_COMMON) + TransitionToState(cmdlist, D3D12_RESOURCE_STATE_COPY_DEST); + else if (m_resource_state != D3D12_RESOURCE_STATE_COPY_DEST) + TransitionSubresourceToState(cmdlist, m_map_layer, m_map_level, m_resource_state, D3D12_RESOURCE_STATE_COPY_DEST); + + D3D12_TEXTURE_COPY_LOCATION srcloc; + srcloc.pResource = sb.GetBuffer(); + srcloc.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; + srcloc.PlacedFootprint.Offset = offset; + srcloc.PlacedFootprint.Footprint.Width = m_map_width; + srcloc.PlacedFootprint.Footprint.Height = m_map_height; + srcloc.PlacedFootprint.Footprint.Depth = 1; + srcloc.PlacedFootprint.Footprint.Format = m_dxgi_format; + srcloc.PlacedFootprint.Footprint.RowPitch = aligned_pitch; + + D3D12_TEXTURE_COPY_LOCATION dstloc; + dstloc.pResource = m_resource.Get(); + dstloc.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; + dstloc.SubresourceIndex = m_map_level; + + const D3D12_BOX srcbox{0u, 0u, 0u, m_map_width, m_map_height, 1}; + cmdlist->CopyTextureRegion(&dstloc, m_map_x, m_map_y, 0, &srcloc, &srcbox); + + if (m_resource_state != D3D12_RESOURCE_STATE_COPY_DEST) + TransitionSubresourceToState(cmdlist, m_map_layer, m_map_level, D3D12_RESOURCE_STATE_COPY_DEST, m_resource_state); + + m_map_x = 0; + m_map_y = 0; + m_map_width = 0; + m_map_height = 0; + m_map_layer = 0; + m_map_level = 0; +} + +void D3D12Texture::CommitClear() +{ + if (m_state != GPUTexture::State::Cleared) + return; + + D3D12Device& dev = D3D12Device::GetInstance(); + dev.EndRenderPass(); + + CommitClear(dev.GetCommandList()); +} + +void D3D12Texture::CommitClear(ID3D12GraphicsCommandList* cmdlist) +{ + if (IsDepthStencil()) + { + TransitionToState(cmdlist, D3D12_RESOURCE_STATE_DEPTH_WRITE); + cmdlist->ClearDepthStencilView(GetWriteDescriptor(), D3D12_CLEAR_FLAG_DEPTH, m_clear_value.depth, 0, 0, nullptr); + } + else + { + TransitionToState(cmdlist, D3D12_RESOURCE_STATE_RENDER_TARGET); + cmdlist->ClearRenderTargetView(GetWriteDescriptor(), D3D12Device::RGBA8ToFloat(m_clear_value.color).data(), 0, + nullptr); + } + + SetState(State::Dirty); +} + +void D3D12Texture::SetDebugName(const std::string_view& name) +{ + D3D12::SetObjectName(m_resource.Get(), name); +} + +u32 D3D12Texture::CalculateSubresource(u32 layer, u32 level, u32 num_levels) +{ + // D3D11CalcSubresource + return level + layer * num_levels; +} + +u32 D3D12Texture::CalculateSubresource(u32 layer, u32 level) const +{ + return CalculateSubresource(layer, level, m_levels); +} + +void D3D12Texture::TransitionToState(D3D12_RESOURCE_STATES state) +{ + TransitionToState(D3D12Device::GetInstance().GetCommandList(), state); +} + +void D3D12Texture::TransitionToState(ID3D12GraphicsCommandList* cmdlist, D3D12_RESOURCE_STATES state) +{ + if (m_resource_state == state) + return; + + const D3D12_RESOURCE_STATES prev_state = m_resource_state; + m_resource_state = state; + + const D3D12_RESOURCE_BARRIER barrier = { + D3D12_RESOURCE_BARRIER_TYPE_TRANSITION, + D3D12_RESOURCE_BARRIER_FLAG_NONE, + {{m_resource.Get(), D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, prev_state, state}}}; + cmdlist->ResourceBarrier(1, &barrier); +} + +void D3D12Texture::TransitionSubresourceToState(ID3D12GraphicsCommandList* cmdlist, u32 layer, u32 level, + D3D12_RESOURCE_STATES before_state, + D3D12_RESOURCE_STATES after_state) const +{ + TransitionSubresourceToState(cmdlist, m_resource.Get(), CalculateSubresource(layer, level), before_state, + after_state); +} + +void D3D12Texture::TransitionSubresourceToState(ID3D12GraphicsCommandList* cmdlist, u32 subresource, + D3D12_RESOURCE_STATES before_state, + D3D12_RESOURCE_STATES after_state) const +{ + TransitionSubresourceToState(cmdlist, m_resource.Get(), subresource, before_state, after_state); +} + +void D3D12Texture::TransitionSubresourceToState(ID3D12GraphicsCommandList* cmdlist, ID3D12Resource* resource, + u32 subresource, D3D12_RESOURCE_STATES before_state, + D3D12_RESOURCE_STATES after_state) +{ + const D3D12_RESOURCE_BARRIER barrier = {D3D12_RESOURCE_BARRIER_TYPE_TRANSITION, + D3D12_RESOURCE_BARRIER_FLAG_NONE, + {{resource, subresource, before_state, after_state}}}; + cmdlist->ResourceBarrier(1, &barrier); +} + +void D3D12Texture::MakeReadyForSampling() +{ + if (m_resource_state == D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE) + return; + + D3D12Device& dev = D3D12Device::GetInstance(); + if (dev.InRenderPass()) + dev.EndRenderPass(); + + TransitionToState(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); +} + +bool D3D12Device::DownloadTexture(GPUTexture* texture, u32 x, u32 y, u32 width, u32 height, void* out_data, + u32 out_data_stride) +{ + D3D12Texture* T = static_cast(texture); + T->CommitClear(); + + const u32 pitch = Common::AlignUp(width * T->GetPixelSize(), D3D12_TEXTURE_DATA_PITCH_ALIGNMENT); + const u32 size = pitch * height; + const u32 subresource = 0; + if (!CheckDownloadBufferSize(size)) + { + Log_ErrorPrintf("Can't read back %ux%u", width, height); + return false; + } + + if (InRenderPass()) + EndRenderPass(); + + ID3D12GraphicsCommandList4* cmdlist = GetCommandList(); + + D3D12_TEXTURE_COPY_LOCATION srcloc; + srcloc.pResource = T->GetResource(); + srcloc.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; + srcloc.SubresourceIndex = subresource; + + D3D12_TEXTURE_COPY_LOCATION dstloc; + dstloc.pResource = m_download_buffer.Get(); + dstloc.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; + dstloc.PlacedFootprint.Offset = 0; + dstloc.PlacedFootprint.Footprint.Format = T->GetDXGIFormat(); + dstloc.PlacedFootprint.Footprint.Width = width; + dstloc.PlacedFootprint.Footprint.Height = height; + dstloc.PlacedFootprint.Footprint.Depth = 1; + dstloc.PlacedFootprint.Footprint.RowPitch = pitch; + + const D3D12_RESOURCE_STATES old_layout = T->GetResourceState(); + if (old_layout != D3D12_RESOURCE_STATE_COPY_SOURCE) + T->TransitionSubresourceToState(cmdlist, subresource, old_layout, D3D12_RESOURCE_STATE_COPY_SOURCE); + + // TODO: Rules for depth buffers here? + const D3D12_BOX srcbox{static_cast(x), static_cast(y), 0u, + static_cast(x + width), static_cast(y + height), 1u}; + cmdlist->CopyTextureRegion(&dstloc, 0, 0, 0, &srcloc, &srcbox); + + if (old_layout != D3D12_RESOURCE_STATE_COPY_SOURCE) + T->TransitionSubresourceToState(cmdlist, subresource, D3D12_RESOURCE_STATE_COPY_SOURCE, old_layout); + + SubmitCommandList(true); + + u8* map_pointer; + const D3D12_RANGE read_range{0u, size}; + const HRESULT hr = m_download_buffer->Map(0, &read_range, reinterpret_cast(const_cast(&map_pointer))); + if (FAILED(hr)) + { + Log_ErrorPrintf("Map() failed with HRESULT %08X", hr); + return false; + } + + StringUtil::StrideMemCpy(out_data, out_data_stride, map_pointer, pitch, width * T->GetPixelSize(), height); + m_download_buffer->Unmap(0, nullptr); + return true; +} + +bool D3D12Device::CheckDownloadBufferSize(u32 required_size) +{ + if (m_download_buffer_size >= required_size) + return true; + + DestroyDownloadBuffer(); + + D3D12MA::ALLOCATION_DESC allocation_desc = {}; + allocation_desc.HeapType = D3D12_HEAP_TYPE_READBACK; + + const D3D12_RESOURCE_DESC resource_desc = {D3D12_RESOURCE_DIMENSION_BUFFER, + 0, + required_size, + 1, + 1, + 1, + DXGI_FORMAT_UNKNOWN, + {1, 0}, + D3D12_TEXTURE_LAYOUT_ROW_MAJOR, + D3D12_RESOURCE_FLAG_NONE}; + + HRESULT hr = m_allocator->CreateResource(&allocation_desc, &resource_desc, D3D12_RESOURCE_STATE_COPY_DEST, nullptr, + m_download_buffer_allocation.ReleaseAndGetAddressOf(), + IID_PPV_ARGS(m_download_buffer.ReleaseAndGetAddressOf())); + if (FAILED(hr)) + { + Log_ErrorPrintf("CreateResource() failed with HRESULT %08X", hr); + return false; + } + + return true; +} + +void D3D12Device::DestroyDownloadBuffer() +{ + if (!m_download_buffer) + return; + + m_download_buffer.Reset(); + m_download_buffer_allocation.Reset(); + m_download_buffer_size = 0; +} + +D3D12Sampler::D3D12Sampler(D3D12DescriptorHandle descriptor) : m_descriptor(descriptor) +{ +} + +D3D12Sampler::~D3D12Sampler() +{ + // Cleaned up by main class. +} + +void D3D12Sampler::SetDebugName(const std::string_view& name) +{ +} + +D3D12DescriptorHandle D3D12Device::GetSampler(const GPUSampler::Config& config) +{ + const auto it = m_sampler_map.find(config.key); + if (it != m_sampler_map.end()) + return it->second; + + static constexpr std::array(GPUSampler::AddressMode::MaxCount)> ta = {{ + D3D12_TEXTURE_ADDRESS_MODE_WRAP, // Repeat + D3D12_TEXTURE_ADDRESS_MODE_CLAMP, // ClampToEdge + D3D12_TEXTURE_ADDRESS_MODE_BORDER, // ClampToBorder + }}; + + static constexpr u8 filter_count = static_cast(GPUSampler::Filter::MaxCount); + static constexpr D3D12_FILTER filters[filter_count][filter_count][filter_count] = { + { + {D3D12_FILTER_MIN_MAG_MIP_POINT, D3D12_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT}, + {D3D12_FILTER_MIN_LINEAR_MAG_MIP_POINT, D3D12_FILTER_MIN_MAG_LINEAR_MIP_POINT}, + }, + { + {D3D12_FILTER_MIN_MAG_POINT_MIP_LINEAR, D3D12_FILTER_MIN_POINT_MAG_MIP_LINEAR}, + {D3D12_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR, D3D12_FILTER_MIN_MAG_MIP_LINEAR}, + }}; + + D3D12_SAMPLER_DESC desc = {}; + desc.AddressU = ta[static_cast(config.address_u.GetValue())]; + desc.AddressV = ta[static_cast(config.address_v.GetValue())]; + desc.AddressW = ta[static_cast(config.address_w.GetValue())]; + std::memcpy(desc.BorderColor, RGBA8ToFloat(config.border_color).data(), sizeof(desc.BorderColor)); + desc.MinLOD = static_cast(config.min_lod); + desc.MaxLOD = static_cast(config.max_lod); + + if (config.anisotropy > 0) + { + desc.Filter = D3D12_FILTER_ANISOTROPIC; + desc.MaxAnisotropy = config.anisotropy; + } + else + { + desc.Filter = filters[static_cast(config.mip_filter.GetValue())][static_cast(config.min_filter.GetValue())] + [static_cast(config.mag_filter.GetValue())]; + desc.MaxAnisotropy = 1; + } + + // TODO: Allocate + D3D12DescriptorHandle handle; + if (m_sampler_heap_manager.Allocate(&handle)) + m_device->CreateSampler(&desc, handle); + + m_sampler_map.emplace(config.key, handle); + return handle; +} + +void D3D12Device::DestroySamplers() +{ + for (auto& it : m_sampler_map) + { + if (it.second) + m_sampler_heap_manager.Free(&it.second); + } + m_sampler_map.clear(); +} + +std::unique_ptr D3D12Device::CreateSampler(const GPUSampler::Config& config) +{ + const D3D12DescriptorHandle handle = GetSampler(config); + if (!handle) + return {}; + + return std::unique_ptr(new D3D12Sampler(std::move(handle))); +} + +D3D12Framebuffer::D3D12Framebuffer(GPUTexture* rt, GPUTexture* ds, u32 width, u32 height, D3D12DescriptorHandle rtv, + D3D12DescriptorHandle dsv) + : GPUFramebuffer(rt, ds, width, height), m_rtv(std::move(rtv)), m_dsv(std::move(dsv)) +{ +} + +D3D12Framebuffer::~D3D12Framebuffer() +{ + D3D12Device& dev = D3D12Device::GetInstance(); + if (m_rtv) + D3D12Device::GetInstance().DeferDescriptorDestruction(dev.GetRTVHeapManager(), &m_rtv); + if (m_dsv) + D3D12Device::GetInstance().DeferDescriptorDestruction(dev.GetDSVHeapManager(), &m_dsv); +} + +void D3D12Framebuffer::SetDebugName(const std::string_view& name) +{ +} + +std::unique_ptr D3D12Device::CreateFramebuffer(GPUTexture* rt_or_ds, GPUTexture* ds /*= nullptr*/) +{ + DebugAssert((rt_or_ds || ds) && (!rt_or_ds || rt_or_ds->IsRenderTarget() || (rt_or_ds->IsDepthStencil() && !ds))); + D3D12Texture* RT = static_cast((rt_or_ds && rt_or_ds->IsDepthStencil()) ? nullptr : rt_or_ds); + D3D12Texture* DS = static_cast((rt_or_ds && rt_or_ds->IsDepthStencil()) ? rt_or_ds : ds); + + const u32 width = RT ? RT->GetWidth() : DS->GetWidth(); + const u32 height = RT ? RT->GetHeight() : DS->GetHeight(); + + D3D12DescriptorHandle rtv, dsv; + if (RT) + rtv = RT->GetWriteDescriptor(); + if (DS) + dsv = DS->GetWriteDescriptor(); + + return std::unique_ptr(new D3D12Framebuffer(RT, DS, width, height, std::move(rtv), std::move(dsv))); +} + +D3D12TextureBuffer::D3D12TextureBuffer(Format format, u32 size_in_elements) : GPUTextureBuffer(format, size_in_elements) +{ +} + +D3D12TextureBuffer::~D3D12TextureBuffer() +{ + Destroy(true); +} + +bool D3D12TextureBuffer::Create(D3D12Device& dev) +{ + static constexpr std::array(GPUTextureBuffer::Format::MaxCount)> format_mapping = {{ + DXGI_FORMAT_R16_UINT, // R16UI + }}; + + if (!m_buffer.Create(GetSizeInBytes())) + return false; + + if (!dev.GetDescriptorHeapManager().Allocate(&m_descriptor)) + return {}; + + D3D12_SHADER_RESOURCE_VIEW_DESC desc = {format_mapping[static_cast(m_format)], D3D12_SRV_DIMENSION_BUFFER, + D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING}; + desc.Buffer.NumElements = m_size_in_elements; + dev.GetDevice()->CreateShaderResourceView(m_buffer.GetBuffer(), &desc, m_descriptor); + return true; +} + +void D3D12TextureBuffer::Destroy(bool defer) +{ + D3D12Device& dev = D3D12Device::GetInstance(); + if (m_descriptor) + { + if (defer) + dev.DeferDescriptorDestruction(dev.GetDescriptorHeapManager(), &m_descriptor); + else + dev.GetDescriptorHeapManager().Free(&m_descriptor); + } +} + +void* D3D12TextureBuffer::Map(u32 required_elements) +{ + const u32 esize = GetElementSize(m_format); + const u32 req_size = esize * required_elements; + if (!m_buffer.ReserveMemory(req_size, esize)) + { + D3D12Device::GetInstance().SubmitCommandListAndRestartRenderPass("out of space in texture buffer"); + if (!m_buffer.ReserveMemory(req_size, esize)) + Panic("Failed to allocate texture buffer space."); + } + + m_current_position = m_buffer.GetCurrentOffset() / esize; + return m_buffer.GetCurrentHostPointer(); +} + +void D3D12TextureBuffer::Unmap(u32 used_elements) +{ + m_buffer.CommitMemory(GetElementSize(m_format) * used_elements); +} + +std::unique_ptr D3D12Device::CreateTextureBuffer(GPUTextureBuffer::Format format, + u32 size_in_elements) +{ + + std::unique_ptr tb = std::make_unique(format, size_in_elements); + if (!tb->Create(*this)) + tb.reset(); + + return tb; +} diff --git a/src/core/gpu/d3d12_texture.h b/src/core/gpu/d3d12_texture.h new file mode 100644 index 000000000..2b8bbdfe4 --- /dev/null +++ b/src/core/gpu/d3d12_texture.h @@ -0,0 +1,166 @@ +// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin +// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) + +#pragma once + +#include "d3d12_descriptor_heap_manager.h" +#include "d3d12_stream_buffer.h" +#include "gpu_device.h" +#include "gpu_texture.h" + +#include +#include +#include + +namespace D3D12MA { +class Allocation; +} + +class D3D12Device; + +class D3D12Texture final : public GPUTexture +{ + friend D3D12Device; + +public: + template + using ComPtr = Microsoft::WRL::ComPtr; + + ~D3D12Texture() override; + + void Destroy(bool defer); + + ALWAYS_INLINE const D3D12DescriptorHandle& GetSRVDescriptor() const { return m_srv_descriptor; } + ALWAYS_INLINE const D3D12DescriptorHandle& GetWriteDescriptor() const { return m_write_descriptor; } + ALWAYS_INLINE const D3D12DescriptorHandle& GetUAVDescriptor() const { return m_uav_descriptor; } + ALWAYS_INLINE D3D12_RESOURCE_STATES GetResourceState() const { return m_resource_state; } + ALWAYS_INLINE DXGI_FORMAT GetDXGIFormat() const { return m_dxgi_format; } + ALWAYS_INLINE ID3D12Resource* GetResource() const { return m_resource.Get(); } + + bool IsValid() const override { return static_cast(m_resource); } + bool Update(u32 x, u32 y, u32 width, u32 height, const void* data, u32 pitch, u32 layer = 0, u32 level = 0) override; + bool Map(void** map, u32* map_stride, u32 x, u32 y, u32 width, u32 height, u32 layer = 0, u32 level = 0) override; + void Unmap() override; + void MakeReadyForSampling() override; + + void SetDebugName(const std::string_view& name) override; + + void TransitionToState(D3D12_RESOURCE_STATES state); + void CommitClear(); + void CommitClear(ID3D12GraphicsCommandList* cmdlist); + + static u32 CalculateSubresource(u32 layer, u32 level, u32 num_levels); + u32 CalculateSubresource(u32 layer, u32 level) const; + + void TransitionToState(ID3D12GraphicsCommandList* cmdlist, D3D12_RESOURCE_STATES state); + void TransitionSubresourceToState(ID3D12GraphicsCommandList* cmdlist, u32 layer, u32 level, + D3D12_RESOURCE_STATES before_state, D3D12_RESOURCE_STATES after_state) const; + void TransitionSubresourceToState(ID3D12GraphicsCommandList* cmdlist, u32 subresource, + D3D12_RESOURCE_STATES before_state, D3D12_RESOURCE_STATES after_state) const; + static void TransitionSubresourceToState(ID3D12GraphicsCommandList* cmdlist, ID3D12Resource* resource, + u32 subresource, D3D12_RESOURCE_STATES before_state, + D3D12_RESOURCE_STATES after_state); + + // Call when the texture is bound to the pipeline, or read from in a copy. + ALWAYS_INLINE void SetUseFenceValue(u64 counter) { m_use_fence_counter = counter; } + +private: + enum class WriteDescriptorType : u8 + { + None, + RTV, + DSV + }; + + D3D12Texture(u32 width, u32 height, u32 layers, u32 levels, u32 samples, Type type, Format format, + DXGI_FORMAT dxgi_format, ComPtr resource, ComPtr allocation, + const D3D12DescriptorHandle& srv_descriptor, const D3D12DescriptorHandle& write_descriptor, + const D3D12DescriptorHandle& uav_descriptor, WriteDescriptorType wdtype, + D3D12_RESOURCE_STATES resource_state); + + ID3D12GraphicsCommandList4* GetCommandBufferForUpdate(); + ID3D12Resource* AllocateUploadStagingBuffer(const void* data, u32 pitch, u32 upload_pitch, u32 width, + u32 height) const; + void CopyTextureDataForUpload(void* dst, const void* src, u32 width, u32 height, u32 pitch, u32 upload_pitch) const; + + ComPtr m_resource; + ComPtr m_allocation; + + D3D12DescriptorHandle m_srv_descriptor = {}; + D3D12DescriptorHandle m_write_descriptor = {}; + D3D12DescriptorHandle m_uav_descriptor = {}; + + DXGI_FORMAT m_dxgi_format = DXGI_FORMAT_UNKNOWN; + D3D12_RESOURCE_STATES m_resource_state = D3D12_RESOURCE_STATE_COMMON; + WriteDescriptorType m_write_descriptor_type = WriteDescriptorType::None; + + // Contains the fence counter when the texture was last used. + // When this matches the current fence counter, the texture was used this command buffer. + u64 m_use_fence_counter = 0; + + u16 m_map_x = 0; + u16 m_map_y = 0; + u16 m_map_width = 0; + u16 m_map_height = 0; + u8 m_map_layer = 0; + u8 m_map_level = 0; +}; + +class D3D12Sampler final : public GPUSampler +{ + friend D3D12Device; + +public: + ~D3D12Sampler() override; + + ALWAYS_INLINE const D3D12DescriptorHandle& GetDescriptor() const { return m_descriptor; } + + void SetDebugName(const std::string_view& name) override; + +private: + D3D12Sampler(D3D12DescriptorHandle descriptor); + + D3D12DescriptorHandle m_descriptor; +}; + +class D3D12Framebuffer final : public GPUFramebuffer +{ + friend D3D12Device; + +public: + ~D3D12Framebuffer() override; + + ALWAYS_INLINE const D3D12DescriptorHandle& GetRTV() const { return m_rtv; } + ALWAYS_INLINE const D3D12DescriptorHandle& GetDSV() const { return m_dsv; } + + void SetDebugName(const std::string_view& name) override; + +private: + D3D12Framebuffer(GPUTexture* rt, GPUTexture* ds, u32 width, u32 height, D3D12DescriptorHandle rtv, + D3D12DescriptorHandle dsv); + + D3D12DescriptorHandle m_rtv; + D3D12DescriptorHandle m_dsv; +}; + +class D3D12TextureBuffer final : public GPUTextureBuffer +{ + friend D3D12Device; + +public: + D3D12TextureBuffer(Format format, u32 size_in_elements); + ~D3D12TextureBuffer() override; + + ALWAYS_INLINE const D3D12DescriptorHandle& GetDescriptor() const { return m_descriptor; } + + bool Create(D3D12Device& dev); + void Destroy(bool defer); + + // Inherited via GPUTextureBuffer + void* Map(u32 required_elements) override; + void Unmap(u32 used_elements) override; + +private: + D3D12StreamBuffer m_buffer; + D3D12DescriptorHandle m_descriptor; +}; diff --git a/src/core/gpu/d3d_common.cpp b/src/core/gpu/d3d_common.cpp new file mode 100644 index 000000000..218d8fd05 --- /dev/null +++ b/src/core/gpu/d3d_common.cpp @@ -0,0 +1,388 @@ +// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin +// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) + +#include "d3d_common.h" + +#include "common/file_system.h" +#include "common/log.h" +#include "common/rectangle.h" +#include "common/string_util.h" + +#include "fmt/format.h" + +#include +#include + +Log_SetChannel(D3DCommon); + +static unsigned s_next_bad_shader_id = 1; + +Microsoft::WRL::ComPtr D3DCommon::CreateFactory(bool debug) +{ + UINT flags = 0; + if (debug) + flags |= DXGI_CREATE_FACTORY_DEBUG; + + Microsoft::WRL::ComPtr factory; + const HRESULT hr = CreateDXGIFactory2(flags, IID_PPV_ARGS(factory.GetAddressOf())); + if (FAILED(hr)) + Log_ErrorPrintf("Failed to create DXGI factory: %08X", hr); + + return factory; +} + +static std::string FixupDuplicateAdapterNames(const std::vector& adapter_names, std::string adapter_name) +{ + if (std::any_of(adapter_names.begin(), adapter_names.end(), + [&adapter_name](const std::string& other) { return (adapter_name == other); })) + { + std::string original_adapter_name = std::move(adapter_name); + + u32 current_extra = 2; + do + { + adapter_name = fmt::format("{} ({})", original_adapter_name.c_str(), current_extra); + current_extra++; + } while (std::any_of(adapter_names.begin(), adapter_names.end(), + [&adapter_name](const std::string& other) { return (adapter_name == other); })); + } + + return adapter_name; +} + +std::vector D3DCommon::GetAdapterNames(IDXGIFactory5* factory) +{ + std::vector adapter_names; + + Microsoft::WRL::ComPtr adapter; + for (u32 index = 0;; index++) + { + const HRESULT hr = factory->EnumAdapters1(index, adapter.ReleaseAndGetAddressOf()); + if (hr == DXGI_ERROR_NOT_FOUND) + break; + + if (FAILED(hr)) + { + Log_ErrorPrintf("IDXGIFactory2::EnumAdapters() returned %08X", hr); + continue; + } + + adapter_names.push_back(FixupDuplicateAdapterNames(adapter_names, GetAdapterName(adapter.Get()))); + } + + return adapter_names; +} + +std::vector D3DCommon::GetFullscreenModes(IDXGIFactory5* factory, const std::string_view& adapter_name) +{ + std::vector modes; + HRESULT hr; + + Microsoft::WRL::ComPtr adapter = GetChosenOrFirstAdapter(factory, adapter_name); + if (!adapter) + return modes; + + Microsoft::WRL::ComPtr output; + if (FAILED(hr = adapter->EnumOutputs(0, &output))) + { + Log_ErrorPrintf("EnumOutputs() failed: %08X", hr); + return modes; + } + + UINT num_modes = 0; + if (FAILED(hr = output->GetDisplayModeList(DXGI_FORMAT_R8G8B8A8_UNORM, 0, &num_modes, nullptr))) + { + Log_ErrorPrintf("GetDisplayModeList() failed: %08X", hr); + return modes; + } + + std::vector dmodes(num_modes); + if (FAILED(hr = output->GetDisplayModeList(DXGI_FORMAT_R8G8B8A8_UNORM, 0, &num_modes, dmodes.data()))) + { + Log_ErrorPrintf("GetDisplayModeList() (2) failed: %08X", hr); + return modes; + } + + for (const DXGI_MODE_DESC& mode : dmodes) + { + modes.push_back(GPUDevice::GetFullscreenModeString(mode.Width, mode.Height, + static_cast(mode.RefreshRate.Numerator) / + static_cast(mode.RefreshRate.Denominator))); + } + + return modes; +} + +bool D3DCommon::GetRequestedExclusiveFullscreenModeDesc(IDXGIFactory5* factory, const RECT& window_rect, u32 width, + u32 height, float refresh_rate, DXGI_FORMAT format, + DXGI_MODE_DESC* fullscreen_mode, IDXGIOutput** output) +{ + // We need to find which monitor the window is located on. + const Common::Rectangle client_rc_vec(window_rect.left, window_rect.top, window_rect.right, window_rect.bottom); + + // The window might be on a different adapter to which we are rendering.. so we have to enumerate them all. + HRESULT hr; + Microsoft::WRL::ComPtr first_output, intersecting_output; + + for (u32 adapter_index = 0; !intersecting_output; adapter_index++) + { + Microsoft::WRL::ComPtr adapter; + hr = factory->EnumAdapters1(adapter_index, adapter.GetAddressOf()); + if (hr == DXGI_ERROR_NOT_FOUND) + break; + else if (FAILED(hr)) + continue; + + for (u32 output_index = 0;; output_index++) + { + Microsoft::WRL::ComPtr this_output; + DXGI_OUTPUT_DESC output_desc; + hr = adapter->EnumOutputs(output_index, this_output.GetAddressOf()); + if (hr == DXGI_ERROR_NOT_FOUND) + break; + else if (FAILED(hr) || FAILED(this_output->GetDesc(&output_desc))) + continue; + + const Common::Rectangle output_rc(output_desc.DesktopCoordinates.left, output_desc.DesktopCoordinates.top, + output_desc.DesktopCoordinates.right, + output_desc.DesktopCoordinates.bottom); + if (!client_rc_vec.Intersects(output_rc)) + { + intersecting_output = std::move(this_output); + break; + } + + // Fallback to the first monitor. + if (!first_output) + first_output = std::move(this_output); + } + } + + if (!intersecting_output) + { + if (!first_output) + { + Log_ErrorPrintf("No DXGI output found. Can't use exclusive fullscreen."); + return false; + } + + Log_WarningPrint("No DXGI output found for window, using first."); + intersecting_output = std::move(first_output); + } + + DXGI_MODE_DESC request_mode = {}; + request_mode.Width = width; + request_mode.Height = height; + request_mode.Format = format; + request_mode.RefreshRate.Numerator = static_cast(std::floor(refresh_rate * 1000.0f)); + request_mode.RefreshRate.Denominator = 1000u; + + if (FAILED(hr = intersecting_output->FindClosestMatchingMode(&request_mode, fullscreen_mode, nullptr)) || + request_mode.Format != format) + { + Log_ErrorPrintf("Failed to find closest matching mode, hr=%08X", hr); + return false; + } + + *output = intersecting_output.Get(); + intersecting_output->AddRef(); + return true; +} + +Microsoft::WRL::ComPtr D3DCommon::GetAdapterByName(IDXGIFactory5* factory, const std::string_view& name) +{ + if (name.empty()) + return {}; + + // This might seem a bit odd to cache the names.. but there's a method to the madness. + // We might have two GPUs with the same name... :) + std::vector adapter_names; + + Microsoft::WRL::ComPtr adapter; + for (u32 index = 0;; index++) + { + const HRESULT hr = factory->EnumAdapters1(index, adapter.ReleaseAndGetAddressOf()); + if (hr == DXGI_ERROR_NOT_FOUND) + break; + + if (FAILED(hr)) + { + Log_ErrorPrintf("IDXGIFactory2::EnumAdapters() returned %08X"); + continue; + } + + std::string adapter_name = FixupDuplicateAdapterNames(adapter_names, GetAdapterName(adapter.Get())); + if (adapter_name == name) + { + Log_VerbosePrintf("Found adapter '%s'", adapter_name.c_str()); + return adapter; + } + + adapter_names.push_back(std::move(adapter_name)); + } + + Log_ErrorPrintf(fmt::format("Adapter '{}' not found.", name).c_str()); + return {}; +} + +Microsoft::WRL::ComPtr D3DCommon::GetFirstAdapter(IDXGIFactory5* factory) +{ + Microsoft::WRL::ComPtr adapter; + HRESULT hr = factory->EnumAdapters1(0, adapter.GetAddressOf()); + if (FAILED(hr)) + Log_ErrorPrintf("IDXGIFactory2::EnumAdapters() for first adapter returned %08X", hr); + + return adapter; +} + +Microsoft::WRL::ComPtr D3DCommon::GetChosenOrFirstAdapter(IDXGIFactory5* factory, + const std::string_view& name) +{ + Microsoft::WRL::ComPtr adapter = GetAdapterByName(factory, name); + if (!adapter) + adapter = GetFirstAdapter(factory); + + return adapter; +} + +std::string D3DCommon::GetAdapterName(IDXGIAdapter1* adapter) +{ + std::string ret; + + DXGI_ADAPTER_DESC1 desc; + HRESULT hr = adapter->GetDesc1(&desc); + if (SUCCEEDED(hr)) + { + ret = StringUtil::WideStringToUTF8String(desc.Description); + } + else + { + Log_ErrorPrintf("IDXGIAdapter1::GetDesc() returned %08X", hr); + } + + if (ret.empty()) + ret = "(Unknown)"; + + return ret; +} + +std::string D3DCommon::GetDriverVersionFromLUID(const LUID& luid) +{ + std::string ret; + + HKEY hKey; + if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\DirectX", 0, KEY_READ, &hKey) == ERROR_SUCCESS) + { + DWORD max_key_len = 0, adapter_count = 0; + if (RegQueryInfoKeyW(hKey, nullptr, nullptr, nullptr, &adapter_count, &max_key_len, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr) == ERROR_SUCCESS) + { + std::vector current_name(max_key_len + 1); + for (DWORD i = 0; i < adapter_count; ++i) + { + DWORD subKeyLength = static_cast(current_name.size()); + if (RegEnumKeyExW(hKey, i, current_name.data(), &subKeyLength, nullptr, nullptr, nullptr, nullptr) == + ERROR_SUCCESS) + { + LUID current_luid = {}; + DWORD current_luid_size = sizeof(uint64_t); + if (RegGetValueW(hKey, current_name.data(), L"AdapterLuid", RRF_RT_QWORD, nullptr, ¤t_luid, + ¤t_luid_size) == ERROR_SUCCESS && + current_luid.HighPart == luid.HighPart && current_luid.LowPart == luid.LowPart) + { + LARGE_INTEGER driver_version = {}; + DWORD driver_version_size = sizeof(driver_version); + if (RegGetValueW(hKey, current_name.data(), L"DriverVersion", RRF_RT_QWORD, nullptr, &driver_version, + &driver_version_size) == ERROR_SUCCESS) + { + WORD nProduct = HIWORD(driver_version.HighPart); + WORD nVersion = LOWORD(driver_version.HighPart); + WORD nSubVersion = HIWORD(driver_version.LowPart); + WORD nBuild = LOWORD(driver_version.LowPart); + ret = fmt::format("{}.{}.{}.{}", nProduct, nVersion, nSubVersion, nBuild); + } + } + } + } + } + + RegCloseKey(hKey); + } + + return ret; +} + +std::optional> D3DCommon::CompileShader(D3D_FEATURE_LEVEL feature_level, bool debug_device, + GPUShaderStage stage, const std::string_view& source) +{ + const char* target; + switch (feature_level) + { + case D3D_FEATURE_LEVEL_10_0: + { + static constexpr std::array targets = {{"vs_4_0", "ps_4_0", "cs_4_0"}}; + target = targets[static_cast(stage)]; + } + break; + + case D3D_FEATURE_LEVEL_10_1: + { + static constexpr std::array targets = {{"vs_4_1", "ps_4_1", "cs_4_1"}}; + target = targets[static_cast(stage)]; + } + break; + + case D3D_FEATURE_LEVEL_11_0: + { + static constexpr std::array targets = {{"vs_5_0", "ps_5_0", "cs_5_0"}}; + target = targets[static_cast(stage)]; + } + break; + + case D3D_FEATURE_LEVEL_11_1: + default: + { + static constexpr std::array targets = {{"vs_5_1", "ps_5_1", "cs_5_1"}}; + target = targets[static_cast(stage)]; + } + break; + } + + static constexpr UINT flags_non_debug = D3DCOMPILE_OPTIMIZATION_LEVEL3; + static constexpr UINT flags_debug = D3DCOMPILE_SKIP_OPTIMIZATION | D3DCOMPILE_DEBUG; + + Microsoft::WRL::ComPtr blob; + Microsoft::WRL::ComPtr error_blob; + const HRESULT hr = + D3DCompile(source.data(), source.size(), "0", nullptr, nullptr, "main", target, + debug_device ? flags_debug : flags_non_debug, 0, blob.GetAddressOf(), error_blob.GetAddressOf()); + + std::string error_string; + if (error_blob) + { + error_string.append(static_cast(error_blob->GetBufferPointer()), error_blob->GetBufferSize()); + error_blob.Reset(); + } + + if (FAILED(hr)) + { + Log_ErrorPrintf("Failed to compile '%s':\n%s", target, error_string.c_str()); + + auto fp = FileSystem::OpenManagedCFile( + GPUDevice::GetShaderDumpPath(fmt::format("bad_shader_{}.txt", s_next_bad_shader_id++)).c_str(), "wb"); + if (fp) + { + std::fwrite(source.data(), source.size(), 1, fp.get()); + std::fprintf(fp.get(), "\n\nCompile as %s failed: %08X\n", target, hr); + std::fwrite(error_string.c_str(), error_string.size(), 1, fp.get()); + } + + return {}; + } + + if (!error_string.empty()) + Log_WarningPrintf("'%s' compiled with warnings:\n%s", target, error_string.c_str()); + + return std::vector(static_cast(blob->GetBufferPointer()), + static_cast(blob->GetBufferPointer()) + blob->GetBufferSize()); +} diff --git a/src/core/gpu/d3d_common.h b/src/core/gpu/d3d_common.h new file mode 100644 index 000000000..34803e126 --- /dev/null +++ b/src/core/gpu/d3d_common.h @@ -0,0 +1,55 @@ +// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin +// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) + +#pragma once + +#include "gpu_device.h" + +#include "common/types.h" +#include "common/windows_headers.h" + +#include +#include +#include +#include +#include +#include + +struct IDXGIFactory5; +struct IDXGIAdapter1; +struct IDXGIOutput; +struct DXGI_MODE_DESC; + +namespace D3DCommon { +// create a dxgi factory +Microsoft::WRL::ComPtr CreateFactory(bool debug); + +// returns a list of all adapter names +std::vector GetAdapterNames(IDXGIFactory5* factory); + +// returns a list of fullscreen modes for the specified adapter +std::vector GetFullscreenModes(IDXGIFactory5* factory, const std::string_view& adapter_name); + +// returns the fullscreen mode to use for the specified dimensions +bool GetRequestedExclusiveFullscreenModeDesc(IDXGIFactory5* factory, const RECT& window_rect, u32 width, u32 height, + float refresh_rate, DXGI_FORMAT format, DXGI_MODE_DESC* fullscreen_mode, + IDXGIOutput** output); + +// get an adapter based on name +Microsoft::WRL::ComPtr GetAdapterByName(IDXGIFactory5* factory, const std::string_view& name); + +// returns the first adapter in the system +Microsoft::WRL::ComPtr GetFirstAdapter(IDXGIFactory5* factory); + +// returns the adapter specified in the configuration, or the default +Microsoft::WRL::ComPtr GetChosenOrFirstAdapter(IDXGIFactory5* factory, const std::string_view& name); + +// returns a utf-8 string of the specified adapter's name +std::string GetAdapterName(IDXGIAdapter1* adapter); + +// returns the driver version from the registry as a string +std::string GetDriverVersionFromLUID(const LUID& luid); + +std::optional> CompileShader(D3D_FEATURE_LEVEL feature_level, bool debug_device, GPUShaderStage stage, + const std::string_view& source); +} // namespace D3DCommon diff --git a/src/core/gpu/d3d_shaders.h b/src/core/gpu/d3d_shaders.h deleted file mode 100644 index 42877ff8e..000000000 --- a/src/core/gpu/d3d_shaders.h +++ /dev/null @@ -1,930 +0,0 @@ -#if 0 -cbuffer UBOBlock : register(b0) -{ - float4 u_src_rect; -}; - -void main(in uint vertex_id : SV_VertexID, - out float2 v_tex0 : TEXCOORD0, - out float4 o_pos : SV_Position) -{ - float2 pos = float2(float((vertex_id << 1) & 2u), float(vertex_id & 2u)); - v_tex0 = u_src_rect.xy + pos * u_src_rect.zw; - o_pos = float4(pos * float2(2.0f, -2.0f) + float2(-1.0f, 1.0f), 0.0f, 1.0f); -} -// -// Generated by Microsoft (R) HLSL Shader Compiler 10.1 -// -// -// Buffer Definitions: -// -// cbuffer UBOBlock -// { -// -// float4 u_src_rect; // Offset: 0 Size: 16 -// -// } -// -// -// Resource Bindings: -// -// Name Type Format Dim HLSL Bind Count -// ------------------------------ ---------- ------- ----------- -------------- ------ -// UBOBlock cbuffer NA NA cb0 1 -// -// -// -// Input signature: -// -// Name Index Mask Register SysValue Format Used -// -------------------- ----- ------ -------- -------- ------- ------ -// SV_VertexID 0 x 0 VERTID uint x -// -// -// Output signature: -// -// Name Index Mask Register SysValue Format Used -// -------------------- ----- ------ -------- -------- ------- ------ -// TEXCOORD 0 xy 0 NONE float xy -// SV_Position 0 xyzw 1 POS float xyzw -// -vs_4_0 -dcl_constantbuffer CB0[1], immediateIndexed -dcl_input_sgv v0.x, vertex_id -dcl_output o0.xy -dcl_output_siv o1.xyzw, position -dcl_temps 1 -ishl r0.x, v0.x, l(1) -and r0.x, r0.x, l(2) -and r0.z, v0.x, l(2) -utof r0.xy, r0.xzxx -mad o0.xy, r0.xyxx, cb0[0].zwzz, cb0[0].xyxx -mad o1.xy, r0.xyxx, l(2.000000, -2.000000, 0.000000, 0.000000), l(-1.000000, 1.000000, 0.000000, 0.000000) -mov o1.zw, l(0,0,0,1.000000) -ret -// Approximately 8 instruction slots used -#endif - -const BYTE static s_display_vs_bytecode[] = -{ - 68, 88, 66, 67, 37, 97, - 157, 234, 112, 10, 38, 98, - 114, 228, 143, 118, 71, 158, - 122, 195, 1, 0, 0, 0, - 72, 3, 0, 0, 5, 0, - 0, 0, 52, 0, 0, 0, - 248, 0, 0, 0, 44, 1, - 0, 0, 132, 1, 0, 0, - 204, 2, 0, 0, 82, 68, - 69, 70, 188, 0, 0, 0, - 1, 0, 0, 0, 72, 0, - 0, 0, 1, 0, 0, 0, - 28, 0, 0, 0, 0, 4, - 254, 255, 0, 129, 0, 0, - 148, 0, 0, 0, 60, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 0, - 0, 0, 1, 0, 0, 0, - 85, 66, 79, 66, 108, 111, - 99, 107, 0, 171, 171, 171, - 60, 0, 0, 0, 1, 0, - 0, 0, 96, 0, 0, 0, - 16, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 120, 0, 0, 0, 0, 0, - 0, 0, 16, 0, 0, 0, - 2, 0, 0, 0, 132, 0, - 0, 0, 0, 0, 0, 0, - 117, 95, 115, 114, 99, 95, - 114, 101, 99, 116, 0, 171, - 1, 0, 3, 0, 1, 0, - 4, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 77, 105, - 99, 114, 111, 115, 111, 102, - 116, 32, 40, 82, 41, 32, - 72, 76, 83, 76, 32, 83, - 104, 97, 100, 101, 114, 32, - 67, 111, 109, 112, 105, 108, - 101, 114, 32, 49, 48, 46, - 49, 0, 73, 83, 71, 78, - 44, 0, 0, 0, 1, 0, - 0, 0, 8, 0, 0, 0, - 32, 0, 0, 0, 0, 0, - 0, 0, 6, 0, 0, 0, - 1, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 0, 0, - 83, 86, 95, 86, 101, 114, - 116, 101, 120, 73, 68, 0, - 79, 83, 71, 78, 80, 0, - 0, 0, 2, 0, 0, 0, - 8, 0, 0, 0, 56, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 3, 0, - 0, 0, 0, 0, 0, 0, - 3, 12, 0, 0, 65, 0, - 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 3, 0, - 0, 0, 1, 0, 0, 0, - 15, 0, 0, 0, 84, 69, - 88, 67, 79, 79, 82, 68, - 0, 83, 86, 95, 80, 111, - 115, 105, 116, 105, 111, 110, - 0, 171, 171, 171, 83, 72, - 68, 82, 64, 1, 0, 0, - 64, 0, 1, 0, 80, 0, - 0, 0, 89, 0, 0, 4, - 70, 142, 32, 0, 0, 0, - 0, 0, 1, 0, 0, 0, - 96, 0, 0, 4, 18, 16, - 16, 0, 0, 0, 0, 0, - 6, 0, 0, 0, 101, 0, - 0, 3, 50, 32, 16, 0, - 0, 0, 0, 0, 103, 0, - 0, 4, 242, 32, 16, 0, - 1, 0, 0, 0, 1, 0, - 0, 0, 104, 0, 0, 2, - 1, 0, 0, 0, 41, 0, - 0, 7, 18, 0, 16, 0, - 0, 0, 0, 0, 10, 16, - 16, 0, 0, 0, 0, 0, - 1, 64, 0, 0, 1, 0, - 0, 0, 1, 0, 0, 7, - 18, 0, 16, 0, 0, 0, - 0, 0, 10, 0, 16, 0, - 0, 0, 0, 0, 1, 64, - 0, 0, 2, 0, 0, 0, - 1, 0, 0, 7, 66, 0, - 16, 0, 0, 0, 0, 0, - 10, 16, 16, 0, 0, 0, - 0, 0, 1, 64, 0, 0, - 2, 0, 0, 0, 86, 0, - 0, 5, 50, 0, 16, 0, - 0, 0, 0, 0, 134, 0, - 16, 0, 0, 0, 0, 0, - 50, 0, 0, 11, 50, 32, - 16, 0, 0, 0, 0, 0, - 70, 0, 16, 0, 0, 0, - 0, 0, 230, 138, 32, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 70, 128, 32, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 50, 0, 0, 15, - 50, 32, 16, 0, 1, 0, - 0, 0, 70, 0, 16, 0, - 0, 0, 0, 0, 2, 64, - 0, 0, 0, 0, 0, 64, - 0, 0, 0, 192, 0, 0, - 0, 0, 0, 0, 0, 0, - 2, 64, 0, 0, 0, 0, - 128, 191, 0, 0, 128, 63, - 0, 0, 0, 0, 0, 0, - 0, 0, 54, 0, 0, 8, - 194, 32, 16, 0, 1, 0, - 0, 0, 2, 64, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 128, 63, 62, 0, - 0, 1, 83, 84, 65, 84, - 116, 0, 0, 0, 8, 0, - 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 3, 0, - 0, 0, 2, 0, 0, 0, - 1, 0, 0, 0, 2, 0, - 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0 -}; - -#if 0 -Texture2D samp0 : register(t0); -SamplerState samp0_ss : register(s0); - -void main(in float2 v_tex0 : TEXCOORD0, - out float4 o_col0 : SV_Target) -{ -#ifdef ALPHA - o_col0 = samp0.Sample(samp0_ss, v_tex0); -#else - o_col0 = float4(samp0.Sample(samp0_ss, v_tex0).rgb, 1.0); -#endif -} -// -// Generated by Microsoft (R) HLSL Shader Compiler 10.1 -// -// -// Resource Bindings: -// -// Name Type Format Dim HLSL Bind Count -// ------------------------------ ---------- ------- ----------- -------------- ------ -// samp0_ss sampler NA NA s0 1 -// samp0 texture float4 2d t0 1 -// -// -// -// Input signature: -// -// Name Index Mask Register SysValue Format Used -// -------------------- ----- ------ -------- -------- ------- ------ -// TEXCOORD 0 xy 0 NONE float xy -// -// -// Output signature: -// -// Name Index Mask Register SysValue Format Used -// -------------------- ----- ------ -------- -------- ------- ------ -// SV_Target 0 xyzw 0 TARGET float xyzw -// -ps_4_0 -dcl_sampler s0, mode_default -dcl_resource_texture2d (float,float,float,float) t0 -dcl_input_ps linear v0.xy -dcl_output o0.xyzw -dcl_temps 1 -sample r0.xyzw, v0.xyxx, t0.xyzw, s0 -mov o0.xyz, r0.xyzx -mov o0.w, l(1.000000) -ret -// Approximately 4 instruction slots used -#endif - -const BYTE static s_display_ps_bytecode[] = -{ - 68, 88, 66, 67, 192, 215, - 150, 96, 210, 93, 209, 128, - 113, 254, 100, 56, 49, 113, - 128, 72, 1, 0, 0, 0, - 80, 2, 0, 0, 5, 0, - 0, 0, 52, 0, 0, 0, - 208, 0, 0, 0, 4, 1, - 0, 0, 56, 1, 0, 0, - 212, 1, 0, 0, 82, 68, - 69, 70, 148, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 2, 0, 0, 0, - 28, 0, 0, 0, 0, 4, - 255, 255, 0, 129, 0, 0, - 107, 0, 0, 0, 92, 0, - 0, 0, 3, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 0, - 0, 0, 1, 0, 0, 0, - 101, 0, 0, 0, 2, 0, - 0, 0, 5, 0, 0, 0, - 4, 0, 0, 0, 255, 255, - 255, 255, 0, 0, 0, 0, - 1, 0, 0, 0, 13, 0, - 0, 0, 115, 97, 109, 112, - 48, 95, 115, 115, 0, 115, - 97, 109, 112, 48, 0, 77, - 105, 99, 114, 111, 115, 111, - 102, 116, 32, 40, 82, 41, - 32, 72, 76, 83, 76, 32, - 83, 104, 97, 100, 101, 114, - 32, 67, 111, 109, 112, 105, - 108, 101, 114, 32, 49, 48, - 46, 49, 0, 171, 73, 83, - 71, 78, 44, 0, 0, 0, - 1, 0, 0, 0, 8, 0, - 0, 0, 32, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 3, 0, 0, 0, - 0, 0, 0, 0, 3, 3, - 0, 0, 84, 69, 88, 67, - 79, 79, 82, 68, 0, 171, - 171, 171, 79, 83, 71, 78, - 44, 0, 0, 0, 1, 0, - 0, 0, 8, 0, 0, 0, - 32, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 3, 0, 0, 0, 0, 0, - 0, 0, 15, 0, 0, 0, - 83, 86, 95, 84, 97, 114, - 103, 101, 116, 0, 171, 171, - 83, 72, 68, 82, 148, 0, - 0, 0, 64, 0, 0, 0, - 37, 0, 0, 0, 90, 0, - 0, 3, 0, 96, 16, 0, - 0, 0, 0, 0, 88, 24, - 0, 4, 0, 112, 16, 0, - 0, 0, 0, 0, 85, 85, - 0, 0, 98, 16, 0, 3, - 50, 16, 16, 0, 0, 0, - 0, 0, 101, 0, 0, 3, - 242, 32, 16, 0, 0, 0, - 0, 0, 104, 0, 0, 2, - 1, 0, 0, 0, 69, 0, - 0, 9, 242, 0, 16, 0, - 0, 0, 0, 0, 70, 16, - 16, 0, 0, 0, 0, 0, - 70, 126, 16, 0, 0, 0, - 0, 0, 0, 96, 16, 0, - 0, 0, 0, 0, 54, 0, - 0, 5, 114, 32, 16, 0, - 0, 0, 0, 0, 70, 2, - 16, 0, 0, 0, 0, 0, - 54, 0, 0, 5, 130, 32, - 16, 0, 0, 0, 0, 0, - 1, 64, 0, 0, 0, 0, - 128, 63, 62, 0, 0, 1, - 83, 84, 65, 84, 116, 0, - 0, 0, 4, 0, 0, 0, - 1, 0, 0, 0, 0, 0, - 0, 0, 2, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 2, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0 -}; - -#if 0 -// -// Generated by Microsoft (R) HLSL Shader Compiler 10.1 -// -// -// Resource Bindings: -// -// Name Type Format Dim HLSL Bind Count -// ------------------------------ ---------- ------- ----------- -------------- ------ -// samp0_ss sampler NA NA s0 1 -// samp0 texture float4 2d t0 1 -// -// -// -// Input signature: -// -// Name Index Mask Register SysValue Format Used -// -------------------- ----- ------ -------- -------- ------- ------ -// TEXCOORD 0 xy 0 NONE float xy -// -// -// Output signature: -// -// Name Index Mask Register SysValue Format Used -// -------------------- ----- ------ -------- -------- ------- ------ -// SV_Target 0 xyzw 0 TARGET float xyzw -// -ps_4_0 -dcl_sampler s0, mode_default -dcl_resource_texture2d(float, float, float, float) t0 -dcl_input_ps linear v0.xy -dcl_output o0.xyzw -sample o0.xyzw, v0.xyxx, t0.xyzw, s0 -ret -// Approximately 2 instruction slots used -#endif - -const BYTE static s_display_ps_alpha_bytecode[] = -{ - 68, 88, 66, 67, 140, 134, - 46, 29, 68, 36, 193, 23, - 94, 171, 102, 123, 183, 66, - 19, 177, 1, 0, 0, 0, - 32, 2, 0, 0, 5, 0, - 0, 0, 52, 0, 0, 0, - 208, 0, 0, 0, 4, 1, - 0, 0, 56, 1, 0, 0, - 164, 1, 0, 0, 82, 68, - 69, 70, 148, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 2, 0, 0, 0, - 28, 0, 0, 0, 0, 4, - 255, 255, 0, 129, 0, 0, - 107, 0, 0, 0, 92, 0, - 0, 0, 3, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 0, - 0, 0, 1, 0, 0, 0, - 101, 0, 0, 0, 2, 0, - 0, 0, 5, 0, 0, 0, - 4, 0, 0, 0, 255, 255, - 255, 255, 0, 0, 0, 0, - 1, 0, 0, 0, 13, 0, - 0, 0, 115, 97, 109, 112, - 48, 95, 115, 115, 0, 115, - 97, 109, 112, 48, 0, 77, - 105, 99, 114, 111, 115, 111, - 102, 116, 32, 40, 82, 41, - 32, 72, 76, 83, 76, 32, - 83, 104, 97, 100, 101, 114, - 32, 67, 111, 109, 112, 105, - 108, 101, 114, 32, 49, 48, - 46, 49, 0, 171, 73, 83, - 71, 78, 44, 0, 0, 0, - 1, 0, 0, 0, 8, 0, - 0, 0, 32, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 3, 0, 0, 0, - 0, 0, 0, 0, 3, 3, - 0, 0, 84, 69, 88, 67, - 79, 79, 82, 68, 0, 171, - 171, 171, 79, 83, 71, 78, - 44, 0, 0, 0, 1, 0, - 0, 0, 8, 0, 0, 0, - 32, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 3, 0, 0, 0, 0, 0, - 0, 0, 15, 0, 0, 0, - 83, 86, 95, 84, 97, 114, - 103, 101, 116, 0, 171, 171, - 83, 72, 68, 82, 100, 0, - 0, 0, 64, 0, 0, 0, - 25, 0, 0, 0, 90, 0, - 0, 3, 0, 96, 16, 0, - 0, 0, 0, 0, 88, 24, - 0, 4, 0, 112, 16, 0, - 0, 0, 0, 0, 85, 85, - 0, 0, 98, 16, 0, 3, - 50, 16, 16, 0, 0, 0, - 0, 0, 101, 0, 0, 3, - 242, 32, 16, 0, 0, 0, - 0, 0, 69, 0, 0, 9, - 242, 32, 16, 0, 0, 0, - 0, 0, 70, 16, 16, 0, - 0, 0, 0, 0, 70, 126, - 16, 0, 0, 0, 0, 0, - 0, 96, 16, 0, 0, 0, - 0, 0, 62, 0, 0, 1, - 83, 84, 65, 84, 116, 0, - 0, 0, 2, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 2, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0 -}; - -#if 0 -cbuffer vertexBuffer : register(b0) -{ - float4x4 ProjectionMatrix; -}; - -struct VS_INPUT -{ - float2 pos : POSITION; - float4 col : COLOR0; - float2 uv : TEXCOORD0; -}; - -struct PS_INPUT -{ - float4 pos : SV_POSITION; - float4 col : COLOR0; - float2 uv : TEXCOORD0; -}; - -PS_INPUT vs_main(VS_INPUT input) -{ - PS_INPUT output; - output.pos = mul(ProjectionMatrix, float4(input.pos.xy, 0.f, 1.f)); - output.col = input.col; - output.uv = input.uv; - return output; -} - -sampler sampler0 : register(s0); -Texture2D texture0 : register(t0); - -float4 ps_main(PS_INPUT input) : SV_Target -{ - float4 out_col = input.col * texture0.Sample(sampler0, input.uv); - return out_col; -} -// -// Generated by Microsoft (R) HLSL Shader Compiler 10.1 -// -// -// Buffer Definitions: -// -// cbuffer vertexBuffer -// { -// -// float4x4 ProjectionMatrix; // Offset: 0 Size: 64 -// -// } -// -// -// Resource Bindings: -// -// Name Type Format Dim HLSL Bind Count -// ------------------------------ ---------- ------- ----------- -------------- ------ -// vertexBuffer cbuffer NA NA cb0 1 -// -// -// -// Input signature: -// -// Name Index Mask Register SysValue Format Used -// -------------------- ----- ------ -------- -------- ------- ------ -// POSITION 0 xy 0 NONE float xy -// COLOR 0 xyzw 1 NONE float xyzw -// TEXCOORD 0 xy 2 NONE float xy -// -// -// Output signature: -// -// Name Index Mask Register SysValue Format Used -// -------------------- ----- ------ -------- -------- ------- ------ -// SV_POSITION 0 xyzw 0 POS float xyzw -// COLOR 0 xyzw 1 NONE float xyzw -// TEXCOORD 0 xy 2 NONE float xy -// -vs_5_0 -dcl_globalFlags refactoringAllowed -dcl_constantbuffer CB0[4], immediateIndexed -dcl_input v0.xy -dcl_input v1.xyzw -dcl_input v2.xy -dcl_output_siv o0.xyzw, position -dcl_output o1.xyzw -dcl_output o2.xy -dcl_temps 1 -mul r0.xyzw, v0.yyyy, cb0[1].xyzw -mad r0.xyzw, cb0[0].xyzw, v0.xxxx, r0.xyzw -add o0.xyzw, r0.xyzw, cb0[3].xyzw -mov o1.xyzw, v1.xyzw -mov o2.xy, v2.xyxx -ret -// Approximately 6 instruction slots used -#endif - -const BYTE static s_imgui_vs_bytecode[] = -{ - 68, 88, 66, 67, 115, 32, - 121, 4, 196, 220, 218, 4, - 63, 218, 114, 0, 25, 78, - 192, 97, 1, 0, 0, 0, - 220, 3, 0, 0, 5, 0, - 0, 0, 52, 0, 0, 0, - 80, 1, 0, 0, 192, 1, - 0, 0, 52, 2, 0, 0, - 64, 3, 0, 0, 82, 68, - 69, 70, 20, 1, 0, 0, - 1, 0, 0, 0, 108, 0, - 0, 0, 1, 0, 0, 0, - 60, 0, 0, 0, 0, 5, - 254, 255, 0, 129, 0, 0, - 236, 0, 0, 0, 82, 68, - 49, 49, 60, 0, 0, 0, - 24, 0, 0, 0, 32, 0, - 0, 0, 40, 0, 0, 0, - 36, 0, 0, 0, 12, 0, - 0, 0, 0, 0, 0, 0, - 92, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 1, 0, - 0, 0, 118, 101, 114, 116, - 101, 120, 66, 117, 102, 102, - 101, 114, 0, 171, 171, 171, - 92, 0, 0, 0, 1, 0, - 0, 0, 132, 0, 0, 0, - 64, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 172, 0, 0, 0, 0, 0, - 0, 0, 64, 0, 0, 0, - 2, 0, 0, 0, 200, 0, - 0, 0, 0, 0, 0, 0, - 255, 255, 255, 255, 0, 0, - 0, 0, 255, 255, 255, 255, - 0, 0, 0, 0, 80, 114, - 111, 106, 101, 99, 116, 105, - 111, 110, 77, 97, 116, 114, - 105, 120, 0, 102, 108, 111, - 97, 116, 52, 120, 52, 0, - 171, 171, 3, 0, 3, 0, - 4, 0, 4, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 189, 0, - 0, 0, 77, 105, 99, 114, - 111, 115, 111, 102, 116, 32, - 40, 82, 41, 32, 72, 76, - 83, 76, 32, 83, 104, 97, - 100, 101, 114, 32, 67, 111, - 109, 112, 105, 108, 101, 114, - 32, 49, 48, 46, 49, 0, - 73, 83, 71, 78, 104, 0, - 0, 0, 3, 0, 0, 0, - 8, 0, 0, 0, 80, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 3, 0, - 0, 0, 0, 0, 0, 0, - 3, 3, 0, 0, 89, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 3, 0, - 0, 0, 1, 0, 0, 0, - 15, 15, 0, 0, 95, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 3, 0, - 0, 0, 2, 0, 0, 0, - 3, 3, 0, 0, 80, 79, - 83, 73, 84, 73, 79, 78, - 0, 67, 79, 76, 79, 82, - 0, 84, 69, 88, 67, 79, - 79, 82, 68, 0, 79, 83, - 71, 78, 108, 0, 0, 0, - 3, 0, 0, 0, 8, 0, - 0, 0, 80, 0, 0, 0, - 0, 0, 0, 0, 1, 0, - 0, 0, 3, 0, 0, 0, - 0, 0, 0, 0, 15, 0, - 0, 0, 92, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 3, 0, 0, 0, - 1, 0, 0, 0, 15, 0, - 0, 0, 98, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 3, 0, 0, 0, - 2, 0, 0, 0, 3, 12, - 0, 0, 83, 86, 95, 80, - 79, 83, 73, 84, 73, 79, - 78, 0, 67, 79, 76, 79, - 82, 0, 84, 69, 88, 67, - 79, 79, 82, 68, 0, 171, - 83, 72, 69, 88, 4, 1, - 0, 0, 80, 0, 1, 0, - 65, 0, 0, 0, 106, 8, - 0, 1, 89, 0, 0, 4, - 70, 142, 32, 0, 0, 0, - 0, 0, 4, 0, 0, 0, - 95, 0, 0, 3, 50, 16, - 16, 0, 0, 0, 0, 0, - 95, 0, 0, 3, 242, 16, - 16, 0, 1, 0, 0, 0, - 95, 0, 0, 3, 50, 16, - 16, 0, 2, 0, 0, 0, - 103, 0, 0, 4, 242, 32, - 16, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 101, 0, - 0, 3, 242, 32, 16, 0, - 1, 0, 0, 0, 101, 0, - 0, 3, 50, 32, 16, 0, - 2, 0, 0, 0, 104, 0, - 0, 2, 1, 0, 0, 0, - 56, 0, 0, 8, 242, 0, - 16, 0, 0, 0, 0, 0, - 86, 21, 16, 0, 0, 0, - 0, 0, 70, 142, 32, 0, - 0, 0, 0, 0, 1, 0, - 0, 0, 50, 0, 0, 10, - 242, 0, 16, 0, 0, 0, - 0, 0, 70, 142, 32, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 6, 16, 16, 0, - 0, 0, 0, 0, 70, 14, - 16, 0, 0, 0, 0, 0, - 0, 0, 0, 8, 242, 32, - 16, 0, 0, 0, 0, 0, - 70, 14, 16, 0, 0, 0, - 0, 0, 70, 142, 32, 0, - 0, 0, 0, 0, 3, 0, - 0, 0, 54, 0, 0, 5, - 242, 32, 16, 0, 1, 0, - 0, 0, 70, 30, 16, 0, - 1, 0, 0, 0, 54, 0, - 0, 5, 50, 32, 16, 0, - 2, 0, 0, 0, 70, 16, - 16, 0, 2, 0, 0, 0, - 62, 0, 0, 1, 83, 84, - 65, 84, 148, 0, 0, 0, - 6, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, - 6, 0, 0, 0, 3, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 2, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0 -}; -#if 0 -// -// Generated by Microsoft (R) HLSL Shader Compiler 10.1 -// -// -// Resource Bindings: -// -// Name Type Format Dim HLSL Bind Count -// ------------------------------ ---------- ------- ----------- -------------- ------ -// sampler0 sampler NA NA s0 1 -// texture0 texture float4 2d t0 1 -// -// -// -// Input signature: -// -// Name Index Mask Register SysValue Format Used -// -------------------- ----- ------ -------- -------- ------- ------ -// SV_POSITION 0 xyzw 0 POS float -// COLOR 0 xyzw 1 NONE float xyzw -// TEXCOORD 0 xy 2 NONE float xy -// -// -// Output signature: -// -// Name Index Mask Register SysValue Format Used -// -------------------- ----- ------ -------- -------- ------- ------ -// SV_Target 0 xyzw 0 TARGET float xyzw -// -ps_5_0 -dcl_globalFlags refactoringAllowed -dcl_sampler s0, mode_default -dcl_resource_texture2d (float,float,float,float) t0 -dcl_input_ps linear v1.xyzw -dcl_input_ps linear v2.xy -dcl_output o0.xyzw -dcl_temps 1 -sample_indexable(texture2d)(float,float,float,float) r0.xyzw, v2.xyxx, t0.xyzw, s0 -mul o0.xyzw, r0.xyzw, v1.xyzw -ret -// Approximately 3 instruction slots used -#endif - -const BYTE static s_imgui_ps_bytecode[] = -{ - 68, 88, 66, 67, 219, 131, - 126, 77, 0, 201, 229, 66, - 111, 92, 42, 162, 229, 97, - 51, 114, 1, 0, 0, 0, - 224, 2, 0, 0, 5, 0, - 0, 0, 52, 0, 0, 0, - 244, 0, 0, 0, 104, 1, - 0, 0, 156, 1, 0, 0, - 68, 2, 0, 0, 82, 68, - 69, 70, 184, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 2, 0, 0, 0, - 60, 0, 0, 0, 0, 5, - 255, 255, 0, 129, 0, 0, - 142, 0, 0, 0, 82, 68, - 49, 49, 60, 0, 0, 0, - 24, 0, 0, 0, 32, 0, - 0, 0, 40, 0, 0, 0, - 36, 0, 0, 0, 12, 0, - 0, 0, 0, 0, 0, 0, - 124, 0, 0, 0, 3, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 1, 0, - 0, 0, 133, 0, 0, 0, - 2, 0, 0, 0, 5, 0, - 0, 0, 4, 0, 0, 0, - 255, 255, 255, 255, 0, 0, - 0, 0, 1, 0, 0, 0, - 13, 0, 0, 0, 115, 97, - 109, 112, 108, 101, 114, 48, - 0, 116, 101, 120, 116, 117, - 114, 101, 48, 0, 77, 105, - 99, 114, 111, 115, 111, 102, - 116, 32, 40, 82, 41, 32, - 72, 76, 83, 76, 32, 83, - 104, 97, 100, 101, 114, 32, - 67, 111, 109, 112, 105, 108, - 101, 114, 32, 49, 48, 46, - 49, 0, 171, 171, 73, 83, - 71, 78, 108, 0, 0, 0, - 3, 0, 0, 0, 8, 0, - 0, 0, 80, 0, 0, 0, - 0, 0, 0, 0, 1, 0, - 0, 0, 3, 0, 0, 0, - 0, 0, 0, 0, 15, 0, - 0, 0, 92, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 3, 0, 0, 0, - 1, 0, 0, 0, 15, 15, - 0, 0, 98, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 3, 0, 0, 0, - 2, 0, 0, 0, 3, 3, - 0, 0, 83, 86, 95, 80, - 79, 83, 73, 84, 73, 79, - 78, 0, 67, 79, 76, 79, - 82, 0, 84, 69, 88, 67, - 79, 79, 82, 68, 0, 171, - 79, 83, 71, 78, 44, 0, - 0, 0, 1, 0, 0, 0, - 8, 0, 0, 0, 32, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 3, 0, - 0, 0, 0, 0, 0, 0, - 15, 0, 0, 0, 83, 86, - 95, 84, 97, 114, 103, 101, - 116, 0, 171, 171, 83, 72, - 69, 88, 160, 0, 0, 0, - 80, 0, 0, 0, 40, 0, - 0, 0, 106, 8, 0, 1, - 90, 0, 0, 3, 0, 96, - 16, 0, 0, 0, 0, 0, - 88, 24, 0, 4, 0, 112, - 16, 0, 0, 0, 0, 0, - 85, 85, 0, 0, 98, 16, - 0, 3, 242, 16, 16, 0, - 1, 0, 0, 0, 98, 16, - 0, 3, 50, 16, 16, 0, - 2, 0, 0, 0, 101, 0, - 0, 3, 242, 32, 16, 0, - 0, 0, 0, 0, 104, 0, - 0, 2, 1, 0, 0, 0, - 69, 0, 0, 139, 194, 0, - 0, 128, 67, 85, 21, 0, - 242, 0, 16, 0, 0, 0, - 0, 0, 70, 16, 16, 0, - 2, 0, 0, 0, 70, 126, - 16, 0, 0, 0, 0, 0, - 0, 96, 16, 0, 0, 0, - 0, 0, 56, 0, 0, 7, - 242, 32, 16, 0, 0, 0, - 0, 0, 70, 14, 16, 0, - 0, 0, 0, 0, 70, 30, - 16, 0, 1, 0, 0, 0, - 62, 0, 0, 1, 83, 84, - 65, 84, 148, 0, 0, 0, - 3, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, - 3, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0 -}; diff --git a/src/core/gpu/gl/context_egl.cpp b/src/core/gpu/gl/context_egl.cpp index 68884d2e9..eec0ca12b 100644 --- a/src/core/gpu/gl/context_egl.cpp +++ b/src/core/gpu/gl/context_egl.cpp @@ -9,7 +9,9 @@ Log_SetChannel(GL::ContextEGL); namespace GL { -ContextEGL::ContextEGL(const WindowInfo& wi) : Context(wi) {} +ContextEGL::ContextEGL(const WindowInfo& wi) : Context(wi) +{ +} ContextEGL::~ContextEGL() { @@ -245,10 +247,27 @@ bool ContextEGL::CheckConfigSurfaceFormat(EGLConfig config, WindowInfo::SurfaceF switch (format) { case GPUTexture::Format::Undefined: - return true; + { + if (red_size == 5 && green_size == 6 && red_size == 5) + { + m_wi.surface_format = GPUTexture::Format::RGB565; + } + else if (red_size == 5 && green_size == 5 && red_size == 5 && alpha_size == 1) + { + m_wi.surface_format = GPUTexture::Format::RGBA5551; + } + else if (red_size == 8 && green_size == 8 && blue_size == 8) + { + m_wi.surface_format = GPUTexture::Format::RGBA8; + } + else + { + Log_ErrorPrintf("Unknown surface format: R=%u, G=%u, B=%u, A=%u", red_size, green_size, blue_size, alpha_size); + m_wi.surface_format = GPUTexture::Format::RGBA8; + } - case GPUTexture::Format::RGB8: - return (red_size == 8 && green_size == 8 && blue_size == 8); + return true; + } case GPUTexture::Format::RGBA8: return (red_size == 8 && green_size == 8 && blue_size == 8 && alpha_size == 8); @@ -256,6 +275,9 @@ bool ContextEGL::CheckConfigSurfaceFormat(EGLConfig config, WindowInfo::SurfaceF case GPUTexture::Format::RGB565: return (red_size == 5 && green_size == 6 && blue_size == 5); + case GPUTexture::Format::RGBA5551: + return (red_size == 5 && green_size == 5 && blue_size == 5 && alpha_size == 1); + default: return false; } diff --git a/src/core/gpu/gl/context_wgl.cpp b/src/core/gpu/gl/context_wgl.cpp index 6a4edcaa5..e16232ae0 100644 --- a/src/core/gpu/gl/context_wgl.cpp +++ b/src/core/gpu/gl/context_wgl.cpp @@ -10,9 +10,6 @@ Log_SetChannel(GL::ContextWGL); -// TODO: get rid of this -#pragma comment(lib, "opengl32.lib") - static void* GetProcAddressCallback(const char* name) { void* addr = wglGetProcAddress(name); @@ -231,6 +228,7 @@ HDC ContextWGL::GetDCAndSetPixelFormat(HWND hwnd) return {}; } + m_wi.surface_format = GPUTexture::Format::RGBA8; return hDC; } diff --git a/src/core/gpu/gpu_device.cpp b/src/core/gpu/gpu_device.cpp index 7c8ef7294..e7b2dcd89 100644 --- a/src/core/gpu/gpu_device.cpp +++ b/src/core/gpu/gpu_device.cpp @@ -33,7 +33,7 @@ Log_SetChannel(GPUDevice); #ifdef _WIN32 #include "common/windows_headers.h" #include "d3d11_device.h" -#include "d3d12_gpu_device.h" +#include "d3d12_device.h" #endif #ifdef __APPLE__ @@ -263,6 +263,7 @@ bool GPUDevice::Create(const std::string_view& adapter, const std::string_view& void GPUDevice::Destroy() { + m_post_processing_chain.reset(); if (HasSurface()) DestroySurface(); DestroyResources(); @@ -1450,8 +1451,7 @@ std::unique_ptr GPUDevice::CreateDeviceForAPI(RenderAPI api) #ifdef _WIN32 case RenderAPI::D3D12: - // return std::make_unique(); - return {}; + return std::make_unique(); case RenderAPI::D3D11: return std::make_unique(); diff --git a/src/core/gpu/imgui_impl_dx12.cpp b/src/core/gpu/imgui_impl_dx12.cpp deleted file mode 100644 index b6e69b9c2..000000000 --- a/src/core/gpu/imgui_impl_dx12.cpp +++ /dev/null @@ -1,545 +0,0 @@ -// dear imgui: Renderer Backend for DirectX12 -// This needs to be used along with a Platform Backend (e.g. Win32) - -// Implemented features: -// [X] Renderer: User texture binding. Use 'D3D12_GPU_DESCRIPTOR_HANDLE' as ImTextureID. Read the FAQ about ImTextureID! -// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices. - -// Important: to compile on 32-bit systems, this backend requires code to be compiled with '#define ImTextureID ImU64'. -// This is because we need ImTextureID to carry a 64-bit value and by default ImTextureID is defined as void*. -// To build this on 32-bit systems: -// - [Solution 1] IDE/msbuild: in "Properties/C++/Preprocessor Definitions" add 'ImTextureID=ImU64' (this is what we do in the 'example_win32_direct12/example_win32_direct12.vcxproj' project file) -// - [Solution 2] IDE/msbuild: in "Properties/C++/Preprocessor Definitions" add 'IMGUI_USER_CONFIG="my_imgui_config.h"' and inside 'my_imgui_config.h' add '#define ImTextureID ImU64' and as many other options as you like. -// - [Solution 3] IDE/msbuild: edit imconfig.h and add '#define ImTextureID ImU64' (prefer solution 2 to create your own config file!) -// - [Solution 4] command-line: add '/D ImTextureID=ImU64' to your cl.exe command-line (this is what we do in the example_win32_direct12/build_win32.bat file) - -// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. -// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. -// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. -// Read online: https://github.com/ocornut/imgui/tree/master/docs - -// CHANGELOG -// (minor and older changes stripped away, please see git history for details) -// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX). -// 2021-05-19: DirectX12: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement) -// 2021-02-18: DirectX12: Change blending equation to preserve alpha in output buffer. -// 2021-01-11: DirectX12: Improve Windows 7 compatibility (for D3D12On7) by loading d3d12.dll dynamically. -// 2020-09-16: DirectX12: Avoid rendering calls with zero-sized scissor rectangle since it generates a validation layer warning. -// 2020-09-08: DirectX12: Clarified support for building on 32-bit systems by redefining ImTextureID. -// 2019-10-18: DirectX12: *BREAKING CHANGE* Added extra ID3D12DescriptorHeap parameter to ImGui_ImplDX12_Init() function. -// 2019-05-29: DirectX12: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag. -// 2019-04-30: DirectX12: Added support for special ImDrawCallback_ResetRenderState callback to reset render state. -// 2019-03-29: Misc: Various minor tidying up. -// 2018-12-03: Misc: Added #pragma comment statement to automatically link with d3dcompiler.lib when using D3DCompile(). -// 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window. -// 2018-06-12: DirectX12: Moved the ID3D12GraphicsCommandList* parameter from NewFrame() to RenderDrawData(). -// 2018-06-08: Misc: Extracted imgui_impl_dx12.cpp/.h away from the old combined DX12+Win32 example. -// 2018-06-08: DirectX12: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle (to ease support for future multi-viewport). -// 2018-02-22: Merged into master with all Win32 code synchronized to other examples. - -#include "common/windows_headers.h" - -#include "common/assert.h" -#include "d3d12/context.h" -#include "d3d12/texture.h" -#include "d3d12/stream_buffer.h" - -#include "imgui.h" -#include "imgui_impl_dx12.h" - -// DirectX -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "d3dcompiler") // Automatically link with d3dcompiler.lib as we are using D3DCompile() below. -#endif - -// If we're doing more than this... wtf? -static constexpr u32 VERTEX_BUFFER_SIZE = 8 * 1024 * 1024; -static constexpr u32 INDEX_BUFFER_SIZE = 4 * 1024 * 1024; - -struct ImGui_ImplDX12_Data -{ - D3D12::StreamBuffer VertexStreamBuffer; - D3D12::StreamBuffer IndexStreamBuffer; - D3D12::Texture FontTexture; - ID3D12RootSignature* pRootSignature = nullptr; - ID3D12PipelineState* pPipelineState = nullptr; - DXGI_FORMAT RTVFormat = DXGI_FORMAT_UNKNOWN; -}; - -struct VERTEX_CONSTANT_BUFFER -{ - float mvp[4][4]; -}; - -// Backend data stored in io.BackendRendererUserData to allow support for multiple Dear ImGui contexts -// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts. -static ImGui_ImplDX12_Data* ImGui_ImplDX12_GetBackendData() -{ - return ImGui::GetCurrentContext() ? (ImGui_ImplDX12_Data*)ImGui::GetIO().BackendRendererUserData : NULL; -} - -// Functions -static void ImGui_ImplDX12_SetupRenderState(ImDrawData* draw_data, ID3D12GraphicsCommandList* ctx) -{ - ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); - - // Setup orthographic projection matrix into our constant buffer - // Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). - VERTEX_CONSTANT_BUFFER vertex_constant_buffer; - { - float L = draw_data->DisplayPos.x; - float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x; - float T = draw_data->DisplayPos.y; - float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y; - float mvp[4][4] = - { - { 2.0f/(R-L), 0.0f, 0.0f, 0.0f }, - { 0.0f, 2.0f/(T-B), 0.0f, 0.0f }, - { 0.0f, 0.0f, 0.5f, 0.0f }, - { (R+L)/(L-R), (T+B)/(B-T), 0.5f, 1.0f }, - }; - memcpy(&vertex_constant_buffer.mvp, mvp, sizeof(mvp)); - } - - // Setup viewport - D3D12_VIEWPORT vp; - memset(&vp, 0, sizeof(D3D12_VIEWPORT)); - vp.Width = draw_data->DisplaySize.x; - vp.Height = draw_data->DisplaySize.y; - vp.MinDepth = 0.0f; - vp.MaxDepth = 1.0f; - vp.TopLeftX = vp.TopLeftY = 0.0f; - ctx->RSSetViewports(1, &vp); - - // Bind shader and vertex buffers - unsigned int stride = sizeof(ImDrawVert); - D3D12_VERTEX_BUFFER_VIEW vbv; - memset(&vbv, 0, sizeof(D3D12_VERTEX_BUFFER_VIEW)); - vbv.BufferLocation = bd->VertexStreamBuffer.GetCurrentGPUPointer(); - vbv.SizeInBytes = bd->VertexStreamBuffer.GetCurrentSpace(); - vbv.StrideInBytes = stride; - ctx->IASetVertexBuffers(0, 1, &vbv); - D3D12_INDEX_BUFFER_VIEW ibv; - memset(&ibv, 0, sizeof(D3D12_INDEX_BUFFER_VIEW)); - ibv.BufferLocation = bd->IndexStreamBuffer.GetCurrentGPUPointer(); - ibv.SizeInBytes = bd->IndexStreamBuffer.GetCurrentSpace(); - ibv.Format = sizeof(ImDrawIdx) == 2 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT; - ctx->IASetIndexBuffer(&ibv); - ctx->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); - ctx->SetPipelineState(bd->pPipelineState); - ctx->SetGraphicsRootSignature(bd->pRootSignature); - ctx->SetGraphicsRoot32BitConstants(0, 16, &vertex_constant_buffer, 0); - - // Setup blend factor - const float blend_factor[4] = { 0.f, 0.f, 0.f, 0.f }; - ctx->OMSetBlendFactor(blend_factor); -} - -template -static inline void SafeRelease(T*& res) -{ - if (res) - res->Release(); - res = NULL; -} - -// Render function -void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data) -{ - // Avoid rendering when minimized - if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f) - return; - - // FIXME: I'm assuming that this only gets called once per frame! - // If not, we can't just re-allocate the IB or VB, we'll have to do a proper allocator. - ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); - - const u32 needed_vb = draw_data->TotalVtxCount * sizeof(ImDrawVert); - const u32 needed_ib = draw_data->TotalIdxCount * sizeof(ImDrawIdx); - - if (!bd->VertexStreamBuffer.ReserveMemory(needed_vb, sizeof(ImDrawVert)) || - !bd->IndexStreamBuffer.ReserveMemory(needed_ib, sizeof(ImDrawIdx))) - { - g_d3d12_context->ExecuteCommandList(false); - if (!bd->VertexStreamBuffer.ReserveMemory(needed_vb, sizeof(ImDrawVert)) || - !bd->IndexStreamBuffer.ReserveMemory(needed_ib, sizeof(ImDrawIdx))) - { - Panic("Failed to allocate space for imgui vertices/indices"); - } - } - - // Upload vertex/index data into a single contiguous GPU buffer - ImDrawVert* vtx_dst = (ImDrawVert*)bd->VertexStreamBuffer.GetCurrentHostPointer(); - ImDrawIdx* idx_dst = (ImDrawIdx*)bd->IndexStreamBuffer.GetCurrentHostPointer(); - for (int n = 0; n < draw_data->CmdListsCount; n++) - { - const ImDrawList* cmd_list = draw_data->CmdLists[n]; - memcpy(vtx_dst, cmd_list->VtxBuffer.Data, cmd_list->VtxBuffer.Size * sizeof(ImDrawVert)); - memcpy(idx_dst, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx)); - vtx_dst += cmd_list->VtxBuffer.Size; - idx_dst += cmd_list->IdxBuffer.Size; - } - - // Setup desired DX state (must happen before commit, because it uses the offsets) - ID3D12GraphicsCommandList* ctx = g_d3d12_context->GetCommandList(); - ImGui_ImplDX12_SetupRenderState(draw_data, ctx); - bd->VertexStreamBuffer.CommitMemory(needed_vb); - bd->IndexStreamBuffer.CommitMemory(needed_ib); - - // Render command lists - // (Because we merged all buffers into a single one, we maintain our own offset into them) - int global_vtx_offset = 0; - int global_idx_offset = 0; - ImVec2 clip_off = draw_data->DisplayPos; - const D3D12::Texture* last_texture = nullptr; - for (int n = 0; n < draw_data->CmdListsCount; n++) - { - const ImDrawList* cmd_list = draw_data->CmdLists[n]; - for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) - { - const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i]; - if (pcmd->UserCallback != NULL) - { - // User callback, registered via ImDrawList::AddCallback() - // (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.) - if (pcmd->UserCallback == ImDrawCallback_ResetRenderState) - ImGui_ImplDX12_SetupRenderState(draw_data, ctx); - else - pcmd->UserCallback(cmd_list, pcmd); - } - else - { - // Project scissor/clipping rectangles into framebuffer space - ImVec2 clip_min(pcmd->ClipRect.x - clip_off.x, pcmd->ClipRect.y - clip_off.y); - ImVec2 clip_max(pcmd->ClipRect.z - clip_off.x, pcmd->ClipRect.w - clip_off.y); - if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y) - continue; - - // Apply Scissor/clipping rectangle, Bind texture, Draw - const D3D12_RECT r = { (LONG)clip_min.x, (LONG)clip_min.y, (LONG)clip_max.x, (LONG)clip_max.y }; - - const D3D12::Texture* tex = (D3D12::Texture*)pcmd->GetTexID(); - if (tex && last_texture != tex) - { -#if 0 - // for when we redo the descriptor stuff - D3D12::DescriptorHandle handle; - if (!g_d3d12_context->GetDescriptorAllocator().Allocate(1, &handle)) - { - // ugh. - g_d3d12_context->ExecuteCommandList(false); - ctx = g_d3d12_context->GetCommandList(); - ImGui_ImplDX12_SetupRenderState(draw_data, ctx); - if (!g_d3d12_context->GetDescriptorAllocator().Allocate(1, &handle)) - Panic("Failed to allocate descriptor after cmdlist kick"); - } - - g_d3d12_context->GetDevice()->CopyDescriptorsSimple(1, handle, tex->GetSRVDescriptor(), D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); - ctx->SetGraphicsRootDescriptorTable(1, handle); -#else - ctx->SetGraphicsRootDescriptorTable(1, tex->GetSRVDescriptor()); -#endif - last_texture = tex; - } - - ctx->RSSetScissorRects(1, &r); - ctx->DrawIndexedInstanced(pcmd->ElemCount, 1, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset, 0); - } - } - global_idx_offset += cmd_list->IdxBuffer.Size; - global_vtx_offset += cmd_list->VtxBuffer.Size; - } -} - -bool ImGui_ImplDX12_CreateFontsTexture() -{ - // Build texture atlas - ImGuiIO& io = ImGui::GetIO(); - ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); - unsigned char* pixels; - int width, height; - io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); - - // Upload texture to graphics system - if (bd->FontTexture.GetWidth() != static_cast(width) || bd->FontTexture.GetHeight() != static_cast(height)) - { - if (!bd->FontTexture.Create(width, height, 1, 1, 1, DXGI_FORMAT_R8G8B8A8_UNORM, - DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, - D3D12_RESOURCE_FLAG_NONE)) - { - return false; - } - } - -#if 0 - if (!bd->FontTexture.LoadData(g_d3d12_context->GetInitCommandList(), 0, 0, 0, width, height, pixels, width * sizeof(u32))) - return false; -#else - if (!bd->FontTexture.LoadData(0, 0, width, height, pixels, width * sizeof(u32))) - return false; -#endif - - io.Fonts->SetTexID((ImTextureID)&bd->FontTexture); - return true; -} - -bool ImGui_ImplDX12_CreateDeviceObjects() -{ - ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); - if (bd->pPipelineState) - ImGui_ImplDX12_DestroyDeviceObjects(); - - // Create the root signature - { - D3D12_DESCRIPTOR_RANGE descRange = {}; - descRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV; - descRange.NumDescriptors = 1; - descRange.BaseShaderRegister = 0; - descRange.RegisterSpace = 0; - descRange.OffsetInDescriptorsFromTableStart = 0; - - D3D12_ROOT_PARAMETER param[2] = {}; - - param[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS; - param[0].Constants.ShaderRegister = 0; - param[0].Constants.RegisterSpace = 0; - param[0].Constants.Num32BitValues = 16; - param[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_VERTEX; - - param[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; - param[1].DescriptorTable.NumDescriptorRanges = 1; - param[1].DescriptorTable.pDescriptorRanges = &descRange; - param[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL; - - // Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling. - D3D12_STATIC_SAMPLER_DESC staticSampler = {}; - staticSampler.Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR; - staticSampler.AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; - staticSampler.AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; - staticSampler.AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; - staticSampler.MipLODBias = 0.f; - staticSampler.MaxAnisotropy = 0; - staticSampler.ComparisonFunc = D3D12_COMPARISON_FUNC_ALWAYS; - staticSampler.BorderColor = D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK; - staticSampler.MinLOD = 0.f; - staticSampler.MaxLOD = 0.f; - staticSampler.ShaderRegister = 0; - staticSampler.RegisterSpace = 0; - staticSampler.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL; - - D3D12_ROOT_SIGNATURE_DESC desc = {}; - desc.NumParameters = _countof(param); - desc.pParameters = param; - desc.NumStaticSamplers = 1; - desc.pStaticSamplers = &staticSampler; - desc.Flags = - D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT | - D3D12_ROOT_SIGNATURE_FLAG_DENY_HULL_SHADER_ROOT_ACCESS | - D3D12_ROOT_SIGNATURE_FLAG_DENY_DOMAIN_SHADER_ROOT_ACCESS | - D3D12_ROOT_SIGNATURE_FLAG_DENY_GEOMETRY_SHADER_ROOT_ACCESS; - - auto blob = g_d3d12_context->SerializeRootSignature(&desc); - if (!blob) - return false; - - g_d3d12_context->GetDevice()->CreateRootSignature(0, blob->GetBufferPointer(), blob->GetBufferSize(), IID_PPV_ARGS(&bd->pRootSignature)); - } - - // By using D3DCompile() from / d3dcompiler.lib, we introduce a dependency to a given version of d3dcompiler_XX.dll (see D3DCOMPILER_DLL_A) - // If you would like to use this DX12 sample code but remove this dependency you can: - // 1) compile once, save the compiled shader blobs into a file or source code and pass them to CreateVertexShader()/CreatePixelShader() [preferred solution] - // 2) use code to detect any version of the DLL and grab a pointer to D3DCompile from the DLL. - // See https://github.com/ocornut/imgui/pull/638 for sources and details. - - D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc; - memset(&psoDesc, 0, sizeof(D3D12_GRAPHICS_PIPELINE_STATE_DESC)); - psoDesc.NodeMask = 1; - psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; - psoDesc.pRootSignature = bd->pRootSignature; - psoDesc.SampleMask = UINT_MAX; - psoDesc.NumRenderTargets = 1; - psoDesc.RTVFormats[0] = bd->RTVFormat; - psoDesc.SampleDesc.Count = 1; - psoDesc.Flags = D3D12_PIPELINE_STATE_FLAG_NONE; - - ID3DBlob* vertexShaderBlob; - ID3DBlob* pixelShaderBlob; - - // Create the vertex shader - { - static const char* vertexShader = - "cbuffer vertexBuffer : register(b0) \ - {\ - float4x4 ProjectionMatrix; \ - };\ - struct VS_INPUT\ - {\ - float2 pos : POSITION;\ - float4 col : COLOR0;\ - float2 uv : TEXCOORD0;\ - };\ - \ - struct PS_INPUT\ - {\ - float4 pos : SV_POSITION;\ - float4 col : COLOR0;\ - float2 uv : TEXCOORD0;\ - };\ - \ - PS_INPUT main(VS_INPUT input)\ - {\ - PS_INPUT output;\ - output.pos = mul( ProjectionMatrix, float4(input.pos.xy, 0.f, 1.f));\ - output.col = input.col;\ - output.uv = input.uv;\ - return output;\ - }"; - - if (FAILED(D3DCompile(vertexShader, strlen(vertexShader), NULL, NULL, NULL, "main", "vs_5_0", 0, 0, &vertexShaderBlob, NULL))) - return false; // NB: Pass ID3D10Blob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob! - psoDesc.VS = { vertexShaderBlob->GetBufferPointer(), vertexShaderBlob->GetBufferSize() }; - - // Create the input layout - static D3D12_INPUT_ELEMENT_DESC local_layout[] = - { - { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)IM_OFFSETOF(ImDrawVert, pos), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, - { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)IM_OFFSETOF(ImDrawVert, uv), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, - { "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, (UINT)IM_OFFSETOF(ImDrawVert, col), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, - }; - psoDesc.InputLayout = { local_layout, 3 }; - } - - // Create the pixel shader - { - static const char* pixelShader = - "struct PS_INPUT\ - {\ - float4 pos : SV_POSITION;\ - float4 col : COLOR0;\ - float2 uv : TEXCOORD0;\ - };\ - SamplerState sampler0 : register(s0);\ - Texture2D texture0 : register(t0);\ - \ - float4 main(PS_INPUT input) : SV_Target\ - {\ - float4 out_col = input.col * texture0.Sample(sampler0, input.uv); \ - return out_col; \ - }"; - - if (FAILED(D3DCompile(pixelShader, strlen(pixelShader), NULL, NULL, NULL, "main", "ps_5_0", 0, 0, &pixelShaderBlob, NULL))) - { - vertexShaderBlob->Release(); - return false; // NB: Pass ID3D10Blob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob! - } - psoDesc.PS = { pixelShaderBlob->GetBufferPointer(), pixelShaderBlob->GetBufferSize() }; - } - - // Create the blending setup - { - D3D12_BLEND_DESC& desc = psoDesc.BlendState; - desc.AlphaToCoverageEnable = false; - desc.RenderTarget[0].BlendEnable = true; - desc.RenderTarget[0].SrcBlend = D3D12_BLEND_SRC_ALPHA; - desc.RenderTarget[0].DestBlend = D3D12_BLEND_INV_SRC_ALPHA; - desc.RenderTarget[0].BlendOp = D3D12_BLEND_OP_ADD; - desc.RenderTarget[0].SrcBlendAlpha = D3D12_BLEND_ONE; - desc.RenderTarget[0].DestBlendAlpha = D3D12_BLEND_INV_SRC_ALPHA; - desc.RenderTarget[0].BlendOpAlpha = D3D12_BLEND_OP_ADD; - desc.RenderTarget[0].RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL; - } - - // Create the rasterizer state - { - D3D12_RASTERIZER_DESC& desc = psoDesc.RasterizerState; - desc.FillMode = D3D12_FILL_MODE_SOLID; - desc.CullMode = D3D12_CULL_MODE_NONE; - desc.FrontCounterClockwise = FALSE; - desc.DepthBias = D3D12_DEFAULT_DEPTH_BIAS; - desc.DepthBiasClamp = D3D12_DEFAULT_DEPTH_BIAS_CLAMP; - desc.SlopeScaledDepthBias = D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS; - desc.DepthClipEnable = true; - desc.MultisampleEnable = FALSE; - desc.AntialiasedLineEnable = FALSE; - desc.ForcedSampleCount = 0; - desc.ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF; - } - - // Create depth-stencil State - { - D3D12_DEPTH_STENCIL_DESC& desc = psoDesc.DepthStencilState; - desc.DepthEnable = false; - desc.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL; - desc.DepthFunc = D3D12_COMPARISON_FUNC_ALWAYS; - desc.StencilEnable = false; - desc.FrontFace.StencilFailOp = desc.FrontFace.StencilDepthFailOp = desc.FrontFace.StencilPassOp = D3D12_STENCIL_OP_KEEP; - desc.FrontFace.StencilFunc = D3D12_COMPARISON_FUNC_ALWAYS; - desc.BackFace = desc.FrontFace; - } - - HRESULT result_pipeline_state = g_d3d12_context->GetDevice()->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&bd->pPipelineState)); - vertexShaderBlob->Release(); - pixelShaderBlob->Release(); - if (result_pipeline_state != S_OK) - return false; - - return true; -} - -void ImGui_ImplDX12_DestroyDeviceObjects() -{ - ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); - if (!bd) - return; - ImGuiIO& io = ImGui::GetIO(); - - SafeRelease(bd->pRootSignature); - SafeRelease(bd->pPipelineState); - bd->FontTexture.Destroy(false); - bd->VertexStreamBuffer.Destroy(false); - bd->IndexStreamBuffer.Destroy(false); - io.Fonts->SetTexID(NULL); // We copied bd->pFontTextureView to io.Fonts->TexID so let's clear that as well. -} - -bool ImGui_ImplDX12_Init(DXGI_FORMAT rtv_format) -{ - ImGuiIO& io = ImGui::GetIO(); - IM_ASSERT(io.BackendRendererUserData == NULL && "Already initialized a renderer backend!"); - - // Setup backend capabilities flags - ImGui_ImplDX12_Data* bd = IM_NEW(ImGui_ImplDX12_Data)(); - io.BackendRendererUserData = (void*)bd; - io.BackendRendererName = "imgui_impl_dx12"; - io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. - - bd->RTVFormat = rtv_format; - - if (!bd->VertexStreamBuffer.Create(VERTEX_BUFFER_SIZE) || !bd->IndexStreamBuffer.Create(INDEX_BUFFER_SIZE)) - return false; - - return ImGui_ImplDX12_CreateDeviceObjects(); -} - -void ImGui_ImplDX12_Shutdown() -{ - ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); - IM_ASSERT(bd != NULL && "No renderer backend to shutdown, or already shutdown?"); - ImGuiIO& io = ImGui::GetIO(); - - ImGui_ImplDX12_DestroyDeviceObjects(); - io.BackendRendererName = NULL; - io.BackendRendererUserData = NULL; - IM_DELETE(bd); -} - -void ImGui_ImplDX12_NewFrame() -{ - ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); - IM_ASSERT(bd != NULL && "Did you call ImGui_ImplDX12_Init()?"); - - if (!bd->pPipelineState) - ImGui_ImplDX12_CreateDeviceObjects(); -} diff --git a/src/core/gpu/imgui_impl_dx12.h b/src/core/gpu/imgui_impl_dx12.h deleted file mode 100644 index 6776da374..000000000 --- a/src/core/gpu/imgui_impl_dx12.h +++ /dev/null @@ -1,14 +0,0 @@ -// dear imgui: Renderer Backend for DirectX12 -// This needs to be used along with a Platform Backend (e.g. Win32) - -#pragma once -#include "imgui.h" // IMGUI_IMPL_API - -bool ImGui_ImplDX12_Init(DXGI_FORMAT rtv_format); -void ImGui_ImplDX12_Shutdown(); -void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data); - -// Use if you want to reset your rendering device without losing Dear ImGui state. -void ImGui_ImplDX12_DestroyDeviceObjects(); -bool ImGui_ImplDX12_CreateDeviceObjects(); -bool ImGui_ImplDX12_CreateFontsTexture(); diff --git a/src/core/gpu/postprocessing_shadergen.cpp b/src/core/gpu/postprocessing_shadergen.cpp index c907a4506..905c5ba09 100644 --- a/src/core/gpu/postprocessing_shadergen.cpp +++ b/src/core/gpu/postprocessing_shadergen.cpp @@ -15,8 +15,8 @@ std::string PostProcessingShaderGen::GeneratePostProcessingVertexShader(const Po std::stringstream ss; WriteHeader(ss); - DeclareTexture(ss, "samp0", 0); WriteUniformBuffer(ss, shader, false); + DeclareTexture(ss, "samp0", 0); DeclareVertexEntryPoint(ss, {}, 0, 1, {}, true); ss << R"( @@ -38,8 +38,8 @@ std::string PostProcessingShaderGen::GeneratePostProcessingFragmentShader(const std::stringstream ss; WriteHeader(ss); - DeclareTexture(ss, "samp0", 0); WriteUniformBuffer(ss, shader, false); + DeclareTexture(ss, "samp0", 0); // Rename main, since we need to set up globals if (!m_glsl) diff --git a/src/core/gpu/vulkan_device.cpp b/src/core/gpu/vulkan_device.cpp index ea1ed13a1..2a17338c8 100644 --- a/src/core/gpu/vulkan_device.cpp +++ b/src/core/gpu/vulkan_device.cpp @@ -1222,17 +1222,22 @@ void VulkanDevice::SubmitCommandBuffer(bool wait_for_completion, const char* rea const std::string reason_str(StringUtil::StdStringFromFormatV(reason, ap)); va_end(ap); - Log_WarningPrintf("Vulkan: Executing command buffer due to '%s'", reason_str.c_str()); + Log_WarningPrintf("Executing command buffer due to '%s'", reason_str.c_str()); SubmitCommandBuffer(wait_for_completion); } void VulkanDevice::SubmitCommandBufferAndRestartRenderPass(const char* reason) { - Log_WarningPrintf("Vulkan: Executing command buffer due to '%s'", reason); if (InRenderPass()) EndRenderPass(); + VulkanFramebuffer* fb = m_current_framebuffer; + VulkanPipeline* pl = m_current_pipeline; SubmitCommandBuffer(false, "%s", reason); + + if (fb) + SetFramebuffer(fb); + SetPipeline(pl); BeginRenderPass(); } @@ -2539,9 +2544,6 @@ void VulkanDevice::BeginRenderPass() { DebugAssert(!InRenderPass()); - if (m_current_render_pass != VK_NULL_HANDLE) - EndRenderPass(); - VkRenderPassBeginInfo bi = {VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, nullptr}; std::array clear_values; diff --git a/src/core/gpu/vulkan_texture.cpp b/src/core/gpu/vulkan_texture.cpp index 66590ecc0..1e6b4941d 100644 --- a/src/core/gpu/vulkan_texture.cpp +++ b/src/core/gpu/vulkan_texture.cpp @@ -333,10 +333,6 @@ bool VulkanTexture::Update(u32 x, u32 y, u32 width, u32 height, const void* data const VkCommandBuffer cmdbuf = GetCommandBufferForUpdate(); - // first time the texture is used? don't leave it undefined - if (m_layout == Layout::Undefined) - TransitionToLayout(cmdbuf, Layout::TransferDst); - // if we're an rt and have been cleared, and the full rect isn't being uploaded, do the clear if (m_type == Type::RenderTarget) { @@ -346,6 +342,10 @@ bool VulkanTexture::Update(u32 x, u32 y, u32 width, u32 height, const void* data m_state = State::Dirty; } + // first time the texture is used? don't leave it undefined + if (m_layout == Layout::Undefined) + TransitionToLayout(cmdbuf, Layout::TransferDst); + UpdateFromBuffer(cmdbuf, x, y, width, height, layer, level, upload_pitch, buffer, buffer_offset); TransitionToLayout(cmdbuf, Layout::ShaderReadOnly); return true; @@ -416,23 +416,6 @@ void VulkanTexture::Unmap() m_map_level = 0; } -#if 0 -void VulkanTexture::Swap(GSTexture* tex) -{ - GSTexture::Swap(tex); - std::swap(m_image, static_cast(tex)->m_image); - std::swap(m_allocation, static_cast(tex)->m_allocation); - std::swap(m_view, static_cast(tex)->m_view); - std::swap(m_vk_format, static_cast(tex)->m_vk_format); - std::swap(m_layout, static_cast(tex)->m_layout); - std::swap(m_use_fence_counter, static_cast(tex)->m_use_fence_counter); - std::swap(m_clear_value, static_cast(tex)->m_clear_value); - std::swap(m_map_area, static_cast(tex)->m_map_area); - std::swap(m_map_level, static_cast(tex)->m_map_level); - std::swap(m_framebuffers, static_cast(tex)->m_framebuffers); -} -#endif - void VulkanTexture::CommitClear() { if (m_state != GPUTexture::State::Cleared) @@ -710,44 +693,6 @@ VkDescriptorSet VulkanTexture::GetDescriptorSetWithSampler(VkSampler sampler) return ds; } -#if 0 -VkFramebuffer VulkanTexture::GetLinkedFramebuffer(VulkanTexture* depth_texture, bool feedback_loop) -{ - pxAssertRel(m_type != Type::Texture, "Texture is a render target"); - - for (const auto& [other_tex, fb, other_feedback_loop] : m_framebuffers) - { - if (other_tex == depth_texture && other_feedback_loop == feedback_loop) - return fb; - } - - const VkRenderPass rp = VulkanDevice::GetInstance()->GetRenderPass( - (m_type != GSTexture::Type::DepthStencil) ? m_vk_format : VK_FORMAT_UNDEFINED, - (m_type != GSTexture::Type::DepthStencil) ? (depth_texture ? depth_texture->m_vk_format : VK_FORMAT_UNDEFINED) : - m_vk_format, - VK_ATTACHMENT_LOAD_OP_LOAD, VK_ATTACHMENT_STORE_OP_STORE, VK_ATTACHMENT_LOAD_OP_LOAD, VK_ATTACHMENT_STORE_OP_STORE, - VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE, feedback_loop); - if (!rp) - return VK_NULL_HANDLE; - - Vulkan::FramebufferBuilder fbb; - fbb.AddAttachment(m_view); - if (depth_texture) - fbb.AddAttachment(depth_texture->m_view); - fbb.SetSize(m_size.x, m_size.y, 1); - fbb.SetRenderPass(rp); - - VkFramebuffer fb = fbb.Create(VulkanDevice::GetInstance()->GetVulkanDevice()); - if (!fb) - return VK_NULL_HANDLE; - - m_framebuffers.emplace_back(depth_texture, fb, feedback_loop); - if (depth_texture) - depth_texture->m_framebuffers.emplace_back(this, fb, feedback_loop); - return fb; -} -#endif - void VulkanTexture::MakeReadyForSampling() { if (m_layout == Layout::ShaderReadOnly) @@ -778,6 +723,7 @@ bool VulkanDevice::DownloadTexture(GPUTexture* texture, u32 x, u32 y, u32 width, u32 out_data_stride) { VulkanTexture* T = static_cast(texture); + T->CommitClear(); const u32 pitch = Common::AlignUp(width * T->GetPixelSize(), GetBufferCopyRowPitchAlignment()); const u32 size = pitch * height; @@ -835,9 +781,7 @@ bool VulkanDevice::DownloadTexture(GPUTexture* texture, u32 x, u32 y, u32 width, if (res != VK_SUCCESS) LOG_VULKAN_ERROR(res, "vmaInvalidateAllocation() failed, readback may be incorrect: "); - StringUtil::StrideMemCpy(out_data, out_data_stride, m_download_buffer_map, pitch, std::min(pitch, out_data_stride), - height); - + StringUtil::StrideMemCpy(out_data, out_data_stride, m_download_buffer_map, pitch, width * T->GetPixelSize(), height); return true; } diff --git a/src/core/gpu/window_info.cpp b/src/core/gpu/window_info.cpp index 6316579a9..181ac9e6f 100644 --- a/src/core/gpu/window_info.cpp +++ b/src/core/gpu/window_info.cpp @@ -25,8 +25,6 @@ void WindowInfo::SetSurfaceless() #include "common/windows_headers.h" #include -#pragma comment(lib, "Dwmapi.lib") - static bool GetRefreshRateFromDWM(HWND hwnd, float* refresh_rate) { BOOL composition_enabled; diff --git a/src/core/gpu/window_info.h b/src/core/gpu/window_info.h index b86a0e2a5..2c3f0edbf 100644 --- a/src/core/gpu/window_info.h +++ b/src/core/gpu/window_info.h @@ -33,6 +33,8 @@ struct WindowInfo void* surface_handle = nullptr; #endif + ALWAYS_INLINE bool IsSurfaceless() const { return type == Type::Surfaceless; } + // Changes the window to be surfaceless (i.e. no handle/size/etc). void SetSurfaceless(); diff --git a/src/core/gpu_hw.cpp b/src/core/gpu_hw.cpp index ea3633dc8..0425340bf 100644 --- a/src/core/gpu_hw.cpp +++ b/src/core/gpu_hw.cpp @@ -509,6 +509,12 @@ bool GPU_HW::CreateBuffers() return false; } + GL_OBJECT_NAME(m_vram_texture, "VRAM Texture"); + GL_OBJECT_NAME(m_vram_depth_texture, "VRAM Depth Texture"); + GL_OBJECT_NAME(m_vram_read_texture, "VRAM Read Texture"); + GL_OBJECT_NAME(m_display_texture, "Display Texture"); + GL_OBJECT_NAME(m_vram_readback_texture, "VRAM Readback Texture"); + // vram framebuffer has both colour and depth if (!(m_vram_framebuffer = g_gpu_device->CreateFramebuffer(m_vram_texture.get(), m_vram_depth_texture.get())) || !(m_vram_update_depth_framebuffer = g_gpu_device->CreateFramebuffer(m_vram_depth_texture.get())) || @@ -972,6 +978,7 @@ bool GPU_HW::CompilePipelines() if (!fs) return false; GL_OBJECT_NAME(fs, "Downsample Composite Pass Fragment Shader"); + plconfig.layout = GPUPipeline::Layout::MultiTextureAndUBO; plconfig.fragment_shader = fs.get(); plconfig.color_format = VRAM_RT_FORMAT; if (!(m_downsample_composite_pass_pipeline = g_gpu_device->CreatePipeline(plconfig))) diff --git a/src/core/gpu_hw_d3d12.cpp b/src/core/gpu_hw_d3d12.cpp deleted file mode 100644 index c5d38e2eb..000000000 --- a/src/core/gpu_hw_d3d12.cpp +++ /dev/null @@ -1,1171 +0,0 @@ -// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin -// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) - -#if 0 - -#include "gpu_hw_d3d12.h" -#include "common/align.h" -#include "common/assert.h" -#include "common/log.h" -#include "common/scoped_guard.h" -#include "common/timer.h" -#include "gpu/d3d12/context.h" -#include "gpu/d3d12/descriptor_heap_manager.h" -#include "gpu/d3d12/shader_cache.h" -#include "gpu/d3d12/util.h" -#include "gpu/gpu_device.h" -#include "gpu_hw_shadergen.h" -#include "system.h" -Log_SetChannel(GPU_HW_D3D12); - -GPU_HW_D3D12::GPU_HW_D3D12() = default; - -GPU_HW_D3D12::~GPU_HW_D3D12() -{ - g_gpu_device->ClearDisplayTexture(); - - DestroyResources(); -} - -bool GPU_HW_D3D12::Initialize() -{ - SetCapabilities(); - - if (!GPU_HW::Initialize()) - return false; - - if (!CreateRootSignatures()) - { - Log_ErrorPrintf("Failed to create root signatures"); - return false; - } - - if (!CreateSamplers()) - { - Log_ErrorPrintf("Failed to create samplers"); - return false; - } - - if (!CreateVertexBuffer()) - { - Log_ErrorPrintf("Failed to create vertex buffer"); - return false; - } - - if (!CreateUniformBuffer()) - { - Log_ErrorPrintf("Failed to create uniform buffer"); - return false; - } - - if (!CreateTextureBuffer()) - { - Log_ErrorPrintf("Failed to create texture buffer"); - return false; - } - - if (!CreateBuffers()) - { - Log_ErrorPrintf("Failed to create framebuffer"); - return false; - } - - if (!CompilePipelines()) - { - Log_ErrorPrintf("Failed to compile pipelines"); - return false; - } - - RestoreGraphicsAPIState(); - UpdateDepthBufferFromMaskBit(); - return true; -} - -void GPU_HW_D3D12::Reset(bool clear_vram) -{ - GPU_HW::Reset(clear_vram); - - if (clear_vram) - ClearFramebuffer(); -} - -void GPU_HW_D3D12::RestoreGraphicsAPIState() -{ - ID3D12GraphicsCommandList* cmdlist = g_d3d12_context->GetCommandList(); - m_vram_texture.TransitionToState(D3D12_RESOURCE_STATE_RENDER_TARGET); - cmdlist->OMSetRenderTargets(1, &m_vram_texture.GetRTVOrDSVDescriptor().cpu_handle, FALSE, - &m_vram_depth_texture.GetRTVOrDSVDescriptor().cpu_handle); - - const D3D12_VERTEX_BUFFER_VIEW vbv{m_vertex_stream_buffer.GetGPUPointer(), m_vertex_stream_buffer.GetSize(), - sizeof(BatchVertex)}; - cmdlist->IASetVertexBuffers(0, 1, &vbv); - cmdlist->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); - - cmdlist->SetGraphicsRootSignature(m_batch_root_signature.Get()); - cmdlist->SetGraphicsRootConstantBufferView(0, - m_uniform_stream_buffer.GetGPUPointer() + m_current_uniform_buffer_offset); - cmdlist->SetGraphicsRootDescriptorTable(1, m_vram_read_texture.GetSRVDescriptor().gpu_handle); - cmdlist->SetGraphicsRootDescriptorTable(2, m_point_sampler.gpu_handle); - - D3D12::SetViewport(cmdlist, 0, 0, m_vram_texture.GetWidth(), m_vram_texture.GetHeight()); - - SetScissor(); -} - -void GPU_HW_D3D12::UpdateSettings() -{ - GPU_HW::UpdateSettings(); - - bool framebuffer_changed, shaders_changed; - UpdateHWSettings(&framebuffer_changed, &shaders_changed); - - if (framebuffer_changed) - { - RestoreGraphicsAPIState(); - ReadVRAM(0, 0, VRAM_WIDTH, VRAM_HEIGHT); - } - - // Everything should be finished executing before recreating resources. - g_gpu_device->ClearDisplayTexture(); - g_d3d12_context->ExecuteCommandList(true); - - if (framebuffer_changed) - CreateBuffers(); - - if (shaders_changed) - { - // clear it since we draw a loading screen and it's not in the correct state - DestroyPipelines(); - CompilePipelines(); - } - - // this has to be done here, because otherwise we're using destroyed pipelines in the same cmdbuffer - if (framebuffer_changed) - { - RestoreGraphicsAPIState(); - UpdateVRAM(0, 0, VRAM_WIDTH, VRAM_HEIGHT, m_vram_ptr, false, false); - UpdateDepthBufferFromMaskBit(); - UpdateDisplay(); - } -} - -#if 0 -void GPU_HW_D3D12::MapBatchVertexPointer(u32 required_vertices) -{ - DebugAssert(!m_batch_start_vertex_ptr); - - const u32 required_space = required_vertices * sizeof(BatchVertex); - if (!m_vertex_stream_buffer.ReserveMemory(required_space, sizeof(BatchVertex))) - { - Log_PerfPrintf("Executing command buffer while waiting for %u bytes in vertex stream buffer", required_space); - g_d3d12_context->ExecuteCommandList(false); - RestoreGraphicsAPIState(); - if (!m_vertex_stream_buffer.ReserveMemory(required_space, sizeof(BatchVertex))) - Panic("Failed to reserve vertex stream buffer memory"); - } - - m_batch_start_vertex_ptr = static_cast(m_vertex_stream_buffer.GetCurrentHostPointer()); - m_batch_current_vertex_ptr = m_batch_start_vertex_ptr; - m_batch_end_vertex_ptr = m_batch_start_vertex_ptr + (m_vertex_stream_buffer.GetCurrentSpace() / sizeof(BatchVertex)); - m_batch_base_vertex = m_vertex_stream_buffer.GetCurrentOffset() / sizeof(BatchVertex); -} - -void GPU_HW_D3D12::UnmapBatchVertexPointer(u32 used_vertices) -{ - DebugAssert(m_batch_start_vertex_ptr); - if (used_vertices > 0) - m_vertex_stream_buffer.CommitMemory(used_vertices * sizeof(BatchVertex)); - - m_batch_start_vertex_ptr = nullptr; - m_batch_end_vertex_ptr = nullptr; - m_batch_current_vertex_ptr = nullptr; -} - -void GPU_HW_D3D12::UploadUniformBuffer(const void* data, u32 data_size) -{ - if (!m_uniform_stream_buffer.ReserveMemory(data_size, D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT)) - { - Log_PerfPrintf("Executing command buffer while waiting for %u bytes in uniform stream buffer", data_size); - g_d3d12_context->ExecuteCommandList(false); - RestoreGraphicsAPIState(); - if (!m_uniform_stream_buffer.ReserveMemory(data_size, D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT)) - Panic("Failed to reserve uniform stream buffer memory"); - } - - m_current_uniform_buffer_offset = m_uniform_stream_buffer.GetCurrentOffset(); - std::memcpy(m_uniform_stream_buffer.GetCurrentHostPointer(), data, data_size); - m_uniform_stream_buffer.CommitMemory(data_size); - - g_d3d12_context->GetCommandList()->SetGraphicsRootConstantBufferView(0, m_uniform_stream_buffer.GetGPUPointer() + - m_current_uniform_buffer_offset); -} -#endif - -void GPU_HW_D3D12::SetCapabilities() -{ - // TODO: Query from device - const u32 max_texture_size = D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION; - const u32 max_texture_scale = max_texture_size / VRAM_WIDTH; - Log_InfoPrintf("Max texture size: %ux%u", max_texture_size, max_texture_size); - m_max_resolution_scale = max_texture_scale; - - m_max_multisamples = 1; - for (u32 multisamples = 2; multisamples < D3D12_MAX_MULTISAMPLE_SAMPLE_COUNT; multisamples++) - { - D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS fd = {DXGI_FORMAT_R8G8B8A8_UNORM, static_cast(multisamples)}; - - if (SUCCEEDED(g_d3d12_context->GetDevice()->CheckFeatureSupport(D3D12_FEATURE_MULTISAMPLE_QUALITY_LEVELS, &fd, - sizeof(fd))) && - fd.NumQualityLevels > 0) - { - m_max_multisamples = multisamples; - } - } - - m_supports_dual_source_blend = true; - m_supports_per_sample_shading = true; - m_supports_disable_color_perspective = true; - Log_InfoPrintf("Dual-source blend: %s", m_supports_dual_source_blend ? "supported" : "not supported"); - Log_InfoPrintf("Per-sample shading: %s", m_supports_per_sample_shading ? "supported" : "not supported"); - Log_InfoPrintf("Max multisamples: %u", m_max_multisamples); -} - -void GPU_HW_D3D12::DestroyResources() -{ - // Everything should be finished executing before recreating resources. - if (g_d3d12_context) - g_d3d12_context->ExecuteCommandList(true); - - DestroyBuffers(); - DestroyPipelines(); - - g_d3d12_context->GetSamplerHeapManager().Free(&m_point_sampler); - g_d3d12_context->GetSamplerHeapManager().Free(&m_linear_sampler); - g_d3d12_context->GetDescriptorHeapManager().Free(&m_texture_stream_buffer_srv); - - m_vertex_stream_buffer.Destroy(false); - m_uniform_stream_buffer.Destroy(false); - m_texture_stream_buffer.Destroy(false); - - m_single_sampler_root_signature.Reset(); - m_batch_root_signature.Reset(); -} - -bool GPU_HW_D3D12::CreateRootSignatures() -{ - D3D12::RootSignatureBuilder rsbuilder; - rsbuilder.SetInputAssemblerFlag(); - rsbuilder.AddCBVParameter(0, D3D12_SHADER_VISIBILITY_ALL); - rsbuilder.AddDescriptorTable(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 0, 1, D3D12_SHADER_VISIBILITY_PIXEL); - rsbuilder.AddDescriptorTable(D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER, 0, 1, D3D12_SHADER_VISIBILITY_PIXEL); - m_batch_root_signature = rsbuilder.Create(); - if (!m_batch_root_signature) - return false; - - rsbuilder.Add32BitConstants(0, MAX_PUSH_CONSTANTS_SIZE / sizeof(u32), D3D12_SHADER_VISIBILITY_ALL); - rsbuilder.AddDescriptorTable(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 0, 1, D3D12_SHADER_VISIBILITY_PIXEL); - rsbuilder.AddDescriptorTable(D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER, 0, 1, D3D12_SHADER_VISIBILITY_PIXEL); - m_single_sampler_root_signature = rsbuilder.Create(); - if (!m_single_sampler_root_signature) - return false; - - return true; -} - -bool GPU_HW_D3D12::CreateSamplers() -{ - D3D12_SAMPLER_DESC desc = {}; - D3D12::SetDefaultSampler(&desc); - desc.AddressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP; - desc.AddressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP; - desc.Filter = D3D12_FILTER_MIN_MAG_MIP_POINT; - - if (!g_d3d12_context->GetSamplerHeapManager().Allocate(&m_point_sampler)) - return false; - - g_d3d12_context->GetDevice()->CreateSampler(&desc, m_point_sampler.cpu_handle); - - desc.Filter = D3D12_FILTER_MIN_MAG_LINEAR_MIP_POINT; - - if (!g_d3d12_context->GetSamplerHeapManager().Allocate(&m_linear_sampler)) - return false; - - g_d3d12_context->GetDevice()->CreateSampler(&desc, m_linear_sampler.cpu_handle); - return true; -} - -bool GPU_HW_D3D12::CreateBuffers() -{ - DestroyBuffers(); - - // scale vram size to internal resolution - const u32 texture_width = VRAM_WIDTH * m_resolution_scale; - const u32 texture_height = VRAM_HEIGHT * m_resolution_scale; - const DXGI_FORMAT texture_format = DXGI_FORMAT_R8G8B8A8_UNORM; - const DXGI_FORMAT depth_format = DXGI_FORMAT_D16_UNORM; - - if (!m_vram_texture.Create(texture_width, texture_height, 1, 1, m_multisamples, texture_format, texture_format, - texture_format, DXGI_FORMAT_UNKNOWN, D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET) || - !m_vram_depth_texture.Create( - texture_width, texture_height, 1, 1, m_multisamples, depth_format, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, - depth_format, D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL | D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE) || - !m_vram_read_texture.Create(texture_width, texture_height, 1, 1, 1, texture_format, texture_format, - DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, D3D12_RESOURCE_FLAG_NONE) || - !m_display_texture.Create(texture_width, texture_height, 1, 1, 1, texture_format, texture_format, texture_format, - DXGI_FORMAT_UNKNOWN, D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET) || - !m_vram_readback_texture.Create(VRAM_WIDTH, VRAM_HEIGHT, 1, 1, 1, texture_format, texture_format, texture_format, - DXGI_FORMAT_UNKNOWN, D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET) || - !m_vram_readback_staging_texture.Create(VRAM_WIDTH / 2, VRAM_HEIGHT, texture_format, false)) - { - return false; - } - - D3D12::SetObjectName(m_vram_texture, "VRAM Texture"); - D3D12::SetObjectName(m_vram_depth_texture, "VRAM Depth Texture"); - D3D12::SetObjectName(m_vram_read_texture, "VRAM Read/Sample Texture"); - D3D12::SetObjectName(m_display_texture, "VRAM Display Texture"); - D3D12::SetObjectName(m_vram_read_texture, "VRAM Readback Texture"); - - m_vram_texture.TransitionToState(D3D12_RESOURCE_STATE_RENDER_TARGET); - m_vram_depth_texture.TransitionToState(D3D12_RESOURCE_STATE_DEPTH_WRITE); - m_vram_read_texture.TransitionToState(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); - - ClearDisplay(); - SetFullVRAMDirtyRectangle(); - return true; -} - -void GPU_HW_D3D12::ClearFramebuffer() -{ - static constexpr float clear_color[4] = {}; - - ID3D12GraphicsCommandList* cmdlist = g_d3d12_context->GetCommandList(); - cmdlist->ClearRenderTargetView(m_vram_texture.GetRTVOrDSVDescriptor(), clear_color, 0, nullptr); - cmdlist->ClearDepthStencilView(m_vram_depth_texture.GetRTVOrDSVDescriptor(), D3D12_CLEAR_FLAG_DEPTH, - m_pgxp_depth_buffer ? 1.0f : 0.0f, 0, 0, nullptr); - SetFullVRAMDirtyRectangle(); -} - -void GPU_HW_D3D12::DestroyBuffers() -{ - m_vram_read_texture.Destroy(false); - m_vram_depth_texture.Destroy(false); - m_vram_texture.Destroy(false); - m_vram_readback_texture.Destroy(false); - m_display_texture.Destroy(false); - m_vram_readback_staging_texture.Destroy(false); -} - -bool GPU_HW_D3D12::CreateVertexBuffer() -{ - if (!m_vertex_stream_buffer.Create(VERTEX_BUFFER_SIZE)) - return false; - - D3D12::SetObjectName(m_vertex_stream_buffer.GetBuffer(), "Vertex Stream Buffer"); - return true; -} - -bool GPU_HW_D3D12::CreateUniformBuffer() -{ - if (!m_uniform_stream_buffer.Create(UNIFORM_BUFFER_SIZE)) - return false; - - D3D12::SetObjectName(m_vertex_stream_buffer.GetBuffer(), "Uniform Stream Buffer"); - return true; -} - -bool GPU_HW_D3D12::CreateTextureBuffer() -{ - if (!m_texture_stream_buffer.Create(VRAM_UPDATE_TEXTURE_BUFFER_SIZE)) - return false; - - if (!g_d3d12_context->GetDescriptorHeapManager().Allocate(&m_texture_stream_buffer_srv)) - return false; - - D3D12_SHADER_RESOURCE_VIEW_DESC desc = {}; - desc.ViewDimension = D3D12_SRV_DIMENSION_BUFFER; - desc.Format = DXGI_FORMAT_R16_UINT; - desc.Buffer.NumElements = VRAM_UPDATE_TEXTURE_BUFFER_SIZE / sizeof(u16); - desc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; - g_d3d12_context->GetDevice()->CreateShaderResourceView(m_texture_stream_buffer.GetBuffer(), &desc, - m_texture_stream_buffer_srv); - - D3D12::SetObjectName(m_texture_stream_buffer.GetBuffer(), "Texture Stream Buffer"); - return true; -} - -bool GPU_HW_D3D12::CompilePipelines() -{ - D3D12::ShaderCache shader_cache; - shader_cache.Open(EmuFolders::Cache, g_d3d12_context->GetFeatureLevel(), g_settings.gpu_use_debug_device); - - GPU_HW_ShaderGen shadergen(g_gpu_device->GetRenderAPI(), m_resolution_scale, m_multisamples, m_per_sample_shading, - m_true_color, m_scaled_dithering, m_texture_filtering, m_using_uv_limits, - m_pgxp_depth_buffer, m_disable_color_perspective, m_supports_dual_source_blend); - - ShaderCompileProgressTracker progress("Compiling Pipelines", 2 + (4 * 9 * 2 * 2) + (2 * 4 * 5 * 9 * 2 * 2) + 1 + - (2 * 2) + 2 + 2 + 1 + 1 + (2 * 3) + 1); - - // vertex shaders - [textured] - // fragment shaders - [render_mode][texture_mode][dithering][interlacing] - DimensionalArray, 2> batch_vertex_shaders{}; - DimensionalArray, 2, 2, 9, 4> batch_fragment_shaders{}; - - for (u8 textured = 0; textured < 2; textured++) - { - const std::string vs = shadergen.GenerateBatchVertexShader(ConvertToBoolUnchecked(textured)); - batch_vertex_shaders[textured] = shader_cache.GetVertexShader(vs); - if (!batch_vertex_shaders[textured]) - return false; - - progress.Increment(); - } - - for (u8 render_mode = 0; render_mode < 4; render_mode++) - { - for (u8 texture_mode = 0; texture_mode < 9; texture_mode++) - { - for (u8 dithering = 0; dithering < 2; dithering++) - { - for (u8 interlacing = 0; interlacing < 2; interlacing++) - { - const std::string fs = shadergen.GenerateBatchFragmentShader( - static_cast(render_mode), static_cast(texture_mode), - ConvertToBoolUnchecked(dithering), ConvertToBoolUnchecked(interlacing)); - - batch_fragment_shaders[render_mode][texture_mode][dithering][interlacing] = shader_cache.GetPixelShader(fs); - if (!batch_fragment_shaders[render_mode][texture_mode][dithering][interlacing]) - return false; - - progress.Increment(); - } - } - } - } - - D3D12::GraphicsPipelineBuilder gpbuilder; - - // [depth_test][render_mode][texture_mode][transparency_mode][dithering][interlacing] - for (u8 depth_test = 0; depth_test < 2; depth_test++) - { - for (u8 render_mode = 0; render_mode < 4; render_mode++) - { - for (u8 transparency_mode = 0; transparency_mode < 5; transparency_mode++) - { - for (u8 texture_mode = 0; texture_mode < 9; texture_mode++) - { - for (u8 dithering = 0; dithering < 2; dithering++) - { - for (u8 interlacing = 0; interlacing < 2; interlacing++) - { - const bool textured = (static_cast(texture_mode) != GPUTextureMode::Disabled); - - gpbuilder.SetRootSignature(m_batch_root_signature.Get()); - gpbuilder.SetRenderTarget(0, m_vram_texture.GetDXGIFormat()); - gpbuilder.SetDepthStencilFormat(m_vram_depth_texture.GetDXGIFormat()); - - gpbuilder.AddVertexAttribute("ATTR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, offsetof(BatchVertex, x)); - gpbuilder.AddVertexAttribute("ATTR", 1, DXGI_FORMAT_R8G8B8A8_UNORM, 0, offsetof(BatchVertex, color)); - if (textured) - { - gpbuilder.AddVertexAttribute("ATTR", 2, DXGI_FORMAT_R32_UINT, 0, offsetof(BatchVertex, u)); - gpbuilder.AddVertexAttribute("ATTR", 3, DXGI_FORMAT_R32_UINT, 0, offsetof(BatchVertex, texpage)); - if (m_using_uv_limits) - gpbuilder.AddVertexAttribute("ATTR", 4, DXGI_FORMAT_R8G8B8A8_UNORM, 0, - offsetof(BatchVertex, uv_limits)); - } - - gpbuilder.SetPrimitiveTopologyType(D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE); - gpbuilder.SetVertexShader(batch_vertex_shaders[BoolToUInt8(textured)].Get()); - gpbuilder.SetPixelShader(batch_fragment_shaders[render_mode][texture_mode][dithering][interlacing].Get()); - - gpbuilder.SetRasterizationState(D3D12_FILL_MODE_SOLID, D3D12_CULL_MODE_NONE, false); - gpbuilder.SetDepthState(true, true, - (depth_test != 0) ? (m_pgxp_depth_buffer ? D3D12_COMPARISON_FUNC_LESS_EQUAL : - D3D12_COMPARISON_FUNC_GREATER_EQUAL) : - D3D12_COMPARISON_FUNC_ALWAYS); - gpbuilder.SetNoBlendingState(); - gpbuilder.SetMultisamples(m_multisamples); - - if ((static_cast(transparency_mode) != GPUTransparencyMode::Disabled && - (static_cast(render_mode) != BatchRenderMode::TransparencyDisabled && - static_cast(render_mode) != BatchRenderMode::OnlyOpaque)) || - m_texture_filtering != GPUTextureFilter::Nearest) - { - gpbuilder.SetBlendState( - 0, true, D3D12_BLEND_ONE, - m_supports_dual_source_blend ? D3D12_BLEND_SRC1_ALPHA : D3D12_BLEND_SRC_ALPHA, - (static_cast(transparency_mode) == - GPUTransparencyMode::BackgroundMinusForeground && - static_cast(render_mode) != BatchRenderMode::TransparencyDisabled && - static_cast(render_mode) != BatchRenderMode::OnlyOpaque) ? - D3D12_BLEND_OP_REV_SUBTRACT : - D3D12_BLEND_OP_ADD, - D3D12_BLEND_ONE, D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD); - } - - m_batch_pipelines[depth_test][render_mode][texture_mode][transparency_mode][dithering][interlacing] = - gpbuilder.Create(g_d3d12_context->GetDevice(), shader_cache); - if (!m_batch_pipelines[depth_test][render_mode][texture_mode][transparency_mode][dithering][interlacing]) - return false; - - D3D12::SetObjectNameFormatted( - m_batch_pipelines[depth_test][render_mode][texture_mode][transparency_mode][dithering][interlacing] - .Get(), - "Batch Pipeline %u,%u,%u,%u,%u,%u", depth_test, render_mode, texture_mode, transparency_mode, dithering, - interlacing); - - progress.Increment(); - } - } - } - } - } - } - - ComPtr fullscreen_quad_vertex_shader = - shader_cache.GetVertexShader(shadergen.GenerateScreenQuadVertexShader()); - if (!fullscreen_quad_vertex_shader) - return false; - - progress.Increment(); - - // common state - gpbuilder.SetRootSignature(m_single_sampler_root_signature.Get()); - gpbuilder.SetRenderTarget(0, m_vram_texture.GetDXGIFormat()); - gpbuilder.SetDepthStencilFormat(m_vram_depth_texture.GetDXGIFormat()); - gpbuilder.SetPrimitiveTopologyType(D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE); - gpbuilder.SetNoCullRasterizationState(); - gpbuilder.SetNoDepthTestState(); - gpbuilder.SetNoBlendingState(); - gpbuilder.SetVertexShader(fullscreen_quad_vertex_shader.Get()); - gpbuilder.SetMultisamples(m_multisamples); - gpbuilder.SetRenderTarget(0, m_vram_texture.GetDXGIFormat()); - gpbuilder.SetDepthStencilFormat(m_vram_depth_texture.GetDXGIFormat()); - - // VRAM fill - for (u8 wrapped = 0; wrapped < 2; wrapped++) - { - for (u8 interlaced = 0; interlaced < 2; interlaced++) - { - ComPtr fs = shader_cache.GetPixelShader( - shadergen.GenerateVRAMFillFragmentShader(ConvertToBoolUnchecked(wrapped), ConvertToBoolUnchecked(interlaced))); - if (!fs) - return false; - - gpbuilder.SetPixelShader(fs.Get()); - gpbuilder.SetDepthState(true, true, D3D12_COMPARISON_FUNC_ALWAYS); - - m_vram_fill_pipelines[wrapped][interlaced] = gpbuilder.Create(g_d3d12_context->GetDevice(), shader_cache, false); - if (!m_vram_fill_pipelines[wrapped][interlaced]) - return false; - - D3D12::SetObjectNameFormatted(m_vram_fill_pipelines[wrapped][interlaced].Get(), - "VRAM Fill Pipeline Wrapped=%u,Interlacing=%u", wrapped, interlaced); - - progress.Increment(); - } - } - - // VRAM copy - { - ComPtr fs = shader_cache.GetPixelShader(shadergen.GenerateVRAMCopyFragmentShader()); - if (!fs) - return false; - - gpbuilder.SetPixelShader(fs.Get()); - for (u8 depth_test = 0; depth_test < 2; depth_test++) - { - gpbuilder.SetDepthState((depth_test != 0), true, - (depth_test != 0) ? D3D12_COMPARISON_FUNC_GREATER_EQUAL : D3D12_COMPARISON_FUNC_ALWAYS); - - m_vram_copy_pipelines[depth_test] = gpbuilder.Create(g_d3d12_context->GetDevice(), shader_cache, false); - if (!m_vram_copy_pipelines[depth_test]) - return false; - - D3D12::SetObjectNameFormatted(m_vram_copy_pipelines[depth_test].Get(), "VRAM Copy Pipeline Depth=%u", depth_test); - - progress.Increment(); - } - } - - // VRAM write - { - ComPtr fs = shader_cache.GetPixelShader(shadergen.GenerateVRAMWriteFragmentShader(false)); - if (!fs) - return false; - - gpbuilder.SetPixelShader(fs.Get()); - for (u8 depth_test = 0; depth_test < 2; depth_test++) - { - gpbuilder.SetDepthState(true, true, - (depth_test != 0) ? D3D12_COMPARISON_FUNC_GREATER_EQUAL : D3D12_COMPARISON_FUNC_ALWAYS); - m_vram_write_pipelines[depth_test] = gpbuilder.Create(g_d3d12_context->GetDevice(), shader_cache, false); - if (!m_vram_write_pipelines[depth_test]) - return false; - - D3D12::SetObjectNameFormatted(m_vram_write_pipelines[depth_test].Get(), "VRAM Write Pipeline Depth=%u", - depth_test); - - progress.Increment(); - } - } - - // VRAM update depth - { - ComPtr fs = shader_cache.GetPixelShader(shadergen.GenerateVRAMUpdateDepthFragmentShader()); - if (!fs) - return false; - - gpbuilder.SetRootSignature(m_batch_root_signature.Get()); - gpbuilder.SetPixelShader(fs.Get()); - gpbuilder.SetDepthState(true, true, D3D12_COMPARISON_FUNC_ALWAYS); - gpbuilder.SetBlendState(0, false, D3D12_BLEND_ONE, D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD, D3D12_BLEND_ONE, - D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD, 0); - gpbuilder.ClearRenderTargets(); - - m_vram_update_depth_pipeline = gpbuilder.Create(g_d3d12_context->GetDevice(), shader_cache, false); - if (!m_vram_update_depth_pipeline) - return false; - - D3D12::SetObjectName(m_vram_update_depth_pipeline.Get(), "VRAM Update Depth Pipeline"); - - progress.Increment(); - } - - gpbuilder.Clear(); - - // VRAM read - { - ComPtr fs = shader_cache.GetPixelShader(shadergen.GenerateVRAMReadFragmentShader()); - if (!fs) - return false; - - gpbuilder.SetRootSignature(m_single_sampler_root_signature.Get()); - gpbuilder.SetPrimitiveTopologyType(D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE); - gpbuilder.SetVertexShader(fullscreen_quad_vertex_shader.Get()); - gpbuilder.SetPixelShader(fs.Get()); - gpbuilder.SetNoCullRasterizationState(); - gpbuilder.SetNoDepthTestState(); - gpbuilder.SetNoBlendingState(); - gpbuilder.SetRenderTarget(0, m_vram_readback_texture.GetDXGIFormat()); - gpbuilder.ClearDepthStencilFormat(); - - m_vram_readback_pipeline = gpbuilder.Create(g_d3d12_context->GetDevice(), shader_cache, false); - if (!m_vram_readback_pipeline) - return false; - - D3D12::SetObjectName(m_vram_update_depth_pipeline.Get(), "VRAM Readback Pipeline"); - - progress.Increment(); - } - - gpbuilder.Clear(); - - // Display - { - gpbuilder.SetRootSignature(m_single_sampler_root_signature.Get()); - gpbuilder.SetPrimitiveTopologyType(D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE); - gpbuilder.SetVertexShader(fullscreen_quad_vertex_shader.Get()); - gpbuilder.SetNoCullRasterizationState(); - gpbuilder.SetNoDepthTestState(); - gpbuilder.SetNoBlendingState(); - gpbuilder.SetRenderTarget(0, m_display_texture.GetDXGIFormat()); - - for (u8 depth_24 = 0; depth_24 < 2; depth_24++) - { - for (u8 interlace_mode = 0; interlace_mode < 3; interlace_mode++) - { - ComPtr fs = shader_cache.GetPixelShader(shadergen.GenerateDisplayFragmentShader( - ConvertToBoolUnchecked(depth_24), static_cast(interlace_mode), m_chroma_smoothing)); - if (!fs) - return false; - - gpbuilder.SetPixelShader(fs.Get()); - - m_display_pipelines[depth_24][interlace_mode] = - gpbuilder.Create(g_d3d12_context->GetDevice(), shader_cache, false); - if (!m_display_pipelines[depth_24][interlace_mode]) - return false; - - D3D12::SetObjectNameFormatted(m_display_pipelines[depth_24][interlace_mode].Get(), - "Display Pipeline Depth=%u Interlace=%u", depth_24, interlace_mode); - - progress.Increment(); - } - } - } - - // copy/blit - { - gpbuilder.Clear(); - gpbuilder.SetRootSignature(m_single_sampler_root_signature.Get()); - gpbuilder.SetPrimitiveTopologyType(D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE); - gpbuilder.SetVertexShader(fullscreen_quad_vertex_shader.Get()); - gpbuilder.SetNoCullRasterizationState(); - gpbuilder.SetNoDepthTestState(); - gpbuilder.SetNoBlendingState(); - gpbuilder.SetRenderTarget(0, DXGI_FORMAT_R8G8B8A8_UNORM); - - ComPtr fs = shader_cache.GetPixelShader(shadergen.GenerateCopyFragmentShader()); - if (!fs) - return false; - - gpbuilder.SetPixelShader(fs.Get()); - - m_copy_pipeline = gpbuilder.Create(g_d3d12_context->GetDevice(), shader_cache); - if (!m_copy_pipeline) - return false; - - progress.Increment(); - } - -#undef UPDATE_PROGRESS - - return true; -} - -void GPU_HW_D3D12::DestroyPipelines() -{ - m_batch_pipelines = {}; - m_vram_fill_pipelines = {}; - m_vram_write_pipelines = {}; - m_vram_copy_pipelines = {}; - m_vram_readback_pipeline.Reset(); - m_vram_update_depth_pipeline.Reset(); - - m_display_pipelines = {}; -} - -bool GPU_HW_D3D12::CreateTextureReplacementStreamBuffer() -{ - if (m_texture_replacment_stream_buffer.IsValid()) - return true; - - if (!m_texture_replacment_stream_buffer.Create(TEXTURE_REPLACEMENT_BUFFER_SIZE)) - { - Log_ErrorPrint("Failed to allocate texture replacement streaming buffer"); - return false; - } - - return true; -} - -bool GPU_HW_D3D12::BlitVRAMReplacementTexture(const TextureReplacementTexture* tex, u32 dst_x, u32 dst_y, u32 width, - u32 height) -{ - if (!CreateTextureReplacementStreamBuffer()) - return false; - - if (m_vram_write_replacement_texture.GetWidth() < tex->GetWidth() || - m_vram_write_replacement_texture.GetHeight() < tex->GetHeight()) - { - if (!m_vram_write_replacement_texture.Create(tex->GetWidth(), tex->GetHeight(), 1, 1, 1, DXGI_FORMAT_R8G8B8A8_UNORM, - DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, - D3D12_RESOURCE_FLAG_NONE)) - { - Log_ErrorPrint("Failed to create VRAM write replacement texture"); - return false; - } - } - - const u32 copy_pitch = Common::AlignUpPow2(tex->GetWidth() * sizeof(u32), D3D12_TEXTURE_DATA_PITCH_ALIGNMENT); - const u32 required_size = copy_pitch * tex->GetHeight(); - if (!m_texture_replacment_stream_buffer.ReserveMemory(required_size, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT)) - { - Log_PerfPrint("Executing command buffer while waiting for texture replacement buffer space"); - g_d3d12_context->ExecuteCommandList(false); - RestoreGraphicsAPIState(); - if (!m_texture_replacment_stream_buffer.ReserveMemory(required_size, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT)) - { - Log_ErrorPrintf("Failed to allocate %u bytes from texture replacement streaming buffer", required_size); - return false; - } - } - - // buffer -> texture - const u32 sb_offset = m_texture_replacment_stream_buffer.GetCurrentOffset(); - D3D12::Texture::CopyToUploadBuffer(tex->GetPixels(), tex->GetPitch(), tex->GetHeight(), - m_texture_replacment_stream_buffer.GetCurrentHostPointer(), copy_pitch); - m_texture_replacment_stream_buffer.CommitMemory(required_size); - m_vram_write_replacement_texture.CopyFromBuffer(0, 0, tex->GetWidth(), tex->GetHeight(), copy_pitch, - m_texture_replacment_stream_buffer.GetBuffer(), sb_offset); - m_vram_write_replacement_texture.TransitionToState(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); - - // texture -> vram - const float uniforms[] = { - 0.0f, 0.0f, static_cast(tex->GetWidth()) / static_cast(m_vram_write_replacement_texture.GetWidth()), - static_cast(tex->GetHeight()) / static_cast(m_vram_write_replacement_texture.GetHeight())}; - ID3D12GraphicsCommandList* cmdlist = g_d3d12_context->GetCommandList(); - cmdlist->SetGraphicsRootSignature(m_single_sampler_root_signature.Get()); - cmdlist->SetGraphicsRoot32BitConstants(0, sizeof(uniforms) / sizeof(u32), uniforms, 0); - cmdlist->SetGraphicsRootDescriptorTable(1, m_vram_write_replacement_texture.GetSRVDescriptor()); - cmdlist->SetGraphicsRootDescriptorTable(2, m_linear_sampler.gpu_handle); - cmdlist->SetPipelineState(m_copy_pipeline.Get()); - D3D12::SetViewportAndScissor(cmdlist, dst_x, dst_y, width, height); - cmdlist->DrawInstanced(3, 1, 0, 0); - RestoreGraphicsAPIState(); - return true; -} - -#if 0 -void GPU_HW_D3D12::DrawBatchVertices(BatchRenderMode render_mode, u32 base_vertex, u32 num_vertices) -{ - ID3D12GraphicsCommandList* cmdlist = g_d3d12_context->GetCommandList(); - - // [primitive][depth_test][render_mode][texture_mode][transparency_mode][dithering][interlacing] - ID3D12PipelineState* pipeline = - m_batch_pipelines[BoolToUInt8(m_batch.check_mask_before_draw || m_batch.use_depth_buffer)][static_cast( - render_mode)][static_cast(m_batch.texture_mode)][static_cast(m_batch.transparency_mode)] - [BoolToUInt8(m_batch.dithering)][BoolToUInt8(m_batch.interlacing)] - .Get(); - - cmdlist->SetPipelineState(pipeline); - cmdlist->DrawInstanced(num_vertices, 1, base_vertex, 0); -} -#endif - -void GPU_HW_D3D12::ClearDisplay() -{ - GPU_HW::ClearDisplay(); - - g_gpu_device->ClearDisplayTexture(); - - static constexpr float clear_color[4] = {0.0f, 0.0f, 0.0f, 1.0f}; - m_display_texture.TransitionToState(D3D12_RESOURCE_STATE_RENDER_TARGET); - g_d3d12_context->GetCommandList()->ClearRenderTargetView(m_display_texture.GetRTVOrDSVDescriptor(), clear_color, 0, - nullptr); -} - -void GPU_HW_D3D12::UpdateDisplay() -{ - GPU_HW::UpdateDisplay(); - - if (g_settings.debugging.show_vram) - { - if (IsUsingMultisampling()) - { - UpdateVRAMReadTexture(); - g_gpu_device->SetDisplayTexture(&m_vram_read_texture, 0, 0, m_vram_read_texture.GetWidth(), - m_vram_read_texture.GetHeight()); - } - else - { - m_vram_texture.TransitionToState(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); - g_gpu_device->SetDisplayTexture(&m_vram_texture, 0, 0, m_vram_texture.GetWidth(), m_vram_texture.GetHeight()); - } - g_gpu_device->SetDisplayParameters(VRAM_WIDTH, VRAM_HEIGHT, 0, 0, VRAM_WIDTH, VRAM_HEIGHT, - static_cast(VRAM_WIDTH) / static_cast(VRAM_HEIGHT)); - } - else - { - g_gpu_device->SetDisplayParameters(m_crtc_state.display_width, m_crtc_state.display_height, - m_crtc_state.display_origin_left, m_crtc_state.display_origin_top, - m_crtc_state.display_vram_width, m_crtc_state.display_vram_height, - GetDisplayAspectRatio()); - - const u32 resolution_scale = m_GPUSTAT.display_area_color_depth_24 ? 1 : m_resolution_scale; - const u32 vram_offset_x = m_crtc_state.display_vram_left; - const u32 vram_offset_y = m_crtc_state.display_vram_top; - const u32 scaled_vram_offset_x = vram_offset_x * resolution_scale; - const u32 scaled_vram_offset_y = vram_offset_y * resolution_scale; - const u32 display_width = m_crtc_state.display_vram_width; - const u32 display_height = m_crtc_state.display_vram_height; - const u32 scaled_display_width = display_width * resolution_scale; - const u32 scaled_display_height = display_height * resolution_scale; - const InterlacedRenderMode interlaced = GetInterlacedRenderMode(); - - if (IsDisplayDisabled()) - { - g_gpu_device->ClearDisplayTexture(); - } - else if (!m_GPUSTAT.display_area_color_depth_24 && interlaced == InterlacedRenderMode::None && - !IsUsingMultisampling() && (scaled_vram_offset_x + scaled_display_width) <= m_vram_texture.GetWidth() && - (scaled_vram_offset_y + scaled_display_height) <= m_vram_texture.GetHeight()) - { - m_vram_texture.TransitionToState(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); - g_gpu_device->SetDisplayTexture(&m_vram_texture, scaled_vram_offset_x, scaled_vram_offset_y, - scaled_display_width, scaled_display_height); - } - else - { - const u32 reinterpret_field_offset = (interlaced != InterlacedRenderMode::None) ? GetInterlacedDisplayField() : 0; - const u32 reinterpret_start_x = m_crtc_state.regs.X * resolution_scale; - const u32 reinterpret_crop_left = (m_crtc_state.display_vram_left - m_crtc_state.regs.X) * resolution_scale; - const u32 uniforms[4] = {reinterpret_start_x, scaled_vram_offset_y + reinterpret_field_offset, - reinterpret_crop_left, reinterpret_field_offset}; - - ID3D12GraphicsCommandList* cmdlist = g_d3d12_context->GetCommandList(); - m_display_texture.TransitionToState(D3D12_RESOURCE_STATE_RENDER_TARGET); - m_vram_texture.TransitionToState(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); - - cmdlist->OMSetRenderTargets(1, &m_display_texture.GetRTVOrDSVDescriptor().cpu_handle, FALSE, nullptr); - cmdlist->SetGraphicsRootSignature(m_single_sampler_root_signature.Get()); - cmdlist->SetGraphicsRoot32BitConstants(0, sizeof(uniforms) / sizeof(u32), uniforms, 0); - cmdlist->SetGraphicsRootDescriptorTable(1, m_vram_texture.GetSRVDescriptor()); - cmdlist->SetPipelineState( - m_display_pipelines[BoolToUInt8(m_GPUSTAT.display_area_color_depth_24)][static_cast(interlaced)].Get()); - D3D12::SetViewportAndScissor(cmdlist, 0, 0, scaled_display_width, scaled_display_height); - cmdlist->DrawInstanced(3, 1, 0, 0); - - m_display_texture.TransitionToState(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); - m_vram_texture.TransitionToState(D3D12_RESOURCE_STATE_RENDER_TARGET); - - g_gpu_device->SetDisplayTexture(&m_display_texture, 0, 0, scaled_display_width, scaled_display_height); - - RestoreGraphicsAPIState(); - } - } -} - -void GPU_HW_D3D12::ReadVRAM(u32 x, u32 y, u32 width, u32 height) -{ - if (IsUsingSoftwareRendererForReadbacks()) - { - ReadSoftwareRendererVRAM(x, y, width, height); - return; - } - - // Get bounds with wrap-around handled. - const Common::Rectangle copy_rect = GetVRAMTransferBounds(x, y, width, height); - const u32 encoded_width = (copy_rect.GetWidth() + 1) / 2; - const u32 encoded_height = copy_rect.GetHeight(); - - ID3D12GraphicsCommandList* cmdlist = g_d3d12_context->GetCommandList(); - m_vram_texture.TransitionToState(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); - m_vram_readback_texture.TransitionToState(D3D12_RESOURCE_STATE_RENDER_TARGET); - - // Encode the 24-bit texture as 16-bit. - const u32 uniforms[4] = {copy_rect.left, copy_rect.top, copy_rect.GetWidth(), copy_rect.GetHeight()}; - cmdlist->OMSetRenderTargets(1, &m_vram_readback_texture.GetRTVOrDSVDescriptor().cpu_handle, FALSE, nullptr); - cmdlist->SetGraphicsRootSignature(m_single_sampler_root_signature.Get()); - cmdlist->SetGraphicsRoot32BitConstants(0, sizeof(uniforms) / sizeof(u32), uniforms, 0); - cmdlist->SetGraphicsRootDescriptorTable(1, m_vram_texture.GetSRVDescriptor()); - cmdlist->SetPipelineState(m_vram_readback_pipeline.Get()); - D3D12::SetViewportAndScissor(cmdlist, 0, 0, encoded_width, encoded_height); - cmdlist->DrawInstanced(3, 1, 0, 0); - - m_vram_readback_texture.TransitionToState(D3D12_RESOURCE_STATE_COPY_SOURCE); - m_vram_texture.TransitionToState(D3D12_RESOURCE_STATE_RENDER_TARGET); - - // Stage the readback. - m_vram_readback_staging_texture.CopyFromTexture(m_vram_readback_texture, 0, 0, 0, 0, 0, encoded_width, - encoded_height); - - // And copy it into our shadow buffer (will execute command buffer and stall). - m_vram_readback_staging_texture.ReadPixels(0, 0, encoded_width, encoded_height, - &m_vram_shadow[copy_rect.top * VRAM_WIDTH + copy_rect.left], - VRAM_WIDTH * sizeof(u16)); - - RestoreGraphicsAPIState(); -} - -void GPU_HW_D3D12::FillVRAM(u32 x, u32 y, u32 width, u32 height, u32 color) -{ - if (IsUsingSoftwareRendererForReadbacks()) - FillSoftwareRendererVRAM(x, y, width, height, color); - - // TODO: Use fast clear - GPU_HW::FillVRAM(x, y, width, height, color); - - const VRAMFillUBOData uniforms = GetVRAMFillUBOData(x, y, width, height, color); - ID3D12GraphicsCommandList* cmdlist = g_d3d12_context->GetCommandList(); - - const bool wrapped = IsVRAMFillOversized(x, y, width, height); - const bool interlaced = IsInterlacedRenderingEnabled(); - if (!wrapped && !interlaced) - { - const D3D12_RECT rc = {static_cast(x * m_resolution_scale), static_cast(y * m_resolution_scale), - static_cast((x + width) * m_resolution_scale), - static_cast((y + height) * m_resolution_scale)}; - cmdlist->ClearRenderTargetView(m_vram_texture.GetRTVOrDSVDescriptor(), uniforms.u_fill_color, 1, &rc); - cmdlist->ClearDepthStencilView(m_vram_depth_texture.GetRTVOrDSVDescriptor(), D3D12_CLEAR_FLAG_DEPTH, - uniforms.u_fill_color[3], 0, 1, &rc); - return; - } - - cmdlist->SetGraphicsRootSignature(m_single_sampler_root_signature.Get()); - cmdlist->SetGraphicsRoot32BitConstants(0, sizeof(uniforms) / sizeof(u32), &uniforms, 0); - cmdlist->SetGraphicsRootDescriptorTable(1, g_d3d12_context->GetNullSRVDescriptor()); - cmdlist->SetPipelineState(m_vram_fill_pipelines[BoolToUInt8(IsVRAMFillOversized(x, y, width, height))] - [BoolToUInt8(IsInterlacedRenderingEnabled())] - .Get()); - - const Common::Rectangle bounds(GetVRAMTransferBounds(x, y, width, height)); - D3D12::SetViewportAndScissor(cmdlist, bounds.left * m_resolution_scale, bounds.top * m_resolution_scale, - bounds.GetWidth() * m_resolution_scale, bounds.GetHeight() * m_resolution_scale); - - cmdlist->DrawInstanced(3, 1, 0, 0); - - RestoreGraphicsAPIState(); -} - -void GPU_HW_D3D12::UpdateVRAM(u32 x, u32 y, u32 width, u32 height, const void* data, bool set_mask, bool check_mask) -{ - if (IsUsingSoftwareRendererForReadbacks()) - UpdateSoftwareRendererVRAM(x, y, width, height, data, set_mask, check_mask); - - const Common::Rectangle bounds = GetVRAMTransferBounds(x, y, width, height); - GPU_HW::UpdateVRAM(bounds.left, bounds.top, bounds.GetWidth(), bounds.GetHeight(), data, set_mask, check_mask); - - if (!check_mask) - { - const TextureReplacementTexture* rtex = g_texture_replacements.GetVRAMWriteReplacement(width, height, data); - if (rtex && BlitVRAMReplacementTexture(rtex, x * m_resolution_scale, y * m_resolution_scale, - width * m_resolution_scale, height * m_resolution_scale)) - { - return; - } - } - - const u32 data_size = width * height * sizeof(u16); - const u32 alignment = D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT; // ??? - if (!m_texture_stream_buffer.ReserveMemory(data_size, alignment)) - { - Log_PerfPrintf("Executing command buffer while waiting for %u bytes in stream buffer", data_size); - g_d3d12_context->ExecuteCommandList(false); - RestoreGraphicsAPIState(); - if (!m_texture_stream_buffer.ReserveMemory(data_size, alignment)) - { - Panic("Failed to allocate space in stream buffer for VRAM write"); - return; - } - } - - const u32 start_index = m_texture_stream_buffer.GetCurrentOffset() / sizeof(u16); - std::memcpy(m_texture_stream_buffer.GetCurrentHostPointer(), data, data_size); - m_texture_stream_buffer.CommitMemory(data_size); - - const VRAMWriteUBOData uniforms = GetVRAMWriteUBOData(x, y, width, height, start_index, set_mask, check_mask); - - ID3D12GraphicsCommandList* cmdlist = g_d3d12_context->GetCommandList(); - cmdlist->SetGraphicsRootSignature(m_single_sampler_root_signature.Get()); - cmdlist->SetGraphicsRoot32BitConstants(0, sizeof(uniforms) / sizeof(u32), &uniforms, 0); - cmdlist->SetGraphicsRootDescriptorTable(1, m_texture_stream_buffer_srv); - cmdlist->SetPipelineState(m_vram_write_pipelines[BoolToUInt8(check_mask)].Get()); - - // the viewport should already be set to the full vram, so just adjust the scissor - const Common::Rectangle scaled_bounds = bounds * m_resolution_scale; - D3D12::SetScissor(cmdlist, scaled_bounds.left, scaled_bounds.top, scaled_bounds.GetWidth(), - scaled_bounds.GetHeight()); - - cmdlist->DrawInstanced(3, 1, 0, 0); - - RestoreGraphicsAPIState(); -} - -void GPU_HW_D3D12::CopyVRAM(u32 src_x, u32 src_y, u32 dst_x, u32 dst_y, u32 width, u32 height) -{ - if (IsUsingSoftwareRendererForReadbacks()) - CopySoftwareRendererVRAM(src_x, src_y, dst_x, dst_y, width, height); - - if (UseVRAMCopyShader(src_x, src_y, dst_x, dst_y, width, height) || IsUsingMultisampling()) - { - const Common::Rectangle src_bounds = GetVRAMTransferBounds(src_x, src_y, width, height); - const Common::Rectangle dst_bounds = GetVRAMTransferBounds(dst_x, dst_y, width, height); - if (m_vram_dirty_rect.Intersects(src_bounds)) - UpdateVRAMReadTexture(); - IncludeVRAMDirtyRectangle(dst_bounds); - - const VRAMCopyUBOData uniforms(GetVRAMCopyUBOData(src_x, src_y, dst_x, dst_y, width, height)); - const Common::Rectangle dst_bounds_scaled(dst_bounds * m_resolution_scale); - - ID3D12GraphicsCommandList* cmdlist = g_d3d12_context->GetCommandList(); - cmdlist->SetGraphicsRootSignature(m_single_sampler_root_signature.Get()); - cmdlist->SetGraphicsRoot32BitConstants(0, sizeof(uniforms) / sizeof(u32), &uniforms, 0); - cmdlist->SetGraphicsRootDescriptorTable(1, m_vram_read_texture.GetSRVDescriptor()); - cmdlist->SetPipelineState(m_vram_copy_pipelines[BoolToUInt8(m_GPUSTAT.check_mask_before_draw)].Get()); - D3D12::SetViewportAndScissor(cmdlist, dst_bounds_scaled.left, dst_bounds_scaled.top, dst_bounds_scaled.GetWidth(), - dst_bounds_scaled.GetHeight()); - cmdlist->DrawInstanced(3, 1, 0, 0); - - RestoreGraphicsAPIState(); - - if (m_GPUSTAT.check_mask_before_draw) - m_current_depth++; - - return; - } - - if (m_vram_dirty_rect.Intersects(Common::Rectangle::FromExtents(src_x, src_y, width, height))) - UpdateVRAMReadTexture(); - - GPU_HW::CopyVRAM(src_x, src_y, dst_x, dst_y, width, height); - - src_x *= m_resolution_scale; - src_y *= m_resolution_scale; - dst_x *= m_resolution_scale; - dst_y *= m_resolution_scale; - width *= m_resolution_scale; - height *= m_resolution_scale; - - const D3D12_TEXTURE_COPY_LOCATION src = {m_vram_read_texture.GetResource(), - D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX}; - const D3D12_TEXTURE_COPY_LOCATION dst = {m_vram_texture.GetResource(), D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX}; - const D3D12_BOX src_box = {src_x, src_y, 0u, src_x + width, src_y + height, 1u}; - - m_vram_texture.TransitionToState(D3D12_RESOURCE_STATE_COPY_DEST); - m_vram_read_texture.TransitionToState(D3D12_RESOURCE_STATE_COPY_SOURCE); - - g_d3d12_context->GetCommandList()->CopyTextureRegion(&dst, dst_x, dst_y, 0, &src, &src_box); - - m_vram_read_texture.TransitionToState(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); - m_vram_texture.TransitionToState(D3D12_RESOURCE_STATE_RENDER_TARGET); -} - -#if 0 - -void GPU_HW_D3D12::UpdateVRAMReadTexture() -{ - ID3D12GraphicsCommandList* cmdlist = g_d3d12_context->GetCommandList(); - - const auto scaled_rect = m_vram_dirty_rect * m_resolution_scale; - - if (m_vram_texture.IsMultisampled()) - { - m_vram_texture.TransitionToState(D3D12_RESOURCE_STATE_RESOLVE_SOURCE); - m_vram_read_texture.TransitionToState(D3D12_RESOURCE_STATE_RESOLVE_DEST); - cmdlist->ResolveSubresource(m_vram_read_texture, 0, m_vram_texture, 0, m_vram_texture.GetDXGIFormat()); - } - else - { - const D3D12_TEXTURE_COPY_LOCATION src = {m_vram_texture.GetResource(), D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX}; - const D3D12_TEXTURE_COPY_LOCATION dst = {m_vram_read_texture.GetResource(), - D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX}; - const D3D12_BOX src_box = {scaled_rect.left, scaled_rect.top, 0u, scaled_rect.right, scaled_rect.bottom, 1u}; - m_vram_texture.TransitionToState(D3D12_RESOURCE_STATE_COPY_SOURCE); - m_vram_read_texture.TransitionToState(D3D12_RESOURCE_STATE_COPY_DEST); - cmdlist->CopyTextureRegion(&dst, scaled_rect.left, scaled_rect.top, 0, &src, &src_box); - } - - m_vram_read_texture.TransitionToState(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); - m_vram_texture.TransitionToState(D3D12_RESOURCE_STATE_RENDER_TARGET); - - GPU_HW::UpdateVRAMReadTexture(); -} - - -void GPU_HW_D3D12::ClearDepthBuffer() -{ - ID3D12GraphicsCommandList* cmdlist = g_d3d12_context->GetCommandList(); - cmdlist->ClearDepthStencilView(m_vram_depth_texture.GetRTVOrDSVDescriptor(), D3D12_CLEAR_FLAG_DEPTH, - m_pgxp_depth_buffer ? 1.0f : 0.0f, 0, 0, nullptr); -} - -std::unique_ptr GPU::CreateHardwareD3D12Renderer() -{ - if (!Host::AcquireHostDisplay(RenderAPI::D3D12)) - { - Log_ErrorPrintf("Host render API is incompatible"); - return nullptr; - } - - std::unique_ptr gpu(std::make_unique()); - if (!gpu->Initialize()) - return nullptr; - - return gpu; -} -#endif - -#endif \ No newline at end of file diff --git a/src/core/gpu_hw_d3d12.h b/src/core/gpu_hw_d3d12.h deleted file mode 100644 index 1afbc004c..000000000 --- a/src/core/gpu_hw_d3d12.h +++ /dev/null @@ -1,107 +0,0 @@ -// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin -// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) - -#pragma once - -#if 0 -#include "common/dimensional_array.h" -#include "gpu/d3d12/staging_texture.h" -#include "gpu/d3d12/stream_buffer.h" -#include "gpu/d3d12/texture.h" -#include "gpu_hw.h" -#include "texture_replacements.h" -#include -#include -#include - -class GPU_HW_D3D12 final : public GPU_HW -{ -public: - template - using ComPtr = Microsoft::WRL::ComPtr; - - GPU_HW_D3D12(); - ~GPU_HW_D3D12() override; - - bool Initialize() override; - void Reset(bool clear_vram) override; - - void RestoreGraphicsAPIState() override; - void UpdateSettings() override; - -protected: - void ClearDisplay() override; - void UpdateDisplay() override; - void ReadVRAM(u32 x, u32 y, u32 width, u32 height) override; - void FillVRAM(u32 x, u32 y, u32 width, u32 height, u32 color) override; - void UpdateVRAM(u32 x, u32 y, u32 width, u32 height, const void* data, bool set_mask, bool check_mask) override; - void CopyVRAM(u32 src_x, u32 src_y, u32 dst_x, u32 dst_y, u32 width, u32 height) override; - -private: - enum : u32 - { - MAX_PUSH_CONSTANTS_SIZE = 64, - TEXTURE_REPLACEMENT_BUFFER_SIZE = 64 * 1024 * 1024, - }; - void SetCapabilities(); - void DestroyResources(); - - bool CreateRootSignatures(); - bool CreateSamplers(); - - bool CreateBuffers(); - void ClearFramebuffer(); - void DestroyBuffers(); - - bool CreateVertexBuffer(); - bool CreateUniformBuffer(); - bool CreateTextureBuffer(); - - bool CompilePipelines(); - void DestroyPipelines(); - - bool CreateTextureReplacementStreamBuffer(); - bool BlitVRAMReplacementTexture(const TextureReplacementTexture* tex, u32 dst_x, u32 dst_y, u32 width, u32 height); - - ComPtr m_batch_root_signature; - ComPtr m_single_sampler_root_signature; - - D3D12::Texture m_vram_texture; - D3D12::Texture m_vram_depth_texture; - D3D12::Texture m_vram_read_texture; - D3D12::Texture m_vram_readback_texture; - D3D12::StagingTexture m_vram_readback_staging_texture; - D3D12::Texture m_display_texture; - - D3D12::DescriptorHandle m_point_sampler; - D3D12::DescriptorHandle m_linear_sampler; - - D3D12::StreamBuffer m_vertex_stream_buffer; - D3D12::StreamBuffer m_uniform_stream_buffer; - D3D12::StreamBuffer m_texture_stream_buffer; - D3D12::DescriptorHandle m_texture_stream_buffer_srv; - - u32 m_current_uniform_buffer_offset = 0; - - // [depth_test][render_mode][texture_mode][transparency_mode][dithering][interlacing] - DimensionalArray, 2, 2, 5, 9, 4, 2> m_batch_pipelines; - - // [wrapped][interlaced] - DimensionalArray, 2, 2> m_vram_fill_pipelines; - - // [depth_test] - std::array, 2> m_vram_write_pipelines; - std::array, 2> m_vram_copy_pipelines; - - ComPtr m_vram_readback_pipeline; - ComPtr m_vram_update_depth_pipeline; - - // [depth_24][interlace_mode] - DimensionalArray, 3, 2> m_display_pipelines; - - ComPtr m_copy_pipeline; - D3D12::Texture m_vram_write_replacement_texture; - D3D12::StreamBuffer m_texture_replacment_stream_buffer; -}; - -#endif \ No newline at end of file diff --git a/src/core/gpu_hw_shadergen.cpp b/src/core/gpu_hw_shadergen.cpp index 4acb72713..bc32c2ed4 100644 --- a/src/core/gpu_hw_shadergen.cpp +++ b/src/core/gpu_hw_shadergen.cpp @@ -1417,8 +1417,8 @@ std::string GPU_HW_ShaderGen::GenerateAdaptiveDownsampleBlurFragmentShader() std::stringstream ss; WriteHeader(ss); WriteCommonFunctions(ss); - DeclareTexture(ss, "samp0", 0, false); WriteAdaptiveDownsampleUniformBuffer(ss); + DeclareTexture(ss, "samp0", 0, false); // mipmap_blur.glsl ported from parallel-rsx. DeclareFragmentEntryPoint(ss, 0, 1, {}, false, 1, false, false, false, false); diff --git a/src/duckstation-qt/displaysettingswidget.cpp b/src/duckstation-qt/displaysettingswidget.cpp index 5678aa97e..07f641fa7 100644 --- a/src/duckstation-qt/displaysettingswidget.cpp +++ b/src/duckstation-qt/displaysettingswidget.cpp @@ -13,7 +13,7 @@ // For enumerating adapters. #ifdef _WIN32 #include "core/gpu/d3d11_device.h" -#include "core/gpu/d3d12_gpu_device.h" +#include "core/gpu/d3d12_device.h" #endif #ifdef WITH_VULKAN #include "core/gpu/vulkan_device.h" @@ -200,7 +200,7 @@ void DisplaySettingsWidget::populateGPUAdaptersAndResolutions() break; case GPURenderer::HardwareD3D12: - aml = D3D12GPUDevice::StaticGetAdapterAndModeList(); + aml = D3D12Device::StaticGetAdapterAndModeList(); break; #endif #ifdef WITH_VULKAN diff --git a/src/frontend-common/cubeb_audio_stream.cpp b/src/frontend-common/cubeb_audio_stream.cpp index fb257df3c..b66232969 100644 --- a/src/frontend-common/cubeb_audio_stream.cpp +++ b/src/frontend-common/cubeb_audio_stream.cpp @@ -16,7 +16,6 @@ Log_SetChannel(CubebAudioStream); #ifdef _WIN32 #include "common/windows_headers.h" #include -#pragma comment(lib, "Ole32.lib") #endif static void StateCallback(cubeb_stream* stream, void* user_ptr, cubeb_state state);