From 9e26964a96a1811b3c6bb9af71e1d3a6a4ce778c Mon Sep 17 00:00:00 2001 From: Robin Kertels Date: Wed, 23 Aug 2023 18:20:14 +0200 Subject: [PATCH 001/246] [d3d9] Divide projected textures by w if ProjectedCount is 0 --- src/d3d9/d3d9_fixed_function.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/d3d9/d3d9_fixed_function.cpp b/src/d3d9/d3d9_fixed_function.cpp index 5581bfa18..099985f04 100644 --- a/src/d3d9/d3d9_fixed_function.cpp +++ b/src/d3d9/d3d9_fixed_function.cpp @@ -1809,8 +1809,9 @@ namespace dxvk { texcoord, texcoord, texcoordCnt, indices.data()); uint32_t projIdx = m_fsKey.Stages[i].Contents.ProjectedCount; - if (projIdx == 0 || projIdx > texcoordCnt) - projIdx = texcoordCnt; + if (projIdx == 0 || projIdx > texcoordCnt) { + projIdx = 4; // Always use w if ProjectedCount is 0. + } --projIdx; uint32_t projValue = 0; From 4705de572592ccd8f90d435c7be035fa1bc29229 Mon Sep 17 00:00:00 2001 From: Margen67 Date: Sun, 3 Sep 2023 12:42:56 -0700 Subject: [PATCH 002/246] [util] Escape . --- src/util/config/config.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index 31977698f..5a4de3f71 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -294,7 +294,7 @@ namespace dxvk { }} }, /* Garden Warfare 2 Won't start on amd Id without atiadlxx */ - { R"(\\GW2.Main_Win64_Retail\.exe$)", {{ + { R"(\\GW2\.Main_Win64_Retail\.exe$)", {{ { "dxgi.customVendorId", "10de" }, }} }, /* DayZ */ @@ -396,7 +396,7 @@ namespace dxvk { { "dxgi.syncInterval", "1" }, }} }, /* Blizzard Entertainment Battle.net */ - { R"(\\Battle.net\.exe$)", {{ + { R"(\\Battle\.net\.exe$)", {{ { "dxvk.maxChunkSize", "1" }, }} }, /* Bladestorm Nightmare * @@ -804,7 +804,7 @@ namespace dxvk { { "d3d9.maxFrameRate", "60" }, }} }, /* Project: Snowblind */ - { R"(\\Snowblind(.SP|.MP|.exe)$)", {{ + { R"(\\Snowblind\.(SP|MP|exe)$)", {{ { "d3d9.maxFrameRate", "60" }, }} }, /* Drakensang: The Dark Eye */ From a53f0e81682af98b82e5c2c4834455dafa473bcc Mon Sep 17 00:00:00 2001 From: Margen67 Date: Sun, 3 Sep 2023 12:43:23 -0700 Subject: [PATCH 003/246] [util] Remove whitespace --- src/util/config/config.cpp | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index 5a4de3f71..280ff6995 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -37,7 +37,7 @@ namespace dxvk { { R"(\\evelauncher\.exe$)", {{ { "d3d11.maxFeatureLevel", "12_1" }, }} }, - /* The Evil Within: Submits command lists * + /* The Evil Within: Submits command lists * * multiple times */ { R"(\\EvilWithin(Demo)?\.exe$)", {{ { "d3d11.dcSingleUseMode", "False" }, @@ -464,7 +464,7 @@ namespace dxvk { { "d3d9.memoryTrackTest", "True" }, }} }, /* Dead Space uses the a NULL render target instead - of a 1x1 one if DF24 is NOT supported + of a 1x1 one if DF24 is NOT supported Mouse and physics issues above 60 FPS Built-in Vsync Locks the game to 30 FPS */ { R"(\\Dead Space\.exe$)", {{ @@ -660,7 +660,7 @@ namespace dxvk { { "d3d9.memoryTrackTest", "True" }, { "d3d9.maxAvailableMemory", "2048" }, }} }, - /* Myst V End of Ages + /* Myst V End of Ages Game has white textures on amd radv. Expects Nvidia, Intel or ATI VendorId. "Radeon" in gpu description also works */ @@ -675,7 +675,7 @@ namespace dxvk { { R"(\\swtor\.exe$)", {{ { "d3d9.forceSamplerTypeSpecConstants", "True" }, }} }, - /* Bionic Commando + /* Bionic Commando Physics break at high fps */ { R"(\\bionic_commando\.exe$)", {{ { "d3d9.maxFrameRate", "60" }, @@ -811,11 +811,11 @@ namespace dxvk { { R"(\\drakensang\.exe$)", {{ { "d3d9.deferSurfaceCreation", "True" }, }} }, - + /**********************************************/ /* D3D12 GAMES (vkd3d-proton with dxvk dxgi) */ /**********************************************/ - + /* Diablo 4 - Will complain about missing * * GPU unless dxgi Id match actual GPU Id */ { R"(\\Diablo IV\.exe$)", {{ @@ -841,7 +841,7 @@ namespace dxvk { return ch == ' ' || ch == '\x9' || ch == '\r'; } - + static bool isValidKeyChar(char ch) { return (ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') @@ -878,12 +878,12 @@ namespace dxvk { while (n < e) key << line[n++]; - + ctx.active = key.str() == env::getExeName(); } else { while (n < line.size() && isValidKeyChar(line[n])) key << line[n++]; - + // Check whether the next char is a '=' n = skipWhitespace(line, n); if (n >= line.size() || line[n] != '=') @@ -903,7 +903,7 @@ namespace dxvk { } else value << line[n++]; } - + if (ctx.active) config.setOption(key.str(), value.str()); } @@ -963,7 +963,7 @@ namespace dxvk { int32_t& result) { if (value.size() == 0) return false; - + // Parse sign, don't allow '+' int32_t sign = 1; size_t start = 0; @@ -979,7 +979,7 @@ namespace dxvk { for (size_t i = start; i < value.size(); i++) { if (value[i] < '0' || value[i] > '9') return false; - + intval *= 10; intval += value[i] - '0'; } @@ -988,8 +988,8 @@ namespace dxvk { result = sign * intval; return true; } - - + + bool Config::parseOptionValue( const std::string& value, float& result) { @@ -1091,7 +1091,7 @@ namespace dxvk { std::regex expr(pair.first, std::regex::extended | std::regex::icase); return std::regex_search(appName, expr); }); - + if (appConfig != g_appDefaults.end()) { // Inform the user that we loaded a default config Logger::info(str::format("Found built-in config:")); @@ -1115,7 +1115,7 @@ namespace dxvk { if (filePath == "") filePath = "dxvk.conf"; - + // Open the file if it exists std::ifstream stream(str::topath(filePath.c_str()).c_str()); From 80e075406b1b7d9d2508c9dc05e9745b3d3cf7e2 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Mon, 4 Sep 2023 17:15:39 +0200 Subject: [PATCH 004/246] [meta] Release 2.3 --- RELEASE | 2 +- meson.build | 2 +- src/dxvk/dxvk_instance.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/RELEASE b/RELEASE index 8bbe6cf74..bb576dbde 100644 --- a/RELEASE +++ b/RELEASE @@ -1 +1 @@ -2.2 +2.3 diff --git a/meson.build b/meson.build index 724b3aeeb..b571729d0 100644 --- a/meson.build +++ b/meson.build @@ -1,4 +1,4 @@ -project('dxvk', ['c', 'cpp'], version : 'v2.2', meson_version : '>= 0.49', default_options : [ 'cpp_std=c++17', 'warning_level=2' ]) +project('dxvk', ['c', 'cpp'], version : 'v2.3', meson_version : '>= 0.49', default_options : [ 'cpp_std=c++17', 'warning_level=2' ]) cpu_family = target_machine.cpu_family() platform = target_machine.system() diff --git a/src/dxvk/dxvk_instance.cpp b/src/dxvk/dxvk_instance.cpp index b32a3ea07..cffde93b1 100644 --- a/src/dxvk/dxvk_instance.cpp +++ b/src/dxvk/dxvk_instance.cpp @@ -176,7 +176,7 @@ namespace dxvk { VkApplicationInfo appInfo = { VK_STRUCTURE_TYPE_APPLICATION_INFO }; appInfo.pApplicationName = appName.c_str(); appInfo.pEngineName = "DXVK"; - appInfo.engineVersion = VK_MAKE_VERSION(2, 2, 0); + appInfo.engineVersion = VK_MAKE_VERSION(2, 3, 0); appInfo.apiVersion = VK_MAKE_VERSION(1, 3, 0); VkInstanceCreateInfo info = { VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO }; From 5828f0e2b9b233b32f7b5edb54dc4e04014d3b55 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Thu, 7 Sep 2023 03:58:39 +0200 Subject: [PATCH 005/246] Revert "[dxgi] Use VK_FORMAT_A8_UNORM if available" This reverts commit 6a5ed02db33cd9b5b8166c1679b14f85c9648281. Native A8 breaks Crysis 2/3 Remastered for unknown reasons. --- src/dxgi/dxgi_format.cpp | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/src/dxgi/dxgi_format.cpp b/src/dxgi/dxgi_format.cpp index 996d36514..306f773ec 100644 --- a/src/dxgi/dxgi_format.cpp +++ b/src/dxgi/dxgi_format.cpp @@ -318,10 +318,12 @@ namespace dxvk { VK_FORMAT_R8_UINT, VK_IMAGE_ASPECT_COLOR_BIT }, // DXGI_FORMAT_A8_UNORM - { VK_FORMAT_A8_UNORM_KHR, + { VK_FORMAT_R8_UNORM, VK_FORMAT_UNDEFINED, - VK_FORMAT_R8_UINT, - VK_IMAGE_ASPECT_COLOR_BIT, 0 }, + VK_FORMAT_UNDEFINED, + VK_IMAGE_ASPECT_COLOR_BIT, 0, + { VK_COMPONENT_SWIZZLE_ZERO, VK_COMPONENT_SWIZZLE_ZERO, + VK_COMPONENT_SWIZZLE_ZERO, VK_COMPONENT_SWIZZLE_R }}, // DXGI_FORMAT_R1_UNORM { }, // Unsupported // DXGI_FORMAT_R9G9B9E5_SHAREDEXP @@ -858,16 +860,6 @@ namespace dxvk { RemapDepthFormat(DXGI_FORMAT_X24_TYPELESS_G8_UINT, VK_FORMAT_D32_SFLOAT_S8_UINT); RemapDepthFormat(DXGI_FORMAT_D24_UNORM_S8_UINT, VK_FORMAT_D32_SFLOAT_S8_UINT); } - - // Map A8_UNORM to R8_UNORM with appropriate swizzles if necessary - if (!CheckImageFormatSupport(device, VK_FORMAT_A8_UNORM_KHR, - VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_BIT | - VK_FORMAT_FEATURE_2_COLOR_ATTACHMENT_BIT | - VK_FORMAT_FEATURE_2_STORAGE_IMAGE_BIT)) { - RemapColorFormat(DXGI_FORMAT_A8_UNORM, VK_FORMAT_R8_UNORM, - { VK_COMPONENT_SWIZZLE_ZERO, VK_COMPONENT_SWIZZLE_ZERO, - VK_COMPONENT_SWIZZLE_ZERO, VK_COMPONENT_SWIZZLE_R }); - } } From 41191af3b140e1c6a44ae50125bfc4897f8281f0 Mon Sep 17 00:00:00 2001 From: Ellie Hermaszewska Date: Thu, 7 Sep 2023 18:41:31 +0800 Subject: [PATCH 006/246] A few more WinDef types in windows_base.h These specific ones are used in MS's d3dx12 headers --- include/native/windows/windows_base.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/include/native/windows/windows_base.h b/include/native/windows/windows_base.h index 0f6e0c390..fe1a2a0ef 100644 --- a/include/native/windows/windows_base.h +++ b/include/native/windows/windows_base.h @@ -48,7 +48,6 @@ typedef const void* LPCVOID; typedef size_t SIZE_T; typedef int8_t INT8; - typedef uint8_t UINT8; typedef uint8_t BYTE; @@ -56,9 +55,13 @@ typedef int16_t SHORT; typedef uint16_t USHORT; typedef int64_t LONGLONG; +typedef int64_t INT64; + typedef uint64_t ULONGLONG; +typedef uint64_t UINT64; typedef intptr_t LONG_PTR; +typedef uintptr_t ULONG_PTR; typedef float FLOAT; From c113b791a148e049171b62cd87d15cc2e61bf894 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Fri, 8 Sep 2023 02:59:52 +0200 Subject: [PATCH 007/246] [util] Enable 60 FPS lock for Aviary Attorney This game (or nw.js) comes with a hard-coded frame rate limit that behaves more like a random number generator which happens to average out at around 16ms on a good day. Fix this complete mess by enabling ours on top of that. --- src/util/config/config.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index 280ff6995..c5d8125ec 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -807,6 +807,10 @@ namespace dxvk { { R"(\\Snowblind\.(SP|MP|exe)$)", {{ { "d3d9.maxFrameRate", "60" }, }} }, + /* Project: Snowblind */ + { R"(\\Aviary Attorney\\nw\.exe$)", {{ + { "d3d9.maxFrameRate", "60" }, + }} }, /* Drakensang: The Dark Eye */ { R"(\\drakensang\.exe$)", {{ { "d3d9.deferSurfaceCreation", "True" }, From f93cfbc26af61b78819941fb93aecc4d3f75b04c Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 13 Sep 2023 14:42:48 -0600 Subject: [PATCH 008/246] [d3d11] Pass device directly to D3D11DXGIKeyedMutex --- src/d3d11/d3d11_buffer.cpp | 2 +- src/d3d11/d3d11_resource.cpp | 14 +++++++------- src/d3d11/d3d11_resource.h | 6 ++++-- src/d3d11/d3d11_texture.cpp | 10 +++++----- 4 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/d3d11/d3d11_buffer.cpp b/src/d3d11/d3d11_buffer.cpp index 11087a979..6d035fdb1 100644 --- a/src/d3d11/d3d11_buffer.cpp +++ b/src/d3d11/d3d11_buffer.cpp @@ -12,7 +12,7 @@ namespace dxvk { const D3D11_ON_12_RESOURCE_INFO* p11on12Info) : D3D11DeviceChild(pDevice), m_desc (*pDesc), - m_resource (this), + m_resource (this, pDevice), m_d3d10 (this) { DxvkBufferCreateInfo info; info.flags = 0; diff --git a/src/d3d11/d3d11_resource.cpp b/src/d3d11/d3d11_resource.cpp index 92b2d6582..db3c0f10c 100644 --- a/src/d3d11/d3d11_resource.cpp +++ b/src/d3d11/d3d11_resource.cpp @@ -9,11 +9,10 @@ namespace dxvk { D3D11DXGIKeyedMutex::D3D11DXGIKeyedMutex( - ID3D11Resource* pResource) - : m_resource(pResource) { - Com device; - m_resource->GetDevice(&device); - m_device = static_cast(device.ptr()); + ID3D11Resource* pResource, + D3D11Device* pDevice) + : m_resource(pResource), + m_device(pDevice) { m_supported = m_device->GetDXVKDevice()->features().khrWin32KeyedMutex && m_device->GetDXVKDevice()->vkd()->wine_vkAcquireKeyedMutex != nullptr @@ -120,9 +119,10 @@ namespace dxvk { } D3D11DXGIResource::D3D11DXGIResource( - ID3D11Resource* pResource) + ID3D11Resource* pResource, + D3D11Device* pDevice) : m_resource(pResource), - m_keyedMutex(pResource) { + m_keyedMutex(pResource, pDevice) { } diff --git a/src/d3d11/d3d11_resource.h b/src/d3d11/d3d11_resource.h index 8dc732209..8085031f2 100644 --- a/src/d3d11/d3d11_resource.h +++ b/src/d3d11/d3d11_resource.h @@ -30,7 +30,8 @@ namespace dxvk { public: D3D11DXGIKeyedMutex( - ID3D11Resource* pResource); + ID3D11Resource* pResource, + D3D11Device* pDevice); ~D3D11DXGIKeyedMutex(); @@ -88,7 +89,8 @@ namespace dxvk { public: D3D11DXGIResource( - ID3D11Resource* pResource); + ID3D11Resource* pResource, + D3D11Device* pDevice); ~D3D11DXGIResource(); diff --git a/src/d3d11/d3d11_texture.cpp b/src/d3d11/d3d11_texture.cpp index 7a8c60439..b75908e12 100644 --- a/src/d3d11/d3d11_texture.cpp +++ b/src/d3d11/d3d11_texture.cpp @@ -1099,7 +1099,7 @@ namespace dxvk { m_texture (this, pDevice, pDesc, p11on12Info, D3D11_RESOURCE_DIMENSION_TEXTURE1D, 0, VK_NULL_HANDLE, nullptr), m_interop (this, &m_texture), m_surface (this, &m_texture), - m_resource(this), + m_resource(this, pDevice), m_d3d10 (this) { } @@ -1205,7 +1205,7 @@ namespace dxvk { m_texture (this, pDevice, pDesc, p11on12Info, D3D11_RESOURCE_DIMENSION_TEXTURE2D, 0, VK_NULL_HANDLE, hSharedHandle), m_interop (this, &m_texture), m_surface (this, &m_texture), - m_resource (this), + m_resource (this, pDevice), m_d3d10 (this), m_swapChain (nullptr) { } @@ -1220,7 +1220,7 @@ namespace dxvk { m_texture (this, pDevice, pDesc, nullptr, D3D11_RESOURCE_DIMENSION_TEXTURE2D, DxgiUsage, vkImage, nullptr), m_interop (this, &m_texture), m_surface (this, &m_texture), - m_resource (this), + m_resource (this, pDevice), m_d3d10 (this), m_swapChain (nullptr) { @@ -1236,7 +1236,7 @@ namespace dxvk { m_texture (this, pDevice, pDesc, nullptr, D3D11_RESOURCE_DIMENSION_TEXTURE2D, DxgiUsage, VK_NULL_HANDLE, nullptr), m_interop (this, &m_texture), m_surface (this, &m_texture), - m_resource (this), + m_resource (this, pDevice), m_d3d10 (this), m_swapChain (pSwapChain) { @@ -1384,7 +1384,7 @@ namespace dxvk { : D3D11DeviceChild(pDevice), m_texture (this, pDevice, pDesc, p11on12Info, D3D11_RESOURCE_DIMENSION_TEXTURE3D, 0, VK_NULL_HANDLE, nullptr), m_interop (this, &m_texture), - m_resource(this), + m_resource(this, pDevice), m_d3d10 (this) { } From 83dc4678df7657821992a927d4aeee510fafa224 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Thu, 14 Sep 2023 16:48:28 +0200 Subject: [PATCH 009/246] [util] Set maximum frame latency to 1 for Age of Empires 2 (2013) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Game seems to be doing something horrible on its own, literally impossible to make it run smoothly. This at least seems to limit excursions to ±10ms and fix the camera flinging back and forth when running the game through Gamescope. --- src/util/config/config.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index c5d8125ec..bfa1ed0b2 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -807,7 +807,7 @@ namespace dxvk { { R"(\\Snowblind\.(SP|MP|exe)$)", {{ { "d3d9.maxFrameRate", "60" }, }} }, - /* Project: Snowblind */ + /* Aviary Attorney */ { R"(\\Aviary Attorney\\nw\.exe$)", {{ { "d3d9.maxFrameRate", "60" }, }} }, @@ -815,6 +815,10 @@ namespace dxvk { { R"(\\drakensang\.exe$)", {{ { "d3d9.deferSurfaceCreation", "True" }, }} }, + /* Age of Empires 2 - janky frame timing */ + { R"(\\AoK HD\.exe$)", {{ + { "d3d9.maxFrameLatency", "1" }, + }} }, /**********************************************/ /* D3D12 GAMES (vkd3d-proton with dxvk dxgi) */ From 0632da19357fb3d846ea36ef07166eb93b09173a Mon Sep 17 00:00:00 2001 From: WinterSnowfall Date: Tue, 19 Sep 2023 17:54:03 +0300 Subject: [PATCH 010/246] [d3d9] Add a device compatibility mode for d3d8 --- src/d3d9/d3d9_bridge.cpp | 6 +++++- src/d3d9/d3d9_bridge.h | 8 ++++++++ src/d3d9/d3d9_device.cpp | 6 ++++-- src/d3d9/d3d9_device.h | 18 +++++++++++++++--- src/d3d9/d3d9_stateblock.cpp | 5 +++-- 5 files changed, 35 insertions(+), 8 deletions(-) diff --git a/src/d3d9/d3d9_bridge.cpp b/src/d3d9/d3d9_bridge.cpp index 88448ba9a..6bb4801a7 100644 --- a/src/d3d9/d3d9_bridge.cpp +++ b/src/d3d9/d3d9_bridge.cpp @@ -32,6 +32,10 @@ namespace dxvk { m_device->m_implicitSwapchain->SetApiName(name); } + void DxvkD3D8Bridge::SetD3D8CompatibilityMode(const bool compatMode) { + m_device->SetD3D8CompatibilityMode(compatMode); + } + HRESULT DxvkD3D8Bridge::UpdateTextureFromBuffer( IDirect3DSurface9* pDestSurface, IDirect3DSurface9* pSrcSurface, @@ -104,4 +108,4 @@ namespace dxvk { const Config* DxvkD3D8InterfaceBridge::GetConfig() const { return &m_interface->GetInstance()->config(); } -} \ No newline at end of file +} diff --git a/src/d3d9/d3d9_bridge.h b/src/d3d9/d3d9_bridge.h index f1c121043..163d4130e 100644 --- a/src/d3d9/d3d9_bridge.h +++ b/src/d3d9/d3d9_bridge.h @@ -30,6 +30,13 @@ IDxvkD3D8Bridge : public IUnknown { */ virtual void SetAPIName(const char* name) = 0; + /** + * \brief Enables or disables D3D9-specific device features and validations + * + * \param [in] compatibility state + */ + virtual void SetD3D8CompatibilityMode(const bool compatMode) = 0; + /** * \brief Updates a D3D9 surface from a D3D9 buffer * @@ -83,6 +90,7 @@ namespace dxvk { void** ppvObject); void SetAPIName(const char* name); + void SetD3D8CompatibilityMode(const bool compatMode); HRESULT UpdateTextureFromBuffer( IDirect3DSurface9* pDestSurface, diff --git a/src/d3d9/d3d9_device.cpp b/src/d3d9/d3d9_device.cpp index 7c14efa1c..a2f6e9c33 100644 --- a/src/d3d9/d3d9_device.cpp +++ b/src/d3d9/d3d9_device.cpp @@ -2360,7 +2360,8 @@ namespace dxvk { try { const Com sb = new D3D9StateBlock(this, ConvertStateBlockType(Type)); *ppSB = sb.ref(); - m_losableResourceCounter++; + if (!m_isD3D8Compatible) + m_losableResourceCounter++; return D3D_OK; } @@ -2392,7 +2393,8 @@ namespace dxvk { return D3DERR_INVALIDCALL; *ppSB = m_recorder.ref(); - m_losableResourceCounter++; + if (!m_isD3D8Compatible) + m_losableResourceCounter++; m_recorder = nullptr; return D3D_OK; diff --git a/src/d3d9/d3d9_device.h b/src/d3d9/d3d9_device.h index 8b979106c..b4a292854 100644 --- a/src/d3d9/d3d9_device.h +++ b/src/d3d9/d3d9_device.h @@ -970,6 +970,17 @@ namespace dxvk { void TouchMappedTexture(D3D9CommonTexture* pTexture); void RemoveMappedTexture(D3D9CommonTexture* pTexture); + bool IsD3D8Compatible() const { + return m_isD3D8Compatible; + } + + void SetD3D8CompatibilityMode(bool compatMode) { + if (compatMode) + Logger::info("The D3D9 device is now operating in D3D8 compatibility mode."); + + m_isD3D8Compatible = compatMode; + } + // Device Lost bool IsDeviceLost() const { return m_deviceLostState != D3D9DeviceLostState::Ok; @@ -1318,9 +1329,10 @@ namespace dxvk { D3D9ShaderMasks m_psShaderMasks = FixedFunctionMask; bool m_isSWVP; - bool m_amdATOC = false; - bool m_nvATOC = false; - bool m_ffZTest = false; + bool m_isD3D8Compatible = false; + bool m_amdATOC = false; + bool m_nvATOC = false; + bool m_ffZTest = false; VkImageLayout m_hazardLayout = VK_IMAGE_LAYOUT_GENERAL; diff --git a/src/d3d9/d3d9_stateblock.cpp b/src/d3d9/d3d9_stateblock.cpp index 9bb7181b9..0d5bacd98 100644 --- a/src/d3d9/d3d9_stateblock.cpp +++ b/src/d3d9/d3d9_stateblock.cpp @@ -18,7 +18,8 @@ namespace dxvk { } D3D9StateBlock::~D3D9StateBlock() { - m_parent->DecrementLosableCounter(); + if (!m_parent->IsD3D8Compatible()) + m_parent->DecrementLosableCounter(); } HRESULT STDMETHODCALLTYPE D3D9StateBlock::QueryInterface( @@ -575,4 +576,4 @@ namespace dxvk { this->Capture(); } -} \ No newline at end of file +} From 494f7fd38df9b0ec6b1d3137a6528d2f31d55452 Mon Sep 17 00:00:00 2001 From: Robin Kertels Date: Mon, 18 Sep 2023 19:49:25 +0200 Subject: [PATCH 011/246] [d3d9] Only set initial NeedsUpload for D3DPOOL_MANAGED textures --- src/d3d9/d3d9_common_texture.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/d3d9/d3d9_common_texture.cpp b/src/d3d9/d3d9_common_texture.cpp index 6aed7038d..1f66a626a 100644 --- a/src/d3d9/d3d9_common_texture.cpp +++ b/src/d3d9/d3d9_common_texture.cpp @@ -31,11 +31,12 @@ namespace dxvk { AddDirtyBox(nullptr, i); } - if (m_desc.Pool != D3DPOOL_DEFAULT) { + if (m_desc.Pool != D3DPOOL_DEFAULT && pSharedHandle) { + throw DxvkError("D3D9: Incompatible pool type for texture sharing."); + } + + if (IsPoolManaged(m_desc.Pool)) { SetAllNeedUpload(); - if (pSharedHandle) { - throw DxvkError("D3D9: Incompatible pool type for texture sharing."); - } } m_mapping = pDevice->LookupFormat(m_desc.Format); From f0ff0007dc7bfc76383b61bad011078758caa3c7 Mon Sep 17 00:00:00 2001 From: Blisto91 <47954800+Blisto91@users.noreply.github.com> Date: Mon, 2 Oct 2023 22:42:11 +0200 Subject: [PATCH 012/246] [util] Enable cachedDynamicBuffers for Battlestations Midway Helps performance dips that can happen in some areas --- src/util/config/config.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index bfa1ed0b2..c0de0ee71 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -819,6 +819,10 @@ namespace dxvk { { R"(\\AoK HD\.exe$)", {{ { "d3d9.maxFrameLatency", "1" }, }} }, + /* Battlestations Midway */ + { R"(\\Battlestationsmidway\.exe$)", {{ + { "d3d9.cachedDynamicBuffers", "True" }, + }} }, /**********************************************/ /* D3D12 GAMES (vkd3d-proton with dxvk dxgi) */ From 4d974685c9c26e41df4406f9fbf30ce46c9234f6 Mon Sep 17 00:00:00 2001 From: WinterSnowfall Date: Sun, 17 Sep 2023 21:53:12 +0300 Subject: [PATCH 013/246] [d3d9] Mark presenter for recreation on device reset with deferSurfaceCreation --- src/d3d9/d3d9_device.cpp | 3 +++ src/d3d9/d3d9_device.h | 7 +++++++ src/d3d9/d3d9_swapchain.cpp | 2 ++ 3 files changed, 12 insertions(+) diff --git a/src/d3d9/d3d9_device.cpp b/src/d3d9/d3d9_device.cpp index a2f6e9c33..30a4a5b29 100644 --- a/src/d3d9/d3d9_device.cpp +++ b/src/d3d9/d3d9_device.cpp @@ -482,6 +482,9 @@ namespace dxvk { Flush(); SynchronizeCsThread(DxvkCsThread::SynchronizeAll); + if (m_d3d9Options.deferSurfaceCreation) + m_deviceHasBeenReset = true; + return D3D_OK; } diff --git a/src/d3d9/d3d9_device.h b/src/d3d9/d3d9_device.h index b4a292854..99a2a7439 100644 --- a/src/d3d9/d3d9_device.h +++ b/src/d3d9/d3d9_device.h @@ -1033,6 +1033,12 @@ namespace dxvk { bool CanSWVP() const { return m_behaviorFlags & (D3DCREATE_MIXED_VERTEXPROCESSING | D3DCREATE_SOFTWARE_VERTEXPROCESSING); } + + // Device Reset detection for D3D9SwapChainEx::Present + bool IsDeviceReset() { + return std::exchange(m_deviceHasBeenReset, false); + } + void DetermineConstantLayouts(bool canSWVP); D3D9BufferSlice AllocUPBuffer(VkDeviceSize size); @@ -1337,6 +1343,7 @@ namespace dxvk { VkImageLayout m_hazardLayout = VK_IMAGE_LAYOUT_GENERAL; bool m_usingGraphicsPipelines = false; + bool m_deviceHasBeenReset = false; DxvkDepthBiasRepresentation m_depthBiasRepresentation = { VK_DEPTH_BIAS_REPRESENTATION_LEAST_REPRESENTABLE_VALUE_FORMAT_EXT, false }; float m_depthBiasScale = 0.0f; diff --git a/src/d3d9/d3d9_swapchain.cpp b/src/d3d9/d3d9_swapchain.cpp index 9e950be0d..1d9c50c14 100644 --- a/src/d3d9/d3d9_swapchain.cpp +++ b/src/d3d9/d3d9_swapchain.cpp @@ -147,6 +147,8 @@ namespace dxvk { bool recreate = false; recreate |= m_wctx->presenter == nullptr; recreate |= m_dialog != m_lastDialog; + if (options->deferSurfaceCreation) + recreate |= m_parent->IsDeviceReset(); if (m_wctx->presenter != nullptr) { m_dirty |= m_wctx->presenter->setSyncInterval(presentInterval) != VK_SUCCESS; From 552d2f0a6d5c5710124ce921d5f9e132082c584a Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 9 Oct 2023 15:16:30 -0600 Subject: [PATCH 014/246] [dxgi] Add useMonitorFallback option And enable it for CP2077. It is supposed to be dropped once QueryDisplayConfig optimization is in Proton Wine. --- dxvk.conf | 11 +++++++++++ src/dxgi/dxgi_factory.cpp | 3 +++ src/dxgi/dxgi_options.cpp | 4 ++++ src/dxgi/dxgi_options.h | 3 +++ src/util/config/config.cpp | 7 +++++++ 5 files changed, 28 insertions(+) diff --git a/dxvk.conf b/dxvk.conf index 198188f64..13ffb6f49 100644 --- a/dxvk.conf +++ b/dxvk.conf @@ -623,3 +623,14 @@ # DO NOT CHANGE THIS UNLESS YOU HAVE A VERY GOOD REASON. # d3d9.textureMemory = 100 + +# Always enumerate all monitors on each dxgi output +# +# Used to avoid performance degradation in some games +# (will be deprecated once QueryDisplayConfig optimization +# is in Proton Wine). +# +# Supported values: +# - True/False + +# dxgi.useMonitorFallback = False diff --git a/src/dxgi/dxgi_factory.cpp b/src/dxgi/dxgi_factory.cpp index 84b3d404b..d14ace5dd 100644 --- a/src/dxgi/dxgi_factory.cpp +++ b/src/dxgi/dxgi_factory.cpp @@ -124,10 +124,13 @@ namespace dxvk { } } + // If any monitors are left on the list, enable the // fallback to always enumerate all monitors. if ((m_monitorFallback = !monitors.empty())) Logger::warn("DXGI: Found monitors not associated with any adapter, using fallback"); + else + m_monitorFallback = m_options.useMonitorFallback; } diff --git a/src/dxgi/dxgi_options.cpp b/src/dxgi/dxgi_options.cpp index c78bc0ee4..c23660777 100644 --- a/src/dxgi/dxgi_options.cpp +++ b/src/dxgi/dxgi_options.cpp @@ -101,6 +101,10 @@ namespace dxvk { Logger::info("HDR was configured to be enabled, but has been force disabled as a UE4 DX11 game was detected."); this->enableHDR = false; } + + this->useMonitorFallback = config.getOption("dxgi.useMonitorFallback", env::getEnvVar("DXVK_MONITOR_FALLBACK") == "1"); + if (this->useMonitorFallback) + Logger::info("Enabled useMonitorFallback option"); } } diff --git a/src/dxgi/dxgi_options.h b/src/dxgi/dxgi_options.h index 326c0b50f..c64924713 100644 --- a/src/dxgi/dxgi_options.h +++ b/src/dxgi/dxgi_options.h @@ -44,6 +44,9 @@ namespace dxvk { /// Enable HDR bool enableHDR; + + /// Use monitor fallback to enumerating all monitors per output + bool useMonitorFallback; }; } diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index c0de0ee71..f4d5ea7a7 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -846,6 +846,13 @@ namespace dxvk { { R"(\\RiftApart\.exe$)", {{ { "dxgi.hideNvidiaGpu", "False" }, }} }, + /* CP2077 enumerates display outputs each frame. + * Avoid using QueryDisplayConfig to avoid + * performance degradation until the + * optimization of that function is in Proton. */ + { R"(\\Cyberpunk2077\.exe$)", {{ + { "dxgi.useMonitorFallback", "True" }, + }} }, }}; From e00db245574c7a51c802a91e86a794f9c632e5d4 Mon Sep 17 00:00:00 2001 From: Blisto91 <47954800+Blisto91@users.noreply.github.com> Date: Tue, 10 Oct 2023 16:15:09 +0200 Subject: [PATCH 015/246] [util] Enable useMonitorFallback for Holocure Temporary performance drop workaround until QueryDisplayConfig optimization is in Proton Wine. --- src/util/config/config.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index f4d5ea7a7..af888be9d 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -417,6 +417,11 @@ namespace dxvk { { R"(\\RidersRepublic(_BE)?\.exe$)", {{ { "dxgi.hideAmdGpu", "True" }, }} }, + /* HoloCure - Save the Fans! + Same as Cyberpunk 2077 */ + { R"(\\HoloCure\.exe$)", {{ + { "dxgi.useMonitorFallback", "True" }, + }} }, /**********************************************/ /* D3D9 GAMES */ From f45911a28f6e6184f82487e983b4a6af6923e61c Mon Sep 17 00:00:00 2001 From: Blisto91 <47954800+Blisto91@users.noreply.github.com> Date: Wed, 11 Oct 2023 12:52:28 +0200 Subject: [PATCH 016/246] [util] Disable allowDirectBufferMapping for SkyDrift Works around a alt tab OOM crash --- src/util/config/config.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index af888be9d..638773f1d 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -828,6 +828,11 @@ namespace dxvk { { R"(\\Battlestationsmidway\.exe$)", {{ { "d3d9.cachedDynamicBuffers", "True" }, }} }, + /* SkyDrift * + * Works around alt tab OOM crash */ + { R"(\\SkyDrift\.exe$)" , {{ + { "d3d9.allowDirectBufferMapping", "False" }, + }} }, /**********************************************/ /* D3D12 GAMES (vkd3d-proton with dxvk dxgi) */ From 22c2abb9b7dff7d062fd1ce3fba4468acce4df0b Mon Sep 17 00:00:00 2001 From: Joshua Ashton Date: Sun, 29 Oct 2023 01:57:13 +0100 Subject: [PATCH 017/246] [dxgi] Treat R16G16B16A16_FLOAT as 32bpp for display HDR in Control (a patch released by a developer post-launch, not actually in the game sadly) tries to set a video mode with DXGI_FORMAT_R16G16B16A16_FLOAT. This seemingly works on Windows, and based on FindClosestMode etc documentaton, this seems required to work for any format that scanout it supported for. It's really not like the bpp is meaningful on Windows with the distinction of 8bit and 10bit not working in GDI modes at all. Nor does it end up actually setting anything on Linux/Deck where modesets are emulated. So, treat DXGI_FORMAT_R16G16B16A16_FLOAT as 32bpp so the FindClosestMatchingMode and EnterFullscreenMode calls succeed. --- src/dxgi/dxgi_monitor.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/dxgi/dxgi_monitor.cpp b/src/dxgi/dxgi_monitor.cpp index b0622b3be..842fb3fc0 100644 --- a/src/dxgi/dxgi_monitor.cpp +++ b/src/dxgi/dxgi_monitor.cpp @@ -107,7 +107,11 @@ namespace dxvk { return 32; case DXGI_FORMAT_R16G16B16A16_FLOAT: - return 64; + // Floating point output doesn't really make sense. + // This seemingly works on Windows, and based on FindClosestMode etc documentaton, + // this seems required to work for any format that scanout it supported for. + // Treat as 10-bit -> 32. + return 32; default: Logger::warn(str::format( From a427d22cdee4a5166e000b0a1f540c52b42afa13 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Tue, 31 Oct 2023 16:05:14 +0100 Subject: [PATCH 018/246] [dxvk] Add Vulkan instance flag for D3D9 apps --- src/d3d11/d3d11_main.cpp | 4 ++-- src/d3d9/d3d9_interface.cpp | 2 +- src/dxgi/dxgi_factory.cpp | 2 +- src/dxvk/dxvk_instance.cpp | 11 ++++++----- src/dxvk/dxvk_instance.h | 25 ++++++++++++++++++++++--- src/util/util_singleton.h | 5 +++-- 6 files changed, 35 insertions(+), 14 deletions(-) diff --git a/src/d3d11/d3d11_main.cpp b/src/d3d11/d3d11_main.cpp index 4bcbec050..8ae32c008 100644 --- a/src/d3d11/d3d11_main.cpp +++ b/src/d3d11/d3d11_main.cpp @@ -38,7 +38,7 @@ extern "C" { DXGI_ADAPTER_DESC desc; pAdapter->GetDesc(&desc); - dxvkInstance = new DxvkInstance(); + dxvkInstance = new DxvkInstance(0); dxvkAdapter = dxvkInstance->findAdapterByLuid(&desc.AdapterLuid); if (dxvkAdapter == nullptr) @@ -376,7 +376,7 @@ extern "C" { instanceInfo.extensionCount = instanceExtensions.size(); instanceInfo.extensionNames = instanceExtensions.data(); - Rc dxvkInstance = new DxvkInstance(instanceInfo); + Rc dxvkInstance = new DxvkInstance(instanceInfo, 0); // Find adapter by physical device handle Rc dxvkAdapter; diff --git a/src/d3d9/d3d9_interface.cpp b/src/d3d9/d3d9_interface.cpp index 690e16d10..8e6b12d2a 100644 --- a/src/d3d9/d3d9_interface.cpp +++ b/src/d3d9/d3d9_interface.cpp @@ -14,7 +14,7 @@ namespace dxvk { Singleton g_dxvkInstance; D3D9InterfaceEx::D3D9InterfaceEx(bool bExtended) - : m_instance ( g_dxvkInstance.acquire() ) + : m_instance ( g_dxvkInstance.acquire(DxvkInstanceFlag::ClientApiIsD3D9) ) , m_d3d8Bridge ( this ) , m_extended ( bExtended ) , m_d3d9Options ( nullptr, m_instance->config() ) diff --git a/src/dxgi/dxgi_factory.cpp b/src/dxgi/dxgi_factory.cpp index d14ace5dd..24824c9d1 100644 --- a/src/dxgi/dxgi_factory.cpp +++ b/src/dxgi/dxgi_factory.cpp @@ -79,7 +79,7 @@ namespace dxvk { DxgiFactory::DxgiFactory(UINT Flags) - : m_instance (g_dxvkInstance.acquire()), + : m_instance (g_dxvkInstance.acquire(0)), m_interop (this), m_options (m_instance->config()), m_monitorInfo (this, m_options), diff --git a/src/dxvk/dxvk_instance.cpp b/src/dxvk/dxvk_instance.cpp index cffde93b1..016881825 100644 --- a/src/dxvk/dxvk_instance.cpp +++ b/src/dxvk/dxvk_instance.cpp @@ -10,13 +10,13 @@ namespace dxvk { - DxvkInstance::DxvkInstance() - : DxvkInstance(DxvkInstanceImportInfo()) { + DxvkInstance::DxvkInstance(DxvkInstanceFlags flags) + : DxvkInstance(DxvkInstanceImportInfo(), flags) { } - DxvkInstance::DxvkInstance(const DxvkInstanceImportInfo& args) { + DxvkInstance::DxvkInstance(const DxvkInstanceImportInfo& args, DxvkInstanceFlags flags) { Logger::info(str::format("Game: ", env::getExeName())); Logger::info(str::format("DXVK: ", DXVK_VERSION)); @@ -46,7 +46,7 @@ namespace dxvk { for (const auto& provider : m_extProviders) provider->initInstanceExtensions(); - createInstanceLoader(args); + createInstanceLoader(args, flags); m_adapters = this->queryAdapters(); for (const auto& provider : m_extProviders) @@ -106,7 +106,7 @@ namespace dxvk { } - void DxvkInstance::createInstanceLoader(const DxvkInstanceImportInfo& args) { + void DxvkInstance::createInstanceLoader(const DxvkInstanceImportInfo& args, DxvkInstanceFlags flags) { DxvkNameList layerList; DxvkNameList extensionList; DxvkNameSet extensionSet; @@ -175,6 +175,7 @@ namespace dxvk { VkApplicationInfo appInfo = { VK_STRUCTURE_TYPE_APPLICATION_INFO }; appInfo.pApplicationName = appName.c_str(); + appInfo.applicationVersion = flags.raw(); appInfo.pEngineName = "DXVK"; appInfo.engineVersion = VK_MAKE_VERSION(2, 3, 0); appInfo.apiVersion = VK_MAKE_VERSION(1, 3, 0); diff --git a/src/dxvk/dxvk_instance.h b/src/dxvk/dxvk_instance.h index 8335d23c9..cc0071e28 100644 --- a/src/dxvk/dxvk_instance.h +++ b/src/dxvk/dxvk_instance.h @@ -20,6 +20,21 @@ namespace dxvk { }; + /** + * \brief Instance creation flags + * + * These flags will be passed to the app version field of the Vulkan + * instance, so that drivers can adjust behaviour for some edge cases + * that are not implementable with Vulkan itself. + */ + enum class DxvkInstanceFlag : uint32_t { + /** Enforce D3D9 behaviour for texture coordinate snapping */ + ClientApiIsD3D9, + }; + + using DxvkInstanceFlags = Flags; + + /** * \brief DXVK instance * @@ -33,14 +48,17 @@ namespace dxvk { /** * \brief Creates new Vulkan instance + * \param [in] flags Instance flags */ - DxvkInstance(); + explicit DxvkInstance(DxvkInstanceFlags flags); /** * \brief Imports existing Vulkan instance + * \param [in] flags Instance flags */ explicit DxvkInstance( - const DxvkInstanceImportInfo& args); + const DxvkInstanceImportInfo& args, + DxvkInstanceFlags flags); ~DxvkInstance(); @@ -145,7 +163,8 @@ namespace dxvk { const DxvkInstanceImportInfo& args); void createInstanceLoader( - const DxvkInstanceImportInfo& args); + const DxvkInstanceImportInfo& args, + DxvkInstanceFlags flags); std::vector getExtensionList( DxvkInstanceExtensions& ext, diff --git a/src/util/util_singleton.h b/src/util/util_singleton.h index 08cd1562d..ee5fe56d1 100644 --- a/src/util/util_singleton.h +++ b/src/util/util_singleton.h @@ -16,11 +16,12 @@ class Singleton { public: - Rc acquire() { + template + Rc acquire(Args... constantArgs) { std::lock_guard lock(m_mutex); if (!(m_useCount++)) - m_object = new T(); + m_object = new T(constantArgs...); return m_object; } From 2ed1778df9ea5d81fff22f76f125b08995454847 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Thu, 2 Nov 2023 17:49:38 +0100 Subject: [PATCH 019/246] [d3d11] Handle potential integer overflow when validating draw offsets Apparently some games use -1 as an argument offset, which is nonsensical and leads to issues. --- src/d3d11/d3d11_context.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/d3d11/d3d11_context.h b/src/d3d11/d3d11_context.h index 618c07d69..7383507f0 100644 --- a/src/d3d11/d3d11_context.h +++ b/src/d3d11/d3d11_context.h @@ -1130,7 +1130,7 @@ namespace dxvk { if (likely(pBuffer != nullptr)) bufferSize = static_cast(pBuffer)->Desc()->ByteWidth; - return bufferSize >= Offset + Size; + return uint64_t(bufferSize) >= uint64_t(Offset) + uint64_t(Size); } private: From 1cb58b0732d96976c4a0a9e268ce55253dd51a8d Mon Sep 17 00:00:00 2001 From: xpander69 Date: Thu, 9 Nov 2023 12:16:55 +0200 Subject: [PATCH 020/246] [Util] two more executables to workaround Warhammer Online Test server executables need the same VendorID to work around the rendering issues. --- src/util/config/config.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index 638773f1d..1d30d844a 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -568,7 +568,7 @@ namespace dxvk { { "d3d9.customDeviceId", "0402" }, }} }, /* Warhammer: Online */ - { R"(\\WAR(-64)?\.exe$)", {{ + { R"(\\(WAR(-64)?|WARTEST(-64)?)\.exe$)", {{ { "d3d9.customVendorId", "1002" }, }} }, /* Dragon Nest */ From ea3149801f94e219163d91b0ab233340b0acec12 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Tue, 14 Nov 2023 12:17:39 +0100 Subject: [PATCH 021/246] [d3d9] Return empty buffer slice for out-of-bounds offsets Fixes #3715. --- src/d3d9/d3d9_common_buffer.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/d3d9/d3d9_common_buffer.h b/src/d3d9/d3d9_common_buffer.h index 782be4274..745961980 100644 --- a/src/d3d9/d3d9_common_buffer.h +++ b/src/d3d9/d3d9_common_buffer.h @@ -125,7 +125,12 @@ namespace dxvk { template inline DxvkBufferSlice GetBufferSlice(VkDeviceSize offset, VkDeviceSize length) const { - return DxvkBufferSlice(GetBuffer(), offset, length); + if (likely(length && offset < m_desc.Size)) { + return DxvkBufferSlice(GetBuffer(), offset, + std::min(m_desc.Size - offset, length)); + } + + return DxvkBufferSlice(); } inline DxvkBufferSliceHandle AllocMapSlice() { From d998dee46e5848da23e7d2eb078845ffb5b73d65 Mon Sep 17 00:00:00 2001 From: Robin Kertels Date: Wed, 15 Nov 2023 16:51:30 +0100 Subject: [PATCH 022/246] [d3d11] Lock context in KeyedMutex::ReleaseSync Co-authored-by: Yuxuan Shui --- src/d3d11/d3d11_context_imm.h | 4 ++++ src/d3d11/d3d11_resource.cpp | 12 +++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/d3d11/d3d11_context_imm.h b/src/d3d11/d3d11_context_imm.h index eb3ed88f0..3e6832500 100644 --- a/src/d3d11/d3d11_context_imm.h +++ b/src/d3d11/d3d11_context_imm.h @@ -89,6 +89,10 @@ namespace dxvk { void SynchronizeCsThread( uint64_t SequenceNumber); + D3D10Multithread& GetMultithread() { + return m_multithread; + } + D3D10DeviceLock LockContext() { return m_multithread.AcquireLock(); } diff --git a/src/d3d11/d3d11_resource.cpp b/src/d3d11/d3d11_resource.cpp index db3c0f10c..adc5b444a 100644 --- a/src/d3d11/d3d11_resource.cpp +++ b/src/d3d11/d3d11_resource.cpp @@ -111,7 +111,17 @@ namespace dxvk { D3D11CommonTexture* texture = GetCommonTexture(m_resource); Rc dxvkDevice = m_device->GetDXVKDevice(); - m_device->GetContext()->WaitForResource(texture->GetImage(), DxvkCsThread::SynchronizeAll, D3D11_MAP_READ_WRITE, 0); + { + D3D11ImmediateContext* context = m_device->GetContext(); + D3D10Multithread& multithread = context->GetMultithread(); + static bool s_errorShown = false; + + if (!multithread.GetMultithreadProtected() && !std::exchange(s_errorShown, true)) + Logger::warn("D3D11DXGIKeyedMutex::ReleaseSync: Called without context locking enabled."); + + D3D10DeviceLock lock = context->LockContext(); + context->WaitForResource(texture->GetImage(), DxvkCsThread::SynchronizeAll, D3D11_MAP_READ_WRITE, 0); + } return dxvkDevice->vkd()->wine_vkReleaseKeyedMutex(dxvkDevice->handle(), texture->GetImage()->memory().memory(), Key) == VK_SUCCESS ? S_OK From 91f7f43c35f240231c9e5efd0b24a27da5c16a8a Mon Sep 17 00:00:00 2001 From: Blisto91 <47954800+Blisto91@users.noreply.github.com> Date: Fri, 17 Nov 2023 22:02:53 +0100 Subject: [PATCH 023/246] [util] Don't spoof Nvidia on AMD GPUs in Hitman 3 Spoofing to Nvidia prevents ray tracing enablement on AMD and the game does not crash without ags anymore --- src/util/config/config.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index 1d30d844a..22735a16d 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -131,8 +131,8 @@ namespace dxvk { { R"(\\vr\.exe$)", {{ { "d3d11.dcSingleUseMode", "False" }, }} }, - /* Hitman 2 and 3 - requires AGS library */ - { R"(\\HITMAN(2|3)\.exe$)", {{ + /* Hitman 2 - requires AGS library */ + { R"(\\HITMAN2\.exe$)", {{ { "dxgi.customVendorId", "10de" }, }} }, /* Modern Warfare Remastered */ @@ -863,6 +863,10 @@ namespace dxvk { { R"(\\Cyberpunk2077\.exe$)", {{ { "dxgi.useMonitorFallback", "True" }, }} }, + /* Hitman 3 - Ray Tracing */ + { R"(\\HITMAN3\.exe$)", {{ + { "dxgi.hideNvidiaGpu", "False" }, + }} }, }}; From 03c09ce15f9f1e3ae4dd24dffb1e3f235127bcfc Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Mon, 27 Nov 2023 04:39:24 +0100 Subject: [PATCH 024/246] [dxvk] Add option to skip integrated GPU adapters --- dxvk.conf | 12 ++++++++++++ src/dxvk/dxvk_instance.cpp | 7 ++++++- src/dxvk/dxvk_options.cpp | 1 + src/dxvk/dxvk_options.h | 5 +++++ 4 files changed, 24 insertions(+), 1 deletion(-) diff --git a/dxvk.conf b/dxvk.conf index 13ffb6f49..ec3876cd3 100644 --- a/dxvk.conf +++ b/dxvk.conf @@ -634,3 +634,15 @@ # - True/False # dxgi.useMonitorFallback = False + + +# Hide integrated graphics from applications +# +# Only has an effect when dedicated GPUs are present on the system. It is +# not recommended to use this option at all unless absolutely necessary for +# a game to work; prefer using DXVK_FILTER_DEVICE_NAME whenever possible. +# +# Supported values: +# - True/False + +# dxvk.hideIntegratedGraphics = False diff --git a/src/dxvk/dxvk_instance.cpp b/src/dxvk/dxvk_instance.cpp index 016881825..55ba52458 100644 --- a/src/dxvk/dxvk_instance.cpp +++ b/src/dxvk/dxvk_instance.cpp @@ -286,7 +286,12 @@ namespace dxvk { return aRank < bRank; }); - + + if (m_options.hideIntegratedGraphics && numDGPU > 0 && numIGPU > 0) { + result.resize(numDGPU); + numIGPU = 0; + } + if (result.empty()) { Logger::warn("DXVK: No adapters found. Please check your " "device filter settings and Vulkan setup. " diff --git a/src/dxvk/dxvk_options.cpp b/src/dxvk/dxvk_options.cpp index 9c4a83554..47e24492d 100644 --- a/src/dxvk/dxvk_options.cpp +++ b/src/dxvk/dxvk_options.cpp @@ -12,6 +12,7 @@ namespace dxvk { maxChunkSize = config.getOption ("dxvk.maxChunkSize", 0); hud = config.getOption("dxvk.hud", ""); tearFree = config.getOption("dxvk.tearFree", Tristate::Auto); + hideIntegratedGraphics = config.getOption ("dxvk.hideIntegratedGraphics", false); } } diff --git a/src/dxvk/dxvk_options.h b/src/dxvk/dxvk_options.h index f367e5c6d..e7e6862f2 100644 --- a/src/dxvk/dxvk_options.h +++ b/src/dxvk/dxvk_options.h @@ -36,6 +36,11 @@ namespace dxvk { /// Forces swap chain into MAILBOX (if true) /// or FIFO_RELAXED (if false) present mode Tristate tearFree; + + // Hides integrated GPUs if dedicated GPUs are + // present. May be necessary for some games that + // incorrectly assume monitor layouts. + bool hideIntegratedGraphics; }; } From 1b31aa5dbca0749d0737cff02043acb061332fb6 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Mon, 27 Nov 2023 17:59:53 +0100 Subject: [PATCH 025/246] [util] Enable hideIntegratedGraphics for Metro Exodus EE --- src/util/config/config.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index 22735a16d..6cd72ae8b 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -867,6 +867,12 @@ namespace dxvk { { R"(\\HITMAN3\.exe$)", {{ { "dxgi.hideNvidiaGpu", "False" }, }} }, + /* Metro Exodus Enhanced Edition picks GPU adapters + * by available VRAM, which causes issues on some + * systems with integrated graphics. */ + { R"(\\Metro Exodus Enhanced Edition\\MetroExodus\.exe$)", {{ + { "dxvk.hideIntegratedGraphics", "True" }, + }} }, }}; From adb33d3af162baef0076c58d9538332ee3379dd8 Mon Sep 17 00:00:00 2001 From: Blisto91 <47954800+Blisto91@users.noreply.github.com> Date: Fri, 22 Dec 2023 22:06:50 +0100 Subject: [PATCH 026/246] [util] Hide Intel in Far Cry 3, 4 and Primal Also unhides Nvidia in Primal as it has the same clear value rounding assumptions as Far Cry 3 and 4 --- src/util/config/config.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index 6cd72ae8b..ba8356d13 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -54,13 +54,16 @@ namespace dxvk { /* Far Cry 3: Assumes clear(0.5) on an UNORM * * format to result in 128 on AMD and 127 on * * Nvidia. We assume that the Vulkan drivers * - * match the clear behaviour of D3D11. */ + * match the clear behaviour of D3D11. * + * Intel needs to match the AMD result */ { R"(\\(farcry3|fc3_blooddragon)_d3d11\.exe$)", {{ { "dxgi.hideNvidiaGpu", "False" }, + { "dxgi.hideIntelGpu", "True" }, }} }, - /* Far Cry 4: Same as Far Cry 3 */ - { R"(\\FarCry4\.exe$)", {{ + /* Far Cry 4 and Primal: Same as Far Cry 3 */ + { R"(\\(FarCry4|FCPrimal)\.exe$)", {{ { "dxgi.hideNvidiaGpu", "False" }, + { "dxgi.hideIntelGpu", "True" }, }} }, /* Frostpunk: Renders one frame with D3D9 * * after creating the DXGI swap chain */ From 9cde0b57987c833abfaf0400594cc14426d8924f Mon Sep 17 00:00:00 2001 From: Robin Kertels Date: Mon, 25 Dec 2023 15:34:55 +0100 Subject: [PATCH 027/246] [d3d9] Fix off-by-one when copying shader defined constants --- src/d3d9/d3d9_device.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/d3d9/d3d9_device.cpp b/src/d3d9/d3d9_device.cpp index 30a4a5b29..732f818c2 100644 --- a/src/d3d9/d3d9_device.cpp +++ b/src/d3d9/d3d9_device.cpp @@ -5260,7 +5260,7 @@ namespace dxvk { uint32_t floatCount = m_vsFloatConstsCount; if (constSet.meta.needsConstantCopies) { auto shader = GetCommonShader(m_state.vertexShader); - floatCount = std::max(floatCount, shader->GetMaxDefinedConstant()); + floatCount = std::max(floatCount, shader->GetMaxDefinedConstant() + 1); } floatCount = std::min(floatCount, constSet.meta.maxConstIndexF); @@ -5322,7 +5322,7 @@ namespace dxvk { uint32_t floatCount = ShaderStage == DxsoProgramType::VertexShader ? m_vsFloatConstsCount : m_psFloatConstsCount; if (constSet.meta.needsConstantCopies) { auto shader = GetCommonShader(Shader); - floatCount = std::max(floatCount, shader->GetMaxDefinedConstant()); + floatCount = std::max(floatCount, shader->GetMaxDefinedConstant() + 1); } floatCount = std::min(constSet.meta.maxConstIndexF, floatCount); From a7a63b37c3b7a8280c6a8487a2f21fe9dd8dbe0e Mon Sep 17 00:00:00 2001 From: Robin Kertels Date: Wed, 27 Dec 2023 00:54:55 +0100 Subject: [PATCH 028/246] [util] Fix incorrect config option name --- src/util/config/config.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index ba8356d13..c3652c430 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -538,7 +538,7 @@ namespace dxvk { { "d3d9.customVendorId", "1002" }, { "dxgi.emulateUMA", "True" }, { "d3d9.supportDFFormats", "False" }, - { "d3d9.deviceLostOnFocusLoss", "True" }, + { "d3d9.deviceLossOnFocusLoss", "True" }, }} }, /* Battlefield 2 (bad z-pass) */ { R"(\\BF2\.exe$)", {{ @@ -787,7 +787,7 @@ namespace dxvk { /* DC Universe Online * * Freezes after alt tabbing */ { R"(\\DCGAME\.EXE$)", {{ - { "d3d9.deviceLostOnFocusLoss", "True" }, + { "d3d9.deviceLossOnFocusLoss", "True" }, }} }, /* Halo Online * * Black textures */ From 2cf590f63628a33a037227ee53ced293bd2cb66d Mon Sep 17 00:00:00 2001 From: Blisto91 <47954800+Blisto91@users.noreply.github.com> Date: Fri, 29 Dec 2023 22:39:45 +0100 Subject: [PATCH 029/246] [util] Enable cached vertex buffers for Kenshi Improves the games performance when CPU bound. --- src/util/config/config.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index c3652c430..745d4c8a6 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -425,6 +425,11 @@ namespace dxvk { { R"(\\HoloCure\.exe$)", {{ { "dxgi.useMonitorFallback", "True" }, }} }, + /* Kenshi * + * Helps CPU bound performance */ + { R"(\\kenshi_x64\.exe$)", {{ + { "d3d11.cachedDynamicResources", "v" }, + }} }, /**********************************************/ /* D3D9 GAMES */ From 5e06cf95737ccf8cdd8a8bcc50c944a4ae0f4ecc Mon Sep 17 00:00:00 2001 From: spiffeeroo <50706239+spiffeeroo@users.noreply.github.com> Date: Wed, 27 Dec 2023 05:12:18 -0800 Subject: [PATCH 030/246] [util] Limit Sonic CD to 60 fps Game engine physics/speed for Sonic CD is tied to frame rate so limit max frame rate to 60 fps. Otherwise, the game runs too quickly for high refresh rate monitors. --- src/util/config/config.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index 745d4c8a6..e19dd7b7e 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -841,6 +841,10 @@ namespace dxvk { { R"(\\SkyDrift\.exe$)" , {{ { "d3d9.allowDirectBufferMapping", "False" }, }} }, + /* Sonic CD */ + { R"(\\soniccd\.exe$)", {{ + { "d3d9.maxFrameRate", "60" }, + }} }, /**********************************************/ /* D3D12 GAMES (vkd3d-proton with dxvk dxgi) */ From a44dfabe262cc7e0ff8ffb5a44f8eab9cea706aa Mon Sep 17 00:00:00 2001 From: Blisto91 Date: Wed, 3 Jan 2024 15:38:22 +0100 Subject: [PATCH 031/246] [util] Set float emulation to Strict for UK Truck Simulator 1 Fixes black foliage --- src/util/config/config.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index e19dd7b7e..fb86ed56e 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -845,6 +845,11 @@ namespace dxvk { { R"(\\soniccd\.exe$)", {{ { "d3d9.maxFrameRate", "60" }, }} }, + /* UK Truck Simulator 1 */ + { R"(\\UK Truck Simulator\\bin\\win_x86\\game\.exe$)", {{ + { "d3d9.floatEmulation", "Strict" }, + }} }, + /**********************************************/ /* D3D12 GAMES (vkd3d-proton with dxvk dxgi) */ From eb806952d8d0da94e3c0d449b643f80ee2005030 Mon Sep 17 00:00:00 2001 From: Blisto91 <47954800+Blisto91@users.noreply.github.com> Date: Tue, 9 Jan 2024 10:43:48 +0100 Subject: [PATCH 032/246] [util] Set deviceLossOnFocusLoss for Assassin's Creed 2 (#3763) Makes it not crash on alt tab using at least Proton. Windows will still have issues with alt tab. --- src/util/config/config.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index fb86ed56e..b30086de0 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -840,6 +840,11 @@ namespace dxvk { * Works around alt tab OOM crash */ { R"(\\SkyDrift\.exe$)" , {{ { "d3d9.allowDirectBufferMapping", "False" }, + }} }, + /* Assassin's Creed 2 * + * Helps alt tab crash on Linux */ + { R"(\\AssassinsCreedIIGame\.exe$)" , {{ + { "d3d9.deviceLossOnFocusLoss", "True" }, }} }, /* Sonic CD */ { R"(\\soniccd\.exe$)", {{ From 854e06d3f04e9d00df0a55a9fe4bbacf6a302189 Mon Sep 17 00:00:00 2001 From: Blisto91 <47954800+Blisto91@users.noreply.github.com> Date: Sun, 14 Jan 2024 22:15:56 +0100 Subject: [PATCH 033/246] [util] Enable deviceLossOnFocusLoss for BF2 and BF2142 The ingame spawn and gear selection GUI can disappear from view unless this is set. --- src/util/config/config.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index b30086de0..665fdf9a2 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -545,9 +545,15 @@ namespace dxvk { { "d3d9.supportDFFormats", "False" }, { "d3d9.deviceLossOnFocusLoss", "True" }, }} }, - /* Battlefield 2 (bad z-pass) */ + /* Battlefield 2 * + * Bad z-pass and ingame GUI loss on alt tab */ { R"(\\BF2\.exe$)", {{ - { "d3d9.longMad", "True" }, + { "d3d9.longMad", "True" }, + { "d3d9.deviceLossOnFocusLoss", "True" }, + }} }, + /* Battlefield 2142 - Same GUI issue as BF2 */ + { R"(\\BF2142\.exe$)", {{ + { "d3d9.deviceLossOnFocusLoss", "True" }, }} }, /* SpellForce 2 Series */ { R"(\\SpellForce2.*\.exe$)", {{ From 14560600a9ad770a20c3e02e687fd6ff78d4f1b6 Mon Sep 17 00:00:00 2001 From: Richard Yao Date: Wed, 10 Jan 2024 22:43:42 -0500 Subject: [PATCH 034/246] Micro-optimize locking in fences When a fence has been missed, we can avoid locking *most* of the time via the double-checked locking pattern. We still lock before a second check in case the scheduler caused us to miss the fence. If the scheduler did cause us to miss the fence, we can drop the lock prior to executing the callback function, as a second micro-optimization. Signed-off-by: Richard Yao --- src/util/sync/sync_signal.h | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/util/sync/sync_signal.h b/src/util/sync/sync_signal.h index cdbcae821..44f99eec3 100644 --- a/src/util/sync/sync_signal.h +++ b/src/util/sync/sync_signal.h @@ -137,14 +137,22 @@ namespace dxvk::sync { template void setCallback(uint64_t value, Fn&& proc) { + if (value <= this->value()) { + proc(); + return; + } + std::unique_lock lock(m_mutex); + // Verify value is still in the future upon lock. if (value > this->value()) m_callbacks.emplace_back(std::piecewise_construct, std::make_tuple(value), std::make_tuple(proc)); - else + else { + lock.unlock(); proc(); + } } private: From ac78048c23c946caf0c734972e880f960080dd62 Mon Sep 17 00:00:00 2001 From: r-a-sattarov Date: Fri, 12 Jan 2024 22:16:51 +0300 Subject: [PATCH 035/246] [util] Fix e2k build --- src/util/util_bit.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/util/util_bit.h b/src/util/util_bit.h index 712be4bea..9f43a9f02 100644 --- a/src/util/util_bit.h +++ b/src/util/util_bit.h @@ -1,8 +1,8 @@ #pragma once -#if defined(__x86_64__) || defined(_M_X64) || defined(__i386__) || defined(_M_IX86) +#if defined(__x86_64__) || defined(_M_X64) || defined(__i386__) || defined(_M_IX86) || defined(__e2k__) #define DXVK_ARCH_X86 - #if defined(__x86_64__) || defined(_M_X64) + #if defined(__x86_64__) || defined(_M_X64) || defined(__e2k__) #define DXVK_ARCH_X86_64 #endif #elif defined(__aarch64__) || defined(_M_ARM64) From 4b8e8bed6e7007144f803ec64485168c82273c6f Mon Sep 17 00:00:00 2001 From: Joshua Ashton Date: Fri, 19 Jan 2024 16:27:19 +0000 Subject: [PATCH 036/246] [d3d9] Move operators out of dxvk namespace --- src/d3d9/d3d9_util.h | 67 ++++++++++++++++++++++---------------------- 1 file changed, 34 insertions(+), 33 deletions(-) diff --git a/src/d3d9/d3d9_util.h b/src/d3d9/d3d9_util.h index 3003b5ac8..89147dacb 100644 --- a/src/d3d9/d3d9_util.h +++ b/src/d3d9/d3d9_util.h @@ -204,38 +204,6 @@ namespace dxvk { bool IsDepthFormat(D3D9Format Format); - inline bool operator == (const D3DVIEWPORT9& a, const D3DVIEWPORT9& b) { - return a.X == b.X && - a.Y == b.Y && - a.Width == b.Width && - a.Height == b.Height && - a.MinZ == b.MinZ && - a.MaxZ == b.MaxZ; - } - - inline bool operator != (const D3DVIEWPORT9& a, const D3DVIEWPORT9& b) { - return !(a == b); - } - - inline bool operator == (const RECT& a, const RECT& b) { - return a.left == b.left && - a.right == b.right && - a.top == b.top && - a.bottom == b.bottom; - } - - inline bool operator != (const RECT& a, const RECT& b) { - return !(a == b); - } - - inline bool operator == (const POINT& a, const POINT& b) { - return a.x == b.x && a.y == b.y; - } - - inline bool operator != (const POINT& a, const POINT& b) { - return !(a == b); - } - inline bool IsPoolManaged(D3DPOOL Pool) { return Pool == D3DPOOL_MANAGED || Pool == D3DPOOL_MANAGED_EX; } @@ -295,4 +263,37 @@ namespace dxvk { return D3D9TextureStageStateTypes(Type - 1); } -} \ No newline at end of file +} + + +inline bool operator == (const D3DVIEWPORT9& a, const D3DVIEWPORT9& b) { + return a.X == b.X && + a.Y == b.Y && + a.Width == b.Width && + a.Height == b.Height && + a.MinZ == b.MinZ && + a.MaxZ == b.MaxZ; +} + +inline bool operator != (const D3DVIEWPORT9& a, const D3DVIEWPORT9& b) { + return !(a == b); +} + +inline bool operator == (const RECT& a, const RECT& b) { + return a.left == b.left && + a.right == b.right && + a.top == b.top && + a.bottom == b.bottom; +} + +inline bool operator != (const RECT& a, const RECT& b) { + return !(a == b); +} + +inline bool operator == (const POINT& a, const POINT& b) { + return a.x == b.x && a.y == b.y; +} + +inline bool operator != (const POINT& a, const POINT& b) { + return !(a == b); +} From 0cd4165658537405d84a43f3ab6180b770f30fe1 Mon Sep 17 00:00:00 2001 From: Joshua Ashton Date: Fri, 19 Jan 2024 16:27:38 +0000 Subject: [PATCH 037/246] [d3d9] Add D3DDISPLAYMODEEX operator --- src/d3d9/d3d9_util.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/d3d9/d3d9_util.h b/src/d3d9/d3d9_util.h index 89147dacb..ecd21a3f4 100644 --- a/src/d3d9/d3d9_util.h +++ b/src/d3d9/d3d9_util.h @@ -297,3 +297,13 @@ inline bool operator == (const POINT& a, const POINT& b) { inline bool operator != (const POINT& a, const POINT& b) { return !(a == b); } + +inline bool operator == (const D3DDISPLAYMODEEX& a, const D3DDISPLAYMODEEX& b) { + return a.Size == b.Size && + a.Width == b.Width && + a.Height == b.Height && + a.RefreshRate == b.RefreshRate && + a.Format == b.Format && + a.ScanLineOrdering == b.ScanLineOrdering; +} + From 1568c263fbec4c8d5f8f9286e0be40af7fdfb9a6 Mon Sep 17 00:00:00 2001 From: Joshua Ashton Date: Fri, 19 Jan 2024 16:28:11 +0000 Subject: [PATCH 038/246] [d3d9] Only add unique modes to mode list --- src/d3d9/d3d9_adapter.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/d3d9/d3d9_adapter.cpp b/src/d3d9/d3d9_adapter.cpp index 44b631a46..e0f1a185f 100644 --- a/src/d3d9/d3d9_adapter.cpp +++ b/src/d3d9/d3d9_adapter.cpp @@ -788,7 +788,8 @@ namespace dxvk { // Fix up the D3DFORMAT to match what we are enumerating mode.Format = static_cast(Format); - m_modes.push_back(mode); + if (std::count(m_modes.begin(), m_modes.end(), mode) == 0) + m_modes.push_back(mode); } // Sort display modes by width, height and refresh rate, From 34d8e65fd75d5a1e1ae3f332c72417c612542ebf Mon Sep 17 00:00:00 2001 From: Blisto91 <47954800+Blisto91@users.noreply.github.com> Date: Tue, 23 Jan 2024 14:35:17 +0100 Subject: [PATCH 039/246] [util] cachedDynamicBuffers for Codename Panzers Phase One/Two Helps CPU bound performance --- src/util/config/config.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index 665fdf9a2..84b10099e 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -791,9 +791,11 @@ namespace dxvk { { "d3d9.maxFrameRate", "60" }, }} }, /* Codename Panzers Phase One/Two * - * Main menu won't render after intros */ + * Main menu won't render after intros * + * and CPU bound performance */ { R"(\\(PANZERS|PANZERS_Phase_2)\.exe$)", {{ { "d3d9.enableDialogMode", "True" }, + { "d3d9.cachedDynamicBuffers", "True" }, }} }, /* DC Universe Online * * Freezes after alt tabbing */ From 89267b62ade1eb6f6425eb1b626746fb64df3cb4 Mon Sep 17 00:00:00 2001 From: Tatsuyuki Ishi Date: Fri, 19 Jan 2024 10:33:58 +0900 Subject: [PATCH 040/246] [build] Remove declspec UUID annotations MIDL_INTERFACE already implies struct DECLSPEC_UUID. --- src/d3d11/d3d11_interfaces.h | 8 +------- src/d3d11/d3d11_on_12_interfaces.h | 4 +--- src/d3d9/d3d9_bridge.h | 5 +---- src/d3d9/d3d9_include.h | 4 +--- src/d3d9/d3d9_interfaces.h | 7 +------ src/dxgi/dxgi_interfaces.h | 16 +--------------- src/dxvk/dxvk_annotation.h | 4 +--- 7 files changed, 7 insertions(+), 41 deletions(-) diff --git a/src/d3d11/d3d11_interfaces.h b/src/d3d11/d3d11_interfaces.h index 587cde139..8a2e6fcf4 100644 --- a/src/d3d11/d3d11_interfaces.h +++ b/src/d3d11/d3d11_interfaces.h @@ -183,13 +183,7 @@ ID3D11VkExtContext1 : public ID3D11VkExtContext { }; -#ifdef _MSC_VER -struct __declspec(uuid("bb8a4fb9-3935-4762-b44b-35189a26414a")) ID3D11VkExtShader; -struct __declspec(uuid("8a6e3c42-f74c-45b7-8265-a231b677ca17")) ID3D11VkExtDevice; -struct __declspec(uuid("cfcf64ef-9586-46d0-bca4-97cf2ca61b06")) ID3D11VkExtDevice1; -struct __declspec(uuid("fd0bca13-5cb6-4c3a-987e-4750de2ca791")) ID3D11VkExtContext; -struct __declspec(uuid("874b09b2-ae0b-41d8-8476-5f3b7a0e879d")) ID3D11VkExtContext1; -#else +#ifndef _MSC_VER __CRT_UUID_DECL(ID3D11VkExtShader, 0xbb8a4fb9,0x3935,0x4762,0xb4,0x4b,0x35,0x18,0x9a,0x26,0x41,0x4a); __CRT_UUID_DECL(ID3D11VkExtDevice, 0x8a6e3c42,0xf74c,0x45b7,0x82,0x65,0xa2,0x31,0xb6,0x77,0xca,0x17); __CRT_UUID_DECL(ID3D11VkExtDevice1, 0xcfcf64ef,0x9586,0x46d0,0xbc,0xa4,0x97,0xcf,0x2c,0xa6,0x1b,0x06); diff --git a/src/d3d11/d3d11_on_12_interfaces.h b/src/d3d11/d3d11_on_12_interfaces.h index 2ec22e0a9..4a40e02ab 100644 --- a/src/d3d11/d3d11_on_12_interfaces.h +++ b/src/d3d11/d3d11_on_12_interfaces.h @@ -49,8 +49,6 @@ ID3D12DXVKInteropDevice : public IUnknown { }; -#ifdef _MSC_VER -struct __declspec(uuid("39da4e09-bd1c-4198-9fae-86bbe3be41fd")) ID3D12DXVKInteropDevice; -#else +#ifndef _MSC_VER __CRT_UUID_DECL(ID3D12DXVKInteropDevice, 0x39da4e09, 0xbd1c, 0x4198, 0x9f,0xae, 0x86,0xbb,0xe3,0xbe,0x41,0xfd) #endif diff --git a/src/d3d9/d3d9_bridge.h b/src/d3d9/d3d9_bridge.h index 163d4130e..7ed00a750 100644 --- a/src/d3d9/d3d9_bridge.h +++ b/src/d3d9/d3d9_bridge.h @@ -65,10 +65,7 @@ IDxvkD3D8InterfaceBridge : public IUnknown { virtual const dxvk::Config* GetConfig() const = 0; }; -#if defined(_MSC_VER) -struct DECLSPEC_UUID("D3D9D3D8-42A9-4C1E-AA97-BEEFCAFE2000") IDxvkD3D8Bridge; -struct DECLSPEC_UUID("D3D9D3D8-A407-773E-18E9-CAFEBEEF3000") IDxvkD3D8InterfaceBridge; -#else +#ifndef _MSC_VER __CRT_UUID_DECL(IDxvkD3D8Bridge, 0xD3D9D3D8, 0x42A9, 0x4C1E, 0xAA, 0x97, 0xBE, 0xEF, 0xCA, 0xFE, 0x20, 0x00); __CRT_UUID_DECL(IDxvkD3D8InterfaceBridge, 0xD3D9D3D8, 0xA407, 0x773E, 0x18, 0xE9, 0xCA, 0xFE, 0xBE, 0xEF, 0x30, 0x00); #endif diff --git a/src/d3d9/d3d9_include.h b/src/d3d9/d3d9_include.h index 9e67e577e..df35f10fb 100644 --- a/src/d3d9/d3d9_include.h +++ b/src/d3d9/d3d9_include.h @@ -101,9 +101,7 @@ IDirect3DDevice9On12 : public IUnknown { virtual HRESULT STDMETHODCALLTYPE ReturnUnderlyingResource(IDirect3DResource9* resource, UINT num_sync, UINT64* signal_values, ID3D12Fence** fences) = 0; }; -#ifdef _MSC_VER -struct __declspec(uuid("e7fda234-b589-4049-940d-8878977531c8")) IDirect3DDevice9On12; -#else +#ifndef _MSC_VER __CRT_UUID_DECL(IDirect3DDevice9On12, 0xe7fda234,0xb589,0x4049,0x94,0x0d,0x88,0x78,0x97,0x75,0x31,0xc8); #endif diff --git a/src/d3d9/d3d9_interfaces.h b/src/d3d9/d3d9_interfaces.h index 09fbcfa4e..c0717eae5 100644 --- a/src/d3d9/d3d9_interfaces.h +++ b/src/d3d9/d3d9_interfaces.h @@ -229,12 +229,7 @@ ID3D9VkExtSwapchain : public IUnknown { virtual void STDMETHODCALLTYPE UnlockAdditionalFormats() = 0; }; -#ifdef _MSC_VER -struct __declspec(uuid("3461a81b-ce41-485b-b6b5-fcf08ba6a6bd")) ID3D9VkInteropInterface; -struct __declspec(uuid("d56344f5-8d35-46fd-806d-94c351b472c1")) ID3D9VkInteropTexture; -struct __declspec(uuid("2eaa4b89-0107-4bdb-87f7-0f541c493ce0")) ID3D9VkInteropDevice; -struct __declspec(uuid("13776e93-4aa9-430a-a4ec-fe9e281181d5")) ID3D9VkExtSwapchain; -#else +#ifndef _MSC_VER __CRT_UUID_DECL(ID3D9VkInteropInterface, 0x3461a81b,0xce41,0x485b,0xb6,0xb5,0xfc,0xf0,0x8b,0xa6,0xa6,0xbd); __CRT_UUID_DECL(ID3D9VkInteropTexture, 0xd56344f5,0x8d35,0x46fd,0x80,0x6d,0x94,0xc3,0x51,0xb4,0x72,0xc1); __CRT_UUID_DECL(ID3D9VkInteropDevice, 0x2eaa4b89,0x0107,0x4bdb,0x87,0xf7,0x0f,0x54,0x1c,0x49,0x3c,0xe0); diff --git a/src/dxgi/dxgi_interfaces.h b/src/dxgi/dxgi_interfaces.h index 3735447f8..4c35bd527 100644 --- a/src/dxgi/dxgi_interfaces.h +++ b/src/dxgi/dxgi_interfaces.h @@ -458,21 +458,7 @@ IDXGIVkInteropFactory1 : public IDXGIVkInteropFactory { }; -#ifdef _MSC_VER -struct __declspec(uuid("907bf281-ea3c-43b4-a8e4-9f231107b4ff")) IDXGIDXVKAdapter; -struct __declspec(uuid("92a5d77b-b6e1-420a-b260-fdd701272827")) IDXGIDXVKDevice; -struct __declspec(uuid("c06a236f-5be3-448a-8943-89c611c0c2c1")) IDXGIVkMonitorInfo; -struct __declspec(uuid("4c5e1b0d-b0c8-4131-bfd8-9b2476f7f408")) IDXGIVkInteropFactory; -struct __declspec(uuid("2a289dbd-2d0a-4a51-89f7-f2adce465cd6")) IDXGIVkInteropFactory1; -struct __declspec(uuid("3a6d8f2c-b0e8-4ab4-b4dc-4fd24891bfa5")) IDXGIVkInteropAdapter; -struct __declspec(uuid("e2ef5fa5-dc21-4af7-90c4-f67ef6a09323")) IDXGIVkInteropDevice; -struct __declspec(uuid("e2ef5fa5-dc21-4af7-90c4-f67ef6a09324")) IDXGIVkInteropDevice1; -struct __declspec(uuid("5546cf8c-77e7-4341-b05d-8d4d5000e77d")) IDXGIVkInteropSurface; -struct __declspec(uuid("1e7895a1-1bc3-4f9c-a670-290a4bc9581a")) IDXGIVkSurfaceFactory; -struct __declspec(uuid("e4a9059e-b569-46ab-8de7-501bd2bc7f7a")) IDXGIVkSwapChain; -struct __declspec(uuid("785326d4-b77b-4826-ae70-8d08308ee6d1")) IDXGIVkSwapChain1; -struct __declspec(uuid("e7d6c3ca-23a0-4e08-9f2f-ea5231df6633")) IDXGIVkSwapChainFactory; -#else +#ifndef _MSC_VER __CRT_UUID_DECL(IDXGIDXVKAdapter, 0x907bf281,0xea3c,0x43b4,0xa8,0xe4,0x9f,0x23,0x11,0x07,0xb4,0xff); __CRT_UUID_DECL(IDXGIDXVKDevice, 0x92a5d77b,0xb6e1,0x420a,0xb2,0x60,0xfd,0xf7,0x01,0x27,0x28,0x27); __CRT_UUID_DECL(IDXGIVkMonitorInfo, 0xc06a236f,0x5be3,0x448a,0x89,0x43,0x89,0xc6,0x11,0xc0,0xc2,0xc1); diff --git a/src/dxvk/dxvk_annotation.h b/src/dxvk/dxvk_annotation.h index e5d1cbc72..a1dc76c95 100644 --- a/src/dxvk/dxvk_annotation.h +++ b/src/dxvk/dxvk_annotation.h @@ -28,8 +28,6 @@ public: }; -#ifdef _MSC_VER -struct __declspec(uuid("7f2c2f72-1cc8-4979-8d9c-7e3faeddecde")) IDXVKUserDefinedAnnotation; -#else +#ifndef _MSC_VER __CRT_UUID_DECL(IDXVKUserDefinedAnnotation, 0x7f2c2f72,0x1cc8,0x4979,0x8d,0x9c,0x7e,0x3f,0xae,0xdd,0xec,0xde); #endif From ab6bd8b17f8c7a64d2a799d325dbf873fbb9378c Mon Sep 17 00:00:00 2001 From: Tatsuyuki Ishi Date: Fri, 19 Jan 2024 09:59:47 +0900 Subject: [PATCH 041/246] [build] Recognize more MSVC-like compilers as MSVC clang-cl has its own compiler id but supports MSVC argument conventions. Use get_argument_syntax to recognize MSVC-like compilers generally. --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index b571729d0..abe9a1e07 100644 --- a/meson.build +++ b/meson.build @@ -5,7 +5,7 @@ platform = target_machine.system() cpp = meson.get_compiler('cpp') cc = meson.get_compiler('c') -dxvk_is_msvc = cpp.get_id() == 'msvc' +dxvk_is_msvc = cpp.get_argument_syntax() == 'msvc' compiler_args = [ '-msse', From 6faf3c1acd26040371ea0a5a387217d6cd85d022 Mon Sep 17 00:00:00 2001 From: Tatsuyuki Ishi Date: Fri, 19 Jan 2024 10:02:50 +0900 Subject: [PATCH 042/246] [build] Don't pass unix compiler and link args to MSVC clang-cl accepts both style of options but these will be generally meaningless: - Static CRT is unnecessary. - File alignment will be introduced in another commit. - Wine has partial support for PDB backtrace so there is probably no need to insist on DWARF (which is unsupported by real MSVC anyway). - MSVC doesn't have the weird stdcall ordinal naming convention that necessiates fixup and kill-at. --- meson.build | 42 ++++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/meson.build b/meson.build index abe9a1e07..acd8d308b 100644 --- a/meson.build +++ b/meson.build @@ -48,28 +48,30 @@ if platform == 'windows' '-D_WIN32_WINNT=0xa00', ] - link_args += [ - '-static', - '-static-libgcc', - '-static-libstdc++', - # We need to set the section alignment for debug symbols to - # work properly as well as avoiding a memcpy from the Wine loader. - '-Wl,--file-alignment=4096', - ] - - # Wine's built-in back traces only work with dwarf4 symbols - if get_option('debug') - compiler_args += [ - '-gdwarf-4', - ] - endif - - # Enable stdcall fixup on 32-bit - if cpu_family == 'x86' + if not dxvk_is_msvc link_args += [ - '-Wl,--enable-stdcall-fixup', - '-Wl,--kill-at', + '-static', + '-static-libgcc', + '-static-libstdc++', + # We need to set the section alignment for debug symbols to + # work properly as well as avoiding a memcpy from the Wine loader. + '-Wl,--file-alignment=4096', ] + + # Wine's built-in back traces only work with dwarf4 symbols + if get_option('debug') + compiler_args += [ + '-gdwarf-4', + ] + endif + + # Enable stdcall fixup on 32-bit + if cpu_family == 'x86' + link_args += [ + '-Wl,--enable-stdcall-fixup', + '-Wl,--kill-at', + ] + endif endif lib_d3d9 = cpp.find_library('d3d9') From 6199776869308b6d8f38095a580bda8ca711fd71 Mon Sep 17 00:00:00 2001 From: Tatsuyuki Ishi Date: Fri, 19 Jan 2024 10:04:55 +0900 Subject: [PATCH 043/246] [build] Set filealign for MSVC builds Useful if you want to run a MSVC build on Wine. --- meson.build | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/meson.build b/meson.build index acd8d308b..8e7f2c4d7 100644 --- a/meson.build +++ b/meson.build @@ -72,6 +72,10 @@ if platform == 'windows' '-Wl,--kill-at', ] endif + else + link_args += [ + '/FILEALIGN:4096', + ] endif lib_d3d9 = cpp.find_library('d3d9') From d4c5fc74e7b749f460d9bd91dea8b8d0091c7507 Mon Sep 17 00:00:00 2001 From: Dean Beeler Date: Tue, 23 Jan 2024 07:01:12 -0800 Subject: [PATCH 044/246] d3d11: Fix crash when srv is submitted to ClearUnorderedAccessViewUint * The Settlers submits (possibly incorrectly) an SRV to ClearUnorderedAccessViewUint. The static_cast in the function does not translate correctly and crashes. Native D3D11 behavior is to ignore the bad parameter entirely. It does not clear the SRV nor does it fault or even error with the DEBUG validator. --- src/d3d11/d3d11_context.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/d3d11/d3d11_context.cpp b/src/d3d11/d3d11_context.cpp index 977a403e0..4b06ae380 100644 --- a/src/d3d11/d3d11_context.cpp +++ b/src/d3d11/d3d11_context.cpp @@ -386,11 +386,16 @@ namespace dxvk { const UINT Values[4]) { D3D10DeviceLock lock = LockContext(); - auto uav = static_cast(pUnorderedAccessView); - - if (!uav) + if (!pUnorderedAccessView) return; + Com qiUav; + + if (FAILED(pUnorderedAccessView->QueryInterface(IID_PPV_ARGS(&qiUav)))) + return; + + auto uav = static_cast(qiUav.ptr()); + // Gather UAV format info. We'll use this to determine // whether we need to create a temporary view or not. D3D11_UNORDERED_ACCESS_VIEW_DESC uavDesc; From 7d9864c077e2c583d1656a374b467c10d8e3ff30 Mon Sep 17 00:00:00 2001 From: Robin Kertels Date: Thu, 25 Jan 2024 21:05:09 +0100 Subject: [PATCH 045/246] [d3d9] Only enable ATOC when rendering to MS RT --- src/d3d9/d3d9_device.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/d3d9/d3d9_device.h b/src/d3d9/d3d9_device.h index 99a2a7439..d7ac8c00a 100644 --- a/src/d3d9/d3d9_device.h +++ b/src/d3d9/d3d9_device.h @@ -816,7 +816,7 @@ namespace dxvk { inline bool IsAlphaToCoverageEnabled() { const bool alphaTest = m_state.renderStates[D3DRS_ALPHATESTENABLE] != 0; - return m_amdATOC || (m_nvATOC && alphaTest); + return (m_amdATOC || (m_nvATOC && alphaTest)) && m_flags.test(D3D9DeviceFlag::ValidSampleMask); } inline bool IsDepthBiasEnabled() { From 2334bbccb046134e32ad84c5b9bcea7cd50614d7 Mon Sep 17 00:00:00 2001 From: Ethan Lee Date: Tue, 12 Dec 2023 09:51:38 -0500 Subject: [PATCH 046/246] [native] Add a DECLARE_INTERFACE define for !CONST_VTABLE. This helps avoid some compiler warnings on GCC in particular. --- include/native/windows/windows_base.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/include/native/windows/windows_base.h b/include/native/windows/windows_base.h index fe1a2a0ef..8ff28aa3d 100644 --- a/include/native/windows/windows_base.h +++ b/include/native/windows/windows_base.h @@ -333,12 +333,21 @@ typedef struct RGNDATA { #define DECLARE_INTERFACE(x) struct x #define DECLARE_INTERFACE_(x, y) struct x : public y #else +#ifdef CONST_VTABLE #define DECLARE_INTERFACE(x) \ typedef interface x { \ const struct x##Vtbl *lpVtbl; \ } x; \ typedef const struct x##Vtbl x##Vtbl; \ const struct x##Vtbl +#else +#define DECLARE_INTERFACE(x) \ + typedef interface x { \ + struct x##Vtbl *lpVtbl; \ + } x; \ + typedef struct x##Vtbl x##Vtbl; \ + struct x##Vtbl +#endif // CONST_VTABLE #define DECLARE_INTERFACE_(x, y) DECLARE_INTERFACE(x) #endif // __cplusplus From 0841f5faf438ca58528670a5edf87ffd34ec27b8 Mon Sep 17 00:00:00 2001 From: Robin Kertels Date: Thu, 25 Jan 2024 21:59:27 +0100 Subject: [PATCH 047/246] [d3d9] Implement config option to disable rejecting reset --- dxvk.conf | 23 +++++++++++++++++++++++ src/d3d9/d3d9_device.cpp | 2 +- src/d3d9/d3d9_options.cpp | 1 + src/d3d9/d3d9_options.h | 3 +++ 4 files changed, 28 insertions(+), 1 deletion(-) diff --git a/dxvk.conf b/dxvk.conf index ec3876cd3..09f5dfbaf 100644 --- a/dxvk.conf +++ b/dxvk.conf @@ -646,3 +646,26 @@ # - True/False # dxvk.hideIntegratedGraphics = False + +# Trigger DEVICELOST when losing focus +# +# D3D9 requires the application to call Device::Reset after +# it loses focus in fullscreen. +# Some games rely on observing a D3DERR_DEVICELOST or D3DERR_NOTRESET. +# Others don't handle it correctly. +# +# Supported values: +# - True/False + +# d3d9.deviceLossOnFocusLoss = False + +# Reject Device::Reset if any losable resource is still alive +# +# D3D9 rejects Device::Reset if there's still any alive resources of specific types. +# (State blocks, additional swapchains, D3DPOOL_DEFAULT resources) +# Some games leak resources leading to a hang. +# +# Supported values: +# - True/False + +# d3d9.countLosableResources = True diff --git a/src/d3d9/d3d9_device.cpp b/src/d3d9/d3d9_device.cpp index 732f818c2..a0c0a1ecb 100644 --- a/src/d3d9/d3d9_device.cpp +++ b/src/d3d9/d3d9_device.cpp @@ -464,7 +464,7 @@ namespace dxvk { * We have to check after ResetState clears the references held by SetTexture, etc. * This matches what Windows D3D9 does. */ - if (unlikely(m_losableResourceCounter.load() != 0 && !IsExtended())) { + if (unlikely(m_losableResourceCounter.load() != 0 && !IsExtended() && m_d3d9Options.countLosableResources)) { Logger::warn(str::format("Device reset failed because device still has alive losable resources: Device not reset. Remaining resources: ", m_losableResourceCounter.load())); m_deviceLostState = D3D9DeviceLostState::NotReset; return D3DERR_INVALIDCALL; diff --git a/src/d3d9/d3d9_options.cpp b/src/d3d9/d3d9_options.cpp index 190ca29b8..fc3d0b481 100644 --- a/src/d3d9/d3d9_options.cpp +++ b/src/d3d9/d3d9_options.cpp @@ -76,6 +76,7 @@ namespace dxvk { this->deviceLossOnFocusLoss = config.getOption ("d3d9.deviceLossOnFocusLoss", false); this->samplerLodBias = config.getOption ("d3d9.samplerLodBias", 0.0f); this->clampNegativeLodBias = config.getOption ("d3d9.clampNegativeLodBias", false); + this->countLosableResources = config.getOption ("d3d9.countLosableResources", true); // Clamp LOD bias so that people don't abuse this in unintended ways this->samplerLodBias = dxvk::fclamp(this->samplerLodBias, -2.0f, 1.0f); diff --git a/src/d3d9/d3d9_options.h b/src/d3d9/d3d9_options.h index f5b1fce46..034c85d3a 100644 --- a/src/d3d9/d3d9_options.h +++ b/src/d3d9/d3d9_options.h @@ -152,6 +152,9 @@ namespace dxvk { /// Enable emulation of device loss when a fullscreen app loses focus bool deviceLossOnFocusLoss; + + /// Disable counting losable resources and rejecting calls to Reset() if any are still alive + bool countLosableResources; }; } From 2ca8fdf8905a2f6adb8294b46aeaad5c1d4b1132 Mon Sep 17 00:00:00 2001 From: Robin Kertels Date: Thu, 25 Jan 2024 22:00:05 +0100 Subject: [PATCH 048/246] [util] Disable counting losable resources for Supreme Ruler Ultimate The game is broken and leaks a state block. --- src/util/config/config.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index 84b10099e..b57bc39b1 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -862,6 +862,12 @@ namespace dxvk { { R"(\\UK Truck Simulator\\bin\\win_x86\\game\.exe$)", {{ { "d3d9.floatEmulation", "Strict" }, }} }, + /* Supreme Ruler Ultimate * + * Leaks a StateBlock leading * + * to Reset calls failing */ + { R"(\\SupremeRulerUltimate\.exe$)", {{ + { "d3d9.countLosableResources", "False" }, + }} }, /**********************************************/ From 799aeff5608dc8f73bc95e1209f29c8020e247c8 Mon Sep 17 00:00:00 2001 From: Tatsuyuki Ishi Date: Sat, 27 Jan 2024 12:38:03 +0900 Subject: [PATCH 049/246] [dxvk] Fix incorrect comparison in DxvkSparsePageTable::updateMapping m_mappings[page] was getting implicitly converted to bool. --- src/dxvk/dxvk_sparse.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dxvk/dxvk_sparse.cpp b/src/dxvk/dxvk_sparse.cpp index cbe318901..a8547bcfe 100644 --- a/src/dxvk/dxvk_sparse.cpp +++ b/src/dxvk/dxvk_sparse.cpp @@ -410,7 +410,7 @@ namespace dxvk { DxvkCommandList* cmd, uint32_t page, DxvkSparseMapping&& mapping) { - if (m_mappings[page] != page) { + if (m_mappings[page] != mapping) { if (m_mappings[page]) cmd->trackResource(m_mappings[page].m_page); From afc6aa70fbe3470248e4b68f9c4aee2cc8e8ca42 Mon Sep 17 00:00:00 2001 From: Tatsuyuki Ishi Date: Sat, 27 Jan 2024 12:39:54 +0900 Subject: [PATCH 050/246] [d3d11] Explicitly convert DxvkExt to bool in D3D11DeviceFeatures When assigning to a BOOL (which is an uint in disguise) and using explicit bool conversion operators (introduced in a latter commit) an explicit cast is required. --- src/d3d11/d3d11_features.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/d3d11/d3d11_features.cpp b/src/d3d11/d3d11_features.cpp index f206e517c..bbaf697f4 100644 --- a/src/d3d11/d3d11_features.cpp +++ b/src/d3d11/d3d11_features.cpp @@ -107,7 +107,7 @@ namespace dxvk { m_gpuVirtualAddress.MaxGPUVirtualAddressBitsPerProcess = 40; // Marker support only depends on the debug utils extension - m_marker.Profile = Instance->extensions().extDebugUtils; + m_marker.Profile = static_cast(Instance->extensions().extDebugUtils); // DXVK will keep all shaders in memory once created, and all Vulkan // drivers that we know of that can run DXVK have an on-disk cache. From e2a46a347d7193ee853bd8c75cbca5939f10a987 Mon Sep 17 00:00:00 2001 From: Tatsuyuki Ishi Date: Sat, 27 Jan 2024 12:42:40 +0900 Subject: [PATCH 051/246] [meta] Declare bool conversion operators as explicit Non-explicit conversion operators in general can participate in very surprising conversion chains. Explicit bool operator is a good place to start with, because even with explicit they do get automatic contextual conversion in a lot of places, e.g., if conditions. --- src/d3d9/d3d9_mem.h | 4 ++-- src/d3d9/d3d9_state.h | 4 ++-- src/dxbc/dxbc_decoder.h | 2 +- src/dxvk/dxvk_cs.h | 2 +- src/dxvk/dxvk_extensions.h | 2 +- src/dxvk/dxvk_memory.h | 2 +- src/dxvk/dxvk_sparse.h | 4 ++-- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/d3d9/d3d9_mem.h b/src/d3d9/d3d9_mem.h index 1da862653..7af79da41 100644 --- a/src/d3d9/d3d9_mem.h +++ b/src/d3d9/d3d9_mem.h @@ -81,7 +81,7 @@ namespace dxvk { D3D9Memory (D3D9Memory&& other); D3D9Memory& operator = (D3D9Memory&& other); - operator bool() const { return m_chunk != nullptr; } + explicit operator bool() const { return m_chunk != nullptr; } void Map(); void Unmap(); @@ -139,7 +139,7 @@ namespace dxvk { D3D9Memory (D3D9Memory&& other); D3D9Memory& operator = (D3D9Memory&& other); - operator bool() const { return m_ptr != nullptr; } + explicit operator bool() const { return m_ptr != nullptr; } void Map() {} void Unmap() {} diff --git a/src/d3d9/d3d9_state.h b/src/d3d9/d3d9_state.h index ad113d130..8aeb23e30 100644 --- a/src/d3d9/d3d9_state.h +++ b/src/d3d9/d3d9_state.h @@ -195,7 +195,7 @@ namespace dxvk { const T* operator & () const { ensure(); return m_data.get(); } T* operator & () { ensure(); return m_data.get(); } - operator bool() { return m_data != nullptr; } + explicit operator bool() const { return m_data != nullptr; } operator T() { ensure(); return *m_data; } void ensure() const { if (!m_data) m_data = std::make_unique(); } @@ -213,7 +213,7 @@ namespace dxvk { T& operator=(const T& x) { m_data = x; return m_data; } - operator bool() { return true; } + explicit operator bool() const { return true; } operator T() { return m_data; } const T* operator -> () const { return &m_data; } diff --git a/src/dxbc/dxbc_decoder.h b/src/dxbc/dxbc_decoder.h index 326e1aaf7..3fa4c3779 100644 --- a/src/dxbc/dxbc_decoder.h +++ b/src/dxbc/dxbc_decoder.h @@ -196,7 +196,7 @@ namespace dxvk { return out; } - operator bool () const { + explicit operator bool () const { return m_mask != 0; } diff --git a/src/dxvk/dxvk_cs.h b/src/dxvk/dxvk_cs.h index 589e5a8d7..ae20746a2 100644 --- a/src/dxvk/dxvk_cs.h +++ b/src/dxvk/dxvk_cs.h @@ -350,7 +350,7 @@ namespace dxvk { return m_chunk; } - operator bool () const { + explicit operator bool () const { return m_chunk != nullptr; } diff --git a/src/dxvk/dxvk_extensions.h b/src/dxvk/dxvk_extensions.h index 8164ccf6a..ae4c8a74f 100644 --- a/src/dxvk/dxvk_extensions.h +++ b/src/dxvk/dxvk_extensions.h @@ -61,7 +61,7 @@ namespace dxvk { * provided by the extension can be used. * \returns \c true if the extension is enabled */ - operator bool () const { + explicit operator bool () const { return m_revision != 0; } diff --git a/src/dxvk/dxvk_memory.h b/src/dxvk/dxvk_memory.h index 1dfb535ad..14dadd5f3 100644 --- a/src/dxvk/dxvk_memory.h +++ b/src/dxvk/dxvk_memory.h @@ -158,7 +158,7 @@ namespace dxvk { * \returns \c true if this slice points to actual device * memory, and \c false if it is undefined. */ - operator bool () const { + explicit operator bool () const { return m_memory != VK_NULL_HANDLE; } diff --git a/src/dxvk/dxvk_sparse.h b/src/dxvk/dxvk_sparse.h index 14d408279..86231d3e2 100644 --- a/src/dxvk/dxvk_sparse.h +++ b/src/dxvk/dxvk_sparse.h @@ -239,7 +239,7 @@ namespace dxvk { return m_page != other.m_page; } - operator bool () const { + explicit operator bool () const { return m_page != nullptr; } @@ -341,7 +341,7 @@ namespace dxvk { * \brief Checks whether page table is defined * \returns \c true if the page table is defined */ - operator bool () const { + explicit operator bool () const { return m_buffer || m_image; } From c423819e9026322e98ad9f8b72a0f25a5d561795 Mon Sep 17 00:00:00 2001 From: Ethan Lee Date: Mon, 29 Jan 2024 17:39:48 -0500 Subject: [PATCH 052/246] [meson] Only use the libdisplay-info subproject as a fallback --- meson.build | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/meson.build b/meson.build index 8e7f2c4d7..020a21a62 100644 --- a/meson.build +++ b/meson.build @@ -39,8 +39,12 @@ dxvk_include_dirs = [ './include/spirv/include' ] -proj_displayinfo = subproject('libdisplay-info') -dep_displayinfo = proj_displayinfo.get_variable('di_dep') +dep_displayinfo = dependency( + 'libdisplay-info', + version: ['>= 0.0.0', '< 0.2.0'], + fallback: ['libdisplay-info', 'di_dep'], + default_options: ['default_library=static'], +) if platform == 'windows' compiler_args += [ From eb339bc7e45f2d9a1e230a19ec14ceb7fe10e241 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Fri, 2 Feb 2024 17:20:24 +0100 Subject: [PATCH 053/246] [dxgi,d3d11] Move syncInterval override to DXGI swap chain This way it also applies to D3D12 games. --- src/d3d11/d3d11_options.cpp | 1 - src/d3d11/d3d11_options.h | 4 ---- src/d3d11/d3d11_swapchain.cpp | 5 ----- src/dxgi/dxgi_options.cpp | 2 ++ src/dxgi/dxgi_options.h | 4 ++++ src/dxgi/dxgi_swapchain.cpp | 5 +++++ 6 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/d3d11/d3d11_options.cpp b/src/d3d11/d3d11_options.cpp index b1833fd0b..edb2272b1 100644 --- a/src/d3d11/d3d11_options.cpp +++ b/src/d3d11/d3d11_options.cpp @@ -31,7 +31,6 @@ namespace dxvk { this->numBackBuffers = config.getOption("dxgi.numBackBuffers", 0); this->maxFrameLatency = config.getOption("dxgi.maxFrameLatency", 0); this->maxFrameRate = config.getOption("dxgi.maxFrameRate", 0); - this->syncInterval = config.getOption("dxgi.syncInterval", -1); // Clamp LOD bias so that people don't abuse this in unintended ways this->samplerLodBias = dxvk::fclamp(this->samplerLodBias, -2.0f, 1.0f); diff --git a/src/d3d11/d3d11_options.h b/src/d3d11/d3d11_options.h index d7e08008b..0da7b9e2a 100644 --- a/src/d3d11/d3d11_options.h +++ b/src/d3d11/d3d11_options.h @@ -76,10 +76,6 @@ namespace dxvk { /// Overrides DXGI_SWAP_CHAIN_DESC::BufferCount. int32_t numBackBuffers; - /// Sync interval. Overrides the value - /// passed to IDXGISwapChain::Present. - int32_t syncInterval; - /// Override maximum frame latency if the app specifies /// a higher value. May help with frame timing issues. int32_t maxFrameLatency; diff --git a/src/d3d11/d3d11_swapchain.cpp b/src/d3d11/d3d11_swapchain.cpp index 0e823f410..73939e2c9 100644 --- a/src/d3d11/d3d11_swapchain.cpp +++ b/src/d3d11/d3d11_swapchain.cpp @@ -254,11 +254,6 @@ namespace dxvk { UINT SyncInterval, UINT PresentFlags, const DXGI_PRESENT_PARAMETERS* pPresentParameters) { - auto options = m_parent->GetOptions(); - - if (options->syncInterval >= 0) - SyncInterval = options->syncInterval; - if (!(PresentFlags & DXGI_PRESENT_TEST)) m_dirty |= m_presenter->setSyncInterval(SyncInterval) != VK_SUCCESS; diff --git a/src/dxgi/dxgi_options.cpp b/src/dxgi/dxgi_options.cpp index c23660777..fe6cebccf 100644 --- a/src/dxgi/dxgi_options.cpp +++ b/src/dxgi/dxgi_options.cpp @@ -78,6 +78,8 @@ namespace dxvk { this->maxDeviceMemory = VkDeviceSize(config.getOption("dxgi.maxDeviceMemory", 0)) << 20; this->maxSharedMemory = VkDeviceSize(config.getOption("dxgi.maxSharedMemory", 0)) << 20; + this->syncInterval = config.getOption("dxgi.syncInterval", -1); + // Expose Nvidia GPUs properly if NvAPI is enabled in environment this->hideNvidiaGpu = !isNvapiEnabled(); diff --git a/src/dxgi/dxgi_options.h b/src/dxgi/dxgi_options.h index c64924713..c09373305 100644 --- a/src/dxgi/dxgi_options.h +++ b/src/dxgi/dxgi_options.h @@ -47,6 +47,10 @@ namespace dxvk { /// Use monitor fallback to enumerating all monitors per output bool useMonitorFallback; + + /// Sync interval. Overrides the value + /// passed to IDXGISwapChain::Present. + int32_t syncInterval; }; } diff --git a/src/dxgi/dxgi_swapchain.cpp b/src/dxgi/dxgi_swapchain.cpp index ba65a86af..ab96bfca1 100644 --- a/src/dxgi/dxgi_swapchain.cpp +++ b/src/dxgi/dxgi_swapchain.cpp @@ -312,6 +312,11 @@ namespace dxvk { if (SyncInterval > 4) return DXGI_ERROR_INVALID_CALL; + auto options = m_factory->GetOptions(); + + if (options->syncInterval >= 0) + SyncInterval = options->syncInterval; + UpdateGlobalHDRState(); std::lock_guard lockWin(m_lockWindow); From 05cb963e229bcb307ce1a433ed6853d4d1181c9b Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Fri, 2 Feb 2024 17:29:27 +0100 Subject: [PATCH 054/246] [util] Set sync interval override for P3R --- src/util/config/config.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index b57bc39b1..50aca5b35 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -909,6 +909,11 @@ namespace dxvk { { R"(\\Metro Exodus Enhanced Edition\\MetroExodus\.exe$)", {{ { "dxvk.hideIntegratedGraphics", "True" }, }} }, + /* Persona 3 Reload - disables vsync by default and + * runs into severe frame latency issues on Deck. */ + { R"(\\P3R\.exe$)", {{ + { "dxgi.syncInterval", "1" }, + }} }, }}; From 30f2b2df31f7ca43a8338465e25c63132a62f315 Mon Sep 17 00:00:00 2001 From: Ethan Lee Date: Sun, 4 Feb 2024 19:15:25 -0500 Subject: [PATCH 055/246] package-native.sh should force libdisplay-info subproject This ensures that the script produces a build similar to the default steamrt build, even if libdisplay-info is available on the build system root. --- package-native.sh | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/package-native.sh b/package-native.sh index 3ef659438..56915e632 100755 --- a/package-native.sh +++ b/package-native.sh @@ -56,13 +56,14 @@ function build_arch { opt_strip=--strip fi - CC="$CC -m$1" CXX="$CXX -m$1" meson setup \ - --buildtype "release" \ - --prefix "$DXVK_BUILD_DIR/usr" \ - $opt_strip \ - --bindir "$2" \ - --libdir "$2" \ - -Dbuild_id=$opt_buildid \ + CC="$CC -m$1" CXX="$CXX -m$1" meson setup \ + --buildtype "release" \ + --prefix "$DXVK_BUILD_DIR/usr" \ + $opt_strip \ + --bindir "$2" \ + --libdir "$2" \ + -Dbuild_id=$opt_buildid \ + --force-fallback-for=libdisplay-info \ "$DXVK_BUILD_DIR/build.$1" cd "$DXVK_BUILD_DIR/build.$1" From f83ba898afac3d58ffb04d989270722cbc8e0b7c Mon Sep 17 00:00:00 2001 From: Robin Kertels Date: Fri, 29 Dec 2023 21:51:05 +0100 Subject: [PATCH 056/246] [dxvk] Use signed int for vertexOffset BaseVertexIndex is signed in Vulkan, D3D11 & D3D9. --- src/dxvk/dxvk_cmdlist.h | 2 +- src/dxvk/dxvk_context.cpp | 2 +- src/dxvk/dxvk_context.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/dxvk/dxvk_cmdlist.h b/src/dxvk/dxvk_cmdlist.h index b9b9a165d..1934b9a05 100644 --- a/src/dxvk/dxvk_cmdlist.h +++ b/src/dxvk/dxvk_cmdlist.h @@ -671,7 +671,7 @@ namespace dxvk { uint32_t indexCount, uint32_t instanceCount, uint32_t firstIndex, - uint32_t vertexOffset, + int32_t vertexOffset, uint32_t firstInstance) { m_vkd->vkCmdDrawIndexed(m_cmd.execBuffer, indexCount, instanceCount, diff --git a/src/dxvk/dxvk_context.cpp b/src/dxvk/dxvk_context.cpp index 80c2620de..bbd139edb 100644 --- a/src/dxvk/dxvk_context.cpp +++ b/src/dxvk/dxvk_context.cpp @@ -1354,7 +1354,7 @@ namespace dxvk { uint32_t indexCount, uint32_t instanceCount, uint32_t firstIndex, - uint32_t vertexOffset, + int32_t vertexOffset, uint32_t firstInstance) { if (this->commitGraphicsState()) { m_cmd->cmdDrawIndexed( diff --git a/src/dxvk/dxvk_context.h b/src/dxvk/dxvk_context.h index 93ed91e36..b1fbc7dfe 100644 --- a/src/dxvk/dxvk_context.h +++ b/src/dxvk/dxvk_context.h @@ -810,7 +810,7 @@ namespace dxvk { uint32_t indexCount, uint32_t instanceCount, uint32_t firstIndex, - uint32_t vertexOffset, + int32_t vertexOffset, uint32_t firstInstance); /** From 62d64bd63a99980676a4f3d5ad171356646b4656 Mon Sep 17 00:00:00 2001 From: Robin Kertels Date: Tue, 19 Dec 2023 17:13:47 +0100 Subject: [PATCH 057/246] [d3d9] Don't upload buffers before Up draws --- src/d3d9/d3d9_device.cpp | 16 ++++++++-------- src/d3d9/d3d9_device.h | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/d3d9/d3d9_device.cpp b/src/d3d9/d3d9_device.cpp index a0c0a1ecb..2716f7382 100644 --- a/src/d3d9/d3d9_device.cpp +++ b/src/d3d9/d3d9_device.cpp @@ -2615,7 +2615,7 @@ namespace dxvk { if (unlikely(!PrimitiveCount)) return S_OK; - PrepareDraw(PrimitiveType); + PrepareDraw(PrimitiveType, true); EmitCs([this, cPrimType = PrimitiveType, @@ -2652,7 +2652,7 @@ namespace dxvk { if (unlikely(!PrimitiveCount)) return S_OK; - PrepareDraw(PrimitiveType); + PrepareDraw(PrimitiveType, true); EmitCs([this, cPrimType = PrimitiveType, @@ -2688,7 +2688,7 @@ namespace dxvk { if (unlikely(!PrimitiveCount)) return S_OK; - PrepareDraw(PrimitiveType); + PrepareDraw(PrimitiveType, false); uint32_t vertexCount = GetVertexCount(PrimitiveType, PrimitiveCount); @@ -2740,7 +2740,7 @@ namespace dxvk { if (unlikely(!PrimitiveCount)) return S_OK; - PrepareDraw(PrimitiveType); + PrepareDraw(PrimitiveType, false); uint32_t vertexCount = GetVertexCount(PrimitiveType, PrimitiveCount); @@ -2827,7 +2827,7 @@ namespace dxvk { D3D9CommonBuffer* dst = static_cast(pDestBuffer)->GetCommonBuffer(); D3D9VertexDecl* decl = static_cast (pVertexDecl); - PrepareDraw(D3DPT_FORCE_DWORD); + PrepareDraw(D3DPT_FORCE_DWORD, true); if (decl == nullptr) { DWORD FVF = dst->Desc()->FVF; @@ -6454,7 +6454,7 @@ namespace dxvk { } - void D3D9DeviceEx::PrepareDraw(D3DPRIMITIVETYPE PrimitiveType) { + void D3D9DeviceEx::PrepareDraw(D3DPRIMITIVETYPE PrimitiveType, bool UploadBuffers) { if (unlikely(m_activeHazardsRT != 0 || m_activeHazardsDS != 0)) MarkRenderHazards(); @@ -6467,7 +6467,7 @@ namespace dxvk { for (uint32_t i = 0; i < caps::MaxStreams; i++) { auto* vbo = GetCommonBuffer(m_state.vertexBuffers[i].vertexBuffer); - if (vbo != nullptr && vbo->NeedsUpload()) + if (vbo != nullptr && vbo->NeedsUpload() && UploadBuffers) FlushBuffer(vbo); } @@ -6483,7 +6483,7 @@ namespace dxvk { GenerateTextureMips(texturesToGen); auto* ibo = GetCommonBuffer(m_state.indices); - if (ibo != nullptr && ibo->NeedsUpload()) + if (ibo != nullptr && ibo->NeedsUpload() && UploadBuffers) FlushBuffer(ibo); UpdateFog(); diff --git a/src/d3d9/d3d9_device.h b/src/d3d9/d3d9_device.h index d7ac8c00a..b58d2910e 100644 --- a/src/d3d9/d3d9_device.h +++ b/src/d3d9/d3d9_device.h @@ -895,7 +895,7 @@ namespace dxvk { uint32_t GetInstanceCount() const; - void PrepareDraw(D3DPRIMITIVETYPE PrimitiveType); + void PrepareDraw(D3DPRIMITIVETYPE PrimitiveType, bool UploadBuffers); template void BindShader( From 5312ef1cf9c201d20048f9e796d21b436420fd49 Mon Sep 17 00:00:00 2001 From: Robin Kertels Date: Tue, 19 Dec 2023 18:37:44 +0100 Subject: [PATCH 058/246] [d3d9] Upload DYNAMIC+SYSMEM vertex and index data for each draw --- src/d3d9/d3d9_common_buffer.h | 4 + src/d3d9/d3d9_device.cpp | 212 +++++++++++++++++++++++++-- src/d3d9/d3d9_device.h | 24 ++- src/d3d9/d3d9_swvp_emu.cpp | 2 +- src/d3d9/d3d9_vertex_declaration.cpp | 4 +- src/d3d9/d3d9_vertex_declaration.h | 7 +- 6 files changed, 233 insertions(+), 20 deletions(-) diff --git a/src/d3d9/d3d9_common_buffer.h b/src/d3d9/d3d9_common_buffer.h index 745961980..00bea9530 100644 --- a/src/d3d9/d3d9_common_buffer.h +++ b/src/d3d9/d3d9_common_buffer.h @@ -211,6 +211,10 @@ namespace dxvk { : DxvkCsThread::SynchronizeAll; } + bool IsSysmemDynamic() const { + return m_desc.Pool == D3DPOOL_SYSTEMMEM && (m_desc.Usage & D3DUSAGE_DYNAMIC) != 0; + } + private: Rc CreateBuffer() const; diff --git a/src/d3d9/d3d9_device.cpp b/src/d3d9/d3d9_device.cpp index 2716f7382..d4330cc59 100644 --- a/src/d3d9/d3d9_device.cpp +++ b/src/d3d9/d3d9_device.cpp @@ -2615,7 +2615,21 @@ namespace dxvk { if (unlikely(!PrimitiveCount)) return S_OK; - PrepareDraw(PrimitiveType, true); + bool dynamicSysmemVBOs; + uint32_t firstIndex = 0; + int32_t baseVertexIndex = 0; + uint32_t vertexCount = GetVertexCount(PrimitiveType, PrimitiveCount); + UploadDynamicSysmemBuffers( + StartVertex, + vertexCount, + firstIndex, + 0, + baseVertexIndex, + &dynamicSysmemVBOs, + nullptr + ); + + PrepareDraw(PrimitiveType, !dynamicSysmemVBOs, false); EmitCs([this, cPrimType = PrimitiveType, @@ -2636,7 +2650,6 @@ namespace dxvk { return D3D_OK; } - HRESULT STDMETHODCALLTYPE D3D9DeviceEx::DrawIndexedPrimitive( D3DPRIMITIVETYPE PrimitiveType, INT BaseVertexIndex, @@ -2651,8 +2664,21 @@ namespace dxvk { if (unlikely(!PrimitiveCount)) return S_OK; + + bool dynamicSysmemVBOs; + bool dynamicSysmemIBO; + uint32_t indexCount = GetVertexCount(PrimitiveType, PrimitiveCount); + UploadDynamicSysmemBuffers( + MinVertexIndex, + NumVertices, + StartIndex, + indexCount, + BaseVertexIndex, + &dynamicSysmemVBOs, + &dynamicSysmemIBO + ); - PrepareDraw(PrimitiveType, true); + PrepareDraw(PrimitiveType, !dynamicSysmemVBOs, !dynamicSysmemIBO); EmitCs([this, cPrimType = PrimitiveType, @@ -2688,7 +2714,7 @@ namespace dxvk { if (unlikely(!PrimitiveCount)) return S_OK; - PrepareDraw(PrimitiveType, false); + PrepareDraw(PrimitiveType, false, false); uint32_t vertexCount = GetVertexCount(PrimitiveType, PrimitiveCount); @@ -2740,7 +2766,7 @@ namespace dxvk { if (unlikely(!PrimitiveCount)) return S_OK; - PrepareDraw(PrimitiveType, false); + PrepareDraw(PrimitiveType, false, false); uint32_t vertexCount = GetVertexCount(PrimitiveType, PrimitiveCount); @@ -2827,7 +2853,7 @@ namespace dxvk { D3D9CommonBuffer* dst = static_cast(pDestBuffer)->GetCommonBuffer(); D3D9VertexDecl* decl = static_cast (pVertexDecl); - PrepareDraw(D3DPT_FORCE_DWORD, true); + PrepareDraw(D3DPT_FORCE_DWORD, true, true); if (decl == nullptr) { DWORD FVF = dst->Desc()->FVF; @@ -2842,7 +2868,7 @@ namespace dxvk { decl = iter->second.ptr(); } - uint32_t offset = DestIndex * decl->GetSize(); + uint32_t offset = DestIndex * decl->GetSize(0); auto slice = dst->GetBufferSlice(); slice = slice.subSlice(offset, slice.length() - offset); @@ -2889,7 +2915,7 @@ namespace dxvk { } if (dst->GetMapMode() == D3D9_COMMON_BUFFER_MAP_MODE_BUFFER) { - uint32_t copySize = VertexCount * decl->GetSize(); + uint32_t copySize = VertexCount * decl->GetSize(0); EmitCs([ cSrcBuffer = dst->GetBuffer(), @@ -5091,6 +5117,157 @@ namespace dxvk { return D3D_OK; } + + + void D3D9DeviceEx::UploadDynamicSysmemBuffers( + UINT& FirstVertexIndex, + UINT NumVertices, + UINT& FirstIndex, + UINT NumIndices, + INT& BaseVertexIndex, + bool* pDynamicVBOs, + bool* pDynamicIBO + ) { + bool dynamicSysmemVBOs = true; + for (uint32_t i = 0; i < caps::MaxStreams && dynamicSysmemVBOs; i++) { + auto* vbo = GetCommonBuffer(m_state.vertexBuffers[i].vertexBuffer); + dynamicSysmemVBOs &= vbo == nullptr || vbo->IsSysmemDynamic(); + } + D3D9CommonBuffer* ibo = GetCommonBuffer(m_state.indices); + bool dynamicSysmemIBO = NumIndices != 0 && ibo->IsSysmemDynamic(); + + *pDynamicVBOs = dynamicSysmemVBOs; + + if (pDynamicIBO) + *pDynamicIBO = dynamicSysmemIBO; + + if (likely(!dynamicSysmemVBOs && !dynamicSysmemIBO)) + return; + + // The UP buffer allocator will invalidate, + // so we can only use 1 UP buffer slice per draw. + // First we calculate the size of that UP buffer slice + // and store all sizes and offsets into it. + + uint32_t upBufferSize = 0; + std::array vboUPBufferOffsets = {}; + std::array vboUPBufferSizes = {}; + for (uint32_t i = 0; i < caps::MaxStreams && dynamicSysmemVBOs; i++) { + vboUPBufferOffsets[i] = upBufferSize; + + auto* vbo = GetCommonBuffer(m_state.vertexBuffers[i].vertexBuffer); + if (likely(vbo == nullptr)) { + vboUPBufferSizes[i] = 0; + continue; + } + const uint32_t vertexStride = m_state.vertexDecl->GetSize(i); + uint32_t offset = (FirstVertexIndex + BaseVertexIndex) * vertexStride; + const uint32_t vertexBufferSize = vbo->Desc()->Size; + if (offset < vertexBufferSize) { + const uint32_t vertexDataSize = std::min(NumVertices * vertexStride, vertexBufferSize - offset); + vboUPBufferSizes[i] = vertexDataSize; + upBufferSize += vertexDataSize; + } + } + + uint32_t iboUPBufferSize = 0; + uint32_t iboUPBufferOffset = 0; + if (dynamicSysmemIBO) { + auto* ibo = GetCommonBuffer(m_state.indices); + if (likely(ibo != nullptr)) { + uint32_t indexStride = ibo->Desc()->Format == D3D9Format::INDEX16 ? 2 : 4; + VkIndexType indexType = DecodeIndexType(ibo->Desc()->Format); + uint32_t offset = indexStride * FirstIndex; + uint32_t indexBufferSize = ibo->Desc()->Size; + if (offset < indexBufferSize) { + iboUPBufferSize = std::min(NumIndices * indexStride, indexBufferSize - offset); + iboUPBufferOffset = upBufferSize; + upBufferSize += iboUPBufferSize; + } + } + } + + if (unlikely(upBufferSize == 0)) { + *pDynamicVBOs = false; + if (pDynamicIBO) + *pDynamicIBO = false; + + return; + } + + auto upSlice = AllocUPBuffer(upBufferSize); + + // Now copy the actual data and bind it. + if (dynamicSysmemVBOs) { + for (uint32_t i = 0; i < caps::MaxStreams; i++) { + if (unlikely(vboUPBufferSizes[i] == 0)) { + EmitCs([ + cStream = i + ](DxvkContext* ctx) { + ctx->bindVertexBuffer(cStream, DxvkBufferSlice(), 0); + }); + m_flags.set(D3D9DeviceFlag::DirtyVertexBuffers); + continue; + } + + auto* vbo = GetCommonBuffer(m_state.vertexBuffers[i].vertexBuffer); + + const uint32_t vertexStride = m_state.vertexDecl->GetSize(i); + uint32_t offset = (BaseVertexIndex + FirstVertexIndex) * vertexStride; + + uint8_t* data = reinterpret_cast(upSlice.mapPtr) + vboUPBufferOffsets[i]; + uint8_t* src = reinterpret_cast(vbo->GetMappedSlice().mapPtr) + offset; + std::memcpy(data, src, vboUPBufferSizes[i]); + + auto vboSlice = upSlice.slice.subSlice(vboUPBufferOffsets[i], vboUPBufferSizes[i]); + EmitCs([ + cStream = i, + cBufferSlice = std::move(vboSlice), + cStride = vertexStride + ](DxvkContext* ctx) mutable { + ctx->bindVertexBuffer(cStream, std::move(cBufferSlice), cStride); + }); + m_flags.set(D3D9DeviceFlag::DirtyVertexBuffers); + } + + // Change the draw call parameters to reflect the changed vertex buffers + if (NumIndices != 0) { + BaseVertexIndex = -FirstVertexIndex; + } else { + FirstVertexIndex = 0; + } + } + + if (dynamicSysmemIBO) { + if (unlikely(iboUPBufferSize == 0)) { + EmitCs([](DxvkContext* ctx) { + ctx->bindIndexBuffer(DxvkBufferSlice(), VK_INDEX_TYPE_UINT32); + }); + m_flags.set(D3D9DeviceFlag::DirtyIndexBuffer); + } else { + auto* ibo = GetCommonBuffer(m_state.indices); + uint32_t indexStride = ibo->Desc()->Format == D3D9Format::INDEX16 ? 2 : 4; + VkIndexType indexType = DecodeIndexType(ibo->Desc()->Format); + uint32_t offset = indexStride * FirstIndex; + uint8_t* data = reinterpret_cast(upSlice.mapPtr) + iboUPBufferOffset; + uint8_t* src = reinterpret_cast(ibo->GetMappedSlice().mapPtr) + offset; + std::memcpy(data, src, iboUPBufferSize); + + auto iboSlice = upSlice.slice.subSlice(iboUPBufferOffset, iboUPBufferSize); + EmitCs([ + cBufferSlice = std::move(iboSlice), + cIndexType = indexType + ](DxvkContext* ctx) mutable { + ctx->bindIndexBuffer(std::move(cBufferSlice), cIndexType); + }); + m_flags.set(D3D9DeviceFlag::DirtyIndexBuffer); + } + + // Change the draw call parameters to reflect the changed index buffer + FirstIndex = 0; + } + } + void D3D9DeviceEx::EmitCsChunk(DxvkCsChunkRef&& chunk) { m_csSeqNum = m_csThread.dispatchChunk(std::move(chunk)); @@ -6454,7 +6631,7 @@ namespace dxvk { } - void D3D9DeviceEx::PrepareDraw(D3DPRIMITIVETYPE PrimitiveType, bool UploadBuffers) { + void D3D9DeviceEx::PrepareDraw(D3DPRIMITIVETYPE PrimitiveType, bool UploadVBOs, bool UploadIBO) { if (unlikely(m_activeHazardsRT != 0 || m_activeHazardsDS != 0)) MarkRenderHazards(); @@ -6467,7 +6644,7 @@ namespace dxvk { for (uint32_t i = 0; i < caps::MaxStreams; i++) { auto* vbo = GetCommonBuffer(m_state.vertexBuffers[i].vertexBuffer); - if (vbo != nullptr && vbo->NeedsUpload() && UploadBuffers) + if (vbo != nullptr && vbo->NeedsUpload() && UploadVBOs) FlushBuffer(vbo); } @@ -6483,7 +6660,7 @@ namespace dxvk { GenerateTextureMips(texturesToGen); auto* ibo = GetCommonBuffer(m_state.indices); - if (ibo != nullptr && ibo->NeedsUpload() && UploadBuffers) + if (ibo != nullptr && ibo->NeedsUpload() && UploadIBO) FlushBuffer(ibo); UpdateFog(); @@ -6616,6 +6793,19 @@ namespace dxvk { } BindSpecConstants(); + + if (unlikely(m_flags.test(D3D9DeviceFlag::DirtyVertexBuffers) && UploadVBOs)) { + for (uint32_t i = 0; i < caps::MaxStreams; i++) { + const D3D9VBO& vbo = m_state.vertexBuffers[i]; + BindVertexBuffer(i, vbo.vertexBuffer.ptr(), vbo.offset, vbo.stride); + } + m_flags.clr(D3D9DeviceFlag::DirtyVertexBuffers); + } + + if (unlikely(m_flags.test(D3D9DeviceFlag::DirtyIndexBuffer) && UploadIBO)) { + BindIndices(); + m_flags.clr(D3D9DeviceFlag::DirtyIndexBuffer); + } } diff --git a/src/d3d9/d3d9_device.h b/src/d3d9/d3d9_device.h index b58d2910e..2fc74c836 100644 --- a/src/d3d9/d3d9_device.h +++ b/src/d3d9/d3d9_device.h @@ -66,6 +66,8 @@ namespace dxvk { DirtyInputLayout, DirtyViewportScissor, DirtyMultiSampleState, + DirtyVertexBuffers, + DirtyIndexBuffer, DirtyFogState, DirtyFogColor, @@ -764,6 +766,24 @@ namespace dxvk { HRESULT UnlockBuffer( D3D9CommonBuffer* pResource); + /** + * @brief Uploads data from D3DPOOL_SYSMEM + D3DUSAGE_DYNAMIC buffers and binds the temporary buffers. + * + * @param FirstVertexIndex The first vertex + * @param NumVertices The number of vertices that are accessed. If this is 0, the vertex buffer binding will not be modified. + * @param FirstIndex The first index + * @param NumIndices The number of indices that will be drawn. If this is 0, the index buffer binding will not be modified. + */ + void UploadDynamicSysmemBuffers( + UINT& FirstVertexIndex, + UINT NumVertices, + UINT& FirstIndex, + UINT NumIndices, + INT& BaseVertexIndex, + bool* pDynamicVBOs, + bool* pDynamicIBO); + + void SetupFPU(); int64_t DetermineInitialTextureMemory(); @@ -895,7 +915,7 @@ namespace dxvk { uint32_t GetInstanceCount() const; - void PrepareDraw(D3DPRIMITIVETYPE PrimitiveType, bool UploadBuffers); + void PrepareDraw(D3DPRIMITIVETYPE PrimitiveType, bool UploadVBOs, bool UploadIBOs); template void BindShader( @@ -1063,7 +1083,7 @@ namespace dxvk { } inline uint32_t GetUPBufferSize(uint32_t vertexCount, uint32_t stride) { - return (vertexCount - 1) * stride + std::max(m_state.vertexDecl->GetSize(), stride); + return (vertexCount - 1) * stride + std::max(m_state.vertexDecl->GetSize(0), stride); } inline void FillUPVertexBuffer(void* buffer, const void* userData, uint32_t dataSize, uint32_t bufferSize) { diff --git a/src/d3d9/d3d9_swvp_emu.cpp b/src/d3d9/d3d9_swvp_emu.cpp index 210edf01f..213bd2790 100644 --- a/src/d3d9/d3d9_swvp_emu.cpp +++ b/src/d3d9/d3d9_swvp_emu.cpp @@ -144,7 +144,7 @@ namespace dxvk { uint32_t primitiveId = m_module.opLoad(uint_t, primitiveIdPtr); // The size of any given vertex - uint32_t vertexSize = m_module.constu32(pDecl->GetSize() / sizeof(uint32_t)); + uint32_t vertexSize = m_module.constu32(pDecl->GetSize(0) / sizeof(uint32_t)); //The offset of this vertex from the beginning of the buffer uint32_t thisVertexOffset = m_module.opIMul(uint_t, vertexSize, primitiveId); diff --git a/src/d3d9/d3d9_vertex_declaration.cpp b/src/d3d9/d3d9_vertex_declaration.cpp index 58c43be43..be9cd25e8 100644 --- a/src/d3d9/d3d9_vertex_declaration.cpp +++ b/src/d3d9/d3d9_vertex_declaration.cpp @@ -354,8 +354,8 @@ namespace dxvk { void D3D9VertexDecl::Classify() { for (const auto& element : m_elements) { - if (element.Stream == 0 && element.Type != D3DDECLTYPE_UNUSED) - m_size = std::max(m_size, element.Offset + GetDecltypeSize(D3DDECLTYPE(element.Type))); + if (element.Type != D3DDECLTYPE_UNUSED) + m_sizes[element.Stream] = std::max(m_sizes[element.Stream], element.Offset + GetDecltypeSize(D3DDECLTYPE(element.Type))); if (element.Usage == D3DDECLUSAGE_COLOR && element.UsageIndex == 0) m_flags.set(D3D9VertexDeclFlag::HasColor0); diff --git a/src/d3d9/d3d9_vertex_declaration.h b/src/d3d9/d3d9_vertex_declaration.h index 82b182756..404555cc8 100644 --- a/src/d3d9/d3d9_vertex_declaration.h +++ b/src/d3d9/d3d9_vertex_declaration.h @@ -50,8 +50,8 @@ namespace dxvk { return m_elements; } - UINT GetSize() const { - return m_size; + UINT GetSize(UINT Stream) const { + return m_sizes[Stream]; } bool TestFlag(D3D9VertexDeclFlag flag) const { @@ -94,8 +94,7 @@ namespace dxvk { uint32_t m_texcoordMask = 0; - // The size of Stream 0. That's all we care about. - uint32_t m_size = 0; + std::array m_sizes = {}; }; From fae78407a2efd017ebe0eb58db463b2072291f67 Mon Sep 17 00:00:00 2001 From: Echo J Date: Tue, 6 Feb 2024 10:59:21 +0200 Subject: [PATCH 059/246] d3d9: Remove an unused variable This fixes a gcc warning (originally reported by Saancreed) --- src/d3d9/d3d9_device.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/d3d9/d3d9_device.cpp b/src/d3d9/d3d9_device.cpp index d4330cc59..92b0eb21f 100644 --- a/src/d3d9/d3d9_device.cpp +++ b/src/d3d9/d3d9_device.cpp @@ -5176,7 +5176,6 @@ namespace dxvk { auto* ibo = GetCommonBuffer(m_state.indices); if (likely(ibo != nullptr)) { uint32_t indexStride = ibo->Desc()->Format == D3D9Format::INDEX16 ? 2 : 4; - VkIndexType indexType = DecodeIndexType(ibo->Desc()->Format); uint32_t offset = indexStride * FirstIndex; uint32_t indexBufferSize = ibo->Desc()->Size; if (offset < indexBufferSize) { From e9a0fec5b3f03c460a8945a6599c99374d6ea614 Mon Sep 17 00:00:00 2001 From: Robin Kertels Date: Mon, 5 Feb 2024 18:08:28 +0100 Subject: [PATCH 060/246] [dxso] Clamp Exp when fast float emulation is enabled --- src/dxso/dxso_compiler.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/dxso/dxso_compiler.cpp b/src/dxso/dxso_compiler.cpp index 905a95134..abc55de81 100644 --- a/src/dxso/dxso_compiler.cpp +++ b/src/dxso/dxso_compiler.cpp @@ -2029,12 +2029,22 @@ namespace dxvk { result.id = resultIndices[0]; else result.id = m_module.opCompositeConstruct(typeId, result.type.ccount, resultIndices.data()); + + if (m_moduleInfo.options.d3d9FloatEmulation == D3D9FloatEmulation::Enabled) { + result.id = m_module.opNMin(typeId, result.id, + m_module.constfReplicant(FLT_MAX, result.type.ccount)); + } break; } [[fallthrough]]; case DxsoOpcode::Exp: result.id = m_module.opExp2(typeId, emitRegisterLoad(src[0], mask).id); + + if (m_moduleInfo.options.d3d9FloatEmulation == D3D9FloatEmulation::Enabled) { + result.id = m_module.opNMin(typeId, result.id, + m_module.constfReplicant(FLT_MAX, result.type.ccount)); + } break; case DxsoOpcode::Pow: { uint32_t base = emitRegisterLoad(src[0], mask).id; From ab3593185f45971977b956323c0a60f66f9f35e7 Mon Sep 17 00:00:00 2001 From: Robin Kertels Date: Mon, 5 Feb 2024 19:14:01 +0100 Subject: [PATCH 061/246] [d3d9] Fail GetRTData if src and dst have mismatching sizes MySims tries to do this and needs this to fail, otherwise mouse picking is off. --- src/d3d9/d3d9_device.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/d3d9/d3d9_device.cpp b/src/d3d9/d3d9_device.cpp index 92b0eb21f..a5fc7f5da 100644 --- a/src/d3d9/d3d9_device.cpp +++ b/src/d3d9/d3d9_device.cpp @@ -1037,6 +1037,9 @@ namespace dxvk { if (srcTexInfo->Desc()->Format != dstTexInfo->Desc()->Format) return D3DERR_INVALIDCALL; + if (src->GetSurfaceExtent() != dst->GetSurfaceExtent()) + return D3DERR_INVALIDCALL; + if (dstTexInfo->Desc()->Pool == D3DPOOL_DEFAULT) return this->StretchRect(pRenderTarget, nullptr, pDestSurface, nullptr, D3DTEXF_NONE); From 9491b56bebb7984c9da2760a6792c6589792e803 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Thu, 15 Feb 2024 20:33:06 +0100 Subject: [PATCH 062/246] util: Enable d3d11.ignoreGraphicsBarriers for Granblue Relink Improves GPU-bound performance due to the game using PS UAVs. --- src/util/config/config.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index 50aca5b35..b43776433 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -430,6 +430,11 @@ namespace dxvk { { R"(\\kenshi_x64\.exe$)", {{ { "d3d11.cachedDynamicResources", "v" }, }} }, + /* Granblue Relink: Spams pixel shader UAVs * + * like crazy, much like God of War */ + { R"(\\granblue_fantasy_relink\.exe$)", {{ + { "d3d11.ignoreGraphicsBarriers", "True" }, + }} }, /**********************************************/ /* D3D9 GAMES */ From 738fd4f895627fdc60a3753a21ab344ca7cc24b9 Mon Sep 17 00:00:00 2001 From: Robin Kertels Date: Thu, 15 Feb 2024 00:03:55 +0100 Subject: [PATCH 063/246] [d3d9] Don't actually unbind buffers --- src/d3d9/d3d9_device.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/d3d9/d3d9_device.cpp b/src/d3d9/d3d9_device.cpp index a5fc7f5da..76f59d1e9 100644 --- a/src/d3d9/d3d9_device.cpp +++ b/src/d3d9/d3d9_device.cpp @@ -3254,6 +3254,11 @@ namespace dxvk { vbo.offset = OffsetInBytes; vbo.stride = Stride; + } else { + // D3D9 doesn't actually unbind any vertex buffer when passing null. + // Operation Flashpoint: Red River relies on this behavior. + needsUpdate = false; + vbo.offset = 0; } if (needsUpdate) @@ -3359,7 +3364,8 @@ namespace dxvk { m_state.indices = buffer; - BindIndices(); + if (buffer != nullptr) + BindIndices(); return D3D_OK; } From 39c19e9299821d6a17a6270c101d253e795ad557 Mon Sep 17 00:00:00 2001 From: Robin Kertels Date: Thu, 15 Feb 2024 00:05:26 +0100 Subject: [PATCH 064/246] [d3d9] End scene on reset --- src/d3d9/d3d9_device.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/d3d9/d3d9_device.cpp b/src/d3d9/d3d9_device.cpp index 76f59d1e9..318235da4 100644 --- a/src/d3d9/d3d9_device.cpp +++ b/src/d3d9/d3d9_device.cpp @@ -455,6 +455,8 @@ namespace dxvk { SetDepthStencilSurface(nullptr); } + m_flags.clr(D3D9DeviceFlag::InScene); + /* * Before calling the IDirect3DDevice9::Reset method for a device, * an application should release any explicit render targets, From f254afb4fbce9f36703c19d8a7f5c4d2d7954cb9 Mon Sep 17 00:00:00 2001 From: Robin Kertels Date: Tue, 20 Feb 2024 21:22:25 +0100 Subject: [PATCH 065/246] [util] Enable strict float emulation for Red River --- src/util/config/config.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index b43776433..60f1d7ca4 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -873,7 +873,12 @@ namespace dxvk { { R"(\\SupremeRulerUltimate\.exe$)", {{ { "d3d9.countLosableResources", "False" }, }} }, - + /* Operation Flashpoint: Red River * + * Flickering issues */ + { R"(\\RedRiver\.exe$)", {{ + { "d3d9.floatEmulation", "Strict" }, + }} }, + /**********************************************/ /* D3D12 GAMES (vkd3d-proton with dxvk dxgi) */ From c5a37d443a580eb26bc77cf68446f7d0aacc92c7 Mon Sep 17 00:00:00 2001 From: Robin Kertels Date: Tue, 20 Feb 2024 20:53:19 +0100 Subject: [PATCH 066/246] [d3d9] Handle null IBO when uploading data for draw --- src/d3d9/d3d9_device.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/d3d9/d3d9_device.cpp b/src/d3d9/d3d9_device.cpp index 318235da4..f11ae4004 100644 --- a/src/d3d9/d3d9_device.cpp +++ b/src/d3d9/d3d9_device.cpp @@ -2669,7 +2669,7 @@ namespace dxvk { if (unlikely(!PrimitiveCount)) return S_OK; - + bool dynamicSysmemVBOs; bool dynamicSysmemIBO; uint32_t indexCount = GetVertexCount(PrimitiveType, PrimitiveCount); @@ -5145,7 +5145,7 @@ namespace dxvk { dynamicSysmemVBOs &= vbo == nullptr || vbo->IsSysmemDynamic(); } D3D9CommonBuffer* ibo = GetCommonBuffer(m_state.indices); - bool dynamicSysmemIBO = NumIndices != 0 && ibo->IsSysmemDynamic(); + bool dynamicSysmemIBO = NumIndices != 0 && ibo != nullptr && ibo->IsSysmemDynamic(); *pDynamicVBOs = dynamicSysmemVBOs; From 234f3ea071489c51c66982d8e19c23cfa29b2367 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Thu, 22 Feb 2024 15:36:58 +0100 Subject: [PATCH 067/246] [d3d11] Add option to hide native command list support --- dxvk.conf | 12 ++++++++++++ src/d3d11/d3d11_device.cpp | 6 +++--- src/d3d11/d3d11_features.cpp | 12 +++++++----- src/d3d11/d3d11_features.h | 2 ++ src/d3d11/d3d11_options.cpp | 3 ++- src/d3d11/d3d11_options.h | 7 ++++++- 6 files changed, 32 insertions(+), 10 deletions(-) diff --git a/dxvk.conf b/dxvk.conf index 09f5dfbaf..110d2d55b 100644 --- a/dxvk.conf +++ b/dxvk.conf @@ -292,6 +292,18 @@ # d3d11.enableContextLock = False +# Exposes or hides support for driver command lists +# +# Some games use the feature flag to decide whether to use deferred +# contexts or not. We enable this by default, but in some situations +# this can lead to issues if games detect an AMD GPU where command +# lists are not natively supported on Windows. +# +# Supported values: True, False + +# d3d11.exposeDriverCommandLists = True + + # Sets number of pipeline compiler threads. # # If the graphics pipeline library feature is enabled, the given diff --git a/src/d3d11/d3d11_device.cpp b/src/d3d11/d3d11_device.cpp index 9398e4842..2b9a82b9e 100644 --- a/src/d3d11/d3d11_device.cpp +++ b/src/d3d11/d3d11_device.cpp @@ -45,10 +45,10 @@ namespace dxvk { m_dxvkDevice (pContainer->GetDXVKDevice()), m_dxvkAdapter (m_dxvkDevice->adapter()), m_d3d11Formats (m_dxvkDevice), - m_d3d11Options (m_dxvkDevice->instance()->config(), m_dxvkDevice), + m_d3d11Options (m_dxvkDevice->instance()->config()), m_dxbcOptions (m_dxvkDevice, m_d3d11Options), m_maxFeatureLevel (GetMaxFeatureLevel(m_dxvkDevice->instance(), m_dxvkDevice->adapter())), - m_deviceFeatures (m_dxvkDevice->instance(), m_dxvkDevice->adapter(), m_featureLevel) { + m_deviceFeatures (m_dxvkDevice->instance(), m_dxvkDevice->adapter(), m_d3d11Options, m_featureLevel) { m_initializer = new D3D11Initializer(this); m_context = new D3D11ImmediateContext(this, m_dxvkDevice); m_d3d10Device = new D3D10Device(this, m_context.ptr()); @@ -1348,7 +1348,7 @@ namespace dxvk { m_deviceFeatures = D3D11DeviceFeatures( m_dxvkDevice->instance(), m_dxvkDevice->adapter(), - m_featureLevel); + m_d3d11Options, m_featureLevel); } if (pChosenFeatureLevel) diff --git a/src/d3d11/d3d11_features.cpp b/src/d3d11/d3d11_features.cpp index bbaf697f4..6b1816c27 100644 --- a/src/d3d11/d3d11_features.cpp +++ b/src/d3d11/d3d11_features.cpp @@ -12,6 +12,7 @@ namespace dxvk { D3D11DeviceFeatures::D3D11DeviceFeatures( const Rc& Instance, const Rc& Adapter, + const D3D11Options& Options, D3D_FEATURE_LEVEL FeatureLevel) : m_features (Adapter->features()), m_properties (Adapter->devicePropertiesExt()) { @@ -118,11 +119,11 @@ namespace dxvk { m_shaderMinPrecision.PixelShaderMinPrecision = 0; m_shaderMinPrecision.AllOtherShaderStagesMinPrecision = 0; - // Report native support for command lists here so that we do not actually have - // to re-implement the UpdateSubresource bug from the D3D11 runtime, see MSDN: - // https://msdn.microsoft.com/en-us/library/windows/desktop/ff476486(v=vs.85).aspx) + // Report native support for command lists by default. Deferred context + // usage can be beneficial for us as ExecuteCommandList has low overhead, + // and we avoid having to deal with known UpdateSubresource bugs this way. m_threading.DriverConcurrentCreates = TRUE; - m_threading.DriverCommandLists = TRUE; + m_threading.DriverCommandLists = Options.exposeDriverCommandLists; } @@ -182,7 +183,8 @@ namespace dxvk { D3D_FEATURE_LEVEL D3D11DeviceFeatures::GetMaxFeatureLevel( const Rc& Instance, const Rc& Adapter) { - D3D11DeviceFeatures features(Instance, Adapter, D3D_FEATURE_LEVEL_12_1); + D3D11Options options(Instance->config()); + D3D11DeviceFeatures features(Instance, Adapter, options, D3D_FEATURE_LEVEL_12_1); return features.GetMaxFeatureLevel(); } diff --git a/src/d3d11/d3d11_features.h b/src/d3d11/d3d11_features.h index 9bdd7c318..905f77b2b 100644 --- a/src/d3d11/d3d11_features.h +++ b/src/d3d11/d3d11_features.h @@ -1,6 +1,7 @@ #pragma once #include "d3d11_include.h" +#include "d3d11_options.h" #include "../dxvk/dxvk_adapter.h" #include "../dxvk/dxvk_instance.h" @@ -21,6 +22,7 @@ namespace dxvk { D3D11DeviceFeatures( const Rc& Instance, const Rc& Adapter, + const D3D11Options& Options, D3D_FEATURE_LEVEL FeatureLevel); ~D3D11DeviceFeatures(); diff --git a/src/d3d11/d3d11_options.cpp b/src/d3d11/d3d11_options.cpp index edb2272b1..bce1a713a 100644 --- a/src/d3d11/d3d11_options.cpp +++ b/src/d3d11/d3d11_options.cpp @@ -12,7 +12,7 @@ namespace dxvk { #endif } - D3D11Options::D3D11Options(const Config& config, const Rc& device) { + D3D11Options::D3D11Options(const Config& config) { this->dcSingleUseMode = config.getOption("d3d11.dcSingleUseMode", true); this->zeroInitWorkgroupMemory = config.getOption("d3d11.zeroInitWorkgroupMemory", false); this->forceVolatileTgsmAccess = config.getOption("d3d11.forceVolatileTgsmAccess", false); @@ -31,6 +31,7 @@ namespace dxvk { this->numBackBuffers = config.getOption("dxgi.numBackBuffers", 0); this->maxFrameLatency = config.getOption("dxgi.maxFrameLatency", 0); this->maxFrameRate = config.getOption("dxgi.maxFrameRate", 0); + this->exposeDriverCommandLists = config.getOption("d3d11.exposeDriverCommandLists", true); // Clamp LOD bias so that people don't abuse this in unintended ways this->samplerLodBias = dxvk::fclamp(this->samplerLodBias, -2.0f, 1.0f); diff --git a/src/d3d11/d3d11_options.h b/src/d3d11/d3d11_options.h index 0da7b9e2a..fe9a2ab69 100644 --- a/src/d3d11/d3d11_options.h +++ b/src/d3d11/d3d11_options.h @@ -11,7 +11,7 @@ namespace dxvk { struct D3D11Options { - D3D11Options(const Config& config, const Rc& device); + D3D11Options(const Config& config); /// Enables speed hack for mapping on deferred contexts /// @@ -113,6 +113,11 @@ namespace dxvk { /// race conditions. bool enableContextLock; + /// Whether to expose the driver command list feature. Enabled by + /// default and generally beneficial, but some games may assume that + /// this is not supported when running on an AMD GPU. + bool exposeDriverCommandLists; + /// Shader dump path std::string shaderDumpPath; }; From 5ded7d67f0f152941e2f2664ca1dae1b5ef690fb Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Thu, 22 Feb 2024 15:37:34 +0100 Subject: [PATCH 068/246] [d3d11] Implement UpdateSubresource bug if native command lists are disabled --- src/d3d11/d3d11_context.cpp | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/d3d11/d3d11_context.cpp b/src/d3d11/d3d11_context.cpp index 4b06ae380..b1399b7c1 100644 --- a/src/d3d11/d3d11_context.cpp +++ b/src/d3d11/d3d11_context.cpp @@ -928,6 +928,34 @@ namespace dxvk { const void* pSrcData, UINT SrcRowPitch, UINT SrcDepthPitch) { + if (IsDeferred && unlikely(pDstBox != nullptr) && unlikely(!m_parent->GetOptions()->exposeDriverCommandLists)) { + // If called from a deferred context and native command list support is not + // exposed, we need to apply the destination box to the source pointer. This + // only applies to UpdateSubresource, not to UpdateSubresource1. See MSDN: + // https://msdn.microsoft.com/en-us/library/windows/desktop/ff476486(v=vs.85).aspx) + size_t srcOffset = pDstBox->left; + + // For textures, the offset logic needs to take the format into account. + // Ignore that multi-planar images exist, this is hairy enough already. + D3D11CommonTexture* dstTexture = GetCommonTexture(pDstResource); + + if (dstTexture) { + auto dstFormat = dstTexture->GetPackedFormat(); + auto dstFormatInfo = lookupFormatInfo(dstFormat); + + size_t blockSize = dstFormatInfo->elementSize; + + VkOffset3D offset; + offset.x = pDstBox->left / dstFormatInfo->blockSize.width; + offset.y = pDstBox->top / dstFormatInfo->blockSize.height; + offset.z = pDstBox->front / dstFormatInfo->blockSize.depth; + + srcOffset = offset.x * blockSize + offset.y * SrcRowPitch + offset.z * SrcDepthPitch; + } + + pSrcData = reinterpret_cast(pSrcData) + srcOffset; + } + UpdateResource(pDstResource, DstSubresource, pDstBox, pSrcData, SrcRowPitch, SrcDepthPitch, 0); } From 24d4c9c93872061a91b175df062b5ed272502b94 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Thu, 22 Feb 2024 15:38:28 +0100 Subject: [PATCH 069/246] [util] Disable command lists for Granblue Fantasy Relink The uses deferred contexts for rendering if driver command lists are enabled, but when AMDAGS is loaded, it will also unconditionally use MultiDrawIndirect functions. Since the AGS version in use does not support deferred contexts, this breaks rendering, so we will have to force it into the immediate context path. Testing also shows slightly higher performance (~3-5%) with this path in CPU-bound scenarios. --- src/util/config/config.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index 60f1d7ca4..efa9f4433 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -431,9 +431,12 @@ namespace dxvk { { "d3d11.cachedDynamicResources", "v" }, }} }, /* Granblue Relink: Spams pixel shader UAVs * - * like crazy, much like God of War */ + * and assumes that AMD GPUs do not expose * + * native command lists for AGS usage */ { R"(\\granblue_fantasy_relink\.exe$)", {{ - { "d3d11.ignoreGraphicsBarriers", "True" }, + { "d3d11.ignoreGraphicsBarriers", "True" }, + { "d3d11.exposeDriverCommandLists", "False" }, + { "dxgi.hideNvidiaGpu", "False" }, }} }, /**********************************************/ From 9004c132ed6fcbcfdb0f8b2c40f49d28b41fcb1c Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Fri, 23 Feb 2024 13:39:29 +0100 Subject: [PATCH 070/246] [dxbc] Declare dynamically indexed UBOs with the maximum possible size Fixes #3861. --- src/dxbc/dxbc_compiler.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/dxbc/dxbc_compiler.cpp b/src/dxbc/dxbc_compiler.cpp index 4b4d71cf2..1db04f524 100644 --- a/src/dxbc/dxbc_compiler.cpp +++ b/src/dxbc/dxbc_compiler.cpp @@ -809,8 +809,13 @@ namespace dxvk { // dcl_constant_buffer has one operand with two indices: // (0) Constant buffer register ID (cb#) // (1) Number of constants in the buffer - const uint32_t bufferId = ins.dst[0].idx[0].offset; - const uint32_t elementCount = ins.dst[0].idx[1].offset; + uint32_t bufferId = ins.dst[0].idx[0].offset; + uint32_t elementCount = ins.dst[0].idx[1].offset; + + // With dynamic indexing, games will often index constant buffers + // out of bounds. Declare an upper bound to stay within spec. + if (ins.controls.accessType() == DxbcConstantBufferAccessType::DynamicallyIndexed) + elementCount = 4096; this->emitDclConstantBufferVar(bufferId, elementCount, str::format("cb", bufferId).c_str()); From 6b3b934471127d5b8ec1517d0893e4df12d4e4a1 Mon Sep 17 00:00:00 2001 From: Blisto91 <47954800+Blisto91@users.noreply.github.com> Date: Wed, 28 Feb 2024 14:07:36 +0100 Subject: [PATCH 071/246] [util] Clarify maxDeviceMemory and maxSharedMemory This config often leads to confusion as people expect applications to honor the limit. --- dxvk.conf | 1 + 1 file changed, 1 insertion(+) diff --git a/dxvk.conf b/dxvk.conf index 110d2d55b..d561f658b 100644 --- a/dxvk.conf +++ b/dxvk.conf @@ -90,6 +90,7 @@ # Override maximum amount of device memory and shared system memory # reported to the application. This may fix texture streaming issues # in games that do not support cards with large amounts of VRAM. +# This is not a hard cap and applications can choose to ignore it. # # Supported values: Any number in Megabytes. From d5c3011f541b709ba9f5cda73ed26a692e034756 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Sat, 2 Mar 2024 16:47:58 +0100 Subject: [PATCH 072/246] [include] Update Vulkan headers --- include/vulkan | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/vulkan b/include/vulkan index 85c2334e9..46dc0f6e5 160000 --- a/include/vulkan +++ b/include/vulkan @@ -1 +1 @@ -Subproject commit 85c2334e92e215cce34e8e0ed8b2dce4700f4a50 +Subproject commit 46dc0f6e514f5730784bb2cac2a7c731636839e8 From f07e5f9eaa14ac3c0b6322138ddadbc8dee90ca6 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Fri, 16 Feb 2024 13:48:40 +0100 Subject: [PATCH 073/246] [include] Update SPIR-V headers --- include/spirv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/spirv b/include/spirv index 0bcc62492..8b246ff75 160000 --- a/include/spirv +++ b/include/spirv @@ -1 +1 @@ -Subproject commit 0bcc624926a25a2a273d07877fd25a6ff5ba1cfb +Subproject commit 8b246ff75c6615ba4532fe4fde20f1be090c3764 From 77c7396ee12edf70d341971f583b494dd10df197 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Fri, 16 Feb 2024 13:54:36 +0100 Subject: [PATCH 074/246] [spirv] Add support for OpRawAccessChainNV --- src/spirv/spirv_module.cpp | 24 ++++++++++++++++++++++++ src/spirv/spirv_module.h | 8 ++++++++ 2 files changed, 32 insertions(+) diff --git a/src/spirv/spirv_module.cpp b/src/spirv/spirv_module.cpp index 09240f290..9c141007d 100644 --- a/src/spirv/spirv_module.cpp +++ b/src/spirv/spirv_module.cpp @@ -2697,6 +2697,30 @@ namespace dxvk { } + uint32_t SpirvModule::opRawAccessChain( + uint32_t resultType, + uint32_t base, + uint32_t stride, + uint32_t index, + uint32_t offset, + uint32_t operand) { + uint32_t resultId = this->allocateId(); + + m_code.putIns (spv::OpRawAccessChainNV, operand ? 8 : 7); + m_code.putWord(resultType); + m_code.putWord(resultId); + m_code.putWord(base); + m_code.putWord(stride); + m_code.putWord(index); + m_code.putWord(offset); + + if (operand) + m_code.putWord(operand); + + return resultId; + } + + uint32_t SpirvModule::opReflect( uint32_t resultType, uint32_t incident, diff --git a/src/spirv/spirv_module.h b/src/spirv/spirv_module.h index a878e2e11..2845909df 100644 --- a/src/spirv/spirv_module.h +++ b/src/spirv/spirv_module.h @@ -951,6 +951,14 @@ namespace dxvk { uint32_t resultType, uint32_t operand); + uint32_t opRawAccessChain( + uint32_t resultType, + uint32_t base, + uint32_t stride, + uint32_t index, + uint32_t offset, + uint32_t operand); + uint32_t opReflect( uint32_t resultType, uint32_t incident, From c677ba9b3ed3f1b264a81a199fab455ee4f13667 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Sat, 2 Mar 2024 16:45:48 +0100 Subject: [PATCH 075/246] [dxvk] Enable VK_NV_raw_access_chains if available --- src/dxvk/dxvk_adapter.cpp | 21 +++++++++++++++++++++ src/dxvk/dxvk_device_info.h | 1 + src/dxvk/dxvk_extensions.h | 1 + 3 files changed, 23 insertions(+) diff --git a/src/dxvk/dxvk_adapter.cpp b/src/dxvk/dxvk_adapter.cpp index cf4c3cce6..7c0e79f5d 100644 --- a/src/dxvk/dxvk_adapter.cpp +++ b/src/dxvk/dxvk_adapter.cpp @@ -415,6 +415,10 @@ namespace dxvk { enabledFeatures.khrPresentWait.presentWait = VK_FALSE; } + // Enable raw access chains for shader backends + enabledFeatures.nvRawAccessChains.shaderRawAccessChains = + m_deviceFeatures.nvRawAccessChains.shaderRawAccessChains; + // Create pNext chain for additional device features initFeatureChain(enabledFeatures, devExtensions, instance->extensions()); @@ -606,6 +610,10 @@ namespace dxvk { enabledFeatures.khrPresentWait = *reinterpret_cast(f); break; + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAW_ACCESS_CHAINS_FEATURES_NV: + enabledFeatures.nvRawAccessChains = *reinterpret_cast(f); + break; + default: // Ignore any unknown feature structs break; @@ -927,6 +935,11 @@ namespace dxvk { m_deviceFeatures.khrPresentWait.pNext = std::exchange(m_deviceFeatures.core.pNext, &m_deviceFeatures.khrPresentWait); } + if (m_deviceExtensions.supports(VK_NV_RAW_ACCESS_CHAINS_EXTENSION_NAME)) { + m_deviceFeatures.nvRawAccessChains.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAW_ACCESS_CHAINS_FEATURES_NV; + m_deviceFeatures.nvRawAccessChains.pNext = std::exchange(m_deviceFeatures.core.pNext, &m_deviceFeatures.nvRawAccessChains); + } + if (m_deviceExtensions.supports(VK_NVX_BINARY_IMPORT_EXTENSION_NAME)) m_deviceFeatures.nvxBinaryImport = VK_TRUE; @@ -994,6 +1007,7 @@ namespace dxvk { &devExtensions.khrPresentWait, &devExtensions.khrSwapchain, &devExtensions.khrWin32KeyedMutex, + &devExtensions.nvRawAccessChains, &devExtensions.nvxBinaryImport, &devExtensions.nvxImageViewHandle, }}; @@ -1133,6 +1147,11 @@ namespace dxvk { enabledFeatures.khrPresentWait.pNext = std::exchange(enabledFeatures.core.pNext, &enabledFeatures.khrPresentWait); } + if (devExtensions.nvRawAccessChains) { + enabledFeatures.nvRawAccessChains.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAW_ACCESS_CHAINS_FEATURES_NV; + enabledFeatures.nvRawAccessChains.pNext = std::exchange(enabledFeatures.core.pNext, &enabledFeatures.nvRawAccessChains); + } + if (devExtensions.nvxBinaryImport) enabledFeatures.nvxBinaryImport = VK_TRUE; @@ -1279,6 +1298,8 @@ namespace dxvk { "\n presentId : ", features.khrPresentId.presentId ? "1" : "0", "\n", VK_KHR_PRESENT_WAIT_EXTENSION_NAME, "\n presentWait : ", features.khrPresentWait.presentWait ? "1" : "0", + "\n", VK_NV_RAW_ACCESS_CHAINS_EXTENSION_NAME, + "\n shaderRawAccessChains : ", features.nvRawAccessChains.shaderRawAccessChains ? "1" : "0", "\n", VK_NVX_BINARY_IMPORT_EXTENSION_NAME, "\n extension supported : ", features.nvxBinaryImport ? "1" : "0", "\n", VK_NVX_IMAGE_VIEW_HANDLE_EXTENSION_NAME, diff --git a/src/dxvk/dxvk_device_info.h b/src/dxvk/dxvk_device_info.h index e23a0e181..c1da783a7 100644 --- a/src/dxvk/dxvk_device_info.h +++ b/src/dxvk/dxvk_device_info.h @@ -68,6 +68,7 @@ namespace dxvk { VkPhysicalDeviceMaintenance5FeaturesKHR khrMaintenance5; VkPhysicalDevicePresentIdFeaturesKHR khrPresentId; VkPhysicalDevicePresentWaitFeaturesKHR khrPresentWait; + VkPhysicalDeviceRawAccessChainsFeaturesNV nvRawAccessChains; VkBool32 nvxBinaryImport; VkBool32 nvxImageViewHandle; VkBool32 khrWin32KeyedMutex; diff --git a/src/dxvk/dxvk_extensions.h b/src/dxvk/dxvk_extensions.h index ae4c8a74f..63c922cf5 100644 --- a/src/dxvk/dxvk_extensions.h +++ b/src/dxvk/dxvk_extensions.h @@ -325,6 +325,7 @@ namespace dxvk { DxvkExt khrPresentWait = { VK_KHR_PRESENT_WAIT_EXTENSION_NAME, DxvkExtMode::Optional }; DxvkExt khrSwapchain = { VK_KHR_SWAPCHAIN_EXTENSION_NAME, DxvkExtMode::Required }; DxvkExt khrWin32KeyedMutex = { VK_KHR_WIN32_KEYED_MUTEX_EXTENSION_NAME, DxvkExtMode::Optional }; + DxvkExt nvRawAccessChains = { VK_NV_RAW_ACCESS_CHAINS_EXTENSION_NAME, DxvkExtMode::Optional }; DxvkExt nvxBinaryImport = { VK_NVX_BINARY_IMPORT_EXTENSION_NAME, DxvkExtMode::Disabled }; DxvkExt nvxImageViewHandle = { VK_NVX_IMAGE_VIEW_HANDLE_EXTENSION_NAME, DxvkExtMode::Disabled }; }; From 94098aa97d86e0db101009391c97eddab5c56f76 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Fri, 16 Feb 2024 19:47:35 +0100 Subject: [PATCH 076/246] [dxbc] Enable SPV_NV_raw_access_chains --- src/dxbc/dxbc_compiler.cpp | 8 ++++++++ src/dxbc/dxbc_compiler.h | 1 + src/dxbc/dxbc_options.cpp | 1 + src/dxbc/dxbc_options.h | 3 +++ 4 files changed, 13 insertions(+) diff --git a/src/dxbc/dxbc_compiler.cpp b/src/dxbc/dxbc_compiler.cpp index 1db04f524..31a0b0a9d 100644 --- a/src/dxbc/dxbc_compiler.cpp +++ b/src/dxbc/dxbc_compiler.cpp @@ -1230,6 +1230,14 @@ namespace dxvk { } m_bindings.push_back(binding); + + // If supported, we'll be using raw access chains to access this + if (!m_hasRawAccessChains && m_moduleInfo.options.supportsRawAccessChains) { + m_module.enableExtension("SPV_NV_raw_access_chains"); + m_module.enableCapability(spv::CapabilityRawAccessChainsNV); + + m_hasRawAccessChains = true; + } } diff --git a/src/dxbc/dxbc_compiler.h b/src/dxbc/dxbc_compiler.h index 7b57358cd..d7fba34fe 100644 --- a/src/dxbc/dxbc_compiler.h +++ b/src/dxbc/dxbc_compiler.h @@ -512,6 +512,7 @@ namespace dxvk { // Entry point description - we'll need to declare // the function ID and all input/output variables. uint32_t m_entryPointId = 0; + bool m_hasRawAccessChains = false; //////////////////////////////////////////// // Inter-stage shader interface slots. Also diff --git a/src/dxbc/dxbc_options.cpp b/src/dxbc/dxbc_options.cpp index e642a0dfd..3cdf2833e 100644 --- a/src/dxbc/dxbc_options.cpp +++ b/src/dxbc/dxbc_options.cpp @@ -27,6 +27,7 @@ namespace dxvk { & device->getFormatFeatures(VK_FORMAT_R32_SINT).optimal; supportsTypedUavLoadR32 = (r32Features & VK_FORMAT_FEATURE_2_STORAGE_READ_WITHOUT_FORMAT_BIT); + supportsRawAccessChains = device->features().nvRawAccessChains.shaderRawAccessChains; switch (device->config().useRawSsbo) { case Tristate::Auto: minSsboAlignment = devInfo.core.properties.limits.minStorageBufferOffsetAlignment; break; diff --git a/src/dxbc/dxbc_options.h b/src/dxbc/dxbc_options.h index 2612a024f..6ea5eccb2 100644 --- a/src/dxbc/dxbc_options.h +++ b/src/dxbc/dxbc_options.h @@ -27,6 +27,9 @@ namespace dxvk { /// on typed UAV loads are required bool supportsTypedUavLoadR32 = false; + /// Determines whether raw access chains are supported + bool supportsRawAccessChains = false; + /// Use subgroup operations to reduce the number of /// atomic operations for append/consume buffers. bool useSubgroupOpsForAtomicCounters = false; From 69d74a46a0f3e9c9b77a93636c0a33a85078d25e Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Fri, 16 Feb 2024 19:49:38 +0100 Subject: [PATCH 077/246] [dxbc] Remove emitRawBuffer{Load,Store} And factor these into the callers. We need to preserve the raw index and offset parameters to use raw access chains. --- src/dxbc/dxbc_compiler.cpp | 439 +++++++++++++++++-------------------- src/dxbc/dxbc_compiler.h | 13 -- 2 files changed, 206 insertions(+), 246 deletions(-) diff --git a/src/dxbc/dxbc_compiler.cpp b/src/dxbc/dxbc_compiler.cpp index 31a0b0a9d..26d0d66e7 100644 --- a/src/dxbc/dxbc_compiler.cpp +++ b/src/dxbc/dxbc_compiler.cpp @@ -2824,11 +2824,55 @@ namespace dxvk { // the data depends on the register type. const DxbcRegister& dstReg = ins.dst[0]; const DxbcRegister& srcReg = isStructured ? ins.src[2] : ins.src[1]; - + // Retrieve common info about the buffer const DxbcBufferInfo bufferInfo = getBufferInfo(srcReg); - - // Compute element index + + // Shared memory is the only type of buffer that + // is not accessed through a texel buffer view + bool isTgsm = srcReg.type == DxbcOperandType::ThreadGroupSharedMemory; + bool isSsbo = bufferInfo.isSsbo; + + // Common types and IDs used while loading the data + uint32_t bufferId = isTgsm || isSsbo ? 0 : m_module.opLoad(bufferInfo.typeId, bufferInfo.varId); + + uint32_t vectorTypeId = getVectorTypeId({ DxbcScalarType::Uint32, 4 }); + uint32_t scalarTypeId = getVectorTypeId({ DxbcScalarType::Uint32, 1 }); + + // Since all data is represented as a sequence of 32-bit + // integers, we have to load each component individually. + std::array ccomps = { 0, 0, 0, 0 }; + std::array scomps = { 0, 0, 0, 0 }; + uint32_t scount = 0; + + // The sparse feedback ID will be non-zero for sparse + // instructions on input. We need to reset it to 0. + SpirvMemoryOperands memoryOperands; + SpirvImageOperands imageOperands; + imageOperands.sparse = ins.dstCount == 2; + + uint32_t coherence = bufferInfo.coherence; + + if (isTgsm && m_moduleInfo.options.forceVolatileTgsmAccess) { + memoryOperands.flags |= spv::MemoryAccessVolatileMask; + coherence = spv::ScopeWorkgroup; + } + + if (coherence) { + memoryOperands.flags |= spv::MemoryAccessNonPrivatePointerMask; + + if (coherence != spv::ScopeInvocation) { + memoryOperands.flags |= spv::MemoryAccessMakePointerVisibleMask; + memoryOperands.makeVisible = m_module.constu32(coherence); + + imageOperands.flags = spv::ImageOperandsNonPrivateTexelMask + | spv::ImageOperandsMakeTexelVisibleMask; + imageOperands.makeVisible = m_module.constu32(coherence); + } + } + + uint32_t sparseFeedbackId = 0; + const DxbcRegisterValue elementIndex = isStructured ? emitCalcBufferIndexStructured( emitRegisterLoad(ins.src[0], DxbcRegMask(true, false, false, false)), @@ -2837,10 +2881,81 @@ namespace dxvk { : emitCalcBufferIndexRaw( emitRegisterLoad(ins.src[0], DxbcRegMask(true, false, false, false))); - uint32_t sparseFeedbackId = uint32_t(ins.dstCount == 2); + for (uint32_t i = 0; i < 4; i++) { + uint32_t sindex = srcReg.swizzle[i]; - emitRegisterStore(dstReg, emitRawBufferLoad(srcReg, - elementIndex, dstReg.mask, sparseFeedbackId)); + if (!dstReg.mask[i]) + continue; + + if (ccomps[sindex] == 0) { + uint32_t elementIndexAdjusted = m_module.opIAdd( + getVectorTypeId(elementIndex.type), elementIndex.id, + m_module.consti32(sindex)); + + // Load requested component from the buffer + uint32_t zero = 0; + + if (isTgsm) { + ccomps[sindex] = m_module.opLoad(scalarTypeId, + m_module.opAccessChain(bufferInfo.typeId, + bufferInfo.varId, 1, &elementIndexAdjusted), + memoryOperands); + } else if (isSsbo) { + uint32_t indices[2] = { m_module.constu32(0), elementIndexAdjusted }; + ccomps[sindex] = m_module.opLoad(scalarTypeId, + m_module.opAccessChain(bufferInfo.typeId, + bufferInfo.varId, 2, indices), + memoryOperands); + } else { + uint32_t resultTypeId = vectorTypeId; + uint32_t resultId = 0; + + if (imageOperands.sparse) + resultTypeId = getSparseResultTypeId(vectorTypeId); + + if (srcReg.type == DxbcOperandType::Resource) { + resultId = m_module.opImageFetch(resultTypeId, + bufferId, elementIndexAdjusted, imageOperands); + } else if (srcReg.type == DxbcOperandType::UnorderedAccessView) { + resultId = m_module.opImageRead(resultTypeId, + bufferId, elementIndexAdjusted, imageOperands); + } else { + throw DxvkError("DxbcCompiler: Invalid operand type for strucured/raw load"); + } + + // Only read sparse feedback once. This may be somewhat inaccurate + // for reads that straddle pages, but we can't easily emulate this. + if (imageOperands.sparse) { + imageOperands.sparse = false; + sparseFeedbackId = resultId; + + resultId = emitExtractSparseTexel(vectorTypeId, resultId); + } + + ccomps[sindex] = m_module.opCompositeExtract(scalarTypeId, resultId, 1, &zero); + } + } + } + + for (uint32_t i = 0; i < 4; i++) { + uint32_t sindex = srcReg.swizzle[i]; + + if (dstReg.mask[i]) + scomps[scount++] = ccomps[sindex]; + } + + DxbcRegisterValue result = { }; + result.type.ctype = DxbcScalarType::Uint32; + result.type.ccount = scount; + result.id = scomps[0]; + + if (scount > 1) { + result.id = m_module.opCompositeConstruct( + getVectorTypeId(result.type), + scount, scomps.data()); + } + + emitRegisterStore(dstReg, result); if (sparseFeedbackId) emitStoreSparseFeedback(ins.dst[1], sparseFeedbackId); @@ -2863,11 +2978,49 @@ namespace dxvk { // the data depends on the register type. const DxbcRegister& dstReg = ins.dst[0]; const DxbcRegister& srcReg = isStructured ? ins.src[2] : ins.src[1]; - + + DxbcRegisterValue value = emitRegisterLoad(srcReg, dstReg.mask); + value = emitRegisterBitcast(value, DxbcScalarType::Uint32); + // Retrieve common info about the buffer const DxbcBufferInfo bufferInfo = getBufferInfo(dstReg); - - // Compute element index + + // Thread Group Shared Memory is not accessed through a texel buffer view + bool isTgsm = dstReg.type == DxbcOperandType::ThreadGroupSharedMemory; + bool isSsbo = bufferInfo.isSsbo; + + uint32_t bufferId = isTgsm || isSsbo ? 0 : m_module.opLoad(bufferInfo.typeId, bufferInfo.varId); + + uint32_t scalarTypeId = getVectorTypeId({ DxbcScalarType::Uint32, 1 }); + uint32_t vectorTypeId = getVectorTypeId({ DxbcScalarType::Uint32, 4 }); + + uint32_t srcComponentIndex = 0; + + // Set memory operands according to resource properties + SpirvMemoryOperands memoryOperands; + SpirvImageOperands imageOperands; + + uint32_t coherence = bufferInfo.coherence; + + if (isTgsm && m_moduleInfo.options.forceVolatileTgsmAccess) { + memoryOperands.flags |= spv::MemoryAccessVolatileMask; + coherence = spv::ScopeWorkgroup; + } + + if (coherence) { + memoryOperands.flags |= spv::MemoryAccessNonPrivatePointerMask; + + if (coherence != spv::ScopeInvocation) { + memoryOperands.flags |= spv::MemoryAccessMakePointerAvailableMask; + memoryOperands.makeAvailable = m_module.constu32(coherence); + + imageOperands.flags = spv::ImageOperandsNonPrivateTexelMask + | spv::ImageOperandsMakeTexelAvailableMask; + imageOperands.makeAvailable = m_module.constu32(coherence); + } + } + + // Compute flat element index const DxbcRegisterValue elementIndex = isStructured ? emitCalcBufferIndexStructured( emitRegisterLoad(ins.src[0], DxbcRegMask(true, false, false, false)), @@ -2875,9 +3028,50 @@ namespace dxvk { bufferInfo.stride) : emitCalcBufferIndexRaw( emitRegisterLoad(ins.src[0], DxbcRegMask(true, false, false, false))); - - emitRawBufferStore(dstReg, elementIndex, - emitRegisterLoad(srcReg, dstReg.mask)); + + for (uint32_t i = 0; i < 4; i++) { + if (dstReg.mask[i]) { + uint32_t srcComponentId = value.type.ccount > 1 + ? m_module.opCompositeExtract(scalarTypeId, + value.id, 1, &srcComponentIndex) + : value.id; + + // Add the component offset to the element index + uint32_t elementIndexAdjusted = i != 0 + ? m_module.opIAdd(getVectorTypeId(elementIndex.type), + elementIndex.id, m_module.consti32(i)) + : elementIndex.id; + + if (isTgsm) { + m_module.opStore( + m_module.opAccessChain(bufferInfo.typeId, + bufferInfo.varId, 1, &elementIndexAdjusted), + srcComponentId, memoryOperands); + } else if (isSsbo) { + uint32_t indices[2] = { m_module.constu32(0), elementIndexAdjusted }; + m_module.opStore( + m_module.opAccessChain(bufferInfo.typeId, + bufferInfo.varId, 2, indices), + srcComponentId, memoryOperands); + } else if (dstReg.type == DxbcOperandType::UnorderedAccessView) { + const std::array srcVectorIds = { + srcComponentId, srcComponentId, + srcComponentId, srcComponentId, + }; + + m_module.opImageWrite( + bufferId, elementIndexAdjusted, + m_module.opCompositeConstruct(vectorTypeId, + 4, srcVectorIds.data()), + imageOperands); + } else { + throw DxvkError("DxbcCompiler: Invalid operand type for strucured/raw store"); + } + + // Write next component + srcComponentIndex += 1; + } + } } @@ -5205,227 +5399,6 @@ namespace dxvk { } - DxbcRegisterValue DxbcCompiler::emitRawBufferLoad( - const DxbcRegister& operand, - DxbcRegisterValue elementIndex, - DxbcRegMask writeMask, - uint32_t& sparseFeedbackId) { - const DxbcBufferInfo bufferInfo = getBufferInfo(operand); - - // Shared memory is the only type of buffer that - // is not accessed through a texel buffer view - bool isTgsm = operand.type == DxbcOperandType::ThreadGroupSharedMemory; - bool isSsbo = bufferInfo.isSsbo; - - // Common types and IDs used while loading the data - uint32_t bufferId = isTgsm || isSsbo ? 0 : m_module.opLoad(bufferInfo.typeId, bufferInfo.varId); - - uint32_t vectorTypeId = getVectorTypeId({ DxbcScalarType::Uint32, 4 }); - uint32_t scalarTypeId = getVectorTypeId({ DxbcScalarType::Uint32, 1 }); - - // Since all data is represented as a sequence of 32-bit - // integers, we have to load each component individually. - std::array ccomps = { 0, 0, 0, 0 }; - std::array scomps = { 0, 0, 0, 0 }; - uint32_t scount = 0; - - // The sparse feedback ID will be non-zero for sparse - // instructions on input. We need to reset it to 0. - SpirvMemoryOperands memoryOperands; - SpirvImageOperands imageOperands; - imageOperands.sparse = sparseFeedbackId != 0; - - uint32_t coherence = bufferInfo.coherence; - - if (isTgsm && m_moduleInfo.options.forceVolatileTgsmAccess) { - memoryOperands.flags |= spv::MemoryAccessVolatileMask; - coherence = spv::ScopeWorkgroup; - } - - if (coherence) { - memoryOperands.flags |= spv::MemoryAccessNonPrivatePointerMask; - - if (coherence != spv::ScopeInvocation) { - memoryOperands.flags |= spv::MemoryAccessMakePointerVisibleMask; - memoryOperands.makeVisible = m_module.constu32(coherence); - - imageOperands.flags = spv::ImageOperandsNonPrivateTexelMask - | spv::ImageOperandsMakeTexelVisibleMask; - imageOperands.makeVisible = m_module.constu32(coherence); - } - } - - sparseFeedbackId = 0; - - for (uint32_t i = 0; i < 4; i++) { - uint32_t sindex = operand.swizzle[i]; - - if (!writeMask[i]) - continue; - - if (ccomps[sindex] == 0) { - uint32_t elementIndexAdjusted = m_module.opIAdd( - getVectorTypeId(elementIndex.type), elementIndex.id, - m_module.consti32(sindex)); - - // Load requested component from the buffer - uint32_t zero = 0; - - if (isTgsm) { - ccomps[sindex] = m_module.opLoad(scalarTypeId, - m_module.opAccessChain(bufferInfo.typeId, - bufferInfo.varId, 1, &elementIndexAdjusted), - memoryOperands); - } else if (isSsbo) { - uint32_t indices[2] = { m_module.constu32(0), elementIndexAdjusted }; - ccomps[sindex] = m_module.opLoad(scalarTypeId, - m_module.opAccessChain(bufferInfo.typeId, - bufferInfo.varId, 2, indices), - memoryOperands); - } else { - uint32_t resultTypeId = vectorTypeId; - uint32_t resultId = 0; - - if (imageOperands.sparse) - resultTypeId = getSparseResultTypeId(vectorTypeId); - - if (operand.type == DxbcOperandType::Resource) { - resultId = m_module.opImageFetch(resultTypeId, - bufferId, elementIndexAdjusted, imageOperands); - } else if (operand.type == DxbcOperandType::UnorderedAccessView) { - resultId = m_module.opImageRead(resultTypeId, - bufferId, elementIndexAdjusted, imageOperands); - } else { - throw DxvkError("DxbcCompiler: Invalid operand type for strucured/raw load"); - } - - // Only read sparse feedback once. This may be somewhat inaccurate - // for reads that straddle pages, but we can't easily emulate this. - if (imageOperands.sparse) { - imageOperands.sparse = false; - sparseFeedbackId = resultId; - - resultId = emitExtractSparseTexel(vectorTypeId, resultId); - } - - ccomps[sindex] = m_module.opCompositeExtract(scalarTypeId, resultId, 1, &zero); - } - } - } - - for (uint32_t i = 0; i < 4; i++) { - uint32_t sindex = operand.swizzle[i]; - - if (writeMask[i]) - scomps[scount++] = ccomps[sindex]; - } - - DxbcRegisterValue result; - result.type.ctype = DxbcScalarType::Uint32; - result.type.ccount = scount; - result.id = scomps[0]; - - if (scount > 1) { - result.id = m_module.opCompositeConstruct( - getVectorTypeId(result.type), - scount, scomps.data()); - } - - return result; - } - - - void DxbcCompiler::emitRawBufferStore( - const DxbcRegister& operand, - DxbcRegisterValue elementIndex, - DxbcRegisterValue value) { - const DxbcBufferInfo bufferInfo = getBufferInfo(operand); - - // Cast source value to the expected data type - value = emitRegisterBitcast(value, DxbcScalarType::Uint32); - - // Thread Group Shared Memory is not accessed through a texel buffer view - bool isTgsm = operand.type == DxbcOperandType::ThreadGroupSharedMemory; - bool isSsbo = bufferInfo.isSsbo; - - // Perform the actual write operation - uint32_t bufferId = isTgsm || isSsbo ? 0 : m_module.opLoad(bufferInfo.typeId, bufferInfo.varId); - - uint32_t scalarTypeId = getVectorTypeId({ DxbcScalarType::Uint32, 1 }); - uint32_t vectorTypeId = getVectorTypeId({ DxbcScalarType::Uint32, 4 }); - - uint32_t srcComponentIndex = 0; - - // Set memory operands according to resource properties - SpirvMemoryOperands memoryOperands; - SpirvImageOperands imageOperands; - - uint32_t coherence = bufferInfo.coherence; - - if (isTgsm && m_moduleInfo.options.forceVolatileTgsmAccess) { - memoryOperands.flags |= spv::MemoryAccessVolatileMask; - coherence = spv::ScopeWorkgroup; - } - - if (coherence) { - memoryOperands.flags |= spv::MemoryAccessNonPrivatePointerMask; - - if (coherence != spv::ScopeInvocation) { - memoryOperands.flags |= spv::MemoryAccessMakePointerAvailableMask; - memoryOperands.makeAvailable = m_module.constu32(coherence); - - imageOperands.flags = spv::ImageOperandsNonPrivateTexelMask - | spv::ImageOperandsMakeTexelAvailableMask; - imageOperands.makeAvailable = m_module.constu32(coherence); - } - } - - for (uint32_t i = 0; i < 4; i++) { - if (operand.mask[i]) { - uint32_t srcComponentId = value.type.ccount > 1 - ? m_module.opCompositeExtract(scalarTypeId, - value.id, 1, &srcComponentIndex) - : value.id; - - // Add the component offset to the element index - uint32_t elementIndexAdjusted = i != 0 - ? m_module.opIAdd(getVectorTypeId(elementIndex.type), - elementIndex.id, m_module.consti32(i)) - : elementIndex.id; - - if (isTgsm) { - m_module.opStore( - m_module.opAccessChain(bufferInfo.typeId, - bufferInfo.varId, 1, &elementIndexAdjusted), - srcComponentId, memoryOperands); - } else if (isSsbo) { - uint32_t indices[2] = { m_module.constu32(0), elementIndexAdjusted }; - m_module.opStore( - m_module.opAccessChain(bufferInfo.typeId, - bufferInfo.varId, 2, indices), - srcComponentId, memoryOperands); - } else if (operand.type == DxbcOperandType::UnorderedAccessView) { - const std::array srcVectorIds = { - srcComponentId, srcComponentId, - srcComponentId, srcComponentId, - }; - - m_module.opImageWrite( - bufferId, elementIndexAdjusted, - m_module.opCompositeConstruct(vectorTypeId, - 4, srcVectorIds.data()), - imageOperands); - } else { - throw DxvkError("DxbcCompiler: Invalid operand type for strucured/raw store"); - } - - // Write next component - srcComponentIndex += 1; - } - } - } - - DxbcRegisterValue DxbcCompiler::emitQueryBufferSize( const DxbcRegister& resource) { const DxbcBufferInfo bufferInfo = getBufferInfo(resource); diff --git a/src/dxbc/dxbc_compiler.h b/src/dxbc/dxbc_compiler.h index d7fba34fe..d23bc59f6 100644 --- a/src/dxbc/dxbc_compiler.h +++ b/src/dxbc/dxbc_compiler.h @@ -945,19 +945,6 @@ namespace dxvk { const DxbcRegister& operand, const DxbcRegister& address); - /////////////////////////////// - // Resource load/store methods - DxbcRegisterValue emitRawBufferLoad( - const DxbcRegister& operand, - DxbcRegisterValue elementIndex, - DxbcRegMask writeMask, - uint32_t& sparseFeedbackId); - - void emitRawBufferStore( - const DxbcRegister& operand, - DxbcRegisterValue elementIndex, - DxbcRegisterValue value); - ////////////////////////// // Resource query methods DxbcRegisterValue emitQueryBufferSize( From c9cea93b7bae32ce2f9781117196675b08bc0c96 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Fri, 16 Feb 2024 21:50:43 +0100 Subject: [PATCH 078/246] [dxbc] Use raw access chains for buffer loads and stores Maps more or less perfectly to D3D raw and structured buffers. --- src/dxbc/dxbc_compiler.cpp | 189 ++++++++++++++++++++++++++++++------- src/dxbc/dxbc_decoder.h | 4 + 2 files changed, 161 insertions(+), 32 deletions(-) diff --git a/src/dxbc/dxbc_compiler.cpp b/src/dxbc/dxbc_compiler.cpp index 26d0d66e7..59323b084 100644 --- a/src/dxbc/dxbc_compiler.cpp +++ b/src/dxbc/dxbc_compiler.cpp @@ -2873,28 +2873,90 @@ namespace dxvk { uint32_t sparseFeedbackId = 0; - const DxbcRegisterValue elementIndex = isStructured - ? emitCalcBufferIndexStructured( - emitRegisterLoad(ins.src[0], DxbcRegMask(true, false, false, false)), - emitRegisterLoad(ins.src[1], DxbcRegMask(true, false, false, false)), - bufferInfo.stride) - : emitCalcBufferIndexRaw( - emitRegisterLoad(ins.src[0], DxbcRegMask(true, false, false, false))); + bool useRawAccessChains = m_hasRawAccessChains && isSsbo && !imageOperands.sparse; + + DxbcRegisterValue index = emitRegisterLoad(ins.src[0], DxbcRegMask(true, false, false, false)); + DxbcRegisterValue offset = index; + + if (isStructured) + offset = emitRegisterLoad(ins.src[1], DxbcRegMask(true, false, false, false)); + + DxbcRegisterValue elementIndex = { }; + + uint32_t baseAlignment = sizeof(uint32_t); + + if (useRawAccessChains) { + memoryOperands.flags |= spv::MemoryAccessAlignedMask; + + if (isStructured && ins.src[1].type == DxbcOperandType::Imm32) { + baseAlignment = bufferInfo.stride | ins.src[1].imm.u32_1; + baseAlignment = baseAlignment & -baseAlignment; + baseAlignment = std::min(baseAlignment, uint32_t(m_moduleInfo.options.minSsboAlignment)); + } + } else { + elementIndex = isStructured + ? emitCalcBufferIndexStructured(index, offset, bufferInfo.stride) + : emitCalcBufferIndexRaw(offset); + } + + uint32_t readMask = 0u; for (uint32_t i = 0; i < 4; i++) { - uint32_t sindex = srcReg.swizzle[i]; + if (dstReg.mask[i]) + readMask |= 1u << srcReg.swizzle[i]; + } - if (!dstReg.mask[i]) - continue; + while (readMask) { + uint32_t sindex = bit::tzcnt(readMask); + uint32_t scount = bit::tzcnt(~(readMask >> sindex)); + uint32_t zero = 0; - if (ccomps[sindex] == 0) { + if (useRawAccessChains) { + uint32_t alignment = baseAlignment; + uint32_t offsetId = offset.id; + + if (sindex) { + offsetId = m_module.opIAdd(scalarTypeId, + offsetId, m_module.constu32(sizeof(uint32_t) * sindex)); + alignment |= sizeof(uint32_t) * sindex; + } + + DxbcRegisterInfo storeInfo; + storeInfo.type.ctype = DxbcScalarType::Uint32; + storeInfo.type.ccount = scount; + storeInfo.type.alength = 0; + storeInfo.sclass = spv::StorageClassStorageBuffer; + + uint32_t loadTypeId = getArrayTypeId(storeInfo.type); + uint32_t ptrTypeId = getPointerTypeId(storeInfo); + + uint32_t accessChain = isStructured + ? m_module.opRawAccessChain(ptrTypeId, bufferInfo.varId, + m_module.constu32(bufferInfo.stride), index.id, offsetId, + spv::RawAccessChainOperandsRobustnessPerElementNVMask) + : m_module.opRawAccessChain(ptrTypeId, bufferInfo.varId, + m_module.constu32(0), m_module.constu32(0), offsetId, + spv::RawAccessChainOperandsRobustnessPerComponentNVMask); + + memoryOperands.alignment = alignment & -alignment; + + uint32_t vectorId = m_module.opLoad(loadTypeId, accessChain, memoryOperands); + + for (uint32_t i = 0; i < scount; i++) { + ccomps[sindex + i] = vectorId; + + if (scount > 1) { + ccomps[sindex + i] = m_module.opCompositeExtract( + scalarTypeId, vectorId, 1, &i); + } + } + + readMask &= ~(((1u << scount) - 1u) << sindex); + } else { uint32_t elementIndexAdjusted = m_module.opIAdd( getVectorTypeId(elementIndex.type), elementIndex.id, m_module.consti32(sindex)); - // Load requested component from the buffer - uint32_t zero = 0; - if (isTgsm) { ccomps[sindex] = m_module.opLoad(scalarTypeId, m_module.opAccessChain(bufferInfo.typeId, @@ -2934,6 +2996,8 @@ namespace dxvk { ccomps[sindex] = m_module.opCompositeExtract(scalarTypeId, resultId, 1, &zero); } + + readMask &= readMask - 1; } } @@ -2994,8 +3058,6 @@ namespace dxvk { uint32_t scalarTypeId = getVectorTypeId({ DxbcScalarType::Uint32, 1 }); uint32_t vectorTypeId = getVectorTypeId({ DxbcScalarType::Uint32, 4 }); - uint32_t srcComponentIndex = 0; - // Set memory operands according to resource properties SpirvMemoryOperands memoryOperands; SpirvImageOperands imageOperands; @@ -3020,26 +3082,90 @@ namespace dxvk { } } - // Compute flat element index - const DxbcRegisterValue elementIndex = isStructured - ? emitCalcBufferIndexStructured( - emitRegisterLoad(ins.src[0], DxbcRegMask(true, false, false, false)), - emitRegisterLoad(ins.src[1], DxbcRegMask(true, false, false, false)), - bufferInfo.stride) - : emitCalcBufferIndexRaw( - emitRegisterLoad(ins.src[0], DxbcRegMask(true, false, false, false))); + // Compute flat element index as necessary + bool useRawAccessChains = isSsbo && m_hasRawAccessChains; - for (uint32_t i = 0; i < 4; i++) { - if (dstReg.mask[i]) { + DxbcRegisterValue index = emitRegisterLoad(ins.src[0], DxbcRegMask(true, false, false, false)); + DxbcRegisterValue offset = index; + + if (isStructured) + offset = emitRegisterLoad(ins.src[1], DxbcRegMask(true, false, false, false)); + + DxbcRegisterValue elementIndex = { }; + + uint32_t baseAlignment = sizeof(uint32_t); + + if (useRawAccessChains) { + memoryOperands.flags |= spv::MemoryAccessAlignedMask; + + if (isStructured && ins.src[1].type == DxbcOperandType::Imm32) { + baseAlignment = bufferInfo.stride | ins.src[1].imm.u32_1; + baseAlignment = baseAlignment & -baseAlignment; + baseAlignment = std::min(baseAlignment, uint32_t(m_moduleInfo.options.minSsboAlignment)); + } + } else { + elementIndex = isStructured + ? emitCalcBufferIndexStructured(index, offset, bufferInfo.stride) + : emitCalcBufferIndexRaw(offset); + } + + uint32_t writeMask = dstReg.mask.raw(); + + while (writeMask) { + uint32_t sindex = bit::tzcnt(writeMask); + uint32_t scount = bit::tzcnt(~(writeMask >> sindex)); + + if (useRawAccessChains) { + uint32_t alignment = baseAlignment; + uint32_t offsetId = offset.id; + + if (sindex) { + offsetId = m_module.opIAdd(scalarTypeId, + offsetId, m_module.constu32(sizeof(uint32_t) * sindex)); + alignment = alignment | (sizeof(uint32_t) * sindex); + } + + DxbcRegisterInfo storeInfo; + storeInfo.type.ctype = DxbcScalarType::Uint32; + storeInfo.type.ccount = scount; + storeInfo.type.alength = 0; + storeInfo.sclass = spv::StorageClassStorageBuffer; + + uint32_t storeTypeId = getArrayTypeId(storeInfo.type); + uint32_t ptrTypeId = getPointerTypeId(storeInfo); + + uint32_t accessChain = isStructured + ? m_module.opRawAccessChain(ptrTypeId, bufferInfo.varId, + m_module.constu32(bufferInfo.stride), index.id, offsetId, + spv::RawAccessChainOperandsRobustnessPerElementNVMask) + : m_module.opRawAccessChain(ptrTypeId, bufferInfo.varId, + m_module.constu32(0), m_module.constu32(0), offsetId, + spv::RawAccessChainOperandsRobustnessPerComponentNVMask); + + uint32_t valueId = value.id; + + if (scount < value.type.ccount) { + if (scount == 1) { + valueId = m_module.opCompositeExtract(storeTypeId, value.id, 1, &sindex); + } else { + std::array indices = { sindex, sindex + 1u, sindex + 2u, sindex + 3u }; + valueId = m_module.opVectorShuffle(storeTypeId, value.id, value.id, scount, indices.data()); + } + } + + memoryOperands.alignment = alignment & -alignment; + m_module.opStore(accessChain, valueId, memoryOperands); + + writeMask &= ~(((1u << scount) - 1u) << sindex); + } else { uint32_t srcComponentId = value.type.ccount > 1 ? m_module.opCompositeExtract(scalarTypeId, - value.id, 1, &srcComponentIndex) + value.id, 1, &sindex) : value.id; - // Add the component offset to the element index - uint32_t elementIndexAdjusted = i != 0 + uint32_t elementIndexAdjusted = sindex != 0 ? m_module.opIAdd(getVectorTypeId(elementIndex.type), - elementIndex.id, m_module.consti32(i)) + elementIndex.id, m_module.consti32(sindex)) : elementIndex.id; if (isTgsm) { @@ -3068,8 +3194,7 @@ namespace dxvk { throw DxvkError("DxbcCompiler: Invalid operand type for strucured/raw store"); } - // Write next component - srcComponentIndex += 1; + writeMask &= writeMask - 1u; } } } diff --git a/src/dxbc/dxbc_decoder.h b/src/dxbc/dxbc_decoder.h index 3fa4c3779..771837152 100644 --- a/src/dxbc/dxbc_decoder.h +++ b/src/dxbc/dxbc_decoder.h @@ -149,6 +149,10 @@ namespace dxvk { : m_mask((x ? 0x1 : 0) | (y ? 0x2 : 0) | (z ? 0x4 : 0) | (w ? 0x8 : 0)) { } + uint32_t raw() const { + return m_mask; + } + bool operator [] (uint32_t id) const { return (m_mask >> id) & 1; } From 49b18f03fe7c6dd29e9aa296c5cd072d3765a1bb Mon Sep 17 00:00:00 2001 From: Robin Kertels Date: Tue, 20 Feb 2024 23:34:26 +0100 Subject: [PATCH 079/246] [d3d9] Unbind buffers in EndScene & Reset --- src/d3d9/d3d9_device.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/d3d9/d3d9_device.cpp b/src/d3d9/d3d9_device.cpp index f11ae4004..2b340c74c 100644 --- a/src/d3d9/d3d9_device.cpp +++ b/src/d3d9/d3d9_device.cpp @@ -481,6 +481,14 @@ namespace dxvk { return hr; } + // Unbind all buffers that were still bound to the backend to avoid leaks. + EmitCs([](DxvkContext* ctx) { + ctx->bindIndexBuffer(DxvkBufferSlice(), VK_INDEX_TYPE_UINT32); + for (uint32_t i = 0; i < DxvkLimits::MaxNumVertexBindings; i++) { + ctx->bindVertexBuffer(i, DxvkBufferSlice(), 0); + } + }); + Flush(); SynchronizeCsThread(DxvkCsThread::SynchronizeAll); @@ -1603,6 +1611,16 @@ namespace dxvk { m_flags.clr(D3D9DeviceFlag::InScene); + // D3D9 resets the internally bound vertex buffers and index buffer in EndScene. + // We have to ignore unbinding those buffers because of Operation Flashpoint Red River, + // so we should also clear the bindings here, to avoid leaks. + EmitCs([](DxvkContext* ctx) { + ctx->bindIndexBuffer(DxvkBufferSlice(), VK_INDEX_TYPE_UINT32); + for (uint32_t i = 0; i < DxvkLimits::MaxNumVertexBindings; i++) { + ctx->bindVertexBuffer(i, DxvkBufferSlice(), 0); + } + }); + return D3D_OK; } From eaa732d0b34374eeb088d3411017aed98423d033 Mon Sep 17 00:00:00 2001 From: Robin Kertels Date: Wed, 7 Feb 2024 00:10:43 +0100 Subject: [PATCH 080/246] [d3d9] Place GetFrontBufferData screenshot at window position --- src/d3d9/d3d9_swapchain.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/d3d9/d3d9_swapchain.cpp b/src/d3d9/d3d9_swapchain.cpp index 1d9c50c14..a19ca55aa 100644 --- a/src/d3d9/d3d9_swapchain.cpp +++ b/src/d3d9/d3d9_swapchain.cpp @@ -382,6 +382,20 @@ namespace dxvk { blitInfo.srcOffsets[0] = VkOffset3D{ 0, 0, 0 }; blitInfo.srcOffsets[1] = VkOffset3D{ int32_t(srcExtent.width), int32_t(srcExtent.height), 1 }; +#ifdef _WIN32 + if (m_presentParams.Windowed) { + // In windowed mode, GetFrontBufferData takes a screenshot of the entire screen. + // So place the copy of the front buffer at the position of the window. + POINT point = { 0, 0 }; + if (ClientToScreen(m_window, &point) != 0) { + blitInfo.dstOffsets[0].x = point.x; + blitInfo.dstOffsets[0].y = point.y; + blitInfo.dstOffsets[1].x += point.x; + blitInfo.dstOffsets[1].y += point.y; + } + } +#endif + m_parent->EmitCs([ cDstImage = blittedSrc, cDstMap = dstTexInfo->GetMapping().Swizzle, From a0e39e94fa47e4fb6c1ad2a1d0e4f5d616d32f67 Mon Sep 17 00:00:00 2001 From: Robin Kertels Date: Wed, 7 Feb 2024 00:12:01 +0100 Subject: [PATCH 081/246] [d3d9] Use most recently used swapchain for GetFrontBufferData --- src/d3d9/d3d9_device.cpp | 8 +++++++- src/d3d9/d3d9_device.h | 30 ++++++++++++++++++++++++++++++ src/d3d9/d3d9_swapchain.cpp | 12 ++++++++++++ 3 files changed, 49 insertions(+), 1 deletion(-) diff --git a/src/d3d9/d3d9_device.cpp b/src/d3d9/d3d9_device.cpp index 2b340c74c..329e9e7b7 100644 --- a/src/d3d9/d3d9_device.cpp +++ b/src/d3d9/d3d9_device.cpp @@ -1093,7 +1093,12 @@ namespace dxvk { if (unlikely(iSwapChain != 0)) return D3DERR_INVALIDCALL; - return m_implicitSwapchain->GetFrontBufferData(pDestSurface); + D3D9DeviceLock lock = LockDevice(); + + // In windowed mode, GetFrontBufferData takes a screenshot of the entire screen. + // We use the last used swapchain as a workaround. + // Total War: Medieval 2 relies on this. + return m_mostRecentlyUsedSwapchain->GetFrontBufferData(pDestSurface); } @@ -7839,6 +7844,7 @@ namespace dxvk { } else m_implicitSwapchain = new D3D9SwapChainEx(this, pPresentationParameters, pFullscreenDisplayMode); + m_mostRecentlyUsedSwapchain = m_implicitSwapchain.ptr(); if (pPresentationParameters->EnableAutoDepthStencil) { D3D9_COMMON_TEXTURE_DESC desc; diff --git a/src/d3d9/d3d9_device.h b/src/d3d9/d3d9_device.h index 2fc74c836..90beb034f 100644 --- a/src/d3d9/d3d9_device.h +++ b/src/d3d9/d3d9_device.h @@ -1241,6 +1241,34 @@ namespace dxvk { uint64_t GetCurrentSequenceNumber(); + /** + * @brief Get the swapchain that was used the most recently for presenting + * Has to be externally synchronized. + * + * @return D3D9SwapChainEx* Swapchain + */ + D3D9SwapChainEx* GetMostRecentlyUsedSwapchain() { + return m_mostRecentlyUsedSwapchain; + } + + /** + * @brief Set the swapchain that was used the most recently for presenting + * Has to be externally synchronized. + * + * @param swapchain Swapchain + */ + void SetMostRecentlyUsedSwapchain(D3D9SwapChainEx* swapchain) { + m_mostRecentlyUsedSwapchain = swapchain; + } + + /** + * @brief Reset the most recently swapchain back to the implicit one + * Has to be externally synchronized. + */ + void ResetMostRecentlyUsedSwapchain() { + m_mostRecentlyUsedSwapchain = m_implicitSwapchain.ptr(); + } + Com m_parent; D3DDEVTYPE m_deviceType; HWND m_window; @@ -1403,6 +1431,8 @@ namespace dxvk { HWND m_fullscreenWindow = NULL; std::atomic m_losableResourceCounter = { 0 }; + D3D9SwapChainEx* m_mostRecentlyUsedSwapchain = nullptr; + #ifdef D3D9_ALLOW_UNMAPPING lru_list m_mappedTextures; #endif diff --git a/src/d3d9/d3d9_swapchain.cpp b/src/d3d9/d3d9_swapchain.cpp index a19ca55aa..577243306 100644 --- a/src/d3d9/d3d9_swapchain.cpp +++ b/src/d3d9/d3d9_swapchain.cpp @@ -65,6 +65,16 @@ namespace dxvk { if (this_thread::isInModuleDetachment()) return; + { + // Locking here and in Device::GetFrontBufferData + // ensures that other threads don't accidentally access a stale pointer. + D3D9DeviceLock lock = m_parent->LockDevice(); + + if (m_parent->GetMostRecentlyUsedSwapchain() == this) { + m_parent->ResetMostRecentlyUsedSwapchain(); + } + } + DestroyBackBuffers(); ResetWindowProc(m_window); @@ -112,6 +122,8 @@ namespace dxvk { DWORD dwFlags) { D3D9DeviceLock lock = m_parent->LockDevice(); + m_parent->SetMostRecentlyUsedSwapchain(this); + if (unlikely(m_parent->IsDeviceLost())) return D3DERR_DEVICELOST; From 428c98bc6365a71951449af97e868b02497c5de9 Mon Sep 17 00:00:00 2001 From: Blisto91 <47954800+Blisto91@users.noreply.github.com> Date: Sat, 27 Jan 2024 14:31:24 +0100 Subject: [PATCH 082/246] [util] Disable countLosableResources for all d3d9 Supreme Ruler games All the d3d9 Supreme ruler games have this issue. --- src/util/config/config.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index efa9f4433..c48570ecf 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -870,10 +870,10 @@ namespace dxvk { { R"(\\UK Truck Simulator\\bin\\win_x86\\game\.exe$)", {{ { "d3d9.floatEmulation", "Strict" }, }} }, - /* Supreme Ruler Ultimate * + /* d3d9 Supreme Ruler games * * Leaks a StateBlock leading * * to Reset calls failing */ - { R"(\\SupremeRulerUltimate\.exe$)", {{ + { R"(\\SupremeRuler(Ultimate|GreatWar|1936|CW)\.exe$)", {{ { "d3d9.countLosableResources", "False" }, }} }, /* Operation Flashpoint: Red River * From 20490b678f6fb88339c8d0cb697c4825b2e391f9 Mon Sep 17 00:00:00 2001 From: Robin Kertels Date: Wed, 6 Mar 2024 12:05:56 +0100 Subject: [PATCH 083/246] [d3d9] Fix missing else brackets in ResetSwapchain --- src/d3d9/d3d9_device.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/d3d9/d3d9_device.cpp b/src/d3d9/d3d9_device.cpp index 329e9e7b7..7e44661b6 100644 --- a/src/d3d9/d3d9_device.cpp +++ b/src/d3d9/d3d9_device.cpp @@ -7842,9 +7842,10 @@ namespace dxvk { if (FAILED(hr)) return hr; } - else + else { m_implicitSwapchain = new D3D9SwapChainEx(this, pPresentationParameters, pFullscreenDisplayMode); m_mostRecentlyUsedSwapchain = m_implicitSwapchain.ptr(); + } if (pPresentationParameters->EnableAutoDepthStencil) { D3D9_COMMON_TEXTURE_DESC desc; From 0414bbe2d5d5ee0df027731a91f251312ca84cf4 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Wed, 6 Mar 2024 14:40:39 +0100 Subject: [PATCH 084/246] [dxgi] Add separate option to override vendor IDs for NVK --- dxvk.conf | 9 ++++++++- src/dxgi/dxgi_adapter.cpp | 8 ++++++-- src/dxgi/dxgi_options.cpp | 9 +++------ src/dxgi/dxgi_options.h | 6 +++++- src/util/config/config.cpp | 3 +++ 5 files changed, 25 insertions(+), 10 deletions(-) diff --git a/dxvk.conf b/dxvk.conf index d561f658b..44b1be67b 100644 --- a/dxvk.conf +++ b/dxvk.conf @@ -62,7 +62,7 @@ # Report Nvidia GPUs as AMD GPUs. Unless NVAPI support is explicitly -# enabled through proton, this is done by default in order to work +# enabled through Proton, this is done by default in order to work # around crashes or low performance with Nvidia-speciic code paths # in games, especially Unreal Engine. # @@ -71,6 +71,13 @@ # dxgi.hideNvidiaGpu = Auto +# Report Nvidia GPUs running on NVK as AMD GPUs. +# +# Supported values: Auto, True, False + +# dxgi.hideNvkGpu = Auto + + # Report AMD GPUs as Nvidia GPUs. This is only done for games that are # known to have issues with AMDAGS or other AMD-specific code paths. # diff --git a/src/dxgi/dxgi_adapter.cpp b/src/dxgi/dxgi_adapter.cpp index b7c1b7163..2ee5d2cd7 100644 --- a/src/dxgi/dxgi_adapter.cpp +++ b/src/dxgi/dxgi_adapter.cpp @@ -272,7 +272,8 @@ namespace dxvk { auto deviceProp = m_adapter->deviceProperties(); auto memoryProp = m_adapter->memoryProperties(); auto vk11 = m_adapter->devicePropertiesExt().vk11; - + auto vk12 = m_adapter->devicePropertiesExt().vk12; + // Custom Vendor / Device ID if (options->customVendorId >= 0) deviceProp.vendorID = options->customVendorId; @@ -298,7 +299,10 @@ namespace dxvk { fallbackDevice = 0x2487; } - bool hideGpu = (deviceProp.vendorID == uint16_t(DxvkGpuVendor::Nvidia) && options->hideNvidiaGpu) + bool hideNvidiaGpu = vk12.driverID == VK_DRIVER_ID_NVIDIA_PROPRIETARY + ? options->hideNvidiaGpu : options->hideNvkGpu; + + bool hideGpu = (deviceProp.vendorID == uint16_t(DxvkGpuVendor::Nvidia) && hideNvidiaGpu) || (deviceProp.vendorID == uint16_t(DxvkGpuVendor::Amd) && options->hideAmdGpu) || (deviceProp.vendorID == uint16_t(DxvkGpuVendor::Intel) && options->hideIntelGpu); diff --git a/src/dxgi/dxgi_options.cpp b/src/dxgi/dxgi_options.cpp index fe6cebccf..f3a3a4928 100644 --- a/src/dxgi/dxgi_options.cpp +++ b/src/dxgi/dxgi_options.cpp @@ -84,14 +84,11 @@ namespace dxvk { this->hideNvidiaGpu = !isNvapiEnabled(); Tristate hideNvidiaGpuOption = config.getOption("dxgi.hideNvidiaGpu", Tristate::Auto); - - if (hideNvidiaGpuOption == Tristate::Auto && !config.getOption("dxgi.nvapiHack", true)) { - Logger::warn("dxgi.nvapiHack is deprecated, please set dxgi.hideNvidiaGpu instead."); - hideNvidiaGpuOption = Tristate::False; - } - applyTristate(this->hideNvidiaGpu, hideNvidiaGpuOption); + // Always hide NVK devices by default since some NVAPI functionality may not work + this->hideNvkGpu = config.getOption("dxgi.hideNvkGpu", Tristate::Auto) == Tristate::True; + // Expose AMD and Intel GPU by default, unless a config override is active. // Implement as a tristate so that we have the option to introduce similar // logic to Nvidia later, if necessary. diff --git a/src/dxgi/dxgi_options.h b/src/dxgi/dxgi_options.h index c09373305..c952ea5e4 100644 --- a/src/dxgi/dxgi_options.h +++ b/src/dxgi/dxgi_options.h @@ -33,9 +33,13 @@ namespace dxvk { /// Emulate UMA bool emulateUMA; - /// Reports Nvidia GPUs as a different vendor (usually AMD) + /// Reports Nvidia GPUs running on the proprietary driver as a different + /// vendor (usually AMD). Proton will generally disable this option. bool hideNvidiaGpu; + /// Reports Nvidia GPUs running on NVK as a different vendor (usually AMD) + bool hideNvkGpu; + /// Reports AMD GPUs as a different vendor (usually Nvidia) bool hideAmdGpu; diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index c48570ecf..8943f8685 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -58,11 +58,13 @@ namespace dxvk { * Intel needs to match the AMD result */ { R"(\\(farcry3|fc3_blooddragon)_d3d11\.exe$)", {{ { "dxgi.hideNvidiaGpu", "False" }, + { "dxgi.hideNvkGpu", "False" }, { "dxgi.hideIntelGpu", "True" }, }} }, /* Far Cry 4 and Primal: Same as Far Cry 3 */ { R"(\\(FarCry4|FCPrimal)\.exe$)", {{ { "dxgi.hideNvidiaGpu", "False" }, + { "dxgi.hideNvkGpu", "False" }, { "dxgi.hideIntelGpu", "True" }, }} }, /* Frostpunk: Renders one frame with D3D9 * @@ -891,6 +893,7 @@ namespace dxvk { * GPU unless dxgi Id match actual GPU Id */ { R"(\\Diablo IV\.exe$)", {{ { "dxgi.hideNvidiaGpu", "False" }, + { "dxgi.hideNvkGpu", "False" }, }} }, /* WILD HEARTS™️ * * D3D12 title using D3D11 device for * From 4b0e3111d1204a5309e57f6ed2621813fca597fe Mon Sep 17 00:00:00 2001 From: Ethan Lee Date: Sun, 4 Feb 2024 19:37:42 -0500 Subject: [PATCH 085/246] meson: Check for bundled Vulkan/SPIR-V headers before adding them to the include list. This feature requires Meson 0.58 or newer. --- meson.build | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/meson.build b/meson.build index 020a21a62..e9e62a7d7 100644 --- a/meson.build +++ b/meson.build @@ -1,7 +1,8 @@ -project('dxvk', ['c', 'cpp'], version : 'v2.3', meson_version : '>= 0.49', default_options : [ 'cpp_std=c++17', 'warning_level=2' ]) +project('dxvk', ['c', 'cpp'], version : 'v2.3', meson_version : '>= 0.58', default_options : [ 'cpp_std=c++17', 'warning_level=2' ]) cpu_family = target_machine.cpu_family() platform = target_machine.system() +fs = import('fs') cpp = meson.get_compiler('cpp') cc = meson.get_compiler('c') @@ -33,11 +34,17 @@ if get_option('build_id') ] endif -dxvk_include_dirs = [ - './include', - './include/vulkan/include', - './include/spirv/include' -] +dxvk_include_dirs = ['./include'] +if fs.is_dir('./include/vulkan/include') + dxvk_include_dirs += ['./include/vulkan/include'] +elif not cpp.check_header('vulkan/vulkan.h') + error('Missing Vulkan-Headers') +endif +if fs.is_dir('./include/spirv/include') + dxvk_include_dirs += ['./include/spirv/include'] +elif not cpp.check_header('spirv/unified1/spirv.hpp') + error('Missing SPIRV-Headers') +endif dep_displayinfo = dependency( 'libdisplay-info', From afec5cce88f3fb6c47624da0e6cf6a15b68896cd Mon Sep 17 00:00:00 2001 From: Blisto91 <47954800+Blisto91@users.noreply.github.com> Date: Sat, 24 Feb 2024 16:27:30 +0100 Subject: [PATCH 086/246] [util] Remove some unneeded built in configs Dirt 5 does not crash without working ags anymore and Ethan Carter Redux also starts fine without a spoof. This allows the built in AMD ags in Proton 9 to be used for these games. The Hitman 3 config is redundant as it doesn't allow RT to be enabled without Nvapi anyway. --- src/util/config/config.cpp | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index 8943f8685..8de4da2ae 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -26,10 +26,6 @@ namespace dxvk { * when running on AMD hardware */ { R"(\\EliteDangerous64\.exe$)", {{ { "dxgi.customVendorId", "10de" }, - }} }, - /* The Vanishing of Ethan Carter Redux */ - { R"(\\EthanCarter-Win64-Shipping\.exe$)", {{ - { "dxgi.customVendorId", "10de" }, }} }, /* EVE Online: Needs this to expose D3D12 * * otherwise D3D12 option on launcher is * @@ -236,11 +232,6 @@ namespace dxvk { { R"(\\nioh2\.exe$)", {{ { "dxgi.deferSurfaceCreation", "True" }, }} }, - /* DIRT 5 - uses amd_ags_x64.dll when it * - * detects an AMD GPU */ - { R"(\\DIRT5\.exe$)", {{ - { "dxgi.customVendorId", "10de" }, - }} }, /* Crazy Machines 3 - crashes on long device * * descriptions */ { R"(\\cm3\.exe$)", {{ @@ -915,10 +906,6 @@ namespace dxvk { { R"(\\Cyberpunk2077\.exe$)", {{ { "dxgi.useMonitorFallback", "True" }, }} }, - /* Hitman 3 - Ray Tracing */ - { R"(\\HITMAN3\.exe$)", {{ - { "dxgi.hideNvidiaGpu", "False" }, - }} }, /* Metro Exodus Enhanced Edition picks GPU adapters * by available VRAM, which causes issues on some * systems with integrated graphics. */ From 0a699fddb64da50e2de506668ef0fb0e638f9a22 Mon Sep 17 00:00:00 2001 From: Minelelol <89744342+Minelelol@users.noreply.github.com> Date: Sun, 10 Sep 2023 20:39:16 +0700 Subject: [PATCH 087/246] Update config.cpp Insane Performance increase --- src/util/config/config.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index 8de4da2ae..4bd98e49b 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -807,6 +807,7 @@ namespace dxvk { * Black textures */ { R"(\\eldorado\.exe$)", {{ { "d3d9.floatEmulation", "Strict" }, + { "d3d9.allowDirectBufferMapping", "False" }, }} }, /* Injustice: Gods Among Us * * Locks a buffer that's still in use */ From fd3fbf66070e85bca8e7261577ea411e2bd620c7 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Wed, 6 Mar 2024 15:33:48 +0100 Subject: [PATCH 088/246] [dxvk] Remove old memory budget code Obsolete since we removed the Nvidia HVV workaround for old drivers. Closes #3877. --- src/dxvk/dxvk_memory.cpp | 10 +--------- src/dxvk/dxvk_memory.h | 1 - 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/src/dxvk/dxvk_memory.cpp b/src/dxvk/dxvk_memory.cpp index 0105bca8d..e516e358e 100644 --- a/src/dxvk/dxvk_memory.cpp +++ b/src/dxvk/dxvk_memory.cpp @@ -188,7 +188,6 @@ namespace dxvk { for (uint32_t i = 0; i < m_memProps.memoryHeapCount; i++) { m_memHeaps[i].properties = m_memProps.memoryHeaps[i]; m_memHeaps[i].stats = DxvkMemoryStats { 0, 0 }; - m_memHeaps[i].budget = 0; } for (uint32_t i = 0; i < m_memProps.memoryTypeCount; i++) { @@ -385,9 +384,6 @@ namespace dxvk { bool useMemoryPriority = (info.flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) && (m_device->features().extMemoryPriority.memoryPriority); - if (type->heap->budget && type->heap->stats.memoryAllocated + size > type->heap->budget) - return DxvkDeviceMemory(); - float priority = 0.0f; if (hints.test(DxvkMemoryFlag::GpuReadable)) @@ -547,11 +543,7 @@ namespace dxvk { bool DxvkMemoryAllocator::shouldFreeEmptyChunks( const DxvkMemoryHeap* heap, VkDeviceSize allocationSize) const { - VkDeviceSize budget = heap->budget; - - if (!budget) - budget = (heap->properties.size * 4) / 5; - + VkDeviceSize budget = (heap->properties.size * 4) / 5; return heap->stats.memoryAllocated + allocationSize > budget; } diff --git a/src/dxvk/dxvk_memory.h b/src/dxvk/dxvk_memory.h index 14dadd5f3..26ae85f49 100644 --- a/src/dxvk/dxvk_memory.h +++ b/src/dxvk/dxvk_memory.h @@ -66,7 +66,6 @@ namespace dxvk { struct DxvkMemoryHeap { VkMemoryHeap properties; DxvkMemoryStats stats; - VkDeviceSize budget; }; From 85215b10d6fa76491c7a496b067d4b55dfaf0d73 Mon Sep 17 00:00:00 2001 From: Robin Kertels Date: Wed, 6 Mar 2024 18:11:29 +0100 Subject: [PATCH 089/246] [d3d9] Respect vertex buffer offset when dynamically uploading geometry --- src/d3d9/d3d9_device.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/d3d9/d3d9_device.cpp b/src/d3d9/d3d9_device.cpp index 7e44661b6..cc4242592 100644 --- a/src/d3d9/d3d9_device.cpp +++ b/src/d3d9/d3d9_device.cpp @@ -5246,7 +5246,7 @@ namespace dxvk { auto* vbo = GetCommonBuffer(m_state.vertexBuffers[i].vertexBuffer); const uint32_t vertexStride = m_state.vertexDecl->GetSize(i); - uint32_t offset = (BaseVertexIndex + FirstVertexIndex) * vertexStride; + uint32_t offset = (BaseVertexIndex + FirstVertexIndex) * vertexStride + m_state.vertexBuffers[i].offset; uint8_t* data = reinterpret_cast(upSlice.mapPtr) + vboUPBufferOffsets[i]; uint8_t* src = reinterpret_cast(vbo->GetMappedSlice().mapPtr) + offset; From 72c86b82291166de874b76fb786a58a9f7417d26 Mon Sep 17 00:00:00 2001 From: Robin Kertels Date: Wed, 6 Mar 2024 22:17:02 +0100 Subject: [PATCH 090/246] [d3d9] Only unbind in EndScene if the game cleared the binding --- src/d3d9/d3d9_device.cpp | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/d3d9/d3d9_device.cpp b/src/d3d9/d3d9_device.cpp index cc4242592..628fc6f6e 100644 --- a/src/d3d9/d3d9_device.cpp +++ b/src/d3d9/d3d9_device.cpp @@ -1616,15 +1616,22 @@ namespace dxvk { m_flags.clr(D3D9DeviceFlag::InScene); - // D3D9 resets the internally bound vertex buffers and index buffer in EndScene. + // D3D9 resets the internally bound vertex buffers and index buffer in EndScene if they were unbound in the meantime. // We have to ignore unbinding those buffers because of Operation Flashpoint Red River, // so we should also clear the bindings here, to avoid leaks. - EmitCs([](DxvkContext* ctx) { - ctx->bindIndexBuffer(DxvkBufferSlice(), VK_INDEX_TYPE_UINT32); - for (uint32_t i = 0; i < DxvkLimits::MaxNumVertexBindings; i++) { - ctx->bindVertexBuffer(i, DxvkBufferSlice(), 0); + if (m_state.indices == nullptr) { + EmitCs([](DxvkContext* ctx) { + ctx->bindIndexBuffer(DxvkBufferSlice(), VK_INDEX_TYPE_UINT32); + }); + } + + for (uint32_t i = 0; i < DxvkLimits::MaxNumVertexBindings; i++) { + if (m_state.vertexBuffers[i].vertexBuffer == nullptr) { + EmitCs([cIndex = i](DxvkContext* ctx) { + ctx->bindVertexBuffer(cIndex, DxvkBufferSlice(), 0); + }); } - }); + } return D3D_OK; } From 3a6992ea9711b26bf90ec3db2a93ca0bea17d786 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Thu, 7 Mar 2024 15:33:20 +0100 Subject: [PATCH 091/246] [dxbc] Enable depth compare types for 1D images Used by Renderdoc internal shaders. --- src/dxbc/dxbc_compiler.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/dxbc/dxbc_compiler.cpp b/src/dxbc/dxbc_compiler.cpp index 59323b084..aaf163ad1 100644 --- a/src/dxbc/dxbc_compiler.cpp +++ b/src/dxbc/dxbc_compiler.cpp @@ -1059,7 +1059,9 @@ namespace dxvk { res.isRawSsbo = false; if ((sampledType == DxbcScalarType::Float32) - && (resourceType == DxbcResourceDim::Texture2D + && (resourceType == DxbcResourceDim::Texture1D + || resourceType == DxbcResourceDim::Texture1DArr + || resourceType == DxbcResourceDim::Texture2D || resourceType == DxbcResourceDim::Texture2DArr || resourceType == DxbcResourceDim::TextureCube || resourceType == DxbcResourceDim::TextureCubeArr)) { From 707ad6f32823ca96f474e808dd110eb71c88df8c Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Thu, 7 Mar 2024 16:10:32 +0100 Subject: [PATCH 092/246] [spirv] Add OpConstantNull --- src/spirv/spirv_module.cpp | 7 +++++++ src/spirv/spirv_module.h | 3 +++ 2 files changed, 10 insertions(+) diff --git a/src/spirv/spirv_module.cpp b/src/spirv/spirv_module.cpp index 9c141007d..ee62307e7 100644 --- a/src/spirv/spirv_module.cpp +++ b/src/spirv/spirv_module.cpp @@ -470,6 +470,13 @@ namespace dxvk { } + uint32_t SpirvModule::constNull( + uint32_t typeId) { + return this->defConst(spv::OpConstantNull, + typeId, 0, nullptr); + } + + uint32_t SpirvModule::lateConst32( uint32_t typeId) { uint32_t resultId = this->allocateId(); diff --git a/src/spirv/spirv_module.h b/src/spirv/spirv_module.h index 2845909df..b33bfe350 100644 --- a/src/spirv/spirv_module.h +++ b/src/spirv/spirv_module.h @@ -217,6 +217,9 @@ namespace dxvk { uint32_t constUndef( uint32_t typeId); + uint32_t constNull( + uint32_t typeId); + uint32_t lateConst32( uint32_t typeId); From 69a52b3da01ba4623a9af261b5fa2e9b44f4619d Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Thu, 7 Mar 2024 16:10:50 +0100 Subject: [PATCH 093/246] [dxbc] Do not emit depth compare for unsupported image types Fixes invalid SPIR-V. --- src/dxbc/dxbc_compiler.cpp | 211 ++++++++++++++++++++----------------- 1 file changed, 113 insertions(+), 98 deletions(-) diff --git a/src/dxbc/dxbc_compiler.cpp b/src/dxbc/dxbc_compiler.cpp index aaf163ad1..c3a6718b3 100644 --- a/src/dxbc/dxbc_compiler.cpp +++ b/src/dxbc/dxbc_compiler.cpp @@ -3881,33 +3881,38 @@ namespace dxvk { if (imageOperands.sparse) resultTypeId = getSparseResultTypeId(texelTypeId); - switch (ins.op) { - // Simple image gather operation - case DxbcOpcode::Gather4: - case DxbcOpcode::Gather4S: - case DxbcOpcode::Gather4Po: - case DxbcOpcode::Gather4PoS: { - resultId = m_module.opImageGather( - resultTypeId, sampledImageId, coord.id, - m_module.consti32(samplerReg.swizzle[0]), - imageOperands); - } break; - - // Depth-compare operation - case DxbcOpcode::Gather4C: - case DxbcOpcode::Gather4CS: - case DxbcOpcode::Gather4PoC: - case DxbcOpcode::Gather4PoCS: { - resultId = m_module.opImageDrefGather( - resultTypeId, sampledImageId, coord.id, - referenceValue.id, imageOperands); - } break; - - default: - Logger::warn(str::format( - "DxbcCompiler: Unhandled instruction: ", - ins.op)); - return; + if (sampledImageId) { + switch (ins.op) { + // Simple image gather operation + case DxbcOpcode::Gather4: + case DxbcOpcode::Gather4S: + case DxbcOpcode::Gather4Po: + case DxbcOpcode::Gather4PoS: { + resultId = m_module.opImageGather( + resultTypeId, sampledImageId, coord.id, + m_module.consti32(samplerReg.swizzle[0]), + imageOperands); + } break; + + // Depth-compare operation + case DxbcOpcode::Gather4C: + case DxbcOpcode::Gather4CS: + case DxbcOpcode::Gather4PoC: + case DxbcOpcode::Gather4PoCS: { + resultId = m_module.opImageDrefGather( + resultTypeId, sampledImageId, coord.id, + referenceValue.id, imageOperands); + } break; + + default: + Logger::warn(str::format( + "DxbcCompiler: Unhandled instruction: ", + ins.op)); + return; + } + } else { + Logger::warn(str::format("DxbcCompiler: ", ins.op, ": Unsupported image type")); + resultId = m_module.constNull(resultTypeId); } // If necessary, deal with the sparse result @@ -4035,73 +4040,78 @@ namespace dxvk { if (imageOperands.sparse) resultTypeId = getSparseResultTypeId(texelTypeId); - switch (ins.op) { - // Simple image sample operation - case DxbcOpcode::Sample: - case DxbcOpcode::SampleClampS: { - resultId = m_module.opImageSampleImplicitLod( - resultTypeId, sampledImageId, coord.id, - imageOperands); - } break; - - // Depth-compare operation - case DxbcOpcode::SampleC: - case DxbcOpcode::SampleCClampS: { - resultId = m_module.opImageSampleDrefImplicitLod( - resultTypeId, sampledImageId, coord.id, - referenceValue.id, imageOperands); - } break; - - // Depth-compare operation on mip level zero - case DxbcOpcode::SampleClz: - case DxbcOpcode::SampleClzS: { - imageOperands.flags |= spv::ImageOperandsLodMask; - imageOperands.sLod = m_module.constf32(0.0f); - - resultId = m_module.opImageSampleDrefExplicitLod( - resultTypeId, sampledImageId, coord.id, - referenceValue.id, imageOperands); - } break; - - // Sample operation with explicit gradients - case DxbcOpcode::SampleD: - case DxbcOpcode::SampleDClampS: { - imageOperands.flags |= spv::ImageOperandsGradMask; - imageOperands.sGradX = explicitGradientX.id; - imageOperands.sGradY = explicitGradientY.id; - - resultId = m_module.opImageSampleExplicitLod( - resultTypeId, sampledImageId, coord.id, - imageOperands); - } break; - - // Sample operation with explicit LOD - case DxbcOpcode::SampleL: - case DxbcOpcode::SampleLS: { - imageOperands.flags |= spv::ImageOperandsLodMask; - imageOperands.sLod = lod.id; - - resultId = m_module.opImageSampleExplicitLod( - resultTypeId, sampledImageId, coord.id, - imageOperands); - } break; - - // Sample operation with LOD bias - case DxbcOpcode::SampleB: - case DxbcOpcode::SampleBClampS: { - imageOperands.flags |= spv::ImageOperandsBiasMask; - imageOperands.sLodBias = lod.id; - - resultId = m_module.opImageSampleImplicitLod( - resultTypeId, sampledImageId, coord.id, - imageOperands); - } break; - - default: - Logger::warn(str::format( - "DxbcCompiler: Unhandled instruction: ", - ins.op)); - return; + if (sampledImageId) { + switch (ins.op) { + // Simple image sample operation + case DxbcOpcode::Sample: + case DxbcOpcode::SampleClampS: { + resultId = m_module.opImageSampleImplicitLod( + resultTypeId, sampledImageId, coord.id, + imageOperands); + } break; + + // Depth-compare operation + case DxbcOpcode::SampleC: + case DxbcOpcode::SampleCClampS: { + resultId = m_module.opImageSampleDrefImplicitLod( + resultTypeId, sampledImageId, coord.id, + referenceValue.id, imageOperands); + } break; + + // Depth-compare operation on mip level zero + case DxbcOpcode::SampleClz: + case DxbcOpcode::SampleClzS: { + imageOperands.flags |= spv::ImageOperandsLodMask; + imageOperands.sLod = m_module.constf32(0.0f); + + resultId = m_module.opImageSampleDrefExplicitLod( + resultTypeId, sampledImageId, coord.id, + referenceValue.id, imageOperands); + } break; + + // Sample operation with explicit gradients + case DxbcOpcode::SampleD: + case DxbcOpcode::SampleDClampS: { + imageOperands.flags |= spv::ImageOperandsGradMask; + imageOperands.sGradX = explicitGradientX.id; + imageOperands.sGradY = explicitGradientY.id; + + resultId = m_module.opImageSampleExplicitLod( + resultTypeId, sampledImageId, coord.id, + imageOperands); + } break; + + // Sample operation with explicit LOD + case DxbcOpcode::SampleL: + case DxbcOpcode::SampleLS: { + imageOperands.flags |= spv::ImageOperandsLodMask; + imageOperands.sLod = lod.id; + + resultId = m_module.opImageSampleExplicitLod( + resultTypeId, sampledImageId, coord.id, + imageOperands); + } break; + + // Sample operation with LOD bias + case DxbcOpcode::SampleB: + case DxbcOpcode::SampleBClampS: { + imageOperands.flags |= spv::ImageOperandsBiasMask; + imageOperands.sLodBias = lod.id; + + resultId = m_module.opImageSampleImplicitLod( + resultTypeId, sampledImageId, coord.id, + imageOperands); + } break; + + default: + Logger::warn(str::format( + "DxbcCompiler: Unhandled instruction: ", + ins.op)); + return; + } + } else { + Logger::warn(str::format("DxbcCompiler: ", ins.op, ": Unsupported image type")); + resultId = m_module.constNull(resultTypeId); } DxbcRegisterValue result; @@ -5147,10 +5157,15 @@ namespace dxvk { const DxbcShaderResource& textureResource, const DxbcSampler& samplerResource, bool isDepthCompare) { - const uint32_t sampledImageType = isDepthCompare - ? m_module.defSampledImageType(textureResource.depthTypeId) - : m_module.defSampledImageType(textureResource.colorTypeId); - + uint32_t baseId = isDepthCompare + ? textureResource.depthTypeId + : textureResource.colorTypeId; + + if (!baseId) + return 0; + + uint32_t sampledImageType = m_module.defSampledImageType(baseId); + return m_module.opSampledImage(sampledImageType, m_module.opLoad(textureResource.imageTypeId, textureResource.varId), m_module.opLoad(samplerResource.typeId, samplerResource.varId)); From 15ddadc4de9df615005884c3abc93786234fb3b5 Mon Sep 17 00:00:00 2001 From: Robin Kertels Date: Thu, 7 Mar 2024 21:01:30 +0100 Subject: [PATCH 094/246] [d3d9] Fix number of streams --- src/d3d9/d3d9_device.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/d3d9/d3d9_device.cpp b/src/d3d9/d3d9_device.cpp index 628fc6f6e..bfbbe2a4b 100644 --- a/src/d3d9/d3d9_device.cpp +++ b/src/d3d9/d3d9_device.cpp @@ -1625,7 +1625,7 @@ namespace dxvk { }); } - for (uint32_t i = 0; i < DxvkLimits::MaxNumVertexBindings; i++) { + for (uint32_t i = 0; i < caps::MaxStreams; i++) { if (m_state.vertexBuffers[i].vertexBuffer == nullptr) { EmitCs([cIndex = i](DxvkContext* ctx) { ctx->bindVertexBuffer(cIndex, DxvkBufferSlice(), 0); From 0776d764a411217bc42a4a442717ee64508638a4 Mon Sep 17 00:00:00 2001 From: Billy Laws Date: Sat, 9 Mar 2024 05:43:46 +0000 Subject: [PATCH 095/246] build: Support building for ARM64EC When targeting ARM64EC, both __x86_64__ and _M_X86_64 are defined but not all x86 intrinsics are present, treat EC as regular ARM64 so the native intrinsics are used instead. --- src/d3d9/d3d9_device.cpp | 2 +- src/util/util_bit.h | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/d3d9/d3d9_device.cpp b/src/d3d9/d3d9_device.cpp index bfbbe2a4b..5b862c056 100644 --- a/src/d3d9/d3d9_device.cpp +++ b/src/d3d9/d3d9_device.cpp @@ -5355,7 +5355,7 @@ namespace dxvk { // Round to nearest _controlfp(_RC_NEAR, _MCW_RC); -#elif (defined(__GNUC__) || defined(__MINGW32__)) && (defined(__i386__) || defined(__x86_64__) || defined(__ia64)) +#elif (defined(__GNUC__) || defined(__MINGW32__)) && (defined(__i386__) || (defined(__x86_64__) && !defined(__arm64ec__)) || defined(__ia64)) // For GCC/MinGW we can use inline asm to set it. // This only works for x86 and x64 processors however. diff --git a/src/util/util_bit.h b/src/util/util_bit.h index 9f43a9f02..1df6c17fe 100644 --- a/src/util/util_bit.h +++ b/src/util/util_bit.h @@ -1,11 +1,12 @@ #pragma once -#if defined(__x86_64__) || defined(_M_X64) || defined(__i386__) || defined(_M_IX86) || defined(__e2k__) +#if (defined(__x86_64__) && !defined(__arm64ec__)) || (defined(_M_X64) && !defined(_M_ARM64EC)) \ + || defined(__i386__) || defined(_M_IX86) || defined(__e2k__) #define DXVK_ARCH_X86 #if defined(__x86_64__) || defined(_M_X64) || defined(__e2k__) #define DXVK_ARCH_X86_64 #endif -#elif defined(__aarch64__) || defined(_M_ARM64) +#elif defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC) #define DXVK_ARCH_ARM64 #else #error "Unknown CPU Architecture" From 8b8be7c2bf5d7dbaa5c6398e4bdae16aed521151 Mon Sep 17 00:00:00 2001 From: Robin Kertels Date: Sun, 10 Mar 2024 16:36:02 +0100 Subject: [PATCH 096/246] [d3d9] Fix stream count in Reset --- src/d3d9/d3d9_device.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/d3d9/d3d9_device.cpp b/src/d3d9/d3d9_device.cpp index 5b862c056..21e81173b 100644 --- a/src/d3d9/d3d9_device.cpp +++ b/src/d3d9/d3d9_device.cpp @@ -484,7 +484,7 @@ namespace dxvk { // Unbind all buffers that were still bound to the backend to avoid leaks. EmitCs([](DxvkContext* ctx) { ctx->bindIndexBuffer(DxvkBufferSlice(), VK_INDEX_TYPE_UINT32); - for (uint32_t i = 0; i < DxvkLimits::MaxNumVertexBindings; i++) { + for (uint32_t i = 0; i < caps::MaxStreams; i++) { ctx->bindVertexBuffer(i, DxvkBufferSlice(), 0); } }); From 1a685b1c674a8af5963521dc8a3035b27c70401e Mon Sep 17 00:00:00 2001 From: Kaitlyn <87990853+BunniKaitlyn@users.noreply.github.com> Date: Wed, 13 Mar 2024 20:50:27 -0400 Subject: [PATCH 097/246] Implement D3D11DXGIDevice::EnqueueSetEvent --- src/d3d11/d3d11_device.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/d3d11/d3d11_device.cpp b/src/d3d11/d3d11_device.cpp index 2b9a82b9e..863a9a311 100644 --- a/src/d3d11/d3d11_device.cpp +++ b/src/d3d11/d3d11_device.cpp @@ -3411,8 +3411,9 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE D3D11DXGIDevice::EnqueueSetEvent(HANDLE hEvent) { - Logger::err("D3D11DXGIDevice::EnqueueSetEvent: Not implemented"); - return DXGI_ERROR_UNSUPPORTED; + auto immediateContext = m_d3d11Device.GetContext(); + immediateContext->Flush1(D3D11_CONTEXT_TYPE_ALL, hEvent); + return S_OK; } From 783c9d459123246fb2b938558674c22ee70f55a4 Mon Sep 17 00:00:00 2001 From: Kaitlyn <87990853+BunniKaitlyn@users.noreply.github.com> Date: Wed, 13 Mar 2024 20:41:11 -0400 Subject: [PATCH 098/246] Fix MiscFlags check in GetDescFromResource --- src/d3d11/d3d11_view_srv.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/d3d11/d3d11_view_srv.cpp b/src/d3d11/d3d11_view_srv.cpp index 065e6005e..2b2f5520b 100644 --- a/src/d3d11/d3d11_view_srv.cpp +++ b/src/d3d11/d3d11_view_srv.cpp @@ -298,7 +298,7 @@ namespace dxvk { D3D11_BUFFER_DESC bufferDesc; static_cast(pResource)->GetDesc(&bufferDesc); - if (bufferDesc.MiscFlags == D3D11_RESOURCE_MISC_BUFFER_STRUCTURED) { + if (bufferDesc.MiscFlags & D3D11_RESOURCE_MISC_BUFFER_STRUCTURED) { pDesc->Format = DXGI_FORMAT_UNKNOWN; pDesc->ViewDimension = D3D11_SRV_DIMENSION_BUFFER; pDesc->Buffer.FirstElement = 0; From 538f1d13d4c83cc7376c5f881dbdb5c0f3d4d178 Mon Sep 17 00:00:00 2001 From: Kaitlyn <87990853+BunniKaitlyn@users.noreply.github.com> Date: Fri, 15 Mar 2024 09:44:23 -0400 Subject: [PATCH 099/246] Fix UAV as well --- src/d3d11/d3d11_view_uav.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/d3d11/d3d11_view_uav.cpp b/src/d3d11/d3d11_view_uav.cpp index 94faf88d9..9f1c2c00d 100644 --- a/src/d3d11/d3d11_view_uav.cpp +++ b/src/d3d11/d3d11_view_uav.cpp @@ -214,7 +214,7 @@ namespace dxvk { D3D11_BUFFER_DESC bufferDesc; static_cast(pResource)->GetDesc(&bufferDesc); - if (bufferDesc.MiscFlags == D3D11_RESOURCE_MISC_BUFFER_STRUCTURED) { + if (bufferDesc.MiscFlags & D3D11_RESOURCE_MISC_BUFFER_STRUCTURED) { pDesc->Format = DXGI_FORMAT_UNKNOWN; pDesc->ViewDimension = D3D11_UAV_DIMENSION_BUFFER; pDesc->Buffer.FirstElement = 0; From e857b09432241c68b8fa6d873c3943d669f561fe Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Fri, 15 Mar 2024 22:48:57 +0100 Subject: [PATCH 100/246] [dxgi] Change default behaviour of hideNvkGpu option --- src/dxgi/dxgi_options.cpp | 10 +++++----- src/util/config/config.cpp | 3 --- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/dxgi/dxgi_options.cpp b/src/dxgi/dxgi_options.cpp index f3a3a4928..5bc98da6a 100644 --- a/src/dxgi/dxgi_options.cpp +++ b/src/dxgi/dxgi_options.cpp @@ -82,12 +82,12 @@ namespace dxvk { // Expose Nvidia GPUs properly if NvAPI is enabled in environment this->hideNvidiaGpu = !isNvapiEnabled(); + applyTristate(this->hideNvidiaGpu, config.getOption("dxgi.hideNvidiaGpu", Tristate::Auto)); - Tristate hideNvidiaGpuOption = config.getOption("dxgi.hideNvidiaGpu", Tristate::Auto); - applyTristate(this->hideNvidiaGpu, hideNvidiaGpuOption); - - // Always hide NVK devices by default since some NVAPI functionality may not work - this->hideNvkGpu = config.getOption("dxgi.hideNvkGpu", Tristate::Auto) == Tristate::True; + // Treat NVK adapters the same as Nvidia cards on the proprietary by + // default, but provide an override in case something isn't working. + this->hideNvkGpu = this->hideNvidiaGpu; + applyTristate(this->hideNvkGpu, config.getOption("dxgi.hideNvkGpu", Tristate::Auto)); // Expose AMD and Intel GPU by default, unless a config override is active. // Implement as a tristate so that we have the option to introduce similar diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index 4bd98e49b..dd9424ef7 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -54,13 +54,11 @@ namespace dxvk { * Intel needs to match the AMD result */ { R"(\\(farcry3|fc3_blooddragon)_d3d11\.exe$)", {{ { "dxgi.hideNvidiaGpu", "False" }, - { "dxgi.hideNvkGpu", "False" }, { "dxgi.hideIntelGpu", "True" }, }} }, /* Far Cry 4 and Primal: Same as Far Cry 3 */ { R"(\\(FarCry4|FCPrimal)\.exe$)", {{ { "dxgi.hideNvidiaGpu", "False" }, - { "dxgi.hideNvkGpu", "False" }, { "dxgi.hideIntelGpu", "True" }, }} }, /* Frostpunk: Renders one frame with D3D9 * @@ -885,7 +883,6 @@ namespace dxvk { * GPU unless dxgi Id match actual GPU Id */ { R"(\\Diablo IV\.exe$)", {{ { "dxgi.hideNvidiaGpu", "False" }, - { "dxgi.hideNvkGpu", "False" }, }} }, /* WILD HEARTS™️ * * D3D12 title using D3D11 device for * From 1085ba713e8b3c2dd7de5e395eeeb5f1a8bc06dd Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Fri, 15 Mar 2024 14:34:51 +0100 Subject: [PATCH 101/246] [dxgi] Implicitly set HDR color space for RGBA16_FLOAT swap chains --- src/dxgi/dxgi_swapchain.cpp | 96 ++++++++++++++++++++++++++++--------- src/dxgi/dxgi_swapchain.h | 10 ++++ 2 files changed, 84 insertions(+), 22 deletions(-) diff --git a/src/dxgi/dxgi_swapchain.cpp b/src/dxgi/dxgi_swapchain.cpp index ab96bfca1..88ee3c6d2 100644 --- a/src/dxgi/dxgi_swapchain.cpp +++ b/src/dxgi/dxgi_swapchain.cpp @@ -32,6 +32,9 @@ namespace dxvk { // Apply initial window mode and fullscreen state if (!m_descFs.Windowed && FAILED(EnterFullscreenMode(nullptr))) throw DxvkError("DXGI: Failed to set initial fullscreen state"); + + // Ensure that RGBA16 swap chains are scRGB if supported + UpdateColorSpace(m_desc.Format, m_colorSpace); } @@ -398,7 +401,13 @@ namespace dxvk { if (Format != DXGI_FORMAT_UNKNOWN) m_desc.Format = Format; - return m_presenter->ChangeProperties(&m_desc, pCreationNodeMask, ppPresentQueue); + HRESULT hr = m_presenter->ChangeProperties(&m_desc, pCreationNodeMask, ppPresentQueue); + + if (FAILED(hr)) + return hr; + + UpdateColorSpace(m_desc.Format, m_colorSpace); + return hr; } @@ -572,36 +581,31 @@ namespace dxvk { if (!pColorSpaceSupport) return E_INVALIDARG; - // Don't expose any color spaces other than standard - // sRGB if the enableHDR option is not set. - // - // If we ever have a use for the non-SRGB non-HDR colorspaces - // some day, we may want to revisit this. - if (ColorSpace != DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709 - && !m_factory->GetOptions()->enableHDR) { - *pColorSpaceSupport = 0; - return S_OK; - } + std::lock_guard lock(m_lockBuffer); + + if (ValidateColorSpaceSupport(m_desc.Format, ColorSpace)) + *pColorSpaceSupport = DXGI_SWAP_CHAIN_COLOR_SPACE_SUPPORT_FLAG_PRESENT; + else + *pColorSpaceSupport = 0; - UINT support = m_presenter->CheckColorSpaceSupport(ColorSpace); - *pColorSpaceSupport = support; return S_OK; } HRESULT STDMETHODCALLTYPE DxgiSwapChain::SetColorSpace1(DXGI_COLOR_SPACE_TYPE ColorSpace) { - UINT support = m_presenter->CheckColorSpaceSupport(ColorSpace); + std::lock_guard lock(m_lockBuffer); - if (!support) + if (!ValidateColorSpaceSupport(m_desc.Format, ColorSpace)) return E_INVALIDARG; - std::lock_guard lock(m_lockBuffer); - HRESULT hr = m_presenter->SetColorSpace(ColorSpace); - if (SUCCEEDED(hr)) { - // If this was a colorspace other than our current one, - // punt us into that one on the DXGI output. - m_monitorInfo->PuntColorSpace(ColorSpace); - } + // Write back color space if setting it up succeeded. This way, we preserve + // the current color space even if the swap chain temporarily switches to a + // back buffer format which does not support it. + HRESULT hr = UpdateColorSpace(m_desc.Format, ColorSpace); + + if (SUCCEEDED(hr)) + m_colorSpace = ColorSpace; + return hr; } @@ -869,6 +873,7 @@ namespace dxvk { m_monitorInfo->ReleaseMonitorData(); } + void DxgiSwapChain::UpdateGlobalHDRState() { // Update the global HDR state if called from the legacy NVAPI // interfaces, etc. @@ -893,4 +898,51 @@ namespace dxvk { } } + + bool DxgiSwapChain::ValidateColorSpaceSupport( + DXGI_FORMAT Format, + DXGI_COLOR_SPACE_TYPE ColorSpace) { + // RGBA16 swap chains are treated as scRGB even on SDR displays, + // and regular sRGB is not exposed when this format is used. + if (Format == DXGI_FORMAT_R16G16B16A16_FLOAT) + return ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709; + + // For everything else, we will always expose plain sRGB + if (ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709) + return true; + + // Only expose HDR10 color space if HDR option is enabled + if (ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020) + return m_factory->GetOptions()->enableHDR && m_presenter->CheckColorSpaceSupport(ColorSpace); + + return false; + } + + + HRESULT DxgiSwapChain::UpdateColorSpace( + DXGI_FORMAT Format, + DXGI_COLOR_SPACE_TYPE ColorSpace) { + // Don't do anything if the explicitly sepected color space + // is compatible with the back buffer format already + if (!ValidateColorSpaceSupport(Format, ColorSpace)) { + ColorSpace = Format == DXGI_FORMAT_R16G16B16A16_FLOAT + ? DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709 + : DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709; + } + + // Ensure that we pick a supported color space. This is relevant for + // mapping scRGB to sRGB on SDR setups, matching Windows behaviour. + if (!m_presenter->CheckColorSpaceSupport(ColorSpace)) + ColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709; + + HRESULT hr = m_presenter->SetColorSpace(ColorSpace); + + // If this was a colorspace other than our current one, + // punt us into that one on the DXGI output. + if (SUCCEEDED(hr)) + m_monitorInfo->PuntColorSpace(ColorSpace); + + return hr; + } + } diff --git a/src/dxgi/dxgi_swapchain.h b/src/dxgi/dxgi_swapchain.h index 7702f879d..a01cdf2e0 100644 --- a/src/dxgi/dxgi_swapchain.h +++ b/src/dxgi/dxgi_swapchain.h @@ -193,6 +193,8 @@ namespace dxvk { bool m_frameStatisticsDisjoint = true; wsi::DxvkWindowState m_windowState; + DXGI_COLOR_SPACE_TYPE m_colorSpace = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709; + uint32_t m_globalHDRStateSerial = 0; HRESULT EnterFullscreenMode( @@ -223,6 +225,14 @@ namespace dxvk { void UpdateGlobalHDRState(); + bool ValidateColorSpaceSupport( + DXGI_FORMAT Format, + DXGI_COLOR_SPACE_TYPE ColorSpace); + + HRESULT UpdateColorSpace( + DXGI_FORMAT Format, + DXGI_COLOR_SPACE_TYPE ColorSpace); + }; } From ef4428ab8c51c839cd1de662658c97f52e8a83ec Mon Sep 17 00:00:00 2001 From: Joshua Ashton Date: Sat, 16 Mar 2024 18:59:03 +0000 Subject: [PATCH 102/246] [d3d9] Improve ReturnUnderlyingResource stub for 9on12 --- src/d3d9/d3d9_on_12.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/d3d9/d3d9_on_12.cpp b/src/d3d9/d3d9_on_12.cpp index 38885894c..2f9c9c447 100644 --- a/src/d3d9/d3d9_on_12.cpp +++ b/src/d3d9/d3d9_on_12.cpp @@ -30,8 +30,11 @@ namespace dxvk { return E_NOINTERFACE; } HRESULT STDMETHODCALLTYPE D3D9On12::ReturnUnderlyingResource(IDirect3DResource9* resource, UINT num_sync, UINT64* signal_values, ID3D12Fence** fences) { - Logger::err("D3D9On12::GetD3D12Device: ReturnUnderlyingResource: Stub"); - return E_NOINTERFACE; + if (num_sync) + Logger::err("D3D9On12::GetD3D12Device: ReturnUnderlyingResource: Stub"); + + m_device->Flush(); + return S_OK; } } From 0beb18ef739fbfcc89aa8cf5ba926b45100a6c95 Mon Sep 17 00:00:00 2001 From: Joshua Ashton Date: Sat, 16 Mar 2024 19:11:11 +0000 Subject: [PATCH 103/246] [d3d9] Wait for submission when calling ReturnUnderlyingResource --- src/d3d9/d3d9_device.cpp | 27 +++++++++++++++++++++++---- src/d3d9/d3d9_device.h | 6 ++++++ src/d3d9/d3d9_on_12.cpp | 2 +- 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/src/d3d9/d3d9_device.cpp b/src/d3d9/d3d9_device.cpp index 21e81173b..e99ad3837 100644 --- a/src/d3d9/d3d9_device.cpp +++ b/src/d3d9/d3d9_device.cpp @@ -5672,10 +5672,13 @@ namespace dxvk { } - - void D3D9DeviceEx::Flush() { + template + void D3D9DeviceEx::ExecuteFlush() { D3D9DeviceLock lock = LockDevice(); + if constexpr (Synchronize9On12) + m_submitStatus.result = VK_NOT_READY; + m_initializer->Flush(); m_converter->Flush(); @@ -5687,16 +5690,32 @@ namespace dxvk { EmitCs([ cSubmissionFence = m_submissionFence, - cSubmissionId = submissionId + cSubmissionId = submissionId, + cSubmissionStatus = Synchronize9On12 ? &m_submitStatus : nullptr ] (DxvkContext* ctx) { ctx->signal(cSubmissionFence, cSubmissionId); - ctx->flushCommandList(nullptr); + ctx->flushCommandList(cSubmissionStatus); }); FlushCsChunk(); m_flushSeqNum = m_csSeqNum; m_flushTracker.notifyFlush(m_flushSeqNum, submissionId); + + // If necessary, block calling thread until the + // Vulkan queue submission is performed. + if constexpr (Synchronize9On12) + m_dxvkDevice->waitForSubmission(&m_submitStatus); + } + + + void D3D9DeviceEx::Flush() { + ExecuteFlush(); + } + + + void D3D9DeviceEx::FlushAndSync9On12() { + ExecuteFlush(); } diff --git a/src/d3d9/d3d9_device.h b/src/d3d9/d3d9_device.h index 90beb034f..114a0b9bb 100644 --- a/src/d3d9/d3d9_device.h +++ b/src/d3d9/d3d9_device.h @@ -793,6 +793,7 @@ namespace dxvk { void SynchronizeCsThread(uint64_t SequenceNumber); void Flush(); + void FlushAndSync9On12(); void EndFrame(); @@ -1059,6 +1060,9 @@ namespace dxvk { return std::exchange(m_deviceHasBeenReset, false); } + template + void ExecuteFlush(); + void DetermineConstantLayouts(bool canSWVP); D3D9BufferSlice AllocUPBuffer(VkDeviceSize size); @@ -1421,6 +1425,8 @@ namespace dxvk { Rc m_submissionFence; uint64_t m_submissionId = 0ull; + DxvkSubmitStatus m_submitStatus; + uint64_t m_flushSeqNum = 0ull; GpuFlushTracker m_flushTracker; diff --git a/src/d3d9/d3d9_on_12.cpp b/src/d3d9/d3d9_on_12.cpp index 2f9c9c447..a0ec43984 100644 --- a/src/d3d9/d3d9_on_12.cpp +++ b/src/d3d9/d3d9_on_12.cpp @@ -33,7 +33,7 @@ namespace dxvk { if (num_sync) Logger::err("D3D9On12::GetD3D12Device: ReturnUnderlyingResource: Stub"); - m_device->Flush(); + m_device->FlushAndSync9On12(); return S_OK; } From 2e1a19c7fdb459d35a111da3ed51aa675391dc09 Mon Sep 17 00:00:00 2001 From: Blisto91 <47954800+Blisto91@users.noreply.github.com> Date: Sun, 17 Mar 2024 21:36:03 +0100 Subject: [PATCH 104/246] [util] Cap Dark Void to 60fps Game crashes in certain places like specific cutscenes unless capped at 60fps. --- src/util/config/config.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index dd9424ef7..698b69cb9 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -873,6 +873,10 @@ namespace dxvk { { R"(\\RedRiver\.exe$)", {{ { "d3d9.floatEmulation", "Strict" }, }} }, + /* Dark Void - Crashes above 60fps in places */ + { R"(\\ShippingPC-SkyGame\.exe$)", {{ + { "d3d9.maxFrameRate", "60" }, + }} }, /**********************************************/ From a16308277063f5825b0f0c0ae00ca4cfb163548d Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Tue, 19 Mar 2024 19:31:42 +0100 Subject: [PATCH 105/246] [dxvk] Align index buffer size to index size Fixes validation errors in FFXIV. --- src/dxvk/dxvk_context.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/dxvk/dxvk_context.cpp b/src/dxvk/dxvk_context.cpp index bbd139edb..9bb9c92c4 100644 --- a/src/dxvk/dxvk_context.cpp +++ b/src/dxvk/dxvk_context.cpp @@ -5594,11 +5594,13 @@ namespace dxvk { auto bufferInfo = m_state.vi.indexBuffer.getDescriptor(); if (m_features.test(DxvkContextFeature::IndexBufferRobustness)) { + VkDeviceSize align = m_state.vi.indexType == VK_INDEX_TYPE_UINT16 ? 2 : 4; + VkDeviceSize range = bufferInfo.buffer.range & ~(align - 1); + m_cmd->cmdBindIndexBuffer2( bufferInfo.buffer.buffer, bufferInfo.buffer.offset, - bufferInfo.buffer.range, - m_state.vi.indexType); + range, m_state.vi.indexType); } else { m_cmd->cmdBindIndexBuffer( bufferInfo.buffer.buffer, From c5aeb0f87a784f08cf4dbaa1bf8fac4a82878d67 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Tue, 19 Mar 2024 15:51:45 +0100 Subject: [PATCH 106/246] [dxvk] Get rid of separate depth shaders for shader-based copies We can export both depth and color in a single shader instead. --- src/dxvk/dxvk_meta_copy.cpp | 14 ++------------ src/dxvk/meson.build | 3 --- src/dxvk/shaders/dxvk_copy_color_1d.frag | 6 ++++-- src/dxvk/shaders/dxvk_copy_color_2d.frag | 6 ++++-- src/dxvk/shaders/dxvk_copy_color_ms.frag | 6 ++++-- src/dxvk/shaders/dxvk_copy_depth_1d.frag | 16 ---------------- src/dxvk/shaders/dxvk_copy_depth_2d.frag | 16 ---------------- src/dxvk/shaders/dxvk_copy_depth_ms.frag | 17 ----------------- 8 files changed, 14 insertions(+), 70 deletions(-) delete mode 100644 src/dxvk/shaders/dxvk_copy_depth_1d.frag delete mode 100644 src/dxvk/shaders/dxvk_copy_depth_2d.frag delete mode 100644 src/dxvk/shaders/dxvk_copy_depth_ms.frag diff --git a/src/dxvk/dxvk_meta_copy.cpp b/src/dxvk/dxvk_meta_copy.cpp index a3be19c82..45339af7f 100644 --- a/src/dxvk/dxvk_meta_copy.cpp +++ b/src/dxvk/dxvk_meta_copy.cpp @@ -9,9 +9,6 @@ #include #include #include -#include -#include -#include #include #include #include @@ -84,11 +81,7 @@ namespace dxvk { m_color { createShaderModule(dxvk_copy_color_1d), createShaderModule(dxvk_copy_color_2d), - createShaderModule(dxvk_copy_color_ms) }, - m_depth { - createShaderModule(dxvk_copy_depth_1d), - createShaderModule(dxvk_copy_depth_2d), - createShaderModule(dxvk_copy_depth_ms) } { + createShaderModule(dxvk_copy_color_ms) } { if (device->features().vk12.shaderOutputLayer) { m_shaderVert = createShaderModule(dxvk_fullscreen_layer_vert); } else { @@ -119,9 +112,6 @@ namespace dxvk { m_vkd->vkDestroyShaderModule(m_vkd->device(), m_depthStencil.fragMs, nullptr); m_vkd->vkDestroyShaderModule(m_vkd->device(), m_depthStencil.frag2D, nullptr); m_vkd->vkDestroyShaderModule(m_vkd->device(), m_depthStencil.frag1D, nullptr); - m_vkd->vkDestroyShaderModule(m_vkd->device(), m_depth.fragMs, nullptr); - m_vkd->vkDestroyShaderModule(m_vkd->device(), m_depth.frag2D, nullptr); - m_vkd->vkDestroyShaderModule(m_vkd->device(), m_depth.frag1D, nullptr); m_vkd->vkDestroyShaderModule(m_vkd->device(), m_color.fragMs, nullptr); m_vkd->vkDestroyShaderModule(m_vkd->device(), m_color.frag2D, nullptr); m_vkd->vkDestroyShaderModule(m_vkd->device(), m_color.frag1D, nullptr); @@ -313,7 +303,7 @@ namespace dxvk { std::array, 3> shaderSets = {{ { &m_color, VK_IMAGE_ASPECT_COLOR_BIT }, - { &m_depth, VK_IMAGE_ASPECT_DEPTH_BIT }, + { &m_color, VK_IMAGE_ASPECT_DEPTH_BIT }, { &m_depthStencil, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT }, }}; diff --git a/src/dxvk/meson.build b/src/dxvk/meson.build index 2bf46c5f1..c8fa9bda5 100644 --- a/src/dxvk/meson.build +++ b/src/dxvk/meson.build @@ -20,9 +20,6 @@ dxvk_shaders = files([ 'shaders/dxvk_copy_color_1d.frag', 'shaders/dxvk_copy_color_2d.frag', 'shaders/dxvk_copy_color_ms.frag', - 'shaders/dxvk_copy_depth_1d.frag', - 'shaders/dxvk_copy_depth_2d.frag', - 'shaders/dxvk_copy_depth_ms.frag', 'shaders/dxvk_copy_depth_stencil_1d.frag', 'shaders/dxvk_copy_depth_stencil_2d.frag', 'shaders/dxvk_copy_depth_stencil_ms.frag', diff --git a/src/dxvk/shaders/dxvk_copy_color_1d.frag b/src/dxvk/shaders/dxvk_copy_color_1d.frag index b91067d91..b2a0da864 100644 --- a/src/dxvk/shaders/dxvk_copy_color_1d.frag +++ b/src/dxvk/shaders/dxvk_copy_color_1d.frag @@ -13,6 +13,8 @@ uniform u_info_t { } u_info; void main() { - o_color = texelFetch(s_image, + vec4 color = texelFetch(s_image, ivec2(gl_FragCoord.x + u_info.offset.x, gl_Layer), 0); -} \ No newline at end of file + o_color = color; + gl_FragDepth = color.r; +} diff --git a/src/dxvk/shaders/dxvk_copy_color_2d.frag b/src/dxvk/shaders/dxvk_copy_color_2d.frag index 99a0cc270..dc53373e1 100644 --- a/src/dxvk/shaders/dxvk_copy_color_2d.frag +++ b/src/dxvk/shaders/dxvk_copy_color_2d.frag @@ -13,6 +13,8 @@ uniform u_info_t { } u_info; void main() { - o_color = texelFetch(s_image, + vec4 color = texelFetch(s_image, ivec3(gl_FragCoord.xy + u_info.offset, gl_Layer), 0); -} \ No newline at end of file + o_color = color; + gl_FragDepth = color.r; +} diff --git a/src/dxvk/shaders/dxvk_copy_color_ms.frag b/src/dxvk/shaders/dxvk_copy_color_ms.frag index b7a79057f..20072d3f2 100644 --- a/src/dxvk/shaders/dxvk_copy_color_ms.frag +++ b/src/dxvk/shaders/dxvk_copy_color_ms.frag @@ -13,7 +13,9 @@ uniform u_info_t { } u_info; void main() { - o_color = texelFetch(s_image, + vec4 color = texelFetch(s_image, ivec3(gl_FragCoord.xy + u_info.offset, gl_Layer), gl_SampleID); -} \ No newline at end of file + o_color = color; + gl_FragDepth = color.r; +} diff --git a/src/dxvk/shaders/dxvk_copy_depth_1d.frag b/src/dxvk/shaders/dxvk_copy_depth_1d.frag deleted file mode 100644 index acf627293..000000000 --- a/src/dxvk/shaders/dxvk_copy_depth_1d.frag +++ /dev/null @@ -1,16 +0,0 @@ -#version 450 - -#extension GL_EXT_samplerless_texture_functions : require - -layout(set = 0, binding = 0) -uniform texture1DArray s_image; - -layout(push_constant) -uniform u_info_t { - ivec2 offset; -} u_info; - -void main() { - gl_FragDepth = texelFetch(s_image, - ivec2(gl_FragCoord.x + u_info.offset.x, gl_Layer), 0).r; -} \ No newline at end of file diff --git a/src/dxvk/shaders/dxvk_copy_depth_2d.frag b/src/dxvk/shaders/dxvk_copy_depth_2d.frag deleted file mode 100644 index dabcd4a56..000000000 --- a/src/dxvk/shaders/dxvk_copy_depth_2d.frag +++ /dev/null @@ -1,16 +0,0 @@ -#version 450 - -#extension GL_EXT_samplerless_texture_functions : require - -layout(set = 0, binding = 0) -uniform texture2DArray s_image; - -layout(push_constant) -uniform u_info_t { - ivec2 offset; -} u_info; - -void main() { - gl_FragDepth = texelFetch(s_image, - ivec3(gl_FragCoord.xy + u_info.offset, gl_Layer), 0).r; -} \ No newline at end of file diff --git a/src/dxvk/shaders/dxvk_copy_depth_ms.frag b/src/dxvk/shaders/dxvk_copy_depth_ms.frag deleted file mode 100644 index 44c07c305..000000000 --- a/src/dxvk/shaders/dxvk_copy_depth_ms.frag +++ /dev/null @@ -1,17 +0,0 @@ -#version 450 - -#extension GL_EXT_samplerless_texture_functions : require - -layout(set = 0, binding = 0) -uniform texture2DMSArray s_image; - -layout(push_constant) -uniform u_info_t { - ivec2 offset; -} u_info; - -void main() { - gl_FragDepth = texelFetch(s_image, - ivec3(gl_FragCoord.xy + u_info.offset, gl_Layer), - gl_SampleID).r; -} \ No newline at end of file From 70e34dc31cf6e3c24e0d37064a7e1611822bd41c Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Tue, 19 Mar 2024 18:55:34 +0100 Subject: [PATCH 107/246] [dxvk] Support arbitrary source formats for color<->depth copies Fixes rendering bugs in War Thunder. --- src/dxvk/dxvk_context.cpp | 102 +++++++++++++++++++++--------------- src/dxvk/dxvk_context.h | 1 + src/dxvk/dxvk_meta_copy.cpp | 39 +++++++------- src/dxvk/dxvk_meta_copy.h | 20 +++++-- 4 files changed, 96 insertions(+), 66 deletions(-) diff --git a/src/dxvk/dxvk_context.cpp b/src/dxvk/dxvk_context.cpp index 9bb9c92c4..dcaa7cb70 100644 --- a/src/dxvk/dxvk_context.cpp +++ b/src/dxvk/dxvk_context.cpp @@ -3563,69 +3563,90 @@ namespace dxvk { VkImageSubresourceLayers srcSubresource, VkOffset3D srcOffset, VkExtent3D extent) { - VkFormat viewFormat = m_common->metaCopy().getCopyDestinationFormat( - dstSubresource.aspectMask, - srcSubresource.aspectMask, - srcImage->info().format); + DxvkMetaCopyFormats viewFormats = m_common->metaCopy().getFormats( + dstImage->info().format, dstSubresource.aspectMask, + srcImage->info().format, srcSubresource.aspectMask); - if (!viewFormat) { - Logger::err("DxvkContext: copyImageFb: Unsupported format"); - return; - } - // Usually we should be able to draw directly to the destination image, // but in some cases this might not be possible, e.g. if when copying // from something like D32_SFLOAT to RGBA8_UNORM. In those situations, // create a temporary image to draw to, and then copy to the actual // destination image using a regular Vulkan transfer function. - bool useDirectCopy = (dstImage->info().usage & (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) - && (dstImage->isViewCompatible(viewFormat)); + bool dstIsCompatible = (dstImage->info().usage & (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) + && (dstImage->isViewCompatible(viewFormats.dstFormat)); + bool srcIsCompatible = (srcImage->info().usage & (VK_IMAGE_USAGE_SAMPLED_BIT)) + && (srcImage->isViewCompatible(viewFormats.srcFormat)); - if (useDirectCopy) { + if (dstIsCompatible && srcIsCompatible) { this->copyImageFbDirect( - dstImage, dstSubresource, dstOffset, viewFormat, - srcImage, srcSubresource, srcOffset, extent); - } else { + dstImage, dstSubresource, dstOffset, viewFormats.dstFormat, + srcImage, srcSubresource, srcOffset, viewFormats.srcFormat, extent); + } else if (dstIsCompatible || srcIsCompatible) { DxvkImageCreateInfo imageInfo = dstImage->info(); - imageInfo.format = viewFormat; imageInfo.flags = 0; - imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT; imageInfo.extent = extent; - imageInfo.numLayers = dstSubresource.layerCount; imageInfo.mipLevels = 1; imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; - imageInfo.layout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; - imageInfo.stages = VK_PIPELINE_STAGE_TRANSFER_BIT; - imageInfo.access = VK_ACCESS_TRANSFER_READ_BIT; imageInfo.viewFormatCount = 0; - if (dstImage->formatInfo()->aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) { - imageInfo.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; - imageInfo.stages |= VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - imageInfo.access |= VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - } else { - imageInfo.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; - imageInfo.stages |= VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT - | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; - imageInfo.access |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + if (!dstIsCompatible) { + imageInfo.format = viewFormats.dstFormat; + imageInfo.numLayers = dstSubresource.layerCount; + imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT; + imageInfo.layout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + imageInfo.stages = VK_PIPELINE_STAGE_TRANSFER_BIT; + imageInfo.access = VK_ACCESS_TRANSFER_READ_BIT; + + if (dstImage->formatInfo()->aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) { + imageInfo.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + imageInfo.stages |= VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + imageInfo.access |= VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + } else { + imageInfo.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; + imageInfo.stages |= VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT + | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; + imageInfo.access |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + } + } else /* if (!srcIsCompatible) */ { + imageInfo.format = viewFormats.srcFormat; + imageInfo.numLayers = srcSubresource.layerCount; + imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; + imageInfo.layout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + imageInfo.stages = VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + imageInfo.access = VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT; } - Rc tmpImage = m_device->createImage(imageInfo, - VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + Rc tmpImage = m_device->createImage(imageInfo, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); - VkImageSubresourceLayers tmpSubresource = dstSubresource; + VkImageSubresourceLayers tmpSubresource = { }; + tmpSubresource.aspectMask = tmpImage->formatInfo()->aspectMask; tmpSubresource.mipLevel = 0; tmpSubresource.baseArrayLayer = 0; + tmpSubresource.layerCount = imageInfo.numLayers; VkOffset3D tmpOffset = { 0, 0, 0 }; - this->copyImageFbDirect( - tmpImage, tmpSubresource, tmpOffset, viewFormat, - srcImage, srcSubresource, srcOffset, extent); + if (!dstIsCompatible) { + this->copyImageFbDirect( + tmpImage, tmpSubresource, tmpOffset, viewFormats.dstFormat, + srcImage, srcSubresource, srcOffset, viewFormats.srcFormat, extent); - this->copyImageHw( - dstImage, dstSubresource, dstOffset, - tmpImage, tmpSubresource, tmpOffset, extent); + this->copyImageHw( + dstImage, dstSubresource, dstOffset, + tmpImage, tmpSubresource, tmpOffset, extent); + } else /* if (!srcIsCompatible) */ { + this->copyImageHw( + tmpImage, tmpSubresource, tmpOffset, + srcImage, srcSubresource, srcOffset, extent); + + this->copyImageFbDirect( + dstImage, dstSubresource, dstOffset, viewFormats.dstFormat, + tmpImage, tmpSubresource, tmpOffset, viewFormats.srcFormat, extent); + } + } else { + Logger::err(str::format("DxvkContext: copyImageFb: Unsupported operation:\n" + " srcFormat = ", srcImage->info().format, " (aspect ", srcSubresource.aspectMask, ")\n", + " dstFormat = ", dstImage->info().format, " (aspect ", dstSubresource.aspectMask, ")")); } } @@ -3638,6 +3659,7 @@ namespace dxvk { const Rc& srcImage, VkImageSubresourceLayers srcSubresource, VkOffset3D srcOffset, + VkFormat srcFormat, VkExtent3D extent) { this->invalidateState(); @@ -3701,8 +3723,6 @@ namespace dxvk { m_execAcquires.recordCommands(m_cmd); // Create source and destination image views - VkFormat srcFormat = srcImage->info().format; - Rc views = new DxvkMetaCopyViews(m_device->vkd(), dstImage, dstSubresource, dstFormat, srcImage, srcSubresource, srcFormat); diff --git a/src/dxvk/dxvk_context.h b/src/dxvk/dxvk_context.h index b1fbc7dfe..3b61d4742 100644 --- a/src/dxvk/dxvk_context.h +++ b/src/dxvk/dxvk_context.h @@ -1506,6 +1506,7 @@ namespace dxvk { const Rc& srcImage, VkImageSubresourceLayers srcSubresource, VkOffset3D srcOffset, + VkFormat srcFormat, VkExtent3D extent); bool copyImageClear( diff --git a/src/dxvk/dxvk_meta_copy.cpp b/src/dxvk/dxvk_meta_copy.cpp index 45339af7f..5b33d0f80 100644 --- a/src/dxvk/dxvk_meta_copy.cpp +++ b/src/dxvk/dxvk_meta_copy.cpp @@ -120,32 +120,29 @@ namespace dxvk { } - VkFormat DxvkMetaCopyObjects::getCopyDestinationFormat( + DxvkMetaCopyFormats DxvkMetaCopyObjects::getFormats( + VkFormat dstFormat, VkImageAspectFlags dstAspect, - VkImageAspectFlags srcAspect, - VkFormat srcFormat) const { - if (srcAspect == dstAspect) - return srcFormat; - - if (dstAspect == VK_IMAGE_ASPECT_COLOR_BIT - && srcAspect == VK_IMAGE_ASPECT_DEPTH_BIT) { + VkFormat srcFormat, + VkImageAspectFlags srcAspect) const { + if (dstAspect == srcAspect) + return { dstFormat, srcFormat }; + + if (dstAspect == VK_IMAGE_ASPECT_COLOR_BIT && srcAspect == VK_IMAGE_ASPECT_DEPTH_BIT) { switch (srcFormat) { - case VK_FORMAT_D16_UNORM: return VK_FORMAT_R16_UNORM; - case VK_FORMAT_D32_SFLOAT: return VK_FORMAT_R32_SFLOAT; - default: return VK_FORMAT_UNDEFINED; + case VK_FORMAT_D16_UNORM: return { VK_FORMAT_R16_UNORM, VK_FORMAT_D16_UNORM }; + case VK_FORMAT_D32_SFLOAT: return { VK_FORMAT_R32_SFLOAT, VK_FORMAT_D32_SFLOAT }; + default: return { VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED }; + } + } else if (dstAspect == VK_IMAGE_ASPECT_DEPTH_BIT && srcAspect == VK_IMAGE_ASPECT_COLOR_BIT) { + switch (dstFormat) { + case VK_FORMAT_D16_UNORM: return { VK_FORMAT_D16_UNORM, VK_FORMAT_R16_UNORM }; + case VK_FORMAT_D32_SFLOAT: return { VK_FORMAT_D32_SFLOAT, VK_FORMAT_R32_SFLOAT }; + default: return { VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED }; } } - if (dstAspect == VK_IMAGE_ASPECT_DEPTH_BIT - && srcAspect == VK_IMAGE_ASPECT_COLOR_BIT) { - switch (srcFormat) { - case VK_FORMAT_R16_UNORM: return VK_FORMAT_D16_UNORM; - case VK_FORMAT_R32_SFLOAT: return VK_FORMAT_D32_SFLOAT; - default: return VK_FORMAT_UNDEFINED; - } - } - - return VK_FORMAT_UNDEFINED; + return { VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED }; } diff --git a/src/dxvk/dxvk_meta_copy.h b/src/dxvk/dxvk_meta_copy.h index c32dbc0d6..3584ad7ed 100644 --- a/src/dxvk/dxvk_meta_copy.h +++ b/src/dxvk/dxvk_meta_copy.h @@ -25,6 +25,14 @@ namespace dxvk { VkExtent2D srcSize; }; + /** + * \brief Pair of view formats for copy operation + */ + struct DxvkMetaCopyFormats { + VkFormat dstFormat; + VkFormat srcFormat; + }; + /** * \brief Copy pipeline * @@ -122,13 +130,17 @@ namespace dxvk { * Returns the color format that we need to use * as the destination image view format in case * of depth to color image copies. - * \param [in] format Depth format + * \param [in] dstFormat Destination image format + * \param [in] dstAspect Destination aspect mask + * \param [in] srcFormat Source image format + * \param [in] srcAspect Source aspect mask * \returns Corresponding color format */ - VkFormat getCopyDestinationFormat( + DxvkMetaCopyFormats getFormats( + VkFormat dstFormat, VkImageAspectFlags dstAspect, - VkImageAspectFlags srcAspect, - VkFormat srcFormat) const; + VkFormat srcFormat, + VkImageAspectFlags srcAspect) const; /** * \brief Creates pipeline for meta copy operation From cbf51a7a2547bd618ff455c517ed828d574f8c76 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Wed, 20 Mar 2024 13:30:31 +0100 Subject: [PATCH 108/246] [d3d11] Enable copy usage for typeless images as necessary --- src/d3d11/d3d11_texture.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/d3d11/d3d11_texture.cpp b/src/d3d11/d3d11_texture.cpp index b75908e12..4bf439892 100644 --- a/src/d3d11/d3d11_texture.cpp +++ b/src/d3d11/d3d11_texture.cpp @@ -214,9 +214,12 @@ namespace dxvk { // For some formats, we need to enable sampled and/or // render target capabilities if available, but these // should in no way affect the default image layout - imageInfo.usage |= EnableMetaCopyUsage(imageInfo.format, imageInfo.tiling); imageInfo.usage |= EnableMetaPackUsage(imageInfo.format, m_desc.CPUAccessFlags); - + imageInfo.usage |= EnableMetaCopyUsage(imageInfo.format, imageInfo.tiling); + + for (uint32_t i = 0; i < imageInfo.viewFormatCount; i++) + imageInfo.usage |= EnableMetaCopyUsage(imageInfo.viewFormats[i], imageInfo.tiling); + // Check if we can actually create the image if (!CheckImageSupport(&imageInfo, imageInfo.tiling)) { throw DxvkError(str::format( From 037d0fa1ad093065b18dad968d5898cbcd143e0e Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Wed, 20 Mar 2024 13:48:30 +0100 Subject: [PATCH 109/246] [meta] Release 2.3.1 --- RELEASE | 2 +- meson.build | 2 +- src/dxvk/dxvk_instance.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/RELEASE b/RELEASE index bb576dbde..2bf1c1ccf 100644 --- a/RELEASE +++ b/RELEASE @@ -1 +1 @@ -2.3 +2.3.1 diff --git a/meson.build b/meson.build index e9e62a7d7..e35a9de02 100644 --- a/meson.build +++ b/meson.build @@ -1,4 +1,4 @@ -project('dxvk', ['c', 'cpp'], version : 'v2.3', meson_version : '>= 0.58', default_options : [ 'cpp_std=c++17', 'warning_level=2' ]) +project('dxvk', ['c', 'cpp'], version : 'v2.3.1', meson_version : '>= 0.58', default_options : [ 'cpp_std=c++17', 'warning_level=2' ]) cpu_family = target_machine.cpu_family() platform = target_machine.system() diff --git a/src/dxvk/dxvk_instance.cpp b/src/dxvk/dxvk_instance.cpp index 55ba52458..e86896d86 100644 --- a/src/dxvk/dxvk_instance.cpp +++ b/src/dxvk/dxvk_instance.cpp @@ -177,7 +177,7 @@ namespace dxvk { appInfo.pApplicationName = appName.c_str(); appInfo.applicationVersion = flags.raw(); appInfo.pEngineName = "DXVK"; - appInfo.engineVersion = VK_MAKE_VERSION(2, 3, 0); + appInfo.engineVersion = VK_MAKE_VERSION(2, 3, 1); appInfo.apiVersion = VK_MAKE_VERSION(1, 3, 0); VkInstanceCreateInfo info = { VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO }; From 274248654090aa23f96d2b073c5674261931a29b Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Thu, 21 Mar 2024 15:32:48 +0100 Subject: [PATCH 110/246] [dxvk] Don't query color space support for null surfaces Fixes a crash in games with the dxgi.deferSurfaceCreation workaround set. Note that this potentially breaks HDR. --- src/dxvk/dxvk_presenter.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/dxvk/dxvk_presenter.cpp b/src/dxvk/dxvk_presenter.cpp index 10f13da27..07749b38e 100644 --- a/src/dxvk/dxvk_presenter.cpp +++ b/src/dxvk/dxvk_presenter.cpp @@ -382,6 +382,9 @@ namespace dxvk { bool Presenter::supportsColorSpace(VkColorSpaceKHR colorspace) { + if (!m_surface) + return false; + std::vector surfaceFormats; getSupportedFormats(surfaceFormats, VK_FULL_SCREEN_EXCLUSIVE_DEFAULT_EXT); From 28c7c09bf58fe98cc999e5395551a7ca7d2ea927 Mon Sep 17 00:00:00 2001 From: Blisto91 Date: Wed, 20 Mar 2024 19:51:29 +0100 Subject: [PATCH 111/246] [dxgi] Remove useMonitorFallback option QueryDisplayConfig optimization is now in Proton 9 Wine --- dxvk.conf | 12 ------------ src/dxgi/dxgi_factory.cpp | 3 --- src/dxgi/dxgi_options.cpp | 4 ---- src/dxgi/dxgi_options.h | 3 --- src/util/config/config.cpp | 12 ------------ 5 files changed, 34 deletions(-) diff --git a/dxvk.conf b/dxvk.conf index 44b1be67b..4f1a443a4 100644 --- a/dxvk.conf +++ b/dxvk.conf @@ -644,18 +644,6 @@ # d3d9.textureMemory = 100 -# Always enumerate all monitors on each dxgi output -# -# Used to avoid performance degradation in some games -# (will be deprecated once QueryDisplayConfig optimization -# is in Proton Wine). -# -# Supported values: -# - True/False - -# dxgi.useMonitorFallback = False - - # Hide integrated graphics from applications # # Only has an effect when dedicated GPUs are present on the system. It is diff --git a/src/dxgi/dxgi_factory.cpp b/src/dxgi/dxgi_factory.cpp index 24824c9d1..fe4943a91 100644 --- a/src/dxgi/dxgi_factory.cpp +++ b/src/dxgi/dxgi_factory.cpp @@ -124,13 +124,10 @@ namespace dxvk { } } - // If any monitors are left on the list, enable the // fallback to always enumerate all monitors. if ((m_monitorFallback = !monitors.empty())) Logger::warn("DXGI: Found monitors not associated with any adapter, using fallback"); - else - m_monitorFallback = m_options.useMonitorFallback; } diff --git a/src/dxgi/dxgi_options.cpp b/src/dxgi/dxgi_options.cpp index 5bc98da6a..6d0e0b07b 100644 --- a/src/dxgi/dxgi_options.cpp +++ b/src/dxgi/dxgi_options.cpp @@ -100,10 +100,6 @@ namespace dxvk { Logger::info("HDR was configured to be enabled, but has been force disabled as a UE4 DX11 game was detected."); this->enableHDR = false; } - - this->useMonitorFallback = config.getOption("dxgi.useMonitorFallback", env::getEnvVar("DXVK_MONITOR_FALLBACK") == "1"); - if (this->useMonitorFallback) - Logger::info("Enabled useMonitorFallback option"); } } diff --git a/src/dxgi/dxgi_options.h b/src/dxgi/dxgi_options.h index c952ea5e4..a22de94f1 100644 --- a/src/dxgi/dxgi_options.h +++ b/src/dxgi/dxgi_options.h @@ -49,9 +49,6 @@ namespace dxvk { /// Enable HDR bool enableHDR; - /// Use monitor fallback to enumerating all monitors per output - bool useMonitorFallback; - /// Sync interval. Overrides the value /// passed to IDXGISwapChain::Present. int32_t syncInterval; diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index 698b69cb9..3c45a825b 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -411,11 +411,6 @@ namespace dxvk { { R"(\\RidersRepublic(_BE)?\.exe$)", {{ { "dxgi.hideAmdGpu", "True" }, }} }, - /* HoloCure - Save the Fans! - Same as Cyberpunk 2077 */ - { R"(\\HoloCure\.exe$)", {{ - { "dxgi.useMonitorFallback", "True" }, - }} }, /* Kenshi * * Helps CPU bound performance */ { R"(\\kenshi_x64\.exe$)", {{ @@ -901,13 +896,6 @@ namespace dxvk { { R"(\\RiftApart\.exe$)", {{ { "dxgi.hideNvidiaGpu", "False" }, }} }, - /* CP2077 enumerates display outputs each frame. - * Avoid using QueryDisplayConfig to avoid - * performance degradation until the - * optimization of that function is in Proton. */ - { R"(\\Cyberpunk2077\.exe$)", {{ - { "dxgi.useMonitorFallback", "True" }, - }} }, /* Metro Exodus Enhanced Edition picks GPU adapters * by available VRAM, which causes issues on some * systems with integrated graphics. */ From 855b2746b61ad944759ae9e31bbb4ad7562e6912 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Tue, 26 Mar 2024 13:42:55 +0100 Subject: [PATCH 112/246] [util] Remove TRAHA Global config This game apparently no longer exists. --- src/util/config/config.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index 3c45a825b..5691c8c29 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -331,11 +331,6 @@ namespace dxvk { { R"(\\SonicFrontiers\.exe$)", {{ { "dxgi.maxFrameLatency", "1" }, }} }, - /* TRAHA Global * - * Shadow issues when it sees AMD/Nvidia */ - { R"(\\RapaNui-Win64-Shipping\.exe$)", {{ - { "dxgi.customVendorId", "8086" }, - }} }, /* SpellForce 3 Reforced & expansions * * Greatly improves CPU bound performance */ { R"(\\SF3ClientFinal\.exe$)", {{ From f06c6463157c72d294720812c5d5445abf3ceb71 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Wed, 3 Apr 2024 14:55:43 +0200 Subject: [PATCH 113/246] [dxbc] Remove broken atomic counter subgroup optimization This is not a legal optimization inside non-uniform control flow due to Vulkan's extremely permissive convergence rules, and apparently breaks on Nvidia as a result. Mesa drivers already do the same thing internally anyway. --- src/dxbc/dxbc_compiler.cpp | 79 ++------------------------------------ src/dxbc/dxbc_options.cpp | 3 -- src/dxbc/dxbc_options.h | 4 -- 3 files changed, 4 insertions(+), 82 deletions(-) diff --git a/src/dxbc/dxbc_compiler.cpp b/src/dxbc/dxbc_compiler.cpp index c3a6718b3..8391d9f4b 100644 --- a/src/dxbc/dxbc_compiler.cpp +++ b/src/dxbc/dxbc_compiler.cpp @@ -2464,58 +2464,6 @@ namespace dxvk { if (m_uavs.at(registerId).ctrId == 0) m_uavs.at(registerId).ctrId = emitDclUavCounter(registerId); - // Only use subgroup ops on compute to avoid having to - // deal with helper invocations or hardware limitations - bool useSubgroupOps = m_moduleInfo.options.useSubgroupOpsForAtomicCounters - && m_programInfo.type() == DxbcProgramType::ComputeShader; - - // Current block ID used in a phi later on - uint32_t baseBlockId = m_module.getBlockId(); - - // In case we have subgroup ops enabled, we need to - // count the number of active lanes, the lane index, - // and we need to perform the atomic op conditionally - uint32_t laneCount = 0; - uint32_t laneIndex = 0; - - DxbcConditional elect; - - if (useSubgroupOps) { - m_module.enableCapability(spv::CapabilityGroupNonUniform); - m_module.enableCapability(spv::CapabilityGroupNonUniformBallot); - - uint32_t ballot = m_module.opGroupNonUniformBallot( - getVectorTypeId({ DxbcScalarType::Uint32, 4 }), - m_module.constu32(spv::ScopeSubgroup), - m_module.constBool(true)); - - laneCount = m_module.opGroupNonUniformBallotBitCount( - getScalarTypeId(DxbcScalarType::Uint32), - m_module.constu32(spv::ScopeSubgroup), - spv::GroupOperationReduce, ballot); - - laneIndex = m_module.opGroupNonUniformBallotBitCount( - getScalarTypeId(DxbcScalarType::Uint32), - m_module.constu32(spv::ScopeSubgroup), - spv::GroupOperationExclusiveScan, ballot); - - // Elect one lane to perform the atomic op - uint32_t election = m_module.opGroupNonUniformElect( - m_module.defBoolType(), - m_module.constu32(spv::ScopeSubgroup)); - - elect.labelIf = m_module.allocateId(); - elect.labelEnd = m_module.allocateId(); - - m_module.opSelectionMerge(elect.labelEnd, spv::SelectionControlMaskNone); - m_module.opBranchConditional(election, elect.labelIf, elect.labelEnd); - - m_module.opLabel(elect.labelIf); - } else { - // We're going to use this for the increment - laneCount = m_module.constu32(1); - } - // Get a pointer to the atomic counter in question DxbcRegisterInfo ptrType; ptrType.type.ctype = DxbcScalarType::Uint32; @@ -2547,13 +2495,14 @@ namespace dxvk { switch (ins.op) { case DxbcOpcode::ImmAtomicAlloc: value.id = m_module.opAtomicIAdd(typeId, ptrId, - scopeId, semanticsId, laneCount); + scopeId, semanticsId, m_module.constu32(1)); break; case DxbcOpcode::ImmAtomicConsume: value.id = m_module.opAtomicISub(typeId, ptrId, - scopeId, semanticsId, laneCount); - value.id = m_module.opISub(typeId, value.id, laneCount); + scopeId, semanticsId, m_module.constu32(1)); + value.id = m_module.opISub(typeId, value.id, + m_module.constu32(1)); break; default: @@ -2563,26 +2512,6 @@ namespace dxvk { return; } - // If we're using subgroup ops, we have to broadcast - // the result of the atomic op and compute the index - if (useSubgroupOps) { - m_module.opBranch(elect.labelEnd); - m_module.opLabel (elect.labelEnd); - - uint32_t undef = m_module.constUndef(typeId); - - std::array phiLabels = {{ - { value.id, elect.labelIf }, - { undef, baseBlockId }, - }}; - - value.id = m_module.opPhi(typeId, - phiLabels.size(), phiLabels.data()); - value.id = m_module.opGroupNonUniformBroadcastFirst(typeId, - m_module.constu32(spv::ScopeSubgroup), value.id); - value.id = m_module.opIAdd(typeId, value.id, laneIndex); - } - // Store the result emitRegisterStore(ins.dst[0], value); } diff --git a/src/dxbc/dxbc_options.cpp b/src/dxbc/dxbc_options.cpp index 3cdf2833e..e55a0f945 100644 --- a/src/dxbc/dxbc_options.cpp +++ b/src/dxbc/dxbc_options.cpp @@ -17,9 +17,6 @@ namespace dxvk { useDepthClipWorkaround = !devFeatures.extDepthClipEnable.depthClipEnable; - useSubgroupOpsForAtomicCounters - = (devInfo.vk11.subgroupSupportedStages & VK_SHADER_STAGE_COMPUTE_BIT) - && (devInfo.vk11.subgroupSupportedOperations & VK_SUBGROUP_FEATURE_BALLOT_BIT); VkFormatFeatureFlags2 r32Features = device->getFormatFeatures(VK_FORMAT_R32_SFLOAT).optimal diff --git a/src/dxbc/dxbc_options.h b/src/dxbc/dxbc_options.h index 6ea5eccb2..27ecca1ff 100644 --- a/src/dxbc/dxbc_options.h +++ b/src/dxbc/dxbc_options.h @@ -30,10 +30,6 @@ namespace dxvk { /// Determines whether raw access chains are supported bool supportsRawAccessChains = false; - /// Use subgroup operations to reduce the number of - /// atomic operations for append/consume buffers. - bool useSubgroupOpsForAtomicCounters = false; - /// Clear thread-group shared memory to zero bool zeroInitWorkgroupMemory = false; From 198bd3a4b8f1551b348a25c4b25d44c25251dc5e Mon Sep 17 00:00:00 2001 From: Blisto91 <47954800+Blisto91@users.noreply.github.com> Date: Wed, 3 Apr 2024 19:41:02 +0200 Subject: [PATCH 114/246] [d3d11] Remove missed Shared Keyedmutex warning D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX is implemented. --- src/d3d11/d3d11_texture.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/d3d11/d3d11_texture.cpp b/src/d3d11/d3d11_texture.cpp index 4bf439892..13e2c00b0 100644 --- a/src/d3d11/d3d11_texture.cpp +++ b/src/d3d11/d3d11_texture.cpp @@ -58,9 +58,6 @@ namespace dxvk { "\n MiscFlags: ", m_desc.MiscFlags, "\n FeatureLevel: ", pDevice->GetFeatureLevel())); - if (m_desc.MiscFlags & D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX) - Logger::warn("D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX: not supported."); - imageInfo.shared = true; imageInfo.sharing.mode = hSharedHandle == INVALID_HANDLE_VALUE ? DxvkSharedHandleMode::Export : DxvkSharedHandleMode::Import; imageInfo.sharing.type = (m_desc.MiscFlags & D3D11_RESOURCE_MISC_SHARED_NTHANDLE) From 49e9ea5f5a78fd635042d442e5781097bbdd6c37 Mon Sep 17 00:00:00 2001 From: Casey Bowman Date: Tue, 19 Mar 2024 12:51:14 -0700 Subject: [PATCH 115/246] [dxgi] Force vendor ID change when XeSS is detected on an Intel GPU Games using libxess.dll or wrapper modules will crash. To work around this, we hide the Intel GPU's vendor ID to avoid using the XeSS module. --- src/dxgi/dxgi_options.cpp | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/dxgi/dxgi_options.cpp b/src/dxgi/dxgi_options.cpp index 6d0e0b07b..d7d55d4e8 100644 --- a/src/dxgi/dxgi_options.cpp +++ b/src/dxgi/dxgi_options.cpp @@ -26,6 +26,21 @@ namespace dxvk { return id; } + /* First generation XeSS causes crash on proton for Intel due to missing + * Intel interface. Avoid crash by pretending to be non-Intel if the + * libxess.dll module is loaded by an application. + */ + static bool isXessUsed() { +#ifdef _WIN32 + if (GetModuleHandleA("libxess") != nullptr || + GetModuleHandleA("libxess_dx11") != nullptr) + return true; + else + return false; +#else + return false; +#endif + } static bool isNvapiEnabled() { return env::getEnvVar("DXVK_ENABLE_NVAPI") == "1"; @@ -95,6 +110,12 @@ namespace dxvk { this->hideAmdGpu = config.getOption("dxgi.hideAmdGpu", Tristate::Auto) == Tristate::True; this->hideIntelGpu = config.getOption("dxgi.hideIntelGpu", Tristate::Auto) == Tristate::True; + /* Force vendor ID to non-Intel ID when XeSS is in use */ + if (isXessUsed()) { + Logger::info(str::format("Detected XeSS usage, hiding Intel GPU Vendor")); + this->hideIntelGpu = true; + } + this->enableHDR = config.getOption("dxgi.enableHDR", env::getEnvVar("DXVK_HDR") == "1"); if (this->enableHDR && isHDRDisallowed()) { Logger::info("HDR was configured to be enabled, but has been force disabled as a UE4 DX11 game was detected."); From 44695f93112060a2093bf5f49cbdbe37a4882356 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Mon, 8 Apr 2024 15:40:25 +0200 Subject: [PATCH 116/246] [dxvk] Adjust desciptor pool reset heuristic Drastically limits the amount of descriptor memory we allocate in situations where an application renders without presenting anything to a swap chain. The new limit is a bit tight for some real-world use cases (e.g. Ashes of the Singularity), but at worst we will start calling vkAllocateDescriptorSets once per set and draw. --- src/dxvk/dxvk_descriptor.cpp | 23 +++++++++-------------- src/dxvk/dxvk_descriptor.h | 4 +--- 2 files changed, 10 insertions(+), 17 deletions(-) diff --git a/src/dxvk/dxvk_descriptor.cpp b/src/dxvk/dxvk_descriptor.cpp index 56ea85a4f..7c2a72f5b 100644 --- a/src/dxvk/dxvk_descriptor.cpp +++ b/src/dxvk/dxvk_descriptor.cpp @@ -72,7 +72,7 @@ namespace dxvk { // memory bloat. This may be necessary for off-screen // rendering applications, or in situations where games // pre-render a lot of images without presenting in between. - return m_descriptorPools.size() >= 8; + return m_descriptorPools.size() > MaxDesiredPoolCount; } @@ -100,29 +100,25 @@ namespace dxvk { void DxvkDescriptorPool::reset() { - // As a heuristic to save memory, check how many descriptors - // have actively been used in the past couple of submissions. - bool isLowUsageFrame = false; - + // As a heuristic to save memory, check how many descriptor + // sets were actually being used in past submissions. size_t poolCount = m_descriptorPools.size(); + bool needsReset = poolCount > MaxDesiredPoolCount; if (poolCount > 1 || m_setsAllocated > m_manager->getMaxSetCount() / 2) { double factor = std::max(11.0 / 3.0 - double(poolCount) / 3.0, 1.0); - isLowUsageFrame = double(m_setsUsed) * factor < double(m_setsAllocated); + needsReset = double(m_setsUsed) * factor < double(m_setsAllocated); } - m_lowUsageFrames = isLowUsageFrame - ? m_lowUsageFrames + 1 - : 0; m_setsUsed = 0; - if (m_lowUsageFrames < 16) { + if (!needsReset) { for (auto& entry : m_setLists) entry.second.reset(); } else { - // If most sets are no longer being used, reset and destroy - // descriptor pools and reset all lookup tables in order to - // accomodate more descriptors of different layouts. + // If most sets are no longer needed, reset and destroy + // descriptor pools and reset all lookup tables in order + // to accomodate more descriptors of different layouts. for (auto pool : m_descriptorPools) m_manager->recycleVulkanDescriptorPool(pool); @@ -131,7 +127,6 @@ namespace dxvk { m_setMaps.clear(); m_setsAllocated = 0; - m_lowUsageFrames = 0; } m_cachedEntry = { nullptr, nullptr }; diff --git a/src/dxvk/dxvk_descriptor.h b/src/dxvk/dxvk_descriptor.h index 95d2c045f..52c088fab 100644 --- a/src/dxvk/dxvk_descriptor.h +++ b/src/dxvk/dxvk_descriptor.h @@ -80,7 +80,7 @@ namespace dxvk { * to be updated. */ class DxvkDescriptorPool : public RcObject { - + constexpr static uint32_t MaxDesiredPoolCount = 2; public: DxvkDescriptorPool( @@ -155,8 +155,6 @@ namespace dxvk { uint32_t m_prevSetsAllocated = 0; - uint32_t m_lowUsageFrames = 0; - DxvkDescriptorSetMap* getSetMapCached( const DxvkBindingLayoutObjects* layout); From 133f0794bcf586df928fb8a06a696fccfb8f8c09 Mon Sep 17 00:00:00 2001 From: Martino Fontana Date: Sat, 6 Apr 2024 16:27:04 +0200 Subject: [PATCH 117/246] [util] Remove framerate limiter for Nier Replicant Without mods, Nier Replicant runs faster when going above 60 FPS. The game had an official patch that implemented a framerate limiter to prevent this. This limiter is terrible, because it's not a stable 60 FPS, but a weird 57-58 FPS. There's no way to disable this in-game, it has to be done by editing a config file or through a mod. So, why remove the default frame limiter from DXVK? - In the default case (a user plays the game as it is), it does nothing, since 57-58 is lower than 60. - If a user is going out of their way to edit the config file, why would they assume that DXVK already provides a frame limiter? They are going to follow [a guide](https://www.pcgamingwiki.com/wiki/NieR_Replicant#Framerate_limited_to_57.7E58_FPS) that says to set up a frame limiter, it doesn't say "set up a frame limiter, unless you are using DXVK, in that case it's already there". - They are using [Special K in order to use a mod to play at high refresh rates at normal speed](https://wiki.special-k.info/SpecialK/Custom/Replicant). In this case, DXVK's default limiter is harmful, since it is not documented that it's there by default. Since this default limiter is useless in the first two cases and harmful in the third, I think it should be removed. The alternative would be to document this (e.g. in PCGamingWiki), but the instructions wouldn't look pretty... "After following the instructions to use Special K in order to play at higher framerates at normal speed, if are using DXVK/Proton, also do these things to disable its default 60 FPS cap: [...]" Especially because that the game isn't broken in the default case, I don't think DXVK should tamper with these things in a way that requires documentation to revert. Tested Special K's mod to play at higher refresh rates on Linux. --- src/util/config/config.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index 5691c8c29..9e6c86967 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -122,8 +122,6 @@ namespace dxvk { }} }, /* NieR Replicant */ { R"(\\NieR Replicant ver\.1\.22474487139\.exe)", {{ - { "dxgi.syncInterval", "1" }, - { "dxgi.maxFrameRate", "60" }, { "d3d11.cachedDynamicResources", "vi" }, }} }, /* SteamVR performance test */ From 571948cfc0a58bb92b9e97ff494ba4c9403b7aa2 Mon Sep 17 00:00:00 2001 From: WinterSnowfall Date: Sat, 13 Apr 2024 20:45:09 +0300 Subject: [PATCH 118/246] [d3d9] Remove support for VERTEXSTATS queries --- src/d3d9/d3d9_query.cpp | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/d3d9/d3d9_query.cpp b/src/d3d9/d3d9_query.cpp index 5eebdb0d6..57e838443 100644 --- a/src/d3d9/d3d9_query.cpp +++ b/src/d3d9/d3d9_query.cpp @@ -41,11 +41,6 @@ namespace dxvk { case D3DQUERYTYPE_TIMESTAMPFREQ: break; - case D3DQUERYTYPE_VERTEXSTATS: - m_query[0] = dxvkDevice->createGpuQuery( - VK_QUERY_TYPE_PIPELINE_STATISTICS, 0, 0); - break; - default: throw DxvkError(str::format("D3D9Query: Unsupported query type ", m_queryType)); } @@ -246,11 +241,6 @@ namespace dxvk { m_dataCache.TimestampFreq = GetTimestampQueryFrequency(); break; - case D3DQUERYTYPE_VERTEXSTATS: - m_dataCache.VertexStats.NumRenderedTriangles = queryData[0].statistic.iaPrimitives; - m_dataCache.VertexStats.NumExtraClippingTriangles = queryData[0].statistic.clipPrimitives; - break; - default: break; } @@ -276,7 +266,6 @@ namespace dxvk { void D3D9Query::Begin(DxvkContext* ctx) { switch (m_queryType) { case D3DQUERYTYPE_OCCLUSION: - case D3DQUERYTYPE_VERTEXSTATS: ctx->beginQuery(m_query[0]); break; @@ -296,7 +285,6 @@ namespace dxvk { ctx->writeTimestamp(m_query[0]); break; - case D3DQUERYTYPE_VERTEXSTATS: case D3DQUERYTYPE_OCCLUSION: ctx->endQuery(m_query[0]); break; @@ -314,7 +302,6 @@ namespace dxvk { bool D3D9Query::QueryBeginnable(D3DQUERYTYPE QueryType) { return QueryType == D3DQUERYTYPE_OCCLUSION - || QueryType == D3DQUERYTYPE_VERTEXSTATS || QueryType == D3DQUERYTYPE_TIMESTAMPDISJOINT; } @@ -338,7 +325,6 @@ namespace dxvk { case D3DQUERYTYPE_TIMESTAMP: case D3DQUERYTYPE_TIMESTAMPDISJOINT: case D3DQUERYTYPE_TIMESTAMPFREQ: - case D3DQUERYTYPE_VERTEXSTATS: return D3D_OK; default: From 7441137a33bca1414f52dadeb848083b446e29b1 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Tue, 16 Apr 2024 14:48:52 +0200 Subject: [PATCH 119/246] [dxbc] Ignore system value components when declaring inputs --- src/dxbc/dxbc_compiler.cpp | 28 ++++++++++++++++++++++++++-- src/dxbc/dxbc_compiler.h | 3 +++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/src/dxbc/dxbc_compiler.cpp b/src/dxbc/dxbc_compiler.cpp index 8391d9f4b..6492671d0 100644 --- a/src/dxbc/dxbc_compiler.cpp +++ b/src/dxbc/dxbc_compiler.cpp @@ -7728,6 +7728,21 @@ namespace dxvk { return DxbcRegMask::firstN(getTexCoordDim(imageType)); } + + bool DxbcCompiler::ignoreInputSystemValue(DxbcSystemValue sv) const { + switch (sv) { + case DxbcSystemValue::Position: + case DxbcSystemValue::IsFrontFace: + case DxbcSystemValue::SampleIndex: + case DxbcSystemValue::PrimitiveId: + case DxbcSystemValue::Coverage: + return m_programInfo.type() == DxbcProgramType::PixelShader; + + default: + return false; + } + } + DxbcVectorType DxbcCompiler::getInputRegType(uint32_t regIdx) const { switch (m_programInfo.type()) { @@ -7758,8 +7773,17 @@ namespace dxvk { result.ctype = DxbcScalarType::Float32; result.ccount = 4; - if (m_isgn->findByRegister(regIdx)) - result.ccount = m_isgn->regMask(regIdx).minComponents(); + if (m_isgn == nullptr || !m_isgn->findByRegister(regIdx)) + return result; + + DxbcRegMask mask(0u); + + for (const auto& e : *m_isgn) { + if (e.registerId == regIdx && !ignoreInputSystemValue(e.systemValue)) + mask |= e.componentMask; + } + + result.ccount = mask.minComponents(); return result; } } diff --git a/src/dxbc/dxbc_compiler.h b/src/dxbc/dxbc_compiler.h index d23bc59f6..935c683d7 100644 --- a/src/dxbc/dxbc_compiler.h +++ b/src/dxbc/dxbc_compiler.h @@ -1221,6 +1221,9 @@ namespace dxvk { uint32_t getUavCoherence( uint32_t registerId, DxbcUavFlags flags); + + bool ignoreInputSystemValue( + DxbcSystemValue sv) const; /////////////////////////// // Type definition methods From 6ef98c613fa2f78861d2bb06f8e2af17ae2db0ef Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Tue, 16 Apr 2024 14:54:47 +0200 Subject: [PATCH 120/246] [dxvk] Re-enable maintenance4 feature Sileces some validation errors. --- src/dxvk/dxvk_adapter.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/dxvk/dxvk_adapter.cpp b/src/dxvk/dxvk_adapter.cpp index 7c0e79f5d..182f7f034 100644 --- a/src/dxvk/dxvk_adapter.cpp +++ b/src/dxvk/dxvk_adapter.cpp @@ -346,6 +346,11 @@ namespace dxvk { enabledFeatures.vk13.synchronization2 = VK_TRUE; enabledFeatures.vk13.dynamicRendering = VK_TRUE; + // Maintenance4 may cause performance problems on amdvlk in some cases + if (m_deviceInfo.vk12.driverID != VK_DRIVER_ID_AMD_OPEN_SOURCE + && m_deviceInfo.vk12.driverID != VK_DRIVER_ID_AMD_PROPRIETARY) + enabledFeatures.vk13.maintenance4 = VK_TRUE; + // We expose depth clip rather than depth clamp to client APIs enabledFeatures.extDepthClipEnable.depthClipEnable = m_deviceFeatures.extDepthClipEnable.depthClipEnable; From c2489d5a455664d587ed5ebd9e96599402161af4 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Tue, 16 Apr 2024 16:31:30 +0200 Subject: [PATCH 121/246] [dxbc] Fix array register anaylsis with multiple dst operands --- src/dxbc/dxbc_analysis.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/dxbc/dxbc_analysis.cpp b/src/dxbc/dxbc_analysis.cpp index 8a8f44b54..dd4a51324 100644 --- a/src/dxbc/dxbc_analysis.cpp +++ b/src/dxbc/dxbc_analysis.cpp @@ -87,9 +87,9 @@ namespace dxvk { } for (uint32_t i = 0; i < ins.dstCount; i++) { - if (ins.dst[0].type == DxbcOperandType::IndexableTemp) { - uint32_t index = ins.dst[0].idx[0].offset; - m_analysis->xRegMasks[index] |= ins.dst[0].mask; + if (ins.dst[i].type == DxbcOperandType::IndexableTemp) { + uint32_t index = ins.dst[i].idx[0].offset; + m_analysis->xRegMasks[index] |= ins.dst[i].mask; } } } From 617ebf4e050343b15943ce838499790ecbcdc03d Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Tue, 16 Apr 2024 17:40:29 +0200 Subject: [PATCH 122/246] [dxbc] Take used components into account for PS inputs --- src/dxbc/dxbc_chunk_isgn.cpp | 6 +++++- src/dxbc/dxbc_chunk_isgn.h | 1 + src/dxbc/dxbc_compiler.cpp | 10 +++++++++- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/dxbc/dxbc_chunk_isgn.cpp b/src/dxbc/dxbc_chunk_isgn.cpp index 47f4eef46..d456a8c0c 100644 --- a/src/dxbc/dxbc_chunk_isgn.cpp +++ b/src/dxbc/dxbc_chunk_isgn.cpp @@ -23,7 +23,11 @@ namespace dxvk { entry.systemValue = static_cast(reader.readu32()); entry.componentType = componentTypes.at(reader.readu32()); entry.registerId = reader.readu32(); - entry.componentMask = bit::extract(reader.readu32(), 0, 3); + + uint32_t mask = reader.readu32(); + + entry.componentMask = bit::extract(mask, 0, 3); + entry.componentUsed = bit::extract(mask, 8, 11); if (hasPrecision) reader.readu32(); diff --git a/src/dxbc/dxbc_chunk_isgn.h b/src/dxbc/dxbc_chunk_isgn.h index 1dce64add..87684016f 100644 --- a/src/dxbc/dxbc_chunk_isgn.h +++ b/src/dxbc/dxbc_chunk_isgn.h @@ -20,6 +20,7 @@ namespace dxvk { uint32_t semanticIndex; uint32_t registerId; DxbcRegMask componentMask; + DxbcRegMask componentUsed; DxbcScalarType componentType; DxbcSystemValue systemValue; uint32_t streamId; diff --git a/src/dxbc/dxbc_compiler.cpp b/src/dxbc/dxbc_compiler.cpp index 6492671d0..2fb15e262 100644 --- a/src/dxbc/dxbc_compiler.cpp +++ b/src/dxbc/dxbc_compiler.cpp @@ -7777,10 +7777,18 @@ namespace dxvk { return result; DxbcRegMask mask(0u); + DxbcRegMask used(0u); for (const auto& e : *m_isgn) { - if (e.registerId == regIdx && !ignoreInputSystemValue(e.systemValue)) + if (e.registerId == regIdx && !ignoreInputSystemValue(e.systemValue)) { mask |= e.componentMask; + used |= e.componentUsed; + } + } + + if (m_programInfo.type() == DxbcProgramType::PixelShader) { + if ((used.raw() & mask.raw()) == used.raw()) + mask = used; } result.ccount = mask.minComponents(); From 35157357dddd3c644fbed53523d54c87ccc81c20 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Fri, 19 Apr 2024 01:43:23 +0200 Subject: [PATCH 123/246] [dxvk] Fix stencil discard being broken --- src/dxvk/dxvk_context.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dxvk/dxvk_context.cpp b/src/dxvk/dxvk_context.cpp index dcaa7cb70..d24d919b2 100644 --- a/src/dxvk/dxvk_context.cpp +++ b/src/dxvk/dxvk_context.cpp @@ -1997,7 +1997,7 @@ namespace dxvk { depthOp.loadOpS = VK_ATTACHMENT_LOAD_OP_LOAD; depthOp.loadLayout = imageView->imageInfo().layout; depthOp.storeLayout = imageView->imageInfo().layout; - + if (clearAspects & VK_IMAGE_ASPECT_COLOR_BIT) colorOp.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; else if (discardAspects & VK_IMAGE_ASPECT_COLOR_BIT) @@ -2010,7 +2010,7 @@ namespace dxvk { if (clearAspects & VK_IMAGE_ASPECT_STENCIL_BIT) depthOp.loadOpS = VK_ATTACHMENT_LOAD_OP_CLEAR; - else if (discardAspects & VK_IMAGE_ASPECT_DEPTH_BIT) + else if (discardAspects & VK_IMAGE_ASPECT_STENCIL_BIT) depthOp.loadOpS = VK_ATTACHMENT_LOAD_OP_DONT_CARE; if (attachmentIndex >= 0 && !m_state.om.framebufferInfo.isWritable(attachmentIndex, clearAspects | discardAspects)) { From 00872e9e4fdd87d92f2dd662791b8de6a7edf10c Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Fri, 19 Apr 2024 01:44:14 +0200 Subject: [PATCH 124/246] [dxvk] Fix render target clears with format reinterpretation With LOAD_OP_CLEAR, we cannot rely on the clear actually being performed with the view format in mind. Use a vkCmdClearAttachment path instead. --- src/dxvk/dxvk_context.cpp | 46 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/src/dxvk/dxvk_context.cpp b/src/dxvk/dxvk_context.cpp index d24d919b2..e12553dff 100644 --- a/src/dxvk/dxvk_context.cpp +++ b/src/dxvk/dxvk_context.cpp @@ -2041,6 +2041,8 @@ namespace dxvk { } if (attachmentIndex < 0) { + bool hasViewFormatMismatch = imageView->info().format != imageView->imageInfo().format; + if (m_execBarriers.isImageDirty(imageView->image(), imageView->imageSubresources(), DxvkAccess::Write)) m_execBarriers.recordCommands(m_cmd); @@ -2075,6 +2077,11 @@ namespace dxvk { attachmentInfo.loadOp = colorOp.loadOp; + // We can't use LOAD_OP_CLEAR if the view format does not match the + // underlying image format, so just discard here and use clear later. + if (hasViewFormatMismatch && attachmentInfo.loadOp == VK_ATTACHMENT_LOAD_OP_CLEAR) + attachmentInfo.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + renderingInfo.colorAttachmentCount = 1; renderingInfo.pColorAttachments = &attachmentInfo; @@ -2110,6 +2117,20 @@ namespace dxvk { } m_cmd->cmdBeginRendering(&renderingInfo); + + if (hasViewFormatMismatch) { + VkClearAttachment clearInfo = { }; + clearInfo.aspectMask = imageView->info().aspect; + clearInfo.clearValue = clearValue; + + VkClearRect clearRect = { }; + clearRect.rect.extent.width = extent.width; + clearRect.rect.extent.height = extent.height; + clearRect.layerCount = imageView->info().numLayers; + + m_cmd->cmdClearAttachments(1, &clearInfo, 1, &clearRect); + } + m_cmd->cmdEndRendering(); m_execBarriers.accessImage( @@ -4741,8 +4762,10 @@ namespace dxvk { this->renderPassEmitPostBarriers(framebufferInfo, ops); uint32_t colorInfoCount = 0; + uint32_t lateClearCount = 0; std::array colorInfos; + std::array lateClears; for (uint32_t i = 0; i < MaxNumRenderTargets; i++) { const auto& colorTarget = framebufferInfo.getColorTarget(i); @@ -4754,9 +4777,21 @@ namespace dxvk { colorInfos[i].loadOp = ops.colorOps[i].loadOp; colorInfos[i].storeOp = VK_ATTACHMENT_STORE_OP_STORE; - if (ops.colorOps[i].loadOp == VK_ATTACHMENT_LOAD_OP_CLEAR) + if (ops.colorOps[i].loadOp == VK_ATTACHMENT_LOAD_OP_CLEAR) { colorInfos[i].clearValue.color = ops.colorOps[i].clearValue; + // We can't use LOAD_OP_CLEAR if the view format does not match the + // underlying image format, so just discard here and use clear later. + if (colorTarget.view->info().format != colorTarget.view->imageInfo().format) { + colorInfos[i].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + + auto& clear = lateClears[lateClearCount++]; + clear.colorAttachment = i; + clear.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + clear.clearValue.color = ops.colorOps[i].clearValue; + } + } + colorInfoCount = i + 1; } } @@ -4804,6 +4839,15 @@ namespace dxvk { m_cmd->cmdBeginRendering(&renderingInfo); + if (lateClearCount) { + VkClearRect clearRect = { }; + clearRect.rect.extent.width = fbSize.width; + clearRect.rect.extent.height = fbSize.height; + clearRect.layerCount = fbSize.layers; + + m_cmd->cmdClearAttachments(lateClearCount, lateClears.data(), 1, &clearRect); + } + for (uint32_t i = 0; i < framebufferInfo.numAttachments(); i++) { m_cmd->trackResource (framebufferInfo.getAttachment(i).view); m_cmd->trackResource(framebufferInfo.getAttachment(i).view->image()); From 9c66c4bf1d81a4004ac6c563e87cb53556caa7ee Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Fri, 19 Apr 2024 13:36:32 +0200 Subject: [PATCH 125/246] [build] Target SPIR-V 1.6 for built-in GLSL shaders Silences a Mesa warning when the HUD is enabled. --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index e35a9de02..2a2637359 100644 --- a/meson.build +++ b/meson.build @@ -160,7 +160,7 @@ def_spec_ext = '.def' glsl_compiler = find_program('glslang', 'glslangValidator') glsl_args = [ '--quiet', - '--target-env', 'vulkan1.2', + '--target-env', 'vulkan1.3', '--vn', '@BASENAME@', '--depfile', '@DEPFILE@', '@INPUT@', From 2b70ba8f7798107308a0c34358a9e83e77017dfd Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Fri, 19 Apr 2024 13:55:31 +0200 Subject: [PATCH 126/246] [dxbc] Do not emit OpImageQueryLevels for multisampled images --- src/dxbc/dxbc_compiler.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dxbc/dxbc_compiler.cpp b/src/dxbc/dxbc_compiler.cpp index 2fb15e262..5b65c8a7d 100644 --- a/src/dxbc/dxbc_compiler.cpp +++ b/src/dxbc/dxbc_compiler.cpp @@ -5513,12 +5513,12 @@ namespace dxvk { result.type.ctype = DxbcScalarType::Uint32; result.type.ccount = 1; - if (info.image.sampled == 1) { + if (info.image.ms == 0 && info.image.sampled == 1) { result.id = m_module.opImageQueryLevels( getVectorTypeId(result.type), m_module.opLoad(info.typeId, info.varId)); } else { - // Report one LOD in case of UAVs + // Report one LOD in case of UAVs or multisampled images result.id = m_module.constu32(1); } From 8ba5256dc723262d592ef8f4746e996df87ca5d1 Mon Sep 17 00:00:00 2001 From: Blisto91 Date: Sun, 21 Apr 2024 21:46:28 +0200 Subject: [PATCH 127/246] [util] Set deferSurfaceCreation for 9th Dawn II OpenGL game that also spins up d3d9. Will black screen without deferSurfaceCreation when using dxvk --- src/util/config/config.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index 9e6c86967..c0c2a085e 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -865,6 +865,12 @@ namespace dxvk { { R"(\\ShippingPC-SkyGame\.exe$)", {{ { "d3d9.maxFrameRate", "60" }, }} }, + /* 9th Dawn II * + * OpenGL game that also spins up d3d9 * + * Black screens without config */ + { R"(\\ninthdawnii\.exe$)", {{ + { "d3d9.deferSurfaceCreation", "True" }, + }} }, /**********************************************/ From 92a43ebf656bd00c5ce41de6f4bcf9aacda2da19 Mon Sep 17 00:00:00 2001 From: Katharine Chui Date: Mon, 22 Apr 2024 14:35:37 +0800 Subject: [PATCH 128/246] [dxgi] unchain DxgiSwapChain::Present1 and Present dxgi hooking tools might hook both, eg. https://github.com/SpecialKO/SpecialK/issues/167 --- src/dxgi/dxgi_swapchain.cpp | 13 ++++++++++--- src/dxgi/dxgi_swapchain.h | 4 ++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/dxgi/dxgi_swapchain.cpp b/src/dxgi/dxgi_swapchain.cpp index 88ee3c6d2..9b1283a1b 100644 --- a/src/dxgi/dxgi_swapchain.cpp +++ b/src/dxgi/dxgi_swapchain.cpp @@ -303,15 +303,22 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE DxgiSwapChain::Present(UINT SyncInterval, UINT Flags) { - return Present1(SyncInterval, Flags, nullptr); + return PresentBase(SyncInterval, Flags, nullptr); } - - + HRESULT STDMETHODCALLTYPE DxgiSwapChain::Present1( UINT SyncInterval, UINT PresentFlags, const DXGI_PRESENT_PARAMETERS* pPresentParameters) { + return PresentBase(SyncInterval, PresentFlags, pPresentParameters); + } + + HRESULT STDMETHODCALLTYPE DxgiSwapChain::PresentBase( + UINT SyncInterval, + UINT PresentFlags, + const DXGI_PRESENT_PARAMETERS* pPresentParameters) { + if (SyncInterval > 4) return DXGI_ERROR_INVALID_CALL; diff --git a/src/dxgi/dxgi_swapchain.h b/src/dxgi/dxgi_swapchain.h index a01cdf2e0..8ec7d5014 100644 --- a/src/dxgi/dxgi_swapchain.h +++ b/src/dxgi/dxgi_swapchain.h @@ -233,6 +233,10 @@ namespace dxvk { DXGI_FORMAT Format, DXGI_COLOR_SPACE_TYPE ColorSpace); + HRESULT STDMETHODCALLTYPE PresentBase( + UINT SyncInterval, + UINT PresentFlags, + const DXGI_PRESENT_PARAMETERS* pPresentParameters); }; } From aac3396671bb1975f57dec0b58430bbb08969226 Mon Sep 17 00:00:00 2001 From: Katharine Chui Date: Wed, 24 Apr 2024 01:18:15 +0800 Subject: [PATCH 129/246] [dxgi] unchain DxgiFactory::CreateSwapChain and CreateSwapChainForHwnd similar to https://github.com/doitsujin/dxvk/pull/3966, avoid chaining so that dxgi tools attempting to wrap swapchains don't end up double wrapping ref: https://github.com/SpecialKO/SpecialK/issues/168 --- src/dxgi/dxgi_factory.cpp | 15 ++++++++++++++- src/dxgi/dxgi_factory.h | 8 ++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/dxgi/dxgi_factory.cpp b/src/dxgi/dxgi_factory.cpp index fe4943a91..dc1c556dd 100644 --- a/src/dxgi/dxgi_factory.cpp +++ b/src/dxgi/dxgi_factory.cpp @@ -230,7 +230,7 @@ namespace dxvk { descFs.Windowed = pDesc->Windowed; IDXGISwapChain1* swapChain = nullptr; - HRESULT hr = CreateSwapChainForHwnd( + HRESULT hr = CreateSwapChainForHwndBase( pDevice, pDesc->OutputWindow, &desc, &descFs, nullptr, &swapChain); @@ -244,6 +244,19 @@ namespace dxvk { IUnknown* pDevice, HWND hWnd, const DXGI_SWAP_CHAIN_DESC1* pDesc, + const DXGI_SWAP_CHAIN_FULLSCREEN_DESC* pFullscreenDesc, + IDXGIOutput* pRestrictToOutput, + IDXGISwapChain1** ppSwapChain) { + return CreateSwapChainForHwndBase( + pDevice, hWnd, + pDesc, pFullscreenDesc, pRestrictToOutput, + ppSwapChain); + } + + HRESULT STDMETHODCALLTYPE DxgiFactory::CreateSwapChainForHwndBase( + IUnknown* pDevice, + HWND hWnd, + const DXGI_SWAP_CHAIN_DESC1* pDesc, const DXGI_SWAP_CHAIN_FULLSCREEN_DESC* pFullscreenDesc, IDXGIOutput* pRestrictToOutput, IDXGISwapChain1** ppSwapChain) { diff --git a/src/dxgi/dxgi_factory.h b/src/dxgi/dxgi_factory.h index 96a4410cb..280b0a84a 100644 --- a/src/dxgi/dxgi_factory.h +++ b/src/dxgi/dxgi_factory.h @@ -200,6 +200,14 @@ namespace dxvk { UINT m_flags; BOOL m_monitorFallback; + + HRESULT STDMETHODCALLTYPE CreateSwapChainForHwndBase( + IUnknown* pDevice, + HWND hWnd, + const DXGI_SWAP_CHAIN_DESC1* pDesc, + const DXGI_SWAP_CHAIN_FULLSCREEN_DESC* pFullscreenDesc, + IDXGIOutput* pRestrictToOutput, + IDXGISwapChain1** ppSwapChain); }; } From 3f27a0ee587e7c545b85f2b7c276d3a572b4db17 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Fri, 26 Apr 2024 13:15:14 +0200 Subject: [PATCH 130/246] [util] Add a way to define app profiles exclusive to Steam Deck --- src/util/config/config.cpp | 41 +++++++++++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index c0c2a085e..18df1b4ce 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -13,7 +13,10 @@ namespace dxvk { - const static std::vector> g_appDefaults = {{ + using ProfileList = std::vector>; + + + const static ProfileList g_profiles = {{ /* Assassin's Creed Syndicate: amdags issues */ { R"(\\ACS\.exe$)", {{ { "dxgi.customVendorId", "10de" }, @@ -909,6 +912,24 @@ namespace dxvk { }}; + const static ProfileList g_deckProfiles = {{ + + }}; + + + const Config* findProfile(const ProfileList& profiles, const std::string& appName) { + auto appConfig = std::find_if(profiles.begin(), profiles.end(), + [&appName] (const std::pair& pair) { + std::regex expr(pair.first, std::regex::extended | std::regex::icase); + return std::regex_search(appName, expr); + }); + + return appConfig != profiles.end() + ? &appConfig->second + : nullptr; + } + + static bool isWhitespace(char ch) { return ch == ' ' || ch == '\x9' || ch == '\r'; } @@ -1158,20 +1179,22 @@ namespace dxvk { Config Config::getAppConfig(const std::string& appName) { - auto appConfig = std::find_if(g_appDefaults.begin(), g_appDefaults.end(), - [&appName] (const std::pair& pair) { - std::regex expr(pair.first, std::regex::extended | std::regex::icase); - return std::regex_search(appName, expr); - }); + const Config* config = nullptr; - if (appConfig != g_appDefaults.end()) { + if (env::getEnvVar("SteamDeck") == "1") + config = findProfile(g_deckProfiles, appName); + + if (!config) + config = findProfile(g_profiles, appName); + + if (config) { // Inform the user that we loaded a default config Logger::info(str::format("Found built-in config:")); - for (auto& pair : appConfig->second.m_options) + for (auto& pair : config->m_options) Logger::info(str::format(" ", pair.first, " = ", pair.second)); - return appConfig->second; + return *config; } return Config(); From 462165da197af2adbb2056806e8e6784c125d386 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Fri, 26 Apr 2024 13:15:59 +0200 Subject: [PATCH 131/246] [util] Add Deck profile for Fallout 4 Should fix the FPS problem on Deck OLED. --- src/util/config/config.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index 18df1b4ce..da0932e63 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -913,7 +913,11 @@ namespace dxvk { const static ProfileList g_deckProfiles = {{ - + /* Fallout 4: Defaults to 45 FPS on OLED, but also breaks above 60 FPS */ + { R"(\\Fallout4\.exe$)", {{ + { "dxgi.syncInterval", "1" }, + { "dxgi.maxFrameRate", "60" }, + }} }, }}; From 2970645f335f76433f62317b6fd38d30ccf7742b Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Fri, 26 Apr 2024 19:06:51 +0200 Subject: [PATCH 132/246] [dxvk] Fix push constant compatibility for pipeline libraries When linking pipelines, all pipeline libraries are required to declare the exact same set of push constants, even for stages not part of the respective libraries. This invalidates all fossilize databases. --- src/d3d9/d3d9_fixed_function.cpp | 28 +++++----------------------- src/d3d9/d3d9_fixed_function.h | 2 +- src/d3d9/d3d9_format_helpers.cpp | 2 +- src/dxbc/dxbc_compiler.cpp | 5 ++--- src/dxso/dxso_compiler.cpp | 29 +++-------------------------- src/dxso/dxso_compiler.h | 2 -- src/dxvk/dxvk_context.cpp | 17 ++++++++++------- src/dxvk/dxvk_pipelayout.cpp | 24 ++++++++++++++++++++---- src/dxvk/dxvk_pipelayout.h | 22 ++++++++++++++++++++-- src/dxvk/dxvk_shader.cpp | 14 ++++++++++++-- src/dxvk/dxvk_shader.h | 2 +- src/dxvk/dxvk_swapchain_blitter.cpp | 3 +++ src/dxvk/hud/dxvk_hud_renderer.cpp | 7 ++++++- 13 files changed, 84 insertions(+), 73 deletions(-) diff --git a/src/d3d9/d3d9_fixed_function.cpp b/src/d3d9/d3d9_fixed_function.cpp index 099985f04..f9a2a1ce5 100644 --- a/src/d3d9/d3d9_fixed_function.cpp +++ b/src/d3d9/d3d9_fixed_function.cpp @@ -336,7 +336,7 @@ namespace dxvk { } - uint32_t SetupRenderStateBlock(SpirvModule& spvModule, uint32_t count) { + uint32_t SetupRenderStateBlock(SpirvModule& spvModule) { uint32_t floatType = spvModule.defFloatType(32); uint32_t uintType = spvModule.defIntType(32, 0); uint32_t vec3Type = spvModule.defVectorType(floatType, 3); @@ -357,7 +357,7 @@ namespace dxvk { floatType, }}; - uint32_t rsStruct = spvModule.defStructTypeUnique(count, rsMembers.data()); + uint32_t rsStruct = spvModule.defStructTypeUnique(rsMembers.size(), rsMembers.data()); uint32_t rsBlock = spvModule.newVar( spvModule.defPointerType(rsStruct, spv::StorageClassPushConstant), spv::StorageClassPushConstant); @@ -369,9 +369,6 @@ namespace dxvk { uint32_t memberIdx = 0; auto SetMemberName = [&](const char* name, uint32_t offset) { - if (memberIdx >= count) - return; - spvModule.setDebugMemberName (rsStruct, memberIdx, name); spvModule.memberDecorateOffset (rsStruct, memberIdx, offset); memberIdx++; @@ -781,8 +778,6 @@ namespace dxvk { uint32_t m_inputMask = 0u; uint32_t m_outputMask = 0u; uint32_t m_flatShadingMask = 0u; - uint32_t m_pushConstOffset = 0u; - uint32_t m_pushConstSize = 0u; DxsoProgramType m_programType; D3D9FFShaderKeyVS m_vsKey; @@ -892,8 +887,8 @@ namespace dxvk { info.inputMask = m_inputMask; info.outputMask = m_outputMask; info.flatShadingInputs = m_flatShadingMask; - info.pushConstOffset = m_pushConstOffset; - info.pushConstSize = m_pushConstSize; + info.pushConstStages = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT; + info.pushConstSize = sizeof(D3D9RenderStateInfo); return new DxvkShader(info, m_module.compile()); } @@ -1384,20 +1379,7 @@ namespace dxvk { void D3D9FFShaderCompiler::setupRenderStateInfo() { - uint32_t count; - - if (m_programType == DxsoProgramType::PixelShader) { - m_pushConstOffset = 0; - m_pushConstSize = offsetof(D3D9RenderStateInfo, pointSize); - count = 5; - } - else { - m_pushConstOffset = offsetof(D3D9RenderStateInfo, pointSize); - m_pushConstSize = sizeof(float) * 6; - count = 11; - } - - m_rsBlock = SetupRenderStateBlock(m_module, count); + m_rsBlock = SetupRenderStateBlock(m_module); } diff --git a/src/d3d9/d3d9_fixed_function.h b/src/d3d9/d3d9_fixed_function.h index 104f044cd..0244755dc 100644 --- a/src/d3d9/d3d9_fixed_function.h +++ b/src/d3d9/d3d9_fixed_function.h @@ -59,7 +59,7 @@ namespace dxvk { void DoFixedFunctionAlphaTest(SpirvModule& spvModule, const D3D9AlphaTestContext& ctx); // Returns a render state block - uint32_t SetupRenderStateBlock(SpirvModule& spvModule, uint32_t count); + uint32_t SetupRenderStateBlock(SpirvModule& spvModule); struct D3D9PointSizeInfoVS { uint32_t defaultValue; diff --git a/src/d3d9/d3d9_format_helpers.cpp b/src/d3d9/d3d9_format_helpers.cpp index f0cdd2b65..7f2848705 100644 --- a/src/d3d9/d3d9_format_helpers.cpp +++ b/src/d3d9/d3d9_format_helpers.cpp @@ -134,7 +134,7 @@ namespace dxvk { info.stage = VK_SHADER_STAGE_COMPUTE_BIT; info.bindingCount = bindings.size(); info.bindings = bindings.data(); - info.pushConstOffset = 0; + info.pushConstStages = VK_SHADER_STAGE_COMPUTE_BIT; info.pushConstSize = sizeof(VkExtent2D); return new DxvkShader(info, std::move(code)); diff --git a/src/dxbc/dxbc_compiler.cpp b/src/dxbc/dxbc_compiler.cpp index 5b65c8a7d..b679c3236 100644 --- a/src/dxbc/dxbc_compiler.cpp +++ b/src/dxbc/dxbc_compiler.cpp @@ -257,14 +257,13 @@ namespace dxvk { info.outputMask = m_outputMask; info.uniformSize = m_immConstData.size(); info.uniformData = m_immConstData.data(); + info.pushConstStages = VK_SHADER_STAGE_FRAGMENT_BIT; + info.pushConstSize = sizeof(DxbcPushConstants); info.outputTopology = m_outputTopology; if (m_programInfo.type() == DxbcProgramType::HullShader) info.patchVertexCount = m_hs.vertexCountIn; - if (m_programInfo.type() == DxbcProgramType::PixelShader && m_ps.pushConstantId) - info.pushConstSize = sizeof(DxbcPushConstants); - if (m_moduleInfo.xfb) { info.xfbRasterizedStream = m_moduleInfo.xfb->rasterizedStream; diff --git a/src/dxso/dxso_compiler.cpp b/src/dxso/dxso_compiler.cpp index abc55de81..97bece7d9 100644 --- a/src/dxso/dxso_compiler.cpp +++ b/src/dxso/dxso_compiler.cpp @@ -228,8 +228,8 @@ namespace dxvk { info.bindings = m_bindings.data(); info.inputMask = m_inputMask; info.outputMask = m_outputMask; - info.pushConstOffset = m_pushConstOffset; - info.pushConstSize = m_pushConstSize; + info.pushConstStages = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT; + info.pushConstSize = sizeof(D3D9RenderStateInfo); if (m_programInfo.type() == DxsoProgramTypes::PixelShader) info.flatShadingInputs = m_ps.flatShadingMask; @@ -3561,30 +3561,7 @@ void DxsoCompiler::emitControlFlowGenericLoop( void DxsoCompiler::setupRenderStateInfo() { - uint32_t count; - - // Only need alpha ref for PS 3. - // No FF fog component. - if (m_programInfo.type() == DxsoProgramType::PixelShader) { - if (m_programInfo.majorVersion() == 3) { - m_pushConstOffset = offsetof(D3D9RenderStateInfo, alphaRef); - m_pushConstSize = sizeof(float); - } - else { - m_pushConstOffset = 0; - m_pushConstSize = offsetof(D3D9RenderStateInfo, pointSize); - } - - count = 5; - } - else { - m_pushConstOffset = offsetof(D3D9RenderStateInfo, pointSize); - // Point scale never triggers on programmable - m_pushConstSize = sizeof(float) * 3; - count = 8; - } - - m_rsBlock = SetupRenderStateBlock(m_module, count); + m_rsBlock = SetupRenderStateBlock(m_module); } diff --git a/src/dxso/dxso_compiler.h b/src/dxso/dxso_compiler.h index 05daca700..6b58a84d3 100644 --- a/src/dxso/dxso_compiler.h +++ b/src/dxso/dxso_compiler.h @@ -344,8 +344,6 @@ namespace dxvk { // covers vertex input and fragment output. uint32_t m_inputMask = 0u; uint32_t m_outputMask = 0u; - uint32_t m_pushConstOffset = 0u; - uint32_t m_pushConstSize = 0u; /////////////////////////////////// // Shader-specific data structures diff --git a/src/dxvk/dxvk_context.cpp b/src/dxvk/dxvk_context.cpp index e12553dff..c63dce533 100644 --- a/src/dxvk/dxvk_context.cpp +++ b/src/dxvk/dxvk_context.cpp @@ -4984,7 +4984,7 @@ namespace dxvk { // Mark compute resources and push constants as dirty m_descriptorState.dirtyStages(VK_SHADER_STAGE_COMPUTE_BIT); - if (newPipeline->getBindings()->layout().getPushConstantRange().size) + if (newPipeline->getBindings()->layout().getPushConstantRange(true).size) m_flags.set(DxvkContextFlag::DirtyPushConstants); m_flags.clr(DxvkContextFlag::CpDirtyPipelineState); @@ -5058,7 +5058,7 @@ namespace dxvk { m_descriptorState.dirtyStages(VK_SHADER_STAGE_ALL_GRAPHICS); - if (newPipeline->getBindings()->layout().getPushConstantRange().size) + if (newPipeline->getBindings()->layout().getPushConstantRange(true).size) m_flags.set(DxvkContextFlag::DirtyPushConstants); m_flags.clr(DxvkContextFlag::GpDirtyPipeline); @@ -5925,16 +5925,19 @@ namespace dxvk { auto bindings = BindPoint == VK_PIPELINE_BIND_POINT_GRAPHICS ? m_state.gp.pipeline->getBindings() : m_state.cp.pipeline->getBindings(); - - VkPushConstantRange pushConstRange = bindings->layout().getPushConstantRange(); + + // Optimized pipelines may have push constants trimmed, so look up + // the exact layout used for the currently bound pipeline. + bool independentSets = BindPoint == VK_PIPELINE_BIND_POINT_GRAPHICS + && m_flags.test(DxvkContextFlag::GpIndependentSets); + + VkPushConstantRange pushConstRange = bindings->layout().getPushConstantRange(independentSets); if (!pushConstRange.size) return; - // Push constants should be compatible between complete and - // independent layouts, so always ask for the complete one m_cmd->cmdPushConstants( - bindings->getPipelineLayout(false), + bindings->getPipelineLayout(independentSets), pushConstRange.stageFlags, pushConstRange.offset, pushConstRange.size, diff --git a/src/dxvk/dxvk_pipelayout.cpp b/src/dxvk/dxvk_pipelayout.cpp index c16992cf8..9132d025c 100644 --- a/src/dxvk/dxvk_pipelayout.cpp +++ b/src/dxvk/dxvk_pipelayout.cpp @@ -205,7 +205,7 @@ namespace dxvk { DxvkBindingLayout::DxvkBindingLayout(VkShaderStageFlags stages) - : m_pushConst { 0, 0, 0 }, m_stages(stages) { + : m_pushConst { 0, 0, 0 }, m_pushConstStages(0), m_stages(stages) { } @@ -249,11 +249,17 @@ namespace dxvk { } + void DxvkBindingLayout::addPushConstantStage(VkShaderStageFlagBits stage) { + m_pushConstStages |= stage; + } + + void DxvkBindingLayout::merge(const DxvkBindingLayout& layout) { for (uint32_t i = 0; i < layout.m_bindings.size(); i++) m_bindings[i].merge(layout.m_bindings[i]); addPushConstantRange(layout.m_pushConst); + m_pushConstStages |= layout.m_pushConstStages; } @@ -266,6 +272,9 @@ namespace dxvk { return false; } + if (m_pushConstStages != other.m_pushConstStages) + return false; + if (m_pushConst.stageFlags != other.m_pushConst.stageFlags || m_pushConst.offset != other.m_pushConst.offset || m_pushConst.size != other.m_pushConst.size) @@ -282,6 +291,7 @@ namespace dxvk { for (uint32_t i = 0; i < m_bindings.size(); i++) hash.add(m_bindings[i].hash()); + hash.add(m_pushConstStages); hash.add(m_pushConst.stageFlags); hash.add(m_pushConst.offset); hash.add(m_pushConst.size); @@ -334,15 +344,16 @@ namespace dxvk { } // Create pipeline layout objects - VkPushConstantRange pushConst = m_layout.getPushConstantRange(); + VkPushConstantRange pushConstComplete = m_layout.getPushConstantRange(false); + VkPushConstantRange pushConstIndependent = m_layout.getPushConstantRange(true); VkPipelineLayoutCreateInfo pipelineLayoutInfo = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO }; pipelineLayoutInfo.setLayoutCount = setCount; pipelineLayoutInfo.pSetLayouts = setLayouts.data(); - if (pushConst.stageFlags && pushConst.size) { + if (pushConstComplete.stageFlags && pushConstComplete.size) { pipelineLayoutInfo.pushConstantRangeCount = 1; - pipelineLayoutInfo.pPushConstantRanges = &pushConst; + pipelineLayoutInfo.pPushConstantRanges = &pushConstComplete; } // If the full set is defined, create a layout without INDEPENDENT_SET_BITS @@ -356,6 +367,11 @@ namespace dxvk { if (m_device->canUseGraphicsPipelineLibrary() && (m_layout.getStages() & VK_SHADER_STAGE_ALL_GRAPHICS)) { pipelineLayoutInfo.flags = VK_PIPELINE_LAYOUT_CREATE_INDEPENDENT_SETS_BIT_EXT; + if (pushConstIndependent.stageFlags && pushConstIndependent.size) { + pipelineLayoutInfo.pushConstantRangeCount = 1; + pipelineLayoutInfo.pPushConstantRanges = &pushConstIndependent; + } + if (vk->vkCreatePipelineLayout(vk->device(), &pipelineLayoutInfo, nullptr, &m_independentLayout)) throw DxvkError("DxvkBindingLayoutObjects: Failed to create pipeline layout"); } diff --git a/src/dxvk/dxvk_pipelayout.h b/src/dxvk/dxvk_pipelayout.h index 1e9b6b1e8..cccfb336e 100644 --- a/src/dxvk/dxvk_pipelayout.h +++ b/src/dxvk/dxvk_pipelayout.h @@ -292,8 +292,19 @@ namespace dxvk { * \brief Retrieves push constant range * \returns Push constant range */ - VkPushConstantRange getPushConstantRange() const { - return m_pushConst; + VkPushConstantRange getPushConstantRange(bool independent) const { + VkPushConstantRange result = m_pushConst; + + if (!independent) { + result.stageFlags &= m_pushConstStages; + + if (!result.stageFlags) { + result.offset = 0; + result.size = 0; + } + } + + return result; } /** @@ -324,6 +335,12 @@ namespace dxvk { */ void addPushConstantRange(VkPushConstantRange range); + /** + * \brief Adds a stage that actively uses push constants + * \param [in] stage Shader stage + */ + void addPushConstantStage(VkShaderStageFlagBits stage); + /** * \brief Merges binding layouts * @@ -353,6 +370,7 @@ namespace dxvk { std::array m_bindings; VkPushConstantRange m_pushConst; + VkShaderStageFlags m_pushConstStages; VkShaderStageFlags m_stages; }; diff --git a/src/dxvk/dxvk_shader.cpp b/src/dxvk/dxvk_shader.cpp index dbebf7153..2748bc177 100644 --- a/src/dxvk/dxvk_shader.cpp +++ b/src/dxvk/dxvk_shader.cpp @@ -59,8 +59,8 @@ namespace dxvk { if (info.pushConstSize) { VkPushConstantRange pushConst; - pushConst.stageFlags = info.stage; - pushConst.offset = info.pushConstOffset; + pushConst.stageFlags = info.pushConstStages; + pushConst.offset = 0; pushConst.size = info.pushConstSize; m_bindings.addPushConstantRange(pushConst); @@ -75,6 +75,8 @@ namespace dxvk { // Run an analysis pass over the SPIR-V code to gather some // info that we may need during pipeline compilation. + bool usesPushConstants = false; + std::vector bindingOffsets; std::vector varIds; std::vector sampleMaskIds; @@ -154,6 +156,9 @@ namespace dxvk { if (std::find(sampleMaskIds.begin(), sampleMaskIds.end(), ins.arg(2)) != sampleMaskIds.end()) m_flags.set(DxvkShaderFlag::ExportsSampleMask); } + + if (ins.arg(3) == spv::StorageClassPushConstant) + usesPushConstants = true; } // Ignore the actual shader code, there's nothing interesting for us in there. @@ -169,6 +174,11 @@ namespace dxvk { m_bindingOffsets.push_back(info); } + // Set flag for stages that actually use push constants + // so that they can be trimmed for optimized pipelines. + if (usesPushConstants) + m_bindings.addPushConstantStage(info.stage); + // Don't set pipeline library flag if the shader // doesn't actually support pipeline libraries m_needsLibraryCompile = canUsePipelineLibrary(true); diff --git a/src/dxvk/dxvk_shader.h b/src/dxvk/dxvk_shader.h index e3c0d8b79..bc138f27d 100644 --- a/src/dxvk/dxvk_shader.h +++ b/src/dxvk/dxvk_shader.h @@ -52,7 +52,7 @@ namespace dxvk { /// Flat shading input mask uint32_t flatShadingInputs = 0; /// Push constant range - uint32_t pushConstOffset = 0; + VkShaderStageFlags pushConstStages = 0; uint32_t pushConstSize = 0; /// Uniform buffer data uint32_t uniformSize = 0; diff --git a/src/dxvk/dxvk_swapchain_blitter.cpp b/src/dxvk/dxvk_swapchain_blitter.cpp index c19188cbf..beaf6e466 100644 --- a/src/dxvk/dxvk_swapchain_blitter.cpp +++ b/src/dxvk/dxvk_swapchain_blitter.cpp @@ -329,6 +329,8 @@ namespace dxvk { DxvkShaderCreateInfo vsInfo; vsInfo.stage = VK_SHADER_STAGE_VERTEX_BIT; + vsInfo.pushConstStages = VK_SHADER_STAGE_FRAGMENT_BIT; + vsInfo.pushConstSize = sizeof(PresenterArgs); vsInfo.outputMask = 0x1; m_vs = new DxvkShader(vsInfo, std::move(vsCode)); @@ -336,6 +338,7 @@ namespace dxvk { fsInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT; fsInfo.bindingCount = fsBindings.size(); fsInfo.bindings = fsBindings.data(); + fsInfo.pushConstStages = VK_SHADER_STAGE_FRAGMENT_BIT; fsInfo.pushConstSize = sizeof(PresenterArgs); fsInfo.inputMask = 0x1; fsInfo.outputMask = 0x1; diff --git a/src/dxvk/hud/dxvk_hud_renderer.cpp b/src/dxvk/hud/dxvk_hud_renderer.cpp index 630706e0f..6e1dfeaf0 100644 --- a/src/dxvk/hud/dxvk_hud_renderer.cpp +++ b/src/dxvk/hud/dxvk_hud_renderer.cpp @@ -183,14 +183,17 @@ namespace dxvk::hud { vsInfo.stage = VK_SHADER_STAGE_VERTEX_BIT; vsInfo.bindingCount = vsBindings.size(); vsInfo.bindings = vsBindings.data(); - vsInfo.outputMask = 0x3; + vsInfo.pushConstStages = VK_SHADER_STAGE_VERTEX_BIT; vsInfo.pushConstSize = sizeof(HudTextPushConstants); + vsInfo.outputMask = 0x3; result.vert = new DxvkShader(vsInfo, std::move(vsCode)); DxvkShaderCreateInfo fsInfo; fsInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT; fsInfo.bindingCount = fsBindings.size(); fsInfo.bindings = fsBindings.data(); + fsInfo.pushConstStages = VK_SHADER_STAGE_VERTEX_BIT; + fsInfo.pushConstSize = sizeof(HudTextPushConstants); fsInfo.inputMask = 0x3; fsInfo.outputMask = 0x1; result.frag = new DxvkShader(fsInfo, std::move(fsCode)); @@ -212,6 +215,7 @@ namespace dxvk::hud { DxvkShaderCreateInfo vsInfo; vsInfo.stage = VK_SHADER_STAGE_VERTEX_BIT; vsInfo.outputMask = 0x1; + vsInfo.pushConstStages = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT; vsInfo.pushConstSize = sizeof(HudGraphPushConstants); result.vert = new DxvkShader(vsInfo, std::move(vsCode)); @@ -221,6 +225,7 @@ namespace dxvk::hud { fsInfo.bindings = fsBindings.data(); fsInfo.inputMask = 0x1; fsInfo.outputMask = 0x1; + fsInfo.pushConstStages = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT; fsInfo.pushConstSize = sizeof(HudGraphPushConstants); result.frag = new DxvkShader(fsInfo, std::move(fsCode)); From c613078ba8936055eb5cf6b21805799b3651dad1 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Fri, 26 Apr 2024 19:10:06 +0200 Subject: [PATCH 133/246] [dxvk] Bump internal version number Potentially useful for drivers and tools to deal with the new pipeline layout changes. --- src/dxvk/dxvk_instance.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dxvk/dxvk_instance.cpp b/src/dxvk/dxvk_instance.cpp index e86896d86..bc204d3a6 100644 --- a/src/dxvk/dxvk_instance.cpp +++ b/src/dxvk/dxvk_instance.cpp @@ -177,7 +177,7 @@ namespace dxvk { appInfo.pApplicationName = appName.c_str(); appInfo.applicationVersion = flags.raw(); appInfo.pEngineName = "DXVK"; - appInfo.engineVersion = VK_MAKE_VERSION(2, 3, 1); + appInfo.engineVersion = VK_MAKE_VERSION(2, 3, 2); appInfo.apiVersion = VK_MAKE_VERSION(1, 3, 0); VkInstanceCreateInfo info = { VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO }; From e7d14e97de1be1a77750da8f1f0f8112816ad417 Mon Sep 17 00:00:00 2001 From: Robin Kertels Date: Sat, 27 Apr 2024 15:42:25 +0200 Subject: [PATCH 134/246] [dxbc] Implement option to split up fma --- dxvk.conf | 1 + src/d3d11/d3d11_options.cpp | 1 + src/d3d11/d3d11_options.h | 3 +++ src/dxbc/dxbc_compiler.cpp | 9 +++++++-- src/dxbc/dxbc_options.cpp | 1 + src/dxbc/dxbc_options.h | 3 +++ 6 files changed, 16 insertions(+), 2 deletions(-) diff --git a/dxvk.conf b/dxvk.conf index 4f1a443a4..3e739704b 100644 --- a/dxvk.conf +++ b/dxvk.conf @@ -499,6 +499,7 @@ # Supported values: # - True/False +# d3d11.longMad = False # d3d9.longMad = False # Device Local Constant Buffers diff --git a/src/d3d11/d3d11_options.cpp b/src/d3d11/d3d11_options.cpp index bce1a713a..ddbfe0a9a 100644 --- a/src/d3d11/d3d11_options.cpp +++ b/src/d3d11/d3d11_options.cpp @@ -32,6 +32,7 @@ namespace dxvk { this->maxFrameLatency = config.getOption("dxgi.maxFrameLatency", 0); this->maxFrameRate = config.getOption("dxgi.maxFrameRate", 0); this->exposeDriverCommandLists = config.getOption("d3d11.exposeDriverCommandLists", true); + this->longMad = config.getOption("d3d11.longMad", false); // Clamp LOD bias so that people don't abuse this in unintended ways this->samplerLodBias = dxvk::fclamp(this->samplerLodBias, -2.0f, 1.0f); diff --git a/src/d3d11/d3d11_options.h b/src/d3d11/d3d11_options.h index fe9a2ab69..5cd548cd5 100644 --- a/src/d3d11/d3d11_options.h +++ b/src/d3d11/d3d11_options.h @@ -120,6 +120,9 @@ namespace dxvk { /// Shader dump path std::string shaderDumpPath; + + /// Should we make our Mads a FFma or do it the long way with an FMul and an FAdd? + bool longMad; }; } \ No newline at end of file diff --git a/src/dxbc/dxbc_compiler.cpp b/src/dxbc/dxbc_compiler.cpp index b679c3236..9975c87d5 100644 --- a/src/dxbc/dxbc_compiler.cpp +++ b/src/dxbc/dxbc_compiler.cpp @@ -1623,8 +1623,13 @@ namespace dxvk { case DxbcOpcode::Mad: case DxbcOpcode::DFma: - dst.id = m_module.opFFma(typeId, - src.at(0).id, src.at(1).id, src.at(2).id); + if (likely(!m_moduleInfo.options.longMad)) { + dst.id = m_module.opFFma(typeId, + src.at(0).id, src.at(1).id, src.at(2).id); + } else { + dst.id = m_module.opFMul(typeId, src.at(0).id, src.at(1).id); + dst.id = m_module.opFAdd(typeId, dst.id, src.at(2).id); + } break; case DxbcOpcode::Max: diff --git a/src/dxbc/dxbc_options.cpp b/src/dxbc/dxbc_options.cpp index e55a0f945..3d8d86c2c 100644 --- a/src/dxbc/dxbc_options.cpp +++ b/src/dxbc/dxbc_options.cpp @@ -38,6 +38,7 @@ namespace dxvk { disableMsaa = options.disableMsaa; forceSampleRateShading = options.forceSampleRateShading; enableSampleShadingInterlock = device->features().extFragmentShaderInterlock.fragmentShaderSampleInterlock; + longMad = options.longMad; // Figure out float control flags to match D3D11 rules if (options.floatControls) { diff --git a/src/dxbc/dxbc_options.h b/src/dxbc/dxbc_options.h index 27ecca1ff..4b21f2f88 100644 --- a/src/dxbc/dxbc_options.h +++ b/src/dxbc/dxbc_options.h @@ -54,6 +54,9 @@ namespace dxvk { /// Minimum storage buffer alignment VkDeviceSize minSsboAlignment = 0; + + /// Should we make our Mads a FFma or do it the long way with an FMul and an FAdd? + bool longMad; }; } \ No newline at end of file From 79398b468d5baf75a953052b19e0f011cdab034c Mon Sep 17 00:00:00 2001 From: Robin Kertels Date: Sat, 27 Apr 2024 15:42:49 +0200 Subject: [PATCH 135/246] [util] Enable longMad for Red Faction Guerrila Remastered --- src/util/config/config.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index da0932e63..b4ac39e98 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -420,6 +420,11 @@ namespace dxvk { { "d3d11.exposeDriverCommandLists", "False" }, { "dxgi.hideNvidiaGpu", "False" }, }} }, + /* Red Faction Guerrilla Re-Mars-tered * + * Broken skybox */ + { R"(\\rfg\.exe$)", {{ + { "d3d11.longMad", "True" }, + }} }, /**********************************************/ /* D3D9 GAMES */ From 29253da3563cc0ecce063f43b2bd1edaa7927e66 Mon Sep 17 00:00:00 2001 From: Lierrmm Date: Mon, 29 Apr 2024 14:53:08 +0100 Subject: [PATCH 136/246] feat: add H2M-Mod to config --- src/util/config/config.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index b4ac39e98..de8c22456 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -139,6 +139,10 @@ namespace dxvk { { R"(\\h1(_[ms]p64_ship|-mod)\.exe$)", {{ { "dxgi.customVendorId", "10de" }, }} }, + /* H2M-Mod - Modern Warfare Remastered */ + { R"(\\h2m-mod\.exe$)", {{ + { "dxgi.customVendorId", "10de" }, + }} }, /* Modern Warfare 2 Campaign Remastered * * AMD AGS crash same as above */ { R"(\\MW2CR\.exe$)", {{ From 65373792d2918fc7ce81d30d4da0b77cc6ceb527 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Mon, 29 Apr 2024 17:19:36 +0200 Subject: [PATCH 137/246] [dxvk] Forward link flags when using shader identifiers Fixes a long-standing bug that now causes validation errors. --- src/dxvk/dxvk_compute.cpp | 2 +- src/dxvk/dxvk_graphics.cpp | 13 +++++---- src/dxvk/dxvk_shader.cpp | 54 +++++++++++++++++++------------------- src/dxvk/dxvk_shader.h | 31 +++++++++++++++------- 4 files changed, 57 insertions(+), 43 deletions(-) diff --git a/src/dxvk/dxvk_compute.cpp b/src/dxvk/dxvk_compute.cpp index 93ebfa5e1..2a12806f3 100644 --- a/src/dxvk/dxvk_compute.cpp +++ b/src/dxvk/dxvk_compute.cpp @@ -49,7 +49,7 @@ namespace dxvk { // may wait for an ongoing compile job to finish, or // compile the pipeline immediately on the calling thread. m_libraryHandle = m_library->acquirePipelineHandle( - DxvkShaderPipelineLibraryCompileArgs()); + DxvkShaderPipelineLibraryCompileArgs()).handle; return m_libraryHandle; } else { diff --git a/src/dxvk/dxvk_graphics.cpp b/src/dxvk/dxvk_graphics.cpp index 76df237dc..65ef51977 100644 --- a/src/dxvk/dxvk_graphics.cpp +++ b/src/dxvk/dxvk_graphics.cpp @@ -1111,7 +1111,8 @@ namespace dxvk { if (doCreateBasePipeline) baseHandle = this->getBasePipeline(state); - else + + if (!baseHandle) fastHandle = this->getOptimizedPipeline(state); // Log pipeline state if requested, or on failure @@ -1236,10 +1237,11 @@ namespace dxvk { const DxvkGraphicsPipelineBaseInstanceKey& key) const { auto vk = m_device->vkd(); + DxvkShaderPipelineLibraryHandle vs = m_vsLibrary->acquirePipelineHandle(key.args); + DxvkShaderPipelineLibraryHandle fs = m_fsLibrary->acquirePipelineHandle(key.args); + std::array libraries = {{ - key.viLibrary->getHandle(), - m_vsLibrary->acquirePipelineHandle(key.args), - m_fsLibrary->acquirePipelineHandle(key.args), + key.viLibrary->getHandle(), vs.handle, fs.handle, key.foLibrary->getHandle(), }}; @@ -1248,13 +1250,14 @@ namespace dxvk { libInfo.pLibraries = libraries.data(); VkGraphicsPipelineCreateInfo info = { VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, &libInfo }; + info.flags = vs.linkFlags | fs.linkFlags; info.layout = m_bindings->getPipelineLayout(true); info.basePipelineIndex = -1; VkPipeline pipeline = VK_NULL_HANDLE; VkResult vr = vk->vkCreateGraphicsPipelines(vk->device(), VK_NULL_HANDLE, 1, &info, nullptr, &pipeline); - if (vr != VK_SUCCESS) + if (vr && vr != VK_PIPELINE_COMPILE_REQUIRED_EXT) Logger::err(str::format("DxvkGraphicsPipeline: Failed to create base pipeline: ", vr)); return pipeline; diff --git a/src/dxvk/dxvk_shader.cpp b/src/dxvk/dxvk_shader.cpp index 2748bc177..aa378cb82 100644 --- a/src/dxvk/dxvk_shader.cpp +++ b/src/dxvk/dxvk_shader.cpp @@ -1042,18 +1042,18 @@ namespace dxvk { } - VkPipeline DxvkShaderPipelineLibrary::acquirePipelineHandle( + DxvkShaderPipelineLibraryHandle DxvkShaderPipelineLibrary::acquirePipelineHandle( const DxvkShaderPipelineLibraryCompileArgs& args) { std::lock_guard lock(m_mutex); if (m_device->mustTrackPipelineLifetime()) m_useCount += 1; - VkPipeline& pipeline = (m_shaders.vs && !args.depthClipEnable) + DxvkShaderPipelineLibraryHandle& pipeline = (m_shaders.vs && !args.depthClipEnable) ? m_pipelineNoDepthClip : m_pipeline; - if (pipeline) + if (pipeline.handle) return pipeline; pipeline = compileShaderPipelineLocked(args); @@ -1079,7 +1079,7 @@ namespace dxvk { return; // Compile the pipeline with default args - VkPipeline pipeline = compileShaderPipelineLocked( + DxvkShaderPipelineLibraryHandle pipeline = compileShaderPipelineLocked( DxvkShaderPipelineLibraryCompileArgs()); // On 32-bit, destroy the pipeline immediately in order to @@ -1087,9 +1087,9 @@ namespace dxvk { // we need to recreate the pipeline. if (m_device->mustTrackPipelineLifetime()) { auto vk = m_device->vkd(); - vk->vkDestroyPipeline(vk->device(), pipeline, nullptr); + vk->vkDestroyPipeline(vk->device(), pipeline.handle, nullptr); - pipeline = VK_NULL_HANDLE; + pipeline.handle = VK_NULL_HANDLE; } // Write back pipeline handle for future use @@ -1100,34 +1100,34 @@ namespace dxvk { void DxvkShaderPipelineLibrary::destroyShaderPipelinesLocked() { auto vk = m_device->vkd(); - vk->vkDestroyPipeline(vk->device(), m_pipeline, nullptr); - vk->vkDestroyPipeline(vk->device(), m_pipelineNoDepthClip, nullptr); + vk->vkDestroyPipeline(vk->device(), m_pipeline.handle, nullptr); + vk->vkDestroyPipeline(vk->device(), m_pipelineNoDepthClip.handle, nullptr); - m_pipeline = VK_NULL_HANDLE; - m_pipelineNoDepthClip = VK_NULL_HANDLE; + m_pipeline.handle = VK_NULL_HANDLE; + m_pipelineNoDepthClip.handle = VK_NULL_HANDLE; } - VkPipeline DxvkShaderPipelineLibrary::compileShaderPipelineLocked( + DxvkShaderPipelineLibraryHandle DxvkShaderPipelineLibrary::compileShaderPipelineLocked( const DxvkShaderPipelineLibraryCompileArgs& args) { this->notifyLibraryCompile(); // If this is not the first time we're compiling the pipeline, // try to get a cache hit using the shader module identifier // so that we don't have to decompress our SPIR-V shader again. - VkPipeline pipeline = VK_NULL_HANDLE; + DxvkShaderPipelineLibraryHandle pipeline = { VK_NULL_HANDLE, 0 }; if (m_compiledOnce && canUsePipelineCacheControl()) { pipeline = this->compileShaderPipeline(args, VK_PIPELINE_CREATE_FAIL_ON_PIPELINE_COMPILE_REQUIRED_BIT); } - if (!pipeline) + if (!pipeline.handle) pipeline = this->compileShaderPipeline(args, 0); // Well that didn't work - if (!pipeline) - return VK_NULL_HANDLE; + if (!pipeline.handle) + return { VK_NULL_HANDLE, 0 }; // Increment stat counter the first time this // shader pipeline gets compiled successfully @@ -1144,7 +1144,7 @@ namespace dxvk { } - VkPipeline DxvkShaderPipelineLibrary::compileShaderPipeline( + DxvkShaderPipelineLibraryHandle DxvkShaderPipelineLibrary::compileShaderPipeline( const DxvkShaderPipelineLibraryCompileArgs& args, VkPipelineCreateFlags flags) { DxvkShaderStageInfo stageInfo(m_device); @@ -1161,7 +1161,7 @@ namespace dxvk { // Fail if we have no idenfitier for whatever reason, caller // should fall back to the slow path if this happens if (!identifier->identifierSize) - return VK_NULL_HANDLE; + return { VK_NULL_HANDLE, 0 }; stageInfo.addStage(stage, *identifier, nullptr); } else { @@ -1178,17 +1178,17 @@ namespace dxvk { } } + VkPipeline pipeline = VK_NULL_HANDLE; + if (stageMask & VK_SHADER_STAGE_VERTEX_BIT) - return compileVertexShaderPipeline(args, stageInfo, flags); - - if (stageMask & VK_SHADER_STAGE_FRAGMENT_BIT) - return compileFragmentShaderPipeline(stageInfo, flags); - - if (stageMask & VK_SHADER_STAGE_COMPUTE_BIT) - return compileComputeShaderPipeline(stageInfo, flags); + pipeline = compileVertexShaderPipeline(args, stageInfo, flags); + else if (stageMask & VK_SHADER_STAGE_FRAGMENT_BIT) + pipeline = compileFragmentShaderPipeline(stageInfo, flags); + else if (stageMask & VK_SHADER_STAGE_COMPUTE_BIT) + pipeline = compileComputeShaderPipeline(stageInfo, flags); // Should be unreachable - return VK_NULL_HANDLE; + return { pipeline, flags }; } @@ -1266,7 +1266,7 @@ namespace dxvk { VkPipeline pipeline = VK_NULL_HANDLE; VkResult vr = vk->vkCreateGraphicsPipelines(vk->device(), VK_NULL_HANDLE, 1, &info, nullptr, &pipeline); - if (vr && !(flags & VK_PIPELINE_CREATE_FAIL_ON_PIPELINE_COMPILE_REQUIRED_BIT)) + if (vr && vr != VK_PIPELINE_COMPILE_REQUIRED_EXT) Logger::err(str::format("DxvkShaderPipelineLibrary: Failed to create vertex shader pipeline: ", vr)); return vr ? VK_NULL_HANDLE : pipeline; @@ -1377,7 +1377,7 @@ namespace dxvk { VkPipeline pipeline = VK_NULL_HANDLE; VkResult vr = vk->vkCreateComputePipelines(vk->device(), VK_NULL_HANDLE, 1, &info, nullptr, &pipeline); - if (vr && !(flags & VK_PIPELINE_CREATE_FAIL_ON_PIPELINE_COMPILE_REQUIRED_BIT)) + if (vr && vr != VK_PIPELINE_COMPILE_REQUIRED_EXT) Logger::err(str::format("DxvkShaderPipelineLibrary: Failed to create compute shader pipeline: ", vr)); return vr ? VK_NULL_HANDLE : pipeline; diff --git a/src/dxvk/dxvk_shader.h b/src/dxvk/dxvk_shader.h index bc138f27d..8e21197c1 100644 --- a/src/dxvk/dxvk_shader.h +++ b/src/dxvk/dxvk_shader.h @@ -482,6 +482,17 @@ namespace dxvk { }; + /** + * \brief Pipeline library handle + * + * Stores a pipeline library handle and the necessary link flags. + */ + struct DxvkShaderPipelineLibraryHandle { + VkPipeline handle; + VkPipelineCreateFlags linkFlags; + }; + + /** * \brief Shader pipeline library * @@ -524,7 +535,7 @@ namespace dxvk { * \param [in] args Compile arguments * \returns Vulkan pipeline handle */ - VkPipeline acquirePipelineHandle( + DxvkShaderPipelineLibraryHandle acquirePipelineHandle( const DxvkShaderPipelineLibraryCompileArgs& args); /** @@ -552,21 +563,21 @@ namespace dxvk { DxvkShaderSet m_shaders; const DxvkBindingLayoutObjects* m_layout; - dxvk::mutex m_mutex; - VkPipeline m_pipeline = VK_NULL_HANDLE; - VkPipeline m_pipelineNoDepthClip = VK_NULL_HANDLE; - uint32_t m_useCount = 0u; - bool m_compiledOnce = false; + dxvk::mutex m_mutex; + DxvkShaderPipelineLibraryHandle m_pipeline = { VK_NULL_HANDLE, 0 }; + DxvkShaderPipelineLibraryHandle m_pipelineNoDepthClip = { VK_NULL_HANDLE, 0 }; + uint32_t m_useCount = 0u; + bool m_compiledOnce = false; - dxvk::mutex m_identifierMutex; - DxvkShaderIdentifierSet m_identifiers; + dxvk::mutex m_identifierMutex; + DxvkShaderIdentifierSet m_identifiers; void destroyShaderPipelinesLocked(); - VkPipeline compileShaderPipelineLocked( + DxvkShaderPipelineLibraryHandle compileShaderPipelineLocked( const DxvkShaderPipelineLibraryCompileArgs& args); - VkPipeline compileShaderPipeline( + DxvkShaderPipelineLibraryHandle compileShaderPipeline( const DxvkShaderPipelineLibraryCompileArgs& args, VkPipelineCreateFlags flags); From ea4cb84d8af447f195d3ac7987c05403371634a8 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Mon, 29 Apr 2024 17:38:22 +0200 Subject: [PATCH 138/246] [dxvk] Remove workaround for non-dynamic depth clip Kind of pointless and everyone supports the required EDS3 subset anyway. --- src/dxvk/dxvk_compute.cpp | 4 +--- src/dxvk/dxvk_graphics.cpp | 15 ++++++++----- src/dxvk/dxvk_graphics.h | 5 +---- src/dxvk/dxvk_shader.cpp | 45 ++++++++++++++------------------------ src/dxvk/dxvk_shader.h | 38 +++++--------------------------- 5 files changed, 34 insertions(+), 73 deletions(-) diff --git a/src/dxvk/dxvk_compute.cpp b/src/dxvk/dxvk_compute.cpp index 2a12806f3..04972b0c0 100644 --- a/src/dxvk/dxvk_compute.cpp +++ b/src/dxvk/dxvk_compute.cpp @@ -48,9 +48,7 @@ namespace dxvk { // Retrieve actual pipeline handle on first use. This // may wait for an ongoing compile job to finish, or // compile the pipeline immediately on the calling thread. - m_libraryHandle = m_library->acquirePipelineHandle( - DxvkShaderPipelineLibraryCompileArgs()).handle; - + m_libraryHandle = m_library->acquirePipelineHandle().handle; return m_libraryHandle; } else { // Slow path for compute shaders that do use spec constants diff --git a/src/dxvk/dxvk_graphics.cpp b/src/dxvk/dxvk_graphics.cpp index 65ef51977..3092f350e 100644 --- a/src/dxvk/dxvk_graphics.cpp +++ b/src/dxvk/dxvk_graphics.cpp @@ -1112,6 +1112,7 @@ namespace dxvk { if (doCreateBasePipeline) baseHandle = this->getBasePipeline(state); + // Fast-linking may fail in some situations if (!baseHandle) fastHandle = this->getOptimizedPipeline(state); @@ -1149,6 +1150,13 @@ namespace dxvk { || (state.rs.lineMode() != VK_LINE_RASTERIZATION_MODE_DEFAULT_EXT && isLineRendering)) return false; + // Depth clip is assumed to be enabled. If the driver does not + // support dynamic depth clip, we'd have to late-compile anyway + // unless the pipeline is used multiple times. + if (!m_device->features().extExtendedDynamicState3.extendedDynamicState3DepthClipEnable + && !state.rs.depthClipEnable()) + return false; + if (m_shaders.tcs != nullptr) { // If tessellation shaders are present, the input patch // vertex count must match the shader's definition. @@ -1220,9 +1228,6 @@ namespace dxvk { key.viLibrary = m_manager->createVertexInputLibrary(viState); key.foLibrary = m_manager->createFragmentOutputLibrary(foState); - if (!m_device->features().extExtendedDynamicState3.extendedDynamicState3DepthClipEnable) - key.args.depthClipEnable = state.rs.depthClipEnable(); - auto entry = m_basePipelines.find(key); if (entry != m_basePipelines.end()) return entry->second; @@ -1237,8 +1242,8 @@ namespace dxvk { const DxvkGraphicsPipelineBaseInstanceKey& key) const { auto vk = m_device->vkd(); - DxvkShaderPipelineLibraryHandle vs = m_vsLibrary->acquirePipelineHandle(key.args); - DxvkShaderPipelineLibraryHandle fs = m_fsLibrary->acquirePipelineHandle(key.args); + DxvkShaderPipelineLibraryHandle vs = m_vsLibrary->acquirePipelineHandle(); + DxvkShaderPipelineLibraryHandle fs = m_fsLibrary->acquirePipelineHandle(); std::array libraries = {{ key.viLibrary->getHandle(), vs.handle, fs.handle, diff --git a/src/dxvk/dxvk_graphics.h b/src/dxvk/dxvk_graphics.h index 435677a85..4f618f783 100644 --- a/src/dxvk/dxvk_graphics.h +++ b/src/dxvk/dxvk_graphics.h @@ -381,19 +381,16 @@ namespace dxvk { struct DxvkGraphicsPipelineBaseInstanceKey { const DxvkGraphicsPipelineVertexInputLibrary* viLibrary = nullptr; const DxvkGraphicsPipelineFragmentOutputLibrary* foLibrary = nullptr; - DxvkShaderPipelineLibraryCompileArgs args; bool eq(const DxvkGraphicsPipelineBaseInstanceKey& other) const { return viLibrary == other.viLibrary - && foLibrary == other.foLibrary - && args == other.args; + && foLibrary == other.foLibrary; } size_t hash() const { DxvkHashState hash; hash.add(size_t(viLibrary)); hash.add(size_t(foLibrary)); - hash.add(args.hash()); return hash; } }; diff --git a/src/dxvk/dxvk_shader.cpp b/src/dxvk/dxvk_shader.cpp index aa378cb82..bdd375604 100644 --- a/src/dxvk/dxvk_shader.cpp +++ b/src/dxvk/dxvk_shader.cpp @@ -1022,7 +1022,7 @@ namespace dxvk { DxvkShaderPipelineLibrary::~DxvkShaderPipelineLibrary() { - this->destroyShaderPipelinesLocked(); + this->destroyShaderPipelineLocked(); } @@ -1042,22 +1042,17 @@ namespace dxvk { } - DxvkShaderPipelineLibraryHandle DxvkShaderPipelineLibrary::acquirePipelineHandle( - const DxvkShaderPipelineLibraryCompileArgs& args) { + DxvkShaderPipelineLibraryHandle DxvkShaderPipelineLibrary::acquirePipelineHandle() { std::lock_guard lock(m_mutex); if (m_device->mustTrackPipelineLifetime()) m_useCount += 1; - DxvkShaderPipelineLibraryHandle& pipeline = (m_shaders.vs && !args.depthClipEnable) - ? m_pipelineNoDepthClip - : m_pipeline; + if (m_pipeline.handle) + return m_pipeline; - if (pipeline.handle) - return pipeline; - - pipeline = compileShaderPipelineLocked(args); - return pipeline; + m_pipeline = compileShaderPipelineLocked(); + return m_pipeline; } @@ -1066,7 +1061,7 @@ namespace dxvk { std::lock_guard lock(m_mutex); if (!(--m_useCount)) - this->destroyShaderPipelinesLocked(); + this->destroyShaderPipelineLocked(); } } @@ -1079,8 +1074,7 @@ namespace dxvk { return; // Compile the pipeline with default args - DxvkShaderPipelineLibraryHandle pipeline = compileShaderPipelineLocked( - DxvkShaderPipelineLibraryCompileArgs()); + DxvkShaderPipelineLibraryHandle pipeline = compileShaderPipelineLocked(); // On 32-bit, destroy the pipeline immediately in order to // save memory. We should hit the driver's disk cache once @@ -1097,19 +1091,16 @@ namespace dxvk { } - void DxvkShaderPipelineLibrary::destroyShaderPipelinesLocked() { + void DxvkShaderPipelineLibrary::destroyShaderPipelineLocked() { auto vk = m_device->vkd(); vk->vkDestroyPipeline(vk->device(), m_pipeline.handle, nullptr); - vk->vkDestroyPipeline(vk->device(), m_pipelineNoDepthClip.handle, nullptr); m_pipeline.handle = VK_NULL_HANDLE; - m_pipelineNoDepthClip.handle = VK_NULL_HANDLE; } - DxvkShaderPipelineLibraryHandle DxvkShaderPipelineLibrary::compileShaderPipelineLocked( - const DxvkShaderPipelineLibraryCompileArgs& args) { + DxvkShaderPipelineLibraryHandle DxvkShaderPipelineLibrary::compileShaderPipelineLocked() { this->notifyLibraryCompile(); // If this is not the first time we're compiling the pipeline, @@ -1117,13 +1108,11 @@ namespace dxvk { // so that we don't have to decompress our SPIR-V shader again. DxvkShaderPipelineLibraryHandle pipeline = { VK_NULL_HANDLE, 0 }; - if (m_compiledOnce && canUsePipelineCacheControl()) { - pipeline = this->compileShaderPipeline(args, - VK_PIPELINE_CREATE_FAIL_ON_PIPELINE_COMPILE_REQUIRED_BIT); - } + if (m_compiledOnce && canUsePipelineCacheControl()) + pipeline = this->compileShaderPipeline(VK_PIPELINE_CREATE_FAIL_ON_PIPELINE_COMPILE_REQUIRED_BIT); if (!pipeline.handle) - pipeline = this->compileShaderPipeline(args, 0); + pipeline = this->compileShaderPipeline(0); // Well that didn't work if (!pipeline.handle) @@ -1145,7 +1134,6 @@ namespace dxvk { DxvkShaderPipelineLibraryHandle DxvkShaderPipelineLibrary::compileShaderPipeline( - const DxvkShaderPipelineLibraryCompileArgs& args, VkPipelineCreateFlags flags) { DxvkShaderStageInfo stageInfo(m_device); VkShaderStageFlags stageMask = getShaderStages(); @@ -1181,7 +1169,7 @@ namespace dxvk { VkPipeline pipeline = VK_NULL_HANDLE; if (stageMask & VK_SHADER_STAGE_VERTEX_BIT) - pipeline = compileVertexShaderPipeline(args, stageInfo, flags); + pipeline = compileVertexShaderPipeline(stageInfo, flags); else if (stageMask & VK_SHADER_STAGE_FRAGMENT_BIT) pipeline = compileFragmentShaderPipeline(stageInfo, flags); else if (stageMask & VK_SHADER_STAGE_COMPUTE_BIT) @@ -1193,7 +1181,6 @@ namespace dxvk { VkPipeline DxvkShaderPipelineLibrary::compileVertexShaderPipeline( - const DxvkShaderPipelineLibraryCompileArgs& args, const DxvkShaderStageInfo& stageInfo, VkPipelineCreateFlags flags) { auto vk = m_device->vkd(); @@ -1240,10 +1227,10 @@ namespace dxvk { // Only use the fixed depth clip state if we can't make it dynamic if (!m_device->features().extExtendedDynamicState3.extendedDynamicState3DepthClipEnable) { rsDepthClipInfo.pNext = std::exchange(rsInfo.pNext, &rsDepthClipInfo); - rsDepthClipInfo.depthClipEnable = args.depthClipEnable; + rsDepthClipInfo.depthClipEnable = VK_TRUE; } } else { - rsInfo.depthClampEnable = !args.depthClipEnable; + rsInfo.depthClampEnable = VK_FALSE; } // Only the view mask is used as input, and since we do not use MultiView, it is always 0 diff --git a/src/dxvk/dxvk_shader.h b/src/dxvk/dxvk_shader.h index 8e21197c1..3d4ad8713 100644 --- a/src/dxvk/dxvk_shader.h +++ b/src/dxvk/dxvk_shader.h @@ -368,26 +368,6 @@ namespace dxvk { }; - /** - * \brief Shader pipeline library compile args - */ - struct DxvkShaderPipelineLibraryCompileArgs { - VkBool32 depthClipEnable = VK_TRUE; - - bool operator == (const DxvkShaderPipelineLibraryCompileArgs& other) const { - return depthClipEnable == other.depthClipEnable; - } - - bool operator != (const DxvkShaderPipelineLibraryCompileArgs& other) const { - return !this->operator == (other); - } - - size_t hash() const { - return size_t(depthClipEnable); - } - }; - - /** * \brief Shader set * @@ -532,11 +512,9 @@ namespace dxvk { * Either returns an already compiled pipeline library object, or * performs the compilation step if that has not happened yet. * Increments the use count by one. - * \param [in] args Compile arguments * \returns Vulkan pipeline handle */ - DxvkShaderPipelineLibraryHandle acquirePipelineHandle( - const DxvkShaderPipelineLibraryCompileArgs& args); + DxvkShaderPipelineLibraryHandle acquirePipelineHandle(); /** * \brief Releases pipeline @@ -564,25 +542,21 @@ namespace dxvk { const DxvkBindingLayoutObjects* m_layout; dxvk::mutex m_mutex; - DxvkShaderPipelineLibraryHandle m_pipeline = { VK_NULL_HANDLE, 0 }; - DxvkShaderPipelineLibraryHandle m_pipelineNoDepthClip = { VK_NULL_HANDLE, 0 }; - uint32_t m_useCount = 0u; - bool m_compiledOnce = false; + DxvkShaderPipelineLibraryHandle m_pipeline = { VK_NULL_HANDLE, 0 }; + uint32_t m_useCount = 0u; + bool m_compiledOnce = false; dxvk::mutex m_identifierMutex; DxvkShaderIdentifierSet m_identifiers; - void destroyShaderPipelinesLocked(); + void destroyShaderPipelineLocked(); - DxvkShaderPipelineLibraryHandle compileShaderPipelineLocked( - const DxvkShaderPipelineLibraryCompileArgs& args); + DxvkShaderPipelineLibraryHandle compileShaderPipelineLocked(); DxvkShaderPipelineLibraryHandle compileShaderPipeline( - const DxvkShaderPipelineLibraryCompileArgs& args, VkPipelineCreateFlags flags); VkPipeline compileVertexShaderPipeline( - const DxvkShaderPipelineLibraryCompileArgs& args, const DxvkShaderStageInfo& stageInfo, VkPipelineCreateFlags flags); From dacb8b434b4da26b81ba859826b0dd15e2a7a765 Mon Sep 17 00:00:00 2001 From: Blisto91 Date: Tue, 30 Apr 2024 19:10:43 +0200 Subject: [PATCH 139/246] [util] Add configs for Delta Force Xtreme 1 & 2 Prevents the games from black screening on Alt-Tab and helps big performance dips. --- src/util/config/config.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index de8c22456..14239ffd2 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -883,6 +883,12 @@ namespace dxvk { { R"(\\ninthdawnii\.exe$)", {{ { "d3d9.deferSurfaceCreation", "True" }, }} }, + /* Delta Force: Xtreme 1 & 2 * + * Black screen on Alt-Tab and performance */ + { R"(\\(DFX|dfx2)\.exe$)", {{ + { "d3d9.deviceLossOnFocusLoss", "True" }, + { "d3d9.cachedDynamicBuffers", "True" }, + }} }, /**********************************************/ From b99d42c6889b27a609e31b8701a72b77c4f356ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 24 Apr 2024 14:02:18 +0200 Subject: [PATCH 140/246] [d3d11] Implement VideoProcessorSetStreamSourceRect scaling --- src/d3d11/d3d11_video.cpp | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/d3d11/d3d11_video.cpp b/src/d3d11/d3d11_video.cpp index 3851a4981..5318be9e5 100644 --- a/src/d3d11/d3d11_video.cpp +++ b/src/d3d11/d3d11_video.cpp @@ -1262,12 +1262,29 @@ namespace dxvk { viewport.height = float(cStreamState.dstRect.bottom) - viewport.y; } + VkExtent3D viewExtent = cViews[0]->mipLevelExtent(0); + VkViewport srcViewport; + + if (cStreamState.srcRectEnabled) { + srcViewport.x = float(cStreamState.srcRect.left); + srcViewport.y = float(cStreamState.srcRect.top); + srcViewport.width = float(cStreamState.srcRect.right) - srcViewport.x; + srcViewport.height = float(cStreamState.srcRect.bottom) - srcViewport.y; + } else { + srcViewport.x = 0.0f; + srcViewport.y = 0.0f; + srcViewport.width = float(viewExtent.width); + srcViewport.height = float(viewExtent.height); + } + UboData uboData = { }; uboData.colorMatrix[0][0] = 1.0f; uboData.colorMatrix[1][1] = 1.0f; uboData.colorMatrix[2][2] = 1.0f; - uboData.coordMatrix[0][0] = 1.0f; - uboData.coordMatrix[1][1] = 1.0f; + uboData.coordMatrix[0][0] = srcViewport.width / float(viewExtent.width); + uboData.coordMatrix[1][1] = srcViewport.height / float(viewExtent.height); + uboData.coordMatrix[2][0] = srcViewport.x / float(viewExtent.width); + uboData.coordMatrix[2][1] = srcViewport.y / float(viewExtent.height); uboData.yMin = 0.0f; uboData.yMax = 1.0f; uboData.isPlanar = cViews[1] != nullptr; From 4333ee872dfbc26110a34267fdb22af23bc640dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 24 Apr 2024 14:02:46 +0200 Subject: [PATCH 141/246] [d3d11] Use nearest filter for ID3D11VideoContext scaling --- src/d3d11/d3d11_video.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/d3d11/d3d11_video.cpp b/src/d3d11/d3d11_video.cpp index 5318be9e5..652fea5cb 100644 --- a/src/d3d11/d3d11_video.cpp +++ b/src/d3d11/d3d11_video.cpp @@ -1334,7 +1334,7 @@ namespace dxvk { void D3D11VideoContext::CreateSampler() { DxvkSamplerCreateInfo samplerInfo; - samplerInfo.magFilter = VK_FILTER_LINEAR; + samplerInfo.magFilter = VK_FILTER_NEAREST; samplerInfo.minFilter = VK_FILTER_LINEAR; samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST; samplerInfo.mipmapLodBias = 0.0f; From 1fb35b6d19dab1ee3c1eeb74513af45509e1137f Mon Sep 17 00:00:00 2001 From: talkingerbil <72637784+talkingerbil@users.noreply.github.com> Date: Fri, 3 May 2024 07:22:58 -0700 Subject: [PATCH 142/246] [dxgi] Initialize UMD version quad to a max signed int64 (#3985) --- src/dxgi/dxgi_adapter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dxgi/dxgi_adapter.cpp b/src/dxgi/dxgi_adapter.cpp index 2ee5d2cd7..b6bfe61e9 100644 --- a/src/dxgi/dxgi_adapter.cpp +++ b/src/dxgi/dxgi_adapter.cpp @@ -130,7 +130,7 @@ namespace dxvk { // We can't really reconstruct the version numbers // returned by Windows drivers from Vulkan data if (SUCCEEDED(hr) && pUMDVersion) - pUMDVersion->QuadPart = ~0ull; + pUMDVersion->QuadPart = INT64_MAX; if (FAILED(hr)) { Logger::err("DXGI: CheckInterfaceSupport: Unsupported interface"); From ab715a8876cf1ac48591352a88d02c07e0fdd541 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Fri, 3 May 2024 04:24:15 +0200 Subject: [PATCH 143/246] [d3d11] Implement better filtering when blitting video content Unlike linear filtering this guarantees that we never read outside the source region, and this also lets us perform color space conversion prior to filtering. --- src/d3d11/d3d11_video.cpp | 63 +++++----------- src/d3d11/d3d11_video.h | 4 +- src/d3d11/shaders/d3d11_video_blit_frag.frag | 77 +++++++++++++++----- 3 files changed, 76 insertions(+), 68 deletions(-) diff --git a/src/d3d11/d3d11_video.cpp b/src/d3d11/d3d11_video.cpp index 652fea5cb..1543528ae 100644 --- a/src/d3d11/d3d11_video.cpp +++ b/src/d3d11/d3d11_video.cpp @@ -1263,28 +1263,27 @@ namespace dxvk { } VkExtent3D viewExtent = cViews[0]->mipLevelExtent(0); - VkViewport srcViewport; + + VkRect2D srcRect; + srcRect.offset = { 0, 0 }; + srcRect.extent = { viewExtent.width, viewExtent.height }; if (cStreamState.srcRectEnabled) { - srcViewport.x = float(cStreamState.srcRect.left); - srcViewport.y = float(cStreamState.srcRect.top); - srcViewport.width = float(cStreamState.srcRect.right) - srcViewport.x; - srcViewport.height = float(cStreamState.srcRect.bottom) - srcViewport.y; - } else { - srcViewport.x = 0.0f; - srcViewport.y = 0.0f; - srcViewport.width = float(viewExtent.width); - srcViewport.height = float(viewExtent.height); + srcRect.offset.x = cStreamState.srcRect.left; + srcRect.offset.y = cStreamState.srcRect.top; + srcRect.extent.width = cStreamState.srcRect.right - srcRect.offset.x; + srcRect.extent.height = cStreamState.srcRect.bottom - srcRect.offset.y; } UboData uboData = { }; uboData.colorMatrix[0][0] = 1.0f; uboData.colorMatrix[1][1] = 1.0f; uboData.colorMatrix[2][2] = 1.0f; - uboData.coordMatrix[0][0] = srcViewport.width / float(viewExtent.width); - uboData.coordMatrix[1][1] = srcViewport.height / float(viewExtent.height); - uboData.coordMatrix[2][0] = srcViewport.x / float(viewExtent.width); - uboData.coordMatrix[2][1] = srcViewport.y / float(viewExtent.height); + uboData.coordMatrix[0][0] = float(srcRect.extent.width) / float(viewExtent.width); + uboData.coordMatrix[1][1] = float(srcRect.extent.height) / float(viewExtent.height); + uboData.coordMatrix[2][0] = float(srcRect.offset.x) / float(viewExtent.width); + uboData.coordMatrix[2][1] = float(srcRect.offset.y) / float(viewExtent.height); + uboData.srcRect = srcRect; uboData.yMin = 0.0f; uboData.yMax = 1.0f; uboData.isPlanar = cViews[1] != nullptr; @@ -1307,17 +1306,14 @@ namespace dxvk { ctx->bindShader(Rc(m_fs)); ctx->bindUniformBuffer(VK_SHADER_STAGE_FRAGMENT_BIT, 0, DxvkBufferSlice(m_ubo)); - ctx->bindResourceSampler(VK_SHADER_STAGE_FRAGMENT_BIT, 1, Rc(m_sampler)); for (uint32_t i = 0; i < cViews.size(); i++) - ctx->bindResourceImageView(VK_SHADER_STAGE_FRAGMENT_BIT, 2 + i, Rc(cViews[i])); + ctx->bindResourceImageView(VK_SHADER_STAGE_FRAGMENT_BIT, 1 + i, Rc(cViews[i])); ctx->draw(3, 1, 0, 0); - ctx->bindResourceSampler(VK_SHADER_STAGE_FRAGMENT_BIT, 1, nullptr); - for (uint32_t i = 0; i < cViews.size(); i++) - ctx->bindResourceImageView(VK_SHADER_STAGE_FRAGMENT_BIT, 2 + i, nullptr); + ctx->bindResourceImageView(VK_SHADER_STAGE_FRAGMENT_BIT, 1 + i, nullptr); }); } @@ -1332,38 +1328,14 @@ namespace dxvk { } - void D3D11VideoContext::CreateSampler() { - DxvkSamplerCreateInfo samplerInfo; - samplerInfo.magFilter = VK_FILTER_NEAREST; - samplerInfo.minFilter = VK_FILTER_LINEAR; - samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST; - samplerInfo.mipmapLodBias = 0.0f; - samplerInfo.mipmapLodMin = 0.0f; - samplerInfo.mipmapLodMax = 0.0f; - samplerInfo.useAnisotropy = VK_FALSE; - samplerInfo.maxAnisotropy = 1.0f; - samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; - samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; - samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; - samplerInfo.compareToDepth = VK_FALSE; - samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS; - samplerInfo.reductionMode = VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE; - samplerInfo.borderColor = VkClearColorValue(); - samplerInfo.usePixelCoord = VK_FALSE; - samplerInfo.nonSeamless = VK_FALSE; - m_sampler = m_device->createSampler(samplerInfo); - } - - void D3D11VideoContext::CreateShaders() { SpirvCodeBuffer vsCode(d3d11_video_blit_vert); SpirvCodeBuffer fsCode(d3d11_video_blit_frag); - const std::array fsBindings = {{ + const std::array fsBindings = {{ { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, VK_IMAGE_VIEW_TYPE_MAX_ENUM, VK_SHADER_STAGE_FRAGMENT_BIT, VK_ACCESS_UNIFORM_READ_BIT, VK_TRUE }, - { VK_DESCRIPTOR_TYPE_SAMPLER, 1, VK_IMAGE_VIEW_TYPE_MAX_ENUM, VK_SHADER_STAGE_FRAGMENT_BIT, 0 }, + { VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1, VK_IMAGE_VIEW_TYPE_2D, VK_SHADER_STAGE_FRAGMENT_BIT, VK_ACCESS_SHADER_READ_BIT }, { VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 2, VK_IMAGE_VIEW_TYPE_2D, VK_SHADER_STAGE_FRAGMENT_BIT, VK_ACCESS_SHADER_READ_BIT }, - { VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 3, VK_IMAGE_VIEW_TYPE_2D, VK_SHADER_STAGE_FRAGMENT_BIT, VK_ACCESS_SHADER_READ_BIT }, }}; DxvkShaderCreateInfo vsInfo; @@ -1385,7 +1357,6 @@ namespace dxvk { if (std::exchange(m_resourcesCreated, true)) return; - CreateSampler(); CreateUniformBuffer(); CreateShaders(); } diff --git a/src/d3d11/d3d11_video.h b/src/d3d11/d3d11_video.h index 30288a586..cac29f914 100644 --- a/src/d3d11/d3d11_video.h +++ b/src/d3d11/d3d11_video.h @@ -584,6 +584,7 @@ namespace dxvk { struct alignas(16) UboData { float colorMatrix[3][4]; float coordMatrix[3][2]; + VkRect2D srcRect; float yMin, yMax; VkBool32 isPlanar; }; @@ -593,7 +594,6 @@ namespace dxvk { Rc m_device; Rc m_vs; Rc m_fs; - Rc m_sampler; Rc m_ubo; VkExtent2D m_dstExtent = { 0u, 0u }; @@ -613,8 +613,6 @@ namespace dxvk { void CreateUniformBuffer(); - void CreateSampler(); - void CreateShaders(); void CreateResources(); diff --git a/src/d3d11/shaders/d3d11_video_blit_frag.frag b/src/d3d11/shaders/d3d11_video_blit_frag.frag index ff54aedaf..96b9b50f7 100644 --- a/src/d3d11/shaders/d3d11_video_blit_frag.frag +++ b/src/d3d11/shaders/d3d11_video_blit_frag.frag @@ -1,5 +1,7 @@ #version 450 +#extension GL_EXT_samplerless_texture_functions : require + // Can't use matrix types here since even a two-row // matrix will be padded to 16 bytes per column for // absolutely no reason @@ -11,6 +13,8 @@ uniform ubo_t { vec2 coord_matrix_c1; vec2 coord_matrix_c2; vec2 coord_matrix_c3; + uvec2 src_offset; + uvec2 src_extent; float y_min; float y_max; bool is_planar; @@ -19,9 +23,8 @@ uniform ubo_t { layout(location = 0) in vec2 i_texcoord; layout(location = 0) out vec4 o_color; -layout(set = 0, binding = 1) uniform sampler s_sampler; -layout(set = 0, binding = 2) uniform texture2D s_inputY; -layout(set = 0, binding = 3) uniform texture2D s_inputCbCr; +layout(set = 0, binding = 1) uniform texture2D s_inputY; +layout(set = 0, binding = 2) uniform texture2D s_inputCbCr; void main() { // Transform input texture coordinates to @@ -31,25 +34,61 @@ void main() { coord_matrix_c2, coord_matrix_c3); - vec2 coord = coord_matrix * vec3(i_texcoord, 1.0f); - - // Fetch source image color - vec4 color = vec4(0.0f, 0.0f, 0.0f, 1.0f); - - if (is_planar) { - color.g = texture(sampler2D(s_inputY, s_sampler), coord).r; - color.rb = texture(sampler2D(s_inputCbCr, s_sampler), coord).gr; - color.g = clamp((color.g - y_min) / (y_max - y_min), 0.0f, 1.0f); - } else { - color = texture(sampler2D(s_inputY, s_sampler), coord); - } - - // Color space transformation + // Load color space transform mat3x4 color_matrix = mat3x4( color_matrix_r1, color_matrix_r2, color_matrix_r3); - o_color.rgb = vec4(color.rgb, 1.0f) * color_matrix; - o_color.a = color.a; + // Compute actual pixel coordinates to sample. We filter + // manually in order to avoid bleeding from pixels outside + // the source rectangle. + vec2 abs_size_y = vec2(textureSize(s_inputY, 0)); + vec2 abs_size_c = vec2(textureSize(s_inputCbCr, 0)); + + vec2 coord = coord_matrix * vec3(i_texcoord, 1.0f); + coord -= 0.5f / abs_size_y; + + vec2 size_factor = abs_size_c / abs_size_y; + + vec2 src_lo = vec2(src_offset); + vec2 src_hi = vec2(src_offset + src_extent - 1u); + + vec2 abs_coord = coord * abs_size_y; + vec2 fract_coord = fract(clamp(abs_coord, src_lo, src_hi)); + + vec4 accum = vec4(0.0f, 0.0f, 0.0f, 0.0f); + + for (int i = 0; i < 4; i++) { + ivec2 offset = ivec2(i & 1, i >> 1); + + // Compute exact pixel coordinates for the current + // iteration and clamp it to the source rectangle. + vec2 fetch_coord = clamp(abs_coord + vec2(offset), src_lo, src_hi); + + // Fetch actual pixel color in source color space + vec4 color; + + if (is_planar) { + color.g = texelFetch(s_inputY, ivec2(fetch_coord), 0).r; + color.rb = texelFetch(s_inputCbCr, ivec2(fetch_coord * size_factor), 0).gr; + color.g = clamp((color.g - y_min) / (y_max - y_min), 0.0f, 1.0f); + color.a = 1.0f; + } else { + color = texelFetch(s_inputY, ivec2(fetch_coord), 0); + } + + // Transform color space before accumulation + color.rgb = vec4(color.rgb, 1.0f) * color_matrix; + + // Filter and accumulate final pixel color + vec2 factor = fract_coord; + + if (offset.x == 0) factor.x = 1.0f - factor.x; + if (offset.y == 0) factor.y = 1.0f - factor.y; + + accum += factor.x * factor.y * color; + } + + o_color = accum; } From b2789ab894de9adfd0a4140d3260f5c3f0374eae Mon Sep 17 00:00:00 2001 From: WinterSnowfall Date: Thu, 2 May 2024 16:25:46 +0300 Subject: [PATCH 144/246] [d3d9] Validate DS format support during CheckDepthStencilMatch --- src/d3d9/d3d9_adapter.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/d3d9/d3d9_adapter.cpp b/src/d3d9/d3d9_adapter.cpp index e0f1a185f..aa14c90aa 100644 --- a/src/d3d9/d3d9_adapter.cpp +++ b/src/d3d9/d3d9_adapter.cpp @@ -224,11 +224,15 @@ namespace dxvk { if (!IsDepthFormat(DepthStencilFormat)) return D3DERR_NOTAVAILABLE; + auto dsfMapping = ConvertFormatUnfixed(DepthStencilFormat); + if (dsfMapping.FormatColor == VK_FORMAT_UNDEFINED) + return D3DERR_NOTAVAILABLE; + if (RenderTargetFormat == dxvk::D3D9Format::NULL_FORMAT) return D3D_OK; - auto mapping = ConvertFormatUnfixed(RenderTargetFormat); - if (mapping.FormatColor == VK_FORMAT_UNDEFINED) + auto rtfMapping = ConvertFormatUnfixed(RenderTargetFormat); + if (rtfMapping.FormatColor == VK_FORMAT_UNDEFINED) return D3DERR_NOTAVAILABLE; return D3D_OK; From 611dc60018391694ec56876638228a2b9c93b4f1 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Wed, 8 May 2024 17:01:18 +0200 Subject: [PATCH 145/246] [d3d9] Do not support cube textures with depth formats --- src/d3d9/d3d9_adapter.cpp | 3 +++ src/d3d9/d3d9_common_texture.cpp | 6 ++++++ src/d3d9/d3d9_common_texture.h | 3 +++ src/d3d9/d3d9_device.cpp | 14 +++++++------- 4 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/d3d9/d3d9_adapter.cpp b/src/d3d9/d3d9_adapter.cpp index aa14c90aa..d320dc6b2 100644 --- a/src/d3d9/d3d9_adapter.cpp +++ b/src/d3d9/d3d9_adapter.cpp @@ -162,6 +162,9 @@ namespace dxvk { if (mapping.FormatSrgb == VK_FORMAT_UNDEFINED && srgb) return D3DERR_NOTAVAILABLE; + if (RType == D3DRTYPE_CUBETEXTURE && mapping.Aspect != VK_IMAGE_ASPECT_COLOR_BIT) + return D3DERR_NOTAVAILABLE; + if (RType == D3DRTYPE_VERTEXBUFFER || RType == D3DRTYPE_INDEXBUFFER) return D3D_OK; diff --git a/src/d3d9/d3d9_common_texture.cpp b/src/d3d9/d3d9_common_texture.cpp index 1f66a626a..ba8a3140a 100644 --- a/src/d3d9/d3d9_common_texture.cpp +++ b/src/d3d9/d3d9_common_texture.cpp @@ -118,6 +118,7 @@ namespace dxvk { HRESULT D3D9CommonTexture::NormalizeTextureProperties( D3D9DeviceEx* pDevice, + D3DRESOURCETYPE ResourceType, D3D9_COMMON_TEXTURE_DESC* pDesc) { auto* options = pDevice->GetOptions(); @@ -131,6 +132,11 @@ namespace dxvk { options->disableA8RT) return D3DERR_INVALIDCALL; + // Cube textures with depth formats are not supported on any native + // driver, and allowing them triggers a broken code path in Gothic 3. + if (ResourceType == D3DRTYPE_CUBETEXTURE && mapping.Aspect != VK_IMAGE_ASPECT_COLOR_BIT) + return D3DERR_INVALIDCALL; + // If the mapping is invalid then lets return invalid // Some edge cases: // NULL format does not map to anything, but should succeed diff --git a/src/d3d9/d3d9_common_texture.h b/src/d3d9/d3d9_common_texture.h index 37b5bc3ae..a83b14ac4 100644 --- a/src/d3d9/d3d9_common_texture.h +++ b/src/d3d9/d3d9_common_texture.h @@ -179,11 +179,14 @@ namespace dxvk { * Fills in undefined values and validates the texture * parameters. Any error returned by this method should * be forwarded to the application. + * \param [in] pDevice D3D9 device + * \param [in] ResourceType Resource type * \param [in,out] pDesc Texture description * \returns \c S_OK if the parameters are valid */ static HRESULT NormalizeTextureProperties( D3D9DeviceEx* pDevice, + D3DRESOURCETYPE ResourceType, D3D9_COMMON_TEXTURE_DESC* pDesc); /** diff --git a/src/d3d9/d3d9_device.cpp b/src/d3d9/d3d9_device.cpp index e99ad3837..87d39eeb7 100644 --- a/src/d3d9/d3d9_device.cpp +++ b/src/d3d9/d3d9_device.cpp @@ -594,7 +594,7 @@ namespace dxvk { || (Usage & D3DUSAGE_DYNAMIC) || IsVendorFormat(EnumerateFormat(Format)); - if (FAILED(D3D9CommonTexture::NormalizeTextureProperties(this, &desc))) + if (FAILED(D3D9CommonTexture::NormalizeTextureProperties(this, D3DRTYPE_TEXTURE, &desc))) return D3DERR_INVALIDCALL; try { @@ -664,7 +664,7 @@ namespace dxvk { || (Usage & D3DUSAGE_DYNAMIC) || IsVendorFormat(EnumerateFormat(Format)); - if (FAILED(D3D9CommonTexture::NormalizeTextureProperties(this, &desc))) + if (FAILED(D3D9CommonTexture::NormalizeTextureProperties(this, D3DRTYPE_VOLUMETEXTURE, &desc))) return D3DERR_INVALIDCALL; try { @@ -721,7 +721,7 @@ namespace dxvk { || (Usage & D3DUSAGE_DYNAMIC) || IsVendorFormat(EnumerateFormat(Format)); - if (FAILED(D3D9CommonTexture::NormalizeTextureProperties(this, &desc))) + if (FAILED(D3D9CommonTexture::NormalizeTextureProperties(this, D3DRTYPE_CUBETEXTURE, &desc))) return D3DERR_INVALIDCALL; try { @@ -3798,7 +3798,7 @@ namespace dxvk { desc.IsAttachmentOnly = TRUE; desc.IsLockable = Lockable; - if (FAILED(D3D9CommonTexture::NormalizeTextureProperties(this, &desc))) + if (FAILED(D3D9CommonTexture::NormalizeTextureProperties(this, D3DRTYPE_TEXTURE, &desc))) return D3DERR_INVALIDCALL; try { @@ -3846,7 +3846,7 @@ namespace dxvk { // Docs: Off-screen plain surfaces are always lockable, regardless of their pool types. desc.IsLockable = TRUE; - if (FAILED(D3D9CommonTexture::NormalizeTextureProperties(this, &desc))) + if (FAILED(D3D9CommonTexture::NormalizeTextureProperties(this, D3DRTYPE_TEXTURE, &desc))) return D3DERR_INVALIDCALL; if (pSharedHandle != nullptr && Pool != D3DPOOL_DEFAULT) @@ -3901,7 +3901,7 @@ namespace dxvk { // Docs don't say anything, so just assume it's lockable. desc.IsLockable = TRUE; - if (FAILED(D3D9CommonTexture::NormalizeTextureProperties(this, &desc))) + if (FAILED(D3D9CommonTexture::NormalizeTextureProperties(this, D3DRTYPE_TEXTURE, &desc))) return D3DERR_INVALIDCALL; try { @@ -7891,7 +7891,7 @@ namespace dxvk { // Docs: Also note that - unlike textures - swap chain back buffers, render targets [..] can be locked desc.IsLockable = TRUE; - if (FAILED(D3D9CommonTexture::NormalizeTextureProperties(this, &desc))) + if (FAILED(D3D9CommonTexture::NormalizeTextureProperties(this, D3DRTYPE_TEXTURE, &desc))) return D3DERR_NOTAVAILABLE; m_autoDepthStencil = new D3D9Surface(this, &desc, nullptr, nullptr); From 6b76d70d9dc585b98f1e16ed0af2d93914549827 Mon Sep 17 00:00:00 2001 From: Blisto91 <47954800+Blisto91@users.noreply.github.com> Date: Thu, 9 May 2024 00:25:54 +0200 Subject: [PATCH 146/246] [util] Enable d3d11.longMad for Guild Wars 2 Fixes invisibility effect flicker when invariantPosition is enabled --- src/util/config/config.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index 14239ffd2..dd07963ab 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -429,6 +429,11 @@ namespace dxvk { { R"(\\rfg\.exe$)", {{ { "d3d11.longMad", "True" }, }} }, + /* Guild Wars 2 - Fixes invisibility effect * + * flicker when invariantPosition is enabled */ + { R"(\\Gw2-64\.exe$)", {{ + { "d3d11.longMad", "True" }, + }} }, /**********************************************/ /* D3D9 GAMES */ From 7bad17c1d1d7864786abad4f7323b48699486c33 Mon Sep 17 00:00:00 2001 From: Blisto91 <47954800+Blisto91@users.noreply.github.com> Date: Fri, 10 May 2024 15:01:06 +0200 Subject: [PATCH 147/246] [util] Set deviceLossOnFocusLoss for The Sims 3 Prevents the game black screening on alt-tab --- src/util/config/config.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index dd07963ab..25d730f82 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -894,6 +894,10 @@ namespace dxvk { { "d3d9.deviceLossOnFocusLoss", "True" }, { "d3d9.cachedDynamicBuffers", "True" }, }} }, + /* The Sims 3 - Black screen on alt-tab */ + { R"(\\TS3(W)?\.exe$)", {{ + { "d3d9.deviceLossOnFocusLoss", "True" }, + }} }, /**********************************************/ From 4055a92856ddd2a182caec467b2ea8e42bc20bdb Mon Sep 17 00:00:00 2001 From: Ethan Lee Date: Thu, 7 Dec 2023 23:57:02 -0500 Subject: [PATCH 148/246] [wsi] Add init/quit functions, integrate them into DxvkInstance. This is preparation for loading/unloading WSI backends at runtime, which will be in an upcoming commit. --- src/dxvk/dxvk_instance.cpp | 5 +++++ src/wsi/meson.build | 1 + src/wsi/wsi_platform.cpp | 11 +++++++++++ src/wsi/wsi_platform.h | 7 +++++++ 4 files changed, 24 insertions(+) create mode 100644 src/wsi/wsi_platform.cpp diff --git a/src/dxvk/dxvk_instance.cpp b/src/dxvk/dxvk_instance.cpp index bc204d3a6..76ba78c54 100644 --- a/src/dxvk/dxvk_instance.cpp +++ b/src/dxvk/dxvk_instance.cpp @@ -4,6 +4,7 @@ #include "dxvk_openvr.h" #include "dxvk_openxr.h" #include "dxvk_platform_exts.h" +#include "../wsi/wsi_platform.h" #include #include @@ -20,6 +21,8 @@ namespace dxvk { Logger::info(str::format("Game: ", env::getExeName())); Logger::info(str::format("DXVK: ", DXVK_VERSION)); + wsi::init(); + m_config = Config::getUserConfig(); m_config.merge(Config::getAppConfig(env::getExePath())); m_config.logOptions(); @@ -64,6 +67,8 @@ namespace dxvk { DxvkInstance::~DxvkInstance() { if (m_messenger) m_vki->vkDestroyDebugUtilsMessengerEXT(m_vki->instance(), m_messenger, nullptr); + + wsi::quit(); } diff --git a/src/wsi/meson.build b/src/wsi/meson.build index 27603ceab..372ae0375 100644 --- a/src/wsi/meson.build +++ b/src/wsi/meson.build @@ -1,5 +1,6 @@ wsi_common_src = [ 'wsi_edid.cpp', + 'wsi_platform.cpp', ] wsi_win32_src = [ diff --git a/src/wsi/wsi_platform.cpp b/src/wsi/wsi_platform.cpp new file mode 100644 index 000000000..56cd6c45b --- /dev/null +++ b/src/wsi/wsi_platform.cpp @@ -0,0 +1,11 @@ +#include "wsi_platform.h" + +namespace dxvk::wsi { + + void init() { + } + + void quit() { + } + +} diff --git a/src/wsi/wsi_platform.h b/src/wsi/wsi_platform.h index 38b5c5aa1..0c94a3fa6 100644 --- a/src/wsi/wsi_platform.h +++ b/src/wsi/wsi_platform.h @@ -7,3 +7,10 @@ #elif defined(DXVK_WSI_GLFW) #include "glfw/wsi_platform_glfw.h" #endif + +namespace dxvk::wsi { + + void init(); + void quit(); + +} From 529129c332ab71308361eca8ae2f1fe0e2604414 Mon Sep 17 00:00:00 2001 From: Ethan Lee Date: Fri, 8 Dec 2023 00:09:56 -0500 Subject: [PATCH 149/246] [dxvk] Move getInstanceExtensions platform logic to wsi. This ensures that all of the WSI backend logic is in one place rather than two. --- ..._win32_exts.cpp => dxvk_platform_exts.cpp} | 12 +++-- src/dxvk/meson.build | 15 +----- src/dxvk/platform/dxvk_glfw_exts.cpp | 49 ------------------ src/dxvk/platform/dxvk_sdl2_exts.cpp | 50 ------------------- src/wsi/glfw/wsi_platform_glfw.cpp | 25 ++++++++++ src/wsi/meson.build | 3 ++ src/wsi/sdl2/wsi_platform_sdl2.cpp | 23 +++++++++ src/wsi/win32/wsi_platform_win32.cpp | 9 ++++ src/wsi/wsi_platform.h | 3 ++ 9 files changed, 72 insertions(+), 117 deletions(-) rename src/dxvk/{platform/dxvk_win32_exts.cpp => dxvk_platform_exts.cpp} (67%) delete mode 100644 src/dxvk/platform/dxvk_glfw_exts.cpp delete mode 100644 src/dxvk/platform/dxvk_sdl2_exts.cpp create mode 100644 src/wsi/glfw/wsi_platform_glfw.cpp create mode 100644 src/wsi/sdl2/wsi_platform_sdl2.cpp create mode 100644 src/wsi/win32/wsi_platform_win32.cpp diff --git a/src/dxvk/platform/dxvk_win32_exts.cpp b/src/dxvk/dxvk_platform_exts.cpp similarity index 67% rename from src/dxvk/platform/dxvk_win32_exts.cpp rename to src/dxvk/dxvk_platform_exts.cpp index 25c81cc5a..a1a7adeae 100644 --- a/src/dxvk/platform/dxvk_win32_exts.cpp +++ b/src/dxvk/dxvk_platform_exts.cpp @@ -1,17 +1,21 @@ -#include "../dxvk_platform_exts.h" +#include "dxvk_platform_exts.h" +#include "../wsi/wsi_platform.h" namespace dxvk { DxvkPlatformExts DxvkPlatformExts::s_instance; std::string_view DxvkPlatformExts::getName() { - return "Win32 WSI"; + return "Platform WSI"; } DxvkNameSet DxvkPlatformExts::getInstanceExtensions() { + std::vector extensionNames = wsi::getInstanceExtensions(); + DxvkNameSet names; - names.add(VK_KHR_WIN32_SURFACE_EXTENSION_NAME); + for (const char* name : extensionNames) + names.add(name); return names; } @@ -33,4 +37,4 @@ namespace dxvk { } -} \ No newline at end of file +} diff --git a/src/dxvk/meson.build b/src/dxvk/meson.build index c8fa9bda5..78ff2ecf2 100644 --- a/src/dxvk/meson.build +++ b/src/dxvk/meson.build @@ -89,6 +89,7 @@ dxvk_src = [ 'dxvk_options.cpp', 'dxvk_pipelayout.cpp', 'dxvk_pipemanager.cpp', + 'dxvk_platform_exts.cpp', 'dxvk_presenter.cpp', 'dxvk_queue.cpp', 'dxvk_resource.cpp', @@ -117,20 +118,6 @@ if platform == 'windows' ] endif -if dxvk_wsi == 'win32' - dxvk_src += [ - 'platform/dxvk_win32_exts.cpp' - ] -elif dxvk_wsi == 'sdl2' - dxvk_src += [ - 'platform/dxvk_sdl2_exts.cpp' - ] -elif dxvk_wsi == 'glfw' - dxvk_src += [ - 'platform/dxvk_glfw_exts.cpp' - ] -endif - dxvk_extra_deps = [ dependency('threads') ] if platform == 'linux' dxvk_extra_deps += [ cpp.find_library('dl', required: false) ] diff --git a/src/dxvk/platform/dxvk_glfw_exts.cpp b/src/dxvk/platform/dxvk_glfw_exts.cpp deleted file mode 100644 index f5cfee88d..000000000 --- a/src/dxvk/platform/dxvk_glfw_exts.cpp +++ /dev/null @@ -1,49 +0,0 @@ -#include "../dxvk_platform_exts.h" - -#include "../../vulkan/vulkan_loader.h" -#include - -namespace dxvk { - - DxvkPlatformExts DxvkPlatformExts::s_instance; - - std::string_view DxvkPlatformExts::getName() { - return "GLFW WSI"; - } - - DxvkNameSet DxvkPlatformExts::getInstanceExtensions() { - if (!glfwVulkanSupported()) - throw DxvkError(str::format("GLFW WSI: Vulkan is not supported in any capacity!")); - - uint32_t extensionCount = 0; - const char** extensionArray = glfwGetRequiredInstanceExtensions(&extensionCount); - - if (extensionCount == 0) - throw DxvkError(str::format("GLFW WSI: Failed to get required instance extensions")); - - DxvkNameSet names; - for (uint32_t i = 0; i < extensionCount; ++i) { - names.add(extensionArray[i]); - } - - return names; - } - - - DxvkNameSet DxvkPlatformExts::getDeviceExtensions( - uint32_t adapterId) { - return DxvkNameSet(); - } - - - void DxvkPlatformExts::initInstanceExtensions() { - //Nothing needs to be done here on GLFW - } - - - void DxvkPlatformExts::initDeviceExtensions( - const DxvkInstance* instance) { - //Nothing needs to be done here on GLFW - } - -} \ No newline at end of file diff --git a/src/dxvk/platform/dxvk_sdl2_exts.cpp b/src/dxvk/platform/dxvk_sdl2_exts.cpp deleted file mode 100644 index 13583aa77..000000000 --- a/src/dxvk/platform/dxvk_sdl2_exts.cpp +++ /dev/null @@ -1,50 +0,0 @@ -#include "../dxvk_platform_exts.h" - -#include -#include - -namespace dxvk { - - DxvkPlatformExts DxvkPlatformExts::s_instance; - - std::string_view DxvkPlatformExts::getName() { - return "SDL2 WSI"; - } - - - DxvkNameSet DxvkPlatformExts::getInstanceExtensions() { - SDL_Vulkan_LoadLibrary(nullptr); - - uint32_t extensionCount = 0; - if (!SDL_Vulkan_GetInstanceExtensions(nullptr, &extensionCount, nullptr)) - throw DxvkError(str::format("SDL2 WSI: Failed to get instance extension count. ", SDL_GetError())); - - auto extensionNames = std::vector(extensionCount); - if (!SDL_Vulkan_GetInstanceExtensions(nullptr, &extensionCount, extensionNames.data())) - throw DxvkError(str::format("SDL2 WSI: Failed to get instance extensions. ", SDL_GetError())); - - DxvkNameSet names; - for (const char* name : extensionNames) - names.add(name); - - return names; - } - - - DxvkNameSet DxvkPlatformExts::getDeviceExtensions( - uint32_t adapterId) { - return DxvkNameSet(); - } - - - void DxvkPlatformExts::initInstanceExtensions() { - - } - - - void DxvkPlatformExts::initDeviceExtensions( - const DxvkInstance* instance) { - - } - -} diff --git a/src/wsi/glfw/wsi_platform_glfw.cpp b/src/wsi/glfw/wsi_platform_glfw.cpp new file mode 100644 index 000000000..d180f5979 --- /dev/null +++ b/src/wsi/glfw/wsi_platform_glfw.cpp @@ -0,0 +1,25 @@ +#include "wsi_platform_glfw.h" +#include "../../util/util_error.h" +#include "../../util/util_string.h" + +namespace dxvk::wsi { + + std::vector getInstanceExtensions() { + if (!glfwVulkanSupported()) + throw DxvkError(str::format("GLFW WSI: Vulkan is not supported in any capacity!")); + + uint32_t extensionCount = 0; + const char** extensionArray = glfwGetRequiredInstanceExtensions(&extensionCount); + + if (extensionCount == 0) + throw DxvkError(str::format("GLFW WSI: Failed to get required instance extensions")); + + std::vector names(extensionCount); + for (uint32_t i = 0; i < extensionCount; ++i) { + names.push_back(extensionArray[i]); + } + + return names; + } + +} diff --git a/src/wsi/meson.build b/src/wsi/meson.build index 372ae0375..1cabcf2f5 100644 --- a/src/wsi/meson.build +++ b/src/wsi/meson.build @@ -5,16 +5,19 @@ wsi_common_src = [ wsi_win32_src = [ 'win32/wsi_monitor_win32.cpp', + 'win32/wsi_platform_win32.cpp', 'win32/wsi_window_win32.cpp', ] wsi_sdl2_src = [ 'sdl2/wsi_monitor_sdl2.cpp', + 'sdl2/wsi_platform_sdl2.cpp', 'sdl2/wsi_window_sdl2.cpp', ] wsi_glfw_src = [ 'glfw/wsi_monitor_glfw.cpp', + 'glfw/wsi_platform_glfw.cpp', 'glfw/wsi_window_glfw.cpp', ] diff --git a/src/wsi/sdl2/wsi_platform_sdl2.cpp b/src/wsi/sdl2/wsi_platform_sdl2.cpp new file mode 100644 index 000000000..cc8ac22b9 --- /dev/null +++ b/src/wsi/sdl2/wsi_platform_sdl2.cpp @@ -0,0 +1,23 @@ +#include "wsi_platform_sdl2.h" +#include "../../util/util_error.h" +#include "../../util/util_string.h" + +#include + +namespace dxvk::wsi { + + std::vector getInstanceExtensions() { + SDL_Vulkan_LoadLibrary(nullptr); + + uint32_t extensionCount = 0; + if (!SDL_Vulkan_GetInstanceExtensions(nullptr, &extensionCount, nullptr)) + throw DxvkError(str::format("SDL2 WSI: Failed to get instance extension count. ", SDL_GetError())); + + auto extensionNames = std::vector(extensionCount); + if (!SDL_Vulkan_GetInstanceExtensions(nullptr, &extensionCount, extensionNames.data())) + throw DxvkError(str::format("SDL2 WSI: Failed to get instance extensions. ", SDL_GetError())); + + return extensionNames; + } + +} diff --git a/src/wsi/win32/wsi_platform_win32.cpp b/src/wsi/win32/wsi_platform_win32.cpp new file mode 100644 index 000000000..8b16de564 --- /dev/null +++ b/src/wsi/win32/wsi_platform_win32.cpp @@ -0,0 +1,9 @@ +#include "wsi_platform_win32.h" + +namespace dxvk::wsi { + + std::vector getInstanceExtensions() { + return { VK_KHR_WIN32_SURFACE_EXTENSION_NAME }; + } + +} diff --git a/src/wsi/wsi_platform.h b/src/wsi/wsi_platform.h index 0c94a3fa6..7ccecc27f 100644 --- a/src/wsi/wsi_platform.h +++ b/src/wsi/wsi_platform.h @@ -8,9 +8,12 @@ #include "glfw/wsi_platform_glfw.h" #endif +#include + namespace dxvk::wsi { void init(); void quit(); + std::vector getInstanceExtensions(); } From 0f7c1f753ad25c0d992c08ba888a70e6aa74814f Mon Sep 17 00:00:00 2001 From: Ethan Lee Date: Fri, 8 Dec 2023 00:45:46 -0500 Subject: [PATCH 150/246] [wsi] Refactor the WSI backends to be implementations of a WsiDriver interface. Rather than directly calling functions, the API now calls shared functions that call into a WsiDriver instance, which is allocated and implemented by the backend. Functionally this should be the same, it just has the extra allocation for the function table. This prepares the WSI library for supporting multiple implementations in a single binary. --- src/wsi/glfw/wsi_monitor_glfw.cpp | 18 ++-- src/wsi/glfw/wsi_platform_glfw.cpp | 6 +- src/wsi/glfw/wsi_platform_glfw.h | 89 ++++++++++++++++-- src/wsi/glfw/wsi_window_glfw.cpp | 22 ++--- src/wsi/sdl2/wsi_monitor_sdl2.cpp | 18 ++-- src/wsi/sdl2/wsi_platform_sdl2.cpp | 6 +- src/wsi/sdl2/wsi_platform_sdl2.h | 89 ++++++++++++++++-- src/wsi/sdl2/wsi_window_sdl2.cpp | 20 ++-- src/wsi/win32/wsi_monitor_win32.cpp | 20 ++-- src/wsi/win32/wsi_platform_win32.cpp | 6 +- src/wsi/win32/wsi_platform_win32.h | 92 ++++++++++++++++-- src/wsi/win32/wsi_window_win32.cpp | 23 +++-- src/wsi/wsi_platform.cpp | 135 +++++++++++++++++++++++++++ src/wsi/wsi_platform.h | 95 +++++++++++++++++-- src/wsi/wsi_window.h | 12 ++- 15 files changed, 559 insertions(+), 92 deletions(-) diff --git a/src/wsi/glfw/wsi_monitor_glfw.cpp b/src/wsi/glfw/wsi_monitor_glfw.cpp index e450f83cb..00abc89aa 100644 --- a/src/wsi/glfw/wsi_monitor_glfw.cpp +++ b/src/wsi/glfw/wsi_monitor_glfw.cpp @@ -11,22 +11,22 @@ namespace dxvk::wsi { - HMONITOR getDefaultMonitor() { + HMONITOR GlfwWsiDriver::getDefaultMonitor() { return enumMonitors(0); } - HMONITOR enumMonitors(uint32_t index) { + HMONITOR GlfwWsiDriver::enumMonitors(uint32_t index) { return isDisplayValid(int32_t(index)) ? toHmonitor(index) : nullptr; } - HMONITOR enumMonitors(const LUID *adapterLUID[], uint32_t numLUIDs, uint32_t index) { + HMONITOR GlfwWsiDriver::enumMonitors(const LUID *adapterLUID[], uint32_t numLUIDs, uint32_t index) { return enumMonitors(index); } - bool getDisplayName( + bool GlfwWsiDriver::getDisplayName( HMONITOR hMonitor, WCHAR (&Name)[32]) { const int32_t displayId = fromHmonitor(hMonitor); @@ -46,7 +46,7 @@ namespace dxvk::wsi { } - bool getDesktopCoordinates( + bool GlfwWsiDriver::getDesktopCoordinates( HMONITOR hMonitor, RECT* pRect) { const int32_t displayId = fromHmonitor(hMonitor); @@ -97,7 +97,7 @@ namespace dxvk::wsi { } - bool getDisplayMode( + bool GlfwWsiDriver::getDisplayMode( HMONITOR hMonitor, uint32_t ModeNumber, WsiMode* pMode) { @@ -121,7 +121,7 @@ namespace dxvk::wsi { } - bool getCurrentDisplayMode( + bool GlfwWsiDriver::getCurrentDisplayMode( HMONITOR hMonitor, WsiMode* pMode) { const int32_t displayId = fromHmonitor(hMonitor); @@ -141,7 +141,7 @@ namespace dxvk::wsi { } - bool getDesktopDisplayMode( + bool GlfwWsiDriver::getDesktopDisplayMode( HMONITOR hMonitor, WsiMode* pMode) { const int32_t displayId = fromHmonitor(hMonitor); @@ -159,7 +159,7 @@ namespace dxvk::wsi { return true; } - std::vector getMonitorEdid(HMONITOR hMonitor) { + std::vector GlfwWsiDriver::getMonitorEdid(HMONITOR hMonitor) { Logger::err("getMonitorEdid not implemented on this platform."); return {}; } diff --git a/src/wsi/glfw/wsi_platform_glfw.cpp b/src/wsi/glfw/wsi_platform_glfw.cpp index d180f5979..fb711aebe 100644 --- a/src/wsi/glfw/wsi_platform_glfw.cpp +++ b/src/wsi/glfw/wsi_platform_glfw.cpp @@ -4,7 +4,7 @@ namespace dxvk::wsi { - std::vector getInstanceExtensions() { + std::vector GlfwWsiDriver::getInstanceExtensions() { if (!glfwVulkanSupported()) throw DxvkError(str::format("GLFW WSI: Vulkan is not supported in any capacity!")); @@ -22,4 +22,8 @@ namespace dxvk::wsi { return names; } + WsiDriver* platformCreateWsiDriver() { + return new GlfwWsiDriver(); + } + } diff --git a/src/wsi/glfw/wsi_platform_glfw.h b/src/wsi/glfw/wsi_platform_glfw.h index 25753494f..83b95c5ee 100644 --- a/src/wsi/glfw/wsi_platform_glfw.h +++ b/src/wsi/glfw/wsi_platform_glfw.h @@ -3,14 +3,91 @@ #include "../../vulkan/vulkan_loader.h" #include -#include "../wsi_monitor.h" +#include "../wsi_platform.h" namespace dxvk::wsi { - /** - * \brief Impl-dependent state - */ - struct DxvkWindowState { + class GlfwWsiDriver : public WsiDriver { + public: + // Platform + virtual std::vector getInstanceExtensions(); + + // Monitor + virtual HMONITOR getDefaultMonitor(); + + virtual HMONITOR enumMonitors(uint32_t index); + + virtual HMONITOR enumMonitors(const LUID *adapterLUID[], uint32_t numLUIDs, uint32_t index); + + virtual bool getDisplayName( + HMONITOR hMonitor, + WCHAR (&Name)[32]); + + virtual bool getDesktopCoordinates( + HMONITOR hMonitor, + RECT* pRect); + + virtual bool getDisplayMode( + HMONITOR hMonitor, + uint32_t modeNumber, + WsiMode* pMode); + + virtual bool getCurrentDisplayMode( + HMONITOR hMonitor, + WsiMode* pMode); + + virtual bool getDesktopDisplayMode( + HMONITOR hMonitor, + WsiMode* pMode); + + virtual WsiEdidData getMonitorEdid(HMONITOR hMonitor); + + // Window + + virtual void getWindowSize( + HWND hWindow, + uint32_t* pWidth, + uint32_t* pWeight); + + virtual void resizeWindow( + HWND hWindow, + DxvkWindowState* pState, + uint32_t width, + uint32_t weight); + + virtual bool setWindowMode( + HMONITOR hMonitor, + HWND hWindow, + const WsiMode& mode); + + virtual bool enterFullscreenMode( + HMONITOR hMonitor, + HWND hWindow, + DxvkWindowState* pState, + [[maybe_unused]] + bool modeSwitch); + + virtual bool leaveFullscreenMode( + HWND hWindow, + DxvkWindowState* pState, + bool restoreCoordinates); + + virtual bool restoreDisplayMode(); + + virtual HMONITOR getWindowMonitor(HWND hWindow); + + virtual bool isWindow(HWND hWindow); + + virtual void updateFullscreenWindow( + HMONITOR hMonitor, + HWND hWindow, + bool forceTopmost); + + virtual VkResult createSurface( + HWND hWindow, + PFN_vkGetInstanceProcAddr pfnVkGetInstanceProcAddr, + VkInstance instance, + VkSurfaceKHR* pSurface); }; inline bool isDisplayValid(int32_t displayId) { @@ -20,4 +97,4 @@ namespace dxvk::wsi { return displayId < displayCount && displayId >= 0; } -} \ No newline at end of file +} diff --git a/src/wsi/glfw/wsi_window_glfw.cpp b/src/wsi/glfw/wsi_window_glfw.cpp index 700312507..b23a79230 100644 --- a/src/wsi/glfw/wsi_window_glfw.cpp +++ b/src/wsi/glfw/wsi_window_glfw.cpp @@ -12,7 +12,7 @@ namespace dxvk::wsi { - void getWindowSize( + void GlfwWsiDriver::getWindowSize( HWND hWindow, uint32_t* pWidth, uint32_t* pHeight) { @@ -29,7 +29,7 @@ namespace dxvk::wsi { } - void resizeWindow( + void GlfwWsiDriver::resizeWindow( HWND hWindow, DxvkWindowState* pState, uint32_t Width, @@ -40,7 +40,7 @@ namespace dxvk::wsi { } - bool setWindowMode( + bool GlfwWsiDriver::setWindowMode( HMONITOR hMonitor, HWND hWindow, const WsiMode& pMode) { @@ -67,7 +67,7 @@ namespace dxvk::wsi { return true; } - bool enterFullscreenMode( + bool GlfwWsiDriver::enterFullscreenMode( HMONITOR hMonitor, HWND hWindow, DxvkWindowState* pState, @@ -89,7 +89,7 @@ namespace dxvk::wsi { } - bool leaveFullscreenMode( + bool GlfwWsiDriver::leaveFullscreenMode( HWND hWindow, DxvkWindowState* pState, bool restoreCoordinates) { @@ -103,13 +103,13 @@ namespace dxvk::wsi { } - bool restoreDisplayMode() { + bool GlfwWsiDriver::restoreDisplayMode() { // Don't need to do anything with GLFW here. return true; } - HMONITOR getWindowMonitor(HWND hWindow) { + HMONITOR GlfwWsiDriver::getWindowMonitor(HWND hWindow) { // TODO: implement this with glfwGetWindowMonitor // (or maybe not? glfwGetWindowMonitor only seems to reference *fullscreen* windows) // GLFWwindow* window = fromHwnd(hWindow); @@ -119,19 +119,19 @@ namespace dxvk::wsi { } - bool isWindow(HWND hWindow) { + bool GlfwWsiDriver::isWindow(HWND hWindow) { GLFWwindow* window = fromHwnd(hWindow); return window != nullptr; } - void updateFullscreenWindow( + void GlfwWsiDriver::updateFullscreenWindow( HMONITOR hMonitor, HWND hWindow, bool forceTopmost) { // Don't need to do anything with GLFW here. } - VkResult createSurface( + VkResult GlfwWsiDriver::createSurface( HWND hWindow, PFN_vkGetInstanceProcAddr pfnVkGetInstanceProcAddr, VkInstance instance, @@ -141,4 +141,4 @@ namespace dxvk::wsi { return glfwCreateWindowSurface(instance, window, nullptr, pSurface); } -} \ No newline at end of file +} diff --git a/src/wsi/sdl2/wsi_monitor_sdl2.cpp b/src/wsi/sdl2/wsi_monitor_sdl2.cpp index f0de7cd64..5903ef68d 100644 --- a/src/wsi/sdl2/wsi_monitor_sdl2.cpp +++ b/src/wsi/sdl2/wsi_monitor_sdl2.cpp @@ -12,22 +12,22 @@ namespace dxvk::wsi { - HMONITOR getDefaultMonitor() { + HMONITOR Sdl2WsiDriver::getDefaultMonitor() { return enumMonitors(0); } - HMONITOR enumMonitors(uint32_t index) { + HMONITOR Sdl2WsiDriver::enumMonitors(uint32_t index) { return isDisplayValid(int32_t(index)) ? toHmonitor(index) : nullptr; } - HMONITOR enumMonitors(const LUID *adapterLUID[], uint32_t numLUIDs, uint32_t index) { + HMONITOR Sdl2WsiDriver::enumMonitors(const LUID *adapterLUID[], uint32_t numLUIDs, uint32_t index) { return enumMonitors(index); } - bool getDisplayName( + bool Sdl2WsiDriver::getDisplayName( HMONITOR hMonitor, WCHAR (&Name)[32]) { const int32_t displayId = fromHmonitor(hMonitor); @@ -47,7 +47,7 @@ namespace dxvk::wsi { } - bool getDesktopCoordinates( + bool Sdl2WsiDriver::getDesktopCoordinates( HMONITOR hMonitor, RECT* pRect) { const int32_t displayId = fromHmonitor(hMonitor); @@ -90,7 +90,7 @@ namespace dxvk::wsi { } - bool getDisplayMode( + bool Sdl2WsiDriver::getDisplayMode( HMONITOR hMonitor, uint32_t ModeNumber, WsiMode* pMode) { @@ -109,7 +109,7 @@ namespace dxvk::wsi { } - bool getCurrentDisplayMode( + bool Sdl2WsiDriver::getCurrentDisplayMode( HMONITOR hMonitor, WsiMode* pMode) { const int32_t displayId = fromHmonitor(hMonitor); @@ -129,7 +129,7 @@ namespace dxvk::wsi { } - bool getDesktopDisplayMode( + bool Sdl2WsiDriver::getDesktopDisplayMode( HMONITOR hMonitor, WsiMode* pMode) { const int32_t displayId = fromHmonitor(hMonitor); @@ -148,7 +148,7 @@ namespace dxvk::wsi { return true; } - std::vector getMonitorEdid(HMONITOR hMonitor) { + std::vector Sdl2WsiDriver::getMonitorEdid(HMONITOR hMonitor) { Logger::err("getMonitorEdid not implemented on this platform."); return {}; } diff --git a/src/wsi/sdl2/wsi_platform_sdl2.cpp b/src/wsi/sdl2/wsi_platform_sdl2.cpp index cc8ac22b9..96ecb0762 100644 --- a/src/wsi/sdl2/wsi_platform_sdl2.cpp +++ b/src/wsi/sdl2/wsi_platform_sdl2.cpp @@ -6,7 +6,7 @@ namespace dxvk::wsi { - std::vector getInstanceExtensions() { + std::vector Sdl2WsiDriver::getInstanceExtensions() { SDL_Vulkan_LoadLibrary(nullptr); uint32_t extensionCount = 0; @@ -20,4 +20,8 @@ namespace dxvk::wsi { return extensionNames; } + WsiDriver* platformCreateWsiDriver() { + return new Sdl2WsiDriver(); + } + } diff --git a/src/wsi/sdl2/wsi_platform_sdl2.h b/src/wsi/sdl2/wsi_platform_sdl2.h index 411fe8f6f..845725abe 100644 --- a/src/wsi/sdl2/wsi_platform_sdl2.h +++ b/src/wsi/sdl2/wsi_platform_sdl2.h @@ -2,14 +2,91 @@ #include -#include "../wsi_monitor.h" +#include "../wsi_platform.h" namespace dxvk::wsi { - /** - * \brief Impl-dependent state - */ - struct DxvkWindowState { + class Sdl2WsiDriver : public WsiDriver { + public: + // Platform + virtual std::vector getInstanceExtensions(); + + // Monitor + virtual HMONITOR getDefaultMonitor(); + + virtual HMONITOR enumMonitors(uint32_t index); + + virtual HMONITOR enumMonitors(const LUID *adapterLUID[], uint32_t numLUIDs, uint32_t index); + + virtual bool getDisplayName( + HMONITOR hMonitor, + WCHAR (&Name)[32]); + + virtual bool getDesktopCoordinates( + HMONITOR hMonitor, + RECT* pRect); + + virtual bool getDisplayMode( + HMONITOR hMonitor, + uint32_t modeNumber, + WsiMode* pMode); + + virtual bool getCurrentDisplayMode( + HMONITOR hMonitor, + WsiMode* pMode); + + virtual bool getDesktopDisplayMode( + HMONITOR hMonitor, + WsiMode* pMode); + + virtual WsiEdidData getMonitorEdid(HMONITOR hMonitor); + + // Window + + virtual void getWindowSize( + HWND hWindow, + uint32_t* pWidth, + uint32_t* pWeight); + + virtual void resizeWindow( + HWND hWindow, + DxvkWindowState* pState, + uint32_t width, + uint32_t weight); + + virtual bool setWindowMode( + HMONITOR hMonitor, + HWND hWindow, + const WsiMode& mode); + + virtual bool enterFullscreenMode( + HMONITOR hMonitor, + HWND hWindow, + DxvkWindowState* pState, + [[maybe_unused]] + bool modeSwitch); + + virtual bool leaveFullscreenMode( + HWND hWindow, + DxvkWindowState* pState, + bool restoreCoordinates); + + virtual bool restoreDisplayMode(); + + virtual HMONITOR getWindowMonitor(HWND hWindow); + + virtual bool isWindow(HWND hWindow); + + virtual void updateFullscreenWindow( + HMONITOR hMonitor, + HWND hWindow, + bool forceTopmost); + + virtual VkResult createSurface( + HWND hWindow, + PFN_vkGetInstanceProcAddr pfnVkGetInstanceProcAddr, + VkInstance instance, + VkSurfaceKHR* pSurface); }; inline bool isDisplayValid(int32_t displayId) { @@ -18,4 +95,4 @@ namespace dxvk::wsi { return displayId < displayCount && displayId >= 0; } -} \ No newline at end of file +} diff --git a/src/wsi/sdl2/wsi_window_sdl2.cpp b/src/wsi/sdl2/wsi_window_sdl2.cpp index 1280b6c10..5622e8762 100644 --- a/src/wsi/sdl2/wsi_window_sdl2.cpp +++ b/src/wsi/sdl2/wsi_window_sdl2.cpp @@ -11,7 +11,7 @@ namespace dxvk::wsi { - void getWindowSize( + void Sdl2WsiDriver::getWindowSize( HWND hWindow, uint32_t* pWidth, uint32_t* pHeight) { @@ -28,7 +28,7 @@ namespace dxvk::wsi { } - void resizeWindow( + void Sdl2WsiDriver::resizeWindow( HWND hWindow, DxvkWindowState* pState, uint32_t Width, @@ -39,7 +39,7 @@ namespace dxvk::wsi { } - bool setWindowMode( + bool Sdl2WsiDriver::setWindowMode( HMONITOR hMonitor, HWND hWindow, const WsiMode& pMode) { @@ -73,7 +73,7 @@ namespace dxvk::wsi { - bool enterFullscreenMode( + bool Sdl2WsiDriver::enterFullscreenMode( HMONITOR hMonitor, HWND hWindow, DxvkWindowState* pState, @@ -99,7 +99,7 @@ namespace dxvk::wsi { } - bool leaveFullscreenMode( + bool Sdl2WsiDriver::leaveFullscreenMode( HWND hWindow, DxvkWindowState* pState, bool restoreCoordinates) { @@ -114,13 +114,13 @@ namespace dxvk::wsi { } - bool restoreDisplayMode() { + bool Sdl2WsiDriver::restoreDisplayMode() { // Don't need to do anything with SDL2 here. return true; } - HMONITOR getWindowMonitor(HWND hWindow) { + HMONITOR Sdl2WsiDriver::getWindowMonitor(HWND hWindow) { SDL_Window* window = fromHwnd(hWindow); const int32_t displayId = SDL_GetWindowDisplayIndex(window); @@ -128,13 +128,13 @@ namespace dxvk::wsi { } - bool isWindow(HWND hWindow) { + bool Sdl2WsiDriver::isWindow(HWND hWindow) { SDL_Window* window = fromHwnd(hWindow); return window != nullptr; } - void updateFullscreenWindow( + void Sdl2WsiDriver::updateFullscreenWindow( HMONITOR hMonitor, HWND hWindow, bool forceTopmost) { @@ -142,7 +142,7 @@ namespace dxvk::wsi { } - VkResult createSurface( + VkResult Sdl2WsiDriver::createSurface( HWND hWindow, PFN_vkGetInstanceProcAddr pfnVkGetInstanceProcAddr, VkInstance instance, diff --git a/src/wsi/win32/wsi_monitor_win32.cpp b/src/wsi/win32/wsi_monitor_win32.cpp index ae4a0ff35..b37e09028 100644 --- a/src/wsi/win32/wsi_monitor_win32.cpp +++ b/src/wsi/win32/wsi_monitor_win32.cpp @@ -1,4 +1,4 @@ -#include "../wsi_monitor.h" +#include "wsi_platform_win32.h" #include "../../util/util_string.h" #include "../../util/log/log.h" @@ -13,7 +13,7 @@ namespace dxvk::wsi { - HMONITOR getDefaultMonitor() { + HMONITOR Win32WsiDriver::getDefaultMonitor() { return ::MonitorFromPoint({ 0, 0 }, MONITOR_DEFAULTTOPRIMARY); } @@ -45,7 +45,7 @@ namespace dxvk::wsi { return FALSE; } - HMONITOR enumMonitors(uint32_t index) { + HMONITOR Win32WsiDriver::enumMonitors(uint32_t index) { MonitorEnumInfo info; info.iMonitorId = index; info.oMonitor = nullptr; @@ -58,7 +58,7 @@ namespace dxvk::wsi { return info.oMonitor; } - HMONITOR enumMonitors(const LUID *adapterLUID[], uint32_t numLUIDs, uint32_t index) { + HMONITOR Win32WsiDriver::enumMonitors(const LUID *adapterLUID[], uint32_t numLUIDs, uint32_t index) { if (!numLUIDs) return enumMonitors(index); @@ -132,7 +132,7 @@ namespace dxvk::wsi { } - bool getDisplayName( + bool Win32WsiDriver::getDisplayName( HMONITOR hMonitor, WCHAR (&Name)[32]) { // Query monitor info to get the device name @@ -150,7 +150,7 @@ namespace dxvk::wsi { } - bool getDesktopCoordinates( + bool Win32WsiDriver::getDesktopCoordinates( HMONITOR hMonitor, RECT* pRect) { ::MONITORINFOEXW monInfo; @@ -200,7 +200,7 @@ namespace dxvk::wsi { } - bool getDisplayMode( + bool Win32WsiDriver::getDisplayMode( HMONITOR hMonitor, uint32_t modeNumber, WsiMode* pMode) { @@ -208,14 +208,14 @@ namespace dxvk::wsi { } - bool getCurrentDisplayMode( + bool Win32WsiDriver::getCurrentDisplayMode( HMONITOR hMonitor, WsiMode* pMode) { return retrieveDisplayMode(hMonitor, ENUM_CURRENT_SETTINGS, pMode); } - bool getDesktopDisplayMode( + bool Win32WsiDriver::getDesktopDisplayMode( HMONITOR hMonitor, WsiMode* pMode) { return retrieveDisplayMode(hMonitor, ENUM_REGISTRY_SETTINGS, pMode); @@ -308,7 +308,7 @@ namespace dxvk::wsi { wchar_t extraChars[MAX_DEVICE_ID_LEN]; }; - WsiEdidData getMonitorEdid(HMONITOR hMonitor) { + WsiEdidData Win32WsiDriver::getMonitorEdid(HMONITOR hMonitor) { static constexpr GUID GUID_DEVINTERFACE_MONITOR = { 0xe6f07b5f, 0xee97, 0x4a90, 0xb0, 0x76, 0x33, 0xf5, 0x7b, 0xf4, 0xea, 0xa7 }; static auto pfnSetupDiGetClassDevsW = reinterpret_cast (::GetProcAddress(::GetModuleHandleW(L"setupapi.dll"), "SetupDiGetClassDevsW")); static auto pfnSetupDiEnumDeviceInterfaces = reinterpret_cast (::GetProcAddress(::GetModuleHandleW(L"setupapi.dll"), "SetupDiEnumDeviceInterfaces")); diff --git a/src/wsi/win32/wsi_platform_win32.cpp b/src/wsi/win32/wsi_platform_win32.cpp index 8b16de564..ab27869a8 100644 --- a/src/wsi/win32/wsi_platform_win32.cpp +++ b/src/wsi/win32/wsi_platform_win32.cpp @@ -2,8 +2,12 @@ namespace dxvk::wsi { - std::vector getInstanceExtensions() { + std::vector Win32WsiDriver::getInstanceExtensions() { return { VK_KHR_WIN32_SURFACE_EXTENSION_NAME }; } + WsiDriver* platformCreateWsiDriver() { + return new Win32WsiDriver(); + } + } diff --git a/src/wsi/win32/wsi_platform_win32.h b/src/wsi/win32/wsi_platform_win32.h index d382f20ad..1364baf15 100644 --- a/src/wsi/win32/wsi_platform_win32.h +++ b/src/wsi/win32/wsi_platform_win32.h @@ -2,15 +2,91 @@ #include +#include "../wsi_platform.h" + namespace dxvk::wsi { - /** - * \brief Impl-dependent state - */ - struct DxvkWindowState { - LONG style = 0; - LONG exstyle = 0; - RECT rect = { 0, 0, 0, 0 }; + class Win32WsiDriver : public WsiDriver { + public: + // Platform + virtual std::vector getInstanceExtensions(); + + // Monitor + virtual HMONITOR getDefaultMonitor(); + + virtual HMONITOR enumMonitors(uint32_t index); + + virtual HMONITOR enumMonitors(const LUID *adapterLUID[], uint32_t numLUIDs, uint32_t index); + + virtual bool getDisplayName( + HMONITOR hMonitor, + WCHAR (&Name)[32]); + + virtual bool getDesktopCoordinates( + HMONITOR hMonitor, + RECT* pRect); + + virtual bool getDisplayMode( + HMONITOR hMonitor, + uint32_t modeNumber, + WsiMode* pMode); + + virtual bool getCurrentDisplayMode( + HMONITOR hMonitor, + WsiMode* pMode); + + virtual bool getDesktopDisplayMode( + HMONITOR hMonitor, + WsiMode* pMode); + + virtual WsiEdidData getMonitorEdid(HMONITOR hMonitor); + + // Window + + virtual void getWindowSize( + HWND hWindow, + uint32_t* pWidth, + uint32_t* pWeight); + + virtual void resizeWindow( + HWND hWindow, + DxvkWindowState* pState, + uint32_t width, + uint32_t weight); + + virtual bool setWindowMode( + HMONITOR hMonitor, + HWND hWindow, + const WsiMode& mode); + + virtual bool enterFullscreenMode( + HMONITOR hMonitor, + HWND hWindow, + DxvkWindowState* pState, + [[maybe_unused]] + bool modeSwitch); + + virtual bool leaveFullscreenMode( + HWND hWindow, + DxvkWindowState* pState, + bool restoreCoordinates); + + virtual bool restoreDisplayMode(); + + virtual HMONITOR getWindowMonitor(HWND hWindow); + + virtual bool isWindow(HWND hWindow); + + virtual void updateFullscreenWindow( + HMONITOR hMonitor, + HWND hWindow, + bool forceTopmost); + + virtual VkResult createSurface( + HWND hWindow, + PFN_vkGetInstanceProcAddr pfnVkGetInstanceProcAddr, + VkInstance instance, + VkSurfaceKHR* pSurface); }; -} \ No newline at end of file +} diff --git a/src/wsi/win32/wsi_window_win32.cpp b/src/wsi/win32/wsi_window_win32.cpp index 597d7478f..cf446980a 100644 --- a/src/wsi/win32/wsi_window_win32.cpp +++ b/src/wsi/win32/wsi_window_win32.cpp @@ -1,5 +1,4 @@ -#include "../wsi_window.h" -#include "../wsi_monitor.h" +#include "wsi_platform_win32.h" #include "../../util/util_string.h" #include "../../util/log/log.h" @@ -94,7 +93,7 @@ namespace dxvk::wsi { } - void getWindowSize( + void Win32WsiDriver::getWindowSize( HWND hWindow, uint32_t* pWidth, uint32_t* pHeight) { @@ -109,7 +108,7 @@ namespace dxvk::wsi { } - void resizeWindow( + void Win32WsiDriver::resizeWindow( HWND hWindow, DxvkWindowState* pState, uint32_t width, @@ -130,7 +129,7 @@ namespace dxvk::wsi { } - bool setWindowMode( + bool Win32WsiDriver::setWindowMode( HMONITOR hMonitor, HWND hWindow, const WsiMode& mode) { @@ -163,7 +162,7 @@ namespace dxvk::wsi { } - bool enterFullscreenMode( + bool Win32WsiDriver::enterFullscreenMode( HMONITOR hMonitor, HWND hWindow, DxvkWindowState* pState, @@ -196,7 +195,7 @@ namespace dxvk::wsi { } - bool leaveFullscreenMode( + bool Win32WsiDriver::leaveFullscreenMode( HWND hWindow, DxvkWindowState* pState, bool restoreCoordinates) { @@ -225,7 +224,7 @@ namespace dxvk::wsi { } - bool restoreDisplayMode() { + bool Win32WsiDriver::restoreDisplayMode() { bool success = true; bool result = ::EnumDisplayMonitors(nullptr, nullptr, &restoreDisplayModeCallback, @@ -235,7 +234,7 @@ namespace dxvk::wsi { } - HMONITOR getWindowMonitor(HWND hWindow) { + HMONITOR Win32WsiDriver::getWindowMonitor(HWND hWindow) { RECT windowRect = { 0, 0, 0, 0 }; ::GetWindowRect(hWindow, &windowRect); @@ -248,12 +247,12 @@ namespace dxvk::wsi { } - bool isWindow(HWND hWindow) { + bool Win32WsiDriver::isWindow(HWND hWindow) { return ::IsWindow(hWindow); } - void updateFullscreenWindow( + void Win32WsiDriver::updateFullscreenWindow( HMONITOR hMonitor, HWND hWindow, bool forceTopmost) { @@ -274,7 +273,7 @@ namespace dxvk::wsi { } - VkResult createSurface( + VkResult Win32WsiDriver::createSurface( HWND hWindow, PFN_vkGetInstanceProcAddr pfnVkGetInstanceProcAddr, VkInstance instance, diff --git a/src/wsi/wsi_platform.cpp b/src/wsi/wsi_platform.cpp index 56cd6c45b..f4802025a 100644 --- a/src/wsi/wsi_platform.cpp +++ b/src/wsi/wsi_platform.cpp @@ -1,11 +1,146 @@ #include "wsi_platform.h" +#include "wsi_monitor.h" +#include "wsi_window.h" +#include "../util/util_error.h" namespace dxvk::wsi { + static WsiDriver* s_driver = nullptr; + static int s_refcount = 0; void init() { + if (s_refcount++ > 0) + return; + + s_driver = platformCreateWsiDriver(); + if (s_driver == nullptr) + throw DxvkError("Failed to initialize WSI."); } void quit() { + if (s_refcount == 0) + return; + + s_refcount--; + if (s_refcount == 0) { + delete s_driver; + s_driver = nullptr; + } + } + + std::vector getInstanceExtensions() { + return s_driver->getInstanceExtensions(); + } + + void getWindowSize( + HWND hWindow, + uint32_t* pWidth, + uint32_t* pHeight) { + s_driver->getWindowSize(hWindow, pWidth, pHeight); + } + + void resizeWindow( + HWND hWindow, + DxvkWindowState* pState, + uint32_t width, + uint32_t height) { + s_driver->resizeWindow(hWindow, pState, width, height); + } + + bool setWindowMode( + HMONITOR hMonitor, + HWND hWindow, + const WsiMode& mode) { + return s_driver->setWindowMode(hMonitor, hWindow, mode); + } + + bool enterFullscreenMode( + HMONITOR hMonitor, + HWND hWindow, + DxvkWindowState* pState, + [[maybe_unused]] + bool modeSwitch) { + return s_driver->enterFullscreenMode(hMonitor, hWindow, pState, modeSwitch); + } + + bool leaveFullscreenMode( + HWND hWindow, + DxvkWindowState* pState, + bool restoreCoordinates) { + return s_driver->leaveFullscreenMode(hWindow, pState, restoreCoordinates); + } + + bool restoreDisplayMode() { + return s_driver->restoreDisplayMode(); + } + + HMONITOR getWindowMonitor(HWND hWindow) { + return s_driver->getWindowMonitor(hWindow); + } + + bool isWindow(HWND hWindow) { + return s_driver->isWindow(hWindow); + } + + void updateFullscreenWindow( + HMONITOR hMonitor, + HWND hWindow, + bool forceTopmost) { + s_driver->updateFullscreenWindow(hMonitor, hWindow, forceTopmost); + } + + VkResult createSurface( + HWND hWindow, + PFN_vkGetInstanceProcAddr pfnVkGetInstanceProcAddr, + VkInstance instance, + VkSurfaceKHR* pSurface) { + return s_driver->createSurface(hWindow, pfnVkGetInstanceProcAddr, instance, pSurface); + } + + HMONITOR getDefaultMonitor() { + return s_driver->getDefaultMonitor(); + } + + HMONITOR enumMonitors(uint32_t index) { + return s_driver->enumMonitors(index); + } + + HMONITOR enumMonitors(const LUID *adapterLUID[], uint32_t numLUIDs, uint32_t index) { + return s_driver->enumMonitors(adapterLUID, numLUIDs, index); + } + + bool getDisplayName( + HMONITOR hMonitor, + WCHAR (&Name)[32]) { + return s_driver->getDisplayName(hMonitor, Name); + } + + bool getDesktopCoordinates( + HMONITOR hMonitor, + RECT* pRect) { + return s_driver->getDesktopCoordinates(hMonitor, pRect); + } + + bool getDisplayMode( + HMONITOR hMonitor, + uint32_t modeNumber, + WsiMode* pMode) { + return s_driver->getDisplayMode(hMonitor, modeNumber, pMode); + } + + bool getCurrentDisplayMode( + HMONITOR hMonitor, + WsiMode* pMode) { + return s_driver->getCurrentDisplayMode(hMonitor, pMode); + } + + bool getDesktopDisplayMode( + HMONITOR hMonitor, + WsiMode* pMode) { + return s_driver->getDesktopDisplayMode(hMonitor, pMode); + } + + WsiEdidData getMonitorEdid(HMONITOR hMonitor) { + return s_driver->getMonitorEdid(hMonitor); } } diff --git a/src/wsi/wsi_platform.h b/src/wsi/wsi_platform.h index 7ccecc27f..f8373fe53 100644 --- a/src/wsi/wsi_platform.h +++ b/src/wsi/wsi_platform.h @@ -1,19 +1,100 @@ #pragma once -#if defined(DXVK_WSI_WIN32) -#include "win32/wsi_platform_win32.h" -#elif defined(DXVK_WSI_SDL2) -#include "sdl2/wsi_platform_sdl2.h" -#elif defined(DXVK_WSI_GLFW) -#include "glfw/wsi_platform_glfw.h" -#endif +#include "wsi_window.h" #include namespace dxvk::wsi { + class WsiDriver { + public: + virtual ~WsiDriver() { + } + + // Platform + virtual std::vector getInstanceExtensions() = 0; + + // Monitor + virtual HMONITOR getDefaultMonitor() = 0; + + virtual HMONITOR enumMonitors(uint32_t index) = 0; + + virtual HMONITOR enumMonitors(const LUID *adapterLUID[], uint32_t numLUIDs, uint32_t index) = 0; + + virtual bool getDisplayName( + HMONITOR hMonitor, + WCHAR (&Name)[32]) = 0; + + virtual bool getDesktopCoordinates( + HMONITOR hMonitor, + RECT* pRect) = 0; + + virtual bool getDisplayMode( + HMONITOR hMonitor, + uint32_t modeNumber, + WsiMode* pMode) = 0; + + virtual bool getCurrentDisplayMode( + HMONITOR hMonitor, + WsiMode* pMode) = 0; + + virtual bool getDesktopDisplayMode( + HMONITOR hMonitor, + WsiMode* pMode) = 0; + + virtual WsiEdidData getMonitorEdid(HMONITOR hMonitor) = 0; + + // Window + + virtual void getWindowSize( + HWND hWindow, + uint32_t* pWidth, + uint32_t* pWeight) = 0; + + virtual void resizeWindow( + HWND hWindow, + DxvkWindowState* pState, + uint32_t width, + uint32_t weight) = 0; + + virtual bool setWindowMode( + HMONITOR hMonitor, + HWND hWindow, + const WsiMode& mode) = 0; + + virtual bool enterFullscreenMode( + HMONITOR hMonitor, + HWND hWindow, + DxvkWindowState* pState, + [[maybe_unused]] + bool modeSwitch) = 0; + + virtual bool leaveFullscreenMode( + HWND hWindow, + DxvkWindowState* pState, + bool restoreCoordinates) = 0; + + virtual bool restoreDisplayMode() = 0; + + virtual HMONITOR getWindowMonitor(HWND hWindow) = 0; + + virtual bool isWindow(HWND hWindow) = 0; + + virtual void updateFullscreenWindow( + HMONITOR hMonitor, + HWND hWindow, + bool forceTopmost) = 0; + + virtual VkResult createSurface( + HWND hWindow, + PFN_vkGetInstanceProcAddr pfnVkGetInstanceProcAddr, + VkInstance instance, + VkSurfaceKHR* pSurface) = 0; + }; + void init(); void quit(); std::vector getInstanceExtensions(); + WsiDriver* platformCreateWsiDriver(); } diff --git a/src/wsi/wsi_window.h b/src/wsi/wsi_window.h index e0587a0fd..68c6db9c3 100644 --- a/src/wsi/wsi_window.h +++ b/src/wsi/wsi_window.h @@ -3,12 +3,22 @@ #include #include "wsi_monitor.h" -#include "wsi_platform.h" #include "../vulkan/vulkan_loader.h" namespace dxvk::wsi { + /** + * \brief Impl-dependent state + */ + struct DxvkWindowState { +#ifdef DXVK_WSI_WIN32 + LONG style = 0; + LONG exstyle = 0; + RECT rect = { 0, 0, 0, 0 }; +#endif + }; + /** * \brief The size of the window * From 10b83d184b0b17327336c9078fa9f23498828768 Mon Sep 17 00:00:00 2001 From: Ethan Lee Date: Fri, 8 Dec 2023 00:59:23 -0500 Subject: [PATCH 151/246] [native] Dynamically load SDL2/GLFW at runtime. Removing these link-time dependencies is important for making a single binary that is compatible with either backend, regardless of whether or not each one is currently available to the program. --- src/wsi/glfw/wsi_platform_glfw.cpp | 28 ++++++++++++++++++++++++++ src/wsi/glfw/wsi_platform_glfw.h | 24 +++++++++++++++------- src/wsi/glfw/wsi_platform_glfw_funcs.h | 12 +++++++++++ src/wsi/meson.build | 4 +--- src/wsi/sdl2/wsi_platform_sdl2.cpp | 28 ++++++++++++++++++++++++++ src/wsi/sdl2/wsi_platform_sdl2.h | 22 ++++++++++++++------ src/wsi/sdl2/wsi_platform_sdl2_funcs.h | 16 +++++++++++++++ 7 files changed, 118 insertions(+), 16 deletions(-) create mode 100644 src/wsi/glfw/wsi_platform_glfw_funcs.h create mode 100644 src/wsi/sdl2/wsi_platform_sdl2_funcs.h diff --git a/src/wsi/glfw/wsi_platform_glfw.cpp b/src/wsi/glfw/wsi_platform_glfw.cpp index fb711aebe..09b956289 100644 --- a/src/wsi/glfw/wsi_platform_glfw.cpp +++ b/src/wsi/glfw/wsi_platform_glfw.cpp @@ -1,9 +1,37 @@ #include "wsi_platform_glfw.h" #include "../../util/util_error.h" #include "../../util/util_string.h" +#include "../../util/util_win32_compat.h" namespace dxvk::wsi { + GlfwWsiDriver::GlfwWsiDriver() { + libglfw = LoadLibraryA( // FIXME: Get soname as string from meson +#if defined(_WIN32) + "glfw.dll" +#elif defined(__APPLE__) + "libglfw.3.dylib" +#else + "libglfw.so.3" +#endif + ); + if (libglfw == nullptr) + throw DxvkError("GLFW WSI: Failed to load GLFW DLL."); + + #define GLFW_PROC(ret, name, params) \ + name = reinterpret_cast(GetProcAddress(libglfw, #name)); \ + if (name == nullptr) { \ + FreeLibrary(libglfw); \ + libglfw = nullptr; \ + throw DxvkError("GLFW WSI: Failed to load " #name "."); \ + } + #include "wsi_platform_glfw_funcs.h" + } + + GlfwWsiDriver::~GlfwWsiDriver() { + FreeLibrary(libglfw); + } + std::vector GlfwWsiDriver::getInstanceExtensions() { if (!glfwVulkanSupported()) throw DxvkError(str::format("GLFW WSI: Vulkan is not supported in any capacity!")); diff --git a/src/wsi/glfw/wsi_platform_glfw.h b/src/wsi/glfw/wsi_platform_glfw.h index 83b95c5ee..849ce4f5f 100644 --- a/src/wsi/glfw/wsi_platform_glfw.h +++ b/src/wsi/glfw/wsi_platform_glfw.h @@ -8,7 +8,24 @@ namespace dxvk::wsi { class GlfwWsiDriver : public WsiDriver { + private: + HMODULE libglfw; + #define GLFW_PROC(ret, name, params) \ + typedef ret (*pfn_##name) params; \ + pfn_##name name; + #include "wsi_platform_glfw_funcs.h" + + inline bool isDisplayValid(int32_t displayId) { + int32_t displayCount = 0; + glfwGetMonitors(&displayCount); + + return displayId < displayCount && displayId >= 0; + } + public: + GlfwWsiDriver(); + ~GlfwWsiDriver(); + // Platform virtual std::vector getInstanceExtensions(); @@ -89,12 +106,5 @@ namespace dxvk::wsi { VkInstance instance, VkSurfaceKHR* pSurface); }; - - inline bool isDisplayValid(int32_t displayId) { - int32_t displayCount = 0; - glfwGetMonitors(&displayCount); - - return displayId < displayCount && displayId >= 0; - } } diff --git a/src/wsi/glfw/wsi_platform_glfw_funcs.h b/src/wsi/glfw/wsi_platform_glfw_funcs.h new file mode 100644 index 000000000..89cedfd0d --- /dev/null +++ b/src/wsi/glfw/wsi_platform_glfw_funcs.h @@ -0,0 +1,12 @@ +GLFW_PROC(VkResult, glfwCreateWindowSurface, (VkInstance, GLFWwindow*, const VkAllocationCallbacks*, VkSurfaceKHR*)) +GLFW_PROC(GLFWmonitor**, glfwGetMonitors, (int*)) +GLFW_PROC(void, glfwGetMonitorWorkarea, (GLFWmonitor*, int*, int*, int*, int*)) +GLFW_PROC(GLFWmonitor*, glfwGetPrimaryMonitor, (void)) +GLFW_PROC(const char**, glfwGetRequiredInstanceExtensions, (uint32_t*)) +GLFW_PROC(const GLFWvidmode*, glfwGetVideoMode, (GLFWmonitor*)) +GLFW_PROC(const GLFWvidmode*, glfwGetVideoModes, (GLFWmonitor*, int*)) +GLFW_PROC(void, glfwGetWindowSize, (GLFWwindow*, int*, int*)) +GLFW_PROC(void, glfwSetWindowMonitor, (GLFWwindow*, GLFWmonitor*, int, int, int, int, int)) +GLFW_PROC(void, glfwSetWindowSize, (GLFWwindow*, int, int)) +GLFW_PROC(int, glfwVulkanSupported, (void)) +#undef GLFW_PROC diff --git a/src/wsi/meson.build b/src/wsi/meson.build index 1cabcf2f5..f16a4e39e 100644 --- a/src/wsi/meson.build +++ b/src/wsi/meson.build @@ -23,16 +23,14 @@ wsi_glfw_src = [ if dxvk_wsi == 'win32' wsi_src = wsi_common_src + wsi_win32_src - wsi_deps = [ dep_displayinfo ] elif dxvk_wsi == 'sdl2' wsi_src = wsi_common_src + wsi_sdl2_src - wsi_deps = [ dep_displayinfo, lib_sdl2 ] elif dxvk_wsi == 'glfw' wsi_src = wsi_common_src + wsi_glfw_src - wsi_deps = [ dep_displayinfo, lib_glfw ] else error('Unknown wsi') endif +wsi_deps = [ dep_displayinfo ] wsi_lib = static_library('wsi', wsi_src, dependencies : wsi_deps, diff --git a/src/wsi/sdl2/wsi_platform_sdl2.cpp b/src/wsi/sdl2/wsi_platform_sdl2.cpp index 96ecb0762..d25744300 100644 --- a/src/wsi/sdl2/wsi_platform_sdl2.cpp +++ b/src/wsi/sdl2/wsi_platform_sdl2.cpp @@ -1,11 +1,39 @@ #include "wsi_platform_sdl2.h" #include "../../util/util_error.h" #include "../../util/util_string.h" +#include "../../util/util_win32_compat.h" #include namespace dxvk::wsi { + Sdl2WsiDriver::Sdl2WsiDriver() { + libsdl = LoadLibraryA( // FIXME: Get soname as string from meson +#if defined(_WIN32) + "SDL2.dll" +#elif defined(__APPLE__) + "libSDL2-2.0.0.dylib" +#else + "libSDL2-2.0.so.0" +#endif + ); + if (libsdl == nullptr) + throw DxvkError("SDL2 WSI: Failed to load SDL2 DLL."); + + #define SDL_PROC(ret, name, params) \ + name = reinterpret_cast(GetProcAddress(libsdl, #name)); \ + if (name == nullptr) { \ + FreeLibrary(libsdl); \ + libsdl = nullptr; \ + throw DxvkError("SDL2 WSI: Failed to load " #name "."); \ + } + #include "wsi_platform_sdl2_funcs.h" + } + + Sdl2WsiDriver::~Sdl2WsiDriver() { + FreeLibrary(libsdl); + } + std::vector Sdl2WsiDriver::getInstanceExtensions() { SDL_Vulkan_LoadLibrary(nullptr); diff --git a/src/wsi/sdl2/wsi_platform_sdl2.h b/src/wsi/sdl2/wsi_platform_sdl2.h index 845725abe..fc3ce353e 100644 --- a/src/wsi/sdl2/wsi_platform_sdl2.h +++ b/src/wsi/sdl2/wsi_platform_sdl2.h @@ -7,7 +7,23 @@ namespace dxvk::wsi { class Sdl2WsiDriver : public WsiDriver { + private: + HMODULE libsdl; + #define SDL_PROC(ret, name, params) \ + typedef ret (SDLCALL *pfn_##name) params; \ + pfn_##name name; + #include "wsi_platform_sdl2_funcs.h" + + inline bool isDisplayValid(int32_t displayId) { + const int32_t displayCount = SDL_GetNumVideoDisplays(); + + return displayId < displayCount && displayId >= 0; + } + public: + Sdl2WsiDriver(); + ~Sdl2WsiDriver(); + // Platform virtual std::vector getInstanceExtensions(); @@ -88,11 +104,5 @@ namespace dxvk::wsi { VkInstance instance, VkSurfaceKHR* pSurface); }; - - inline bool isDisplayValid(int32_t displayId) { - const int32_t displayCount = SDL_GetNumVideoDisplays(); - - return displayId < displayCount && displayId >= 0; - } } diff --git a/src/wsi/sdl2/wsi_platform_sdl2_funcs.h b/src/wsi/sdl2/wsi_platform_sdl2_funcs.h new file mode 100644 index 000000000..3da94922d --- /dev/null +++ b/src/wsi/sdl2/wsi_platform_sdl2_funcs.h @@ -0,0 +1,16 @@ +SDL_PROC(SDL_DisplayMode*, SDL_GetClosestDisplayMode, (int, const SDL_DisplayMode*, SDL_DisplayMode*)) +SDL_PROC(int, SDL_GetCurrentDisplayMode, (int, SDL_DisplayMode*)) +SDL_PROC(int, SDL_GetDesktopDisplayMode, (int, SDL_DisplayMode*)) +SDL_PROC(int, SDL_GetDisplayBounds, (int, SDL_Rect*)) +SDL_PROC(int, SDL_GetDisplayMode, (int, int, SDL_DisplayMode*)) +SDL_PROC(const char*, SDL_GetError, (void)) +SDL_PROC(int, SDL_GetNumVideoDisplays, (void)) +SDL_PROC(int, SDL_GetWindowDisplayIndex, (SDL_Window*)) +SDL_PROC(int, SDL_SetWindowDisplayMode, (SDL_Window*, const SDL_DisplayMode*)) +SDL_PROC(int, SDL_SetWindowFullscreen, (SDL_Window*, Uint32)) +SDL_PROC(void, SDL_GetWindowSize, (SDL_Window*, int*, int*)) +SDL_PROC(void, SDL_SetWindowSize, (SDL_Window*, int, int)) +SDL_PROC(SDL_bool, SDL_Vulkan_CreateSurface, (SDL_Window*, VkInstance, VkSurfaceKHR*)) +SDL_PROC(SDL_bool, SDL_Vulkan_GetInstanceExtensions, (SDL_Window*, unsigned int*, const char**)) +SDL_PROC(int, SDL_Vulkan_LoadLibrary, (const char*)) +#undef SDL_PROC From d5d236a1e2f98873e2fe2be0686cc75359659346 Mon Sep 17 00:00:00 2001 From: Ethan Lee Date: Fri, 8 Dec 2023 01:18:54 -0500 Subject: [PATCH 152/246] [wsi] Refactor platform system to support multiple WSI implementations --- meson.build | 15 ++++++------ src/wsi/glfw/wsi_monitor_glfw.cpp | 4 ++++ src/wsi/glfw/wsi_platform_glfw.cpp | 18 ++++++++++++-- src/wsi/glfw/wsi_window_glfw.cpp | 6 ++++- src/wsi/meson.build | 20 +--------------- src/wsi/sdl2/wsi_monitor_sdl2.cpp | 4 ++++ src/wsi/sdl2/wsi_platform_sdl2.cpp | 18 ++++++++++++-- src/wsi/sdl2/wsi_window_sdl2.cpp | 6 ++++- src/wsi/win32/wsi_monitor_win32.cpp | 4 ++++ src/wsi/win32/wsi_platform_win32.cpp | 14 +++++++++-- src/wsi/win32/wsi_window_win32.cpp | 22 ++++++++++------- src/wsi/wsi_platform.cpp | 35 ++++++++++++++++++++++++++-- src/wsi/wsi_platform.h | 16 ++++++++++++- src/wsi/wsi_window.h | 16 +++++++++---- 14 files changed, 148 insertions(+), 50 deletions(-) diff --git a/meson.build b/meson.build index 2a2637359..158952af4 100644 --- a/meson.build +++ b/meson.build @@ -115,7 +115,6 @@ if platform == 'windows' ) endif - dxvk_wsi = 'win32' dxvk_name_prefix = '' compiler_args += ['-DDXVK_WSI_WIN32'] else @@ -128,15 +127,17 @@ else './include/native/directx' ] - dxvk_wsi = get_option('dxvk_native_wsi') - - if dxvk_wsi == 'sdl2' - lib_sdl2 = cpp.find_library('SDL2') + lib_sdl2 = cpp.find_library('SDL2', required: false) + lib_glfw = cpp.find_library('glfw', required: false) + if lib_sdl2.found() compiler_args += ['-DDXVK_WSI_SDL2'] - elif dxvk_wsi == 'glfw' - lib_glfw = cpp.find_library('glfw') + endif + if lib_glfw.found() compiler_args += ['-DDXVK_WSI_GLFW'] endif + if (not lib_sdl2.found() and not lib_glfw.found()) + error('SDL2 or GLFW are required to build dxvk-native') + endif dxvk_name_prefix = 'libdxvk_' diff --git a/src/wsi/glfw/wsi_monitor_glfw.cpp b/src/wsi/glfw/wsi_monitor_glfw.cpp index 00abc89aa..f6c09aaf0 100644 --- a/src/wsi/glfw/wsi_monitor_glfw.cpp +++ b/src/wsi/glfw/wsi_monitor_glfw.cpp @@ -1,3 +1,5 @@ +#if defined(DXVK_WSI_GLFW) + #include "../wsi_monitor.h" #include "wsi/native_wsi.h" @@ -165,3 +167,5 @@ namespace dxvk::wsi { } } + +#endif diff --git a/src/wsi/glfw/wsi_platform_glfw.cpp b/src/wsi/glfw/wsi_platform_glfw.cpp index 09b956289..ee818bb20 100644 --- a/src/wsi/glfw/wsi_platform_glfw.cpp +++ b/src/wsi/glfw/wsi_platform_glfw.cpp @@ -1,3 +1,5 @@ +#if defined(DXVK_WSI_GLFW) + #include "wsi_platform_glfw.h" #include "../../util/util_error.h" #include "../../util/util_string.h" @@ -50,8 +52,20 @@ namespace dxvk::wsi { return names; } - WsiDriver* platformCreateWsiDriver() { - return new GlfwWsiDriver(); + static bool createGlfwWsiDriver(WsiDriver **driver) { + try { + *driver = new GlfwWsiDriver(); + } catch (const DxvkError& e) { + return false; + } + return true; } + WsiBootstrap GlfwWSI = { + "GLFW", + createGlfwWsiDriver + }; + } + +#endif diff --git a/src/wsi/glfw/wsi_window_glfw.cpp b/src/wsi/glfw/wsi_window_glfw.cpp index b23a79230..f763e759a 100644 --- a/src/wsi/glfw/wsi_window_glfw.cpp +++ b/src/wsi/glfw/wsi_window_glfw.cpp @@ -1,6 +1,8 @@ +#if defined(DXVK_WSI_GLFW) + #include "../wsi_window.h" -#include "native/wsi/native_wsi.h" +#include "native/wsi/native_glfw.h" #include "wsi_platform_glfw.h" #include "../../util/util_string.h" @@ -142,3 +144,5 @@ namespace dxvk::wsi { } } + +#endif diff --git a/src/wsi/meson.build b/src/wsi/meson.build index f16a4e39e..f60840b93 100644 --- a/src/wsi/meson.build +++ b/src/wsi/meson.build @@ -1,35 +1,17 @@ -wsi_common_src = [ +wsi_src = [ 'wsi_edid.cpp', 'wsi_platform.cpp', -] - -wsi_win32_src = [ 'win32/wsi_monitor_win32.cpp', 'win32/wsi_platform_win32.cpp', 'win32/wsi_window_win32.cpp', -] - -wsi_sdl2_src = [ 'sdl2/wsi_monitor_sdl2.cpp', 'sdl2/wsi_platform_sdl2.cpp', 'sdl2/wsi_window_sdl2.cpp', -] - -wsi_glfw_src = [ 'glfw/wsi_monitor_glfw.cpp', 'glfw/wsi_platform_glfw.cpp', 'glfw/wsi_window_glfw.cpp', ] -if dxvk_wsi == 'win32' - wsi_src = wsi_common_src + wsi_win32_src -elif dxvk_wsi == 'sdl2' - wsi_src = wsi_common_src + wsi_sdl2_src -elif dxvk_wsi == 'glfw' - wsi_src = wsi_common_src + wsi_glfw_src -else - error('Unknown wsi') -endif wsi_deps = [ dep_displayinfo ] wsi_lib = static_library('wsi', wsi_src, diff --git a/src/wsi/sdl2/wsi_monitor_sdl2.cpp b/src/wsi/sdl2/wsi_monitor_sdl2.cpp index 5903ef68d..8f5f32900 100644 --- a/src/wsi/sdl2/wsi_monitor_sdl2.cpp +++ b/src/wsi/sdl2/wsi_monitor_sdl2.cpp @@ -1,3 +1,5 @@ +#if defined(DXVK_WSI_SDL2) + #include "../wsi_monitor.h" #include "wsi/native_wsi.h" @@ -154,3 +156,5 @@ namespace dxvk::wsi { } } + +#endif diff --git a/src/wsi/sdl2/wsi_platform_sdl2.cpp b/src/wsi/sdl2/wsi_platform_sdl2.cpp index d25744300..9223b5980 100644 --- a/src/wsi/sdl2/wsi_platform_sdl2.cpp +++ b/src/wsi/sdl2/wsi_platform_sdl2.cpp @@ -1,3 +1,5 @@ +#if defined(DXVK_WSI_SDL2) + #include "wsi_platform_sdl2.h" #include "../../util/util_error.h" #include "../../util/util_string.h" @@ -48,8 +50,20 @@ namespace dxvk::wsi { return extensionNames; } - WsiDriver* platformCreateWsiDriver() { - return new Sdl2WsiDriver(); + static bool createSdl2WsiDriver(WsiDriver **driver) { + try { + *driver = new Sdl2WsiDriver(); + } catch (const DxvkError& e) { + return false; + } + return true; } + WsiBootstrap Sdl2WSI = { + "SDL2", + createSdl2WsiDriver + }; + } + +#endif diff --git a/src/wsi/sdl2/wsi_window_sdl2.cpp b/src/wsi/sdl2/wsi_window_sdl2.cpp index 5622e8762..68e902298 100644 --- a/src/wsi/sdl2/wsi_window_sdl2.cpp +++ b/src/wsi/sdl2/wsi_window_sdl2.cpp @@ -1,6 +1,8 @@ +#if defined(DXVK_WSI_SDL2) + #include "../wsi_window.h" -#include "native/wsi/native_wsi.h" +#include "native/wsi/native_sdl2.h" #include "wsi_platform_sdl2.h" #include "../../util/util_string.h" @@ -155,3 +157,5 @@ namespace dxvk::wsi { } } + +#endif diff --git a/src/wsi/win32/wsi_monitor_win32.cpp b/src/wsi/win32/wsi_monitor_win32.cpp index b37e09028..0387d87f4 100644 --- a/src/wsi/win32/wsi_monitor_win32.cpp +++ b/src/wsi/win32/wsi_monitor_win32.cpp @@ -1,3 +1,5 @@ +#if defined(DXVK_WSI_WIN32) + #include "wsi_platform_win32.h" #include "../../util/util_string.h" @@ -370,3 +372,5 @@ namespace dxvk::wsi { } } + +#endif diff --git a/src/wsi/win32/wsi_platform_win32.cpp b/src/wsi/win32/wsi_platform_win32.cpp index ab27869a8..03bf40a55 100644 --- a/src/wsi/win32/wsi_platform_win32.cpp +++ b/src/wsi/win32/wsi_platform_win32.cpp @@ -1,3 +1,5 @@ +#if defined(DXVK_WSI_WIN32) + #include "wsi_platform_win32.h" namespace dxvk::wsi { @@ -6,8 +8,16 @@ namespace dxvk::wsi { return { VK_KHR_WIN32_SURFACE_EXTENSION_NAME }; } - WsiDriver* platformCreateWsiDriver() { - return new Win32WsiDriver(); + static bool createWin32WsiDriver(WsiDriver **driver) { + *driver = new Win32WsiDriver(); + return true; } + WsiBootstrap Win32WSI = { + "Win32", + createWin32WsiDriver + }; + } + +#endif diff --git a/src/wsi/win32/wsi_window_win32.cpp b/src/wsi/win32/wsi_window_win32.cpp index cf446980a..894da8e7b 100644 --- a/src/wsi/win32/wsi_window_win32.cpp +++ b/src/wsi/win32/wsi_window_win32.cpp @@ -1,3 +1,5 @@ +#if defined(DXVK_WSI_WIN32) + #include "wsi_platform_win32.h" #include "../../util/util_string.h" @@ -169,14 +171,14 @@ namespace dxvk::wsi { [[maybe_unused]] bool modeSwitch) { // Find a display mode that matches what we need - ::GetWindowRect(hWindow, &pState->rect); + ::GetWindowRect(hWindow, &pState->win.rect); // Change the window flags to remove the decoration etc. LONG style = ::GetWindowLongW(hWindow, GWL_STYLE); LONG exstyle = ::GetWindowLongW(hWindow, GWL_EXSTYLE); - pState->style = style; - pState->exstyle = exstyle; + pState->win.style = style; + pState->win.exstyle = exstyle; style &= ~WS_OVERLAPPEDWINDOW; exstyle &= ~WS_EX_OVERLAPPEDWINDOW; @@ -204,20 +206,20 @@ namespace dxvk::wsi { LONG curStyle = ::GetWindowLongW(hWindow, GWL_STYLE) & ~WS_VISIBLE; LONG curExstyle = ::GetWindowLongW(hWindow, GWL_EXSTYLE) & ~WS_EX_TOPMOST; - if (curStyle == (pState->style & ~(WS_VISIBLE | WS_OVERLAPPEDWINDOW)) - && curExstyle == (pState->exstyle & ~(WS_EX_TOPMOST | WS_EX_OVERLAPPEDWINDOW))) { - ::SetWindowLongW(hWindow, GWL_STYLE, pState->style); - ::SetWindowLongW(hWindow, GWL_EXSTYLE, pState->exstyle); + if (curStyle == (pState->win.style & ~(WS_VISIBLE | WS_OVERLAPPEDWINDOW)) + && curExstyle == (pState->win.exstyle & ~(WS_EX_TOPMOST | WS_EX_OVERLAPPEDWINDOW))) { + ::SetWindowLongW(hWindow, GWL_STYLE, pState->win.style); + ::SetWindowLongW(hWindow, GWL_EXSTYLE, pState->win.exstyle); } // Restore window position and apply the style UINT flags = SWP_FRAMECHANGED | SWP_NOACTIVATE; - const RECT rect = pState->rect; + const RECT rect = pState->win.rect; if (!restoreCoordinates) flags |= SWP_NOSIZE | SWP_NOMOVE; - ::SetWindowPos(hWindow, (pState->exstyle & WS_EX_TOPMOST) ? HWND_TOPMOST : HWND_NOTOPMOST, + ::SetWindowPos(hWindow, (pState->win.exstyle & WS_EX_TOPMOST) ? HWND_TOPMOST : HWND_NOTOPMOST, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, flags); return true; @@ -295,3 +297,5 @@ namespace dxvk::wsi { } } + +#endif diff --git a/src/wsi/wsi_platform.cpp b/src/wsi/wsi_platform.cpp index f4802025a..a72e1de44 100644 --- a/src/wsi/wsi_platform.cpp +++ b/src/wsi/wsi_platform.cpp @@ -1,18 +1,49 @@ #include "wsi_platform.h" #include "wsi_monitor.h" #include "wsi_window.h" +#include "../util/util_env.h" #include "../util/util_error.h" namespace dxvk::wsi { static WsiDriver* s_driver = nullptr; static int s_refcount = 0; + static const WsiBootstrap *wsiBootstrap[] = { +#if defined(DXVK_WSI_WIN32) + &Win32WSI, +#endif +#if defined(DXVK_WSI_SDL2) + &Sdl2WSI, +#endif +#if defined(DXVK_WSI_GLFW) + &GlfwWSI, +#endif + }; + void init() { if (s_refcount++ > 0) return; - s_driver = platformCreateWsiDriver(); - if (s_driver == nullptr) + std::string hint = dxvk::env::getEnvVar("DXVK_WSIDRIVER"); + if (hint == "") { + // At least for Windows, it is reasonable to fall back to a default; + // for other platforms however we _need_ to know which WSI to use! +#if defined(DXVK_WSI_WIN32) + hint = "Win32"; +#else + throw DxvkError("DXVK_WSIDRIVER environment variable unset"); +#endif + } + + bool success = false; + for (const WsiBootstrap *b : wsiBootstrap) { + if (hint == b->name && b->createDriver(&s_driver)) { + success = true; + break; + } + } + + if (!success) throw DxvkError("Failed to initialize WSI."); } diff --git a/src/wsi/wsi_platform.h b/src/wsi/wsi_platform.h index f8373fe53..8c37bc949 100644 --- a/src/wsi/wsi_platform.h +++ b/src/wsi/wsi_platform.h @@ -92,9 +92,23 @@ namespace dxvk::wsi { VkSurfaceKHR* pSurface) = 0; }; + struct WsiBootstrap { + const std::string name; + bool (*createDriver)(WsiDriver **driver); + }; + +#if defined(DXVK_WSI_WIN32) + extern WsiBootstrap Win32WSI; +#endif +#if defined(DXVK_WSI_SDL2) + extern WsiBootstrap Sdl2WSI; +#endif +#if defined(DXVK_WSI_GLFW) + extern WsiBootstrap GlfwWSI; +#endif + void init(); void quit(); std::vector getInstanceExtensions(); - WsiDriver* platformCreateWsiDriver(); } diff --git a/src/wsi/wsi_window.h b/src/wsi/wsi_window.h index 68c6db9c3..ceb149238 100644 --- a/src/wsi/wsi_window.h +++ b/src/wsi/wsi_window.h @@ -12,10 +12,18 @@ namespace dxvk::wsi { * \brief Impl-dependent state */ struct DxvkWindowState { -#ifdef DXVK_WSI_WIN32 - LONG style = 0; - LONG exstyle = 0; - RECT rect = { 0, 0, 0, 0 }; +#if defined(DXVK_WSI_WIN32) + struct { + LONG style = 0; + LONG exstyle = 0; + RECT rect = { 0, 0, 0, 0 }; + } win; +#endif +#if defined(DXVK_WSI_SDL2) + // Nothing to store +#endif +#if defined(DXVK_WSI_GLFW) + // Nothing to store #endif }; From 6259e863921777dfbdff5f907cbcfb02ce700b99 Mon Sep 17 00:00:00 2001 From: Ethan Lee Date: Sat, 10 Feb 2024 13:06:33 -0500 Subject: [PATCH 153/246] [meson] Use dependency() instead of find_library() for SDL2/GLFW detection. Since we're not linking to the libraries anymore, it doesn't make much sense to use find_library, and in fact we need to use dependency() in order to get the right CFLAGS for includes, defines, etc, so use that instead. As a result, we can remove the 'SDL2/' folders from the includes, making the SDL includes more correct. --- include/native/wsi/native_sdl2.h | 4 ++-- meson.build | 4 ++-- src/wsi/meson.build | 7 +++++++ src/wsi/sdl2/wsi_platform_sdl2.h | 2 +- src/wsi/sdl2/wsi_window_sdl2.cpp | 2 +- 5 files changed, 13 insertions(+), 6 deletions(-) diff --git a/include/native/wsi/native_sdl2.h b/include/native/wsi/native_sdl2.h index b197d9527..7ece3a550 100644 --- a/include/native/wsi/native_sdl2.h +++ b/include/native/wsi/native_sdl2.h @@ -1,6 +1,6 @@ #include -#include +#include namespace dxvk::wsi { @@ -22,4 +22,4 @@ namespace dxvk::wsi { return reinterpret_cast(static_cast(displayId + 1)); } -} \ No newline at end of file +} diff --git a/meson.build b/meson.build index 158952af4..823ffbbd4 100644 --- a/meson.build +++ b/meson.build @@ -127,8 +127,8 @@ else './include/native/directx' ] - lib_sdl2 = cpp.find_library('SDL2', required: false) - lib_glfw = cpp.find_library('glfw', required: false) + lib_sdl2 = dependency('SDL2', required: false) + lib_glfw = dependency('glfw', required: false) if lib_sdl2.found() compiler_args += ['-DDXVK_WSI_SDL2'] endif diff --git a/src/wsi/meson.build b/src/wsi/meson.build index f60840b93..4cfb13a8e 100644 --- a/src/wsi/meson.build +++ b/src/wsi/meson.build @@ -14,6 +14,13 @@ wsi_src = [ wsi_deps = [ dep_displayinfo ] +if platform != 'windows' + wsi_deps += [ + lib_sdl2.partial_dependency(compile_args: true, includes: true), + lib_glfw.partial_dependency(compile_args: true, includes: true), + ] +endif + wsi_lib = static_library('wsi', wsi_src, dependencies : wsi_deps, include_directories : [ dxvk_include_path ]) diff --git a/src/wsi/sdl2/wsi_platform_sdl2.h b/src/wsi/sdl2/wsi_platform_sdl2.h index fc3ce353e..1a6ac8aad 100644 --- a/src/wsi/sdl2/wsi_platform_sdl2.h +++ b/src/wsi/sdl2/wsi_platform_sdl2.h @@ -1,6 +1,6 @@ #pragma once -#include +#include #include "../wsi_platform.h" diff --git a/src/wsi/sdl2/wsi_window_sdl2.cpp b/src/wsi/sdl2/wsi_window_sdl2.cpp index 68e902298..33084dc34 100644 --- a/src/wsi/sdl2/wsi_window_sdl2.cpp +++ b/src/wsi/sdl2/wsi_window_sdl2.cpp @@ -9,7 +9,7 @@ #include "../../util/log/log.h" #include -#include +#include namespace dxvk::wsi { From c7d61b2fc0a588d6812fe2b3fd5750c54e530df4 Mon Sep 17 00:00:00 2001 From: Blisto91 <47954800+Blisto91@users.noreply.github.com> Date: Thu, 16 May 2024 12:20:36 +0200 Subject: [PATCH 154/246] [native] Change DXVK_WSIDRIVER to DXVK_WSI_DRIVER --- src/wsi/wsi_platform.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wsi/wsi_platform.cpp b/src/wsi/wsi_platform.cpp index a72e1de44..3245eb13e 100644 --- a/src/wsi/wsi_platform.cpp +++ b/src/wsi/wsi_platform.cpp @@ -24,14 +24,14 @@ namespace dxvk::wsi { if (s_refcount++ > 0) return; - std::string hint = dxvk::env::getEnvVar("DXVK_WSIDRIVER"); + std::string hint = dxvk::env::getEnvVar("DXVK_WSI_DRIVER"); if (hint == "") { // At least for Windows, it is reasonable to fall back to a default; // for other platforms however we _need_ to know which WSI to use! #if defined(DXVK_WSI_WIN32) hint = "Win32"; #else - throw DxvkError("DXVK_WSIDRIVER environment variable unset"); + throw DxvkError("DXVK_WSI_DRIVER environment variable unset"); #endif } From 0c2efda804c30a42bd5df2ff39244069f822304f Mon Sep 17 00:00:00 2001 From: Blisto91 <47954800+Blisto91@users.noreply.github.com> Date: Thu, 16 May 2024 12:23:36 +0200 Subject: [PATCH 155/246] [meta] Add DXVK Native section to the readme --- README.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/README.md b/README.md index 7cc7f787b..364f2bb74 100644 --- a/README.md +++ b/README.md @@ -163,3 +163,22 @@ For non debian based distros, make sure that your mingw-w64-gcc cross compiler does have `--enable-threads=posix` enabled during configure. If your distro does ship its mingw-w64-gcc binary with `--enable-threads=win32` you might have to recompile locally or open a bug at your distro's bugtracker to ask for it. + +# DXVK Native + +DXVK Native is a version of DXVK which allows it to be used natively without Wine. + +This is primarily useful for game and application ports to either avoid having to write another rendering backend, or to help with port bringup during development. + +[Release builds](https://github.com/doitsujin/dxvk/releases) are built using the Steam Runtime. + +### How does it work? + +DXVK Native replaces certain Windows-isms with a platform and framework-agnostic replacement, for example, `HWND`s can become `SDL_Window*`s, etc. +All it takes to do that is to add another WSI backend. + +**Note:** DXVK Native requires a backend to be explicitly set via the `DXVK_WSI_DRIVER` environment variable. The current built-in options are `SDL2` and `GLFW`. + +DXVK Native comes with a slim set of Windows header definitions required for D3D9/11 and the MinGW headers for D3D9/11. +In most cases, it will end up being plug and play with your renderer, but there may be certain teething issues such as: +- `__uuidof(type)` is supported, but `__uuidof(variable)` is not supported. Use `__uuidof_var(variable)` instead. From 20185a53096d22185a5897dcfd6c6f4720a8c200 Mon Sep 17 00:00:00 2001 From: WinterSnowfall Date: Sat, 18 May 2024 16:27:45 +0300 Subject: [PATCH 156/246] [d3d9] Do not enable support for DF formats on Nvidia --- dxvk.conf | 4 +++- src/d3d9/d3d9_format.cpp | 6 +++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/dxvk.conf b/dxvk.conf index 3e739704b..46edbb9c0 100644 --- a/dxvk.conf +++ b/dxvk.conf @@ -515,7 +515,9 @@ # Support DF formats # -# Support the vendor extension DF floating point depth formats +# Support the vendor extension DF floating point depth formats on AMD and Intel. +# Note that this config is ignored and disabled by default on Nvidia, or when +# spoofing a Nvidia GPU, as it does not support these formats natively. # # Supported values: # - True/False diff --git a/src/d3d9/d3d9_format.cpp b/src/d3d9/d3d9_format.cpp index 87431a874..31ce7290b 100644 --- a/src/d3d9/d3d9_format.cpp +++ b/src/d3d9/d3d9_format.cpp @@ -438,7 +438,11 @@ namespace dxvk { D3D9VkFormatTable::D3D9VkFormatTable( const Rc& adapter, const D3D9Options& options) { - m_dfSupport = options.supportDFFormats; + + const auto& props = adapter->deviceProperties(); + uint32_t vendorId = options.customVendorId == -1 ? props.vendorID : uint32_t(options.customVendorId); + + m_dfSupport = options.supportDFFormats && DxvkGpuVendor(vendorId) != DxvkGpuVendor::Nvidia; m_x4r4g4b4Support = options.supportX4R4G4B4; m_d32supportFinal = options.supportD32; From c1f665f92be1b6bcd8ef53afbdbb684424cb4942 Mon Sep 17 00:00:00 2001 From: Blisto91 <47954800+Blisto91@users.noreply.github.com> Date: Sat, 18 May 2024 19:15:57 +0200 Subject: [PATCH 157/246] [util] Disable supportDFFormats for Prototype Incorrect shadows on AMD & Intel --- src/util/config/config.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index 25d730f82..be78b58f8 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -898,6 +898,11 @@ namespace dxvk { { R"(\\TS3(W)?\.exe$)", {{ { "d3d9.deviceLossOnFocusLoss", "True" }, }} }, + /* Prototype * + * Incorrect shadows on AMD & Intel */ + { R"(\\prototypef\.exe$)", {{ + { "d3d9.supportDFFormats", "False" }, + }} }, /**********************************************/ From 2cb2f8694e4695a8fa28ca9501f1f9a12477b1b6 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Fri, 17 May 2024 20:39:18 +0200 Subject: [PATCH 158/246] [dxvk] Use VK_MAKE_API_VERSION instead of VK_MAKE_VERSION. The old macro is deprecated. --- src/dxvk/dxvk_device_filter.cpp | 6 +++--- src/dxvk/dxvk_instance.cpp | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/dxvk/dxvk_device_filter.cpp b/src/dxvk/dxvk_device_filter.cpp index 23d182480..a7546d4b0 100644 --- a/src/dxvk/dxvk_device_filter.cpp +++ b/src/dxvk/dxvk_device_filter.cpp @@ -17,10 +17,10 @@ namespace dxvk { bool DxvkDeviceFilter::testAdapter(const VkPhysicalDeviceProperties& properties) const { - if (properties.apiVersion < VK_MAKE_VERSION(1, 3, 0)) { + if (properties.apiVersion < VK_MAKE_API_VERSION(0, 1, 3, 0)) { Logger::warn(str::format("Skipping Vulkan ", - VK_VERSION_MAJOR(properties.apiVersion), ".", - VK_VERSION_MINOR(properties.apiVersion), " adapter: ", + VK_API_VERSION_MAJOR(properties.apiVersion), ".", + VK_API_VERSION_MINOR(properties.apiVersion), " adapter: ", properties.deviceName)); return false; } diff --git a/src/dxvk/dxvk_instance.cpp b/src/dxvk/dxvk_instance.cpp index 76ba78c54..d23a35619 100644 --- a/src/dxvk/dxvk_instance.cpp +++ b/src/dxvk/dxvk_instance.cpp @@ -182,8 +182,8 @@ namespace dxvk { appInfo.pApplicationName = appName.c_str(); appInfo.applicationVersion = flags.raw(); appInfo.pEngineName = "DXVK"; - appInfo.engineVersion = VK_MAKE_VERSION(2, 3, 2); - appInfo.apiVersion = VK_MAKE_VERSION(1, 3, 0); + appInfo.engineVersion = VK_MAKE_API_VERSION(0, 2, 3, 2); + appInfo.apiVersion = VK_MAKE_API_VERSION(0, 1, 3, 0); VkInstanceCreateInfo info = { VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO }; info.pApplicationInfo = &appInfo; From 4225f35034630f5f9304880ca851de2e984cb9ef Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Fri, 17 May 2024 20:41:03 +0200 Subject: [PATCH 159/246] [util] Add version helper class Useful to decode, store and compare human-readable driver versions. --- src/util/util_version.h | 48 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 src/util/util_version.h diff --git a/src/util/util_version.h b/src/util/util_version.h new file mode 100644 index 000000000..995d8b94d --- /dev/null +++ b/src/util/util_version.h @@ -0,0 +1,48 @@ +#pragma once + +#include + +#include "../vulkan/vulkan_loader.h" + +#include "util_string.h" + +namespace dxvk { + + /** + * \brief Decoded driver version + */ + class Version { + + public: + + Version() = default; + + Version(uint32_t major, uint32_t minor, uint32_t patch) + : m_raw((uint64_t(major) << 48) | (uint64_t(minor) << 24) | uint64_t(patch)) { } + + uint32_t major() const { return uint32_t(m_raw >> 48); } + uint32_t minor() const { return uint32_t(m_raw >> 24) & 0xffffffu; } + uint32_t patch() const { return uint32_t(m_raw) & 0xffffffu; } + + bool operator == (const Version& other) const { return m_raw == other.m_raw; } + bool operator != (const Version& other) const { return m_raw != other.m_raw; } + bool operator >= (const Version& other) const { return m_raw >= other.m_raw; } + bool operator <= (const Version& other) const { return m_raw <= other.m_raw; } + bool operator > (const Version& other) const { return m_raw > other.m_raw; } + bool operator < (const Version& other) const { return m_raw < other.m_raw; } + + std::string toString() const { + return str::format(major(), ".", minor(), ".", patch()); + } + + explicit operator bool () const { + return m_raw != 0; + } + + private: + + uint64_t m_raw = 0; + + }; + +} From 3420cd78ac18867e18f7b60a0e653e8367fe8785 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Fri, 17 May 2024 21:07:37 +0200 Subject: [PATCH 160/246] [dxvk] Use new Version helper to deal with driver version numbers --- src/d3d9/d3d9_options.cpp | 4 +- src/dxvk/dxvk_adapter.cpp | 69 ++++++++++++++++++---------------- src/dxvk/dxvk_adapter.h | 15 +++++++- src/dxvk/dxvk_device.cpp | 12 +++--- src/dxvk/dxvk_device_info.h | 3 ++ src/dxvk/hud/dxvk_hud_item.cpp | 8 +--- 6 files changed, 63 insertions(+), 48 deletions(-) diff --git a/src/d3d9/d3d9_options.cpp b/src/d3d9/d3d9_options.cpp index fc3d0b481..1845daabd 100644 --- a/src/d3d9/d3d9_options.cpp +++ b/src/d3d9/d3d9_options.cpp @@ -90,8 +90,8 @@ namespace dxvk { d3d9FloatEmulation = D3D9FloatEmulation::Enabled; } else { bool hasMulz = adapter != nullptr - && (adapter->matchesDriver(VK_DRIVER_ID_MESA_RADV, 0, 0) - || adapter->matchesDriver(VK_DRIVER_ID_MESA_NVK, 0, 0)); + && (adapter->matchesDriver(VK_DRIVER_ID_MESA_RADV) + || adapter->matchesDriver(VK_DRIVER_ID_MESA_NVK)); d3d9FloatEmulation = hasMulz ? D3D9FloatEmulation::Strict : D3D9FloatEmulation::Enabled; } diff --git a/src/dxvk/dxvk_adapter.cpp b/src/dxvk/dxvk_adapter.cpp index 182f7f034..4866465df 100644 --- a/src/dxvk/dxvk_adapter.cpp +++ b/src/dxvk/dxvk_adapter.cpp @@ -415,7 +415,7 @@ namespace dxvk { m_deviceFeatures.khrPresentWait.presentWait; // Unless we're on an Nvidia driver where these extensions are known to be broken - if (matchesDriver(VK_DRIVER_ID_NVIDIA_PROPRIETARY, 0, VK_MAKE_VERSION(535, 0, 0))) { + if (matchesDriver(VK_DRIVER_ID_NVIDIA_PROPRIETARY, Version(), Version(535, 0, 0))) { enabledFeatures.khrPresentId.presentId = VK_FALSE; enabledFeatures.khrPresentWait.presentWait = VK_FALSE; } @@ -430,10 +430,7 @@ namespace dxvk { // Log feature support info an extension list Logger::info(str::format("Device properties:" "\n Device : ", m_deviceInfo.core.properties.deviceName, - "\n Driver : ", m_deviceInfo.vk12.driverName, " ", - VK_VERSION_MAJOR(m_deviceInfo.core.properties.driverVersion), ".", - VK_VERSION_MINOR(m_deviceInfo.core.properties.driverVersion), ".", - VK_VERSION_PATCH(m_deviceInfo.core.properties.driverVersion))); + "\n Driver : ", m_deviceInfo.vk12.driverName, " ", m_deviceInfo.driverVersion.toString())); Logger::info("Enabled device extensions:"); this->logNameList(extensionNameList); @@ -631,9 +628,7 @@ namespace dxvk { Logger::info(str::format("Device properties:" "\n Device name: ", m_deviceInfo.core.properties.deviceName, "\n Driver: ", m_deviceInfo.vk12.driverName, " ", - VK_VERSION_MAJOR(m_deviceInfo.core.properties.driverVersion), ".", - VK_VERSION_MINOR(m_deviceInfo.core.properties.driverVersion), ".", - VK_VERSION_PATCH(m_deviceInfo.core.properties.driverVersion))); + m_deviceInfo.driverVersion.toString())); Logger::info("Enabled device extensions:"); this->logNameList(extensionNameList); @@ -669,26 +664,29 @@ namespace dxvk { bool DxvkAdapter::matchesDriver( VkDriverIdKHR driver, - uint32_t minVer, - uint32_t maxVer) const { + Version minVer, + Version maxVer) const { bool driverMatches = driver == m_deviceInfo.vk12.driverID; - if (minVer) driverMatches &= m_deviceInfo.core.properties.driverVersion >= minVer; - if (maxVer) driverMatches &= m_deviceInfo.core.properties.driverVersion < maxVer; + if (minVer) driverMatches &= m_deviceInfo.driverVersion >= minVer; + if (maxVer) driverMatches &= m_deviceInfo.driverVersion < maxVer; return driverMatches; } + bool DxvkAdapter::matchesDriver( + VkDriverIdKHR driver) const { + return driver == m_deviceInfo.vk12.driverID; + } + + void DxvkAdapter::logAdapterInfo() const { const auto deviceInfo = this->devicePropertiesExt(); const auto memoryInfo = this->memoryProperties(); Logger::info(str::format(deviceInfo.core.properties.deviceName, ":", - "\n Driver : ", deviceInfo.vk12.driverName, " ", - VK_VERSION_MAJOR(deviceInfo.core.properties.driverVersion), ".", - VK_VERSION_MINOR(deviceInfo.core.properties.driverVersion), ".", - VK_VERSION_PATCH(deviceInfo.core.properties.driverVersion))); + "\n Driver : ", deviceInfo.vk12.driverName, " ", deviceInfo.driverVersion.toString())); for (uint32_t i = 0; i < memoryInfo.memoryHeapCount; i++) { constexpr VkDeviceSize mib = 1024 * 1024; @@ -790,22 +788,8 @@ namespace dxvk { m_vki->vkGetPhysicalDeviceProperties2(m_handle, &m_deviceInfo.core); // Some drivers reports the driver version in a slightly different format - switch (m_deviceInfo.vk12.driverID) { - case VK_DRIVER_ID_NVIDIA_PROPRIETARY: - m_deviceInfo.core.properties.driverVersion = VK_MAKE_VERSION( - (m_deviceInfo.core.properties.driverVersion >> 22) & 0x3ff, - (m_deviceInfo.core.properties.driverVersion >> 14) & 0x0ff, - (m_deviceInfo.core.properties.driverVersion >> 6) & 0x0ff); - break; - - case VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS: - m_deviceInfo.core.properties.driverVersion = VK_MAKE_VERSION( - m_deviceInfo.core.properties.driverVersion >> 14, - m_deviceInfo.core.properties.driverVersion & 0x3fff, 0); - break; - - default:; - } + m_deviceInfo.driverVersion = decodeDriverVersion( + m_deviceInfo.vk12.driverID, m_deviceInfo.core.properties.driverVersion); } @@ -1321,4 +1305,25 @@ namespace dxvk { "\n Sparse : ", queues.sparse != VK_QUEUE_FAMILY_IGNORED ? str::format(queues.sparse) : "n/a")); } + + Version DxvkAdapter::decodeDriverVersion(VkDriverId driverId, uint32_t version) { + switch (driverId) { + case VK_DRIVER_ID_NVIDIA_PROPRIETARY: + return Version( + (version >> 22) & 0x3ff, + (version >> 14) & 0x0ff, + (version >> 6) & 0x0ff); + break; + + case VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS: + return Version(version >> 14, version & 0x3fff, 0); + + default: + return Version( + VK_API_VERSION_MAJOR(version), + VK_API_VERSION_MINOR(version), + VK_API_VERSION_PATCH(version)); + } + } + } diff --git a/src/dxvk/dxvk_adapter.h b/src/dxvk/dxvk_adapter.h index ad3ca23ce..04c9c9ba8 100644 --- a/src/dxvk/dxvk_adapter.h +++ b/src/dxvk/dxvk_adapter.h @@ -255,8 +255,17 @@ namespace dxvk { */ bool matchesDriver( VkDriverIdKHR driver, - uint32_t minVer, - uint32_t maxVer) const; + Version minVer, + Version maxVer) const; + + /** + * \brief Tests if the driver matches certain criteria + * + * \param [in] driver Driver ID + * \returns \c True if the driver matches these criteria + */ + bool matchesDriver( + VkDriverIdKHR driver) const; /** * \brief Logs DXVK adapter info @@ -343,6 +352,8 @@ namespace dxvk { static void logFeatures(const DxvkDeviceFeatures& features); static void logQueueFamilies(const DxvkAdapterQueueIndices& queues); + static Version decodeDriverVersion(VkDriverId driverId, uint32_t version); + }; } diff --git a/src/dxvk/dxvk_device.cpp b/src/dxvk/dxvk_device.cpp index 9a053791a..226596f00 100644 --- a/src/dxvk/dxvk_device.cpp +++ b/src/dxvk/dxvk_device.cpp @@ -82,7 +82,7 @@ namespace dxvk { // Disable lifetime tracking for drivers that do not have any // significant issues with 32-bit address space to begin with - if (m_adapter->matchesDriver(VK_DRIVER_ID_MESA_RADV_KHR, 0, 0)) + if (m_adapter->matchesDriver(VK_DRIVER_ID_MESA_RADV_KHR)) return false; return true; @@ -325,12 +325,12 @@ namespace dxvk { DxvkDevicePerfHints DxvkDevice::getPerfHints() { DxvkDevicePerfHints hints; hints.preferFbDepthStencilCopy = m_features.extShaderStencilExport - && (m_adapter->matchesDriver(VK_DRIVER_ID_MESA_RADV_KHR, 0, 0) - || m_adapter->matchesDriver(VK_DRIVER_ID_AMD_OPEN_SOURCE_KHR, 0, 0) - || m_adapter->matchesDriver(VK_DRIVER_ID_AMD_PROPRIETARY_KHR, 0, 0)); + && (m_adapter->matchesDriver(VK_DRIVER_ID_MESA_RADV_KHR) + || m_adapter->matchesDriver(VK_DRIVER_ID_AMD_OPEN_SOURCE_KHR) + || m_adapter->matchesDriver(VK_DRIVER_ID_AMD_PROPRIETARY_KHR)); hints.preferFbResolve = m_features.amdShaderFragmentMask - && (m_adapter->matchesDriver(VK_DRIVER_ID_AMD_OPEN_SOURCE_KHR, 0, 0) - || m_adapter->matchesDriver(VK_DRIVER_ID_AMD_PROPRIETARY_KHR, 0, 0)); + && (m_adapter->matchesDriver(VK_DRIVER_ID_AMD_OPEN_SOURCE_KHR) + || m_adapter->matchesDriver(VK_DRIVER_ID_AMD_PROPRIETARY_KHR)); return hints; } diff --git a/src/dxvk/dxvk_device_info.h b/src/dxvk/dxvk_device_info.h index c1da783a7..a629c01ea 100644 --- a/src/dxvk/dxvk_device_info.h +++ b/src/dxvk/dxvk_device_info.h @@ -2,6 +2,8 @@ #include "dxvk_include.h" +#include "../util/util_version.h" + namespace dxvk { /** @@ -13,6 +15,7 @@ namespace dxvk { * so before using them, check whether they are supported. */ struct DxvkDeviceInfo { + Version driverVersion; VkPhysicalDeviceProperties2 core; VkPhysicalDeviceVulkan11Properties vk11; VkPhysicalDeviceVulkan12Properties vk12; diff --git a/src/dxvk/hud/dxvk_hud_item.cpp b/src/dxvk/hud/dxvk_hud_item.cpp index cbabd658b..699262ea7 100644 --- a/src/dxvk/hud/dxvk_hud_item.cpp +++ b/src/dxvk/hud/dxvk_hud_item.cpp @@ -129,12 +129,8 @@ namespace dxvk::hud { std::string driverInfo = props.vk12.driverInfo; - if (driverInfo.empty()) { - driverInfo = str::format( - VK_VERSION_MAJOR(props.core.properties.driverVersion), ".", - VK_VERSION_MINOR(props.core.properties.driverVersion), ".", - VK_VERSION_PATCH(props.core.properties.driverVersion)); - } + if (driverInfo.empty()) + driverInfo = props.driverVersion.toString(); m_deviceName = props.core.properties.deviceName; m_driverName = str::format("Driver: ", props.vk12.driverName); From dfc3776b2481dd8d0ee72edd7b519e5e65691878 Mon Sep 17 00:00:00 2001 From: Robin Kertels Date: Mon, 20 May 2024 17:24:02 +0200 Subject: [PATCH 161/246] [d3d9] FF: Fix a bunch of wine tests with FF texture coordinates --- src/d3d9/d3d9_fixed_function.cpp | 44 +++++++++++++++++++------------- 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/src/d3d9/d3d9_fixed_function.cpp b/src/d3d9/d3d9_fixed_function.cpp index f9a2a1ce5..2af5509e3 100644 --- a/src/d3d9/d3d9_fixed_function.cpp +++ b/src/d3d9/d3d9_fixed_function.cpp @@ -1106,6 +1106,17 @@ namespace dxvk { const uint32_t wIndex = 3; uint32_t flags = (m_vsKey.Data.Contents.TransformFlags >> (i * 3)) & 0b111; + + if (flags == D3DTTFF_COUNT1) { + // D3DTTFF_COUNT1 behaves like D3DTTFF_DISABLE on NV and like D3DTTFF_COUNT2 on AMD. + // The Nvidia behavior is easier to implement. + flags = D3DTTFF_DISABLE; + } + + // Passing 0xffffffff results in it getting clamped to the dimensions of the texture coords and getting treated as PROJECTED + // but D3D9 does not apply the transformation matrix. + bool applyTransform = flags >= D3DTTFF_COUNT1 && flags <= D3DTTFF_COUNT4; + uint32_t count; switch (inputFlags) { default: @@ -1116,11 +1127,14 @@ namespace dxvk { count = flags; if (texcoordCount) { // Clamp by the number of elements in the texcoord input. - if (!count || count > texcoordCount) + if (!count || count > texcoordCount) { count = texcoordCount; - } - else + } + } else { + count = 0; flags = D3DTTFF_DISABLE; + applyTransform = false; + } break; case (DXVK_TSS_TCI_CAMERASPACENORMAL >> TCIOffset): @@ -1136,7 +1150,7 @@ namespace dxvk { case (DXVK_TSS_TCI_CAMERASPACEREFLECTIONVECTOR >> TCIOffset): { uint32_t vtx3 = m_module.opVectorShuffle(m_vec3Type, vtx, vtx, 3, indices.data()); vtx3 = m_module.opNormalize(m_vec3Type, vtx3); - + uint32_t reflection = m_module.opReflect(m_vec3Type, vtx3, normal); std::array transformIndices; @@ -1174,9 +1188,8 @@ namespace dxvk { } } - uint32_t type = flags; - if (type != D3DTTFF_DISABLE) { - if (!m_vsKey.Data.Contents.HasPositionT) { + if (applyTransform || (count != 4 && count != 0)) { + if (applyTransform && !m_vsKey.Data.Contents.HasPositionT) { for (uint32_t j = count; j < 4; j++) { // If we're outside the component count of the vertex decl for this texcoord then we pad with zeroes. // Otherwise, pad with ones. @@ -1193,8 +1206,8 @@ namespace dxvk { } // Pad the unused section of it with the value for projection. - uint32_t lastIdx = count - 1; - uint32_t projValue = m_module.opCompositeExtract(m_floatType, transformed, 1, &lastIdx); + uint32_t projIdx = std::max(2u, count - 1); + uint32_t projValue = m_module.opCompositeExtract(m_floatType, transformed, 1, &projIdx); for (uint32_t j = count; j < 4; j++) transformed = m_module.opCompositeInsert(m_vec4Type, projValue, transformed, 1, &j); @@ -1790,22 +1803,17 @@ namespace dxvk { texcoord = m_module.opVectorShuffle(texcoord_t, texcoord, texcoord, texcoordCnt, indices.data()); - uint32_t projIdx = m_fsKey.Stages[i].Contents.ProjectedCount; - if (projIdx == 0 || projIdx > texcoordCnt) { - projIdx = 4; // Always use w if ProjectedCount is 0. - } - --projIdx; - + bool shouldProject = m_fsKey.Stages[i].Contents.Projected; uint32_t projValue = 0; - if (m_fsKey.Stages[i].Contents.Projected) { + if (shouldProject) { + // Always use w, the vertex shader puts the correct value there. + const uint32_t projIdx = 3; projValue = m_module.opCompositeExtract(m_floatType, m_ps.in.TEXCOORD[i], 1, &projIdx); uint32_t insertIdx = texcoordCnt - 1; texcoord = m_module.opCompositeInsert(texcoord_t, projValue, texcoord, 1, &insertIdx); } - bool shouldProject = m_fsKey.Stages[i].Contents.Projected; - if (i != 0 && ( m_fsKey.Stages[i - 1].Contents.ColorOp == D3DTOP_BUMPENVMAP || m_fsKey.Stages[i - 1].Contents.ColorOp == D3DTOP_BUMPENVMAPLUMINANCE)) { From 65dd3c7df31b36afcdb40813c20c915ec2a10d4e Mon Sep 17 00:00:00 2001 From: Robin Kertels Date: Mon, 20 May 2024 18:46:45 +0200 Subject: [PATCH 162/246] [d3d9] Always enable STORAGE_BUFFER usage Fixes a validation error. Drivers don't care about buffer usage bits anyway. --- src/d3d9/d3d9_constant_buffer.cpp | 19 +------------------ src/d3d9/d3d9_constant_buffer.h | 5 ----- 2 files changed, 1 insertion(+), 23 deletions(-) diff --git a/src/d3d9/d3d9_constant_buffer.cpp b/src/d3d9/d3d9_constant_buffer.cpp index 5bbd9a1d9..5f6bdf0f4 100644 --- a/src/d3d9/d3d9_constant_buffer.cpp +++ b/src/d3d9/d3d9_constant_buffer.cpp @@ -13,7 +13,7 @@ namespace dxvk { DxsoProgramType ShaderStage, DxsoConstantBuffers BufferType, VkDeviceSize Size) - : D3D9ConstantBuffer(pDevice, getBufferUsage(pDevice, ShaderStage, BufferType), GetShaderStage(ShaderStage), + : D3D9ConstantBuffer(pDevice, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, GetShaderStage(ShaderStage), computeResourceSlotId(ShaderStage, DxsoBindingType::ConstantBuffer, BufferType), Size) { @@ -135,21 +135,4 @@ namespace dxvk { device->properties().extRobustness2.robustUniformBufferAccessSizeAlignment); } - - VkBufferUsageFlags D3D9ConstantBuffer::getBufferUsage( - D3D9DeviceEx* pDevice, - DxsoProgramType ShaderStage, - DxsoConstantBuffers BufferType) { - VkBufferUsageFlags result = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; - - // We won't always need this, but the only buffer that - // this applies to is so large that it will not matter. - if (pDevice->CanSWVP() - && ShaderStage == DxsoProgramType::VertexShader - && BufferType == DxsoConstantBuffers::VSConstantBuffer) - result |= VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; - - return result; - } - } diff --git a/src/d3d9/d3d9_constant_buffer.h b/src/d3d9/d3d9_constant_buffer.h index 22abe2225..469e7a014 100644 --- a/src/d3d9/d3d9_constant_buffer.h +++ b/src/d3d9/d3d9_constant_buffer.h @@ -80,11 +80,6 @@ namespace dxvk { VkDeviceSize getAlignment(const Rc& device) const; - static VkBufferUsageFlags getBufferUsage( - D3D9DeviceEx* pDevice, - DxsoProgramType ShaderStage, - DxsoConstantBuffers BufferType); - }; } \ No newline at end of file From f33453afbbae6dc3d0821f808b3c3b3782388b94 Mon Sep 17 00:00:00 2001 From: Ethan Lee Date: Tue, 12 Dec 2023 11:58:12 -0500 Subject: [PATCH 163/246] [build] Add soversion to dxvk-native binaries --- meson.build | 6 ++++++ src/d3d10/meson.build | 1 + src/d3d11/meson.build | 1 + src/d3d9/meson.build | 1 + src/dxgi/meson.build | 1 + 5 files changed, 10 insertions(+) diff --git a/meson.build b/meson.build index 823ffbbd4..de12ee8ad 100644 --- a/meson.build +++ b/meson.build @@ -54,6 +54,8 @@ dep_displayinfo = dependency( ) if platform == 'windows' + dxvk_so_version = {} + compiler_args += [ '-DNOMINMAX', '-D_WIN32_WINNT=0xa00', @@ -118,6 +120,10 @@ if platform == 'windows' dxvk_name_prefix = '' compiler_args += ['-DDXVK_WSI_WIN32'] else + dxvk_abi_version = '0' + dxvk_version = meson.project_version().strip('v').split('.') + dxvk_so_version = {'version': dxvk_abi_version + '.' + dxvk_version[0] + dxvk_version[1] + dxvk_version[2]} + wrc = find_program('touch') wrc_generator = generator(wrc, output : [ '@BASENAME@_ignored.h' ], arguments : [ '@OUTPUT@' ] ) diff --git a/src/d3d10/meson.build b/src/d3d10/meson.build index 51db067c0..3d130b326 100644 --- a/src/d3d10/meson.build +++ b/src/d3d10/meson.build @@ -23,6 +23,7 @@ d3d10_core_dll = shared_library('d3d10core'+dll_ext, d3d10_core_src, d3d10_core_ vs_module_defs : 'd3d10core'+def_spec_ext, link_args : d3d10_core_ld_args, link_depends : [ d3d10_core_link_depends ], + kwargs : dxvk_so_version, ) d3d10_core_dep = declare_dependency( diff --git a/src/d3d11/meson.build b/src/d3d11/meson.build index 9b51e6ead..87b8b19f9 100644 --- a/src/d3d11/meson.build +++ b/src/d3d11/meson.build @@ -86,6 +86,7 @@ d3d11_dll = shared_library('d3d11'+dll_ext, dxgi_common_src + d3d11_src + d3d10_ vs_module_defs : 'd3d11'+def_spec_ext, link_args : d3d11_ld_args, link_depends : [ d3d11_link_depends ], + kwargs : dxvk_so_version, ) d3d11_dep = declare_dependency( diff --git a/src/d3d9/meson.build b/src/d3d9/meson.build index dd6b23164..a4a5ee8a5 100644 --- a/src/d3d9/meson.build +++ b/src/d3d9/meson.build @@ -65,6 +65,7 @@ d3d9_dll = shared_library('d3d9'+dll_ext, d3d9_src, glsl_generator.process(d3d9_ vs_module_defs : 'd3d9'+def_spec_ext, link_args : d3d9_ld_args, link_depends : [ d3d9_link_depends ], + kwargs : dxvk_so_version, ) d3d9_dep = declare_dependency( diff --git a/src/dxgi/meson.build b/src/dxgi/meson.build index a6e83b54e..fda900208 100644 --- a/src/dxgi/meson.build +++ b/src/dxgi/meson.build @@ -29,6 +29,7 @@ dxgi_dll = shared_library('dxgi'+dll_ext, dxgi_src, dxgi_res, vs_module_defs : 'dxgi'+def_spec_ext, link_args : dxgi_ld_args, link_depends : [ dxgi_link_depends ], + kwargs : dxvk_so_version, ) dxgi_dep = declare_dependency( From e991bfa6047a636cd8f2c6c0f994dc63f12df24b Mon Sep 17 00:00:00 2001 From: Ethan Lee Date: Tue, 12 Dec 2023 17:41:54 -0500 Subject: [PATCH 164/246] [ci] Use a tarball for the steamrt-sniper artifact. Zips can't preserve the symlink, so make the .tar.gz package with package_native.sh and zip that up instead. --- .github/workflows/artifacts.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/artifacts.yml b/.github/workflows/artifacts.yml index 9fe6485ad..669a38fac 100644 --- a/.github/workflows/artifacts.yml +++ b/.github/workflows/artifacts.yml @@ -54,7 +54,7 @@ jobs: shell: bash run: | export VERSION_NAME="${GITHUB_REF##*/}-${GITHUB_SHA##*/}" - ./package-native.sh ${VERSION_NAME} build --no-package + ./package-native.sh ${VERSION_NAME} build echo "VERSION_NAME=${VERSION_NAME}" >> $GITHUB_ENV - name: Upload artifacts @@ -62,5 +62,5 @@ jobs: uses: actions/upload-artifact@v3 with: name: dxvk-${{ env.VERSION_NAME }} - path: build/dxvk-native-${{ env.VERSION_NAME }} + path: build/dxvk-native-${{ env.VERSION_NAME }}.tar.gz if-no-files-found: error From 83436a97f2eca30b3d5520fdc2667797ce236c09 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Fri, 31 Mar 2023 15:01:43 +0100 Subject: [PATCH 165/246] [meson] Set the stem of library names instead of the name_prefix This is necessary for compatibility with Meson's pkg module, which generates pkg-config metadata containing "-lNAME" where NAME is the first argument to shared_library(). Changing the name_prefix parameter would break that. Conversely, including .dll or .so in the first parameter would also break that, so remove the `+dll_ext` part (in practice this is not a functional change, because `dll_ext` is always set to an empty string). Signed-off-by: Simon McVittie --- meson.build | 5 ++--- src/d3d10/meson.build | 3 +-- src/d3d11/meson.build | 3 +-- src/d3d9/meson.build | 3 +-- src/dxgi/meson.build | 3 +-- 5 files changed, 6 insertions(+), 11 deletions(-) diff --git a/meson.build b/meson.build index de12ee8ad..7764fc7ef 100644 --- a/meson.build +++ b/meson.build @@ -54,7 +54,7 @@ dep_displayinfo = dependency( ) if platform == 'windows' - dxvk_so_version = {} + dxvk_so_version = {'name_prefix': ''} compiler_args += [ '-DNOMINMAX', @@ -145,7 +145,7 @@ else error('SDL2 or GLFW are required to build dxvk-native') endif - dxvk_name_prefix = 'libdxvk_' + dxvk_name_prefix = 'dxvk_' link_args += [ '-static-libgcc', @@ -161,7 +161,6 @@ add_project_link_arguments(cpp.get_supported_link_arguments(link_args), language add_project_link_arguments(cc.get_supported_link_arguments(link_args), language: 'c') exe_ext = '' -dll_ext = '' def_spec_ext = '.def' glsl_compiler = find_program('glslang', 'glslangValidator') diff --git a/src/d3d10/meson.build b/src/d3d10/meson.build index 3d130b326..80038a961 100644 --- a/src/d3d10/meson.build +++ b/src/d3d10/meson.build @@ -15,8 +15,7 @@ else d3d10_d3d11_dep = d3d11_dep endif -d3d10_core_dll = shared_library('d3d10core'+dll_ext, d3d10_core_src, d3d10_core_res, - name_prefix : dxvk_name_prefix, +d3d10_core_dll = shared_library(dxvk_name_prefix+'d3d10core', d3d10_core_src, d3d10_core_res, dependencies : [ d3d10_d3d11_dep ], include_directories : dxvk_include_path, install : true, diff --git a/src/d3d11/meson.build b/src/d3d11/meson.build index 87b8b19f9..195f2b77d 100644 --- a/src/d3d11/meson.build +++ b/src/d3d11/meson.build @@ -77,9 +77,8 @@ else d3d11_dxgi_dep = dxgi_dep endif -d3d11_dll = shared_library('d3d11'+dll_ext, dxgi_common_src + d3d11_src + d3d10_src, +d3d11_dll = shared_library(dxvk_name_prefix+'d3d11', dxgi_common_src + d3d11_src + d3d10_src, glsl_generator.process(d3d11_shaders), d3d11_res, - name_prefix : dxvk_name_prefix, dependencies : [ d3d11_dxgi_dep, dxbc_dep, dxvk_dep ], include_directories : dxvk_include_path, install : true, diff --git a/src/d3d9/meson.build b/src/d3d9/meson.build index a4a5ee8a5..34ef3fd84 100644 --- a/src/d3d9/meson.build +++ b/src/d3d9/meson.build @@ -57,8 +57,7 @@ if platform != 'windows' d3d9_link_depends += files('d3d9.sym') endif -d3d9_dll = shared_library('d3d9'+dll_ext, d3d9_src, glsl_generator.process(d3d9_shaders), d3d9_res, - name_prefix : dxvk_name_prefix, +d3d9_dll = shared_library(dxvk_name_prefix+'d3d9', d3d9_src, glsl_generator.process(d3d9_shaders), d3d9_res, dependencies : [ dxso_dep, dxvk_dep ], include_directories : dxvk_include_path, install : true, diff --git a/src/dxgi/meson.build b/src/dxgi/meson.build index fda900208..f34c73182 100644 --- a/src/dxgi/meson.build +++ b/src/dxgi/meson.build @@ -21,8 +21,7 @@ if platform != 'windows' dxgi_link_depends += files('dxgi.sym') endif -dxgi_dll = shared_library('dxgi'+dll_ext, dxgi_src, dxgi_res, - name_prefix : dxvk_name_prefix, +dxgi_dll = shared_library(dxvk_name_prefix+'dxgi', dxgi_src, dxgi_res, dependencies : [ dxvk_dep ], include_directories : dxvk_include_path, install : true, From f9b40462235b9b4c3fbf87513f84da23c40d48b5 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Fri, 31 Mar 2023 15:36:07 +0100 Subject: [PATCH 166/246] [build] Install headers for native builds When building a game that has been ported to Linux using DXVK Native, these headers are necessary to provide the Direct3D and DXVK APIs. Signed-off-by: Simon McVittie --- include/native/meson.build | 19 +++++++++++++++++++ meson.build | 4 ++++ 2 files changed, 23 insertions(+) create mode 100644 include/native/meson.build diff --git a/include/native/meson.build b/include/native/meson.build new file mode 100644 index 000000000..14e191a5f --- /dev/null +++ b/include/native/meson.build @@ -0,0 +1,19 @@ +install_subdir( + 'directx', + install_dir: get_option('includedir') / 'dxvk', + strip_directory: true, + exclude_files: '.git' +) + +install_subdir( + 'windows', + install_dir: get_option('includedir') / 'dxvk', + strip_directory: true, +) + +install_headers( + 'wsi/native_wsi.h', + 'wsi/native_sdl2.h', + 'wsi/native_glfw.h', + subdir: 'dxvk/wsi', +) diff --git a/meson.build b/meson.build index 7764fc7ef..d6e26d90b 100644 --- a/meson.build +++ b/meson.build @@ -185,4 +185,8 @@ dxvk_version = vcs_tag( output: 'version.h', ) +if platform != 'windows' + subdir('include/native') +endif + subdir('src') From 2ff2c826a521e993bdab0a805e830c77d9346349 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Fri, 31 Mar 2023 15:23:03 +0100 Subject: [PATCH 167/246] [build] Generate pkg-config metadata to link to DXVK libraries This allows dependent projects to query the version and location of DXVK via the pkg-config interface. The include directories aren't yet set, because the headers aren't installed; that will follow in a subsequent commit. The naming of these pkg-config files is based on proposed Fedora packages for DXVK 2.0, and is not compatible with older Fedora packages for DXVK 1.x (which used the naming convention dxvk-native-d3d9 and so on). Packagers can create symlinks such as dxvk-native-d3d9.pc -> dxvk-d3d9.pc if they want to retain compatibility with older names. Signed-off-by: Simon McVittie --- meson.build | 2 ++ src/d3d10/meson.build | 7 +++++++ src/d3d11/meson.build | 7 +++++++ src/d3d9/meson.build | 7 +++++++ src/dxgi/meson.build | 7 +++++++ 5 files changed, 30 insertions(+) diff --git a/meson.build b/meson.build index d6e26d90b..cfa841a6a 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,6 @@ project('dxvk', ['c', 'cpp'], version : 'v2.3.1', meson_version : '>= 0.58', default_options : [ 'cpp_std=c++17', 'warning_level=2' ]) +pkg = import('pkgconfig') cpu_family = target_machine.cpu_family() platform = target_machine.system() fs = import('fs') @@ -146,6 +147,7 @@ else endif dxvk_name_prefix = 'dxvk_' + dxvk_pkg_prefix = 'dxvk-' link_args += [ '-static-libgcc', diff --git a/src/d3d10/meson.build b/src/d3d10/meson.build index 80038a961..6a690c435 100644 --- a/src/d3d10/meson.build +++ b/src/d3d10/meson.build @@ -28,3 +28,10 @@ d3d10_core_dll = shared_library(dxvk_name_prefix+'d3d10core', d3d10_core_src, d3 d3d10_core_dep = declare_dependency( link_with : [ d3d10_core_dll ], ) + +if platform != 'windows' + pkg.generate(d3d10_core_dll, + filebase: dxvk_pkg_prefix + 'd3d10core', + subdirs: 'dxvk', + ) +endif diff --git a/src/d3d11/meson.build b/src/d3d11/meson.build index 195f2b77d..20bec0820 100644 --- a/src/d3d11/meson.build +++ b/src/d3d11/meson.build @@ -92,3 +92,10 @@ d3d11_dep = declare_dependency( link_with : [ d3d11_dll ], include_directories : [ dxvk_include_path ], ) + +if platform != 'windows' + pkg.generate(d3d11_dll, + filebase: dxvk_pkg_prefix + 'd3d11', + subdirs: 'dxvk', + ) +endif diff --git a/src/d3d9/meson.build b/src/d3d9/meson.build index 34ef3fd84..bc3eac428 100644 --- a/src/d3d9/meson.build +++ b/src/d3d9/meson.build @@ -71,3 +71,10 @@ d3d9_dep = declare_dependency( link_with : [ d3d9_dll ], include_directories : [ dxvk_include_path ], ) + +if platform != 'windows' + pkg.generate(d3d9_dll, + filebase: dxvk_pkg_prefix + 'd3d9', + subdirs: 'dxvk', + ) +endif diff --git a/src/dxgi/meson.build b/src/dxgi/meson.build index f34c73182..7c3f43208 100644 --- a/src/dxgi/meson.build +++ b/src/dxgi/meson.build @@ -35,3 +35,10 @@ dxgi_dep = declare_dependency( link_with : [ dxgi_dll ], include_directories : [ dxvk_include_path ], ) + +if platform != 'windows' + pkg.generate(dxgi_dll, + filebase: dxvk_pkg_prefix + 'dxgi', + subdirs: 'dxvk', + ) +endif From 7bc77b597e058a44d322ccb35202240c97a12a2b Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Wed, 22 May 2024 13:16:05 +0200 Subject: [PATCH 168/246] [dxvk] Enable VK_NV_descriptor_pool_overallocation if available --- src/dxvk/dxvk_adapter.cpp | 21 +++++++++++++++++++++ src/dxvk/dxvk_device_info.h | 1 + src/dxvk/dxvk_extensions.h | 1 + 3 files changed, 23 insertions(+) diff --git a/src/dxvk/dxvk_adapter.cpp b/src/dxvk/dxvk_adapter.cpp index 4866465df..debbe0114 100644 --- a/src/dxvk/dxvk_adapter.cpp +++ b/src/dxvk/dxvk_adapter.cpp @@ -420,6 +420,10 @@ namespace dxvk { enabledFeatures.khrPresentWait.presentWait = VK_FALSE; } + // Enable descriptor pool overallocation if supported + enabledFeatures.nvDescriptorPoolOverallocation.descriptorPoolOverallocation = + m_deviceFeatures.nvDescriptorPoolOverallocation.descriptorPoolOverallocation; + // Enable raw access chains for shader backends enabledFeatures.nvRawAccessChains.shaderRawAccessChains = m_deviceFeatures.nvRawAccessChains.shaderRawAccessChains; @@ -612,6 +616,10 @@ namespace dxvk { enabledFeatures.khrPresentWait = *reinterpret_cast(f); break; + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_POOL_OVERALLOCATION_FEATURES_NV: + enabledFeatures.nvDescriptorPoolOverallocation = *reinterpret_cast(f); + break; + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAW_ACCESS_CHAINS_FEATURES_NV: enabledFeatures.nvRawAccessChains = *reinterpret_cast(f); break; @@ -924,6 +932,11 @@ namespace dxvk { m_deviceFeatures.khrPresentWait.pNext = std::exchange(m_deviceFeatures.core.pNext, &m_deviceFeatures.khrPresentWait); } + if (m_deviceExtensions.supports(VK_NV_DESCRIPTOR_POOL_OVERALLOCATION_EXTENSION_NAME)) { + m_deviceFeatures.nvDescriptorPoolOverallocation.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_POOL_OVERALLOCATION_FEATURES_NV; + m_deviceFeatures.nvDescriptorPoolOverallocation.pNext = std::exchange(m_deviceFeatures.core.pNext, &m_deviceFeatures.nvDescriptorPoolOverallocation); + } + if (m_deviceExtensions.supports(VK_NV_RAW_ACCESS_CHAINS_EXTENSION_NAME)) { m_deviceFeatures.nvRawAccessChains.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAW_ACCESS_CHAINS_FEATURES_NV; m_deviceFeatures.nvRawAccessChains.pNext = std::exchange(m_deviceFeatures.core.pNext, &m_deviceFeatures.nvRawAccessChains); @@ -996,6 +1009,7 @@ namespace dxvk { &devExtensions.khrPresentWait, &devExtensions.khrSwapchain, &devExtensions.khrWin32KeyedMutex, + &devExtensions.nvDescriptorPoolOverallocation, &devExtensions.nvRawAccessChains, &devExtensions.nvxBinaryImport, &devExtensions.nvxImageViewHandle, @@ -1136,6 +1150,11 @@ namespace dxvk { enabledFeatures.khrPresentWait.pNext = std::exchange(enabledFeatures.core.pNext, &enabledFeatures.khrPresentWait); } + if (devExtensions.nvDescriptorPoolOverallocation) { + enabledFeatures.nvDescriptorPoolOverallocation.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_POOL_OVERALLOCATION_FEATURES_NV; + enabledFeatures.nvDescriptorPoolOverallocation.pNext = std::exchange(enabledFeatures.core.pNext, &enabledFeatures.nvDescriptorPoolOverallocation); + } + if (devExtensions.nvRawAccessChains) { enabledFeatures.nvRawAccessChains.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAW_ACCESS_CHAINS_FEATURES_NV; enabledFeatures.nvRawAccessChains.pNext = std::exchange(enabledFeatures.core.pNext, &enabledFeatures.nvRawAccessChains); @@ -1287,6 +1306,8 @@ namespace dxvk { "\n presentId : ", features.khrPresentId.presentId ? "1" : "0", "\n", VK_KHR_PRESENT_WAIT_EXTENSION_NAME, "\n presentWait : ", features.khrPresentWait.presentWait ? "1" : "0", + "\n", VK_NV_DESCRIPTOR_POOL_OVERALLOCATION_EXTENSION_NAME, + "\n descriptorPoolOverallocation : ", features.nvDescriptorPoolOverallocation.descriptorPoolOverallocation ? "1" : "0", "\n", VK_NV_RAW_ACCESS_CHAINS_EXTENSION_NAME, "\n shaderRawAccessChains : ", features.nvRawAccessChains.shaderRawAccessChains ? "1" : "0", "\n", VK_NVX_BINARY_IMPORT_EXTENSION_NAME, diff --git a/src/dxvk/dxvk_device_info.h b/src/dxvk/dxvk_device_info.h index a629c01ea..0c4c4e4cb 100644 --- a/src/dxvk/dxvk_device_info.h +++ b/src/dxvk/dxvk_device_info.h @@ -71,6 +71,7 @@ namespace dxvk { VkPhysicalDeviceMaintenance5FeaturesKHR khrMaintenance5; VkPhysicalDevicePresentIdFeaturesKHR khrPresentId; VkPhysicalDevicePresentWaitFeaturesKHR khrPresentWait; + VkPhysicalDeviceDescriptorPoolOverallocationFeaturesNV nvDescriptorPoolOverallocation; VkPhysicalDeviceRawAccessChainsFeaturesNV nvRawAccessChains; VkBool32 nvxBinaryImport; VkBool32 nvxImageViewHandle; diff --git a/src/dxvk/dxvk_extensions.h b/src/dxvk/dxvk_extensions.h index 63c922cf5..729f09c19 100644 --- a/src/dxvk/dxvk_extensions.h +++ b/src/dxvk/dxvk_extensions.h @@ -325,6 +325,7 @@ namespace dxvk { DxvkExt khrPresentWait = { VK_KHR_PRESENT_WAIT_EXTENSION_NAME, DxvkExtMode::Optional }; DxvkExt khrSwapchain = { VK_KHR_SWAPCHAIN_EXTENSION_NAME, DxvkExtMode::Required }; DxvkExt khrWin32KeyedMutex = { VK_KHR_WIN32_KEYED_MUTEX_EXTENSION_NAME, DxvkExtMode::Optional }; + DxvkExt nvDescriptorPoolOverallocation = { VK_NV_DESCRIPTOR_POOL_OVERALLOCATION_EXTENSION_NAME, DxvkExtMode::Optional }; DxvkExt nvRawAccessChains = { VK_NV_RAW_ACCESS_CHAINS_EXTENSION_NAME, DxvkExtMode::Optional }; DxvkExt nvxBinaryImport = { VK_NVX_BINARY_IMPORT_EXTENSION_NAME, DxvkExtMode::Disabled }; DxvkExt nvxImageViewHandle = { VK_NVX_IMAGE_VIEW_HANDLE_EXTENSION_NAME, DxvkExtMode::Disabled }; From 61bd62c327d40cb97447242e6baeb4a9a05373ec Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Wed, 22 May 2024 13:17:53 +0200 Subject: [PATCH 169/246] [dxvk] Allow descriptor pool overallocation if supported --- src/dxvk/dxvk_descriptor.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/dxvk/dxvk_descriptor.cpp b/src/dxvk/dxvk_descriptor.cpp index 7c2a72f5b..3e36aed9b 100644 --- a/src/dxvk/dxvk_descriptor.cpp +++ b/src/dxvk/dxvk_descriptor.cpp @@ -306,7 +306,12 @@ namespace dxvk { info.maxSets = m_maxSets; info.poolSizeCount = pools.size(); info.pPoolSizes = pools.data(); - + + if (m_device->features().nvDescriptorPoolOverallocation.descriptorPoolOverallocation) { + info.flags |= VK_DESCRIPTOR_POOL_CREATE_ALLOW_OVERALLOCATION_POOLS_BIT_NV + | VK_DESCRIPTOR_POOL_CREATE_ALLOW_OVERALLOCATION_SETS_BIT_NV; + } + VkDescriptorPool pool = VK_NULL_HANDLE; if (vk->vkCreateDescriptorPool(vk->device(), &info, nullptr, &pool) != VK_SUCCESS) From 58d8ea2d31b372bad6a6986889fc16bb084cc6bb Mon Sep 17 00:00:00 2001 From: Danylo Piliaiev Date: Thu, 16 May 2024 13:50:37 +0200 Subject: [PATCH 170/246] [d3d11,d3d9,util] Add a config option for reproducible VK output It ensures that for the same D3D commands the output VK commands don't change between runs. Useful for comparative benchmarking, can negatively affect performance. Signed-off-by: Danylo Piliaiev --- dxvk.conf | 14 ++++++++++++++ src/d3d11/d3d11_context_imm.cpp | 1 + src/d3d11/d3d11_options.cpp | 1 + src/d3d11/d3d11_options.h | 5 +++++ src/d3d9/d3d9_device.cpp | 1 + src/d3d9/d3d9_options.cpp | 1 + src/d3d9/d3d9_options.h | 5 +++++ src/util/util_flush.cpp | 9 +++++++++ src/util/util_flush.h | 4 ++++ 9 files changed, 41 insertions(+) diff --git a/dxvk.conf b/dxvk.conf index 46edbb9c0..3543876a5 100644 --- a/dxvk.conf +++ b/dxvk.conf @@ -312,6 +312,20 @@ # d3d11.exposeDriverCommandLists = True +# Reproducible Command Stream +# +# Ensure that for the same D3D commands the output VK commands +# don't change between runs. Useful for comparative benchmarking, +# can negatively affect performance and can break some games +# that don't use queries correctly. +# +# Supported values: +# - True/False + +# d3d11.reproducibleCommandStream = False +# d3d9.reproducibleCommandStream = False + + # Sets number of pipeline compiler threads. # # If the graphics pipeline library feature is enabled, the given diff --git a/src/d3d11/d3d11_context_imm.cpp b/src/d3d11/d3d11_context_imm.cpp index 466e9a967..aa379d41d 100644 --- a/src/d3d11/d3d11_context_imm.cpp +++ b/src/d3d11/d3d11_context_imm.cpp @@ -19,6 +19,7 @@ namespace dxvk { m_csThread(Device, Device->createContext(DxvkContextType::Primary)), m_maxImplicitDiscardSize(pParent->GetOptions()->maxImplicitDiscardSize), m_submissionFence(new sync::CallbackFence()), + m_flushTracker(pParent->GetOptions()->reproducibleCommandStream), m_multithread(this, false, pParent->GetOptions()->enableContextLock), m_videoContext(this, Device) { EmitCs([ diff --git a/src/d3d11/d3d11_options.cpp b/src/d3d11/d3d11_options.cpp index ddbfe0a9a..c6b41ecb7 100644 --- a/src/d3d11/d3d11_options.cpp +++ b/src/d3d11/d3d11_options.cpp @@ -33,6 +33,7 @@ namespace dxvk { this->maxFrameRate = config.getOption("dxgi.maxFrameRate", 0); this->exposeDriverCommandLists = config.getOption("d3d11.exposeDriverCommandLists", true); this->longMad = config.getOption("d3d11.longMad", false); + this->reproducibleCommandStream = config.getOption("d3d11.reproducibleCommandStream", false); // Clamp LOD bias so that people don't abuse this in unintended ways this->samplerLodBias = dxvk::fclamp(this->samplerLodBias, -2.0f, 1.0f); diff --git a/src/d3d11/d3d11_options.h b/src/d3d11/d3d11_options.h index 5cd548cd5..a5e21e17e 100644 --- a/src/d3d11/d3d11_options.h +++ b/src/d3d11/d3d11_options.h @@ -123,6 +123,11 @@ namespace dxvk { /// Should we make our Mads a FFma or do it the long way with an FMul and an FAdd? bool longMad; + + /// Ensure that for the same D3D commands the output VK commands + /// don't change between runs. Useful for comparative benchmarking, + /// can negatively affect performance. + bool reproducibleCommandStream; }; } \ No newline at end of file diff --git a/src/d3d9/d3d9_device.cpp b/src/d3d9/d3d9_device.cpp index 87d39eeb7..2a4a7b32c 100644 --- a/src/d3d9/d3d9_device.cpp +++ b/src/d3d9/d3d9_device.cpp @@ -56,6 +56,7 @@ namespace dxvk { , m_csThread ( dxvkDevice, dxvkDevice->createContext(DxvkContextType::Primary) ) , m_csChunk ( AllocCsChunk() ) , m_submissionFence (new sync::Fence()) + , m_flushTracker (m_d3d9Options.reproducibleCommandStream) , m_d3d9Interop ( this ) , m_d3d9On12 ( this ) , m_d3d8Bridge ( this ) { diff --git a/src/d3d9/d3d9_options.cpp b/src/d3d9/d3d9_options.cpp index 1845daabd..0884819f1 100644 --- a/src/d3d9/d3d9_options.cpp +++ b/src/d3d9/d3d9_options.cpp @@ -77,6 +77,7 @@ namespace dxvk { this->samplerLodBias = config.getOption ("d3d9.samplerLodBias", 0.0f); this->clampNegativeLodBias = config.getOption ("d3d9.clampNegativeLodBias", false); this->countLosableResources = config.getOption ("d3d9.countLosableResources", true); + this->reproducibleCommandStream = config.getOption ("d3d9.reproducibleCommandStream", false); // Clamp LOD bias so that people don't abuse this in unintended ways this->samplerLodBias = dxvk::fclamp(this->samplerLodBias, -2.0f, 1.0f); diff --git a/src/d3d9/d3d9_options.h b/src/d3d9/d3d9_options.h index 034c85d3a..824ba076c 100644 --- a/src/d3d9/d3d9_options.h +++ b/src/d3d9/d3d9_options.h @@ -155,6 +155,11 @@ namespace dxvk { /// Disable counting losable resources and rejecting calls to Reset() if any are still alive bool countLosableResources; + + /// Ensure that for the same D3D commands the output VK commands + /// don't change between runs. Useful for comparative benchmarking, + /// can negatively affect performance. + bool reproducibleCommandStream; }; } diff --git a/src/util/util_flush.cpp b/src/util/util_flush.cpp index a4d91bd43..c2b1ce6a6 100644 --- a/src/util/util_flush.cpp +++ b/src/util/util_flush.cpp @@ -2,6 +2,12 @@ namespace dxvk { + GpuFlushTracker::GpuFlushTracker( + bool ensureReproducibleHeuristic) + : m_ensureReproducibleHeuristic(ensureReproducibleHeuristic) { + + } + bool GpuFlushTracker::considerFlush( GpuFlushType flushType, uint64_t chunkId, @@ -17,6 +23,9 @@ namespace dxvk { if (!chunkCount) return false; + if (m_ensureReproducibleHeuristic && flushType != GpuFlushType::ExplicitFlush) + return false; + // Take any earlier missed flush with a stronger hint into account, so // that we still flush those as soon as possible. Ignore synchronization // commands since they will either perform a flush or not need it at all. diff --git a/src/util/util_flush.h b/src/util/util_flush.h index 138aa992b..c3e71ebcb 100644 --- a/src/util/util_flush.h +++ b/src/util/util_flush.h @@ -34,6 +34,8 @@ namespace dxvk { public: + GpuFlushTracker(bool ensureReproducibleHeuristic); + /** * \brief Checks whether a context flush should be performed * @@ -61,6 +63,8 @@ namespace dxvk { private: + bool m_ensureReproducibleHeuristic; + GpuFlushType m_lastMissedType = GpuFlushType::ImplicitWeakHint; uint64_t m_lastFlushChunkId = 0ull; From 07d007c6420e0994a4c96c5aaef6b5062fa56f19 Mon Sep 17 00:00:00 2001 From: WinterSnowfall Date: Wed, 22 May 2024 00:49:56 +0300 Subject: [PATCH 171/246] [d3d9] Use customVendorId to determine the options vendorId --- src/d3d9/d3d9_options.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/d3d9/d3d9_options.cpp b/src/d3d9/d3d9_options.cpp index 0884819f1..19300b1de 100644 --- a/src/d3d9/d3d9_options.cpp +++ b/src/d3d9/d3d9_options.cpp @@ -37,8 +37,8 @@ namespace dxvk { this->customDeviceId = parsePciId(config.getOption("d3d9.customDeviceId")); this->customDeviceDesc = config.getOption("d3d9.customDeviceDesc"); - const int32_t vendorId = this->customDeviceId != -1 - ? this->customDeviceId + const int32_t vendorId = this->customVendorId != -1 + ? this->customVendorId : (adapter != nullptr ? adapter->deviceProperties().vendorID : 0); this->maxFrameLatency = config.getOption ("d3d9.maxFrameLatency", 0); From a1ce690c5ce3fd6646f6c55f9bc6186babe0b991 Mon Sep 17 00:00:00 2001 From: WinterSnowfall Date: Wed, 22 May 2024 00:51:58 +0300 Subject: [PATCH 172/246] [d3d9] Determine DF format support in the options parser --- src/d3d9/d3d9_format.cpp | 6 +----- src/d3d9/d3d9_options.cpp | 6 +++--- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/d3d9/d3d9_format.cpp b/src/d3d9/d3d9_format.cpp index 31ce7290b..87431a874 100644 --- a/src/d3d9/d3d9_format.cpp +++ b/src/d3d9/d3d9_format.cpp @@ -438,11 +438,7 @@ namespace dxvk { D3D9VkFormatTable::D3D9VkFormatTable( const Rc& adapter, const D3D9Options& options) { - - const auto& props = adapter->deviceProperties(); - uint32_t vendorId = options.customVendorId == -1 ? props.vendorID : uint32_t(options.customVendorId); - - m_dfSupport = options.supportDFFormats && DxvkGpuVendor(vendorId) != DxvkGpuVendor::Nvidia; + m_dfSupport = options.supportDFFormats; m_x4r4g4b4Support = options.supportX4R4G4B4; m_d32supportFinal = options.supportD32; diff --git a/src/d3d9/d3d9_options.cpp b/src/d3d9/d3d9_options.cpp index 19300b1de..9671545ea 100644 --- a/src/d3d9/d3d9_options.cpp +++ b/src/d3d9/d3d9_options.cpp @@ -37,7 +37,7 @@ namespace dxvk { this->customDeviceId = parsePciId(config.getOption("d3d9.customDeviceId")); this->customDeviceDesc = config.getOption("d3d9.customDeviceDesc"); - const int32_t vendorId = this->customVendorId != -1 + const uint32_t vendorId = this->customVendorId != -1 ? this->customVendorId : (adapter != nullptr ? adapter->deviceProperties().vendorID : 0); @@ -53,14 +53,14 @@ namespace dxvk { this->deferSurfaceCreation = config.getOption ("d3d9.deferSurfaceCreation", false); this->samplerAnisotropy = config.getOption ("d3d9.samplerAnisotropy", -1); this->maxAvailableMemory = config.getOption ("d3d9.maxAvailableMemory", 4096); - this->supportDFFormats = config.getOption ("d3d9.supportDFFormats", true); + this->supportDFFormats = config.getOption ("d3d9.supportDFFormats", vendorId != uint32_t(DxvkGpuVendor::Nvidia)); this->supportX4R4G4B4 = config.getOption ("d3d9.supportX4R4G4B4", true); this->supportD32 = config.getOption ("d3d9.supportD32", true); this->useD32forD24 = config.getOption ("d3d9.useD32forD24", false); this->disableA8RT = config.getOption ("d3d9.disableA8RT", false); this->invariantPosition = config.getOption ("d3d9.invariantPosition", true); this->memoryTrackTest = config.getOption ("d3d9.memoryTrackTest", false); - this->supportVCache = config.getOption ("d3d9.supportVCache", vendorId == 0x10de); + this->supportVCache = config.getOption ("d3d9.supportVCache", vendorId == uint32_t(DxvkGpuVendor::Nvidia)); this->enableDialogMode = config.getOption ("d3d9.enableDialogMode", false); this->forceSamplerTypeSpecConstants = config.getOption ("d3d9.forceSamplerTypeSpecConstants", false); this->forceSwapchainMSAA = config.getOption ("d3d9.forceSwapchainMSAA", -1); From 889802887fa58cece6b95b9da80058e5781b7a21 Mon Sep 17 00:00:00 2001 From: Robin Kertels Date: Thu, 21 Mar 2024 21:11:10 +0100 Subject: [PATCH 173/246] [d3d9] Rework uploading dynamic sysmem buffers at draw time ... and handle mismatching vertex sizes and vertex strides. --- src/d3d9/d3d9_device.cpp | 143 ++++++++++++++++++++++++--------------- 1 file changed, 89 insertions(+), 54 deletions(-) diff --git a/src/d3d9/d3d9_device.cpp b/src/d3d9/d3d9_device.cpp index 2a4a7b32c..f91d1a43a 100644 --- a/src/d3d9/d3d9_device.cpp +++ b/src/d3d9/d3d9_device.cpp @@ -5159,7 +5159,7 @@ namespace dxvk { return D3D_OK; } - + void D3D9DeviceEx::UploadDynamicSysmemBuffers( UINT& FirstVertexIndex, @@ -5191,25 +5191,58 @@ namespace dxvk { // First we calculate the size of that UP buffer slice // and store all sizes and offsets into it. - uint32_t upBufferSize = 0; - std::array vboUPBufferOffsets = {}; - std::array vboUPBufferSizes = {}; - for (uint32_t i = 0; i < caps::MaxStreams && dynamicSysmemVBOs; i++) { - vboUPBufferOffsets[i] = upBufferSize; + struct VBOCopy { + uint32_t srcOffset; + uint32_t dstOffset; + uint32_t copyBufferLength; + uint32_t copyElementCount; + uint32_t copyElementSize; + uint32_t copyElementStride; + }; + uint32_t totalUpBufferSize = 0; + std::array vboCopies = {}; + for (uint32_t i = 0; i < caps::MaxStreams && dynamicSysmemVBOs; i++) { auto* vbo = GetCommonBuffer(m_state.vertexBuffers[i].vertexBuffer); if (likely(vbo == nullptr)) { - vboUPBufferSizes[i] = 0; continue; } - const uint32_t vertexStride = m_state.vertexDecl->GetSize(i); - uint32_t offset = (FirstVertexIndex + BaseVertexIndex) * vertexStride; - const uint32_t vertexBufferSize = vbo->Desc()->Size; - if (offset < vertexBufferSize) { - const uint32_t vertexDataSize = std::min(NumVertices * vertexStride, vertexBufferSize - offset); - vboUPBufferSizes[i] = vertexDataSize; - upBufferSize += vertexDataSize; + const uint32_t vertexSize = m_state.vertexDecl->GetSize(i); + const uint32_t vertexStride = m_state.vertexBuffers[i].stride; + const uint32_t srcStride = vertexStride; + const uint32_t dstStride = std::min(vertexStride, vertexSize); + + uint32_t elementCount = NumVertices; + if (m_state.streamFreq[i] & D3DSTREAMSOURCE_INSTANCEDATA) { + elementCount = GetInstanceCount(); } + const uint32_t vboOffset = m_state.vertexBuffers[i].offset; + const uint32_t vertexOffset = (FirstVertexIndex + BaseVertexIndex) * srcStride; + const uint32_t vertexBufferSize = vbo->Desc()->Size; + const uint32_t srcOffset = vboOffset + vertexOffset; + + if (unlikely(srcOffset > vertexBufferSize)) { + // All vertices are out of bounds + vboCopies[i].copyBufferLength = 0; + } else if (unlikely(srcOffset + elementCount * srcStride > vertexBufferSize)) { + // Some vertices are (partially) out of bounds + uint32_t boundVertexBufferRange = vertexBufferSize - vboOffset; + elementCount = boundVertexBufferRange / srcStride; + // Copy all complete vertices + vboCopies[i].copyBufferLength = elementCount * dstStride; + // Copy the remaining partial vertex + vboCopies[i].copyBufferLength += std::min(dstStride, boundVertexBufferRange % srcStride); + } else { + // No vertices are out of bounds + vboCopies[i].copyBufferLength = elementCount * dstStride; + } + + vboCopies[i].copyElementCount = elementCount; + vboCopies[i].copyElementStride = srcStride; + vboCopies[i].copyElementSize = dstStride; + vboCopies[i].srcOffset = srcOffset; + vboCopies[i].dstOffset = totalUpBufferSize; + totalUpBufferSize += vboCopies[i].copyBufferLength; } uint32_t iboUPBufferSize = 0; @@ -5222,13 +5255,13 @@ namespace dxvk { uint32_t indexBufferSize = ibo->Desc()->Size; if (offset < indexBufferSize) { iboUPBufferSize = std::min(NumIndices * indexStride, indexBufferSize - offset); - iboUPBufferOffset = upBufferSize; - upBufferSize += iboUPBufferSize; + iboUPBufferOffset = totalUpBufferSize; + totalUpBufferSize += iboUPBufferSize; } } } - if (unlikely(upBufferSize == 0)) { + if (unlikely(totalUpBufferSize == 0)) { *pDynamicVBOs = false; if (pDynamicIBO) *pDynamicIBO = false; @@ -5236,47 +5269,49 @@ namespace dxvk { return; } - auto upSlice = AllocUPBuffer(upBufferSize); + auto upSlice = AllocUPBuffer(totalUpBufferSize); // Now copy the actual data and bind it. - if (dynamicSysmemVBOs) { - for (uint32_t i = 0; i < caps::MaxStreams; i++) { - if (unlikely(vboUPBufferSizes[i] == 0)) { - EmitCs([ - cStream = i - ](DxvkContext* ctx) { - ctx->bindVertexBuffer(cStream, DxvkBufferSlice(), 0); - }); - m_flags.set(D3D9DeviceFlag::DirtyVertexBuffers); - continue; + for (uint32_t i = 0; i < caps::MaxStreams && dynamicSysmemVBOs; i++) { + const VBOCopy& copy = vboCopies[i]; + + if (likely(copy.copyBufferLength != 0)) { + const auto* vbo = GetCommonBuffer(m_state.vertexBuffers[i].vertexBuffer); + uint8_t* data = reinterpret_cast(upSlice.mapPtr) + copy.dstOffset; + const uint8_t* src = reinterpret_cast(vbo->GetMappedSlice().mapPtr) + copy.srcOffset; + + if (likely(copy.copyElementStride == copy.copyElementSize)) { + std::memcpy(data, src, copy.copyBufferLength); + } else { + for (uint32_t j = 0; j * copy.copyElementCount; j++) { + std::memcpy(data + j * copy.copyElementSize, src + j * copy.copyElementStride, copy.copyElementSize); + } + if (unlikely(copy.copyBufferLength > copy.copyElementCount * copy.copyElementSize)) { + // Partial vertex at the end + std::memcpy( + data + copy.copyElementCount * copy.copyElementSize, + src + copy.copyElementCount * copy.copyElementStride, + copy.copyBufferLength - copy.copyElementCount * copy.copyElementSize); + } } - - auto* vbo = GetCommonBuffer(m_state.vertexBuffers[i].vertexBuffer); - - const uint32_t vertexStride = m_state.vertexDecl->GetSize(i); - uint32_t offset = (BaseVertexIndex + FirstVertexIndex) * vertexStride + m_state.vertexBuffers[i].offset; - - uint8_t* data = reinterpret_cast(upSlice.mapPtr) + vboUPBufferOffsets[i]; - uint8_t* src = reinterpret_cast(vbo->GetMappedSlice().mapPtr) + offset; - std::memcpy(data, src, vboUPBufferSizes[i]); - - auto vboSlice = upSlice.slice.subSlice(vboUPBufferOffsets[i], vboUPBufferSizes[i]); - EmitCs([ - cStream = i, - cBufferSlice = std::move(vboSlice), - cStride = vertexStride - ](DxvkContext* ctx) mutable { - ctx->bindVertexBuffer(cStream, std::move(cBufferSlice), cStride); - }); - m_flags.set(D3D9DeviceFlag::DirtyVertexBuffers); } - // Change the draw call parameters to reflect the changed vertex buffers - if (NumIndices != 0) { - BaseVertexIndex = -FirstVertexIndex; - } else { - FirstVertexIndex = 0; - } + auto vboSlice = upSlice.slice.subSlice(copy.dstOffset, copy.copyBufferLength); + EmitCs([ + cStream = i, + cBufferSlice = std::move(vboSlice), + cStride = copy.copyElementSize + ](DxvkContext* ctx) mutable { + ctx->bindVertexBuffer(cStream, std::move(cBufferSlice), cStride); + }); + m_flags.set(D3D9DeviceFlag::DirtyVertexBuffers); + } + + // Change the draw call parameters to reflect the changed vertex buffers + if (NumIndices != 0) { + BaseVertexIndex = -FirstVertexIndex; + } else { + FirstVertexIndex = 0; } if (dynamicSysmemIBO) { @@ -5293,7 +5328,7 @@ namespace dxvk { uint8_t* data = reinterpret_cast(upSlice.mapPtr) + iboUPBufferOffset; uint8_t* src = reinterpret_cast(ibo->GetMappedSlice().mapPtr) + offset; std::memcpy(data, src, iboUPBufferSize); - + auto iboSlice = upSlice.slice.subSlice(iboUPBufferOffset, iboUPBufferSize); EmitCs([ cBufferSlice = std::move(iboSlice), From 60cfafe027492deab236edf80333ee3de9fa5b76 Mon Sep 17 00:00:00 2001 From: Robin Kertels Date: Sat, 25 May 2024 19:47:14 +0200 Subject: [PATCH 174/246] [d3d9] Fix strange type in dynamic vertex upload --- src/d3d9/d3d9_device.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/d3d9/d3d9_device.cpp b/src/d3d9/d3d9_device.cpp index f91d1a43a..e53c96a31 100644 --- a/src/d3d9/d3d9_device.cpp +++ b/src/d3d9/d3d9_device.cpp @@ -5283,7 +5283,7 @@ namespace dxvk { if (likely(copy.copyElementStride == copy.copyElementSize)) { std::memcpy(data, src, copy.copyBufferLength); } else { - for (uint32_t j = 0; j * copy.copyElementCount; j++) { + for (uint32_t j = 0; j < copy.copyElementCount; j++) { std::memcpy(data + j * copy.copyElementSize, src + j * copy.copyElementStride, copy.copyElementSize); } if (unlikely(copy.copyBufferLength > copy.copyElementCount * copy.copyElementSize)) { From 890ad3f47f183c7db1d6990284365b7175587889 Mon Sep 17 00:00:00 2001 From: Blisto91 Date: Sat, 25 May 2024 22:27:01 +0200 Subject: [PATCH 175/246] [CI] Update CI actions --- .github/workflows/artifacts.yml | 33 ++++++++++++++++++------ .github/workflows/test-build-windows.yml | 2 +- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/.github/workflows/artifacts.yml b/.github/workflows/artifacts.yml index 669a38fac..735b4fc23 100644 --- a/.github/workflows/artifacts.yml +++ b/.github/workflows/artifacts.yml @@ -9,13 +9,13 @@ jobs: steps: - name: Checkout code id: checkout-code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: submodules: recursive fetch-depth: 0 - name: Setup problem matcher - uses: Joshua-Ashton/gcc-problem-matcher@v2 + uses: Joshua-Ashton/gcc-problem-matcher@v3 - name: Build release id: build-release @@ -28,9 +28,9 @@ jobs: - name: Upload artifacts id: upload-artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: dxvk-${{ env.VERSION_NAME }} + name: dxvk-win-${{ env.VERSION_NAME }} path: build/dxvk-${{ env.VERSION_NAME }} if-no-files-found: error @@ -41,13 +41,13 @@ jobs: steps: - name: Checkout code id: checkout-code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: submodules: recursive fetch-depth: 0 - name: Setup problem matcher - uses: Joshua-Ashton/gcc-problem-matcher@v2 + uses: Joshua-Ashton/gcc-problem-matcher@v3 - name: Build release id: build-release @@ -59,8 +59,25 @@ jobs: - name: Upload artifacts id: upload-artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: dxvk-${{ env.VERSION_NAME }} + name: dxvk-native-${{ env.VERSION_NAME }} path: build/dxvk-native-${{ env.VERSION_NAME }}.tar.gz if-no-files-found: error + + merge-artifacts: + runs-on: ubuntu-20.04 + needs: [artifacts-mingw-w64, artifacts-steamrt-sniper] + steps: + - name: Get version + id: get-version + shell: bash + run: | + echo "VERSION_NAME=${GITHUB_REF##*/}-${GITHUB_SHA##*/}" >> $GITHUB_ENV + + - name: Merge Artifacts + uses: actions/upload-artifact/merge@v4 + with: + name: dxvk-${{ env.VERSION_NAME }} + pattern: dxvk* + delete-merged: true diff --git a/.github/workflows/test-build-windows.yml b/.github/workflows/test-build-windows.yml index 6d8489ad3..503bd4805 100644 --- a/.github/workflows/test-build-windows.yml +++ b/.github/workflows/test-build-windows.yml @@ -9,7 +9,7 @@ jobs: steps: - name: Checkout code id: checkout-code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: submodules: recursive From c98152683f824f13b0b5d62c121a0b5947b79c81 Mon Sep 17 00:00:00 2001 From: WinterSnowfall Date: Wed, 22 May 2024 10:32:07 +0300 Subject: [PATCH 176/246] [d3d9] Tweak VCache query results --- src/d3d9/d3d9_query.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/d3d9/d3d9_query.cpp b/src/d3d9/d3d9_query.cpp index 57e838443..1e1f8e89e 100644 --- a/src/d3d9/d3d9_query.cpp +++ b/src/d3d9/d3d9_query.cpp @@ -217,12 +217,10 @@ namespace dxvk { switch (m_queryType) { case D3DQUERYTYPE_VCACHE: - // Don't know what the hell any of this means. - // Nor do I care. This just makes games work. - m_dataCache.VCache.Pattern = MAKEFOURCC('H', 'C', 'A', 'C'); + m_dataCache.VCache.Pattern = MAKEFOURCC('C', 'A', 'C', 'H'); m_dataCache.VCache.OptMethod = 1; - m_dataCache.VCache.CacheSize = 24; - m_dataCache.VCache.MagicNumber = 20; + m_dataCache.VCache.CacheSize = 16; + m_dataCache.VCache.MagicNumber = 7; break; case D3DQUERYTYPE_OCCLUSION: From 7df8017e46692e972c710435261950c941b03acd Mon Sep 17 00:00:00 2001 From: Robin Kertels Date: Thu, 23 May 2024 00:18:01 +0200 Subject: [PATCH 177/246] [d3d9] FF: Apply transform flags count to generated texture coords ... and fix cases where projection doesn't get applied. --- src/d3d9/d3d9_fixed_function.cpp | 55 ++++++++++++++++++-------------- 1 file changed, 31 insertions(+), 24 deletions(-) diff --git a/src/d3d9/d3d9_fixed_function.cpp b/src/d3d9/d3d9_fixed_function.cpp index 2af5509e3..0aff819a4 100644 --- a/src/d3d9/d3d9_fixed_function.cpp +++ b/src/d3d9/d3d9_fixed_function.cpp @@ -1139,12 +1139,12 @@ namespace dxvk { case (DXVK_TSS_TCI_CAMERASPACENORMAL >> TCIOffset): transformed = outNrm; - count = 4; + count = std::min(flags, 4u); break; case (DXVK_TSS_TCI_CAMERASPACEPOSITION >> TCIOffset): transformed = m_module.opCompositeInsert(m_vec4Type, m_module.constf32(1.0f), vtx, 1, &wIndex); - count = 4; + count = std::min(flags, 4u); break; case (DXVK_TSS_TCI_CAMERASPACEREFLECTIONVECTOR >> TCIOffset): { @@ -1159,7 +1159,7 @@ namespace dxvk { transformIndices[3] = m_module.constf32(1.0f); transformed = m_module.opCompositeConstruct(m_vec4Type, transformIndices.size(), transformIndices.data()); - count = 4; + count = std::min(flags, 4u); break; } @@ -1183,36 +1183,43 @@ namespace dxvk { transformIndices[3] = m_module.constf32(1.0f); transformed = m_module.opCompositeConstruct(m_vec4Type, transformIndices.size(), transformIndices.data()); - count = 4; + count = std::min(flags, 4u); break; } } - if (applyTransform || (count != 4 && count != 0)) { - if (applyTransform && !m_vsKey.Data.Contents.HasPositionT) { - for (uint32_t j = count; j < 4; j++) { - // If we're outside the component count of the vertex decl for this texcoord then we pad with zeroes. - // Otherwise, pad with ones. + if (applyTransform && !m_vsKey.Data.Contents.HasPositionT) { + for (uint32_t j = count; j < 4; j++) { + // If we're outside the component count of the vertex decl for this texcoord then we pad with zeroes. + // Otherwise, pad with ones. - // Very weird quirk in order to get texcoord transforms to work like they do in native. - // In future, maybe we could sort this out properly by chopping matrices of different sizes, but thats - // a project for another day. - uint32_t texcoordCount = (m_vsKey.Data.Contents.TexcoordDeclMask >> (3 * inputIndex)) & 0x7; - uint32_t value = j > texcoordCount ? m_module.constf32(0) : m_module.constf32(1); - transformed = m_module.opCompositeInsert(m_vec4Type, value, transformed, 1, &j); - } - - transformed = m_module.opVectorTimesMatrix(m_vec4Type, transformed, m_vs.constants.texcoord[i]); + // Very weird quirk in order to get texcoord transforms to work like they do in native. + // In future, maybe we could sort this out properly by chopping matrices of different sizes, but thats + // a project for another day. + uint32_t texcoordCount = (m_vsKey.Data.Contents.TexcoordDeclMask >> (3 * inputIndex)) & 0x7; + uint32_t value = j > texcoordCount ? m_module.constf32(0) : m_module.constf32(1); + transformed = m_module.opCompositeInsert(m_vec4Type, value, transformed, 1, &j); } - // Pad the unused section of it with the value for projection. - uint32_t projIdx = std::max(2u, count - 1); - uint32_t projValue = m_module.opCompositeExtract(m_floatType, transformed, 1, &projIdx); - - for (uint32_t j = count; j < 4; j++) - transformed = m_module.opCompositeInsert(m_vec4Type, projValue, transformed, 1, &j); + transformed = m_module.opVectorTimesMatrix(m_vec4Type, transformed, m_vs.constants.texcoord[i]); } + // The projection idx is always based on the flags, even when the input mode is not DXVK_TSS_TCI_PASSTHRU. + uint32_t projValue; + if (count < 3) { + // Not enough components to do projection. + // Native drivers render normally or garbage with D3DFVF_TEXCOORDSIZE2 or D3DTTFF_COUNT <3 + projValue = m_module.constf32(1.0f); + } else { + uint32_t projIdx = count - 1; + projValue = m_module.opCompositeExtract(m_floatType, transformed, 1, &projIdx); + } + + // The w component is only used for projection or unused, so always insert the component that's supposed to be divided by there. + // The fragment shader will then decide whether to project or not. + uint32_t wIdx = 3; + transformed = m_module.opCompositeInsert(m_vec4Type, projValue, transformed, 1, &wIdx); + m_module.opStore(m_vs.out.TEXCOORD[i], transformed); } From 79eea564fbcd9cf4b76ae79362090213cdb63ba8 Mon Sep 17 00:00:00 2001 From: Tiagoquix <30274161+Tiagoquix@users.noreply.github.com> Date: Tue, 28 May 2024 17:21:13 -0300 Subject: [PATCH 178/246] Add dxvk.maxChunkSize 1 to Origin --- src/util/config/config.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index be78b58f8..1a4d75661 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -370,6 +370,10 @@ namespace dxvk { { R"(\\EADesktop\.exe$)", {{ { "dxvk.maxChunkSize", "1" }, }} }, + /* Origin app (legacy EA Desktop) */ + { R"(\\Origin\.exe$)", {{ + { "dxvk.maxChunkSize", "1" }, + }} }, /* GOG Galaxy */ { R"(\\GalaxyClient\.exe$)", {{ { "dxvk.maxChunkSize", "1" }, From c2fd91f835741d7f38a1b7af8b982935dfbad241 Mon Sep 17 00:00:00 2001 From: Blisto91 <47954800+Blisto91@users.noreply.github.com> Date: Sun, 2 Jun 2024 00:57:19 +0200 Subject: [PATCH 179/246] [util] Set longMad for Ghostbusters Remastered Works around flickering on character faces --- src/util/config/config.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index 1a4d75661..8ff66956b 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -438,6 +438,11 @@ namespace dxvk { { R"(\\Gw2-64\.exe$)", {{ { "d3d11.longMad", "True" }, }} }, + /* Ghostbusters: The Video Game Remastered * + * Flickering on character faces */ + { R"(\\ghost\.exe$)", {{ + { "d3d11.longMad", "True" }, + }} }, /**********************************************/ /* D3D9 GAMES */ From ee18aecb8a29987b3131e80761cea41fad3392af Mon Sep 17 00:00:00 2001 From: Tiagoquix <30274161+Tiagoquix@users.noreply.github.com> Date: Sun, 2 Jun 2024 15:08:52 -0300 Subject: [PATCH 180/246] [util] Add dxvk.maxChunkSize 1 for Origin Web Helper Service and Ubisoft Connect (UPlay) (#4047) * Add dxvk.maxChunkSize 1 to Ubisoft Connect (UPlay) * Add Origin Web Helper Service and fix Rockstar Games entries * Revert Rockstar changes, improve Origin and Ubisoft --- src/util/config/config.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index 8ff66956b..ce03b4f0c 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -371,7 +371,11 @@ namespace dxvk { { "dxvk.maxChunkSize", "1" }, }} }, /* Origin app (legacy EA Desktop) */ - { R"(\\Origin\.exe$)", {{ + { R"(\\Origin\\(Origin|OriginWebHelperService)\.exe$)", {{ + { "dxvk.maxChunkSize", "1" }, + }} }, + /* Ubisoft Connect (UPlay) */ + { R"(\\Ubisoft\\Ubisoft Game Launcher\\(UbisoftConnect|upc)\.exe$)", {{ { "dxvk.maxChunkSize", "1" }, }} }, /* GOG Galaxy */ From fd978704fb75cb965fee32c7e15b167e490f848b Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Wed, 5 Jun 2024 00:43:38 +0200 Subject: [PATCH 181/246] [dxvk] Add dxvk.deviceFilter config option --- dxvk.conf | 8 ++++++++ src/dxvk/dxvk_device_filter.cpp | 11 ++++++++--- src/dxvk/dxvk_device_filter.h | 6 +++++- src/dxvk/dxvk_instance.cpp | 2 +- src/dxvk/dxvk_options.cpp | 1 + src/dxvk/dxvk_options.h | 3 +++ 6 files changed, 26 insertions(+), 5 deletions(-) diff --git a/dxvk.conf b/dxvk.conf index 3543876a5..0fa96f215 100644 --- a/dxvk.conf +++ b/dxvk.conf @@ -1,3 +1,10 @@ +# Device filter. Only exposes devices whose Vulkan device name contains +# the given string. May be useful to force an application to run on a +# specific GPU, but not applications launched by that application. + +# dxvk.deviceFilter = "" + + # Expose the HDR10 ColorSpace (DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020) # to the application by default. # This shows to the game that the global Windows 'HDR Mode' is enabled. @@ -10,6 +17,7 @@ # dxgi.enableHDR = True + # Create the VkSurface on the first call to IDXGISwapChain::Present, # rather than when creating the swap chain. Some games that start # rendering with a different graphics API may require this option, diff --git a/src/dxvk/dxvk_device_filter.cpp b/src/dxvk/dxvk_device_filter.cpp index a7546d4b0..0bd0e5ca5 100644 --- a/src/dxvk/dxvk_device_filter.cpp +++ b/src/dxvk/dxvk_device_filter.cpp @@ -2,11 +2,16 @@ namespace dxvk { - DxvkDeviceFilter::DxvkDeviceFilter(DxvkDeviceFilterFlags flags) + DxvkDeviceFilter::DxvkDeviceFilter( + DxvkDeviceFilterFlags flags, + const DxvkOptions& options) : m_flags(flags) { m_matchDeviceName = env::getEnvVar("DXVK_FILTER_DEVICE_NAME"); - - if (m_matchDeviceName.size() != 0) + + if (m_matchDeviceName.empty()) + m_matchDeviceName = options.deviceFilter; + + if (!m_matchDeviceName.empty()) m_flags.set(DxvkDeviceFilterFlag::MatchDeviceName); } diff --git a/src/dxvk/dxvk_device_filter.h b/src/dxvk/dxvk_device_filter.h index 7b411e6ad..79a6db1f7 100644 --- a/src/dxvk/dxvk_device_filter.h +++ b/src/dxvk/dxvk_device_filter.h @@ -1,6 +1,7 @@ #pragma once #include "dxvk_adapter.h" +#include "dxvk_options.h" namespace dxvk { @@ -31,7 +32,10 @@ namespace dxvk { public: - DxvkDeviceFilter(DxvkDeviceFilterFlags flags); + DxvkDeviceFilter( + DxvkDeviceFilterFlags flags, + const DxvkOptions& options); + ~DxvkDeviceFilter(); /** diff --git a/src/dxvk/dxvk_instance.cpp b/src/dxvk/dxvk_instance.cpp index d23a35619..e8e7783e5 100644 --- a/src/dxvk/dxvk_instance.cpp +++ b/src/dxvk/dxvk_instance.cpp @@ -256,7 +256,7 @@ namespace dxvk { filterFlags.set(DxvkDeviceFilterFlag::SkipCpuDevices); } - DxvkDeviceFilter filter(filterFlags); + DxvkDeviceFilter filter(filterFlags, m_options); std::vector> result; uint32_t numDGPU = 0; diff --git a/src/dxvk/dxvk_options.cpp b/src/dxvk/dxvk_options.cpp index 47e24492d..d84f08a6e 100644 --- a/src/dxvk/dxvk_options.cpp +++ b/src/dxvk/dxvk_options.cpp @@ -13,6 +13,7 @@ namespace dxvk { hud = config.getOption("dxvk.hud", ""); tearFree = config.getOption("dxvk.tearFree", Tristate::Auto); hideIntegratedGraphics = config.getOption ("dxvk.hideIntegratedGraphics", false); + deviceFilter = config.getOption("dxvk.deviceFilter", ""); } } diff --git a/src/dxvk/dxvk_options.h b/src/dxvk/dxvk_options.h index e7e6862f2..ac63bc108 100644 --- a/src/dxvk/dxvk_options.h +++ b/src/dxvk/dxvk_options.h @@ -41,6 +41,9 @@ namespace dxvk { // present. May be necessary for some games that // incorrectly assume monitor layouts. bool hideIntegratedGraphics; + + // Device name + std::string deviceFilter; }; } From 8d965359a59540c0590039303a8f6d848533175e Mon Sep 17 00:00:00 2001 From: Blisto91 <47954800+Blisto91@users.noreply.github.com> Date: Thu, 6 Jun 2024 18:55:39 +0200 Subject: [PATCH 182/246] [util] Set longMad for Watch_Dogs Works around flickering on some objects such as the blue light arrow on lowering gates --- src/util/config/config.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index ce03b4f0c..62745524a 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -447,6 +447,10 @@ namespace dxvk { { R"(\\ghost\.exe$)", {{ { "d3d11.longMad", "True" }, }} }, + /* Watch_Dogs - Some objects flicker without */ + { R"(\\watch_dogs\.exe$)", {{ + { "d3d11.longMad", "True" }, + }} }, /**********************************************/ /* D3D9 GAMES */ From 2188caae8ebd159a26cbc56ff6087e877c289015 Mon Sep 17 00:00:00 2001 From: Sanakan8472 Date: Thu, 6 Jun 2024 22:14:53 +0200 Subject: [PATCH 183/246] Fix GLFW exception at startup `GlfwWsiDriver::getInstanceExtensions` was creating an `std::vector` with a size argument in the ctor but then used `push_back` instead of filling the pre-allocated elements, leading to a bunch of nullptr entries at the start that caused an exception later on when accessed. --- src/wsi/glfw/wsi_platform_glfw.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wsi/glfw/wsi_platform_glfw.cpp b/src/wsi/glfw/wsi_platform_glfw.cpp index ee818bb20..62d7271af 100644 --- a/src/wsi/glfw/wsi_platform_glfw.cpp +++ b/src/wsi/glfw/wsi_platform_glfw.cpp @@ -44,7 +44,7 @@ namespace dxvk::wsi { if (extensionCount == 0) throw DxvkError(str::format("GLFW WSI: Failed to get required instance extensions")); - std::vector names(extensionCount); + std::vector names; for (uint32_t i = 0; i < extensionCount; ++i) { names.push_back(extensionArray[i]); } From b4c4c9e6832368047390610123a0a2c28ed76add Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Thu, 6 Jun 2024 09:20:04 +0200 Subject: [PATCH 184/246] [util] Remove useless vsync parameter from FPS limiter --- src/dxvk/dxvk_presenter.cpp | 12 ++---------- src/dxvk/dxvk_presenter.h | 3 --- src/util/util_fps_limiter.cpp | 4 ++-- src/util/util_fps_limiter.h | 3 +-- 4 files changed, 5 insertions(+), 17 deletions(-) diff --git a/src/dxvk/dxvk_presenter.cpp b/src/dxvk/dxvk_presenter.cpp index 07749b38e..397bbcbc2 100644 --- a/src/dxvk/dxvk_presenter.cpp +++ b/src/dxvk/dxvk_presenter.cpp @@ -130,7 +130,7 @@ namespace dxvk { m_frameQueue.push(frame); m_frameCond.notify_one(); } else { - applyFrameRateLimit(mode); + m_fpsLimiter.delay(); m_signal->signal(frameId); } @@ -648,14 +648,6 @@ namespace dxvk { } - void Presenter::applyFrameRateLimit(VkPresentModeKHR mode) { - bool vsync = mode == VK_PRESENT_MODE_FIFO_KHR - || mode == VK_PRESENT_MODE_FIFO_RELAXED_KHR; - - m_fpsLimiter.delay(vsync); - } - - void Presenter::runFrameThread() { env::setThreadName("dxvk-frame"); @@ -677,7 +669,7 @@ namespace dxvk { // Apply the FPS limiter before signaling the frame event in // order to reduce latency if the app uses it for frame pacing. - applyFrameRateLimit(frame.mode); + m_fpsLimiter.delay(); // If the present operation has succeeded, actually wait for it to complete. // Don't bother with it on MAILBOX / IMMEDIATE modes since doing so would diff --git a/src/dxvk/dxvk_presenter.h b/src/dxvk/dxvk_presenter.h index c5ba12733..30247796c 100644 --- a/src/dxvk/dxvk_presenter.h +++ b/src/dxvk/dxvk_presenter.h @@ -294,9 +294,6 @@ namespace dxvk { void destroySurface(); - void applyFrameRateLimit( - VkPresentModeKHR mode); - void runFrameThread(); }; diff --git a/src/util/util_fps_limiter.cpp b/src/util/util_fps_limiter.cpp index 8bec7b523..ff9ec40e0 100644 --- a/src/util/util_fps_limiter.cpp +++ b/src/util/util_fps_limiter.cpp @@ -35,7 +35,7 @@ namespace dxvk { std::lock_guard lock(m_mutex); if (!m_envOverride) { - m_targetInterval = frameRate > 0.0 + m_targetInterval = frameRate != 0.0 ? TimerDuration(int64_t(double(TimerDuration::period::den) / frameRate)) : TimerDuration::zero(); @@ -45,7 +45,7 @@ namespace dxvk { } - void FpsLimiter::delay(bool vsyncEnabled) { + void FpsLimiter::delay() { std::lock_guard lock(m_mutex); if (!isEnabled()) diff --git a/src/util/util_fps_limiter.h b/src/util/util_fps_limiter.h index e75cbc706..fe863cfc8 100644 --- a/src/util/util_fps_limiter.h +++ b/src/util/util_fps_limiter.h @@ -34,9 +34,8 @@ namespace dxvk { * Blocks the calling thread if the limiter is enabled * and the time since the last call to \ref delay is * shorter than the target interval. - * \param [in] vsyncEnabled \c true if vsync is enabled */ - void delay(bool vsyncEnabled); + void delay(); /** * \brief Checks whether the frame rate limiter is enabled From 379346751a102500a70a87e67d66941140e82040 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Thu, 6 Jun 2024 10:31:20 +0200 Subject: [PATCH 185/246] [util] Implement refresh rate heuristic for frame rate limiter --- src/util/util_fps_limiter.cpp | 72 ++++++++++++++++++++++++++++++----- src/util/util_fps_limiter.h | 18 ++++----- 2 files changed, 72 insertions(+), 18 deletions(-) diff --git a/src/util/util_fps_limiter.cpp b/src/util/util_fps_limiter.cpp index ff9ec40e0..6b3e13cd2 100644 --- a/src/util/util_fps_limiter.cpp +++ b/src/util/util_fps_limiter.cpp @@ -35,49 +35,103 @@ namespace dxvk { std::lock_guard lock(m_mutex); if (!m_envOverride) { - m_targetInterval = frameRate != 0.0 + TimerDuration interval = frameRate != 0.0 ? TimerDuration(int64_t(double(TimerDuration::period::den) / frameRate)) : TimerDuration::zero(); - if (isEnabled() && !m_initialized) - initialize(); + if (m_targetInterval != interval) { + m_targetInterval = interval; + + m_heuristicFrameCount = 0; + m_heuristicEnable = false; + + if (m_targetInterval != TimerDuration::zero() && !m_initialized) + initialize(); + } } } void FpsLimiter::delay() { - std::lock_guard lock(m_mutex); + std::unique_lock lock(m_mutex); + auto interval = m_targetInterval; - if (!isEnabled()) + if (interval == TimerDuration::zero()) return; auto t0 = m_lastFrame; auto t1 = dxvk::high_resolution_clock::now(); + if (interval < TimerDuration::zero()) { + interval = -interval; + + if (!testRefreshHeuristic(interval, t1)) + return; + } + + // Subsequent code must not access any class members + // that can be written by setTargetFrameRate + lock.unlock(); + auto frameTime = std::chrono::duration_cast(t1 - t0); - if (frameTime * 100 > m_targetInterval * 103 - m_deviation * 100) { + if (frameTime * 100 > interval * 103 - m_deviation * 100) { // If we have a slow frame, reset the deviation since we // do not want to compensate for low performance later on m_deviation = TimerDuration::zero(); } else { // Don't call sleep if the amount of time to sleep is shorter // than the time the function calls are likely going to take - TimerDuration sleepDuration = m_targetInterval - m_deviation - frameTime; + TimerDuration sleepDuration = interval - m_deviation - frameTime; t1 = Sleep::sleepFor(t1, sleepDuration); // Compensate for any sleep inaccuracies in the next frame, and // limit cumulative deviation in order to avoid stutter in case we // have a number of slow frames immediately followed by a fast one. frameTime = std::chrono::duration_cast(t1 - t0); - m_deviation += frameTime - m_targetInterval; - m_deviation = std::min(m_deviation, m_targetInterval / 16); + m_deviation += frameTime - interval; + m_deviation = std::min(m_deviation, interval / 16); } m_lastFrame = t1; } + bool FpsLimiter::testRefreshHeuristic(TimerDuration interval, TimePoint now) { + if (m_heuristicEnable) + return true; + + // Use a sliding window to determine whether the current + // frame rate is higher than the targeted refresh rate + uint32_t heuristicWindow = m_heuristicFrameTimes.size(); + auto windowStart = m_heuristicFrameTimes[m_heuristicFrameCount % heuristicWindow]; + auto windowDuration = std::chrono::duration_cast(now - windowStart); + + m_heuristicFrameTimes[m_heuristicFrameCount % heuristicWindow] = now; + m_heuristicFrameCount += 1; + + // The first window of frames may contain faster frames as the + // internal swap chain queue fills up, so we should ignore it. + if (m_heuristicFrameCount < 2 * heuristicWindow) + return false; + + // Test whether we should engage the frame rate limiter. It will + // stay enabled until the refresh rate or vsync enablement change. + m_heuristicEnable = (103 * windowDuration) < (100 * heuristicWindow) * interval; + + if (m_heuristicEnable) { + double got = (double(heuristicWindow) * double(TimerDuration::period::den)) + / (double(windowDuration.count()) * double(TimerDuration::period::num)); + double refresh = double(TimerDuration::period::den) / (double(TimerDuration::period::num) * double(interval.count())); + + Logger::info(str::format("Detected frame rate (~", uint32_t(got), ") higher than selected refresh rate of ~", + uint32_t(refresh), " Hz.\n", "Engaging frame rate limiter.")); + } + + return m_heuristicEnable; + } + + void FpsLimiter::initialize() { m_lastFrame = dxvk::high_resolution_clock::now(); m_initialized = true; diff --git a/src/util/util_fps_limiter.h b/src/util/util_fps_limiter.h index fe863cfc8..a30e37258 100644 --- a/src/util/util_fps_limiter.h +++ b/src/util/util_fps_limiter.h @@ -1,5 +1,7 @@ #pragma once +#include + #include "thread.h" #include "util_time.h" @@ -37,14 +39,6 @@ namespace dxvk { */ void delay(); - /** - * \brief Checks whether the frame rate limiter is enabled - * \returns \c true if the target frame rate is non-zero. - */ - bool isEnabled() const { - return m_targetInterval != TimerDuration::zero(); - } - private: using TimePoint = dxvk::high_resolution_clock::time_point; @@ -54,11 +48,17 @@ namespace dxvk { TimerDuration m_targetInterval = TimerDuration::zero(); TimerDuration m_deviation = TimerDuration::zero(); - TimePoint m_lastFrame; + TimePoint m_lastFrame = TimePoint(); bool m_initialized = false; bool m_envOverride = false; + uint32_t m_heuristicFrameCount = 0; + std::array m_heuristicFrameTimes = { }; + bool m_heuristicEnable = false; + + bool testRefreshHeuristic(TimerDuration interval, TimePoint now); + void initialize(); }; From 1c198dcd48cf834029ad9bb2dff58b89269f2678 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Thu, 6 Jun 2024 10:32:02 +0200 Subject: [PATCH 186/246] [dxgi] Limit frame rate to display refresh as necessary --- dxvk.conf | 7 ++++++- src/d3d11/d3d11_options.cpp | 1 - src/d3d11/d3d11_options.h | 3 --- src/d3d11/d3d11_swapchain.cpp | 14 ++++++++++++-- src/d3d11/d3d11_swapchain.h | 23 ++++++++++++++--------- src/dxgi/dxgi_interfaces.h | 8 ++++++++ src/dxgi/dxgi_options.cpp | 1 + src/dxgi/dxgi_options.h | 3 +++ src/dxgi/dxgi_swapchain.cpp | 31 ++++++++++++++++++++++++++++++- src/dxgi/dxgi_swapchain.h | 8 ++++++++ 10 files changed, 82 insertions(+), 17 deletions(-) diff --git a/dxvk.conf b/dxvk.conf index 0fa96f215..10c95d442 100644 --- a/dxvk.conf +++ b/dxvk.conf @@ -43,7 +43,12 @@ # bugs in games that have physics or other simulation tied to their frame # rate, but do not provide their own limiter. # -# Supported values : Any non-negative integer +# Supported values +# -1: Always disables the limiter +# 0: Default behaviour. Limits the frame rate to the selected display +# refresh rate when vertical synchronization is enabled if the +# actual display mode does not match the game's one. +# n: Limit to n frames per second. # dxgi.maxFrameRate = 0 # d3d9.maxFrameRate = 0 diff --git a/src/d3d11/d3d11_options.cpp b/src/d3d11/d3d11_options.cpp index c6b41ecb7..835ce985b 100644 --- a/src/d3d11/d3d11_options.cpp +++ b/src/d3d11/d3d11_options.cpp @@ -30,7 +30,6 @@ namespace dxvk { this->deferSurfaceCreation = config.getOption("dxgi.deferSurfaceCreation", false); this->numBackBuffers = config.getOption("dxgi.numBackBuffers", 0); this->maxFrameLatency = config.getOption("dxgi.maxFrameLatency", 0); - this->maxFrameRate = config.getOption("dxgi.maxFrameRate", 0); this->exposeDriverCommandLists = config.getOption("d3d11.exposeDriverCommandLists", true); this->longMad = config.getOption("d3d11.longMad", false); this->reproducibleCommandStream = config.getOption("d3d11.reproducibleCommandStream", false); diff --git a/src/d3d11/d3d11_options.h b/src/d3d11/d3d11_options.h index a5e21e17e..e556a89d8 100644 --- a/src/d3d11/d3d11_options.h +++ b/src/d3d11/d3d11_options.h @@ -80,9 +80,6 @@ namespace dxvk { /// a higher value. May help with frame timing issues. int32_t maxFrameLatency; - /// Limit frame rate - int32_t maxFrameRate; - /// Limit discardable resource size VkDeviceSize maxImplicitDiscardSize; diff --git a/src/d3d11/d3d11_swapchain.cpp b/src/d3d11/d3d11_swapchain.cpp index 73939e2c9..ce6faed53 100644 --- a/src/d3d11/d3d11_swapchain.cpp +++ b/src/d3d11/d3d11_swapchain.cpp @@ -99,7 +99,8 @@ namespace dxvk { if (riid == __uuidof(IUnknown) || riid == __uuidof(IDXGIVkSwapChain) - || riid == __uuidof(IDXGIVkSwapChain1)) { + || riid == __uuidof(IDXGIVkSwapChain1) + || riid == __uuidof(IDXGIVkSwapChain2)) { *ppvObject = ref(this); return S_OK; } @@ -347,6 +348,15 @@ namespace dxvk { } + void STDMETHODCALLTYPE D3D11SwapChain::SetTargetFrameRate( + double FrameRate) { + m_targetFrameRate = FrameRate; + + if (m_presenter != nullptr) + m_presenter->setFrameRateLimit(m_targetFrameRate); + } + + HRESULT D3D11SwapChain::PresentImage(UINT SyncInterval) { // Flush pending rendering commands before auto immediateContext = m_parent->GetContext(); @@ -496,7 +506,7 @@ namespace dxvk { presenterDesc.fullScreenExclusive = PickFullscreenMode(); m_presenter = new Presenter(m_device, m_frameLatencySignal, presenterDesc); - m_presenter->setFrameRateLimit(m_parent->GetOptions()->maxFrameRate); + m_presenter->setFrameRateLimit(m_targetFrameRate); } diff --git a/src/d3d11/d3d11_swapchain.h b/src/d3d11/d3d11_swapchain.h index 00073d769..9a2b0cac9 100644 --- a/src/d3d11/d3d11_swapchain.h +++ b/src/d3d11/d3d11_swapchain.h @@ -13,7 +13,7 @@ namespace dxvk { class D3D11Device; class D3D11DXGIDevice; - class D3D11SwapChain : public ComObject { + class D3D11SwapChain : public ComObject { constexpr static uint32_t DefaultFrameLatency = 1; public: @@ -86,6 +86,9 @@ namespace dxvk { void STDMETHODCALLTYPE GetFrameStatistics( DXGI_VK_FRAME_STATISTICS* pFrameStatistics); + void STDMETHODCALLTYPE SetTargetFrameRate( + double FrameRate); + private: enum BindingIds : uint32_t { @@ -116,18 +119,20 @@ namespace dxvk { std::vector> m_imageViews; - uint64_t m_frameId = DXGI_MAX_SWAP_CHAIN_BUFFERS; - uint32_t m_frameLatency = DefaultFrameLatency; - uint32_t m_frameLatencyCap = 0; - HANDLE m_frameLatencyEvent = nullptr; - Rc m_frameLatencySignal; + uint64_t m_frameId = DXGI_MAX_SWAP_CHAIN_BUFFERS; + uint32_t m_frameLatency = DefaultFrameLatency; + uint32_t m_frameLatencyCap = 0; + HANDLE m_frameLatencyEvent = nullptr; + Rc m_frameLatencySignal; - bool m_dirty = true; + bool m_dirty = true; - VkColorSpaceKHR m_colorspace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; + VkColorSpaceKHR m_colorspace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; std::optional m_hdrMetadata; - bool m_dirtyHdrMetadata = true; + bool m_dirtyHdrMetadata = true; + + double m_targetFrameRate = 0.0; dxvk::mutex m_frameStatisticsLock; DXGI_VK_FRAME_STATISTICS m_frameStatistics = { }; diff --git a/src/dxgi/dxgi_interfaces.h b/src/dxgi/dxgi_interfaces.h index 4c35bd527..390eee55a 100644 --- a/src/dxgi/dxgi_interfaces.h +++ b/src/dxgi/dxgi_interfaces.h @@ -137,6 +137,13 @@ IDXGIVkSwapChain1 : public IDXGIVkSwapChain { }; +MIDL_INTERFACE("aed91093-e02e-458c-bdef-a97da1a7e6d2") +IDXGIVkSwapChain2 : public IDXGIVkSwapChain1 { + virtual void STDMETHODCALLTYPE SetTargetFrameRate( + double FrameRate) = 0; +}; + + /** * \brief Private DXGI presenter factory */ @@ -471,5 +478,6 @@ __CRT_UUID_DECL(IDXGIVkInteropSurface, 0x5546cf8c,0x77e7,0x4341,0xb0,0x5d,0x __CRT_UUID_DECL(IDXGIVkSurfaceFactory, 0x1e7895a1,0x1bc3,0x4f9c,0xa6,0x70,0x29,0x0a,0x4b,0xc9,0x58,0x1a); __CRT_UUID_DECL(IDXGIVkSwapChain, 0xe4a9059e,0xb569,0x46ab,0x8d,0xe7,0x50,0x1b,0xd2,0xbc,0x7f,0x7a); __CRT_UUID_DECL(IDXGIVkSwapChain1, 0x785326d4,0xb77b,0x4826,0xae,0x70,0x8d,0x08,0x30,0x8e,0xe6,0xd1); +__CRT_UUID_DECL(IDXGIVkSwapChain2, 0xaed91093,0xe02e,0x458c,0xbd,0xef,0xa9,0x7d,0xa1,0xa7,0xe6,0xd2); __CRT_UUID_DECL(IDXGIVkSwapChainFactory, 0xe7d6c3ca,0x23a0,0x4e08,0x9f,0x2f,0xea,0x52,0x31,0xdf,0x66,0x33); #endif diff --git a/src/dxgi/dxgi_options.cpp b/src/dxgi/dxgi_options.cpp index d7d55d4e8..f352dfdbf 100644 --- a/src/dxgi/dxgi_options.cpp +++ b/src/dxgi/dxgi_options.cpp @@ -93,6 +93,7 @@ namespace dxvk { this->maxDeviceMemory = VkDeviceSize(config.getOption("dxgi.maxDeviceMemory", 0)) << 20; this->maxSharedMemory = VkDeviceSize(config.getOption("dxgi.maxSharedMemory", 0)) << 20; + this->maxFrameRate = config.getOption("dxgi.maxFrameRate", 0); this->syncInterval = config.getOption("dxgi.syncInterval", -1); // Expose Nvidia GPUs properly if NvAPI is enabled in environment diff --git a/src/dxgi/dxgi_options.h b/src/dxgi/dxgi_options.h index a22de94f1..95b223f1b 100644 --- a/src/dxgi/dxgi_options.h +++ b/src/dxgi/dxgi_options.h @@ -49,6 +49,9 @@ namespace dxvk { /// Enable HDR bool enableHDR; + /// Limit frame rate + int32_t maxFrameRate; + /// Sync interval. Overrides the value /// passed to IDXGISwapChain::Present. int32_t syncInterval; diff --git a/src/dxgi/dxgi_swapchain.cpp b/src/dxgi/dxgi_swapchain.cpp index 9b1283a1b..a55278a66 100644 --- a/src/dxgi/dxgi_swapchain.cpp +++ b/src/dxgi/dxgi_swapchain.cpp @@ -25,6 +25,9 @@ namespace dxvk { // Query updated interface versions from presenter, this // may fail e.g. with older vkd3d-proton builds. m_presenter->QueryInterface(__uuidof(IDXGIVkSwapChain1), reinterpret_cast(&m_presenter1)); + m_presenter->QueryInterface(__uuidof(IDXGIVkSwapChain2), reinterpret_cast(&m_presenter2)); + + m_frameRateOption = m_factory->GetOptions()->maxFrameRate; // Query monitor info form DXVK's DXGI factory, if available m_factory->QueryInterface(__uuidof(IDXGIVkMonitorInfo), reinterpret_cast(&m_monitorInfo)); @@ -328,6 +331,7 @@ namespace dxvk { SyncInterval = options->syncInterval; UpdateGlobalHDRState(); + UpdateTargetFrameRate(SyncInterval); std::lock_guard lockWin(m_lockWindow); HRESULT hr = S_OK; @@ -767,7 +771,7 @@ namespace dxvk { HRESULT hr = pOutput->FindClosestMatchingMode1( &preferredMode, &selectedMode, nullptr); - + if (FAILED(hr)) { Logger::err(str::format( "DXGI: Failed to query closest mode:", @@ -777,6 +781,9 @@ namespace dxvk { return hr; } + if (!selectedMode.RefreshRate.Denominator) + selectedMode.RefreshRate.Denominator = 1; + if (!wsi::setWindowMode(outputDesc.Monitor, m_window, ConvertDisplayMode(selectedMode))) return DXGI_ERROR_NOT_CURRENTLY_AVAILABLE; @@ -798,6 +805,8 @@ namespace dxvk { ReleaseMonitorData(); } + m_frameRateRefresh = double(selectedMode.RefreshRate.Numerator) + / double(selectedMode.RefreshRate.Denominator); return S_OK; } @@ -809,6 +818,7 @@ namespace dxvk { if (!wsi::restoreDisplayMode()) return DXGI_ERROR_NOT_CURRENTLY_AVAILABLE; + m_frameRateRefresh = 0.0; return S_OK; } @@ -952,4 +962,23 @@ namespace dxvk { return hr; } + + void DxgiSwapChain::UpdateTargetFrameRate( + UINT SyncInterval) { + if (m_presenter2 == nullptr) + return; + + // Use a negative number to indicate that the limiter should only + // be engaged if the target frame rate is actually exceeded + double frameRate = std::max(m_frameRateOption, 0.0); + + if (SyncInterval && m_frameRateOption == 0.0) + frameRate = -m_frameRateRefresh / double(SyncInterval); + + if (m_frameRateLimit != frameRate) { + m_frameRateLimit = frameRate; + m_presenter2->SetTargetFrameRate(frameRate); + } + } + } diff --git a/src/dxgi/dxgi_swapchain.h b/src/dxgi/dxgi_swapchain.h index 8ec7d5014..4bdad9d0f 100644 --- a/src/dxgi/dxgi_swapchain.h +++ b/src/dxgi/dxgi_swapchain.h @@ -187,12 +187,17 @@ namespace dxvk { Com m_presenter; Com m_presenter1; + Com m_presenter2; HMONITOR m_monitor; bool m_monitorHasOutput = true; bool m_frameStatisticsDisjoint = true; wsi::DxvkWindowState m_windowState; + double m_frameRateOption = 0.0; + double m_frameRateRefresh = 0.0; + double m_frameRateLimit = 0.0; + DXGI_COLOR_SPACE_TYPE m_colorSpace = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709; uint32_t m_globalHDRStateSerial = 0; @@ -233,6 +238,9 @@ namespace dxvk { DXGI_FORMAT Format, DXGI_COLOR_SPACE_TYPE ColorSpace); + void UpdateTargetFrameRate( + UINT SyncInterval); + HRESULT STDMETHODCALLTYPE PresentBase( UINT SyncInterval, UINT PresentFlags, From 5674abe483b4350fe173317132438ea57a3b5bba Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Thu, 6 Jun 2024 10:44:20 +0200 Subject: [PATCH 187/246] [d3d9] Limit frame rate to display refresh as necessary --- src/d3d9/d3d9_swapchain.cpp | 13 ++++++++++++- src/d3d9/d3d9_swapchain.h | 2 ++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/d3d9/d3d9_swapchain.cpp b/src/d3d9/d3d9_swapchain.cpp index 577243306..1d1c7c382 100644 --- a/src/d3d9/d3d9_swapchain.cpp +++ b/src/d3d9/d3d9_swapchain.cpp @@ -191,6 +191,7 @@ namespace dxvk { if (!m_wctx->presenter->hasSwapChain()) return D3D_OK; + UpdateTargetFrameRate(presentInterval); PresentImage(presentInterval); return D3D_OK; } catch (const DxvkError& e) { @@ -927,7 +928,6 @@ namespace dxvk { presenterDesc.fullScreenExclusive = PickFullscreenMode(); m_wctx->presenter = new Presenter(m_device, m_wctx->frameLatencySignal, presenterDesc); - m_wctx->presenter->setFrameRateLimit(m_parent->GetOptions()->maxFrameRate); } @@ -1107,6 +1107,17 @@ namespace dxvk { } + void D3D9SwapChainEx::UpdateTargetFrameRate(uint32_t SyncInterval) { + double frameRateOption = double(m_parent->GetOptions()->maxFrameRate); + double frameRate = std::max(frameRateOption, 0.0); + + if (SyncInterval && frameRateOption == 0.0) + frameRate = -m_displayRefreshRate / double(SyncInterval); + + m_wctx->presenter->setFrameRateLimit(frameRate); + } + + void D3D9SwapChainEx::SyncFrameLatency() { // Wait for the sync event so that we respect the maximum frame latency m_wctx->frameLatencySignal->wait(m_wctx->frameId - GetActualFrameLatency()); diff --git a/src/d3d9/d3d9_swapchain.h b/src/d3d9/d3d9_swapchain.h index c6cfef27d..325ba18a1 100644 --- a/src/d3d9/d3d9_swapchain.h +++ b/src/d3d9/d3d9_swapchain.h @@ -213,6 +213,8 @@ namespace dxvk { void InitRamp(); + void UpdateTargetFrameRate(uint32_t SyncInterval); + uint32_t GetActualFrameLatency(); uint32_t PickFormats( From 1811f4b9950818ba5c8bfbaf06776831ea2cd0cb Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Thu, 6 Jun 2024 11:01:04 +0200 Subject: [PATCH 188/246] [util] Consolidate maxFrameRate options --- dxvk.conf | 3 +-- src/d3d9/d3d9_options.cpp | 2 +- src/dxgi/dxgi_options.cpp | 2 +- src/util/config/config.cpp | 42 +++++++++++++++++++------------------- 4 files changed, 24 insertions(+), 25 deletions(-) diff --git a/dxvk.conf b/dxvk.conf index 10c95d442..472a1904e 100644 --- a/dxvk.conf +++ b/dxvk.conf @@ -50,8 +50,7 @@ # actual display mode does not match the game's one. # n: Limit to n frames per second. -# dxgi.maxFrameRate = 0 -# d3d9.maxFrameRate = 0 +# dxvk.maxFrameRate = 0 # Override PCI vendor and device IDs reported to the application. Can diff --git a/src/d3d9/d3d9_options.cpp b/src/d3d9/d3d9_options.cpp index 9671545ea..bda5c49f1 100644 --- a/src/d3d9/d3d9_options.cpp +++ b/src/d3d9/d3d9_options.cpp @@ -42,7 +42,7 @@ namespace dxvk { : (adapter != nullptr ? adapter->deviceProperties().vendorID : 0); this->maxFrameLatency = config.getOption ("d3d9.maxFrameLatency", 0); - this->maxFrameRate = config.getOption ("d3d9.maxFrameRate", 0); + this->maxFrameRate = config.getOption ("dxvk.maxFrameRate", 0); this->presentInterval = config.getOption ("d3d9.presentInterval", -1); this->shaderModel = config.getOption ("d3d9.shaderModel", 3); this->dpiAware = config.getOption ("d3d9.dpiAware", true); diff --git a/src/dxgi/dxgi_options.cpp b/src/dxgi/dxgi_options.cpp index f352dfdbf..994deceda 100644 --- a/src/dxgi/dxgi_options.cpp +++ b/src/dxgi/dxgi_options.cpp @@ -93,7 +93,7 @@ namespace dxvk { this->maxDeviceMemory = VkDeviceSize(config.getOption("dxgi.maxDeviceMemory", 0)) << 20; this->maxSharedMemory = VkDeviceSize(config.getOption("dxgi.maxSharedMemory", 0)) << 20; - this->maxFrameRate = config.getOption("dxgi.maxFrameRate", 0); + this->maxFrameRate = config.getOption("dxvk.maxFrameRate", 0); this->syncInterval = config.getOption("dxgi.syncInterval", -1); // Expose Nvidia GPUs properly if NvAPI is enabled in environment diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index 62745524a..0c7c61758 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -314,12 +314,12 @@ namespace dxvk { /* Shantae and the Pirate's Curse * * Game speeds up above 60 fps */ { R"(\\ShantaeCurse\.exe$)", {{ - { "dxgi.maxFrameRate", "60" }, + { "dxvk.maxFrameRate", "60" }, }} }, /* Mighty Switch Force! Collection * * Games speed up above 60 fps */ { R"(\\MSFC\.exe$)", {{ - { "dxgi.maxFrameRate", "60" }, + { "dxvk.maxFrameRate", "60" }, }} }, /* Cardfight!! Vanguard Dear Days: * * Submits command lists multiple times */ @@ -405,7 +405,7 @@ namespace dxvk { * Game speed increases when above 60 fps in * * the tavern area */ { R"(\\BLADESTORM Nightmare\\Launch_(EA|JP)\.exe$)", {{ - { "dxgi.maxFrameRate", "60" }, + { "dxvk.maxFrameRate", "60" }, }} }, /* Ghost Recon Wildlands */ { R"(\\GRW\.exe$)", {{ @@ -481,7 +481,7 @@ namespace dxvk { /* Sonic Adventure 2 */ { R"(\\Sonic Adventure 2\\(launcher|sonic2app)\.exe$)", {{ { "d3d9.floatEmulation", "Strict" }, - { "d3d9.maxFrameRate", "60" }, + { "dxvk.maxFrameRate", "60" }, }} }, /* The Sims 2, Body Shop, @@ -503,7 +503,7 @@ namespace dxvk { Built-in Vsync Locks the game to 30 FPS */ { R"(\\Dead Space\.exe$)", {{ { "d3d9.supportDFFormats", "False" }, - { "d3d9.maxFrameRate", "60" }, + { "dxvk.maxFrameRate", "60" }, { "d3d9.presentInterval", "1" }, }} }, /* Halo CE/HaloPC */ @@ -586,7 +586,7 @@ namespace dxvk { * D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY buffer */ { R"(\\(trl|tra|tru)\.exe$)", {{ { "d3d9.cachedDynamicBuffers", "True" }, - { "d3d9.maxFrameRate", "60" }, + { "dxvk.maxFrameRate", "60" }, }} }, /* Everquest */ { R"(\\eqgame\.exe$)", {{ @@ -624,7 +624,7 @@ namespace dxvk { }} }, /* Demon Stone breaks at frame rates > 60fps */ { R"(\\Demonstone\.exe$)", {{ - { "d3d9.maxFrameRate", "60" }, + { "dxvk.maxFrameRate", "60" }, }} }, /* Far Cry 1 has worse water rendering when it detects AMD GPUs */ { R"(\\FarCry\.exe$)", {{ @@ -637,7 +637,7 @@ namespace dxvk { }} }, /* Sine Mora EX */ { R"(\\SineMoraEX\.exe$)", {{ - { "d3d9.maxFrameRate", "60" }, + { "dxvk.maxFrameRate", "60" }, }} }, /* Red Orchestra 2 */ { R"(\\ROGame\.exe$)", {{ @@ -674,7 +674,7 @@ namespace dxvk { }} }, /* Limbo */ { R"(\\limbo\.exe$)", {{ - { "d3d9.maxFrameRate", "60" }, + { "dxvk.maxFrameRate", "60" }, }} }, /* Escape from Tarkov launcher Same issue as Warhammer: RoR above */ @@ -718,7 +718,7 @@ namespace dxvk { /* Bionic Commando Physics break at high fps */ { R"(\\bionic_commando\.exe$)", {{ - { "d3d9.maxFrameRate", "60" }, + { "dxvk.maxFrameRate", "60" }, }} }, /* Need For Speed 3 modern patch */ { R"(\\nfs3\.exe$)", {{ @@ -727,12 +727,12 @@ namespace dxvk { /* Beyond Good And Evil * * UI breaks at high fps */ { R"(\\BGE\.exe$)", {{ - { "d3d9.maxFrameRate", "60" }, + { "dxvk.maxFrameRate", "60" }, }} }, /* King Of Fighters XIII * * In-game speed increases on high FPS */ { R"(\\kof(xiii|13_win32_Release)\.exe$)", {{ - { "d3d9.maxFrameRate", "60" }, + { "dxvk.maxFrameRate", "60" }, }} }, /* YS Origin * * Helps very bad frametimes in some areas */ @@ -809,7 +809,7 @@ namespace dxvk { /* Battle Fantasia Revised Edition * * Speedup above 60fps */ { R"(\\bf10\.exe$)", {{ - { "d3d9.maxFrameRate", "60" }, + { "dxvk.maxFrameRate", "60" }, }} }, /* Codename Panzers Phase One/Two * * Main menu won't render after intros * @@ -836,23 +836,23 @@ namespace dxvk { }} }, /* STEINS;GATE ELITE */ { R"(\\SG_ELITE\\Game\.exe$)", {{ - { "d3d9.maxFrameRate", "60" }, + { "dxvk.maxFrameRate", "60" }, }} }, /* The Incredibles */ { R"(\\IncPC\.exe$)", {{ - { "d3d9.maxFrameRate", "59" }, + { "dxvk.maxFrameRate", "59" }, }} }, /* Conflict Vietnam */ { R"(\\Vietnam\.exe$)", {{ - { "d3d9.maxFrameRate", "60" }, + { "dxvk.maxFrameRate", "60" }, }} }, /* Project: Snowblind */ { R"(\\Snowblind\.(SP|MP|exe)$)", {{ - { "d3d9.maxFrameRate", "60" }, + { "dxvk.maxFrameRate", "60" }, }} }, /* Aviary Attorney */ { R"(\\Aviary Attorney\\nw\.exe$)", {{ - { "d3d9.maxFrameRate", "60" }, + { "dxvk.maxFrameRate", "60" }, }} }, /* Drakensang: The Dark Eye */ { R"(\\drakensang\.exe$)", {{ @@ -878,7 +878,7 @@ namespace dxvk { }} }, /* Sonic CD */ { R"(\\soniccd\.exe$)", {{ - { "d3d9.maxFrameRate", "60" }, + { "dxvk.maxFrameRate", "60" }, }} }, /* UK Truck Simulator 1 */ { R"(\\UK Truck Simulator\\bin\\win_x86\\game\.exe$)", {{ @@ -897,7 +897,7 @@ namespace dxvk { }} }, /* Dark Void - Crashes above 60fps in places */ { R"(\\ShippingPC-SkyGame\.exe$)", {{ - { "d3d9.maxFrameRate", "60" }, + { "dxvk.maxFrameRate", "60" }, }} }, /* 9th Dawn II * * OpenGL game that also spins up d3d9 * @@ -962,7 +962,7 @@ namespace dxvk { /* Fallout 4: Defaults to 45 FPS on OLED, but also breaks above 60 FPS */ { R"(\\Fallout4\.exe$)", {{ { "dxgi.syncInterval", "1" }, - { "dxgi.maxFrameRate", "60" }, + { "dxvk.maxFrameRate", "60" }, }} }, }}; From 51f2e246fac2ecdfd05960a336c4b73b3f7a0621 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Mon, 10 Jun 2024 19:36:17 +0200 Subject: [PATCH 189/246] [util,dxvk] Limit frame rate based on deadline This should work better now that present_wait is universally supported. --- src/dxvk/dxvk_presenter.cpp | 7 +++---- src/util/util_fps_limiter.cpp | 39 +++++++---------------------------- src/util/util_fps_limiter.h | 6 +----- 3 files changed, 12 insertions(+), 40 deletions(-) diff --git a/src/dxvk/dxvk_presenter.cpp b/src/dxvk/dxvk_presenter.cpp index 397bbcbc2..efd3d7272 100644 --- a/src/dxvk/dxvk_presenter.cpp +++ b/src/dxvk/dxvk_presenter.cpp @@ -667,10 +667,6 @@ namespace dxvk { if (!frame.frameId) return; - // Apply the FPS limiter before signaling the frame event in - // order to reduce latency if the app uses it for frame pacing. - m_fpsLimiter.delay(); - // If the present operation has succeeded, actually wait for it to complete. // Don't bother with it on MAILBOX / IMMEDIATE modes since doing so would // restrict us to the display refresh rate on some platforms (XWayland). @@ -685,6 +681,9 @@ namespace dxvk { // Always signal even on error, since failures here // are transparent to the front-end. m_signal->signal(frame.frameId); + + // Apply FPS limtier here to align it as closely with scanout as we can. + m_fpsLimiter.delay(); } } diff --git a/src/util/util_fps_limiter.cpp b/src/util/util_fps_limiter.cpp index 6b3e13cd2..4186a5405 100644 --- a/src/util/util_fps_limiter.cpp +++ b/src/util/util_fps_limiter.cpp @@ -44,9 +44,6 @@ namespace dxvk { m_heuristicFrameCount = 0; m_heuristicEnable = false; - - if (m_targetInterval != TimerDuration::zero() && !m_initialized) - initialize(); } } } @@ -56,10 +53,11 @@ namespace dxvk { std::unique_lock lock(m_mutex); auto interval = m_targetInterval; - if (interval == TimerDuration::zero()) + if (interval == TimerDuration::zero()) { + m_nextFrame = TimePoint(); return; + } - auto t0 = m_lastFrame; auto t1 = dxvk::high_resolution_clock::now(); if (interval < TimerDuration::zero()) { @@ -73,27 +71,12 @@ namespace dxvk { // that can be written by setTargetFrameRate lock.unlock(); - auto frameTime = std::chrono::duration_cast(t1 - t0); + if (t1 < m_nextFrame) + Sleep::sleepUntil(t1, m_nextFrame); - if (frameTime * 100 > interval * 103 - m_deviation * 100) { - // If we have a slow frame, reset the deviation since we - // do not want to compensate for low performance later on - m_deviation = TimerDuration::zero(); - } else { - // Don't call sleep if the amount of time to sleep is shorter - // than the time the function calls are likely going to take - TimerDuration sleepDuration = interval - m_deviation - frameTime; - t1 = Sleep::sleepFor(t1, sleepDuration); - - // Compensate for any sleep inaccuracies in the next frame, and - // limit cumulative deviation in order to avoid stutter in case we - // have a number of slow frames immediately followed by a fast one. - frameTime = std::chrono::duration_cast(t1 - t0); - m_deviation += frameTime - interval; - m_deviation = std::min(m_deviation, interval / 16); - } - - m_lastFrame = t1; + m_nextFrame = (t1 < m_nextFrame + interval) + ? m_nextFrame + interval + : t1 + interval; } @@ -131,10 +114,4 @@ namespace dxvk { return m_heuristicEnable; } - - void FpsLimiter::initialize() { - m_lastFrame = dxvk::high_resolution_clock::now(); - m_initialized = true; - } - } diff --git a/src/util/util_fps_limiter.h b/src/util/util_fps_limiter.h index a30e37258..87f8bcf9b 100644 --- a/src/util/util_fps_limiter.h +++ b/src/util/util_fps_limiter.h @@ -47,10 +47,8 @@ namespace dxvk { dxvk::mutex m_mutex; TimerDuration m_targetInterval = TimerDuration::zero(); - TimerDuration m_deviation = TimerDuration::zero(); - TimePoint m_lastFrame = TimePoint(); + TimePoint m_nextFrame = TimePoint(); - bool m_initialized = false; bool m_envOverride = false; uint32_t m_heuristicFrameCount = 0; @@ -59,8 +57,6 @@ namespace dxvk { bool testRefreshHeuristic(TimerDuration interval, TimePoint now); - void initialize(); - }; } From 018db92342769fa847d356e5f578c43c89b46a18 Mon Sep 17 00:00:00 2001 From: Blisto91 <47954800+Blisto91@users.noreply.github.com> Date: Mon, 10 Jun 2024 19:42:44 +0200 Subject: [PATCH 190/246] [util] Disable Crysis 1 refresh rate fps limiter Game bug in its d3d10 mode where it selects the lowest supported refresh rate. --- src/util/config/config.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index 0c7c61758..87f8d2085 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -451,6 +451,11 @@ namespace dxvk { { R"(\\watch_dogs\.exe$)", {{ { "d3d11.longMad", "True" }, }} }, + /* Crysis 1/Warhead - Game bug in d3d10 makes * + * it select lowest supported refresh rate */ + { R"(\\Crysis(64)?\.exe$)", {{ + { "dxvk.maxFrameRate", "-1" }, + }} }, /**********************************************/ /* D3D9 GAMES */ From 336c9b6acc483c3b8d50cd236adb4639e561d316 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Wed, 12 Jun 2024 17:31:31 +0200 Subject: [PATCH 191/246] [dxgi] Allow changing refresh rate even without ALLOW_MODE_SWITCH Matches Windows behaviour, entering fullscreen state will only prevent us from changing the display resolution if the flag is not set. --- src/dxgi/dxgi_swapchain.cpp | 45 +++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/src/dxgi/dxgi_swapchain.cpp b/src/dxgi/dxgi_swapchain.cpp index a55278a66..682473065 100644 --- a/src/dxgi/dxgi_swapchain.cpp +++ b/src/dxgi/dxgi_swapchain.cpp @@ -460,9 +460,7 @@ namespace dxvk { return E_FAIL; } - // If the swap chain allows it, change the display mode - if (m_desc.Flags & DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH) - ChangeDisplayMode(output.ptr(), &newDisplayMode); + ChangeDisplayMode(output.ptr(), &newDisplayMode); wsi::updateFullscreenWindow(m_monitor, m_window, false); } @@ -672,35 +670,33 @@ namespace dxvk { } } - const bool modeSwitch = m_desc.Flags & DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH; + DXGI_MODE_DESC1 displayMode = { }; + displayMode.Width = m_desc.Width; + displayMode.Height = m_desc.Height; + displayMode.RefreshRate = m_descFs.RefreshRate; + displayMode.Format = m_desc.Format; + // Ignore these two, games usually use them wrong and we don't + // support any scaling modes except UNSPECIFIED anyway. + displayMode.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED; + displayMode.Scaling = DXGI_MODE_SCALING_UNSPECIFIED; - if (modeSwitch) { - DXGI_MODE_DESC1 displayMode = { }; - displayMode.Width = m_desc.Width; - displayMode.Height = m_desc.Height; - displayMode.RefreshRate = m_descFs.RefreshRate; - displayMode.Format = m_desc.Format; - // Ignore these two, games usually use them wrong and we don't - // support any scaling modes except UNSPECIFIED anyway. - displayMode.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED; - displayMode.Scaling = DXGI_MODE_SCALING_UNSPECIFIED; - - if (FAILED(ChangeDisplayMode(output.ptr(), &displayMode))) { - Logger::err("DXGI: EnterFullscreenMode: Failed to change display mode"); - return DXGI_ERROR_NOT_CURRENTLY_AVAILABLE; - } + if (FAILED(ChangeDisplayMode(output.ptr(), &displayMode))) { + Logger::err("DXGI: EnterFullscreenMode: Failed to change display mode"); + return DXGI_ERROR_NOT_CURRENTLY_AVAILABLE; } // Update swap chain description m_descFs.Windowed = FALSE; // Move the window so that it covers the entire output + bool modeSwitch = (m_desc.Flags & DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH) != 0u; + DXGI_OUTPUT_DESC desc; output->GetDesc(&desc); if (!wsi::enterFullscreenMode(desc.Monitor, m_window, &m_windowState, modeSwitch)) { - Logger::err("DXGI: EnterFullscreenMode: Failed to enter fullscreen mode"); - return DXGI_ERROR_NOT_CURRENTLY_AVAILABLE; + Logger::err("DXGI: EnterFullscreenMode: Failed to enter fullscreen mode"); + return DXGI_ERROR_NOT_CURRENTLY_AVAILABLE; } m_monitor = desc.Monitor; @@ -764,7 +760,12 @@ namespace dxvk { pOutput->GetDesc(&outputDesc); DXGI_MODE_DESC1 preferredMode = *pDisplayMode; - DXGI_MODE_DESC1 selectedMode; + DXGI_MODE_DESC1 selectedMode = { }; + + if (!(m_desc.Flags & DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH)) { + preferredMode.Width = 0; + preferredMode.Height = 0; + } if (preferredMode.Format == DXGI_FORMAT_UNKNOWN) preferredMode.Format = m_desc.Format; From a8710f70a50c9ccab564f450b3b48fd6bf4e6a8e Mon Sep 17 00:00:00 2001 From: Trevonn Date: Wed, 12 Jun 2024 23:03:25 +0100 Subject: [PATCH 192/246] [util] Dead Space 2 - Lock to 60 FPS and enable vsync Similar to the first game it has a poor vsync implementation and physics issues when the frame rate is unlocked. Locking to 60 FPS and enabling vsync externally provides a better experience after the ingame vsync is disabled --- src/util/config/config.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index 87f8d2085..7eb6a5295 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -511,6 +511,14 @@ namespace dxvk { { "dxvk.maxFrameRate", "60" }, { "d3d9.presentInterval", "1" }, }} }, + /* Dead Space 2 + Physics issues above 60 FPS + Built-in Vsync Locks the game to 30 FPS + */ + { R"(\\deadspace2\.exe$)", {{ + { "dxvk.maxFrameRate", "60" }, + { "d3d9.presentInterval", "1" }, + }} }, /* Halo CE/HaloPC */ { R"(\\halo(ce)?\.exe$)", {{ // Game enables minor decal layering fixes From 3e5eb1660f69d73dd36ffee49e2e903c511a945e Mon Sep 17 00:00:00 2001 From: Jeff Date: Fri, 14 Jun 2024 11:10:49 +0100 Subject: [PATCH 193/246] [d3d8] Implement Dref scaling and fixed-function depth textures (#3565) --- src/d3d9/d3d9_device.cpp | 2 ++ src/d3d9/d3d9_fixed_function.cpp | 33 +++++++++++++++++++++++++------- src/d3d9/d3d9_fixed_function.h | 10 ++++++++-- src/d3d9/d3d9_options.cpp | 3 +++ src/d3d9/d3d9_options.h | 3 +++ src/dxso/dxso_compiler.cpp | 8 ++++++++ src/dxso/dxso_options.cpp | 2 ++ src/dxso/dxso_options.h | 6 ++++++ 8 files changed, 58 insertions(+), 9 deletions(-) diff --git a/src/d3d9/d3d9_device.cpp b/src/d3d9/d3d9_device.cpp index e53c96a31..776dedb56 100644 --- a/src/d3d9/d3d9_device.cpp +++ b/src/d3d9/d3d9_device.cpp @@ -7441,6 +7441,8 @@ namespace dxvk { stage.Projected = (ttff & D3DTTFF_PROJECTED) ? 1 : 0; stage.ProjectedCount = (ttff & D3DTTFF_PROJECTED) ? count : 0; + + stage.SampleDref = (m_depthTextures & (1 << idx)) != 0; } auto& stage0 = key.Stages[0].Contents; diff --git a/src/d3d9/d3d9_fixed_function.cpp b/src/d3d9/d3d9_fixed_function.cpp index 0aff819a4..ca4f9266f 100644 --- a/src/d3d9/d3d9_fixed_function.cpp +++ b/src/d3d9/d3d9_fixed_function.cpp @@ -15,6 +15,7 @@ namespace dxvk { D3D9FixedFunctionOptions::D3D9FixedFunctionOptions(const D3D9Options* options) { invariantPosition = options->invariantPosition; forceSampleRateShading = options->forceSampleRateShading; + drefScaling = options->drefScaling; } uint32_t DoFixedFunctionFog(D3D9ShaderSpecConstantManager& spec, SpirvModule& spvModule, const D3D9FogContext& fogCtx) { @@ -1791,6 +1792,11 @@ namespace dxvk { return coords; }; + auto ScalarReplicate = [&](uint32_t reg) { + std::array replicant = { reg, reg, reg, reg }; + return m_module.opCompositeConstruct(m_vec4Type, replicant.size(), replicant.data()); + }; + auto GetTexture = [&]() { if (!processedTexture) { SpirvImageOperands imageOperands; @@ -1834,10 +1840,23 @@ namespace dxvk { shouldProject = false; } - if (shouldProject) + if (unlikely(stage.SampleDref)) { + uint32_t component = 2; + uint32_t reference = m_module.opCompositeExtract(m_floatType, texcoord, 1, &component); + + // [D3D8] Scale Dref to [0..(2^N - 1)] for D24S8 and D16 if Dref scaling is enabled + if (m_options.drefScaling) { + uint32_t maxDref = m_module.constf32(1.0f / (float(1 << m_options.drefScaling) - 1.0f)); + reference = m_module.opFMul(m_floatType, reference, maxDref); + } + + texture = m_module.opImageSampleDrefImplicitLod(m_floatType, imageVarId, texcoord, reference, imageOperands); + texture = ScalarReplicate(texture); + } else if (shouldProject) { texture = m_module.opImageSampleProjImplicitLod(m_vec4Type, imageVarId, texcoord, imageOperands); - else + } else { texture = m_module.opImageSampleImplicitLod(m_vec4Type, imageVarId, texcoord, imageOperands); + } if (i != 0 && m_fsKey.Stages[i - 1].Contents.ColorOp == D3DTOP_BUMPENVMAPLUMINANCE) { uint32_t index = m_module.constu32(D3D9SharedPSStages_Count * (i - 1) + D3D9SharedPSStages_BumpEnvLScale); @@ -1865,11 +1884,6 @@ namespace dxvk { return texture; }; - auto ScalarReplicate = [&](uint32_t reg) { - std::array replicant = { reg, reg, reg, reg }; - return m_module.opCompositeConstruct(m_vec4Type, replicant.size(), replicant.data()); - }; - auto AlphaReplicate = [&](uint32_t reg) { uint32_t alphaComponentId = 3; uint32_t alpha = m_module.opCompositeExtract(m_floatType, reg, 1, &alphaComponentId); @@ -2266,6 +2280,11 @@ namespace dxvk { dimensionality = spv::Dim2D; sampler.texcoordCnt = 2; viewType = VK_IMAGE_VIEW_TYPE_2D; + + // Z coordinate for Dref sampling + if (m_fsKey.Stages[i].Contents.SampleDref) + sampler.texcoordCnt++; + break; case D3DRTYPE_CUBETEXTURE: dimensionality = spv::DimCube; diff --git a/src/d3d9/d3d9_fixed_function.h b/src/d3d9/d3d9_fixed_function.h index 0244755dc..b111051a2 100644 --- a/src/d3d9/d3d9_fixed_function.h +++ b/src/d3d9/d3d9_fixed_function.h @@ -48,10 +48,15 @@ namespace dxvk { struct D3D9FixedFunctionOptions { D3D9FixedFunctionOptions(const D3D9Options* options); - bool invariantPosition; - bool forceSampleRateShading; + bool invariantPosition; + bool forceSampleRateShading; + int32_t drefScaling; }; + constexpr float GetDrefScaleFactor(int32_t bitDepth) { + return 1.0f / (float(1 << bitDepth) - 1.0f); + } + // Returns new oFog if VS // Returns new oColor if PS uint32_t DoFixedFunctionFog(D3D9ShaderSpecConstantManager& spec, SpirvModule& spvModule, const D3D9FogContext& fogCtx); @@ -163,6 +168,7 @@ namespace dxvk { uint32_t Projected : 1; uint32_t ProjectedCount : 3; + uint32_t SampleDref : 1; uint32_t TextureBound : 1; diff --git a/src/d3d9/d3d9_options.cpp b/src/d3d9/d3d9_options.cpp index bda5c49f1..ffdb2675a 100644 --- a/src/d3d9/d3d9_options.cpp +++ b/src/d3d9/d3d9_options.cpp @@ -79,6 +79,9 @@ namespace dxvk { this->countLosableResources = config.getOption ("d3d9.countLosableResources", true); this->reproducibleCommandStream = config.getOption ("d3d9.reproducibleCommandStream", false); + // D3D8 options + this->drefScaling = config.getOption ("d3d8.scaleDref", 0); + // Clamp LOD bias so that people don't abuse this in unintended ways this->samplerLodBias = dxvk::fclamp(this->samplerLodBias, -2.0f, 1.0f); diff --git a/src/d3d9/d3d9_options.h b/src/d3d9/d3d9_options.h index 824ba076c..0bc298663 100644 --- a/src/d3d9/d3d9_options.h +++ b/src/d3d9/d3d9_options.h @@ -160,6 +160,9 @@ namespace dxvk { /// don't change between runs. Useful for comparative benchmarking, /// can negatively affect performance. bool reproducibleCommandStream; + + /// Enable depth texcoord Z (Dref) scaling (D3D8 quirk) + int32_t drefScaling; }; } diff --git a/src/dxso/dxso_compiler.cpp b/src/dxso/dxso_compiler.cpp index 97bece7d9..669caa2a4 100644 --- a/src/dxso/dxso_compiler.cpp +++ b/src/dxso/dxso_compiler.cpp @@ -2909,6 +2909,14 @@ void DxsoCompiler::emitControlFlowGenericLoop( uint32_t component = sampler.dimensions; reference = m_module.opCompositeExtract( fType, texcoordVar.id, 1, &component); + + // [D3D8] Scale Dref from [0..(2^N - 1)] for D24S8 and D16 if Dref scaling is enabled + if (m_moduleInfo.options.drefScaling) { + uint32_t drefScale = m_module.constf32(GetDrefScaleFactor(m_moduleInfo.options.drefScaling)); + reference = m_module.opFMul(fType, reference, drefScale); + } + + // Clamp Dref to [0..1] for D32F emulating UNORM textures uint32_t clampDref = m_spec.get(m_module, m_specUbo, SpecDrefClamp, samplerIdx, 1); clampDref = m_module.opINotEqual(bool_t, clampDref, m_module.constu32(0)); uint32_t clampedDref = m_module.opFClamp(fType, reference, m_module.constf32(0.0f), m_module.constf32(1.0f)); diff --git a/src/dxso/dxso_options.cpp b/src/dxso/dxso_options.cpp index d9f7834ed..ffe627137 100644 --- a/src/dxso/dxso_options.cpp +++ b/src/dxso/dxso_options.cpp @@ -31,6 +31,8 @@ namespace dxvk { longMad = options.longMad; robustness2Supported = devFeatures.extRobustness2.robustBufferAccess2; + + drefScaling = options.drefScaling; } } \ No newline at end of file diff --git a/src/dxso/dxso_options.h b/src/dxso/dxso_options.h index cd0ab8e11..04518412c 100644 --- a/src/dxso/dxso_options.h +++ b/src/dxso/dxso_options.h @@ -49,6 +49,12 @@ namespace dxvk { /// Whether or not we can rely on robustness2 to handle oob constant access bool robustness2Supported; + + /// Whether runtime to apply Dref scaling for depth textures of specified bit depth + /// (24: D24S8, 16: D16, 0: Disabled). This allows compatability with games + /// that expect a different depth test range, which was typically a D3D8 quirk on + /// early NVIDIA hardware. + int32_t drefScaling = 0; }; } \ No newline at end of file From 22b13a94cadb0cd1a82035d6d57a3228d68eb1ed Mon Sep 17 00:00:00 2001 From: Blisto91 <47954800+Blisto91@users.noreply.github.com> Date: Sat, 15 Jun 2024 18:05:49 +0200 Subject: [PATCH 194/246] [util] Enable deviceLossOnFocusLoss for The Force Unleased Prevents the game from black screening on each alt-tab --- src/util/config/config.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index 7eb6a5295..956430e8a 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -933,6 +933,11 @@ namespace dxvk { { R"(\\prototypef\.exe$)", {{ { "d3d9.supportDFFormats", "False" }, }} }, + /* STAR WARS: The Force Unleashed * + * Prevents black screen on each alt-tab */ + { R"(\\SWTFU\.exe$)", {{ + { "d3d9.deviceLossOnFocusLoss", "True" }, + }} }, /**********************************************/ From d9994665a85407af9107f9fa50667ac191597cad Mon Sep 17 00:00:00 2001 From: Blisto91 <47954800+Blisto91@users.noreply.github.com> Date: Mon, 17 Jun 2024 22:40:39 +0200 Subject: [PATCH 195/246] [util] Set strict floatEmulation for New Vegas Fixes various visual issues with some effects in mods such as New Vegas Reloaded. White spots/shapes, black streaks across the screen (rain?) and probably more. --- src/util/config/config.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index 956430e8a..f4c1d9700 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -938,6 +938,11 @@ namespace dxvk { { R"(\\SWTFU\.exe$)", {{ { "d3d9.deviceLossOnFocusLoss", "True" }, }} }, + /* Fallout New Vegas - Various visual issues * + * with mods such as New Vegas Reloaded */ + { R"(\\FalloutNV\.exe$)", {{ + { "d3d9.floatEmulation", "Strict" }, + }} }, /**********************************************/ From 4b4d323ec36c0593135f26dd7dcb035d14653ae9 Mon Sep 17 00:00:00 2001 From: Blisto91 <47954800+Blisto91@users.noreply.github.com> Date: Mon, 17 Jun 2024 22:43:56 +0200 Subject: [PATCH 196/246] [util] Set cachedDynamicBuffers for Dragonshard Works around massive FPS decreases in some scenes --- src/util/config/config.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index f4c1d9700..efbaf304c 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -943,6 +943,11 @@ namespace dxvk { { R"(\\FalloutNV\.exe$)", {{ { "d3d9.floatEmulation", "Strict" }, }} }, + /* Dungeons and Dragons: Dragonshard * + * Massive FPS decreases in some scenes */ + { R"(\\Dragonshard\.exe$)", {{ + { "d3d9.cachedDynamicBuffers", "True" }, + }} }, /**********************************************/ From a276e138212da6460e19364a28b77ee577765202 Mon Sep 17 00:00:00 2001 From: Blisto91 <47954800+Blisto91@users.noreply.github.com> Date: Wed, 19 Jun 2024 13:34:21 +0200 Subject: [PATCH 197/246] [util] Set deviceLossOnFocusLoss for Guild Wars Works around the game black screening on alt-tab when it is set to fullscreen with a non native resolution --- src/util/config/config.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index efbaf304c..756af0531 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -948,6 +948,11 @@ namespace dxvk { { R"(\\Dragonshard\.exe$)", {{ { "d3d9.cachedDynamicBuffers", "True" }, }} }, + /* Guild Wars 1 - Alt-tab black screen when * + * fullscreen with non native resolution */ + { R"(\\Gw\.exe$)", {{ + { "d3d9.deviceLossOnFocusLoss", "True" }, + }} }, /**********************************************/ From e9fc071d9548a6e865f09b9c03f9349ef0aa5c1e Mon Sep 17 00:00:00 2001 From: WinterSnowfall Date: Thu, 20 Jun 2024 00:57:47 +0300 Subject: [PATCH 198/246] [d3d9] Validate D3DCREATE_PUREDEVICE usage --- src/d3d9/d3d9_interface.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/d3d9/d3d9_interface.cpp b/src/d3d9/d3d9_interface.cpp index 8e6b12d2a..be302bced 100644 --- a/src/d3d9/d3d9_interface.cpp +++ b/src/d3d9/d3d9_interface.cpp @@ -354,6 +354,12 @@ namespace dxvk { || pPresentationParameters == nullptr) return D3DERR_INVALIDCALL; + // creating a device with D3DCREATE_PUREDEVICE only works in conjunction + // with D3DCREATE_HARDWARE_VERTEXPROCESSING on native drivers + if (BehaviorFlags & D3DCREATE_PUREDEVICE && + !(BehaviorFlags & D3DCREATE_HARDWARE_VERTEXPROCESSING)) + return D3DERR_INVALIDCALL; + auto* adapter = GetAdapter(Adapter); if (adapter == nullptr) From 3145020a62895548881dbb1886d46f191191124a Mon Sep 17 00:00:00 2001 From: Robin Kertels Date: Fri, 21 Jun 2024 12:34:08 +0200 Subject: [PATCH 199/246] [d3d9] Only change vertex offset draw parameters when necessary ... Only change it when the vertex data is actually dynamically uploaded. --- src/d3d9/d3d9_device.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/d3d9/d3d9_device.cpp b/src/d3d9/d3d9_device.cpp index 776dedb56..023322ef6 100644 --- a/src/d3d9/d3d9_device.cpp +++ b/src/d3d9/d3d9_device.cpp @@ -5307,11 +5307,13 @@ namespace dxvk { m_flags.set(D3D9DeviceFlag::DirtyVertexBuffers); } - // Change the draw call parameters to reflect the changed vertex buffers - if (NumIndices != 0) { - BaseVertexIndex = -FirstVertexIndex; - } else { - FirstVertexIndex = 0; + if (dynamicSysmemVBOs) { + // Change the draw call parameters to reflect the changed vertex buffers + if (NumIndices != 0) { + BaseVertexIndex = -FirstVertexIndex; + } else { + FirstVertexIndex = 0; + } } if (dynamicSysmemIBO) { From fe0e02de18db7344073b54caa5a60d538875fa41 Mon Sep 17 00:00:00 2001 From: Blisto91 Date: Sat, 22 Jun 2024 21:41:17 +0200 Subject: [PATCH 200/246] [util] Set cachedDynamicBuffers for Battle for Middle-Earth 2 Helps slowdowns below 30fps in certain scenarios like when moving the camera over heavy vegetation areas among others --- src/util/config/config.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index 756af0531..183173cc8 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -953,6 +953,11 @@ namespace dxvk { { R"(\\Gw\.exe$)", {{ { "d3d9.deviceLossOnFocusLoss", "True" }, }} }, + /* Battle for Middle-earth 2 and expansion * + * Slowdowns in certain scenarios */ + { R"(\\(The Battle for Middle-earth (\(tm\))? II( Demo)?|The Lord of the Rings, The Rise of the Witch-king)\\game\.dat$)", {{ + { "d3d9.cachedDynamicBuffers", "True" }, + }} }, /**********************************************/ From 8573190c7dbda89528ec6ae5f6b650909c4d4151 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Mon, 24 Jun 2024 18:12:59 +0200 Subject: [PATCH 201/246] [dxvk] Apply frame rate limiter before signaling frame latency event Otherwise we add latency for no reason. --- src/dxvk/dxvk_presenter.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/dxvk/dxvk_presenter.cpp b/src/dxvk/dxvk_presenter.cpp index efd3d7272..17a406e51 100644 --- a/src/dxvk/dxvk_presenter.cpp +++ b/src/dxvk/dxvk_presenter.cpp @@ -678,12 +678,14 @@ namespace dxvk { Logger::err(str::format("Presenter: vkWaitForPresentKHR failed: ", vr)); } + // Apply FPS limtier here to align it as closely with scanout as we can, + // and delay signaling the frame latency event to emulate behaviour of a + // low refresh rate display as closely as we can. + m_fpsLimiter.delay(); + // Always signal even on error, since failures here // are transparent to the front-end. m_signal->signal(frame.frameId); - - // Apply FPS limtier here to align it as closely with scanout as we can. - m_fpsLimiter.delay(); } } From 86f04a2da3256cfc115bee3d7968a4ad62cd1533 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Wed, 26 Jun 2024 00:42:31 +0200 Subject: [PATCH 202/246] Revert "[util] Consolidate maxFrameRate options" This reverts commit 1811f4b9950818ba5c8bfbaf06776831ea2cd0cb. The D3D12 Beyond Good and Evil remaster uses the same exe name as the original D3D9 game, so it turns out the separation was useful after all. --- dxvk.conf | 3 ++- src/d3d9/d3d9_options.cpp | 2 +- src/dxgi/dxgi_options.cpp | 2 +- src/util/config/config.cpp | 42 +++++++++++++++++++------------------- 4 files changed, 25 insertions(+), 24 deletions(-) diff --git a/dxvk.conf b/dxvk.conf index 472a1904e..10c95d442 100644 --- a/dxvk.conf +++ b/dxvk.conf @@ -50,7 +50,8 @@ # actual display mode does not match the game's one. # n: Limit to n frames per second. -# dxvk.maxFrameRate = 0 +# dxgi.maxFrameRate = 0 +# d3d9.maxFrameRate = 0 # Override PCI vendor and device IDs reported to the application. Can diff --git a/src/d3d9/d3d9_options.cpp b/src/d3d9/d3d9_options.cpp index ffdb2675a..6988b711e 100644 --- a/src/d3d9/d3d9_options.cpp +++ b/src/d3d9/d3d9_options.cpp @@ -42,7 +42,7 @@ namespace dxvk { : (adapter != nullptr ? adapter->deviceProperties().vendorID : 0); this->maxFrameLatency = config.getOption ("d3d9.maxFrameLatency", 0); - this->maxFrameRate = config.getOption ("dxvk.maxFrameRate", 0); + this->maxFrameRate = config.getOption ("d3d9.maxFrameRate", 0); this->presentInterval = config.getOption ("d3d9.presentInterval", -1); this->shaderModel = config.getOption ("d3d9.shaderModel", 3); this->dpiAware = config.getOption ("d3d9.dpiAware", true); diff --git a/src/dxgi/dxgi_options.cpp b/src/dxgi/dxgi_options.cpp index 994deceda..f352dfdbf 100644 --- a/src/dxgi/dxgi_options.cpp +++ b/src/dxgi/dxgi_options.cpp @@ -93,7 +93,7 @@ namespace dxvk { this->maxDeviceMemory = VkDeviceSize(config.getOption("dxgi.maxDeviceMemory", 0)) << 20; this->maxSharedMemory = VkDeviceSize(config.getOption("dxgi.maxSharedMemory", 0)) << 20; - this->maxFrameRate = config.getOption("dxvk.maxFrameRate", 0); + this->maxFrameRate = config.getOption("dxgi.maxFrameRate", 0); this->syncInterval = config.getOption("dxgi.syncInterval", -1); // Expose Nvidia GPUs properly if NvAPI is enabled in environment diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index 183173cc8..cf413f64d 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -314,12 +314,12 @@ namespace dxvk { /* Shantae and the Pirate's Curse * * Game speeds up above 60 fps */ { R"(\\ShantaeCurse\.exe$)", {{ - { "dxvk.maxFrameRate", "60" }, + { "dxgi.maxFrameRate", "60" }, }} }, /* Mighty Switch Force! Collection * * Games speed up above 60 fps */ { R"(\\MSFC\.exe$)", {{ - { "dxvk.maxFrameRate", "60" }, + { "dxgi.maxFrameRate", "60" }, }} }, /* Cardfight!! Vanguard Dear Days: * * Submits command lists multiple times */ @@ -405,7 +405,7 @@ namespace dxvk { * Game speed increases when above 60 fps in * * the tavern area */ { R"(\\BLADESTORM Nightmare\\Launch_(EA|JP)\.exe$)", {{ - { "dxvk.maxFrameRate", "60" }, + { "dxgi.maxFrameRate", "60" }, }} }, /* Ghost Recon Wildlands */ { R"(\\GRW\.exe$)", {{ @@ -486,7 +486,7 @@ namespace dxvk { /* Sonic Adventure 2 */ { R"(\\Sonic Adventure 2\\(launcher|sonic2app)\.exe$)", {{ { "d3d9.floatEmulation", "Strict" }, - { "dxvk.maxFrameRate", "60" }, + { "d3d9.maxFrameRate", "60" }, }} }, /* The Sims 2, Body Shop, @@ -508,7 +508,7 @@ namespace dxvk { Built-in Vsync Locks the game to 30 FPS */ { R"(\\Dead Space\.exe$)", {{ { "d3d9.supportDFFormats", "False" }, - { "dxvk.maxFrameRate", "60" }, + { "d3d9.maxFrameRate", "60" }, { "d3d9.presentInterval", "1" }, }} }, /* Dead Space 2 @@ -599,7 +599,7 @@ namespace dxvk { * D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY buffer */ { R"(\\(trl|tra|tru)\.exe$)", {{ { "d3d9.cachedDynamicBuffers", "True" }, - { "dxvk.maxFrameRate", "60" }, + { "d3d9.maxFrameRate", "60" }, }} }, /* Everquest */ { R"(\\eqgame\.exe$)", {{ @@ -637,7 +637,7 @@ namespace dxvk { }} }, /* Demon Stone breaks at frame rates > 60fps */ { R"(\\Demonstone\.exe$)", {{ - { "dxvk.maxFrameRate", "60" }, + { "d3d9.maxFrameRate", "60" }, }} }, /* Far Cry 1 has worse water rendering when it detects AMD GPUs */ { R"(\\FarCry\.exe$)", {{ @@ -650,7 +650,7 @@ namespace dxvk { }} }, /* Sine Mora EX */ { R"(\\SineMoraEX\.exe$)", {{ - { "dxvk.maxFrameRate", "60" }, + { "d3d9.maxFrameRate", "60" }, }} }, /* Red Orchestra 2 */ { R"(\\ROGame\.exe$)", {{ @@ -687,7 +687,7 @@ namespace dxvk { }} }, /* Limbo */ { R"(\\limbo\.exe$)", {{ - { "dxvk.maxFrameRate", "60" }, + { "d3d9.maxFrameRate", "60" }, }} }, /* Escape from Tarkov launcher Same issue as Warhammer: RoR above */ @@ -731,7 +731,7 @@ namespace dxvk { /* Bionic Commando Physics break at high fps */ { R"(\\bionic_commando\.exe$)", {{ - { "dxvk.maxFrameRate", "60" }, + { "d3d9.maxFrameRate", "60" }, }} }, /* Need For Speed 3 modern patch */ { R"(\\nfs3\.exe$)", {{ @@ -740,12 +740,12 @@ namespace dxvk { /* Beyond Good And Evil * * UI breaks at high fps */ { R"(\\BGE\.exe$)", {{ - { "dxvk.maxFrameRate", "60" }, + { "d3d9.maxFrameRate", "60" }, }} }, /* King Of Fighters XIII * * In-game speed increases on high FPS */ { R"(\\kof(xiii|13_win32_Release)\.exe$)", {{ - { "dxvk.maxFrameRate", "60" }, + { "d3d9.maxFrameRate", "60" }, }} }, /* YS Origin * * Helps very bad frametimes in some areas */ @@ -822,7 +822,7 @@ namespace dxvk { /* Battle Fantasia Revised Edition * * Speedup above 60fps */ { R"(\\bf10\.exe$)", {{ - { "dxvk.maxFrameRate", "60" }, + { "d3d9.maxFrameRate", "60" }, }} }, /* Codename Panzers Phase One/Two * * Main menu won't render after intros * @@ -849,23 +849,23 @@ namespace dxvk { }} }, /* STEINS;GATE ELITE */ { R"(\\SG_ELITE\\Game\.exe$)", {{ - { "dxvk.maxFrameRate", "60" }, + { "d3d9.maxFrameRate", "60" }, }} }, /* The Incredibles */ { R"(\\IncPC\.exe$)", {{ - { "dxvk.maxFrameRate", "59" }, + { "d3d9.maxFrameRate", "59" }, }} }, /* Conflict Vietnam */ { R"(\\Vietnam\.exe$)", {{ - { "dxvk.maxFrameRate", "60" }, + { "d3d9.maxFrameRate", "60" }, }} }, /* Project: Snowblind */ { R"(\\Snowblind\.(SP|MP|exe)$)", {{ - { "dxvk.maxFrameRate", "60" }, + { "d3d9.maxFrameRate", "60" }, }} }, /* Aviary Attorney */ { R"(\\Aviary Attorney\\nw\.exe$)", {{ - { "dxvk.maxFrameRate", "60" }, + { "d3d9.maxFrameRate", "60" }, }} }, /* Drakensang: The Dark Eye */ { R"(\\drakensang\.exe$)", {{ @@ -891,7 +891,7 @@ namespace dxvk { }} }, /* Sonic CD */ { R"(\\soniccd\.exe$)", {{ - { "dxvk.maxFrameRate", "60" }, + { "d3d9.maxFrameRate", "60" }, }} }, /* UK Truck Simulator 1 */ { R"(\\UK Truck Simulator\\bin\\win_x86\\game\.exe$)", {{ @@ -910,7 +910,7 @@ namespace dxvk { }} }, /* Dark Void - Crashes above 60fps in places */ { R"(\\ShippingPC-SkyGame\.exe$)", {{ - { "dxvk.maxFrameRate", "60" }, + { "d3d9.maxFrameRate", "60" }, }} }, /* 9th Dawn II * * OpenGL game that also spins up d3d9 * @@ -1000,7 +1000,7 @@ namespace dxvk { /* Fallout 4: Defaults to 45 FPS on OLED, but also breaks above 60 FPS */ { R"(\\Fallout4\.exe$)", {{ { "dxgi.syncInterval", "1" }, - { "dxvk.maxFrameRate", "60" }, + { "dxgi.maxFrameRate", "60" }, }} }, }}; From afbcd945691f11136a19fb9f2d323f0e7bf0d758 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Wed, 26 Jun 2024 00:44:43 +0200 Subject: [PATCH 203/246] [util] Fix up recently added app profiles --- src/util/config/config.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index cf413f64d..8d575e40c 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -454,7 +454,8 @@ namespace dxvk { /* Crysis 1/Warhead - Game bug in d3d10 makes * * it select lowest supported refresh rate */ { R"(\\Crysis(64)?\.exe$)", {{ - { "dxvk.maxFrameRate", "-1" }, + { "d3d9.maxFrameRate", "-1" }, + { "dxgi.maxFrameRate", "-1" }, }} }, /**********************************************/ @@ -516,7 +517,7 @@ namespace dxvk { Built-in Vsync Locks the game to 30 FPS */ { R"(\\deadspace2\.exe$)", {{ - { "dxvk.maxFrameRate", "60" }, + { "d3d9.maxFrameRate", "60" }, { "d3d9.presentInterval", "1" }, }} }, /* Halo CE/HaloPC */ From 499460184af5fb51a9f81dab413dbd7a7c505dfa Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Wed, 3 Jul 2024 14:57:30 +0200 Subject: [PATCH 204/246] [build] Zero-pad soversion components as necessary --- meson.build | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/meson.build b/meson.build index cfa841a6a..c6294126d 100644 --- a/meson.build +++ b/meson.build @@ -122,7 +122,15 @@ if platform == 'windows' compiler_args += ['-DDXVK_WSI_WIN32'] else dxvk_abi_version = '0' - dxvk_version = meson.project_version().strip('v').split('.') + dxvk_version_raw = meson.project_version().strip('v').split('.') + dxvk_version = [ dxvk_version_raw[0] ] + foreach i : [ 1, 2 ] + padded = dxvk_version_raw[i] + if padded.to_int() < 10 + padded = '0' + padded + endif + dxvk_version += [ padded ] + endforeach dxvk_so_version = {'version': dxvk_abi_version + '.' + dxvk_version[0] + dxvk_version[1] + dxvk_version[2]} wrc = find_program('touch') From 1783b9591af787bc41dcdf8994158f67bb00fae5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Zaj=C4=85c?= <4561094+ToRRent1812@users.noreply.github.com> Date: Thu, 4 Jul 2024 20:12:08 +0200 Subject: [PATCH 205/246] [util] Enable 60 FPS lock for WRC4 Fixes an audio issue. --- src/util/config/config.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index 8d575e40c..7d2837046 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -959,6 +959,10 @@ namespace dxvk { { R"(\\(The Battle for Middle-earth (\(tm\))? II( Demo)?|The Lord of the Rings, The Rise of the Witch-king)\\game\.dat$)", {{ { "d3d9.cachedDynamicBuffers", "True" }, }} }, + /* WRC4 - Audio breaks above 60fps */ + { R"(\\WRC4\.exe$)", {{ + { "d3d9.maxFrameRate", "60" }, + }} }, /**********************************************/ From 18035820dea6c3e6a2ee16f48e20f32d10f71c96 Mon Sep 17 00:00:00 2001 From: WinterSnowfall Date: Sun, 23 Jun 2024 03:27:04 +0300 Subject: [PATCH 206/246] [d3d9] Remove vestigial D32 support --- dxvk.conf | 9 --------- src/d3d9/d3d9_format.cpp | 4 ---- src/d3d9/d3d9_format.h | 1 - src/d3d9/d3d9_options.cpp | 1 - src/d3d9/d3d9_options.h | 3 --- src/util/config/config.cpp | 4 ---- 6 files changed, 22 deletions(-) diff --git a/dxvk.conf b/dxvk.conf index 10c95d442..5d552adf8 100644 --- a/dxvk.conf +++ b/dxvk.conf @@ -570,15 +570,6 @@ # d3d9.supportX4R4G4B4 = True -# Support D32 -# -# Support the D32 format. -# -# Supported values: -# - True/False - -# d3d9.supportD32 = True - # Disable A8 as a Render Target # # Disable support for A8 format render targets diff --git a/src/d3d9/d3d9_format.cpp b/src/d3d9/d3d9_format.cpp index 87431a874..5ba1985ca 100644 --- a/src/d3d9/d3d9_format.cpp +++ b/src/d3d9/d3d9_format.cpp @@ -440,7 +440,6 @@ namespace dxvk { const D3D9Options& options) { m_dfSupport = options.supportDFFormats; m_x4r4g4b4Support = options.supportX4R4G4B4; - m_d32supportFinal = options.supportD32; // AMD do not support 24-bit depth buffers on Vulkan, // so we have to fall back to a 32-bit depth format. @@ -478,9 +477,6 @@ namespace dxvk { if (Format == D3D9Format::DF24 && !m_dfSupport) return D3D9_VK_FORMAT_MAPPING(); - - if (Format == D3D9Format::D32 && !m_d32supportFinal) - return D3D9_VK_FORMAT_MAPPING(); if (!m_d24s8Support && mapping.FormatColor == VK_FORMAT_D24_UNORM_S8_UINT) mapping.FormatColor = mapping.Aspect & VK_IMAGE_ASPECT_STENCIL_BIT ? VK_FORMAT_D32_SFLOAT_S8_UINT : VK_FORMAT_D32_SFLOAT; diff --git a/src/d3d9/d3d9_format.h b/src/d3d9/d3d9_format.h index 6054717df..beff7f67a 100644 --- a/src/d3d9/d3d9_format.h +++ b/src/d3d9/d3d9_format.h @@ -217,7 +217,6 @@ namespace dxvk { bool m_dfSupport; bool m_x4r4g4b4Support; - bool m_d32supportFinal; }; inline bool IsFourCCFormat(D3D9Format format) { diff --git a/src/d3d9/d3d9_options.cpp b/src/d3d9/d3d9_options.cpp index 6988b711e..4e2bfc35c 100644 --- a/src/d3d9/d3d9_options.cpp +++ b/src/d3d9/d3d9_options.cpp @@ -55,7 +55,6 @@ namespace dxvk { this->maxAvailableMemory = config.getOption ("d3d9.maxAvailableMemory", 4096); this->supportDFFormats = config.getOption ("d3d9.supportDFFormats", vendorId != uint32_t(DxvkGpuVendor::Nvidia)); this->supportX4R4G4B4 = config.getOption ("d3d9.supportX4R4G4B4", true); - this->supportD32 = config.getOption ("d3d9.supportD32", true); this->useD32forD24 = config.getOption ("d3d9.useD32forD24", false); this->disableA8RT = config.getOption ("d3d9.disableA8RT", false); this->invariantPosition = config.getOption ("d3d9.invariantPosition", true); diff --git a/src/d3d9/d3d9_options.h b/src/d3d9/d3d9_options.h index 0bc298663..3ba4e3406 100644 --- a/src/d3d9/d3d9_options.h +++ b/src/d3d9/d3d9_options.h @@ -78,9 +78,6 @@ namespace dxvk { /// Support X4R4G4B4 bool supportX4R4G4B4; - /// Support D32 - bool supportD32; - /// Use D32f for D24 bool useD32forD24; diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index 7d2837046..43c354c54 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -555,10 +555,6 @@ namespace dxvk { { R"(\\SKShinoviVersus\.exe$)", {{ { "d3d9.forceAspectRatio", "16:9" }, }} }, - /* Metal Slug X */ - { R"(\\mslugx\.exe$)", {{ - { "d3d9.supportD32", "False" }, - }} }, /* Skyrim (NVAPI) */ { R"(\\TESV\.exe$)", {{ { "d3d9.customVendorId", "1002" }, From b03de97f1b6058723f8b4978ab023b22111621b7 Mon Sep 17 00:00:00 2001 From: WinterSnowfall Date: Sun, 23 Jun 2024 03:43:14 +0300 Subject: [PATCH 207/246] [d3d9] Expose support for D16_LOCKABLE only on AMD --- dxvk.conf | 11 +++++++++++ src/d3d9/d3d9_adapter.cpp | 4 ++-- src/d3d9/d3d9_format.cpp | 9 +++++++++ src/d3d9/d3d9_format.h | 1 + src/d3d9/d3d9_options.cpp | 1 + src/d3d9/d3d9_options.h | 3 +++ 6 files changed, 27 insertions(+), 2 deletions(-) diff --git a/dxvk.conf b/dxvk.conf index 5d552adf8..453024feb 100644 --- a/dxvk.conf +++ b/dxvk.conf @@ -570,6 +570,17 @@ # d3d9.supportX4R4G4B4 = True +# Support D16_LOCKABLE +# +# Support the D16_LOCKABLE format. +# Always enabled on AMD, or when spoofing an AMD GPU +# via customVendorId, disabled by default on Nvidia and Intel. +# +# Supported values: +# - True/False + +# d3d9.supportD16Lockable = False + # Disable A8 as a Render Target # # Disable support for A8 format render targets diff --git a/src/d3d9/d3d9_adapter.cpp b/src/d3d9/d3d9_adapter.cpp index d320dc6b2..d1fa49c47 100644 --- a/src/d3d9/d3d9_adapter.cpp +++ b/src/d3d9/d3d9_adapter.cpp @@ -227,14 +227,14 @@ namespace dxvk { if (!IsDepthFormat(DepthStencilFormat)) return D3DERR_NOTAVAILABLE; - auto dsfMapping = ConvertFormatUnfixed(DepthStencilFormat); + auto dsfMapping = GetFormatMapping(DepthStencilFormat); if (dsfMapping.FormatColor == VK_FORMAT_UNDEFINED) return D3DERR_NOTAVAILABLE; if (RenderTargetFormat == dxvk::D3D9Format::NULL_FORMAT) return D3D_OK; - auto rtfMapping = ConvertFormatUnfixed(RenderTargetFormat); + auto rtfMapping = GetFormatMapping(RenderTargetFormat); if (rtfMapping.FormatColor == VK_FORMAT_UNDEFINED) return D3DERR_NOTAVAILABLE; diff --git a/src/d3d9/d3d9_format.cpp b/src/d3d9/d3d9_format.cpp index 5ba1985ca..63a00d522 100644 --- a/src/d3d9/d3d9_format.cpp +++ b/src/d3d9/d3d9_format.cpp @@ -438,8 +438,14 @@ namespace dxvk { D3D9VkFormatTable::D3D9VkFormatTable( const Rc& adapter, const D3D9Options& options) { + + const auto& props = adapter->deviceProperties(); + uint32_t vendorId = options.customVendorId == -1 ? props.vendorID : uint32_t(options.customVendorId); + m_dfSupport = options.supportDFFormats; m_x4r4g4b4Support = options.supportX4R4G4B4; + // Only AMD supports D16_LOCKABLE natively + m_d16lockableSupport = vendorId == uint32_t(DxvkGpuVendor::Amd) ? true : options.supportD16Lockable; // AMD do not support 24-bit depth buffers on Vulkan, // so we have to fall back to a 32-bit depth format. @@ -472,6 +478,9 @@ namespace dxvk { if (Format == D3D9Format::X4R4G4B4 && !m_x4r4g4b4Support) return D3D9_VK_FORMAT_MAPPING(); + if (Format == D3D9Format::D16_LOCKABLE && !m_d16lockableSupport) + return D3D9_VK_FORMAT_MAPPING(); + if (Format == D3D9Format::DF16 && !m_dfSupport) return D3D9_VK_FORMAT_MAPPING(); diff --git a/src/d3d9/d3d9_format.h b/src/d3d9/d3d9_format.h index beff7f67a..a8927a9c1 100644 --- a/src/d3d9/d3d9_format.h +++ b/src/d3d9/d3d9_format.h @@ -217,6 +217,7 @@ namespace dxvk { bool m_dfSupport; bool m_x4r4g4b4Support; + bool m_d16lockableSupport; }; inline bool IsFourCCFormat(D3D9Format format) { diff --git a/src/d3d9/d3d9_options.cpp b/src/d3d9/d3d9_options.cpp index 4e2bfc35c..3221c29c8 100644 --- a/src/d3d9/d3d9_options.cpp +++ b/src/d3d9/d3d9_options.cpp @@ -55,6 +55,7 @@ namespace dxvk { this->maxAvailableMemory = config.getOption ("d3d9.maxAvailableMemory", 4096); this->supportDFFormats = config.getOption ("d3d9.supportDFFormats", vendorId != uint32_t(DxvkGpuVendor::Nvidia)); this->supportX4R4G4B4 = config.getOption ("d3d9.supportX4R4G4B4", true); + this->supportD16Lockable = config.getOption ("d3d9.supportD16Lockable", false); this->useD32forD24 = config.getOption ("d3d9.useD32forD24", false); this->disableA8RT = config.getOption ("d3d9.disableA8RT", false); this->invariantPosition = config.getOption ("d3d9.invariantPosition", true); diff --git a/src/d3d9/d3d9_options.h b/src/d3d9/d3d9_options.h index 3ba4e3406..6ab697ed3 100644 --- a/src/d3d9/d3d9_options.h +++ b/src/d3d9/d3d9_options.h @@ -78,6 +78,9 @@ namespace dxvk { /// Support X4R4G4B4 bool supportX4R4G4B4; + /// Support D16_LOCKABLE + bool supportD16Lockable; + /// Use D32f for D24 bool useD32forD24; From 5ae3cfe40200cb716ea78a2b02b8b4a11b1b3432 Mon Sep 17 00:00:00 2001 From: WinterSnowfall Date: Thu, 4 Jul 2024 11:24:25 +0300 Subject: [PATCH 208/246] [d3d9] Fix DF Formats selection logic --- src/d3d9/d3d9_format.cpp | 3 ++- src/d3d9/d3d9_options.cpp | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/d3d9/d3d9_format.cpp b/src/d3d9/d3d9_format.cpp index 63a00d522..92069d60a 100644 --- a/src/d3d9/d3d9_format.cpp +++ b/src/d3d9/d3d9_format.cpp @@ -442,7 +442,8 @@ namespace dxvk { const auto& props = adapter->deviceProperties(); uint32_t vendorId = options.customVendorId == -1 ? props.vendorID : uint32_t(options.customVendorId); - m_dfSupport = options.supportDFFormats; + // NVIDIA does not natively support any DF formats + m_dfSupport = vendorId == uint32_t(DxvkGpuVendor::Nvidia) ? false : options.supportDFFormats; m_x4r4g4b4Support = options.supportX4R4G4B4; // Only AMD supports D16_LOCKABLE natively m_d16lockableSupport = vendorId == uint32_t(DxvkGpuVendor::Amd) ? true : options.supportD16Lockable; diff --git a/src/d3d9/d3d9_options.cpp b/src/d3d9/d3d9_options.cpp index 3221c29c8..dbc46a769 100644 --- a/src/d3d9/d3d9_options.cpp +++ b/src/d3d9/d3d9_options.cpp @@ -53,7 +53,7 @@ namespace dxvk { this->deferSurfaceCreation = config.getOption ("d3d9.deferSurfaceCreation", false); this->samplerAnisotropy = config.getOption ("d3d9.samplerAnisotropy", -1); this->maxAvailableMemory = config.getOption ("d3d9.maxAvailableMemory", 4096); - this->supportDFFormats = config.getOption ("d3d9.supportDFFormats", vendorId != uint32_t(DxvkGpuVendor::Nvidia)); + this->supportDFFormats = config.getOption ("d3d9.supportDFFormats", true); this->supportX4R4G4B4 = config.getOption ("d3d9.supportX4R4G4B4", true); this->supportD16Lockable = config.getOption ("d3d9.supportD16Lockable", false); this->useD32forD24 = config.getOption ("d3d9.useD32forD24", false); From 175772944ca94a6cd001dad70a98cec4f1805907 Mon Sep 17 00:00:00 2001 From: Robin Kertels Date: Wed, 6 Mar 2024 23:39:08 +0100 Subject: [PATCH 209/246] [d3d9] Fix Wine test failures in StretchRect --- src/d3d9/d3d9_device.cpp | 44 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/src/d3d9/d3d9_device.cpp b/src/d3d9/d3d9_device.cpp index 023322ef6..563d78d16 100644 --- a/src/d3d9/d3d9_device.cpp +++ b/src/d3d9/d3d9_device.cpp @@ -1144,6 +1144,9 @@ namespace dxvk { if (unlikely((srcSubresource.aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) && m_flags.test(D3D9DeviceFlag::InScene))) return D3DERR_INVALIDCALL; + if (unlikely(Filter != D3DTEXF_NONE && Filter != D3DTEXF_LINEAR && Filter != D3DTEXF_POINT)) + return D3DERR_INVALIDCALL; + VkExtent3D srcExtent = srcImage->mipLevelExtent(srcSubresource.mipLevel); VkExtent3D dstExtent = dstImage->mipLevelExtent(dstSubresource.mipLevel); @@ -1222,8 +1225,49 @@ namespace dxvk { uint32_t(blitInfo.dstOffsets[1].y - blitInfo.dstOffsets[0].y), uint32_t(blitInfo.dstOffsets[1].z - blitInfo.dstOffsets[0].z) }; + bool srcIsDepth = IsDepthFormat(srcFormat); + bool dstIsDepth = IsDepthFormat(dstFormat); + if (unlikely(srcIsDepth || dstIsDepth)) { + if (unlikely(!srcIsDepth || !dstIsDepth)) + return D3DERR_INVALIDCALL; + + if (unlikely(srcTextureInfo->Desc()->Discard || dstTextureInfo->Desc()->Discard)) + return D3DERR_INVALIDCALL; + + if (unlikely(srcCopyExtent.width != srcExtent.width || srcCopyExtent.height != srcExtent.height)) + return D3DERR_INVALIDCALL; + + if (unlikely(m_flags.test(D3D9DeviceFlag::InScene))) + return D3DERR_INVALIDCALL; + } + // Copies would only work if the extents match. (ie. no stretching) bool stretch = srcCopyExtent != dstCopyExtent; + + bool dstHasRTUsage = (dstTextureInfo->Desc()->Usage & (D3DUSAGE_RENDERTARGET | D3DUSAGE_DEPTHSTENCIL)) != 0; + if (stretch) { + if (unlikely(pSourceSurface == pDestSurface)) + return D3DERR_INVALIDCALL; + + if (unlikely(dstIsDepth)) + return D3DERR_INVALIDCALL; + + // Stretching is only allowed if the destination is either a render target surface or a render target texture + if (unlikely(!dstHasRTUsage)) + return D3DERR_INVALIDCALL; + } else { + bool srcIsSurface = srcTextureInfo->GetType() == D3DRTYPE_SURFACE; + bool dstIsSurface = dstTextureInfo->GetType() == D3DRTYPE_SURFACE; + bool srcHasRTUsage = (srcTextureInfo->Desc()->Usage & (D3DUSAGE_RENDERTARGET | D3DUSAGE_DEPTHSTENCIL)) != 0; + // Non-stretching copies are only allowed if: + // - the destination is either a render target surface or a render target texture + // - both destination and source are depth stencil surfaces + // - both destination and source are offscreen plain surfaces. + // The only way to get a surface with resource type D3DRTYPE_SURFACE without USAGE_RT or USAGE_DS is CreateOffscreenPlainSurface. + if (unlikely(!dstHasRTUsage && (!dstIsSurface || !srcIsSurface || srcHasRTUsage))) + return D3DERR_INVALIDCALL; + } + fastPath &= !stretch; if (!fastPath || needsResolve) { From 9c898bd269cb3bfcf8e7e9c0b7c4acabe9505898 Mon Sep 17 00:00:00 2001 From: Robin Kertels Date: Thu, 7 Mar 2024 15:28:52 +0100 Subject: [PATCH 210/246] [d3d9] Skip presenting if D3D9Swapchain has no associated window --- src/d3d9/d3d9_swapchain.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/d3d9/d3d9_swapchain.cpp b/src/d3d9/d3d9_swapchain.cpp index 1d1c7c382..712a5b5e9 100644 --- a/src/d3d9/d3d9_swapchain.cpp +++ b/src/d3d9/d3d9_swapchain.cpp @@ -172,6 +172,9 @@ namespace dxvk { m_lastDialog = m_dialog; + if (m_window == nullptr) + return D3D_OK; + #ifdef _WIN32 const bool useGDIFallback = m_partialCopy && !HasFrontBuffer(); if (useGDIFallback) @@ -994,6 +997,9 @@ namespace dxvk { void D3D9SwapChainEx::UpdateWindowCtx() { + if (m_window == nullptr) + return; + if (!m_presenters.count(m_window)) { auto res = m_presenters.emplace( std::piecewise_construct, @@ -1334,10 +1340,10 @@ namespace dxvk { || dstRect.right - dstRect.left != LONG(width) || dstRect.bottom - dstRect.top != LONG(height); - bool recreate = - m_wctx->presenter == nullptr - || m_wctx->presenter->info().imageExtent.width != width - || m_wctx->presenter->info().imageExtent.height != height; + bool recreate = m_wctx != nullptr + && (m_wctx->presenter == nullptr + || m_wctx->presenter->info().imageExtent.width != width + || m_wctx->presenter->info().imageExtent.height != height); m_swapchainExtent = { width, height }; m_dstRect = dstRect; From 11db6d691c2ed4f80e9c7e23f13cfed9438d2ed2 Mon Sep 17 00:00:00 2001 From: Robin Kertels Date: Thu, 7 Mar 2024 19:17:54 +0100 Subject: [PATCH 211/246] [d3d9] Fix reference leak in ProcessVertices Also fixes a Wine test. --- src/d3d9/d3d9_device.cpp | 12 ++++++------ src/d3d9/d3d9_swvp_emu.cpp | 21 +++++++++++++-------- src/d3d9/d3d9_swvp_emu.h | 2 +- 3 files changed, 20 insertions(+), 15 deletions(-) diff --git a/src/d3d9/d3d9_device.cpp b/src/d3d9/d3d9_device.cpp index 563d78d16..583c39e9e 100644 --- a/src/d3d9/d3d9_device.cpp +++ b/src/d3d9/d3d9_device.cpp @@ -2954,13 +2954,13 @@ namespace dxvk { slice = slice.subSlice(offset, slice.length() - offset); EmitCs([this, - cDecl = ref(decl), - cVertexCount = VertexCount, - cStartIndex = SrcStartIndex, - cInstanceCount = GetInstanceCount(), - cBufferSlice = slice + cVertexElements = decl->GetElements(), + cVertexCount = VertexCount, + cStartIndex = SrcStartIndex, + cInstanceCount = GetInstanceCount(), + cBufferSlice = slice ](DxvkContext* ctx) mutable { - Rc shader = m_swvpEmulator.GetShaderModule(this, cDecl); + Rc shader = m_swvpEmulator.GetShaderModule(this, cVertexElements); auto drawInfo = GenerateDrawInfo(D3DPT_POINTLIST, cVertexCount, cInstanceCount); diff --git a/src/d3d9/d3d9_swvp_emu.cpp b/src/d3d9/d3d9_swvp_emu.cpp index 213bd2790..43e30a70a 100644 --- a/src/d3d9/d3d9_swvp_emu.cpp +++ b/src/d3d9/d3d9_swvp_emu.cpp @@ -109,7 +109,7 @@ namespace dxvk { m_module.opLabel(m_module.allocateId()); } - void compile(const D3D9VertexDecl* pDecl) { + void compile(const D3D9VertexElements& elements) { uint32_t uint_t = m_module.defIntType(32, false); uint32_t float_t = m_module.defFloatType(32); uint32_t vec4_t = m_module.defVectorType(float_t, 4); @@ -142,15 +142,22 @@ namespace dxvk { m_module.decorateBuiltIn(primitiveIdPtr, spv::BuiltInPrimitiveId); uint32_t primitiveId = m_module.opLoad(uint_t, primitiveIdPtr); - + // The size of any given vertex - uint32_t vertexSize = m_module.constu32(pDecl->GetSize(0) / sizeof(uint32_t)); + uint32_t size = 0; + for (const auto& element : elements) { + if (element.Stream == 0 && element.Type != D3DDECLTYPE_UNUSED) { + size = std::max(size, element.Offset + GetDecltypeSize(D3DDECLTYPE(element.Type))); + } + } + + uint32_t vertexSize = m_module.constu32(size / sizeof(uint32_t)); //The offset of this vertex from the beginning of the buffer uint32_t thisVertexOffset = m_module.opIMul(uint_t, vertexSize, primitiveId); - for (auto& element : pDecl->GetElements()) { + for (auto& element : elements) { // Load the slot associated with this element DxsoSemantic semantic = { DxsoUsage(element.Usage), element.UsageIndex }; @@ -297,9 +304,7 @@ namespace dxvk { }; - Rc D3D9SWVPEmulator::GetShaderModule(D3D9DeviceEx* pDevice, const D3D9VertexDecl* pDecl) { - auto& elements = pDecl->GetElements(); - + Rc D3D9SWVPEmulator::GetShaderModule(D3D9DeviceEx* pDevice, const D3D9VertexElements& elements) { // Use the shader's unique key for the lookup { std::unique_lock lock(m_mutex); @@ -317,7 +322,7 @@ namespace dxvk { // This shader has not been compiled yet, so we have to create a // new module. This takes a while, so we won't lock the structure. D3D9SWVPEmulatorGenerator generator(name); - generator.compile(pDecl); + generator.compile(elements); Rc shader = generator.finalize(); shader->setShaderKey(key); diff --git a/src/d3d9/d3d9_swvp_emu.h b/src/d3d9/d3d9_swvp_emu.h index 91aae4c25..c80823f0b 100644 --- a/src/d3d9/d3d9_swvp_emu.h +++ b/src/d3d9/d3d9_swvp_emu.h @@ -23,7 +23,7 @@ namespace dxvk { public: - Rc GetShaderModule(D3D9DeviceEx* pDevice, const D3D9VertexDecl* pDecl); + Rc GetShaderModule(D3D9DeviceEx* pDevice, const D3D9VertexElements& elements); private: From 9e422a2b638921cf79ab6a3f4dcbb6d91ebdaa0d Mon Sep 17 00:00:00 2001 From: Robin Kertels Date: Mon, 11 Mar 2024 17:33:02 +0100 Subject: [PATCH 212/246] [d3d9] Fix default light Fixes the diffuse alpha and the direction. --- src/d3d9/d3d9_state.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/d3d9/d3d9_state.h b/src/d3d9/d3d9_state.h index 8aeb23e30..2f2af56ad 100644 --- a/src/d3d9/d3d9_state.h +++ b/src/d3d9/d3d9_state.h @@ -169,11 +169,11 @@ namespace dxvk { constexpr D3DLIGHT9 DefaultLight = { D3DLIGHT_DIRECTIONAL, // Type - {1.0f, 1.0f, 1.0f, 1.0f}, // Diffuse + {1.0f, 1.0f, 1.0f, 0.0f}, // Diffuse {0.0f, 0.0f, 0.0f, 0.0f}, // Specular {0.0f, 0.0f, 0.0f, 0.0f}, // Ambient {0.0f, 0.0f, 0.0f}, // Position - {0.0f, 0.0f, 0.0f}, // Direction + {0.0f, 0.0f, 1.0f}, // Direction 0.0f, // Range 0.0f, // Falloff 0.0f, 0.0f, 0.0f, // Attenuations [constant, linear, quadratic] From df9bdfc6ea248200558f1f64961f558097e04b1a Mon Sep 17 00:00:00 2001 From: Robin Kertels Date: Mon, 11 Mar 2024 19:12:20 +0100 Subject: [PATCH 213/246] [util] Return unchanged matrix if matrix cannot be inverted --- src/util/util_matrix.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/util/util_matrix.cpp b/src/util/util_matrix.cpp index 2c5e93140..4b513d119 100644 --- a/src/util/util_matrix.cpp +++ b/src/util/util_matrix.cpp @@ -205,6 +205,10 @@ namespace dxvk { Vector4 dot0 = { m[0] * row0 }; float dot1 = (dot0.x + dot0.y) + (dot0.z + dot0.w); + if (unlikely(std::abs(dot1) <= 0.000001f)) { + return m; + } + return inverse * (1.0f / dot1); } From da814668bc3fbad0c332832a9759791735e9b0ca Mon Sep 17 00:00:00 2001 From: Robin Kertels Date: Tue, 12 Mar 2024 23:38:18 +0100 Subject: [PATCH 214/246] [d3d9] FF: Prevent specular highlights on the back of geometry Fixes the Wine test "test_specular_lighting". --- src/d3d9/d3d9_fixed_function.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/d3d9/d3d9_fixed_function.cpp b/src/d3d9/d3d9_fixed_function.cpp index ca4f9266f..da90604dc 100644 --- a/src/d3d9/d3d9_fixed_function.cpp +++ b/src/d3d9/d3d9_fixed_function.cpp @@ -1331,6 +1331,8 @@ namespace dxvk { uint32_t midDot = m_module.opDot(m_floatType, normal, mid); midDot = m_module.opFClamp(m_floatType, midDot, m_module.constf32(0.0f), m_module.constf32(1.0f)); uint32_t doSpec = m_module.opFOrdGreaterThan(bool_t, midDot, m_module.constf32(0.0f)); + doSpec = m_module.opLogicalAnd(bool_t, doSpec, m_module.opFOrdGreaterThan(m_floatType, hitDot, m_module.constf32(0.0f))); + uint32_t specularness = m_module.opPow(m_floatType, midDot, m_vs.constants.materialPower); specularness = m_module.opFMul(m_floatType, specularness, atten); specularness = m_module.opSelect(m_floatType, doSpec, specularness, m_module.constf32(0.0f)); From dc3dd0f88c07e374b10b9b683142d8fa07a9c585 Mon Sep 17 00:00:00 2001 From: Robin Kertels Date: Wed, 13 Mar 2024 01:25:16 +0100 Subject: [PATCH 215/246] [d3d9] Ignore the vertex stream offset in StateBlock::Capture Fixes a Wine test and matches further testing on Windows. --- src/d3d9/d3d9_stateblock.cpp | 26 ++++++++++++++++++++++---- src/d3d9/d3d9_stateblock.h | 31 ++++++++++++++++++++++--------- 2 files changed, 44 insertions(+), 13 deletions(-) diff --git a/src/d3d9/d3d9_stateblock.cpp b/src/d3d9/d3d9_stateblock.cpp index 0d5bacd98..fb2033cdc 100644 --- a/src/d3d9/d3d9_stateblock.cpp +++ b/src/d3d9/d3d9_stateblock.cpp @@ -49,7 +49,7 @@ namespace dxvk { if (m_captures.flags.test(D3D9CapturedStateFlag::VertexDecl)) SetVertexDeclaration(m_deviceState->vertexDecl.ptr()); - ApplyOrCapture(); + ApplyOrCapture(); return D3D_OK; } @@ -61,7 +61,7 @@ namespace dxvk { if (m_captures.flags.test(D3D9CapturedStateFlag::VertexDecl) && m_state.vertexDecl != nullptr) m_parent->SetVertexDeclaration(m_state.vertexDecl.ptr()); - ApplyOrCapture(); + ApplyOrCapture(); m_applying = false; return D3D_OK; @@ -122,6 +122,20 @@ namespace dxvk { } + HRESULT D3D9StateBlock::SetStreamSourceWithoutOffset( + UINT StreamNumber, + D3D9VertexBuffer* pStreamData, + UINT Stride) { + m_state.vertexBuffers[StreamNumber].vertexBuffer = pStreamData; + + m_state.vertexBuffers[StreamNumber].stride = Stride; + + m_captures.flags.set(D3D9CapturedStateFlag::VertexBuffers); + m_captures.vertexBuffers.set(StreamNumber, true); + return D3D_OK; + } + + HRESULT D3D9StateBlock::SetStreamSourceFreq(UINT StreamNumber, UINT Setting) { m_state.streamFreq[StreamNumber] = Setting; @@ -572,8 +586,12 @@ namespace dxvk { m_captures.flags.set(D3D9CapturedStateFlag::Material); } - if (Type != D3D9StateBlockType::None) - this->Capture(); + if (Type != D3D9StateBlockType::None) { + if (m_captures.flags.test(D3D9CapturedStateFlag::VertexDecl)) + SetVertexDeclaration(m_deviceState->vertexDecl.ptr()); + + ApplyOrCapture(); + } } } diff --git a/src/d3d9/d3d9_stateblock.h b/src/d3d9/d3d9_stateblock.h index 508266175..64bea6f22 100644 --- a/src/d3d9/d3d9_stateblock.h +++ b/src/d3d9/d3d9_stateblock.h @@ -115,6 +115,11 @@ namespace dxvk { UINT OffsetInBytes, UINT Stride); + HRESULT SetStreamSourceWithoutOffset( + UINT StreamNumber, + D3D9VertexBuffer* pStreamData, + UINT Stride); + HRESULT SetStreamSourceFreq(UINT StreamNumber, UINT Setting); HRESULT SetStateTexture(DWORD StateSampler, IDirect3DBaseTexture9* pTexture); @@ -181,7 +186,7 @@ namespace dxvk { Capture }; - template + template void ApplyOrCapture(Dst* dst, const Src* src) { if (m_captures.flags.test(D3D9CapturedStateFlag::StreamFreq)) { for (uint32_t idx : bit::BitMask(m_captures.streamFreq.dword(0))) @@ -211,11 +216,19 @@ namespace dxvk { if (m_captures.flags.test(D3D9CapturedStateFlag::VertexBuffers)) { for (uint32_t idx : bit::BitMask(m_captures.vertexBuffers.dword(0))) { const auto& vbo = src->vertexBuffers[idx]; - dst->SetStreamSource( - idx, - vbo.vertexBuffer.ptr(), - vbo.offset, - vbo.stride); + if constexpr (!IgnoreStreamOffset) { + dst->SetStreamSource( + idx, + vbo.vertexBuffer.ptr(), + vbo.offset, + vbo.stride); + } else { + // For whatever reason, D3D9 doesn't capture the stream offset + dst->SetStreamSourceWithoutOffset( + idx, + vbo.vertexBuffer.ptr(), + vbo.stride); + } } } @@ -324,12 +337,12 @@ namespace dxvk { } } - template + template void ApplyOrCapture() { if constexpr (Func == D3D9StateFunction::Apply) - ApplyOrCapture(m_parent, &m_state); + ApplyOrCapture(m_parent, &m_state); else if constexpr (Func == D3D9StateFunction::Capture) - ApplyOrCapture(this, m_deviceState); + ApplyOrCapture(this, m_deviceState); } template < From ab12ffa0da84bd854608a2966cf61082966b8443 Mon Sep 17 00:00:00 2001 From: Robin Kertels Date: Wed, 13 Mar 2024 02:02:44 +0100 Subject: [PATCH 216/246] [d3d9] Use max point size of Vulkan device The default render state value has to match what we report in the device caps. Fixes a Wine stateblock test. --- src/d3d9/d3d9_adapter.cpp | 4 +++- src/d3d9/d3d9_device.cpp | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/d3d9/d3d9_adapter.cpp b/src/d3d9/d3d9_adapter.cpp index d1fa49c47..e82d28c68 100644 --- a/src/d3d9/d3d9_adapter.cpp +++ b/src/d3d9/d3d9_adapter.cpp @@ -278,6 +278,8 @@ namespace dxvk { auto& options = m_parent->GetOptions(); + const VkPhysicalDeviceLimits& limits = m_adapter->deviceProperties().limits; + // TODO: Actually care about what the adapter supports here. // ^ For Intel and older cards most likely here. @@ -538,7 +540,7 @@ namespace dxvk { // Max Vertex Blend Matrix Index pCaps->MaxVertexBlendMatrixIndex = 0; // Max Point Size - pCaps->MaxPointSize = 256.0f; + pCaps->MaxPointSize = limits.pointSizeRange[1]; // Max Primitive Count pCaps->MaxPrimitiveCount = 0x00555555; // Max Vertex Index diff --git a/src/d3d9/d3d9_device.cpp b/src/d3d9/d3d9_device.cpp index 583c39e9e..3f4c7c128 100644 --- a/src/d3d9/d3d9_device.cpp +++ b/src/d3d9/d3d9_device.cpp @@ -7771,6 +7771,8 @@ namespace dxvk { rs[D3DRS_CLIPPLANEENABLE] = 0; m_flags.set(D3D9DeviceFlag::DirtyClipPlanes); + const VkPhysicalDeviceLimits& limits = m_dxvkDevice->adapter()->deviceProperties().limits; + rs[D3DRS_POINTSPRITEENABLE] = FALSE; rs[D3DRS_POINTSCALEENABLE] = FALSE; rs[D3DRS_POINTSCALE_A] = bit::cast(1.0f); @@ -7778,7 +7780,7 @@ namespace dxvk { rs[D3DRS_POINTSCALE_C] = bit::cast(0.0f); rs[D3DRS_POINTSIZE] = bit::cast(1.0f); rs[D3DRS_POINTSIZE_MIN] = bit::cast(1.0f); - rs[D3DRS_POINTSIZE_MAX] = bit::cast(64.0f); + rs[D3DRS_POINTSIZE_MAX] = bit::cast(limits.pointSizeRange[1]); UpdatePushConstant(); UpdatePushConstant(); UpdatePushConstant(); From 7de88ff9935d563e63bbafdaabff73418451cd33 Mon Sep 17 00:00:00 2001 From: Robin Kertels Date: Wed, 13 Mar 2024 15:49:01 +0100 Subject: [PATCH 217/246] [util] Add move & copy constructors to small_vector --- src/util/util_small_vector.h | 39 +++++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/src/util/util_small_vector.h b/src/util/util_small_vector.h index 48fe8bff7..d96e58e02 100644 --- a/src/util/util_small_vector.h +++ b/src/util/util_small_vector.h @@ -13,8 +13,37 @@ namespace dxvk { small_vector() { } - small_vector (const small_vector&) = delete; - small_vector& operator = (const small_vector&) = delete; + small_vector(const small_vector& other) { + reserve(other.m_size); + for (size_t i = 0; i < other.m_size; i++) { + *ptr(i) = *other.ptr(i); + } + m_size = other.m_size; + }; + + small_vector& operator = (const small_vector& other) { + for (size_t i = 0; i < m_size; i++) + ptr(i)->~T(); + + reserve(other.m_size); + for (size_t i = 0; i < other.m_size; i++) { + *ptr(i) = *other.ptr(i); + } + m_size = other.m_size; + }; + + small_vector(small_vector&& other) { + if (other.m_capacity == N) { + for (size_t i = 0; i < other.m_size; i++) { + u.m_data[i] = std::move(other.u.m_data[i]); + } + } else { + u.m_ptr = other.u.m_ptr; + other.m_capacity = N; + } + m_size = other.m_size; + other.m_size = 0; + } ~small_vector() { for (size_t i = 0; i < m_size; i++) @@ -23,7 +52,7 @@ namespace dxvk { if (m_capacity > N) delete[] u.m_ptr; } - + size_t size() const { return m_size; } @@ -43,7 +72,7 @@ namespace dxvk { if (m_capacity > N) delete[] u.m_ptr; - + m_capacity = n; u.m_ptr = data; } @@ -56,7 +85,7 @@ namespace dxvk { for (size_t i = n; i < m_size; i++) ptr(i)->~T(); - + for (size_t i = m_size; i < n; i++) new (ptr(i)) T(); From bbe851f6a25b57f320362c97b12afc52bfa99da1 Mon Sep 17 00:00:00 2001 From: Robin Kertels Date: Wed, 13 Mar 2024 15:49:46 +0100 Subject: [PATCH 218/246] [d3d9] Reduce data copied for SWVP vertex decls --- src/d3d9/d3d9_device.cpp | 9 +++++++-- src/d3d9/d3d9_swvp_emu.cpp | 22 +++++++++++++--------- src/d3d9/d3d9_swvp_emu.h | 23 +++++++++++++++++++---- 3 files changed, 39 insertions(+), 15 deletions(-) diff --git a/src/d3d9/d3d9_device.cpp b/src/d3d9/d3d9_device.cpp index 3f4c7c128..1db44ddfe 100644 --- a/src/d3d9/d3d9_device.cpp +++ b/src/d3d9/d3d9_device.cpp @@ -2953,14 +2953,19 @@ namespace dxvk { auto slice = dst->GetBufferSlice(); slice = slice.subSlice(offset, slice.length() - offset); + D3D9CompactVertexElements elements; + for (const D3DVERTEXELEMENT9& element : decl->GetElements()) { + elements.emplace_back(element); + } + EmitCs([this, - cVertexElements = decl->GetElements(), + cVertexElements = std::move(elements), cVertexCount = VertexCount, cStartIndex = SrcStartIndex, cInstanceCount = GetInstanceCount(), cBufferSlice = slice ](DxvkContext* ctx) mutable { - Rc shader = m_swvpEmulator.GetShaderModule(this, cVertexElements); + Rc shader = m_swvpEmulator.GetShaderModule(this, std::move(cVertexElements)); auto drawInfo = GenerateDrawInfo(D3DPT_POINTLIST, cVertexCount, cInstanceCount); diff --git a/src/d3d9/d3d9_swvp_emu.cpp b/src/d3d9/d3d9_swvp_emu.cpp index 43e30a70a..98ed77829 100644 --- a/src/d3d9/d3d9_swvp_emu.cpp +++ b/src/d3d9/d3d9_swvp_emu.cpp @@ -9,13 +9,14 @@ namespace dxvk { // Doesn't compare everything, only what we use in SWVP. - size_t D3D9VertexDeclHash::operator () (const D3D9VertexElements& key) const { + size_t D3D9VertexDeclHash::operator () (const D3D9CompactVertexElements& key) const { DxvkHashState hash; std::hash bytehash; std::hash wordhash; - for (auto& element : key) { + for (uint32_t i = 0; i < key.size(); i++) { + const auto& element = key[i]; hash.add(wordhash(element.Stream)); hash.add(wordhash(element.Offset)); hash.add(bytehash(element.Type)); @@ -27,7 +28,7 @@ namespace dxvk { return hash; } - bool D3D9VertexDeclEq::operator () (const D3D9VertexElements& a, const D3D9VertexElements& b) const { + bool D3D9VertexDeclEq::operator () (const D3D9CompactVertexElements& a, const D3D9CompactVertexElements& b) const { if (a.size() != b.size()) return false; @@ -109,7 +110,7 @@ namespace dxvk { m_module.opLabel(m_module.allocateId()); } - void compile(const D3D9VertexElements& elements) { + void compile(const D3D9CompactVertexElements& elements) { uint32_t uint_t = m_module.defIntType(32, false); uint32_t float_t = m_module.defFloatType(32); uint32_t vec4_t = m_module.defVectorType(float_t, 4); @@ -144,8 +145,9 @@ namespace dxvk { uint32_t primitiveId = m_module.opLoad(uint_t, primitiveIdPtr); // The size of any given vertex - uint32_t size = 0; - for (const auto& element : elements) { + uint32_t size = 0; + for (uint32_t i = 0; i < elements.size(); i++) { + const auto& element = elements[i]; if (element.Stream == 0 && element.Type != D3DDECLTYPE_UNUSED) { size = std::max(size, element.Offset + GetDecltypeSize(D3DDECLTYPE(element.Type))); } @@ -157,7 +159,8 @@ namespace dxvk { uint32_t thisVertexOffset = m_module.opIMul(uint_t, vertexSize, primitiveId); - for (auto& element : elements) { + for (uint32_t i = 0; i < elements.size(); i++) { + const auto& element = elements[i]; // Load the slot associated with this element DxsoSemantic semantic = { DxsoUsage(element.Usage), element.UsageIndex }; @@ -304,7 +307,7 @@ namespace dxvk { }; - Rc D3D9SWVPEmulator::GetShaderModule(D3D9DeviceEx* pDevice, const D3D9VertexElements& elements) { + Rc D3D9SWVPEmulator::GetShaderModule(D3D9DeviceEx* pDevice, D3D9CompactVertexElements&& elements) { // Use the shader's unique key for the lookup { std::unique_lock lock(m_mutex); @@ -343,7 +346,8 @@ namespace dxvk { // that object instead and discard the newly created module. { std::unique_lock lock(m_mutex); - auto status = m_modules.insert({ elements, shader }); + std::pair> pair = { std::move(elements), shader }; + auto status = m_modules.insert(std::move(pair)); if (!status.second) return status.first->second; } diff --git a/src/d3d9/d3d9_swvp_emu.h b/src/d3d9/d3d9_swvp_emu.h index c80823f0b..905a5766b 100644 --- a/src/d3d9/d3d9_swvp_emu.h +++ b/src/d3d9/d3d9_swvp_emu.h @@ -11,26 +11,41 @@ namespace dxvk { class D3D9VertexDecl; class D3D9DeviceEx; + struct D3D9CompactVertexElement { + uint16_t Stream : 4; + uint16_t Type : 5; + uint16_t Method : 3; + uint16_t Usage : 4; + uint16_t UsageIndex; + uint16_t Offset; + + D3D9CompactVertexElement(const D3DVERTEXELEMENT9& element) + : Stream(element.Stream), Type(element.Type), Method(element.Method), + Usage(element.Usage), UsageIndex(element.UsageIndex), Offset(element.Offset) {} + }; + + using D3D9CompactVertexElements = small_vector; + struct D3D9VertexDeclHash { - size_t operator () (const D3D9VertexElements& key) const; + size_t operator () (const D3D9CompactVertexElements& key) const; }; struct D3D9VertexDeclEq { - bool operator () (const D3D9VertexElements& a, const D3D9VertexElements& b) const; + bool operator () (const D3D9CompactVertexElements& a, const D3D9CompactVertexElements& b) const; }; class D3D9SWVPEmulator { public: - Rc GetShaderModule(D3D9DeviceEx* pDevice, const D3D9VertexElements& elements); + Rc GetShaderModule(D3D9DeviceEx* pDevice, D3D9CompactVertexElements&& elements); private: dxvk::mutex m_mutex; std::unordered_map< - D3D9VertexElements, Rc, + D3D9CompactVertexElements, Rc, D3D9VertexDeclHash, D3D9VertexDeclEq> m_modules; }; From fa5ce496758b2669926507c6d44a06ed49ca3258 Mon Sep 17 00:00:00 2001 From: Blisto91 <47954800+Blisto91@users.noreply.github.com> Date: Thu, 4 Jul 2024 23:33:17 +0200 Subject: [PATCH 219/246] [util] Enable longMad for Watch_Dogs 2 and Legion Works around flickering on characters when a outline or highlight effect is showing. --- src/util/config/config.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index 43c354c54..c22c97aaa 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -447,8 +447,8 @@ namespace dxvk { { R"(\\ghost\.exe$)", {{ { "d3d11.longMad", "True" }, }} }, - /* Watch_Dogs - Some objects flicker without */ - { R"(\\watch_dogs\.exe$)", {{ + /* Watch_Dogs series - Some objects flicker */ + { R"(\\watch(_)?dogs(2|Legion)?\.exe$)", {{ { "d3d11.longMad", "True" }, }} }, /* Crysis 1/Warhead - Game bug in d3d10 makes * From b2d89db8d8bb8f7babe5fa0a3e8429563202c419 Mon Sep 17 00:00:00 2001 From: Blisto91 <47954800+Blisto91@users.noreply.github.com> Date: Mon, 3 Jun 2024 14:19:35 +0200 Subject: [PATCH 220/246] [util] Disable countLosableResources for Myst V Works around a crash regression in dxvk 2.3 upon resolution change. --- src/util/config/config.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index c22c97aaa..52b2c2ac4 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -710,12 +710,14 @@ namespace dxvk { { "d3d9.memoryTrackTest", "True" }, { "d3d9.maxAvailableMemory", "2048" }, }} }, - /* Myst V End of Ages - Game has white textures on amd radv. - Expects Nvidia, Intel or ATI VendorId. - "Radeon" in gpu description also works */ + /* Myst V End of Ages * + * White textures unless it sees Nvidia, * + * Intel or ATI VendorId. * + * "Radeon" in gpu description also works. * + * countLosable for resolution change crash.*/ { R"(\\eoa\.exe$)", {{ { "d3d9.customVendorId", "10de" }, + { "d3d9.countLosableResources", "False" }, }} }, /* Supreme Commander & Forged Alliance Forever */ { R"(\\(SupremeCommander|ForgedAlliance)\.exe$)", {{ From 9b504b506e06b70d2ae3c61c21d823473548be3f Mon Sep 17 00:00:00 2001 From: Robin Kertels Date: Tue, 28 May 2024 15:39:41 +0200 Subject: [PATCH 221/246] [d3d9] Rework FF texcoord processing to be more accurate --- src/d3d9/d3d9_device.cpp | 1 + src/d3d9/d3d9_fixed_function.cpp | 107 ++++++++++++++++--------------- src/d3d9/d3d9_fixed_function.h | 4 +- 3 files changed, 61 insertions(+), 51 deletions(-) diff --git a/src/d3d9/d3d9_device.cpp b/src/d3d9/d3d9_device.cpp index 1db44ddfe..dbb6fbba1 100644 --- a/src/d3d9/d3d9_device.cpp +++ b/src/d3d9/d3d9_device.cpp @@ -7322,6 +7322,7 @@ namespace dxvk { key.Data.Contents.TransformFlags |= transformFlags << (i * 3); key.Data.Contents.TexcoordFlags |= indexFlags << (i * 3); key.Data.Contents.TexcoordIndices |= index << (i * 3); + key.Data.Contents.Projected |= ((m_state.textureStages[i][DXVK_TSS_TEXTURETRANSFORMFLAGS] & D3DTTFF_PROJECTED) == D3DTTFF_PROJECTED) << i; } key.Data.Contents.TexcoordDeclMask = m_state.vertexDecl != nullptr ? m_state.vertexDecl->GetTexcoordMask() : 0; diff --git a/src/d3d9/d3d9_fixed_function.cpp b/src/d3d9/d3d9_fixed_function.cpp index da90604dc..4da5cf266 100644 --- a/src/d3d9/d3d9_fixed_function.cpp +++ b/src/d3d9/d3d9_fixed_function.cpp @@ -1108,44 +1108,62 @@ namespace dxvk { uint32_t flags = (m_vsKey.Data.Contents.TransformFlags >> (i * 3)) & 0b111; - if (flags == D3DTTFF_COUNT1) { - // D3DTTFF_COUNT1 behaves like D3DTTFF_DISABLE on NV and like D3DTTFF_COUNT2 on AMD. - // The Nvidia behavior is easier to implement. - flags = D3DTTFF_DISABLE; - } - // Passing 0xffffffff results in it getting clamped to the dimensions of the texture coords and getting treated as PROJECTED // but D3D9 does not apply the transformation matrix. - bool applyTransform = flags >= D3DTTFF_COUNT1 && flags <= D3DTTFF_COUNT4; + bool applyTransform = flags > D3DTTFF_COUNT1 && flags <= D3DTTFF_COUNT4; + + uint32_t count = std::min(flags, 4u); + + // A projection component index of 4 means we won't do projection + uint32_t projIndex = count != 0 ? count - 1 : 4; - uint32_t count; switch (inputFlags) { default: case (DXVK_TSS_TCI_PASSTHRU >> TCIOffset): transformed = m_vs.in.TEXCOORD[inputIndex & 0xFF]; - // flags is actually the number of elements that get passed - // to the rasterizer. - count = flags; - if (texcoordCount) { - // Clamp by the number of elements in the texcoord input. - if (!count || count > texcoordCount) { - count = texcoordCount; - } - } else { - count = 0; - flags = D3DTTFF_DISABLE; - applyTransform = false; + + if (texcoordCount < 4) { + // Vulkan sets the w component to 1.0 if that's not provided by the vertex buffer, D3D9 expects 0 here + transformed = m_module.opCompositeInsert(m_vec4Type, m_module.constf32(0), transformed, 1, &wIndex); } + + if (applyTransform && !m_vsKey.Data.Contents.HasPositionT) { + /*This doesn't happen every time and I cannot figure out the difference between when it does and doesn't. + Keep it disabled for now, it's more likely that games rely on the zero texcoord than the weird 1 here. + if (texcoordCount <= 1) { + // y gets padded to 1 for some reason + uint32_t idx = 1; + transformed = m_module.opCompositeInsert(m_vec4Type, m_module.constf32(1), transformed, 1, &idx); + }*/ + + if (texcoordCount >= 1 && texcoordCount < 4) { + // The first component after the last one thats backed by a vertex buffer gets padded to 1 for some reason. + uint32_t idx = texcoordCount; + transformed = m_module.opCompositeInsert(m_vec4Type, m_module.constf32(1), transformed, 1, &idx); + } + } else if (texcoordCount != 0 && !applyTransform) { + // COUNT0, COUNT1, COUNT > 4 => take count from vertex decl if that's not zero + count = texcoordCount; + } + + projIndex = count != 0 ? count - 1 : 4; break; case (DXVK_TSS_TCI_CAMERASPACENORMAL >> TCIOffset): transformed = outNrm; - count = std::min(flags, 4u); + if (!applyTransform) { + count = 3; + projIndex = 4; + } break; case (DXVK_TSS_TCI_CAMERASPACEPOSITION >> TCIOffset): - transformed = m_module.opCompositeInsert(m_vec4Type, m_module.constf32(1.0f), vtx, 1, &wIndex); - count = std::min(flags, 4u); + transformed = vtx; + if (!applyTransform) { + Logger::warn(str::format("!applyTransform flags: ", flags, " projidx: ", projIndex)); + count = 3; + projIndex = 4; + } break; case (DXVK_TSS_TCI_CAMERASPACEREFLECTIONVECTOR >> TCIOffset): { @@ -1160,7 +1178,10 @@ namespace dxvk { transformIndices[3] = m_module.constf32(1.0f); transformed = m_module.opCompositeConstruct(m_vec4Type, transformIndices.size(), transformIndices.data()); - count = std::min(flags, 4u); + if (!applyTransform) { + count = 3; + projIndex = 4; + } break; } @@ -1184,42 +1205,28 @@ namespace dxvk { transformIndices[3] = m_module.constf32(1.0f); transformed = m_module.opCompositeConstruct(m_vec4Type, transformIndices.size(), transformIndices.data()); - count = std::min(flags, 4u); break; } } if (applyTransform && !m_vsKey.Data.Contents.HasPositionT) { - for (uint32_t j = count; j < 4; j++) { - // If we're outside the component count of the vertex decl for this texcoord then we pad with zeroes. - // Otherwise, pad with ones. - - // Very weird quirk in order to get texcoord transforms to work like they do in native. - // In future, maybe we could sort this out properly by chopping matrices of different sizes, but thats - // a project for another day. - uint32_t texcoordCount = (m_vsKey.Data.Contents.TexcoordDeclMask >> (3 * inputIndex)) & 0x7; - uint32_t value = j > texcoordCount ? m_module.constf32(0) : m_module.constf32(1); - transformed = m_module.opCompositeInsert(m_vec4Type, value, transformed, 1, &j); - } - transformed = m_module.opVectorTimesMatrix(m_vec4Type, transformed, m_vs.constants.texcoord[i]); } - // The projection idx is always based on the flags, even when the input mode is not DXVK_TSS_TCI_PASSTHRU. - uint32_t projValue; - if (count < 3) { - // Not enough components to do projection. - // Native drivers render normally or garbage with D3DFVF_TEXCOORDSIZE2 or D3DTTFF_COUNT <3 - projValue = m_module.constf32(1.0f); - } else { - uint32_t projIdx = count - 1; - projValue = m_module.opCompositeExtract(m_floatType, transformed, 1, &projIdx); + if (m_vsKey.Data.Contents.Projected && projIndex < 4) { + // The projection idx is always based on the flags, even when the input mode is not DXVK_TSS_TCI_PASSTHRU. + uint32_t projValue = m_module.opCompositeExtract(m_floatType, transformed, 1, &projIndex); + + // The w component is only used for projection or unused, so always insert the component that's supposed to be divided by there. + // The fragment shader will then decide whether to project or not. + transformed = m_module.opCompositeInsert(m_vec4Type, projValue, transformed, 1, &wIndex); } - // The w component is only used for projection or unused, so always insert the component that's supposed to be divided by there. - // The fragment shader will then decide whether to project or not. - uint32_t wIdx = 3; - transformed = m_module.opCompositeInsert(m_vec4Type, projValue, transformed, 1, &wIdx); + uint32_t totalComponents = (m_vsKey.Data.Contents.Projected && projIndex < 4) ? 3 : 4; + for (uint32_t i = count; i < totalComponents; i++) { + // Discard the components that exceed the specified D3DTTFF_COUNT + transformed = m_module.opCompositeInsert(m_vec4Type, m_module.constf32(0), transformed, 1, &i); + } m_module.opStore(m_vs.out.TEXCOORD[i], transformed); } diff --git a/src/d3d9/d3d9_fixed_function.h b/src/d3d9/d3d9_fixed_function.h index b111051a2..58ebdbcdc 100644 --- a/src/d3d9/d3d9_fixed_function.h +++ b/src/d3d9/d3d9_fixed_function.h @@ -133,9 +133,11 @@ namespace dxvk { uint32_t VertexBlendCount : 3; uint32_t VertexClipping : 1; + + uint32_t Projected : 8; } Contents; - uint32_t Primitive[4]; + uint32_t Primitive[5]; }; }; From ef0c6b6f6fd756d80767bd651ed36479ec5a6ea0 Mon Sep 17 00:00:00 2001 From: WinterSnowfall Date: Sat, 6 Jul 2024 23:43:38 +0300 Subject: [PATCH 222/246] [d3d9] Hardware cursor fixes --- src/d3d9/d3d9_cursor.cpp | 4 ++-- src/d3d9/d3d9_cursor.h | 9 +++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/d3d9/d3d9_cursor.cpp b/src/d3d9/d3d9_cursor.cpp index 16ec41d60..0224c776b 100644 --- a/src/d3d9/d3d9_cursor.cpp +++ b/src/d3d9/d3d9_cursor.cpp @@ -26,14 +26,14 @@ namespace dxvk { HRESULT D3D9Cursor::SetHardwareCursor(UINT XHotSpot, UINT YHotSpot, const CursorBitmap& bitmap) { - DWORD mask[32]; + CursorMask mask; std::memset(mask, ~0, sizeof(mask)); ICONINFO info; info.fIcon = FALSE; info.xHotspot = XHotSpot; info.yHotspot = YHotSpot; - info.hbmMask = ::CreateBitmap(HardwareCursorWidth, HardwareCursorHeight, 1, 1, mask); + info.hbmMask = ::CreateBitmap(HardwareCursorWidth, HardwareCursorHeight, 1, 1, &mask[0]); info.hbmColor = ::CreateBitmap(HardwareCursorWidth, HardwareCursorHeight, 1, 32, &bitmap[0]); if (m_hCursor != nullptr) diff --git a/src/d3d9/d3d9_cursor.h b/src/d3d9/d3d9_cursor.h index d69e39748..b2ca5537d 100644 --- a/src/d3d9/d3d9_cursor.h +++ b/src/d3d9/d3d9_cursor.h @@ -11,11 +11,20 @@ namespace dxvk { // Format Size of 4 bytes (ARGB) using CursorBitmap = uint8_t[HardwareCursorHeight * HardwareCursorPitch]; + // Monochrome mask (1 bit) + using CursorMask = uint8_t[HardwareCursorHeight * HardwareCursorWidth / 8]; class D3D9Cursor { public: +#ifdef _WIN32 + ~D3D9Cursor() { + if (m_hCursor != nullptr) + ::DestroyCursor(m_hCursor); + } +#endif + void UpdateCursor(int X, int Y); BOOL ShowCursor(BOOL bShow); From 60e523b4bf9c30d4794094710c5ac5214ad78561 Mon Sep 17 00:00:00 2001 From: Jeff Date: Sun, 7 Jul 2024 12:10:48 +0100 Subject: [PATCH 223/246] [d3d8] Implement Direct3D 8 Frontend Co-authored-by: WinterSnowfall ## Config Changes Co-authored-by: Blisto91 <47954800+Blisto91@users.noreply.github.com> Co-authored-by: simifor --- .github/workflows/test-build-windows.yml | 7 + LICENSE | 1 + README.md | 2 +- dxvk.conf | 26 + meson_options.txt | 1 + src/d3d8/d3d8.def | 3 + src/d3d8/d3d8.sym | 7 + src/d3d8/d3d8_batch.h | 239 +++ src/d3d8/d3d8_buffer.h | 102 ++ src/d3d8/d3d8_caps.h | 8 + src/d3d8/d3d8_d3d9_util.h | 164 ++ src/d3d8/d3d8_device.cpp | 1738 ++++++++++++++++++++++ src/d3d8/d3d8_device.h | 454 ++++++ src/d3d8/d3d8_device_child.h | 71 + src/d3d8/d3d8_format.h | 220 +++ src/d3d8/d3d8_include.h | 200 +++ src/d3d8/d3d8_interface.cpp | 136 ++ src/d3d8/d3d8_interface.h | 163 ++ src/d3d8/d3d8_main.cpp | 24 + src/d3d8/d3d8_options.cpp | 55 + src/d3d8/d3d8_options.h | 48 + src/d3d8/d3d8_resource.h | 100 ++ src/d3d8/d3d8_shader.cpp | 336 +++++ src/d3d8/d3d8_shader.h | 18 + src/d3d8/d3d8_state_block.cpp | 49 + src/d3d8/d3d8_state_block.h | 134 ++ src/d3d8/d3d8_subresource.h | 61 + src/d3d8/d3d8_surface.cpp | 26 + src/d3d8/d3d8_surface.h | 81 + src/d3d8/d3d8_swapchain.h | 42 + src/d3d8/d3d8_texture.h | 233 +++ src/d3d8/d3d8_volume.h | 40 + src/d3d8/d3d8_wrapped_object.h | 68 + src/d3d8/meson.build | 42 + src/d3d8/version.rc | 31 + src/meson.build | 9 +- src/util/config/config.cpp | 135 +- src/vulkan/vulkan_util.h | 5 + 38 files changed, 5069 insertions(+), 10 deletions(-) create mode 100644 src/d3d8/d3d8.def create mode 100644 src/d3d8/d3d8.sym create mode 100644 src/d3d8/d3d8_batch.h create mode 100644 src/d3d8/d3d8_buffer.h create mode 100644 src/d3d8/d3d8_caps.h create mode 100644 src/d3d8/d3d8_d3d9_util.h create mode 100644 src/d3d8/d3d8_device.cpp create mode 100644 src/d3d8/d3d8_device.h create mode 100644 src/d3d8/d3d8_device_child.h create mode 100644 src/d3d8/d3d8_format.h create mode 100644 src/d3d8/d3d8_include.h create mode 100644 src/d3d8/d3d8_interface.cpp create mode 100644 src/d3d8/d3d8_interface.h create mode 100644 src/d3d8/d3d8_main.cpp create mode 100644 src/d3d8/d3d8_options.cpp create mode 100644 src/d3d8/d3d8_options.h create mode 100644 src/d3d8/d3d8_resource.h create mode 100644 src/d3d8/d3d8_shader.cpp create mode 100644 src/d3d8/d3d8_shader.h create mode 100644 src/d3d8/d3d8_state_block.cpp create mode 100644 src/d3d8/d3d8_state_block.h create mode 100644 src/d3d8/d3d8_subresource.h create mode 100644 src/d3d8/d3d8_surface.cpp create mode 100644 src/d3d8/d3d8_surface.h create mode 100644 src/d3d8/d3d8_swapchain.h create mode 100644 src/d3d8/d3d8_texture.h create mode 100644 src/d3d8/d3d8_volume.h create mode 100644 src/d3d8/d3d8_wrapped_object.h create mode 100644 src/d3d8/meson.build create mode 100644 src/d3d8/version.rc diff --git a/.github/workflows/test-build-windows.yml b/.github/workflows/test-build-windows.yml index 503bd4805..bae8414ba 100644 --- a/.github/workflows/test-build-windows.yml +++ b/.github/workflows/test-build-windows.yml @@ -32,6 +32,13 @@ jobs: Write-Output "VSDEVCMD=${installationPath}\Common7\Tools\VsDevCmd.bat" ` | Out-File -FilePath "${Env:GITHUB_ENV}" -Append + - name: Download D3D8 SDK Headers + shell: pwsh + run: | + Invoke-WebRequest -URI https://raw.githubusercontent.com/NovaRain/DXSDK_Collection/master/DXSDK_Aug2007/Include/d3d8.h -OutFile include/d3d8.h + Invoke-WebRequest -URI https://raw.githubusercontent.com/NovaRain/DXSDK_Collection/master/DXSDK_Aug2007/Include/d3d8types.h -OutFile include/d3d8types.h + Invoke-WebRequest -URI https://raw.githubusercontent.com/NovaRain/DXSDK_Collection/master/DXSDK_Aug2007/Include/d3d8caps.h -OutFile include/d3d8caps.h + - name: Build MSVC x86 shell: pwsh run: | diff --git a/LICENSE b/LICENSE index 253cadd62..68b6498ff 100644 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,6 @@ Copyright (c) 2017 Philip Rebohle Copyright (c) 2019 Joshua Ashton + Copyright (c) 2023 Jeffrey Ellison zlib/libpng license diff --git a/README.md b/README.md index 364f2bb74..753b375f7 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # DXVK -A Vulkan-based translation layer for Direct3D 9/10/11 which allows running 3D applications on Linux using Wine. +A Vulkan-based translation layer for Direct3D 8/9/10/11 which allows running 3D applications on Linux using Wine. For the current status of the project, please refer to the [project wiki](https://github.com/doitsujin/dxvk/wiki). diff --git a/dxvk.conf b/dxvk.conf index 453024feb..87642979e 100644 --- a/dxvk.conf +++ b/dxvk.conf @@ -709,3 +709,29 @@ # - True/False # d3d9.countLosableResources = True + +# Use NVIDIA Shadow Buffers +# +# Vendor behavior for GeForce3 and GeForce4 cards that allows +# sampling depth textures with non-normalized Z coordinates +# and applies hardware shadow filtering. +# +# Supported values: +# - True/False + +# d3d8.useShadowBuffers = False + + +# MANAGED Buffer Placement +# +# Remap some DEFAULT pool vertex and index buffers to the MANAGED pool to improve +# performance by avoiding waiting for games that frequently lock (large) buffers. +# +# This implicitly disables direct buffer mapping. Some applications may need this option +# disabled for certain (smaller) buffers to keep from overwriting in-use buffer regions. +# +# Supported values: +# - True/False +# - Any non-negative integer - sets the minimum size in bytes for a buffer to be MANAGED + +# d3d8.managedBufferPlacement = True diff --git a/meson_options.txt b/meson_options.txt index 5ac9ea7b4..2a621eced 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -1,4 +1,5 @@ option('enable_dxgi', type : 'boolean', value : true, description: 'Build DXGI') +option('enable_d3d8', type : 'boolean', value : true, description: 'Build D3D8') option('enable_d3d9', type : 'boolean', value : true, description: 'Build D3D9') option('enable_d3d10', type : 'boolean', value : true, description: 'Build D3D10') option('enable_d3d11', type : 'boolean', value : true, description: 'Build D3D11') diff --git a/src/d3d8/d3d8.def b/src/d3d8/d3d8.def new file mode 100644 index 000000000..62aca0c9b --- /dev/null +++ b/src/d3d8/d3d8.def @@ -0,0 +1,3 @@ +LIBRARY D3D8.DLL +EXPORTS + Direct3DCreate8 @ 5 diff --git a/src/d3d8/d3d8.sym b/src/d3d8/d3d8.sym new file mode 100644 index 000000000..357113c3c --- /dev/null +++ b/src/d3d8/d3d8.sym @@ -0,0 +1,7 @@ +{ + global: + Direct3DCreate8; + + local: + *; +}; diff --git a/src/d3d8/d3d8_batch.h b/src/d3d8/d3d8_batch.h new file mode 100644 index 000000000..a510b67ac --- /dev/null +++ b/src/d3d8/d3d8_batch.h @@ -0,0 +1,239 @@ +#pragma once + +#include "d3d8_include.h" +#include "d3d8_buffer.h" +#include "d3d8_format.h" + +#include +#include +#include + +namespace dxvk { + + inline constexpr size_t D3DPT_COUNT = size_t(D3DPT_TRIANGLEFAN) + 1; + inline constexpr D3DPRIMITIVETYPE D3DPT_INVALID = D3DPRIMITIVETYPE(0); + + // Vertex buffer that can handle many tiny locks while + // still maintaing the lock ordering of direct-mapped buffers. + class D3D8BatchBuffer final : public D3D8VertexBuffer { + public: + D3D8BatchBuffer( + D3D8Device* pDevice, + D3DPOOL Pool, + DWORD Usage, + UINT Length, + DWORD FVF) + : D3D8VertexBuffer(pDevice, nullptr, Pool, Usage) + , m_data(Length) + , m_fvf(FVF) { + } + + HRESULT STDMETHODCALLTYPE Lock( + UINT OffsetToLock, + UINT SizeToLock, + BYTE** ppbData, + DWORD Flags) { + *ppbData = m_data.data() + OffsetToLock; + return D3D_OK; + } + + HRESULT STDMETHODCALLTYPE Unlock() { + return D3D_OK; + } + + HRESULT STDMETHODCALLTYPE GetDesc(D3DVERTEXBUFFER_DESC* pDesc) { + pDesc->Format = D3DFMT_VERTEXDATA; + pDesc->Type = D3DRTYPE_VERTEXBUFFER; + pDesc->Usage = m_usage; + pDesc->Pool = m_pool; + pDesc->Size = m_data.size(); + pDesc->FVF = m_fvf; + return D3D_OK; + } + + void STDMETHODCALLTYPE PreLoad() { + } + + const void* GetPtr(UINT byteOffset = 0) const { + return m_data.data() + byteOffset; + } + + UINT Size() const { + return m_data.size(); + } + + private: + std::vector m_data; + DWORD m_fvf; + }; + + + // Main handler for batching D3D8 draw calls. + class D3D8Batcher { + + struct Batch { + D3DPRIMITIVETYPE PrimitiveType = D3DPT_INVALID; + std::vector Indices; + UINT Offset = 0; + UINT MinVertex = UINT_MAX; + UINT MaxVertex = 0; + UINT PrimitiveCount = 0; + UINT DrawCallCount = 0; + }; + + public: + D3D8Batcher(D3D8Device* pDevice8, Com&& pDevice9) + : m_device8(pDevice8) + , m_device(std::move(pDevice9)) { + } + + inline D3D8BatchBuffer* CreateVertexBuffer(UINT Length, DWORD Usage, DWORD FVF, D3DPOOL Pool) { + return ref(new D3D8BatchBuffer(m_device8, Pool, Usage, Length, FVF)); + } + + inline void StateChange() { + if (likely(m_batches.empty())) + return; + for (auto& draw : m_batches) { + + if (draw.PrimitiveType == D3DPT_INVALID) + continue; + + for (auto& index : draw.Indices) + index -= draw.MinVertex; + + m_device->DrawIndexedPrimitiveUP( + d3d9::D3DPRIMITIVETYPE(draw.PrimitiveType), + 0, + draw.MaxVertex - draw.MinVertex, + draw.PrimitiveCount, + draw.Indices.data(), + d3d9::D3DFMT_INDEX16, + m_stream->GetPtr(draw.MinVertex * m_stride), + m_stride); + + m_device->SetStreamSource(0, D3D8VertexBuffer::GetD3D9Nullable(m_stream), 0, m_stride); + m_device->SetIndices(D3D8IndexBuffer::GetD3D9Nullable(m_indices)); + + draw.PrimitiveType = D3DPRIMITIVETYPE(0); + draw.Offset = 0; + draw.MinVertex = UINT_MAX; + draw.MaxVertex = 0; + draw.PrimitiveCount = 0; + draw.DrawCallCount = 0; + } + } + + inline void EndFrame() { + // Nothing to be done. + } + + inline HRESULT DrawPrimitive( + D3DPRIMITIVETYPE PrimitiveType, + UINT StartVertex, + UINT PrimitiveCount) { + + // None of this linestrip or fan malarkey + D3DPRIMITIVETYPE batchedPrimType = PrimitiveType; + switch (PrimitiveType) { + case D3DPT_LINESTRIP: batchedPrimType = D3DPT_LINELIST; break; + case D3DPT_TRIANGLEFAN: batchedPrimType = D3DPT_TRIANGLELIST; break; + default: break; + } + + Batch* batch = &m_batches[size_t(batchedPrimType)]; + batch->PrimitiveType = batchedPrimType; + + //UINT vertices = GetVertexCount8(PrimitiveType, PrimitiveCount); + + switch (PrimitiveType) { + case D3DPT_POINTLIST: + batch->Indices.resize(batch->Offset + PrimitiveCount); + for (UINT i = 0; i < PrimitiveCount; i++) + batch->Indices[batch->Offset++] = (StartVertex + i); + break; + case D3DPT_LINELIST: + batch->Indices.resize(batch->Offset + PrimitiveCount * 2); + for (UINT i = 0; i < PrimitiveCount; i++) { + batch->Indices[batch->Offset++] = (StartVertex + i * 2 + 0); + batch->Indices[batch->Offset++] = (StartVertex + i * 2 + 1); + } + break; + case D3DPT_LINESTRIP: + batch->Indices.resize(batch->Offset + PrimitiveCount * 2); + for (UINT i = 0; i < PrimitiveCount; i++) { + batch->Indices[batch->Offset++] = (StartVertex + i + 0); + batch->Indices[batch->Offset++] = (StartVertex + i + 1); + } + break; + case D3DPT_TRIANGLELIST: + batch->Indices.resize(batch->Offset + PrimitiveCount * 3); + for (UINT i = 0; i < PrimitiveCount; i++) { + batch->Indices[batch->Offset++] = (StartVertex + i * 3 + 0); + batch->Indices[batch->Offset++] = (StartVertex + i * 3 + 1); + batch->Indices[batch->Offset++] = (StartVertex + i * 3 + 2); + } + break; + case D3DPT_TRIANGLESTRIP: + // Join with degenerate triangle + // 1 2 3, 3 4, 4 5 6 + batch->Indices.resize(batch->Offset + PrimitiveCount + 2); + if (batch->Offset > 0) { + batch->Indices[batch->Offset + 1] = batch->Indices[batch->Offset-2]; + batch->Indices[batch->Offset += 2] = StartVertex; + } + for (UINT i = 0; i < PrimitiveCount; i++) { + batch->Indices[batch->Offset++] = (StartVertex + i + 0); + } + break; + // 1 2 3 4 5 6 7 -> 1 2 3, 1 3 4, 1 4 5, 1 5 6, 1 6 7 + case D3DPT_TRIANGLEFAN: + batch->Indices.resize(batch->Offset + PrimitiveCount * 3); + for (UINT i = 0; i < PrimitiveCount; i++) { + batch->Indices[batch->Offset++] = (StartVertex + 0); + batch->Indices[batch->Offset++] = (StartVertex + i + 1); + batch->Indices[batch->Offset++] = (StartVertex + i + 2); + } + break; + default: + return D3DERR_INVALIDCALL; + } + batch->MinVertex = std::min(batch->MinVertex, StartVertex); + if (!batch->Indices.empty()) + batch->MaxVertex = std::max(batch->MaxVertex, UINT(batch->Indices.back() + 1)); + batch->PrimitiveCount += PrimitiveCount; + batch->DrawCallCount++; + return D3D_OK; + } + + inline void SetStream(UINT num, D3D8VertexBuffer* stream, UINT stride) { + if (unlikely(num != 0)) { + StateChange(); + return; + } + if (unlikely(m_stream != stream || m_stride != stride)) { + StateChange(); + m_stream = static_cast(stream); + m_stride = stride; + } + } + + inline void SetIndices(D3D8IndexBuffer* indices, INT baseVertexIndex) { + if (m_indices != indices || m_baseVertexIndex != baseVertexIndex) { + StateChange(); + m_indices = indices; + m_baseVertexIndex = baseVertexIndex; + } + } + + private: + D3D8Device* m_device8; + Com m_device; + + D3D8BatchBuffer* m_stream = nullptr; + UINT m_stride = 0; + D3D8IndexBuffer* m_indices = nullptr; + INT m_baseVertexIndex = 0; + std::array m_batches; + }; +} diff --git a/src/d3d8/d3d8_buffer.h b/src/d3d8/d3d8_buffer.h new file mode 100644 index 000000000..89b1aae91 --- /dev/null +++ b/src/d3d8/d3d8_buffer.h @@ -0,0 +1,102 @@ +#pragma once + +#include "d3d8_include.h" +#include "d3d8_resource.h" + +namespace dxvk { + + template + class D3D8Buffer : public D3D8Resource { + + public: + + D3D8Buffer( + D3D8Device* pDevice, + Com&& pBuffer, + D3DPOOL Pool, + DWORD Usage) + : D3D8Resource (pDevice, std::move(pBuffer)) + , m_pool (Pool) + , m_usage (Usage) { + } + + HRESULT STDMETHODCALLTYPE Lock( + UINT OffsetToLock, + UINT SizeToLock, + BYTE** ppbData, + DWORD Flags) { + return this->GetD3D9()->Lock( + OffsetToLock, + SizeToLock, + reinterpret_cast(ppbData), + Flags); + } + + HRESULT STDMETHODCALLTYPE Unlock() { + return this->GetD3D9()->Unlock(); + } + + void STDMETHODCALLTYPE PreLoad() { + this->GetD3D9()->PreLoad(); + } + + protected: + // This is the D3D8 pool, not necessarily what's given to D3D9. + const D3DPOOL m_pool; + // This is the D3D8 usage, not necessarily what's given to D3D9. + const DWORD m_usage; + }; + + + using D3D8VertexBufferBase = D3D8Buffer; + class D3D8VertexBuffer : public D3D8VertexBufferBase { + + public: + + D3D8VertexBuffer( + D3D8Device* pDevice, + Com&& pBuffer, + D3DPOOL Pool, + DWORD Usage) + : D3D8VertexBufferBase(pDevice, std::move(pBuffer), Pool, Usage) { + } + + D3DRESOURCETYPE STDMETHODCALLTYPE GetType() final { return D3DRTYPE_VERTEXBUFFER; } + + HRESULT STDMETHODCALLTYPE GetDesc(D3DVERTEXBUFFER_DESC* pDesc) { + HRESULT hr = GetD3D9()->GetDesc(reinterpret_cast(pDesc)); + if (!FAILED(hr)) { + pDesc->Pool = m_pool; + pDesc->Usage = m_usage; + } + return hr; + } + + }; + + using D3D8IndexBufferBase = D3D8Buffer; + class D3D8IndexBuffer final : public D3D8IndexBufferBase { + + public: + + D3D8IndexBuffer( + D3D8Device* pDevice, + Com&& pBuffer, + D3DPOOL Pool, + DWORD Usage) + : D3D8IndexBufferBase(pDevice, std::move(pBuffer), Pool, Usage) { + } + + D3DRESOURCETYPE STDMETHODCALLTYPE GetType() final { return D3DRTYPE_INDEXBUFFER; } + + HRESULT STDMETHODCALLTYPE GetDesc(D3DINDEXBUFFER_DESC* pDesc) final { + HRESULT hr = GetD3D9()->GetDesc(reinterpret_cast(pDesc)); + if (!FAILED(hr)) { + pDesc->Pool = m_pool; + pDesc->Usage = m_usage; + } + return hr; + } + + }; +} \ No newline at end of file diff --git a/src/d3d8/d3d8_caps.h b/src/d3d8/d3d8_caps.h new file mode 100644 index 000000000..0da600c78 --- /dev/null +++ b/src/d3d8/d3d8_caps.h @@ -0,0 +1,8 @@ +#pragma once + +namespace dxvk::d8caps { + + inline constexpr uint32_t MAX_TEXTURE_STAGES = 8; + inline constexpr uint32_t MAX_STREAMS = 16; + +} \ No newline at end of file diff --git a/src/d3d8/d3d8_d3d9_util.h b/src/d3d8/d3d8_d3d9_util.h new file mode 100644 index 000000000..1143899e5 --- /dev/null +++ b/src/d3d8/d3d8_d3d9_util.h @@ -0,0 +1,164 @@ +#pragma once + +// Utility functions for converting +// between DirectX8 and DirectX9 types. + +#include "d3d8_include.h" +#include "d3d8_format.h" +#include "d3d8_options.h" + +#include + +namespace dxvk { + + // (8<-9) D3DCAPSX: Writes to D3DCAPS8 from D3DCAPS9 + inline void ConvertCaps8(const d3d9::D3DCAPS9& caps9, D3DCAPS8* pCaps8) { + + // should be aligned + std::memcpy(pCaps8, &caps9, sizeof(D3DCAPS8)); + + // Max supported shader model is PS 1.4 and VS 1.1 + pCaps8->VertexShaderVersion = D3DVS_VERSION(1, 1); + pCaps8->PixelShaderVersion = D3DPS_VERSION(1, 4); + + // This was removed by D3D9. We can probably render windowed. + pCaps8->Caps2 |= D3DCAPS2_CANRENDERWINDOWED; + + // Replaced by D3DPRASTERCAPS_DEPTHBIAS in D3D9 + pCaps8->RasterCaps |= D3DPRASTERCAPS_ZBIAS; + + + // Remove D3D9-specific caps: + pCaps8->Caps2 &= ~D3DCAPS2_CANAUTOGENMIPMAP; + + pCaps8->Caps3 &= ~D3DCAPS3_LINEAR_TO_SRGB_PRESENTATION + & ~D3DCAPS3_COPY_TO_VIDMEM + & ~D3DCAPS3_COPY_TO_SYSTEMMEM; + + pCaps8->PrimitiveMiscCaps &= ~D3DPMISCCAPS_INDEPENDENTWRITEMASKS + & ~D3DPMISCCAPS_PERSTAGECONSTANT + & ~D3DPMISCCAPS_FOGANDSPECULARALPHA + & ~D3DPMISCCAPS_SEPARATEALPHABLEND + & ~D3DPMISCCAPS_MRTINDEPENDENTBITDEPTHS + & ~D3DPMISCCAPS_MRTPOSTPIXELSHADERBLENDING + & ~D3DPMISCCAPS_FOGVERTEXCLAMPED + & ~D3DPMISCCAPS_POSTBLENDSRGBCONVERT; + + pCaps8->RasterCaps &= ~D3DPRASTERCAPS_SCISSORTEST + & ~D3DPRASTERCAPS_SLOPESCALEDEPTHBIAS + & ~D3DPRASTERCAPS_DEPTHBIAS + & ~D3DPRASTERCAPS_MULTISAMPLE_TOGGLE; + + pCaps8->SrcBlendCaps &= ~D3DPBLENDCAPS_INVSRCCOLOR2 + & ~D3DPBLENDCAPS_SRCCOLOR2; + + pCaps8->LineCaps &= ~D3DLINECAPS_ANTIALIAS; + + pCaps8->StencilCaps &= ~D3DSTENCILCAPS_TWOSIDED; + } + + // (9<-8) D3DD3DPRESENT_PARAMETERS: Returns D3D9's params given an input for D3D8 + inline d3d9::D3DPRESENT_PARAMETERS ConvertPresentParameters9(const D3DPRESENT_PARAMETERS* pParams) { + + d3d9::D3DPRESENT_PARAMETERS params; + params.BackBufferWidth = pParams->BackBufferWidth; + params.BackBufferHeight = pParams->BackBufferHeight; + params.BackBufferFormat = d3d9::D3DFORMAT(pParams->BackBufferFormat); + params.BackBufferCount = pParams->BackBufferCount; + + params.MultiSampleType = d3d9::D3DMULTISAMPLE_TYPE(pParams->MultiSampleType); + params.MultiSampleQuality = 0; // (D3D8: no MultiSampleQuality), TODO: get a value for this + + + UINT PresentationInterval = pParams->FullScreen_PresentationInterval; + + if (pParams->Windowed) { + + if (unlikely(PresentationInterval != D3DPRESENT_INTERVAL_DEFAULT)) { + // TODO: what does dx8 do if windowed app sets FullScreen_PresentationInterval? + Logger::warn(str::format( + "D3D8 Application is windowed yet requested FullScreen_PresentationInterval ", PresentationInterval, + " (should be D3DPRESENT_INTERVAL_DEFAULT). This will be ignored.")); + } + + // D3D8: For windowed swap chain, the back buffer is copied to the window immediately. + PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; + } + + D3DSWAPEFFECT SwapEffect = pParams->SwapEffect; + + // D3DSWAPEFFECT_COPY_VSYNC has been removed + if (SwapEffect == D3DSWAPEFFECT_COPY_VSYNC) { + + SwapEffect = D3DSWAPEFFECT_COPY; + + // D3D8: In windowed mode, D3DSWAPEFFECT_COPY_VSYNC enables VSYNC. + // In fullscreen, D3DPRESENT_INTERVAL_IMMEDIATE is meaningless. + if (pParams->Windowed || (PresentationInterval & D3DPRESENT_INTERVAL_IMMEDIATE) != 0) { + PresentationInterval = D3DPRESENT_INTERVAL_ONE; + // TODO: what does dx8 do if multiple D3DPRESENT_INTERVAL flags are set? + } + } + + params.SwapEffect = d3d9::D3DSWAPEFFECT(SwapEffect); + params.hDeviceWindow = pParams->hDeviceWindow; + params.Windowed = pParams->Windowed; + params.EnableAutoDepthStencil = pParams->EnableAutoDepthStencil; + params.AutoDepthStencilFormat = d3d9::D3DFORMAT(pParams->AutoDepthStencilFormat); + params.Flags = pParams->Flags; + + params.FullScreen_RefreshRateInHz = pParams->FullScreen_RefreshRateInHz; + + // FullScreen_PresentationInterval -> PresentationInterval + params.PresentationInterval = PresentationInterval; + + return params; + } + + // (8<-9) Convert D3DSURFACE_DESC + inline void ConvertSurfaceDesc8(const d3d9::D3DSURFACE_DESC* pSurf9, D3DSURFACE_DESC* pSurf8) { + pSurf8->Format = D3DFORMAT(pSurf9->Format); + pSurf8->Type = D3DRESOURCETYPE(pSurf9->Type); + pSurf8->Usage = pSurf9->Usage; + pSurf8->Pool = D3DPOOL(pSurf9->Pool); + pSurf8->Size = getSurfaceSize(pSurf8->Format, pSurf9->Width, pSurf9->Height); + + pSurf8->MultiSampleType = D3DMULTISAMPLE_TYPE(pSurf9->MultiSampleType); + // DX8: No multisample quality + pSurf8->Width = pSurf9->Width; + pSurf8->Height = pSurf9->Height; + } + + // (8<-9) Convert D3DVOLUME_DESC + inline void ConvertVolumeDesc8(const d3d9::D3DVOLUME_DESC* pVol9, D3DVOLUME_DESC* pVol8) { + pVol8->Format = D3DFORMAT(pVol9->Format); + pVol8->Type = D3DRESOURCETYPE(pVol9->Type); + pVol8->Usage = pVol9->Usage; + pVol8->Pool = D3DPOOL(pVol9->Pool); + pVol8->Size = getSurfaceSize(pVol8->Format, pVol9->Width, pVol9->Height) * pVol9->Depth; + pVol8->Width = pVol9->Width; + pVol8->Height = pVol9->Height; + pVol8->Depth = pVol9->Depth; + } + + // If this D3DTEXTURESTAGESTATETYPE has been remapped to a d3d9::D3DSAMPLERSTATETYPE + // it will be returned, otherwise returns -1 + inline d3d9::D3DSAMPLERSTATETYPE GetSamplerStateType9(const D3DTEXTURESTAGESTATETYPE StageType) { + switch (StageType) { + // 13-21: + case D3DTSS_ADDRESSU: return d3d9::D3DSAMP_ADDRESSU; + case D3DTSS_ADDRESSV: return d3d9::D3DSAMP_ADDRESSV; + case D3DTSS_BORDERCOLOR: return d3d9::D3DSAMP_BORDERCOLOR; + case D3DTSS_MAGFILTER: return d3d9::D3DSAMP_MAGFILTER; + case D3DTSS_MINFILTER: return d3d9::D3DSAMP_MINFILTER; + case D3DTSS_MIPFILTER: return d3d9::D3DSAMP_MIPFILTER; + case D3DTSS_MIPMAPLODBIAS: return d3d9::D3DSAMP_MIPMAPLODBIAS; + case D3DTSS_MAXMIPLEVEL: return d3d9::D3DSAMP_MIPFILTER; + case D3DTSS_MAXANISOTROPY: return d3d9::D3DSAMP_MAXANISOTROPY; + // 25: + case D3DTSS_ADDRESSW: return d3d9::D3DSAMP_ADDRESSW; + default: return d3d9::D3DSAMPLERSTATETYPE(-1); + } + } +} + diff --git a/src/d3d8/d3d8_device.cpp b/src/d3d8/d3d8_device.cpp new file mode 100644 index 000000000..61be21e1d --- /dev/null +++ b/src/d3d8/d3d8_device.cpp @@ -0,0 +1,1738 @@ +#include "d3d8_device.h" +#include "d3d8_interface.h" +#include "d3d8_shader.h" + +#ifdef MSC_VER +#pragma fenv_access (on) +#endif + +namespace dxvk { + + static constexpr DWORD isFVF(DWORD Handle) { + return (Handle & D3DFVF_RESERVED0) == 0; + } + + static constexpr DWORD getShaderHandle(DWORD Index) { + return (Index << 1) | D3DFVF_RESERVED0; + } + + static constexpr DWORD getShaderIndex(DWORD Handle) { + if ((Handle & D3DFVF_RESERVED0) != 0) { + return (Handle & ~(D3DFVF_RESERVED0)) >> 1; + } else { + return Handle; + } + } + + struct D3D8VertexShaderInfo { + d3d9::IDirect3DVertexDeclaration9* pVertexDecl = nullptr; + d3d9::IDirect3DVertexShader9* pVertexShader = nullptr; + std::vector declaration; + std::vector function; + }; + + D3D8Device::D3D8Device( + D3D8Interface* pParent, + Com&& pDevice, + D3DDEVTYPE DeviceType, + HWND hFocusWindow, + DWORD BehaviorFlags, + D3DPRESENT_PARAMETERS* pParams) + : D3D8DeviceBase(std::move(pDevice)) + , m_d3d8Options(pParent->GetOptions()) + , m_parent(pParent) + , m_presentParams(*pParams) + , m_deviceType(DeviceType) + , m_window(hFocusWindow) + , m_behaviorFlags(BehaviorFlags) { + // Get the bridge interface to D3D9. + if (FAILED(GetD3D9()->QueryInterface(__uuidof(IDxvkD3D8Bridge), (void**)&m_bridge))) { + throw DxvkError("D3D8Device: ERROR! Failed to get D3D9 Bridge. d3d9.dll might not be DXVK!"); + } + + m_bridge->SetAPIName("D3D8"); + m_bridge->SetD3D8CompatibilityMode(true); + + ResetState(); + RecreateBackBuffersAndAutoDepthStencil(); + + if (m_d3d8Options.batching) + m_batcher = new D3D8Batcher(this, GetD3D9()); + } + + D3D8Device::~D3D8Device() { + if (m_batcher) + delete m_batcher; + + // Delete any remaining state blocks. + for (D3D8StateBlock* block : m_stateBlocks) { + delete block; + } + } + + HRESULT STDMETHODCALLTYPE D3D8Device::GetInfo(DWORD DevInfoID, void* pDevInfoStruct, DWORD DevInfoStructSize) { + Logger::debug(str::format("D3D8Device::GetInfo: ", DevInfoID)); + + if (unlikely(pDevInfoStruct == nullptr || DevInfoStructSize == 0)) + return D3DERR_INVALIDCALL; + + HRESULT res; + d3d9::IDirect3DQuery9* pQuery = nullptr; + + switch (DevInfoID) { + // pre-D3D8 queries + case 0: + case D3DDEVINFOID_TEXTUREMANAGER: + case D3DDEVINFOID_D3DTEXTUREMANAGER: + case D3DDEVINFOID_TEXTURING: + return E_FAIL; + + case D3DDEVINFOID_VCACHE: + // The query will return D3D_OK on Nvidia and D3DERR_NOTAVAILABLE on AMD/Intel + // in D3D9, however in the case of the latter we'll need to return a + // zeroed out query result and S_FALSE. This behavior has been observed both + // on modern native AMD drivers and D3D8-era native ATI drivers. + res = GetD3D9()->CreateQuery(d3d9::D3DQUERYTYPE_VCACHE, &pQuery); + + struct D3DDEVINFO_VCACHE { + DWORD Pattern; + DWORD OptMethod; + DWORD CacheSize; + DWORD MagicNumber; + }; + + if(FAILED(res)) { + if (DevInfoStructSize != sizeof(D3DDEVINFO_VCACHE)) + return D3DERR_INVALIDCALL; + + memset(pDevInfoStruct, 0, sizeof(D3DDEVINFO_VCACHE)); + return S_FALSE; + } + + break; + case D3DDEVINFOID_RESOURCEMANAGER: + // May not be implemented by D9VK. + res = GetD3D9()->CreateQuery(d3d9::D3DQUERYTYPE_RESOURCEMANAGER, &pQuery); + break; + case D3DDEVINFOID_VERTEXSTATS: + res = GetD3D9()->CreateQuery(d3d9::D3DQUERYTYPE_VERTEXSTATS, &pQuery); + break; + + default: + Logger::warn(str::format("D3D8Device::GetInfo: Unsupported device info ID: ", DevInfoID)); + return E_FAIL; + } + + if (unlikely(FAILED(res))) + goto done; + + // Immediately issue the query. + // D3D9 will begin it automatically before ending. + res = pQuery->Issue(D3DISSUE_END); + if (unlikely(FAILED(res))) { + goto done; + } + + // TODO: Will immediately issuing the query without doing any API calls + // actually yield meaingful results? And should we flush or let it mellow? + res = pQuery->GetData(pDevInfoStruct, DevInfoStructSize, D3DGETDATA_FLUSH); + + done: + if (pQuery != nullptr) + pQuery->Release(); + + if (unlikely(FAILED(res))) { + if (res == D3DERR_NOTAVAILABLE) // unsupported + return E_FAIL; + else // any unknown error + return S_FALSE; + } + return res; + } + + + HRESULT STDMETHODCALLTYPE D3D8Device::TestCooperativeLevel() { + // Equivalent of D3D11/DXGI present tests. + return GetD3D9()->TestCooperativeLevel(); + } + + UINT STDMETHODCALLTYPE D3D8Device::GetAvailableTextureMem() { + return GetD3D9()->GetAvailableTextureMem(); + } + + HRESULT STDMETHODCALLTYPE D3D8Device::ResourceManagerDiscardBytes(DWORD bytes) { + return GetD3D9()->EvictManagedResources(); + } + + HRESULT STDMETHODCALLTYPE D3D8Device::GetDirect3D(IDirect3D8** ppD3D8) { + if (ppD3D8 == nullptr) + return D3DERR_INVALIDCALL; + + *ppD3D8 = m_parent.ref(); + return D3D_OK; + } + + HRESULT STDMETHODCALLTYPE D3D8Device::GetDeviceCaps(D3DCAPS8* pCaps) { + d3d9::D3DCAPS9 caps9; + HRESULT res = GetD3D9()->GetDeviceCaps(&caps9); + dxvk::ConvertCaps8(caps9, pCaps); + return res; + } + + HRESULT STDMETHODCALLTYPE D3D8Device::GetDisplayMode(D3DDISPLAYMODE* pMode) { + // swap chain 0 + return GetD3D9()->GetDisplayMode(0, (d3d9::D3DDISPLAYMODE*)pMode); + } + + HRESULT STDMETHODCALLTYPE D3D8Device::GetCreationParameters(D3DDEVICE_CREATION_PARAMETERS* pParameters) { + return GetD3D9()->GetCreationParameters((d3d9::D3DDEVICE_CREATION_PARAMETERS*)pParameters); + } + + HRESULT STDMETHODCALLTYPE D3D8Device::SetCursorProperties( + UINT XHotSpot, + UINT YHotSpot, + IDirect3DSurface8* pCursorBitmap) { + D3D8Surface* surf = static_cast(pCursorBitmap); + return GetD3D9()->SetCursorProperties(XHotSpot, YHotSpot, D3D8Surface::GetD3D9Nullable(surf)); + } + + void STDMETHODCALLTYPE D3D8Device::SetCursorPosition(UINT XScreenSpace, UINT YScreenSpace, DWORD Flags) { + GetD3D9()->SetCursorPosition(XScreenSpace, YScreenSpace, Flags); + } + + // Microsoft d3d8.h in the DirectX 9 SDK uses a different function signature... + void STDMETHODCALLTYPE D3D8Device::SetCursorPosition(int X, int Y, DWORD Flags) { + GetD3D9()->SetCursorPosition(X, Y, Flags); + } + + BOOL STDMETHODCALLTYPE D3D8Device::ShowCursor(BOOL bShow) { + return GetD3D9()->ShowCursor(bShow); + } + + HRESULT STDMETHODCALLTYPE D3D8Device::CreateAdditionalSwapChain( + D3DPRESENT_PARAMETERS* pPresentationParameters, + IDirect3DSwapChain8** ppSwapChain) { + + Com pSwapChain9; + d3d9::D3DPRESENT_PARAMETERS params = ConvertPresentParameters9(pPresentationParameters); + HRESULT res = GetD3D9()->CreateAdditionalSwapChain( + ¶ms, + &pSwapChain9 + ); + + *ppSwapChain = ref(new D3D8SwapChain(this, std::move(pSwapChain9))); + + return res; + } + + + HRESULT STDMETHODCALLTYPE D3D8Device::Reset(D3DPRESENT_PARAMETERS* pPresentationParameters) { + StateChange(); + + m_presentParams = *pPresentationParameters; + ResetState(); + + d3d9::D3DPRESENT_PARAMETERS params = ConvertPresentParameters9(pPresentationParameters); + HRESULT res = GetD3D9()->Reset(¶ms); + + if (FAILED(res)) + return res; + + RecreateBackBuffersAndAutoDepthStencil(); + + return res; + } + + HRESULT STDMETHODCALLTYPE D3D8Device::Present( + const RECT* pSourceRect, + const RECT* pDestRect, + HWND hDestWindowOverride, + const RGNDATA* pDirtyRegion) { + m_batcher->EndFrame(); + StateChange(); + return GetD3D9()->Present(pSourceRect, pDestRect, hDestWindowOverride, pDirtyRegion); + } + + HRESULT STDMETHODCALLTYPE D3D8Device::GetBackBuffer( + UINT iBackBuffer, + D3DBACKBUFFER_TYPE Type, + IDirect3DSurface8** ppBackBuffer) { + InitReturnPtr(ppBackBuffer); + + if (iBackBuffer >= m_backBuffers.size() || m_backBuffers[iBackBuffer] == nullptr) { + Com pSurface9; + HRESULT res = GetD3D9()->GetBackBuffer(0, iBackBuffer, (d3d9::D3DBACKBUFFER_TYPE)Type, &pSurface9); + + if (FAILED(res)) return res; + + m_backBuffers[iBackBuffer] = new D3D8Surface(this, std::move(pSurface9)); + *ppBackBuffer = m_backBuffers[iBackBuffer].ref(); + + return res; + } + + *ppBackBuffer = m_backBuffers[iBackBuffer].ref(); + return D3D_OK; + } + + HRESULT STDMETHODCALLTYPE D3D8Device::GetRasterStatus(D3DRASTER_STATUS* pRasterStatus) { + return GetD3D9()->GetRasterStatus(0, (d3d9::D3DRASTER_STATUS*)pRasterStatus); + } + + void STDMETHODCALLTYPE D3D8Device::SetGammaRamp(DWORD Flags, const D3DGAMMARAMP* pRamp) { + StateChange(); + // For swap chain 0 + GetD3D9()->SetGammaRamp(0, Flags, reinterpret_cast(pRamp)); + } + + void STDMETHODCALLTYPE D3D8Device::GetGammaRamp(D3DGAMMARAMP* pRamp) { + // For swap chain 0 + GetD3D9()->GetGammaRamp(0, reinterpret_cast(pRamp)); + } + + HRESULT STDMETHODCALLTYPE D3D8Device::CreateTexture( + UINT Width, + UINT Height, + UINT Levels, + DWORD Usage, + D3DFORMAT Format, + D3DPOOL Pool, + IDirect3DTexture8** ppTexture) { + InitReturnPtr(ppTexture); + + // Nvidia & Intel workaround for The Lord of the Rings: The Fellowship of the Ring + if (m_d3d8Options.placeP8InScratch && Format == D3DFMT_P8) + Pool = D3DPOOL_SCRATCH; + + Com pTex9 = nullptr; + HRESULT res = GetD3D9()->CreateTexture( + Width, + Height, + Levels, + Usage, + d3d9::D3DFORMAT(Format), + d3d9::D3DPOOL(Pool), + &pTex9, + NULL); + + if (FAILED(res)) + return res; + + *ppTexture = ref(new D3D8Texture2D(this, std::move(pTex9))); + + return res; + } + + HRESULT STDMETHODCALLTYPE D3D8Device::CreateVolumeTexture( + UINT Width, + UINT Height, + UINT Depth, + UINT Levels, + DWORD Usage, + D3DFORMAT Format, + D3DPOOL Pool, + IDirect3DVolumeTexture8** ppVolumeTexture) { + Com pVolume9 = nullptr; + HRESULT res = GetD3D9()->CreateVolumeTexture( + Width, Height, Depth, Levels, + Usage, + d3d9::D3DFORMAT(Format), + d3d9::D3DPOOL(Pool), + &pVolume9, + NULL); + + *ppVolumeTexture = ref(new D3D8Texture3D(this, std::move(pVolume9))); + + return res; + + } + + HRESULT STDMETHODCALLTYPE D3D8Device::CreateCubeTexture( + UINT EdgeLength, + UINT Levels, + DWORD Usage, + D3DFORMAT Format, + D3DPOOL Pool, + IDirect3DCubeTexture8** ppCubeTexture) { + Com pCube9 = nullptr; + HRESULT res = GetD3D9()->CreateCubeTexture( + EdgeLength, + Levels, + Usage, + d3d9::D3DFORMAT(Format), + d3d9::D3DPOOL(Pool), + &pCube9, + NULL); + + *ppCubeTexture = ref(new D3D8TextureCube(this, std::move(pCube9))); + + return res; + } + + HRESULT STDMETHODCALLTYPE D3D8Device::CreateVertexBuffer( + UINT Length, + DWORD Usage, + DWORD FVF, + D3DPOOL Pool, + IDirect3DVertexBuffer8** ppVertexBuffer) { + InitReturnPtr(ppVertexBuffer); + + if (ShouldBatch()) { + *ppVertexBuffer = m_batcher->CreateVertexBuffer(Length, Usage, FVF, Pool); + return D3D_OK; + } + + Com pVertexBuffer9 = nullptr; + + HRESULT res = GetD3D9()->CreateVertexBuffer(Length, Usage, FVF, d3d9::D3DPOOL(Pool), &pVertexBuffer9, NULL); + + if (!FAILED(res)) + *ppVertexBuffer = ref(new D3D8VertexBuffer(this, std::move(pVertexBuffer9), Pool, Usage)); + + return res; + } + + HRESULT STDMETHODCALLTYPE D3D8Device::CreateIndexBuffer( + UINT Length, + DWORD Usage, + D3DFORMAT Format, + D3DPOOL Pool, + IDirect3DIndexBuffer8** ppIndexBuffer) { + InitReturnPtr(ppIndexBuffer); + Com pIndexBuffer9 = nullptr; + + HRESULT res = GetD3D9()->CreateIndexBuffer(Length, Usage, d3d9::D3DFORMAT(Format), d3d9::D3DPOOL(Pool), &pIndexBuffer9, NULL); + + if (!FAILED(res)) + *ppIndexBuffer = ref(new D3D8IndexBuffer(this, std::move(pIndexBuffer9), Pool, Usage)); + + return res; + } + + HRESULT STDMETHODCALLTYPE D3D8Device::CreateRenderTarget( + UINT Width, + UINT Height, + D3DFORMAT Format, + D3DMULTISAMPLE_TYPE MultiSample, + BOOL Lockable, + IDirect3DSurface8** ppSurface) { + Com pSurf9 = nullptr; + HRESULT res = GetD3D9()->CreateRenderTarget( + Width, + Height, + d3d9::D3DFORMAT(Format), + d3d9::D3DMULTISAMPLE_TYPE(MultiSample), + 0, // TODO: CreateRenderTarget MultisampleQuality + Lockable, + &pSurf9, + NULL); + + *ppSurface = ref(new D3D8Surface(this, std::move(pSurf9))); + + return res; + } + + HRESULT STDMETHODCALLTYPE D3D8Device::CreateDepthStencilSurface( + UINT Width, + UINT Height, + D3DFORMAT Format, + D3DMULTISAMPLE_TYPE MultiSample, + IDirect3DSurface8** ppSurface) { + Com pSurf9 = nullptr; + HRESULT res = GetD3D9()->CreateDepthStencilSurface( + Width, + Height, + d3d9::D3DFORMAT(Format), + d3d9::D3DMULTISAMPLE_TYPE(MultiSample), + 0, // TODO: CreateDepthStencilSurface MultisampleQuality + true, // TODO: CreateDepthStencilSurface Discard + &pSurf9, + NULL); + + *ppSurface = ref(new D3D8Surface(this, std::move(pSurf9))); + + return res; + } + + HRESULT STDMETHODCALLTYPE D3D8Device::CreateImageSurface( + UINT Width, + UINT Height, + D3DFORMAT Format, + IDirect3DSurface8** ppSurface) { + // FIXME: Handle D3DPOOL_SCRATCH in CopyRects + D3DPOOL pool = isUnsupportedSurfaceFormat(Format) ? D3DPOOL_SCRATCH : D3DPOOL_SYSTEMMEM; + + Com pSurf = nullptr; + HRESULT res = GetD3D9()->CreateOffscreenPlainSurface( + Width, + Height, + d3d9::D3DFORMAT(Format), + d3d9::D3DPOOL(pool), + &pSurf, + NULL); + + *ppSurface = ref(new D3D8Surface(this, std::move(pSurf))); + + return res; + } + + // Copies texture rect in system mem using memcpy. + // Rects must be congruent, but need not be aligned. + HRESULT copyTextureBuffers( + D3D8Surface* src, + D3D8Surface* dst, + const d3d9::D3DSURFACE_DESC& srcDesc, + const d3d9::D3DSURFACE_DESC& dstDesc, + const RECT& srcRect, + const RECT& dstRect) { + HRESULT res = D3D_OK; + D3DLOCKED_RECT srcLocked, dstLocked; + + // CopyRects cannot perform format conversions. + if (srcDesc.Format != dstDesc.Format) + return D3DERR_INVALIDCALL; + + bool compressed = isDXT(srcDesc.Format); + + res = src->LockRect(&srcLocked, &srcRect, D3DLOCK_READONLY); + if (FAILED(res)) + return res; + + res = dst->LockRect(&dstLocked, &dstRect, 0); + if (FAILED(res)) { + src->UnlockRect(); + return res; + } + + auto rows = srcRect.bottom - srcRect.top; + auto cols = srcRect.right - srcRect.left; + auto bpp = srcLocked.Pitch / srcDesc.Width; + + if (!compressed + && srcRect.left == 0 + && srcRect.right == LONG(srcDesc.Width) + && srcDesc.Width == dstDesc.Width + && srcLocked.Pitch == dstLocked.Pitch) { + + // If copying the entire texture into a congruent destination, + // we can do this in one continuous copy. + std::memcpy(dstLocked.pBits, srcLocked.pBits, srcLocked.Pitch * rows); + + } else { + // Bytes per row of the rect + auto amplitude = cols * bpp; + + // Handle DXT compressed textures. + // TODO: Are rects always 4x4 aligned? + if (compressed) { + // Assume that DXT blocks are 4x4 pixels. + constexpr UINT blockWidth = 4; + constexpr UINT blockHeight = 4; + + // Compute rect dimensions in 4x4 blocks + UINT rectWidthBlocks = cols / blockWidth; + UINT rectHeightBlocks = rows / blockHeight; + + // Compute total texture width in blocks + // to derive block size in bytes using the pitch. + UINT texWidthBlocks = std::max(srcDesc.Width / blockWidth, 1u); + UINT bytesPerBlock = srcLocked.Pitch / texWidthBlocks; + + // Copy H/4 rows of W/4 blocks + amplitude = rectWidthBlocks * bytesPerBlock; + rows = rectHeightBlocks; + } + + // Copy one row at a time + size_t srcOffset = 0, dstOffset = 0; + for (auto i = 0; i < rows; i++) { + std::memcpy( + (uint8_t*)dstLocked.pBits + dstOffset, + (uint8_t*)srcLocked.pBits + srcOffset, + amplitude); + srcOffset += srcLocked.Pitch; + dstOffset += dstLocked.Pitch; + } + } + + res = dst->UnlockRect(); + res = src->UnlockRect(); + return res; + } + + /** + * \brief D3D8 CopyRects implementation + * + * \details + * The following table shows the possible combinations of source + * and destination surface pools, and how we handle each of them. + * + * ┌────────────┬───────────────────────────┬───────────────────────┬───────────────────────┬──────────┐ + * │ Src/Dst │ DEFAULT │ MANAGED │ SYSTEMMEM │ SCRATCH │ + * ├────────────┼───────────────────────────┼───────────────────────┼───────────────────────┼──────────┤ + * │ DEFAULT │ StretchRect │ GetRenderTargetData │ GetRenderTargetData │ - │ + * │ MANAGED │ UpdateTextureFromBuffer │ memcpy │ memcpy │ - │ + * │ SYSTEMMEM │ UpdateSurface │ memcpy │ memcpy │ - │ + * │ SCRATCH │ - │ - │ - │ - │ + * └────────────┴───────────────────────────┴───────────────────────┴───────────────────────┴──────────┘ + */ + HRESULT STDMETHODCALLTYPE D3D8Device::CopyRects( + IDirect3DSurface8* pSourceSurface, + const RECT* pSourceRectsArray, + UINT cRects, + IDirect3DSurface8* pDestinationSurface, + const POINT* pDestPointsArray) { + if (pSourceSurface == NULL || pDestinationSurface == NULL) { + return D3DERR_INVALIDCALL; + } + + // TODO: No format conversion, no stretching, no clipping. + // All src/dest rectangles must fit within the dest surface. + + Com src = static_cast(pSourceSurface); + Com dst = static_cast(pDestinationSurface); + + d3d9::D3DSURFACE_DESC srcDesc, dstDesc; + src->GetD3D9()->GetDesc(&srcDesc); + dst->GetD3D9()->GetDesc(&dstDesc); + + // This method cannot be applied to surfaces whose formats + // are classified as depth stencil formats. + if (unlikely(isDepthStencilFormat(D3DFORMAT(srcDesc.Format)) || + isDepthStencilFormat(D3DFORMAT(dstDesc.Format)))) { + return D3DERR_INVALIDCALL; + } + + StateChange(); + + // If pSourceRectsArray is NULL, then the entire surface is copied + RECT rect; + POINT point = { 0, 0 }; + if (pSourceRectsArray == NULL) { + cRects = 1; + rect.top = rect.left = 0; + rect.right = srcDesc.Width; + rect.bottom = srcDesc.Height; + pSourceRectsArray = ▭ + + pDestPointsArray = &point; + } + + HRESULT res = D3DERR_INVALIDCALL; + + for (UINT i = 0; i < cRects; i++) { + + RECT srcRect, dstRect; + srcRect = pSourceRectsArray[i]; + + // True if the copy is asymmetric + bool asymmetric = true; + // True if the copy requires stretching (not technically supported) + bool stretch = true; + // True if the copy is not perfectly aligned (supported) + bool offset = true; + + if (pDestPointsArray != NULL) { + dstRect.left = pDestPointsArray[i].x; + dstRect.right = dstRect.left + (srcRect.right - srcRect.left); + dstRect.top = pDestPointsArray[i].y; + dstRect.bottom = dstRect.top + (srcRect.bottom - srcRect.top); + asymmetric = dstRect.left != srcRect.left || dstRect.top != srcRect.top + || dstRect.right != srcRect.right || dstRect.bottom != srcRect.bottom; + + stretch = (dstRect.right-dstRect.left) != (srcRect.right-srcRect.left) + || (dstRect.bottom-dstRect.top) != (srcRect.bottom-srcRect.top); + + offset = !stretch && asymmetric; + } else { + dstRect = srcRect; + asymmetric = stretch = offset = false; + } + + POINT dstPt = { dstRect.left, dstRect.top }; + + res = D3DERR_INVALIDCALL; + + auto unhandled = [&] { + Logger::debug(str::format("CopyRects: Hit unhandled case from src pool ", srcDesc.Pool, " to dst pool ", dstDesc.Pool)); + return D3DERR_INVALIDCALL; + }; + + switch (dstDesc.Pool) { + + // Dest: DEFAULT + case D3DPOOL_DEFAULT: + switch (srcDesc.Pool) { + case D3DPOOL_DEFAULT: { + // DEFAULT -> DEFAULT: use StretchRect + res = GetD3D9()->StretchRect( + src->GetD3D9(), + &srcRect, + dst->GetD3D9(), + &dstRect, + d3d9::D3DTEXF_NONE + ); + goto done; + } + case D3DPOOL_MANAGED: { + // MANAGED -> DEFAULT: UpdateTextureFromBuffer + res = m_bridge->UpdateTextureFromBuffer( + src->GetD3D9(), + dst->GetD3D9(), + &srcRect, + &dstPt + ); + goto done; + } + case D3DPOOL_SYSTEMMEM: { + // SYSTEMMEM -> DEFAULT: use UpdateSurface + res = GetD3D9()->UpdateSurface( + src->GetD3D9(), + &srcRect, + dst->GetD3D9(), + &dstPt + ); + goto done; + } + case D3DPOOL_SCRATCH: + default: { + // TODO: Unhandled case. + return unhandled(); + } + } break; + + // Dest: MANAGED + case D3DPOOL_MANAGED: + switch (srcDesc.Pool) { + case D3DPOOL_DEFAULT: { + // TODO: Copy on GPU (handle MANAGED similarly to SYSTEMMEM for now) + + // Get temporary off-screen surface for stretching. + Com pBlitImage = dst->GetBlitImage(); + + // Stretch the source RT to the temporary surface. + res = GetD3D9()->StretchRect( + src->GetD3D9(), + &srcRect, + pBlitImage.ptr(), + &dstRect, + d3d9::D3DTEXF_NONE); + if (FAILED(res)) { + goto done; + } + + // Now sync the rendertarget data into main memory. + res = GetD3D9()->GetRenderTargetData(pBlitImage.ptr(), dst->GetD3D9()); + goto done; + } + case D3DPOOL_MANAGED: + case D3DPOOL_SYSTEMMEM: { + // SYSTEMMEM -> MANAGED: LockRect / memcpy + + if (stretch) { + res = D3DERR_INVALIDCALL; + goto done; + } + + res = copyTextureBuffers(src.ptr(), dst.ptr(), srcDesc, dstDesc, srcRect, dstRect); + + goto done; + } + case D3DPOOL_SCRATCH: + default: { + // TODO: Unhandled case. + return unhandled(); + } + } break; + + // DEST: SYSTEMMEM + case D3DPOOL_SYSTEMMEM: { + + // RT (DEFAULT) -> SYSTEMMEM: Use GetRenderTargetData as fast path if possible + if ((srcDesc.Usage & D3DUSAGE_RENDERTARGET || m_renderTarget.ptr() == src.ptr())) { + + // GetRenderTargetData works if the formats and sizes match + if (srcDesc.MultiSampleType == d3d9::D3DMULTISAMPLE_NONE + && srcDesc.Width == dstDesc.Width + && srcDesc.Height == dstDesc.Height + && srcDesc.Format == dstDesc.Format + && !asymmetric) { + res = GetD3D9()->GetRenderTargetData(src->GetD3D9(), dst->GetD3D9()); + goto done; + } + } + + switch (srcDesc.Pool) { + case D3DPOOL_DEFAULT: { + // Get temporary off-screen surface for stretching. + Com pBlitImage = dst->GetBlitImage(); + + // Stretch the source RT to the temporary surface. + res = GetD3D9()->StretchRect( + src->GetD3D9(), + &srcRect, + pBlitImage.ptr(), + &dstRect, + d3d9::D3DTEXF_NONE); + if (FAILED(res)) { + goto done; + } + + // Now sync the rendertarget data into main memory. + res = GetD3D9()->GetRenderTargetData(pBlitImage.ptr(), dst->GetD3D9()); + goto done; + } + + // SYSMEM/MANAGED -> SYSMEM: LockRect / memcpy + case D3DPOOL_MANAGED: + case D3DPOOL_SYSTEMMEM: { + if (stretch) { + res = D3DERR_INVALIDCALL; + goto done; + } + + res = copyTextureBuffers(src.ptr(), dst.ptr(), srcDesc, dstDesc, srcRect, dstRect); + goto done; + } + case D3DPOOL_SCRATCH: + default: { + // TODO: Unhandled case. + return unhandled(); + } + } break; + } + + // DEST: SCRATCH + case D3DPOOL_SCRATCH: + default: { + // TODO: Unhandled case. + return unhandled(); + } + } + + done: + if (FAILED(res)) { + Logger::debug(str::format("CopyRects: FAILED to copy from src pool ", srcDesc.Pool, " to dst pool ", dstDesc.Pool)); + return res; + } + + } + + return res; + } + + HRESULT STDMETHODCALLTYPE D3D8Device::UpdateTexture( + IDirect3DBaseTexture8* pSourceTexture, + IDirect3DBaseTexture8* pDestinationTexture) { + D3D8Texture2D* src = static_cast(pSourceTexture); + D3D8Texture2D* dst = static_cast(pDestinationTexture); + + StateChange(); + return GetD3D9()->UpdateTexture(D3D8Texture2D::GetD3D9Nullable(src), D3D8Texture2D::GetD3D9Nullable(dst)); + } + + HRESULT STDMETHODCALLTYPE D3D8Device::GetFrontBuffer(IDirect3DSurface8* pDestSurface) { + if (unlikely(pDestSurface == nullptr)) + return D3DERR_INVALIDCALL; + + Com surf = static_cast(pDestSurface); + + StateChange(); + // This actually gets a copy of the front buffer and writes it to pDestSurface + return GetD3D9()->GetFrontBufferData(0, D3D8Surface::GetD3D9Nullable(surf)); + } + + HRESULT STDMETHODCALLTYPE D3D8Device::SetRenderTarget(IDirect3DSurface8* pRenderTarget, IDirect3DSurface8* pNewZStencil) { + HRESULT res; + + if (pRenderTarget != NULL) { + D3D8Surface* surf = static_cast(pRenderTarget); + + if(likely(m_renderTarget.ptr() != surf)) { + StateChange(); + res = GetD3D9()->SetRenderTarget(0, surf->GetD3D9()); + + if (FAILED(res)) return res; + + if (likely(m_renderTarget != surf)) + m_renderTarget = surf; + } + } + + // SetDepthStencilSurface is a separate call + D3D8Surface* zStencil = static_cast(pNewZStencil); + + if(likely(m_depthStencil.ptr() != zStencil)) { + StateChange(); + res = GetD3D9()->SetDepthStencilSurface(D3D8Surface::GetD3D9Nullable(zStencil)); + + if (FAILED(res)) return res; + + if (likely(m_depthStencil != zStencil)) + m_depthStencil = zStencil; + } + + return D3D_OK; + } + + HRESULT STDMETHODCALLTYPE D3D8Device::GetRenderTarget(IDirect3DSurface8** ppRenderTarget) { + InitReturnPtr(ppRenderTarget); + + if (unlikely(m_renderTarget == nullptr)) { + Com pRT9 = nullptr; + HRESULT res = GetD3D9()->GetRenderTarget(0, &pRT9); // use RT index 0 + + if(FAILED(res)) return res; + + m_renderTarget = new D3D8Surface(this, std::move(pRT9)); + + *ppRenderTarget = m_renderTarget.ref(); + return res; + } + + *ppRenderTarget = m_renderTarget.ref(); + return D3D_OK; + } + + HRESULT STDMETHODCALLTYPE D3D8Device::GetDepthStencilSurface(IDirect3DSurface8** ppZStencilSurface) { + InitReturnPtr(ppZStencilSurface); + + if (unlikely(m_depthStencil == nullptr)) { + Com pStencil9 = nullptr; + HRESULT res = GetD3D9()->GetDepthStencilSurface(&pStencil9); + + if(FAILED(res)) return res; + + m_depthStencil = new D3D8Surface(this, std::move(pStencil9)); + + *ppZStencilSurface = m_depthStencil.ref(); + return res; + } + + *ppZStencilSurface = m_depthStencil.ref(); + return D3D_OK; + } + + HRESULT STDMETHODCALLTYPE D3D8Device::BeginScene() { return GetD3D9()->BeginScene(); } + + HRESULT STDMETHODCALLTYPE D3D8Device::EndScene() { StateChange(); return GetD3D9()->EndScene(); } + + HRESULT STDMETHODCALLTYPE D3D8Device::Clear( + DWORD Count, + const D3DRECT* pRects, + DWORD Flags, + D3DCOLOR Color, + float Z, + DWORD Stencil) { + StateChange(); + return GetD3D9()->Clear(Count, pRects, Flags, Color, Z, Stencil); + } + + HRESULT STDMETHODCALLTYPE D3D8Device::SetTransform(D3DTRANSFORMSTATETYPE State, const D3DMATRIX* pMatrix) { + StateChange(); + return GetD3D9()->SetTransform(d3d9::D3DTRANSFORMSTATETYPE(State), pMatrix); + } + + HRESULT STDMETHODCALLTYPE D3D8Device::GetTransform(D3DTRANSFORMSTATETYPE State, D3DMATRIX* pMatrix) { + return GetD3D9()->GetTransform(d3d9::D3DTRANSFORMSTATETYPE(State), pMatrix); + } + + HRESULT STDMETHODCALLTYPE D3D8Device::MultiplyTransform(D3DTRANSFORMSTATETYPE TransformState, const D3DMATRIX* pMatrix) { + StateChange(); + return GetD3D9()->MultiplyTransform(d3d9::D3DTRANSFORMSTATETYPE(TransformState), pMatrix); + } + + HRESULT STDMETHODCALLTYPE D3D8Device::SetViewport(const D3DVIEWPORT8* pViewport) { + StateChange(); + return GetD3D9()->SetViewport(reinterpret_cast(pViewport)); + } + + HRESULT STDMETHODCALLTYPE D3D8Device::GetViewport(D3DVIEWPORT8* pViewport) { + return GetD3D9()->GetViewport(reinterpret_cast(pViewport)); + } + + HRESULT STDMETHODCALLTYPE D3D8Device::SetMaterial(const D3DMATERIAL8* pMaterial) { + StateChange(); + return GetD3D9()->SetMaterial((const d3d9::D3DMATERIAL9*)pMaterial); + } + + HRESULT STDMETHODCALLTYPE D3D8Device::GetMaterial(D3DMATERIAL8* pMaterial) { + return GetD3D9()->GetMaterial((d3d9::D3DMATERIAL9*)pMaterial); + } + + HRESULT STDMETHODCALLTYPE D3D8Device::SetLight(DWORD Index, const D3DLIGHT8* pLight) { + StateChange(); + return GetD3D9()->SetLight(Index, (const d3d9::D3DLIGHT9*)pLight); + } + + HRESULT STDMETHODCALLTYPE D3D8Device::GetLight(DWORD Index, D3DLIGHT8* pLight) { + return GetD3D9()->GetLight(Index, (d3d9::D3DLIGHT9*)pLight); + } + + HRESULT STDMETHODCALLTYPE D3D8Device::LightEnable(DWORD Index, BOOL Enable) { + StateChange(); + return GetD3D9()->LightEnable(Index, Enable); + } + + HRESULT STDMETHODCALLTYPE D3D8Device::GetLightEnable(DWORD Index, BOOL* pEnable) { + return GetD3D9()->GetLightEnable(Index, pEnable); + } + + HRESULT STDMETHODCALLTYPE D3D8Device::SetClipPlane(DWORD Index, const float* pPlane) { + StateChange(); + return GetD3D9()->SetClipPlane(Index, pPlane); + } + + HRESULT STDMETHODCALLTYPE D3D8Device::GetClipPlane(DWORD Index, float* pPlane) { + return GetD3D9()->GetClipPlane(Index, pPlane); + } + + HRESULT STDMETHODCALLTYPE D3D8Device::CreateStateBlock( + D3DSTATEBLOCKTYPE Type, + DWORD* pToken) { + Com pStateBlock9; + HRESULT res = GetD3D9()->CreateStateBlock(d3d9::D3DSTATEBLOCKTYPE(Type), &pStateBlock9); + + D3D8StateBlock* pStateBlock = new D3D8StateBlock(this, Type, pStateBlock9.ref()); + m_stateBlocks.insert(pStateBlock); + + *pToken = DWORD(reinterpret_cast(pStateBlock)); + + return res; + } + + HRESULT STDMETHODCALLTYPE D3D8Device::CaptureStateBlock(DWORD Token) { + return reinterpret_cast(Token)->Capture(); + } + + HRESULT STDMETHODCALLTYPE D3D8Device::ApplyStateBlock(DWORD Token) { + StateChange(); + return reinterpret_cast(Token)->Apply(); + } + + HRESULT STDMETHODCALLTYPE D3D8Device::DeleteStateBlock(DWORD Token) { + // "Applications cannot delete a device-state block while another is being recorded" + if (unlikely(ShouldRecord())) + return D3DERR_INVALIDCALL; + + D3D8StateBlock* block = reinterpret_cast(Token); + m_stateBlocks.erase(block); + delete block; + + return D3D_OK; + } + + HRESULT STDMETHODCALLTYPE D3D8Device::BeginStateBlock() { + if (unlikely(m_recorder != nullptr)) + return D3DERR_INVALIDCALL; + + m_recorder = new D3D8StateBlock(this); + m_stateBlocks.insert(m_recorder); + + return GetD3D9()->BeginStateBlock(); + } + + HRESULT STDMETHODCALLTYPE D3D8Device::EndStateBlock(DWORD* pToken) { + if (unlikely(pToken == nullptr || m_recorder == nullptr)) + return D3DERR_INVALIDCALL; + + Com pStateBlock; + HRESULT res = GetD3D9()->EndStateBlock(&pStateBlock); + + m_recorder->SetD3D9(std::move(pStateBlock)); + + *pToken = DWORD(reinterpret_cast(m_recorder)); + + m_recorder = nullptr; + + return res; + } + + HRESULT STDMETHODCALLTYPE D3D8Device::SetClipStatus(const D3DCLIPSTATUS8* pClipStatus) { + StateChange(); + return GetD3D9()->SetClipStatus(reinterpret_cast(pClipStatus)); + } + + HRESULT STDMETHODCALLTYPE D3D8Device::GetClipStatus(D3DCLIPSTATUS8* pClipStatus) { + return GetD3D9()->GetClipStatus(reinterpret_cast(pClipStatus)); + } + + HRESULT STDMETHODCALLTYPE D3D8Device::GetTexture(DWORD Stage, IDirect3DBaseTexture8** ppTexture) { + InitReturnPtr(ppTexture); + + *ppTexture = m_textures[Stage].ref(); + + return D3D_OK; + } + + HRESULT STDMETHODCALLTYPE D3D8Device::SetTexture(DWORD Stage, IDirect3DBaseTexture8* pTexture) { + if (unlikely(Stage >= d8caps::MAX_TEXTURE_STAGES)) + return D3DERR_INVALIDCALL; + + if (unlikely(ShouldRecord())) + return m_recorder->SetTexture(Stage, pTexture); + + D3D8Texture2D* tex = static_cast(pTexture); + + if(unlikely(m_textures[Stage].ptr() == tex)) + return D3D_OK; + + StateChange(); + + m_textures[Stage] = tex; + + return GetD3D9()->SetTexture(Stage, D3D8Texture2D::GetD3D9Nullable(tex)); + } + + HRESULT STDMETHODCALLTYPE D3D8Device::GetTextureStageState( + DWORD Stage, + D3DTEXTURESTAGESTATETYPE Type, + DWORD* pValue) { + d3d9::D3DSAMPLERSTATETYPE stateType = GetSamplerStateType9(Type); + + if (stateType != -1) { + // if the type has been remapped to a sampler state type: + return GetD3D9()->GetSamplerState(Stage, stateType, pValue); + } + else { + return GetD3D9()->GetTextureStageState(Stage, d3d9::D3DTEXTURESTAGESTATETYPE(Type), pValue); + } + } + + HRESULT STDMETHODCALLTYPE D3D8Device::SetTextureStageState( + DWORD Stage, + D3DTEXTURESTAGESTATETYPE Type, + DWORD Value) { + d3d9::D3DSAMPLERSTATETYPE stateType = GetSamplerStateType9(Type); + + StateChange(); + if (stateType != -1) { + // if the type has been remapped to a sampler state type: + return GetD3D9()->SetSamplerState(Stage, stateType, Value); + } else { + return GetD3D9()->SetTextureStageState(Stage, d3d9::D3DTEXTURESTAGESTATETYPE(Type), Value); + } + } + + HRESULT STDMETHODCALLTYPE D3D8Device::ValidateDevice(DWORD* pNumPasses) { + return GetD3D9()->ValidateDevice(pNumPasses); + } + + HRESULT STDMETHODCALLTYPE D3D8Device::SetPaletteEntries(UINT PaletteNumber, const PALETTEENTRY* pEntries) { + StateChange(); + return GetD3D9()->SetPaletteEntries(PaletteNumber, pEntries); + } + + HRESULT STDMETHODCALLTYPE D3D8Device::GetPaletteEntries(UINT PaletteNumber, PALETTEENTRY* pEntries) { + return GetD3D9()->GetPaletteEntries(PaletteNumber, pEntries); + } + + HRESULT STDMETHODCALLTYPE D3D8Device::SetCurrentTexturePalette(UINT PaletteNumber) { + StateChange(); + return GetD3D9()->SetCurrentTexturePalette(PaletteNumber); + } + + HRESULT STDMETHODCALLTYPE D3D8Device::GetCurrentTexturePalette(UINT* PaletteNumber) { + return GetD3D9()->GetCurrentTexturePalette(PaletteNumber); + } + + HRESULT STDMETHODCALLTYPE D3D8Device::DrawPrimitive( + D3DPRIMITIVETYPE PrimitiveType, + UINT StartVertex, + UINT PrimitiveCount) { + if (ShouldBatch()) + return m_batcher->DrawPrimitive(PrimitiveType, StartVertex, PrimitiveCount); + return GetD3D9()->DrawPrimitive(d3d9::D3DPRIMITIVETYPE(PrimitiveType), StartVertex, PrimitiveCount); + } + + HRESULT STDMETHODCALLTYPE D3D8Device::DrawIndexedPrimitive( + D3DPRIMITIVETYPE PrimitiveType, + UINT MinVertexIndex, + UINT NumVertices, + UINT StartIndex, + UINT PrimitiveCount) { + return GetD3D9()->DrawIndexedPrimitive( + d3d9::D3DPRIMITIVETYPE(PrimitiveType), + m_baseVertexIndex, // set by SetIndices + MinVertexIndex, + NumVertices, + StartIndex, + PrimitiveCount); + } + + HRESULT STDMETHODCALLTYPE D3D8Device::DrawPrimitiveUP( + D3DPRIMITIVETYPE PrimitiveType, + UINT PrimitiveCount, + const void* pVertexStreamZeroData, + UINT VertexStreamZeroStride) { + StateChange(); + + // Stream 0 is set to null by this call + m_streams[0] = D3D8VBO {nullptr, 0}; + + return GetD3D9()->DrawPrimitiveUP(d3d9::D3DPRIMITIVETYPE(PrimitiveType), PrimitiveCount, pVertexStreamZeroData, VertexStreamZeroStride); + } + + HRESULT STDMETHODCALLTYPE D3D8Device::DrawIndexedPrimitiveUP( + D3DPRIMITIVETYPE PrimitiveType, + UINT MinVertexIndex, + UINT NumVertices, + UINT PrimitiveCount, + const void* pIndexData, + D3DFORMAT IndexDataFormat, + const void* pVertexStreamZeroData, + UINT VertexStreamZeroStride) { + StateChange(); + + // Stream 0 and the index buffer are set to null by this call + m_streams[0] = D3D8VBO {nullptr, 0}; + m_indices = nullptr; + m_baseVertexIndex = 0; + + return GetD3D9()->DrawIndexedPrimitiveUP( + d3d9::D3DPRIMITIVETYPE(PrimitiveType), + MinVertexIndex, + NumVertices, + PrimitiveCount, + pIndexData, + d3d9::D3DFORMAT(IndexDataFormat), + pVertexStreamZeroData, + VertexStreamZeroStride); + } + + HRESULT STDMETHODCALLTYPE D3D8Device::ProcessVertices( + UINT SrcStartIndex, + UINT DestIndex, + UINT VertexCount, + IDirect3DVertexBuffer8* pDestBuffer, + DWORD Flags) { + if (unlikely(!pDestBuffer)) + return D3DERR_INVALIDCALL; + D3D8VertexBuffer* buffer = static_cast(pDestBuffer); + return GetD3D9()->ProcessVertices( + SrcStartIndex, + DestIndex, + VertexCount, + buffer->GetD3D9(), + nullptr, + Flags + ); + } + + HRESULT STDMETHODCALLTYPE D3D8Device::SetVertexShaderConstant( + DWORD StartRegister, + const void* pConstantData, + DWORD ConstantCount) { + StateChange(); + // ConstantCount is actually the same as Vector4fCount + return GetD3D9()->SetVertexShaderConstantF(StartRegister, reinterpret_cast(pConstantData), ConstantCount); + } + + HRESULT STDMETHODCALLTYPE D3D8Device::GetVertexShaderConstant(DWORD Register, void* pConstantData, DWORD ConstantCount) { + return GetD3D9()->GetVertexShaderConstantF(Register, (float*)pConstantData, ConstantCount); + } + + HRESULT STDMETHODCALLTYPE D3D8Device::SetStreamSource( + UINT StreamNumber, + IDirect3DVertexBuffer8* pStreamData, + UINT Stride) { + if (unlikely(StreamNumber >= d8caps::MAX_STREAMS)) + return D3DERR_INVALIDCALL; + + D3D8VertexBuffer* buffer = static_cast(pStreamData); + + if (ShouldBatch()) + m_batcher->SetStream(StreamNumber, buffer, Stride); + + m_streams[StreamNumber] = D3D8VBO {buffer, Stride}; + + // DXVK: Never fails + return GetD3D9()->SetStreamSource(StreamNumber, D3D8VertexBuffer::GetD3D9Nullable(buffer), 0, Stride); + } + + HRESULT STDMETHODCALLTYPE D3D8Device::GetStreamSource( + UINT StreamNumber, + IDirect3DVertexBuffer8** ppStreamData, + UINT* pStride) { + InitReturnPtr(ppStreamData); + + if (likely(pStride != nullptr)) + *pStride = 0; + + if (unlikely(ppStreamData == nullptr || pStride == nullptr)) + return D3DERR_INVALIDCALL; + + if (unlikely(StreamNumber >= d8caps::MAX_STREAMS)) + return D3DERR_INVALIDCALL; + + const D3D8VBO& vbo = m_streams[StreamNumber]; + + *ppStreamData = vbo.buffer.ref(); + *pStride = vbo.stride; + + return D3D_OK; + } + + HRESULT STDMETHODCALLTYPE D3D8Device::SetIndices(IDirect3DIndexBuffer8* pIndexData, UINT BaseVertexIndex) { + if (unlikely(ShouldRecord())) + return m_recorder->SetIndices(pIndexData, BaseVertexIndex); + + // used by DrawIndexedPrimitive + m_baseVertexIndex = static_cast(BaseVertexIndex); + + D3D8IndexBuffer* buffer = static_cast(pIndexData); + + if (ShouldBatch()) + m_batcher->SetIndices(buffer, m_baseVertexIndex); + + m_indices = buffer; + + // DXVK: Never fails + return GetD3D9()->SetIndices(D3D8IndexBuffer::GetD3D9Nullable(buffer)); + } + + HRESULT STDMETHODCALLTYPE D3D8Device::GetIndices( + IDirect3DIndexBuffer8** ppIndexData, + UINT* pBaseVertexIndex) { + InitReturnPtr(ppIndexData); + + *ppIndexData = m_indices.ref(); + *pBaseVertexIndex = m_baseVertexIndex; + + return D3D_OK; + } + + HRESULT STDMETHODCALLTYPE D3D8Device::GetPixelShaderConstant(DWORD Register, void* pConstantData, DWORD ConstantCount) { + return GetD3D9()->GetPixelShaderConstantF(Register, (float*)pConstantData, ConstantCount); + } + + HRESULT STDMETHODCALLTYPE D3D8Device::SetPixelShaderConstant( + DWORD StartRegister, + const void* pConstantData, + DWORD ConstantCount) { + StateChange(); + // ConstantCount is actually the same as Vector4fCount + return GetD3D9()->SetPixelShaderConstantF(StartRegister, reinterpret_cast(pConstantData), ConstantCount); + } + + HRESULT STDMETHODCALLTYPE D3D8Device::DrawRectPatch( + UINT Handle, + const float* pNumSegs, + const D3DRECTPATCH_INFO* pRectPatchInfo) { + return GetD3D9()->DrawRectPatch(Handle, pNumSegs, reinterpret_cast(pRectPatchInfo)); + } + + HRESULT STDMETHODCALLTYPE D3D8Device::DrawTriPatch( + UINT Handle, + const float* pNumSegs, + const D3DTRIPATCH_INFO* pTriPatchInfo) { + return GetD3D9()->DrawTriPatch(Handle, pNumSegs, reinterpret_cast(pTriPatchInfo)); + } + + HRESULT STDMETHODCALLTYPE D3D8Device::DeletePatch(UINT Handle) { + return GetD3D9()->DeletePatch(Handle); + } + + + // Render States // + + // ZBIAS can be an integer from 0 to 1 and needs to be remapped to float + static constexpr float ZBIAS_SCALE = -0.000005f; + static constexpr float ZBIAS_SCALE_INV = 1 / ZBIAS_SCALE; + + HRESULT STDMETHODCALLTYPE D3D8Device::SetRenderState(D3DRENDERSTATETYPE State, DWORD Value) { + d3d9::D3DRENDERSTATETYPE State9 = (d3d9::D3DRENDERSTATETYPE)State; + bool stateChange = true; + + switch (State) { + // Most render states translate 1:1 to D3D9 + default: + break; + + // TODO: D3DRS_LINEPATTERN - vkCmdSetLineRasterizationModeEXT + case D3DRS_LINEPATTERN: { + [[maybe_unused]] + D3DLINEPATTERN pattern = bit::cast(Value); + stateChange = false; + } break; + + // Not supported by D3D8. + case D3DRS_ZVISIBLE: + stateChange = false; + break; + + // TODO: Not implemented by D9VK. Try anyway. + case D3DRS_EDGEANTIALIAS: + State9 = d3d9::D3DRS_ANTIALIASEDLINEENABLE; + break; + + case D3DRS_ZBIAS: + State9 = d3d9::D3DRS_DEPTHBIAS; + Value = bit::cast(float(Value) * ZBIAS_SCALE); + break; + + case D3DRS_SOFTWAREVERTEXPROCESSING: + // D3D9 can return D3DERR_INVALIDCALL, but we don't care. + if (!(m_behaviorFlags & D3DCREATE_MIXED_VERTEXPROCESSING)) + return D3D_OK; + + // This was a very easy footgun for D3D8 applications. + if (unlikely(ShouldRecord())) + return m_recorder->SetSoftwareVertexProcessing(Value); + + return GetD3D9()->SetSoftwareVertexProcessing(Value); + + // TODO: D3DRS_PATCHSEGMENTS + case D3DRS_PATCHSEGMENTS: + stateChange = false; + break; + } + + if (stateChange) { + DWORD value; + GetRenderState(State, &value); + if (value != Value) + StateChange(); + } + + return GetD3D9()->SetRenderState(State9, Value); + } + + HRESULT STDMETHODCALLTYPE D3D8Device::GetRenderState(D3DRENDERSTATETYPE State, DWORD* pValue) { + d3d9::D3DRENDERSTATETYPE State9 = (d3d9::D3DRENDERSTATETYPE)State; + + switch (State) { + // Most render states translate 1:1 to D3D9 + default: + break; + + // TODO: D3DRS_LINEPATTERN + case D3DRS_LINEPATTERN: + break; + + // Not supported by D3D8. + case D3DRS_ZVISIBLE: + break; + + case D3DRS_EDGEANTIALIAS: + State9 = d3d9::D3DRS_ANTIALIASEDLINEENABLE; + break; + + case D3DRS_ZBIAS: { + float bias = 0; + HRESULT res = GetD3D9()->GetRenderState(d3d9::D3DRS_DEPTHBIAS, (DWORD*)&bias); + *pValue = bit::cast(bias * ZBIAS_SCALE_INV); + return res; + } break; + + case D3DRS_SOFTWAREVERTEXPROCESSING: + return GetD3D9()->GetSoftwareVertexProcessing(); + + // TODO: D3DRS_PATCHSEGMENTS + case D3DRS_PATCHSEGMENTS: + break; + } + + return GetD3D9()->GetRenderState(State9, pValue); + } + + // Vertex Shaders // + + HRESULT STDMETHODCALLTYPE D3D8Device::CreateVertexShader( + const DWORD* pDeclaration, + const DWORD* pFunction, + DWORD* pHandle, + DWORD Usage ) { + D3D8VertexShaderInfo& info = m_vertexShaders.emplace_back(); + + // Store D3D8 bytecodes in the shader info + if (pDeclaration != nullptr) + for (UINT i = 0; pDeclaration[i+1] != D3DVSD_END(); i++) + info.declaration.push_back(pDeclaration[i]); + + if (pFunction != nullptr) + for (UINT i = 0; pFunction[i+1] != D3DVS_END(); i++) + info.function.push_back(pFunction[i]); + + D3D9VertexShaderCode result = TranslateVertexShader8(pDeclaration, pFunction, m_d3d8Options); + + // Create vertex declaration + HRESULT res = GetD3D9()->CreateVertexDeclaration(result.declaration, &(info.pVertexDecl)); + if (FAILED(res)) + return res; + + if (pFunction != nullptr) { + res = GetD3D9()->CreateVertexShader(result.function.data(), &(info.pVertexShader)); + } else { + // pFunction is NULL: fixed function pipeline + info.pVertexShader = nullptr; + } + + // Set bit to indicate this is not an FVF + *pHandle = getShaderHandle(m_vertexShaders.size() - 1); + + return res; + } + + inline D3D8VertexShaderInfo* getVertexShaderInfo(D3D8Device* device, DWORD Handle) { + + Handle = getShaderIndex(Handle); + + if (unlikely(Handle >= device->m_vertexShaders.size())) { + Logger::debug(str::format("getVertexShaderInfo: Invalid vertex shader index ", std::hex, Handle)); + return nullptr; + } + + D3D8VertexShaderInfo& info = device->m_vertexShaders[Handle]; + + if (unlikely(!info.pVertexDecl && !info.pVertexShader)) { + Logger::debug(str::format("getVertexShaderInfo: Application provided deleted vertex shader ", std::hex, Handle)); + return nullptr; + } + + return &info; + } + + HRESULT STDMETHODCALLTYPE D3D8Device::SetVertexShader( DWORD Handle ) { + if (unlikely(ShouldRecord())) { + return m_recorder->SetVertexShader(Handle); + } + + // Check for extra bit that indicates this is not an FVF + if (!isFVF(Handle)) { + + D3D8VertexShaderInfo* info = getVertexShaderInfo(this, Handle); + + if (!info) + return D3DERR_INVALIDCALL; + + StateChange(); + + // Cache current shader + m_currentVertexShader = Handle; + + GetD3D9()->SetVertexDeclaration(info->pVertexDecl); + return GetD3D9()->SetVertexShader(info->pVertexShader); + + } else if (m_currentVertexShader != Handle) { + StateChange(); + + // Cache current FVF + m_currentVertexShader = Handle; + + //GetD3D9()->SetVertexDeclaration(nullptr); + GetD3D9()->SetVertexShader(nullptr); + return GetD3D9()->SetFVF( Handle ); + } + return D3D_OK; + } + + HRESULT STDMETHODCALLTYPE D3D8Device::GetVertexShader(DWORD* pHandle) { + // Return cached shader + *pHandle = m_currentVertexShader; + + return D3D_OK; + + /* + // Slow path. Use to debug cached shader validation. // + + d3d9::IDirect3DVertexShader9* pVertexShader; + HRESULT res = GetD3D9()->GetVertexShader(&pVertexShader); + + if (FAILED(res) || pVertexShader == nullptr) { + return GetD3D9()->GetFVF(pHandle); + } + + for (unsigned int i = 0; i < m_vertexShaders.size(); i++) { + D3D8VertexShaderInfo& info = m_vertexShaders[i]; + + if (info.pVertexShader == pVertexShader) { + *pHandle = getShaderHandle(DWORD(i)); + return res; + } + } + + return res; + */ + } + + HRESULT STDMETHODCALLTYPE D3D8Device::DeleteVertexShader(DWORD Handle) { + if (!isFVF(Handle)) { + D3D8VertexShaderInfo* info = getVertexShaderInfo(this, Handle); + + if (!info) + return D3DERR_INVALIDCALL; + + if (info->pVertexDecl) + info->pVertexDecl->Release(); + if (info->pVertexShader) + info->pVertexShader->Release(); + + info->declaration.clear(); + info->function.clear(); + } + + return D3D_OK; + } + + HRESULT STDMETHODCALLTYPE D3D8Device::GetVertexShaderDeclaration(DWORD Handle, void* pData, DWORD* pSizeOfData) { + D3D8VertexShaderInfo* pInfo = getVertexShaderInfo(this, Handle); + + if (unlikely(!pInfo)) + return D3DERR_INVALIDCALL; + + UINT SizeOfData = *pSizeOfData; + + // Get actual size + UINT ActualSize = pInfo->declaration.size() * sizeof(DWORD); + + if (pData == nullptr) { + *pSizeOfData = ActualSize; + return D3D_OK; + } + + // D3D8-specific behavior + if (SizeOfData < ActualSize) { + *pSizeOfData = ActualSize; + return D3DERR_MOREDATA; + } + + memcpy(pData, pInfo->declaration.data(), ActualSize); + return D3D_OK; + } + + HRESULT STDMETHODCALLTYPE D3D8Device::GetVertexShaderFunction(DWORD Handle, void* pData, DWORD* pSizeOfData) { + D3D8VertexShaderInfo* pInfo = getVertexShaderInfo(this, Handle); + + if (unlikely(!pInfo)) + return D3DERR_INVALIDCALL; + + UINT SizeOfData = *pSizeOfData; + + // Get actual size + UINT ActualSize = pInfo->function.size() * sizeof(DWORD); + + if (pData == nullptr) { + *pSizeOfData = ActualSize; + return D3D_OK; + } + + // D3D8-specific behavior + if (SizeOfData < ActualSize) { + *pSizeOfData = ActualSize; + return D3DERR_MOREDATA; + } + + memcpy(pData, pInfo->function.data(), ActualSize); + return D3D_OK; + + } + + // Pixel Shaders // + + HRESULT STDMETHODCALLTYPE D3D8Device::CreatePixelShader( + const DWORD* pFunction, + DWORD* pHandle) { + d3d9::IDirect3DPixelShader9* pPixelShader; + + HRESULT res = GetD3D9()->CreatePixelShader(pFunction, &pPixelShader); + + m_pixelShaders.push_back(pPixelShader); + + // Still set the shader bit, to prevent conflicts with NULL. + *pHandle = getShaderHandle(m_pixelShaders.size() - 1); + + return res; + } + + inline d3d9::IDirect3DPixelShader9* getPixelShaderPtr(D3D8Device* device, DWORD Handle) { + Handle = getShaderIndex(Handle); + + if (unlikely(Handle >= device->m_pixelShaders.size())) { + Logger::debug(str::format("getPixelShaderPtr: Invalid pixel shader index ", std::hex, Handle)); + return nullptr; + } + + d3d9::IDirect3DPixelShader9* pPixelShader = device->m_pixelShaders[Handle]; + + if (unlikely(pPixelShader == nullptr)) { + Logger::debug(str::format("getPixelShaderPtr: Application provided deleted pixel shader ", std::hex, Handle)); + return nullptr; + } + + return pPixelShader; + } + + HRESULT STDMETHODCALLTYPE D3D8Device::SetPixelShader(DWORD Handle) { + if (unlikely(ShouldRecord())) { + return m_recorder->SetPixelShader(Handle); + } + + if (Handle == DWORD(NULL)) { + StateChange(); + m_currentPixelShader = DWORD(NULL); + return GetD3D9()->SetPixelShader(nullptr); + } + + d3d9::IDirect3DPixelShader9* pPixelShader = getPixelShaderPtr(this, Handle); + + if (unlikely(!pPixelShader)) { + return D3DERR_INVALIDCALL; + } + + StateChange(); + + // Cache current pixel shader + m_currentPixelShader = Handle; + + return GetD3D9()->SetPixelShader(pPixelShader); + } + + HRESULT STDMETHODCALLTYPE D3D8Device::GetPixelShader(DWORD* pHandle) { + // Return cached shader + *pHandle = m_currentPixelShader; + + return D3D_OK; + } + + HRESULT STDMETHODCALLTYPE D3D8Device::DeletePixelShader(DWORD Handle) { + d3d9::IDirect3DPixelShader9* pPixelShader = getPixelShaderPtr(this, Handle); + + if (unlikely(!pPixelShader)) { + return D3DERR_INVALIDCALL; + } + + pPixelShader->Release(); + + m_pixelShaders[getShaderIndex(Handle)] = nullptr; + + return D3D_OK; + } + + HRESULT STDMETHODCALLTYPE D3D8Device::GetPixelShaderFunction(DWORD Handle, void* pData, DWORD* pSizeOfData) { + d3d9::IDirect3DPixelShader9* pPixelShader = getPixelShaderPtr(this, Handle); + + if (unlikely(!pPixelShader)) + return D3DERR_INVALIDCALL; + + UINT SizeOfData = *pSizeOfData; + + // Get actual size + UINT ActualSize = 0; + pPixelShader->GetFunction(nullptr, &ActualSize); + + if (pData == nullptr) { + *pSizeOfData = ActualSize; + return D3D_OK; + } + + // D3D8-specific behavior + if (SizeOfData < ActualSize) { + *pSizeOfData = ActualSize; + return D3DERR_MOREDATA; + } + + return pPixelShader->GetFunction(pData, &SizeOfData); + } + +} diff --git a/src/d3d8/d3d8_device.h b/src/d3d8/d3d8_device.h new file mode 100644 index 000000000..141723638 --- /dev/null +++ b/src/d3d8/d3d8_device.h @@ -0,0 +1,454 @@ +#pragma once + +#include "d3d8_include.h" +#include "d3d8_texture.h" +#include "d3d8_buffer.h" +#include "d3d8_swapchain.h" +#include "d3d8_state_block.h" +#include "d3d8_d3d9_util.h" +#include "d3d8_caps.h" +#include "d3d8_batch.h" + +#include "../d3d9/d3d9_bridge.h" + +#include +#include +#include +#include + +namespace dxvk { + + class D3D8Interface; + + struct D3D8VertexShaderInfo; + + using D3D8DeviceBase = D3D8WrappedObject; + class D3D8Device final : public D3D8DeviceBase { + + friend class D3D8StateBlock; + public: + + D3D8Device( + D3D8Interface* pParent, + Com&& pDevice, + D3DDEVTYPE DeviceType, + HWND hFocusWindow, + DWORD BehaviorFlags, + D3DPRESENT_PARAMETERS* pParams); + + ~D3D8Device(); + + HRESULT STDMETHODCALLTYPE TestCooperativeLevel(); + + UINT STDMETHODCALLTYPE GetAvailableTextureMem(); + + HRESULT STDMETHODCALLTYPE ResourceManagerDiscardBytes(DWORD bytes); + + HRESULT STDMETHODCALLTYPE GetDirect3D(IDirect3D8** ppD3D8); + + HRESULT STDMETHODCALLTYPE GetDeviceCaps(D3DCAPS8* pCaps); + + HRESULT STDMETHODCALLTYPE GetDisplayMode(D3DDISPLAYMODE* pMode); + + HRESULT STDMETHODCALLTYPE GetCreationParameters(D3DDEVICE_CREATION_PARAMETERS* pParameters); + + HRESULT STDMETHODCALLTYPE SetCursorProperties( + UINT XHotSpot, + UINT YHotSpot, + IDirect3DSurface8* pCursorBitmap); + + void STDMETHODCALLTYPE SetCursorPosition(UINT XScreenSpace, UINT YScreenSpace, DWORD Flags); + + // Microsoft d3d8.h in the DirectX 9 SDK uses a different function signature... + void STDMETHODCALLTYPE SetCursorPosition(int X, int Y, DWORD Flags); + + BOOL STDMETHODCALLTYPE ShowCursor(BOOL bShow); + + HRESULT STDMETHODCALLTYPE CreateAdditionalSwapChain( + D3DPRESENT_PARAMETERS* pPresentationParameters, + IDirect3DSwapChain8** ppSwapChain); + + HRESULT STDMETHODCALLTYPE Reset(D3DPRESENT_PARAMETERS* pPresentationParameters); + + HRESULT STDMETHODCALLTYPE Present( + const RECT* pSourceRect, + const RECT* pDestRect, + HWND hDestWindowOverride, + const RGNDATA* pDirtyRegion); + + HRESULT STDMETHODCALLTYPE GetBackBuffer( + UINT iBackBuffer, + D3DBACKBUFFER_TYPE Type, + IDirect3DSurface8** ppBackBuffer); + + HRESULT STDMETHODCALLTYPE GetRasterStatus(D3DRASTER_STATUS* pRasterStatus); + + void STDMETHODCALLTYPE SetGammaRamp(DWORD Flags, const D3DGAMMARAMP* pRamp); + + void STDMETHODCALLTYPE GetGammaRamp(D3DGAMMARAMP* pRamp); + + HRESULT STDMETHODCALLTYPE CreateTexture( + UINT Width, + UINT Height, + UINT Levels, + DWORD Usage, + D3DFORMAT Format, + D3DPOOL Pool, + IDirect3DTexture8** ppTexture); + + HRESULT STDMETHODCALLTYPE CreateVolumeTexture( + UINT Width, + UINT Height, + UINT Depth, + UINT Levels, + DWORD Usage, + D3DFORMAT Format, + D3DPOOL Pool, + IDirect3DVolumeTexture8** ppVolumeTexture); + + HRESULT STDMETHODCALLTYPE CreateCubeTexture( + UINT EdgeLength, + UINT Levels, + DWORD Usage, + D3DFORMAT Format, + D3DPOOL Pool, + IDirect3DCubeTexture8** ppCubeTexture); + + HRESULT STDMETHODCALLTYPE CreateVertexBuffer( + UINT Length, + DWORD Usage, + DWORD FVF, + D3DPOOL Pool, + IDirect3DVertexBuffer8** ppVertexBuffer); + + HRESULT STDMETHODCALLTYPE CreateIndexBuffer( + UINT Length, + DWORD Usage, + D3DFORMAT Format, + D3DPOOL Pool, + IDirect3DIndexBuffer8** ppIndexBuffer); + + HRESULT STDMETHODCALLTYPE CreateRenderTarget( + UINT Width, + UINT Height, + D3DFORMAT Format, + D3DMULTISAMPLE_TYPE MultiSample, + BOOL Lockable, + IDirect3DSurface8** ppSurface); + + HRESULT STDMETHODCALLTYPE CreateDepthStencilSurface( + UINT Width, + UINT Height, + D3DFORMAT Format, + D3DMULTISAMPLE_TYPE MultiSample, + IDirect3DSurface8** ppSurface); + + HRESULT STDMETHODCALLTYPE CreateImageSurface(UINT Width, UINT Height, D3DFORMAT Format, IDirect3DSurface8** ppSurface); + + HRESULT STDMETHODCALLTYPE CopyRects( + IDirect3DSurface8* pSourceSurface, + const RECT* pSourceRectsArray, + UINT cRects, + IDirect3DSurface8* pDestinationSurface, + const POINT* pDestPointsArray); + + HRESULT STDMETHODCALLTYPE UpdateTexture( + IDirect3DBaseTexture8* pSourceTexture, + IDirect3DBaseTexture8* pDestinationTexture); + + HRESULT STDMETHODCALLTYPE GetFrontBuffer(IDirect3DSurface8* pDestSurface); + + HRESULT STDMETHODCALLTYPE SetRenderTarget(IDirect3DSurface8* pRenderTarget, IDirect3DSurface8* pNewZStencil); + + HRESULT STDMETHODCALLTYPE GetRenderTarget(IDirect3DSurface8** ppRenderTarget); + + HRESULT STDMETHODCALLTYPE GetDepthStencilSurface(IDirect3DSurface8** ppZStencilSurface); + + HRESULT STDMETHODCALLTYPE BeginScene(); + + HRESULT STDMETHODCALLTYPE EndScene(); + + HRESULT STDMETHODCALLTYPE Clear( + DWORD Count, + const D3DRECT* pRects, + DWORD Flags, + D3DCOLOR Color, + float Z, + DWORD Stencil); + + HRESULT STDMETHODCALLTYPE SetTransform(D3DTRANSFORMSTATETYPE State, const D3DMATRIX* pMatrix); + + HRESULT STDMETHODCALLTYPE GetTransform(D3DTRANSFORMSTATETYPE State, D3DMATRIX* pMatrix); + + HRESULT STDMETHODCALLTYPE MultiplyTransform(D3DTRANSFORMSTATETYPE TransformState, const D3DMATRIX* pMatrix); + + HRESULT STDMETHODCALLTYPE SetViewport(const D3DVIEWPORT8* pViewport); + + HRESULT STDMETHODCALLTYPE GetViewport(D3DVIEWPORT8* pViewport); + + HRESULT STDMETHODCALLTYPE SetMaterial(const D3DMATERIAL8* pMaterial); + + HRESULT STDMETHODCALLTYPE GetMaterial(D3DMATERIAL8* pMaterial); + + HRESULT STDMETHODCALLTYPE SetLight(DWORD Index, const D3DLIGHT8* pLight); + + HRESULT STDMETHODCALLTYPE GetLight(DWORD Index, D3DLIGHT8* pLight); + + HRESULT STDMETHODCALLTYPE LightEnable(DWORD Index, BOOL Enable); + + HRESULT STDMETHODCALLTYPE GetLightEnable(DWORD Index, BOOL* pEnable); + + HRESULT STDMETHODCALLTYPE SetClipPlane(DWORD Index, const float* pPlane); + + HRESULT STDMETHODCALLTYPE GetClipPlane(DWORD Index, float* pPlane); + + HRESULT STDMETHODCALLTYPE SetRenderState(D3DRENDERSTATETYPE State, DWORD Value); + + HRESULT STDMETHODCALLTYPE GetRenderState(D3DRENDERSTATETYPE State, DWORD* pValue); + + HRESULT STDMETHODCALLTYPE CreateStateBlock( + D3DSTATEBLOCKTYPE Type, + DWORD* pToken); + + HRESULT STDMETHODCALLTYPE CaptureStateBlock(DWORD Token); + + HRESULT STDMETHODCALLTYPE ApplyStateBlock(DWORD Token); + + HRESULT STDMETHODCALLTYPE DeleteStateBlock(DWORD Token); + + HRESULT STDMETHODCALLTYPE BeginStateBlock(); + + HRESULT STDMETHODCALLTYPE EndStateBlock(DWORD* pToken); + + HRESULT STDMETHODCALLTYPE SetClipStatus(const D3DCLIPSTATUS8* pClipStatus); + + HRESULT STDMETHODCALLTYPE GetClipStatus(D3DCLIPSTATUS8* pClipStatus); + + HRESULT STDMETHODCALLTYPE GetTexture(DWORD Stage, IDirect3DBaseTexture8** ppTexture); + + HRESULT STDMETHODCALLTYPE SetTexture(DWORD Stage, IDirect3DBaseTexture8* pTexture); + + HRESULT STDMETHODCALLTYPE GetTextureStageState( + DWORD Stage, + D3DTEXTURESTAGESTATETYPE Type, + DWORD* pValue); + + HRESULT STDMETHODCALLTYPE SetTextureStageState( + DWORD Stage, + D3DTEXTURESTAGESTATETYPE Type, + DWORD Value); + + HRESULT STDMETHODCALLTYPE ValidateDevice(DWORD* pNumPasses); + + HRESULT STDMETHODCALLTYPE GetInfo(DWORD DevInfoID, void* pDevInfoStruct, DWORD DevInfoStructSize); + + HRESULT STDMETHODCALLTYPE SetPaletteEntries(UINT PaletteNumber, const PALETTEENTRY* pEntries); + + HRESULT STDMETHODCALLTYPE GetPaletteEntries(UINT PaletteNumber, PALETTEENTRY* pEntries); + + HRESULT STDMETHODCALLTYPE SetCurrentTexturePalette(UINT PaletteNumber); + + HRESULT STDMETHODCALLTYPE GetCurrentTexturePalette(UINT* PaletteNumber); + + HRESULT STDMETHODCALLTYPE DrawPrimitive( + D3DPRIMITIVETYPE PrimitiveType, + UINT StartVertex, + UINT PrimitiveCount); + + HRESULT STDMETHODCALLTYPE DrawIndexedPrimitive( + D3DPRIMITIVETYPE PrimitiveType, + UINT MinVertexIndex, + UINT NumVertices, + UINT StartIndex, + UINT PrimitiveCount); + + HRESULT STDMETHODCALLTYPE DrawPrimitiveUP( + D3DPRIMITIVETYPE PrimitiveType, + UINT PrimitiveCount, + const void* pVertexStreamZeroData, + UINT VertexStreamZeroStride); + + HRESULT STDMETHODCALLTYPE DrawIndexedPrimitiveUP( + D3DPRIMITIVETYPE PrimitiveType, + UINT MinVertexIndex, + UINT NumVertices, + UINT PrimitiveCount, + const void* pIndexData, + D3DFORMAT IndexDataFormat, + const void* pVertexStreamZeroData, + UINT VertexStreamZeroStride); + + HRESULT STDMETHODCALLTYPE ProcessVertices( + UINT SrcStartIndex, + UINT DestIndex, + UINT VertexCount, + IDirect3DVertexBuffer8* pDestBuffer, + DWORD Flags); + + HRESULT STDMETHODCALLTYPE CreateVertexShader( + const DWORD* pDeclaration, + const DWORD* pFunction, + DWORD* pHandle, + DWORD Usage); + + HRESULT STDMETHODCALLTYPE SetVertexShader(DWORD Handle); + + HRESULT STDMETHODCALLTYPE GetVertexShader(DWORD* pHandle); + + HRESULT STDMETHODCALLTYPE DeleteVertexShader(DWORD Handle); + + HRESULT STDMETHODCALLTYPE SetVertexShaderConstant( + DWORD StartRegister, + const void* pConstantData, + DWORD ConstantCount); + + HRESULT STDMETHODCALLTYPE GetVertexShaderConstant(DWORD Register, void* pConstantData, DWORD ConstantCount); + + HRESULT STDMETHODCALLTYPE GetVertexShaderDeclaration(DWORD Handle, void* pData, DWORD* pSizeOfData); + + HRESULT STDMETHODCALLTYPE GetVertexShaderFunction(DWORD Handle, void* pData, DWORD* pSizeOfData); + + HRESULT STDMETHODCALLTYPE SetStreamSource( + UINT StreamNumber, + IDirect3DVertexBuffer8* pStreamData, + UINT Stride); + + HRESULT STDMETHODCALLTYPE GetStreamSource( + UINT StreamNumber, + IDirect3DVertexBuffer8** ppStreamData, + UINT* pStride); + + HRESULT STDMETHODCALLTYPE SetIndices(IDirect3DIndexBuffer8* pIndexData, UINT BaseVertexIndex); + + HRESULT STDMETHODCALLTYPE GetIndices( + IDirect3DIndexBuffer8** ppIndexData, + UINT* pBaseVertexIndex); + + HRESULT STDMETHODCALLTYPE CreatePixelShader( + const DWORD* pFunction, + DWORD* pHandle); + + HRESULT STDMETHODCALLTYPE SetPixelShader(DWORD Handle); + + HRESULT STDMETHODCALLTYPE GetPixelShader(DWORD* pHandle); + + HRESULT STDMETHODCALLTYPE DeletePixelShader(THIS_ DWORD Handle); + + HRESULT STDMETHODCALLTYPE GetPixelShaderConstant(DWORD Register, void* pConstantData, DWORD ConstantCount); + + HRESULT STDMETHODCALLTYPE SetPixelShaderConstant( + DWORD StartRegister, + const void* pConstantData, + DWORD ConstantCount); + + HRESULT STDMETHODCALLTYPE GetPixelShaderFunction(DWORD Handle, void* pData, DWORD* pSizeOfData); + + HRESULT STDMETHODCALLTYPE DrawRectPatch( + UINT Handle, + const float* pNumSegs, + const D3DRECTPATCH_INFO* pRectPatchInfo); + + HRESULT STDMETHODCALLTYPE DrawTriPatch( + UINT Handle, + const float* pNumSegs, + const D3DTRIPATCH_INFO* pTriPatchInfo); + + HRESULT STDMETHODCALLTYPE DeletePatch(UINT Handle); + + public: // Internal Methods // + + inline bool ShouldRecord() { return m_recorder != nullptr; } + inline bool ShouldBatch() { return m_batcher != nullptr; } + + /** + * Marks any state change in the device, so we can signal + * the batcher to emit draw calls. StateChange should be + * called immediately before changing any D3D9 state. + */ + inline void StateChange() { + if (ShouldBatch()) + m_batcher->StateChange(); + } + + inline void ResetState() { + // Mirrors how D3D9 handles the BackBufferCount + m_presentParams.BackBufferCount = std::max(m_presentParams.BackBufferCount, 1u); + + // Purge cached objects + // TODO: Some functions may need to be called here (e.g. SetTexture, etc.) + // in case Reset can be recorded by state blocks and other things. + m_textures.fill(nullptr); + m_streams.fill(D3D8VBO()); + m_indices = nullptr; + m_renderTarget = nullptr; + m_depthStencil = nullptr; + + m_backBuffers.clear(); + m_backBuffers.resize(m_presentParams.BackBufferCount); + + m_autoDepthStencil = nullptr; + } + + inline void RecreateBackBuffersAndAutoDepthStencil() { + for (UINT i = 0; i < m_presentParams.BackBufferCount; i++) { + Com pSurface9; + GetD3D9()->GetBackBuffer(0, i, d3d9::D3DBACKBUFFER_TYPE_MONO, &pSurface9); + m_backBuffers[i] = new D3D8Surface(this, std::move(pSurface9)); + } + + Com pStencil9 = nullptr; + GetD3D9()->GetDepthStencilSurface(&pStencil9); + m_autoDepthStencil = new D3D8Surface(this, std::move(pStencil9)); + + m_renderTarget = m_backBuffers[0]; + m_depthStencil = m_autoDepthStencil; + } + + friend d3d9::IDirect3DPixelShader9* getPixelShaderPtr(D3D8Device* device, DWORD Handle); + friend D3D8VertexShaderInfo* getVertexShaderInfo(D3D8Device* device, DWORD Handle); + + private: + + Com m_bridge; + const D3D8Options& m_d3d8Options; + + Com m_parent; + + D3DPRESENT_PARAMETERS m_presentParams; + + D3D8StateBlock* m_recorder = nullptr; + std::unordered_set m_stateBlocks; + D3D8Batcher* m_batcher = nullptr; + + struct D3D8VBO { + Com buffer = nullptr; + UINT stride = 0; + }; + + // Remember to fill() these in the constructor! + std::array, d8caps::MAX_TEXTURE_STAGES> m_textures; + std::array m_streams; + + Com m_indices; + INT m_baseVertexIndex = 0; + + // TODO: Which of these should be a private ref + std::vector> m_backBuffers; + Com m_autoDepthStencil; + + Com m_renderTarget; + Com m_depthStencil; + + std::vector m_vertexShaders; + std::vector m_pixelShaders; + DWORD m_currentVertexShader = 0; // can be FVF or vs index (marked by D3DFVF_RESERVED0) + DWORD m_currentPixelShader = 0; + + D3DDEVTYPE m_deviceType; + HWND m_window; + + DWORD m_behaviorFlags; + + }; + +} diff --git a/src/d3d8/d3d8_device_child.h b/src/d3d8/d3d8_device_child.h new file mode 100644 index 000000000..d4c2523eb --- /dev/null +++ b/src/d3d8/d3d8_device_child.h @@ -0,0 +1,71 @@ +#pragma once + +// Common methods for device-tied objects. +// - AddRef, Release from IUnknown +// - GetDevice from various classes including IDirect3DResource8 + +#include "d3d8_include.h" +#include "d3d8_wrapped_object.h" + +namespace dxvk { + + class D3D8Device; + + template + class D3D8DeviceChild : public D3D8WrappedObject { + + public: + + D3D8DeviceChild(D3D8Device* pDevice, Com&& Object) + : D3D8WrappedObject(std::move(Object)) + , m_parent( pDevice ) { } + + ULONG STDMETHODCALLTYPE AddRef() { + uint32_t refCount = this->m_refCount++; + if (unlikely(!refCount)) { + this->AddRefPrivate(); + GetDevice()->AddRef(); + } + + return refCount + 1; + } + + ULONG STDMETHODCALLTYPE Release() { + // ignore Release calls on objects with 0 refCount + if(unlikely(!this->m_refCount)) + return this->m_refCount; + + uint32_t refCount = --this->m_refCount; + if (unlikely(!refCount)) { + auto* pDevice = GetDevice(); + this->ReleasePrivate(); + pDevice->Release(); + } + return refCount; + } + + HRESULT STDMETHODCALLTYPE GetDevice(IDirect3DDevice8** ppDevice) { + InitReturnPtr(ppDevice); + + if (ppDevice == nullptr) + return D3DERR_INVALIDCALL; + + *ppDevice = ref(GetDevice()); + return D3D_OK; + } + + IDirect3DDevice8* GetDevice() { + return reinterpret_cast(m_parent); + } + + D3D8Device* GetParent() { + return m_parent; + } + + protected: + + D3D8Device* m_parent; + + }; + +} \ No newline at end of file diff --git a/src/d3d8/d3d8_format.h b/src/d3d8/d3d8_format.h new file mode 100644 index 000000000..3aea400df --- /dev/null +++ b/src/d3d8/d3d8_format.h @@ -0,0 +1,220 @@ +#pragma once + +#include "d3d8_include.h" + +namespace dxvk { + constexpr bool isDXT(D3DFORMAT fmt) { + return fmt == D3DFMT_DXT1 + || fmt == D3DFMT_DXT2 + || fmt == D3DFMT_DXT3 + || fmt == D3DFMT_DXT4 + || fmt == D3DFMT_DXT5; + } + + constexpr bool isDXT(d3d9::D3DFORMAT fmt) { + return isDXT(D3DFORMAT(fmt)); + } + + constexpr bool isUnsupportedSurfaceFormat(D3DFORMAT fmt) { + // mirror what dxvk doesn't support in terms of d3d9 surface formats + return fmt == D3DFMT_R8G8B8 + || fmt == D3DFMT_R3G3B2 + || fmt == D3DFMT_A8R3G3B2 + || fmt == D3DFMT_A8P8 + || fmt == D3DFMT_P8; + // not included in the d3d8 spec + //|| fmt == D3DFMT_CXV8U8; + } + + constexpr bool isSupportedDepthStencilFormat(D3DFORMAT fmt) { + // native d3d8 doesn't support D3DFMT_D32, D3DFMT_D15S1 or D3DFMT_D24X4S4 + return fmt == D3DFMT_D16_LOCKABLE + || fmt == D3DFMT_D16 + //|| fmt == D3DFMT_D32 + //|| fmt == D3DFMT_D15S1 + //|| fmt == D3DFMT_D24X4S4 + || fmt == D3DFMT_D24S8 + || fmt == D3DFMT_D24X8; + } + + constexpr bool isDepthStencilFormat(D3DFORMAT fmt) { + return fmt == D3DFMT_D16_LOCKABLE + || fmt == D3DFMT_D16 + || fmt == D3DFMT_D32 + || fmt == D3DFMT_D15S1 + || fmt == D3DFMT_D24X4S4 + || fmt == D3DFMT_D24S8 + || fmt == D3DFMT_D24X8; + } + + // Get bytes per pixel (or 4x4 block for DXT) + constexpr UINT getFormatStride(D3DFORMAT fmt) { + switch (fmt) { + default: + case D3DFMT_UNKNOWN: + return 0; + case D3DFMT_R3G3B2: + case D3DFMT_A8: + case D3DFMT_P8: + case D3DFMT_L8: + case D3DFMT_A4L4: + return 1; + case D3DFMT_R5G6B5: + case D3DFMT_X1R5G5B5: + case D3DFMT_A1R5G5B5: + case D3DFMT_A4R4G4B4: + case D3DFMT_A8R3G3B2: + case D3DFMT_X4R4G4B4: + case D3DFMT_A8P8: + case D3DFMT_A8L8: + case D3DFMT_V8U8: + case D3DFMT_L6V5U5: + case D3DFMT_D16_LOCKABLE: + case D3DFMT_D15S1: + case D3DFMT_D16: + case D3DFMT_UYVY: + case D3DFMT_YUY2: + return 2; + case D3DFMT_R8G8B8: + return 3; + case D3DFMT_A8R8G8B8: + case D3DFMT_X8R8G8B8: + case D3DFMT_A2B10G10R10: + //case D3DFMT_A8B8G8R8: + //case D3DFMT_X8B8G8R8: + case D3DFMT_G16R16: + case D3DFMT_X8L8V8U8: + case D3DFMT_Q8W8V8U8: + case D3DFMT_V16U16: + case D3DFMT_W11V11U10: + case D3DFMT_A2W10V10U10: + case D3DFMT_D32: + case D3DFMT_D24S8: + case D3DFMT_D24X8: + case D3DFMT_D24X4S4: + return 4; + case D3DFMT_DXT1: + return 8; + case D3DFMT_DXT2: + case D3DFMT_DXT3: + case D3DFMT_DXT4: + case D3DFMT_DXT5: + return 16; + } + } + + constexpr uint32_t GetVertexCount8(D3DPRIMITIVETYPE type, UINT count) { + switch (type) { + default: + case D3DPT_TRIANGLELIST: return count * 3; + case D3DPT_POINTLIST: return count; + case D3DPT_LINELIST: return count * 2; + case D3DPT_LINESTRIP: return count + 1; + case D3DPT_TRIANGLESTRIP: return count + 2; + case D3DPT_TRIANGLEFAN: return count + 2; + } + } + + // Essentially the same logic as D3D9VertexDecl::SetFVF + constexpr UINT GetFVFStride(DWORD FVF) { + uint32_t texCount = 0; + + uint32_t betas = 0; + uint8_t betaIdx = 0xFF; + + UINT size = 0; + + switch (FVF & D3DFVF_POSITION_MASK) { + case D3DFVF_XYZ: + case D3DFVF_XYZB1: + case D3DFVF_XYZB2: + case D3DFVF_XYZB3: + case D3DFVF_XYZB4: + case D3DFVF_XYZB5: + size += sizeof(float) * 3; + + if ((FVF & D3DFVF_POSITION_MASK) == D3DFVF_XYZ) + break; + + betas = (((FVF & D3DFVF_XYZB5) - D3DFVF_XYZB1) >> 1) + 1; + if (FVF & D3DFVF_LASTBETA_D3DCOLOR) + betaIdx = sizeof(D3DCOLOR); + else if (FVF & D3DFVF_LASTBETA_UBYTE4) + betaIdx = sizeof(BYTE) * 4; + else if ((FVF & D3DFVF_XYZB5) == D3DFVF_XYZB5) + betaIdx = sizeof(float); + + if (betaIdx != 0xFF) + betas--; + + if (betas > 0) { + if (betas <= 4) + size += sizeof(float) * betas; + } + + if (betaIdx != 0xFF) { + size += betaIdx; + } + break; + + case D3DFVF_XYZW: + case D3DFVF_XYZRHW: + size += sizeof(float) * 4; + break; + + default: + break; + } + + if (FVF & D3DFVF_NORMAL) { + size += sizeof(float) * 3; + } + if (FVF & D3DFVF_PSIZE) { + size += sizeof(float); + } + if (FVF & D3DFVF_DIFFUSE) { + size += sizeof(D3DCOLOR); + } + if (FVF & D3DFVF_SPECULAR) { + size += sizeof(D3DCOLOR); + } + + texCount = (FVF & D3DFVF_TEXCOUNT_MASK) >> D3DFVF_TEXCOUNT_SHIFT; + texCount = std::min(texCount, 8u); + + for (uint32_t i = 0; i < texCount; i++) { + switch ((FVF >> (16 + i * 2)) & 0x3) { + case D3DFVF_TEXTUREFORMAT1: + size += sizeof(float); + break; + + case D3DFVF_TEXTUREFORMAT2: + size += sizeof(float) * 2; + break; + + case D3DFVF_TEXTUREFORMAT3: + size += sizeof(float) * 3; + break; + + case D3DFVF_TEXTUREFORMAT4: + size += sizeof(float) * 4; + break; + + default: + break; + } + } + + return size; + } + + + constexpr UINT getSurfaceSize(D3DFORMAT Format, UINT Width, UINT Height) { + if (isDXT(Format)) { + Width = ((Width + 3) >> 2); + Height = ((Height + 3) >> 2); + } + return Width * Height * getFormatStride(Format); + } + +} diff --git a/src/d3d8/d3d8_include.h b/src/d3d8/d3d8_include.h new file mode 100644 index 000000000..78d7dcd16 --- /dev/null +++ b/src/d3d8/d3d8_include.h @@ -0,0 +1,200 @@ +#pragma once + +#ifndef _MSC_VER +#ifdef _WIN32_WINNT +#undef _WIN32_WINNT +#endif +#define _WIN32_WINNT 0x0A00 +#endif + +#include +#include + +// Declare __uuidof for D3D8 interfaces +#ifdef __CRT_UUID_DECL +__CRT_UUID_DECL(IDirect3D8, 0x1DD9E8DA,0x1C77,0x4D40,0xB0,0xCF,0x98,0xFE,0xFD,0xFF,0x95,0x12); +__CRT_UUID_DECL(IDirect3DDevice8, 0x7385E5DF,0x8FE8,0x41D5,0x86,0xB6,0xD7,0xB4,0x85,0x47,0xB6,0xCF); +__CRT_UUID_DECL(IDirect3DResource8, 0x1B36BB7B,0x09B7,0x410A,0xB4,0x45,0x7D,0x14,0x30,0xD7,0xB3,0x3F); +__CRT_UUID_DECL(IDirect3DVertexBuffer8, 0x8AEEEAC7,0x05F9,0x44D4,0xB5,0x91,0x00,0x0B,0x0D,0xF1,0xCB,0x95); +__CRT_UUID_DECL(IDirect3DVolume8, 0xBD7349F5,0x14F1,0x42E4,0x9C,0x79,0x97,0x23,0x80,0xDB,0x40,0xC0); +__CRT_UUID_DECL(IDirect3DSwapChain8, 0x928C088B,0x76B9,0x4C6B,0xA5,0x36,0xA5,0x90,0x85,0x38,0x76,0xCD); +__CRT_UUID_DECL(IDirect3DSurface8, 0xB96EEBCA,0xB326,0x4EA5,0x88,0x2F,0x2F,0xF5,0xBA,0xE0,0x21,0xDD); +__CRT_UUID_DECL(IDirect3DIndexBuffer8, 0x0E689C9A,0x053D,0x44A0,0x9D,0x92,0xDB,0x0E,0x3D,0x75,0x0F,0x86); +__CRT_UUID_DECL(IDirect3DBaseTexture8, 0xB4211CFA,0x51B9,0x4A9F,0xAB,0x78,0xDB,0x99,0xB2,0xBB,0x67,0x8E); +__CRT_UUID_DECL(IDirect3DTexture8, 0xE4CDD575,0x2866,0x4F01,0xB1,0x2E,0x7E,0xEC,0xE1,0xEC,0x93,0x58); +__CRT_UUID_DECL(IDirect3DCubeTexture8, 0x3EE5B968,0x2ACA,0x4C34,0x8B,0xB5,0x7E,0x0C,0x3D,0x19,0xB7,0x50); +__CRT_UUID_DECL(IDirect3DVolumeTexture8, 0x4B8AAAFA,0x140F,0x42BA,0x91,0x31,0x59,0x7E,0xAF,0xAA,0x2E,0xAD); +#elif defined(_MSC_VER) +interface DECLSPEC_UUID("1DD9E8DA-1C77-4D40-B0CF-98FEFDFF9512") IDirect3D8; +interface DECLSPEC_UUID("7385E5DF-8FE8-41D5-86B6-D7B48547B6CF") IDirect3DDevice8; +interface DECLSPEC_UUID("1B36BB7B-09B7-410A-B445-7D1430D7B33F") IDirect3DResource8; +interface DECLSPEC_UUID("8AEEEAC7-05F9-44D4-B591-000B0DF1CB95") IDirect3DVertexBuffer8; +interface DECLSPEC_UUID("BD7349F5-14F1-42E4-9C79-972380DB40C0") IDirect3DVolume8; +interface DECLSPEC_UUID("928C088B-76B9-4C6B-A536-A590853876CD") IDirect3DSwapChain8; +interface DECLSPEC_UUID("B96EEBCA-B326-4EA5-882F-2FF5BAE021DD") IDirect3DSurface8; +interface DECLSPEC_UUID("0E689C9A-053D-44A0-9D92-DB0E3D750F86") IDirect3DIndexBuffer8; +interface DECLSPEC_UUID("B4211CFA-51B9-4A9F-AB78-DB99B2BB678E") IDirect3DBaseTexture8; +interface DECLSPEC_UUID("E4CDD575-2866-4F01-B12E-7EECE1EC9358") IDirect3DTexture8; +interface DECLSPEC_UUID("3EE5B968-2ACA-4C34-8BB5-7E0C3D19B750") IDirect3DCubeTexture8; +interface DECLSPEC_UUID("4B8AAAFA-140F-42BA-9131-597EAFAA2EAD") IDirect3DVolumeTexture8; +#endif + +// Undefine D3D8 macros +#undef DIRECT3D_VERSION +#undef D3D_SDK_VERSION + +#undef D3DCS_ALL // parentheses added in D3D9 +#undef D3DFVF_POSITION_MASK // changed from 0x00E to 0x400E in D3D9 +#undef D3DFVF_RESERVED2 // reduced from 4 to 2 in DX9 + +#undef D3DSP_REGNUM_MASK // changed from 0x00000FFF to 0x000007FF in D3D9 + + +#if defined(__MINGW32__) || defined(__GNUC__) + +// Avoid redundant definitions (add D3D*_DEFINED macros here) +#define D3DRECT_DEFINED +#define D3DMATRIX_DEFINED + +// Temporarily override __CRT_UUID_DECL to allow usage in d3d9 namespace +#pragma push_macro("__CRT_UUID_DECL") +#ifdef __CRT_UUID_DECL +#undef __CRT_UUID_DECL +#endif + +#ifdef __MINGW32__ +#define __CRT_UUID_DECL(type,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) \ +} \ + extern "C++" template<> struct __mingw_uuidof_s { static constexpr IID __uuid_inst = {l,w1,w2, {b1,b2,b3,b4,b5,b6,b7,b8}}; }; \ + extern "C++" template<> constexpr const GUID &__mingw_uuidof() { return __mingw_uuidof_s::__uuid_inst; } \ + extern "C++" template<> constexpr const GUID &__mingw_uuidof() { return __mingw_uuidof_s::__uuid_inst; } \ +namespace d3d9 { + +#elif defined(__GNUC__) +#define __CRT_UUID_DECL(type, a, b, c, d, e, f, g, h, i, j, k) \ +} \ + extern "C++" { template <> constexpr GUID __uuidof_helper() { return GUID{a,b,c,{d,e,f,g,h,i,j,k}}; } } \ + extern "C++" { template <> constexpr GUID __uuidof_helper() { return __uuidof_helper(); } } \ + extern "C++" { template <> constexpr GUID __uuidof_helper() { return __uuidof_helper(); } } \ + extern "C++" { template <> constexpr GUID __uuidof_helper() { return __uuidof_helper(); } } \ + extern "C++" { template <> constexpr GUID __uuidof_helper() { return __uuidof_helper(); } } \ +namespace d3d9 { +#endif + +#endif // defined(__MINGW32__) || defined(__GNUC__) + + +/** +* \brief Direct3D 9 +* +* All D3D9 interfaces are included within +* a namespace, so as not to collide with +* D3D8 interfaces. +*/ +namespace d3d9 { +#include +} + +// Indicates d3d9:: namespace is in-use. +#define DXVK_D3D9_NAMESPACE + +#if defined(__MINGW32__) || defined(__GNUC__) +#pragma pop_macro("__CRT_UUID_DECL") +#endif + +//for some reason we need to specify __declspec(dllexport) for MinGW +#if defined(__WINE__) || !defined(_WIN32) + #define DLLEXPORT __attribute__((visibility("default"))) +#else + #define DLLEXPORT +#endif + + +#include "../util/com/com_guid.h" +#include "../util/com/com_object.h" +#include "../util/com/com_pointer.h" + +#include "../util/log/log.h" +#include "../util/log/log_debug.h" + +#include "../util/util_error.h" +#include "../util/util_likely.h" +#include "../util/util_string.h" + +// Missed definitions in Wine/MinGW. + +#ifndef D3DPRESENT_BACK_BUFFERS_MAX_EX +#define D3DPRESENT_BACK_BUFFERS_MAX_EX 30 +#endif + +#ifndef D3DSI_OPCODE_MASK +#define D3DSI_OPCODE_MASK 0x0000FFFF +#endif + +#ifndef D3DSP_TEXTURETYPE_MASK +#define D3DSP_TEXTURETYPE_MASK 0x78000000 +#endif + +#ifndef D3DUSAGE_AUTOGENMIPMAP +#define D3DUSAGE_AUTOGENMIPMAP 0x00000400L +#endif + +#ifndef D3DSP_DCL_USAGE_MASK +#define D3DSP_DCL_USAGE_MASK 0x0000000f +#endif + +#ifndef D3DSP_OPCODESPECIFICCONTROL_MASK +#define D3DSP_OPCODESPECIFICCONTROL_MASK 0x00ff0000 +#endif + +#ifndef D3DSP_OPCODESPECIFICCONTROL_SHIFT +#define D3DSP_OPCODESPECIFICCONTROL_SHIFT 16 +#endif + +#ifndef D3DCURSOR_IMMEDIATE_UPDATE +#define D3DCURSOR_IMMEDIATE_UPDATE 0x00000001L +#endif + +#ifndef D3DPRESENT_FORCEIMMEDIATE +#define D3DPRESENT_FORCEIMMEDIATE 0x00000100L +#endif + +// From d3dtypes.h + +#ifndef D3DDEVINFOID_TEXTUREMANAGER +#define D3DDEVINFOID_TEXTUREMANAGER 1 +#endif + +#ifndef D3DDEVINFOID_D3DTEXTUREMANAGER +#define D3DDEVINFOID_D3DTEXTUREMANAGER 2 +#endif + +#ifndef D3DDEVINFOID_TEXTURING +#define D3DDEVINFOID_TEXTURING 3 +#endif + +// From d3dhal.h + +#ifndef D3DDEVINFOID_VCACHE +#define D3DDEVINFOID_VCACHE 4 +#endif + +// MinGW headers are broken. Who'dve guessed? +#ifndef _MSC_VER + +// Missing from d3d8types.h +#ifndef D3DDEVINFOID_RESOURCEMANAGER +#define D3DDEVINFOID_RESOURCEMANAGER 5 +#endif + +#ifndef D3DDEVINFOID_VERTEXSTATS +#define D3DDEVINFOID_VERTEXSTATS 6 // Aka D3DDEVINFOID_D3DVERTEXSTATS +#endif + +#else // _MSC_VER + +// These are enum typedefs in the MinGW headers, but not defined by Microsoft +#define D3DVSDT_TYPE DWORD +#define D3DVSDE_REGISTER DWORD + +#endif diff --git a/src/d3d8/d3d8_interface.cpp b/src/d3d8/d3d8_interface.cpp new file mode 100644 index 000000000..b17fd35c5 --- /dev/null +++ b/src/d3d8/d3d8_interface.cpp @@ -0,0 +1,136 @@ +#include "d3d8_interface.h" + +#include "d3d8_device.h" +#include "d3d8_texture.h" + +#include + +namespace dxvk +{ + D3D8Interface::D3D8Interface() { + m_d3d9 = d3d9::Direct3DCreate9(D3D_SDK_VERSION); + + // Get the bridge interface to D3D9. + if (FAILED(m_d3d9->QueryInterface(__uuidof(IDxvkD3D8InterfaceBridge), (void**)&m_bridge))) { + throw DxvkError("D3D8Device: ERROR! Failed to get D3D9 Bridge. d3d9.dll might not be DXVK!"); + } + + m_d3d8Options = D3D8Options(*m_bridge->GetConfig()); + + m_adapterCount = m_d3d9->GetAdapterCount(); + m_adapterModeCounts.resize(m_adapterCount); + m_adapterModes.reserve(m_adapterCount); + + for (UINT adapter = 0; adapter < m_adapterCount; adapter++) { + m_adapterModes.emplace_back(); + + // cache adapter modes and mode counts for each d3d9 format + for (d3d9::D3DFORMAT fmt : ADAPTER_FORMATS) { + + const UINT modeCount = m_d3d9->GetAdapterModeCount(adapter, fmt); + for (UINT mode = 0; mode < modeCount; mode++) { + + m_adapterModes[adapter].emplace_back(); + m_d3d9->EnumAdapterModes(adapter, fmt, mode, &(m_adapterModes[adapter].back())); + + // can't use modeCount as it's only for one fmt + m_adapterModeCounts[adapter]++; + } + } + } + } + + HRESULT STDMETHODCALLTYPE D3D8Interface::QueryInterface(REFIID riid, void** ppvObject) { + if (ppvObject == nullptr) + return E_POINTER; + + *ppvObject = nullptr; + + if (riid == __uuidof(IUnknown) + || riid == __uuidof(IDirect3D8)) { + *ppvObject = ref(this); + return S_OK; + } + + Logger::warn("D3D8Interface::QueryInterface: Unknown interface query"); + Logger::warn(str::format(riid)); + return E_NOINTERFACE; + } + + HRESULT STDMETHODCALLTYPE D3D8Interface::GetAdapterIdentifier( + UINT Adapter, + DWORD Flags, + D3DADAPTER_IDENTIFIER8* pIdentifier) { + // This flag now has the opposite effect. + // Either way, WHQLevel will be 1 with Direct3D9Ex + if (Flags & D3DENUM_NO_WHQL_LEVEL) + Flags &= ~D3DENUM_WHQL_LEVEL; + else + Flags |= D3DENUM_WHQL_LEVEL; + + d3d9::D3DADAPTER_IDENTIFIER9 identifier9; + HRESULT res = m_d3d9->GetAdapterIdentifier(Adapter, Flags, &identifier9); + + strncpy(pIdentifier->Driver, identifier9.Driver, MAX_DEVICE_IDENTIFIER_STRING); + strncpy(pIdentifier->Description, identifier9.Description, MAX_DEVICE_IDENTIFIER_STRING); + + pIdentifier->DriverVersion = identifier9.DriverVersion; + pIdentifier->VendorId = identifier9.VendorId; + pIdentifier->DeviceId = identifier9.DeviceId; + pIdentifier->SubSysId = identifier9.SubSysId; + pIdentifier->Revision = identifier9.Revision; + pIdentifier->DeviceIdentifier = identifier9.DeviceIdentifier; + + pIdentifier->WHQLLevel = identifier9.WHQLLevel; + + return res; + } + + HRESULT __stdcall D3D8Interface::EnumAdapterModes( + UINT Adapter, + UINT Mode, + D3DDISPLAYMODE* pMode) { + if (Adapter >= m_adapterCount || Mode >= m_adapterModeCounts[Adapter] || pMode == nullptr) { + return D3DERR_INVALIDCALL; + } + + pMode->Width = m_adapterModes[Adapter][Mode].Width; + pMode->Height = m_adapterModes[Adapter][Mode].Height; + pMode->RefreshRate = m_adapterModes[Adapter][Mode].RefreshRate; + pMode->Format = D3DFORMAT(m_adapterModes[Adapter][Mode].Format); + return D3D_OK; + } + + HRESULT __stdcall D3D8Interface::CreateDevice( + UINT Adapter, + D3DDEVTYPE DeviceType, + HWND hFocusWindow, + DWORD BehaviorFlags, + D3DPRESENT_PARAMETERS* pPresentationParameters, + IDirect3DDevice8** ppReturnedDeviceInterface) { + Com pDevice9 = nullptr; + d3d9::D3DPRESENT_PARAMETERS params = ConvertPresentParameters9(pPresentationParameters); + HRESULT res = m_d3d9->CreateDevice( + Adapter, + (d3d9::D3DDEVTYPE)DeviceType, + hFocusWindow, + BehaviorFlags, + ¶ms, + &pDevice9 + ); + + if (FAILED(res)) { + return res; + } + + *ppReturnedDeviceInterface = ref(new D3D8Device( + this, std::move(pDevice9), + DeviceType, hFocusWindow, BehaviorFlags, + pPresentationParameters + )); + + return res; + } + + +} \ No newline at end of file diff --git a/src/d3d8/d3d8_interface.h b/src/d3d8/d3d8_interface.h new file mode 100644 index 000000000..59b87a860 --- /dev/null +++ b/src/d3d8/d3d8_interface.h @@ -0,0 +1,163 @@ +#pragma once + +#include "d3d8_include.h" +#include "d3d8_d3d9_util.h" +#include "d3d8_options.h" +#include "d3d8_format.h" +#include "../d3d9/d3d9_bridge.h" + +namespace dxvk { + + /** + * \brief D3D8 interface implementation + * + * Implements the IDirect3DDevice8 interfaces + * which provides the way to get adapters and create other objects such as \ref IDirect3DDevice8. + * similar to \ref DxgiFactory but for D3D8. + */ + class D3D8Interface final : public ComObjectClamp { + + static constexpr d3d9::D3DFORMAT ADAPTER_FORMATS[] = { + d3d9::D3DFMT_A1R5G5B5, + //d3d9::D3DFMT_A2R10G10B10, (not in D3D8) + d3d9::D3DFMT_A8R8G8B8, + d3d9::D3DFMT_R5G6B5, + d3d9::D3DFMT_X1R5G5B5, + d3d9::D3DFMT_X8R8G8B8 + }; + + public: + D3D8Interface(); + + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject); + + HRESULT STDMETHODCALLTYPE RegisterSoftwareDevice(void* pInitializeFunction) { + return m_d3d9->RegisterSoftwareDevice(pInitializeFunction); + } + + UINT STDMETHODCALLTYPE GetAdapterCount() { + return m_d3d9->GetAdapterCount(); + } + + HRESULT STDMETHODCALLTYPE GetAdapterIdentifier( + UINT Adapter, + DWORD Flags, + D3DADAPTER_IDENTIFIER8* pIdentifier); + + UINT STDMETHODCALLTYPE GetAdapterModeCount(UINT Adapter) { + return m_adapterModeCounts[Adapter]; + } + + HRESULT STDMETHODCALLTYPE EnumAdapterModes( + UINT Adapter, + UINT Mode, + D3DDISPLAYMODE* pMode); + + HRESULT STDMETHODCALLTYPE GetAdapterDisplayMode(UINT Adapter, D3DDISPLAYMODE* pMode) { + return m_d3d9->GetAdapterDisplayMode(Adapter, (d3d9::D3DDISPLAYMODE*)pMode); + } + + HRESULT STDMETHODCALLTYPE CheckDeviceType( + UINT Adapter, + D3DDEVTYPE DevType, + D3DFORMAT AdapterFormat, + D3DFORMAT BackBufferFormat, + BOOL bWindowed) { + return m_d3d9->CheckDeviceType( + Adapter, + (d3d9::D3DDEVTYPE)DevType, + (d3d9::D3DFORMAT)AdapterFormat, + (d3d9::D3DFORMAT)BackBufferFormat, + bWindowed + ); + } + + HRESULT STDMETHODCALLTYPE CheckDeviceFormat( + UINT Adapter, + D3DDEVTYPE DeviceType, + D3DFORMAT AdapterFormat, + DWORD Usage, + D3DRESOURCETYPE RType, + D3DFORMAT CheckFormat) { + return m_d3d9->CheckDeviceFormat( + Adapter, + (d3d9::D3DDEVTYPE)DeviceType, + (d3d9::D3DFORMAT)AdapterFormat, + Usage, + (d3d9::D3DRESOURCETYPE)RType, + (d3d9::D3DFORMAT)CheckFormat + ); + } + + HRESULT STDMETHODCALLTYPE CheckDeviceMultiSampleType( + UINT Adapter, + D3DDEVTYPE DeviceType, + D3DFORMAT SurfaceFormat, + BOOL Windowed, + D3DMULTISAMPLE_TYPE MultiSampleType) { + DWORD* pQualityLevels = nullptr; + return m_d3d9->CheckDeviceMultiSampleType( + Adapter, + (d3d9::D3DDEVTYPE)DeviceType, + (d3d9::D3DFORMAT)SurfaceFormat, + Windowed, + (d3d9::D3DMULTISAMPLE_TYPE)MultiSampleType, + pQualityLevels + ); + } + + HRESULT STDMETHODCALLTYPE CheckDepthStencilMatch( + UINT Adapter, + D3DDEVTYPE DeviceType, + D3DFORMAT AdapterFormat, + D3DFORMAT RenderTargetFormat, + D3DFORMAT DepthStencilFormat) { + if (isSupportedDepthStencilFormat(DepthStencilFormat)) + return m_d3d9->CheckDepthStencilMatch( + Adapter, + (d3d9::D3DDEVTYPE)DeviceType, + (d3d9::D3DFORMAT)AdapterFormat, + (d3d9::D3DFORMAT)RenderTargetFormat, + (d3d9::D3DFORMAT)DepthStencilFormat + ); + + return D3DERR_NOTAVAILABLE; + } + + HRESULT STDMETHODCALLTYPE GetDeviceCaps( + UINT Adapter, + D3DDEVTYPE DeviceType, + D3DCAPS8* pCaps) { + d3d9::D3DCAPS9 caps9; + HRESULT res = m_d3d9->GetDeviceCaps(Adapter, (d3d9::D3DDEVTYPE)DeviceType, &caps9); + dxvk::ConvertCaps8(caps9, pCaps); + return res; + } + + HMONITOR STDMETHODCALLTYPE GetAdapterMonitor(UINT Adapter) { + return m_d3d9->GetAdapterMonitor(Adapter); + } + + HRESULT STDMETHODCALLTYPE CreateDevice( + UINT Adapter, + D3DDEVTYPE DeviceType, + HWND hFocusWindow, + DWORD BehaviorFlags, + D3DPRESENT_PARAMETERS* pPresentationParameters, + IDirect3DDevice8** ppReturnedDeviceInterface); + + + const D3D8Options& GetOptions() { return m_d3d8Options; } + + private: + + UINT m_adapterCount; + std::vector m_adapterModeCounts; + std::vector> m_adapterModes; + + d3d9::IDirect3D9* m_d3d9; + Com m_bridge; + D3D8Options m_d3d8Options; + }; + +} \ No newline at end of file diff --git a/src/d3d8/d3d8_main.cpp b/src/d3d8/d3d8_main.cpp new file mode 100644 index 000000000..265a5baab --- /dev/null +++ b/src/d3d8/d3d8_main.cpp @@ -0,0 +1,24 @@ +#include "d3d8_interface.h" + +namespace dxvk { + Logger Logger::s_instance("d3d8.log"); + + HRESULT CreateD3D8(IDirect3D8** ppDirect3D8) { + if (!ppDirect3D8) + return D3DERR_INVALIDCALL; + + *ppDirect3D8 = ref(new D3D8Interface()); + return D3D_OK; + } +} + +extern "C" { + DLLEXPORT IDirect3D8* __stdcall Direct3DCreate8(UINT nSDKVersion) { + dxvk::Logger::trace("Direct3DCreate8 called"); + + IDirect3D8* pDirect3D = nullptr; + dxvk::CreateD3D8(&pDirect3D); + + return pDirect3D; + } +} diff --git a/src/d3d8/d3d8_options.cpp b/src/d3d8/d3d8_options.cpp new file mode 100644 index 000000000..61f778058 --- /dev/null +++ b/src/d3d8/d3d8_options.cpp @@ -0,0 +1,55 @@ +#include "d3d8_options.h" +#include "../d3d9/d3d9_bridge.h" +#include "../util/config/config.h" +#include "../util/util_string.h" + +#include + +namespace dxvk { + static inline uint32_t parseDword(std::string_view str) { + uint32_t value = UINT32_MAX; + std::from_chars(str.data(), str.data() + str.size(), value); + return value; + } + + void D3D8Options::parseVsDecl(const std::string& decl) { + if (decl.empty()) + return; + + if (decl.find_first_of("0123456789") == std::string::npos) { + Logger::warn(str::format("D3D8: Invalid forceVsDecl value: ", decl)); + Logger::warn("D3D8: Expected numbers."); + return; + } + + if (decl.find_first_of(":,;") == std::string::npos) { + Logger::warn(str::format("D3D8: Invalid forceVsDecl value: ", decl)); + Logger::warn("D3D8: Expected a comma-separated list of colon-separated number pairs."); + return; + } + + std::vector decls = str::split(decl, ":,;"); + + if (decls.size() % 2 != 0) { + Logger::warn(str::format("D3D8: Invalid forceVsDecl value: ", decl)); + Logger::warn("D3D8: Expected an even number of numbers."); + return; + } + + for (size_t i = 0; i < decls.size(); i += 2) { + uint32_t reg = parseDword(decls[i]); + uint32_t type = parseDword(decls[i+1]); + if (reg > D3DVSDE_NORMAL2) { + Logger::warn(str::format("D3D8: Invalid forceVsDecl register number: ", decls[i])); + return; + } + if (type > D3DVSDT_SHORT4) { + Logger::warn(str::format("D3D8: Invalid forceVsDecl type: ", decls[i+1])); + return; + } + + forceVsDecl.emplace_back(D3DVSDE_REGISTER(reg), D3DVSDT_TYPE(type)); + } + + } +} diff --git a/src/d3d8/d3d8_options.h b/src/d3d8/d3d8_options.h new file mode 100644 index 000000000..67bdf3592 --- /dev/null +++ b/src/d3d8/d3d8_options.h @@ -0,0 +1,48 @@ +#pragma once + +#include "d3d8_include.h" +#include "../d3d9/d3d9_bridge.h" +#include "../util/config/config.h" + +namespace dxvk { + struct D3D8Options { + + /// Some games rely on undefined behavior by using undeclared vertex shader inputs. + /// The simplest way to fix them is to simply modify their vertex shader decl. + /// + /// This option takes a comma-separated list of colon-separated number pairs, where + /// the first number is a D3DVSDE_REGISTER value, the second is a D3DVSDT_TYPE value. + /// e.g. "0:2,3:2,7:1" for float3 position : v0, float3 normal : v3, float2 uv : v7 + std::vector> forceVsDecl; + + /// Specialized drawcall batcher, typically for games that draw a lot of similar + /// geometry in separate drawcalls (sometimes even one triangle at a time). + /// + /// May hurt performance outside of specifc games that benefit from it. + bool batching = false; + + /// The Lord of the Rings: The Fellowship of the Ring tries to create a P8 texture + /// in D3DPOOL_MANAGED on Nvidia and Intel, which fails, but has a separate code + /// path for ATI/AMD that creates it in D3DPOOL_SCRATCH instead, which works. + /// + /// The internal logic determining this path doesn't seem to be d3d-related, but + /// the game works universally if we mimic its own ATI/AMD workaround during P8 + /// texture creation. + /// + /// Early Nvidia GPUs, such as the GeForce 4 generation cards, included and exposed + /// P8 texture support. However, it was no longer advertised with cards in the FX series + /// and above. Most likely ATI/AMD drivers never supported P8 in the first place. + bool placeP8InScratch = false; + + D3D8Options() {} + D3D8Options(const Config& config) { + auto forceVsDeclStr = config.getOption("d3d8.forceVsDecl", ""); + batching = config.getOption ("d3d8.batching", batching); + placeP8InScratch = config.getOption ("d3d8.placeP8InScratch", placeP8InScratch); + + parseVsDecl(forceVsDeclStr); + } + + void parseVsDecl(const std::string& decl); + }; +} diff --git a/src/d3d8/d3d8_resource.h b/src/d3d8/d3d8_resource.h new file mode 100644 index 000000000..c5562e1ae --- /dev/null +++ b/src/d3d8/d3d8_resource.h @@ -0,0 +1,100 @@ +#pragma once + +/** Implements IDirect3DResource8 +* +* - SetPrivateData, GetPrivateData, FreePrivateData +* - SetPriority, GetPriority +* +* - Subclasses provide: PreLoad, GetType +*/ + +#include "d3d8_device_child.h" +#include "../util/com/com_private_data.h" + +namespace dxvk { + + template + class D3D8Resource : public D3D8DeviceChild { + + public: + + D3D8Resource(D3D8Device* pDevice, Com&& Object) + : D3D8DeviceChild(pDevice, std::move(Object)) + , m_priority ( 0 ) { } + + HRESULT STDMETHODCALLTYPE SetPrivateData( + REFGUID refguid, + const void* pData, + DWORD SizeOfData, + DWORD Flags) final { + HRESULT hr; + if (Flags & D3DSPD_IUNKNOWN) { + IUnknown* unknown = + const_cast( + reinterpret_cast(pData)); + hr = m_privateData.setInterface( + refguid, unknown); + } + else + hr = m_privateData.setData( + refguid, SizeOfData, pData); + + if (FAILED(hr)) + return D3DERR_INVALIDCALL; + + return D3D_OK; + } + + HRESULT STDMETHODCALLTYPE GetPrivateData( + REFGUID refguid, + void* pData, + DWORD* pSizeOfData) final { + HRESULT hr = m_privateData.getData( + refguid, reinterpret_cast(pSizeOfData), pData); + + if (FAILED(hr)) + return D3DERR_INVALIDCALL; + + return D3D_OK; + } + + HRESULT STDMETHODCALLTYPE FreePrivateData(REFGUID refguid) final { + HRESULT hr = m_privateData.setData(refguid, 0, nullptr); + + if (FAILED(hr)) + return D3DERR_INVALIDCALL; + + return D3D_OK; + } + + DWORD STDMETHODCALLTYPE SetPriority(DWORD PriorityNew) { + DWORD oldPriority = m_priority; + m_priority = PriorityNew; + return oldPriority; + } + + DWORD STDMETHODCALLTYPE GetPriority() { + return m_priority; + } + + virtual IUnknown* GetInterface(REFIID riid) override try { + return D3D8DeviceChild::GetInterface(riid); + } catch (HRESULT err) { + if (riid == __uuidof(IDirect3DResource8)) + return this; + + throw err; + } + + protected: + + DWORD m_priority; + + private: + + ComPrivateData m_privateData; + + }; + + +} \ No newline at end of file diff --git a/src/d3d8/d3d8_shader.cpp b/src/d3d8/d3d8_shader.cpp new file mode 100644 index 000000000..7b44ba5ee --- /dev/null +++ b/src/d3d8/d3d8_shader.cpp @@ -0,0 +1,336 @@ + +#include "d3d8_shader.h" + +#define VSD_SHIFT_MASK(token, field) ((token & field ## MASK) >> field ## SHIFT) +#define VSD_ENCODE(token, field) ((token << field ## _SHIFT) & field ## _MASK) + +// Magic number from D3DVSD_SKIP(...) +#define VSD_SKIP_FLAG 0x10000000 + +// This bit is set on all parameter (non-instruction) tokens. +#define VS_BIT_PARAM 0x80000000 + +namespace dxvk { + + static constexpr int D3D8_NUM_VERTEX_INPUT_REGISTERS = 17; + + /** + * Standard mapping of vertex input registers v0-v16 to D3D9 usages and usage indices + * (See D3DVSDE_REGISTER values in d3d8types.h or DirectX 8 docs for vertex shader input registers vN) + * + * \cite https://learn.microsoft.com/en-us/windows/win32/direct3d9/mapping-between-a-directx-9-declaration-and-directx-8 + */ + static constexpr BYTE D3D8_VERTEX_INPUT_REGISTERS[D3D8_NUM_VERTEX_INPUT_REGISTERS][2] = { + {d3d9::D3DDECLUSAGE_POSITION, 0}, // dcl_position v0 + {d3d9::D3DDECLUSAGE_BLENDWEIGHT, 0}, // dcl_blendweight v1 + {d3d9::D3DDECLUSAGE_BLENDINDICES, 0}, // dcl_blendindices v2 + {d3d9::D3DDECLUSAGE_NORMAL, 0}, // dcl_normal v3 + {d3d9::D3DDECLUSAGE_PSIZE, 0}, // dcl_psize v4 + {d3d9::D3DDECLUSAGE_COLOR, 0}, // dcl_color v5 ; diffuse + {d3d9::D3DDECLUSAGE_COLOR, 1}, // dcl_color1 v6 ; specular + {d3d9::D3DDECLUSAGE_TEXCOORD, 0}, // dcl_texcoord0 v7 + {d3d9::D3DDECLUSAGE_TEXCOORD, 1}, // dcl_texcoord1 v8 + {d3d9::D3DDECLUSAGE_TEXCOORD, 2}, // dcl_texcoord2 v9 + {d3d9::D3DDECLUSAGE_TEXCOORD, 3}, // dcl_texcoord3 v10 + {d3d9::D3DDECLUSAGE_TEXCOORD, 4}, // dcl_texcoord4 v11 + {d3d9::D3DDECLUSAGE_TEXCOORD, 5}, // dcl_texcoord5 v12 + {d3d9::D3DDECLUSAGE_TEXCOORD, 6}, // dcl_texcoord6 v13 + {d3d9::D3DDECLUSAGE_TEXCOORD, 7}, // dcl_texcoord7 v14 + {d3d9::D3DDECLUSAGE_POSITION, 1}, // dcl_position1 v15 ; position 2 + {d3d9::D3DDECLUSAGE_NORMAL, 1}, // dcl_normal1 v16 ; normal 2 + }; + + /** Width in bytes of each d3d9::D3DDECLTYPE or d3d8 D3DVSDT_TYPE */ + static constexpr BYTE D3D9_DECL_TYPE_SIZES[d3d9::MAXD3DDECLTYPE + 1] = { + 4, // FLOAT1 + 8, // FLOAT2 + 12, // FLOAT3 + 16, // FLOAT4 + 4, // D3DCOLOR + + 4, // UBYTE4 + 4, // SHORT2 + 8, // SHORT4 + + // The following are for vs2.0+ // + 4, // UBYTE4N + 4, // SHORT2N + 8, // SHORT4N + 4, // USHORT2N + 8, // USHORT4N + 6, // UDEC3 + 6, // DEC3N + 8, // FLOAT16_2 + 16, // FLOAT16_4 + + 0 // UNUSED + }; + + /** + * Encodes a \ref DxsoShaderInstruction + * + * \param [in] opcode DxsoOpcode + * \cite https://learn.microsoft.com/en-us/windows-hardware/drivers/display/instruction-token + */ + constexpr DWORD encodeInstruction(d3d9::D3DSHADER_INSTRUCTION_OPCODE_TYPE opcode) { + DWORD token = 0; + token |= opcode & 0xFFFF; // bits 0:15 + return token; + } + + /** + * Encodes a \ref DxsoRegister + * + * \param [in] regType DxsoRegisterType + * \cite https://learn.microsoft.com/en-us/windows-hardware/drivers/display/destination-parameter-token + */ + constexpr DWORD encodeDestRegister(d3d9::D3DSHADER_PARAM_REGISTER_TYPE type, UINT reg) { + DWORD token = 0; + token |= reg & 0x7FF; // bits 0:10 num + token |= ((type & 0x07) << 28); // bits 28:30 type[0:2] + token |= ((type & 0x18) >> 3) << 11; // bits 11:12 type[3:4] + // UINT addrMode : 1; // bit 13 hasRelative + token |= 0b1111 << 16; // bits 16:19 DxsoRegMask + // UINT resultModifier : 3; // bits 20:23 + // UINT resultShift : 3; // bits 24:27 + token |= 1 << 31; // bit 31 always 1 + return token; + } + + /** + * Encodes a \ref DxsoDeclaration + * + * \cite https://learn.microsoft.com/en-us/windows-hardware/drivers/display/dcl-instruction + */ + constexpr DWORD encodeDeclaration(d3d9::D3DDECLUSAGE usage, DWORD index) { + DWORD token = 0; + token |= VSD_ENCODE(usage, D3DSP_DCL_USAGE); // bits 0:4 DxsoUsage (TODO: missing MSB) + token |= VSD_ENCODE(index, D3DSP_DCL_USAGEINDEX); // bits 16:19 usageIndex + token |= 1 << 31; // bit 31 always 1 + return token; + } + + /** + * Converts a D3D8 vertex shader + declaration + * to a D3D9 vertex shader + declaration. + */ + D3D9VertexShaderCode TranslateVertexShader8( + const DWORD* pDeclaration, + const DWORD* pFunction, + const D3D8Options& options) { + using d3d9::D3DDECLTYPE; + using d3d9::D3DDECLTYPE_UNUSED; + + D3D9VertexShaderCode result; + + std::vector& tokens = result.function; + std::vector defs; // Constant definitions + + // shaderInputRegisters: + // set bit N to enable input register vN + DWORD shaderInputRegisters = 0; + + d3d9::D3DVERTEXELEMENT9* vertexElements = result.declaration; + unsigned int elementIdx = 0; + + // These are used for pDeclaration and pFunction + int i = 0; + DWORD token; + + std::stringstream dbg; + dbg << "Vertex Declaration Tokens:\n\t"; + + WORD currentStream = 0; + WORD currentOffset = 0; + + auto addVertexElement = [&] (D3DVSDE_REGISTER reg, D3DVSDT_TYPE type) { + vertexElements[elementIdx].Stream = currentStream; + vertexElements[elementIdx].Offset = currentOffset; + vertexElements[elementIdx].Method = d3d9::D3DDECLMETHOD_DEFAULT; + vertexElements[elementIdx].Type = D3DDECLTYPE(type); // (D3DVSDT_TYPE values map directly to D3DDECLTYPE) + vertexElements[elementIdx].Usage = D3D8_VERTEX_INPUT_REGISTERS[reg][0]; + vertexElements[elementIdx].UsageIndex = D3D8_VERTEX_INPUT_REGISTERS[reg][1]; + + // Increase stream offset + currentOffset += D3D9_DECL_TYPE_SIZES[type]; + + // Enable register vn + shaderInputRegisters |= 1 << reg; + + // Finished with this element + elementIdx++; + }; + + // Remap d3d8 decl tokens to d3d9 vertex elements, + // and enable bits on shaderInputRegisters for each. + if (options.forceVsDecl.size() == 0) do { + token = pDeclaration[i++]; + + D3DVSD_TOKENTYPE tokenType = D3DVSD_TOKENTYPE(VSD_SHIFT_MASK(token, D3DVSD_TOKENTYPE)); + + switch (tokenType) { + case D3DVSD_TOKEN_NOP: + dbg << "NOP"; + break; + case D3DVSD_TOKEN_STREAM: { + dbg << "STREAM "; + + // TODO: D3DVSD_STREAM_TESS + if (token & D3DVSD_STREAMTESSMASK) { + dbg << "TESS"; + } + + DWORD streamNum = VSD_SHIFT_MASK(token, D3DVSD_STREAMNUMBER); + + currentStream = WORD(streamNum); + currentOffset = 0; // reset offset + + dbg << ", num=" << streamNum; + break; + } + case D3DVSD_TOKEN_STREAMDATA: { + + dbg << "STREAMDATA "; + + // D3DVSD_SKIP + if (token & VSD_SKIP_FLAG) { + auto skipCount = VSD_SHIFT_MASK(token, D3DVSD_SKIPCOUNT); + dbg << "SKIP " << " count=" << skipCount; + currentOffset += WORD(skipCount) * sizeof(DWORD); + break; + } + + // D3DVSD_REG + DWORD dataLoadType = VSD_SHIFT_MASK(token, D3DVSD_DATALOADTYPE); + + if ( dataLoadType == 0 ) { // vertex + D3DVSDT_TYPE type = D3DVSDT_TYPE(VSD_SHIFT_MASK(token, D3DVSD_DATATYPE)); + D3DVSDE_REGISTER reg = D3DVSDE_REGISTER(VSD_SHIFT_MASK(token, D3DVSD_VERTEXREG)); + + addVertexElement(reg, type); + + dbg << "type=" << type << ", register=" << reg; + } else { + // TODO: When would this bit be 1? + dbg << "D3DVSD_DATALOADTYPE " << dataLoadType; + } + break; + } + case D3DVSD_TOKEN_TESSELLATOR: + dbg << "TESSELLATOR " << std::hex << token; + // TODO: D3DVSD_TOKEN_TESSELLATOR + break; + case D3DVSD_TOKEN_CONSTMEM: { + dbg << "CONSTMEM "; + DWORD count = VSD_SHIFT_MASK(token, D3DVSD_CONSTCOUNT); + DWORD regCount = count * 4; + DWORD addr = VSD_SHIFT_MASK(token, D3DVSD_CONSTADDRESS); + DWORD rs = VSD_SHIFT_MASK(token, D3DVSD_CONSTRS); + + dbg << "count=" << count << ", addr=" << addr << ", rs=" << rs; + + // Add a DEF instruction for each constant + for (DWORD j = 0; j < regCount; j += 4) { + defs.push_back(encodeInstruction(d3d9::D3DSIO_DEF)); + defs.push_back(encodeDestRegister(d3d9::D3DSPR_CONST2, addr)); + defs.push_back(pDeclaration[i+j+0]); + defs.push_back(pDeclaration[i+j+1]); + defs.push_back(pDeclaration[i+j+2]); + defs.push_back(pDeclaration[i+j+3]); + addr++; + } + i += regCount; + break; + } + case D3DVSD_TOKEN_EXT: { + dbg << "EXT " << std::hex << token << " "; + DWORD extInfo = VSD_SHIFT_MASK(token, D3DVSD_EXTINFO); + DWORD extCount = VSD_SHIFT_MASK(token, D3DVSD_EXTCOUNT); + dbg << "info=" << extInfo << ", count=" << extCount; + break; + } + case D3DVSD_TOKEN_END: { + vertexElements[elementIdx++] = D3DDECL_END(); + dbg << "END"; + break; + } + default: + dbg << "UNKNOWN TYPE"; + break; + } + dbg << "\n\t"; + //dbg << std::hex << token << " "; + } while (token != D3DVSD_END()); + + Logger::debug(dbg.str()); + + // If forceVsDecl is set, use that decl instead. + if (options.forceVsDecl.size() > 0) { + for (auto [reg, type] : options.forceVsDecl) { + addVertexElement(reg, type); + } + vertexElements[elementIdx++] = D3DDECL_END(); + } + + if (pFunction != nullptr) { + // Copy first token (version) + tokens.push_back(pFunction[0]); + + DWORD vsMajor = D3DSHADER_VERSION_MAJOR(pFunction[0]); + DWORD vsMinor = D3DSHADER_VERSION_MINOR(pFunction[0]); + Logger::debug(str::format("VS version: ", vsMajor, ".", vsMinor)); + + // Insert dcl instructions + for (int vn = 0; vn < D3D8_NUM_VERTEX_INPUT_REGISTERS; vn++) { + + // If bit N is set then we need to dcl register vN + if ((shaderInputRegisters & (1 << vn)) != 0) { + + Logger::debug(str::format("\tShader Input Regsiter: v", vn)); + + DWORD usage = D3D8_VERTEX_INPUT_REGISTERS[vn][0]; + DWORD index = D3D8_VERTEX_INPUT_REGISTERS[vn][1]; + + tokens.push_back(encodeInstruction(d3d9::D3DSIO_DCL)); // dcl opcode + tokens.push_back(encodeDeclaration(d3d9::D3DDECLUSAGE(usage), index)); // usage token + tokens.push_back(encodeDestRegister(d3d9::D3DSPR_INPUT, vn)); // dest register num + } + } + + // Copy constant defs + for (DWORD def : defs) { + tokens.push_back(def); + } + + // Copy shader tokens from input, + // skip first token (we already copied it) + i = 1; + do { + token = pFunction[i++]; + + DWORD opcode = token & D3DSI_OPCODE_MASK; + + // Instructions + if ((token & VS_BIT_PARAM) == 0) { + + // RSQ swizzle fixup + if (opcode == D3DSIO_RSQ) { + tokens.push_back(token); // instr + tokens.push_back(token = pFunction[i++]); // dest + token = pFunction[i++]; // src0 + + // If no swizzling is done, then use the w-component. + // See d8vk#43 for more information as this may need to change in some cases. + if (((token & D3DVS_NOSWIZZLE) == D3DVS_NOSWIZZLE)) { + token &= ~D3DVS_SWIZZLE_MASK; + token |= (D3DVS_X_W | D3DVS_Y_W | D3DVS_Z_W | D3DVS_W_W); + } + } + } + tokens.push_back(token); + } while (token != D3DVS_END()); + } + + return result; + } +} diff --git a/src/d3d8/d3d8_shader.h b/src/d3d8/d3d8_shader.h new file mode 100644 index 000000000..f0a83ae12 --- /dev/null +++ b/src/d3d8/d3d8_shader.h @@ -0,0 +1,18 @@ +#pragma once + +#include "d3d8_include.h" +#include "d3d8_options.h" + +namespace dxvk { + + struct D3D9VertexShaderCode { + d3d9::D3DVERTEXELEMENT9 declaration[MAXD3DDECLLENGTH + 1]; + std::vector function; + }; + + D3D9VertexShaderCode TranslateVertexShader8( + const DWORD* pDeclaration, + const DWORD* pFunction, + const D3D8Options& overrides); + +} \ No newline at end of file diff --git a/src/d3d8/d3d8_state_block.cpp b/src/d3d8/d3d8_state_block.cpp new file mode 100644 index 000000000..b4cb468e3 --- /dev/null +++ b/src/d3d8/d3d8_state_block.cpp @@ -0,0 +1,49 @@ +#include "d3d8_device.h" +#include "d3d8_state_block.h" + +HRESULT dxvk::D3D8StateBlock::Capture() { + if (unlikely(m_stateBlock == nullptr)) + return D3DERR_INVALIDCALL; + + if (m_capture.vs) m_device->GetVertexShader(&m_vertexShader); + if (m_capture.ps) m_device->GetPixelShader(&m_pixelShader); + + for (DWORD stage = 0; stage < m_textures.size(); stage++) { + if (m_capture.textures.get(stage)) + m_textures[stage] = m_device->m_textures[stage].ptr(); + } + + if (m_capture.indices) { + m_baseVertexIndex = m_device->m_baseVertexIndex; + m_indices = m_device->m_indices.ptr(); + } + + if (m_capture.swvp) + m_device->GetRenderState(D3DRS_SOFTWAREVERTEXPROCESSING, (DWORD*)&m_isSWVP); + + return m_stateBlock->Capture(); +} + +HRESULT dxvk::D3D8StateBlock::Apply() { + if (unlikely(m_stateBlock == nullptr)) + return D3DERR_INVALIDCALL; + + HRESULT res = m_stateBlock->Apply(); + + if (m_capture.vs) m_device->SetVertexShader(m_vertexShader); + if (m_capture.ps) m_device->SetPixelShader(m_pixelShader); + + for (DWORD stage = 0; stage < m_textures.size(); stage++) { + if (m_capture.textures.get(stage)) + m_device->SetTexture(stage, m_textures[stage]); + } + + if (m_capture.indices) + m_device->SetIndices(m_indices, m_baseVertexIndex); + + // This was a very easy footgun for D3D8 applications. + if (m_capture.swvp) + m_device->SetRenderState(D3DRS_SOFTWAREVERTEXPROCESSING, m_isSWVP); + + return res; +} diff --git a/src/d3d8/d3d8_state_block.h b/src/d3d8/d3d8_state_block.h new file mode 100644 index 000000000..18ab4cb91 --- /dev/null +++ b/src/d3d8/d3d8_state_block.h @@ -0,0 +1,134 @@ +#pragma once + +#include "d3d8_caps.h" +#include "d3d8_include.h" +#include "d3d8_device.h" +#include "d3d8_device_child.h" + +#include + +namespace dxvk { + + struct D3D8StateCapture { + bool vs : 1; + bool ps : 1; + bool indices : 1; + bool swvp : 1; + + bit::bitset textures; + + D3D8StateCapture() + : vs(false) + , ps(false) + , indices(false) + , swvp(false) { + // Ensure all bits are initialized to false + textures.clearAll(); + } + }; + + // Wrapper class for D3D9 state blocks. Captures D3D8-specific state. + class D3D8StateBlock { + + public: + + D3D8StateBlock( + D3D8Device* pDevice, + D3DSTATEBLOCKTYPE Type, + Com&& pStateBlock) + : m_device(pDevice) + , m_stateBlock(std::move(pStateBlock)) + , m_type(Type) { + if (Type == D3DSBT_VERTEXSTATE || Type == D3DSBT_ALL) { + // Lights, D3DTSS_TEXCOORDINDEX and D3DTSS_TEXTURETRANSFORMFLAGS, + // vertex shader, VS constants, and various render states. + m_capture.vs = true; + } + + if (Type == D3DSBT_PIXELSTATE || Type == D3DSBT_ALL) { + // Pixel shader, PS constants, and various RS/TSS states. + m_capture.ps = true; + } + + if (Type == D3DSBT_ALL) { + m_capture.indices = true; + m_capture.swvp = true; + m_capture.textures.setAll(); + } + + m_textures.fill(nullptr); + } + + ~D3D8StateBlock() {} + + // Construct a state block without a D3D9 object + D3D8StateBlock(D3D8Device* pDevice) + : D3D8StateBlock(pDevice, D3DSTATEBLOCKTYPE(0), nullptr) { + } + + // Attach a D3D9 object to a state block that doesn't have one yet + void SetD3D9(Com&& pStateBlock) { + if (likely(m_stateBlock == nullptr)) { + m_stateBlock = std::move(pStateBlock); + } else { + Logger::err("D3D8StateBlock::SetD3D9 called when m_stateBlock has already been initialized"); + } + } + + HRESULT Capture(); + + HRESULT Apply(); + + inline HRESULT SetVertexShader(DWORD Handle) { + m_vertexShader = Handle; + m_capture.vs = true; + return D3D_OK; + } + + inline HRESULT SetPixelShader(DWORD Handle) { + m_pixelShader = Handle; + m_capture.ps = true; + return D3D_OK; + } + + inline HRESULT SetTexture(DWORD Stage, IDirect3DBaseTexture8* pTexture) { + m_textures[Stage] = pTexture; + m_capture.textures.set(Stage, true); + return D3D_OK; + } + + inline HRESULT SetIndices(IDirect3DIndexBuffer8* pIndexData, UINT BaseVertexIndex) { + m_indices = pIndexData; + m_baseVertexIndex = BaseVertexIndex; + m_capture.indices = true; + return D3D_OK; + } + + inline HRESULT SetSoftwareVertexProcessing(bool value) { + m_isSWVP = value; + m_capture.swvp = true; + return D3D_OK; + } + + private: + D3D8Device* m_device; + Com m_stateBlock; + D3DSTATEBLOCKTYPE m_type; + + private: // State Data // + + D3D8StateCapture m_capture; + + DWORD m_vertexShader; // vs + DWORD m_pixelShader; // ps + + std::array m_textures; // textures + + IDirect3DIndexBuffer8* m_indices = nullptr; // indices + UINT m_baseVertexIndex; // indices + + bool m_isSWVP; // D3DRS_SOFTWAREVERTEXPROCESSING + }; + + +} \ No newline at end of file diff --git a/src/d3d8/d3d8_subresource.h b/src/d3d8/d3d8_subresource.h new file mode 100644 index 000000000..c4cd14609 --- /dev/null +++ b/src/d3d8/d3d8_subresource.h @@ -0,0 +1,61 @@ +#pragma once + +#include "d3d8_resource.h" + +namespace dxvk { + + // Base class for Surfaces and Volumes, + // which can be attached to Textures. + + template + class D3D8Subresource : public D3D8Resource { + + using Resource = D3D8Resource; + + public: + + D3D8Subresource( + D3D8Device* pDevice, + Com&& Object, + IDirect3DBaseTexture8* pBaseTexture) + : Resource(pDevice, std::move(Object)), + m_container(pBaseTexture) { + } + + ~D3D8Subresource() { + } + + // Refing subresources implicitly refs the container texture, + ULONG STDMETHODCALLTYPE AddRef() final { + if (m_container != nullptr) + return m_container->AddRef(); + + return Resource::AddRef(); + } + + // and releasing them implicitly releases the texture. + ULONG STDMETHODCALLTYPE Release() final { + if (m_container != nullptr) + return m_container->Release(); + + return Resource::Release(); + } + + // Clients can grab the container if they want + HRESULT STDMETHODCALLTYPE GetContainer(REFIID riid, void** ppContainer) final { + if (m_container != nullptr) + return m_container->QueryInterface(riid, ppContainer); + + return this->GetDevice()->QueryInterface(riid, ppContainer); + } + + inline IDirect3DBaseTexture8* GetBaseTexture() { + return m_container; + } + + protected: + + IDirect3DBaseTexture8* m_container; + }; + +} \ No newline at end of file diff --git a/src/d3d8/d3d8_surface.cpp b/src/d3d8/d3d8_surface.cpp new file mode 100644 index 000000000..8d7c970bb --- /dev/null +++ b/src/d3d8/d3d8_surface.cpp @@ -0,0 +1,26 @@ + +#include "d3d8_surface.h" +#include "d3d8_device.h" + +namespace dxvk { + + Com D3D8Surface::CreateBlitImage() { + d3d9::D3DSURFACE_DESC desc; + GetD3D9()->GetDesc(&desc); + + // NOTE: This adds a D3DPOOL_DEFAULT resource to the + // device, which counts as losable during device reset + Com image = nullptr; + HRESULT res = GetParent()->GetD3D9()->CreateRenderTarget( + desc.Width, desc.Height, desc.Format, + d3d9::D3DMULTISAMPLE_NONE, 0, + FALSE, + &image, + NULL); + + if (FAILED(res)) + throw new DxvkError("D3D8: Failed to create blit image"); + + return image; + } +} \ No newline at end of file diff --git a/src/d3d8/d3d8_surface.h b/src/d3d8/d3d8_surface.h new file mode 100644 index 000000000..600f47eb7 --- /dev/null +++ b/src/d3d8/d3d8_surface.h @@ -0,0 +1,81 @@ +#pragma once + +#include "d3d8_include.h" +#include "d3d8_subresource.h" +#include "d3d8_d3d9_util.h" + +namespace dxvk { + + // TODO: all inherited methods in D3D8Surface should be final like in d9vk + + using D3D8SurfaceBase = D3D8Subresource; + class D3D8Surface final : public D3D8SurfaceBase { + + public: + + D3D8Surface( + D3D8Device* pDevice, + IDirect3DBaseTexture8* pTexture, + Com&& pSurface) + : D3D8SurfaceBase (pDevice, std::move(pSurface), pTexture) { + } + + // A surface does not need to be attached to a texture + D3D8Surface( + D3D8Device* pDevice, + Com&& pSurface) + : D3D8Surface (pDevice, nullptr, std::move(pSurface)) { + } + + D3DRESOURCETYPE STDMETHODCALLTYPE GetType() { + return D3DRESOURCETYPE(GetD3D9()->GetType()); + } + + HRESULT STDMETHODCALLTYPE GetDesc(D3DSURFACE_DESC* pDesc) { + d3d9::D3DSURFACE_DESC desc; + HRESULT res = GetD3D9()->GetDesc(&desc); + ConvertSurfaceDesc8(&desc, pDesc); + return res; + } + + HRESULT STDMETHODCALLTYPE LockRect(D3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) { + return GetD3D9()->LockRect((d3d9::D3DLOCKED_RECT*)pLockedRect, pRect, Flags); + } + + HRESULT STDMETHODCALLTYPE UnlockRect() { + return GetD3D9()->UnlockRect(); + } + + HRESULT STDMETHODCALLTYPE GetDC(HDC* phDC) { + return GetD3D9()->GetDC(phDC); + } + + HRESULT STDMETHODCALLTYPE ReleaseDC(HDC hDC) { + return GetD3D9()->ReleaseDC(hDC); + } + + public: + + /** + * \brief Allocate or reuse an image of the same size + * as this texture for performing blit into system mem. + * + * TODO: Consider creating only one texture to + * encompass all surface levels of a texture. + */ + Com GetBlitImage() { + if (unlikely(m_blitImage == nullptr)) { + m_blitImage = CreateBlitImage(); + } + + return m_blitImage; + } + + + private: + Com CreateBlitImage(); + + Com m_blitImage = nullptr; + + }; +} \ No newline at end of file diff --git a/src/d3d8/d3d8_swapchain.h b/src/d3d8/d3d8_swapchain.h new file mode 100644 index 000000000..357072131 --- /dev/null +++ b/src/d3d8/d3d8_swapchain.h @@ -0,0 +1,42 @@ +#pragma once + +#include "d3d8_device_child.h" +#include "d3d8_surface.h" +#include "d3d8_d3d9_util.h" + +namespace dxvk { + + using D3D8SwapChainBase = D3D8DeviceChild; + class D3D8SwapChain final : public D3D8SwapChainBase { + + public: + + D3D8SwapChain( + D3D8Device* pDevice, + Com&& pSwapChain) + : D3D8SwapChainBase(pDevice, std::move(pSwapChain)) {} + + HRESULT STDMETHODCALLTYPE Present(const RECT *src, const RECT *dst, HWND hWnd, const RGNDATA *dirtyRegion) final { + return GetD3D9()->Present(src, dst, hWnd, dirtyRegion, 0); + } + + HRESULT STDMETHODCALLTYPE GetBackBuffer(UINT BackBuffer, D3DBACKBUFFER_TYPE Type, IDirect3DSurface8** ppBackBuffer) final { + // Same logic as in D3D8Device::GetBackBuffer + if (unlikely(m_backBuffer == nullptr)) { + Com pSurface9; + HRESULT res = GetD3D9()->GetBackBuffer(BackBuffer, (d3d9::D3DBACKBUFFER_TYPE)Type, &pSurface9); + + m_backBuffer = new D3D8Surface(GetParent(), std::move(pSurface9)); + *ppBackBuffer = m_backBuffer.ref(); + return res; + } + + *ppBackBuffer = m_backBuffer.ref(); + return D3D_OK; + } + private: + Com m_backBuffer = nullptr; + + }; + +} \ No newline at end of file diff --git a/src/d3d8/d3d8_texture.h b/src/d3d8/d3d8_texture.h new file mode 100644 index 000000000..6978adb84 --- /dev/null +++ b/src/d3d8/d3d8_texture.h @@ -0,0 +1,233 @@ +#pragma once + +#include "d3d8_resource.h" +#include "d3d8_surface.h" +#include "d3d8_volume.h" + +#include "d3d8_d3d9_util.h" + +#include +#include + +namespace dxvk { + + template + class D3D8BaseTexture : public D3D8Resource { + + public: + + constexpr static UINT CUBE_FACES = 6; + + using SubresourceType8 = typename SubresourceType::D3D8; + using SubresourceType9 = typename SubresourceType::D3D9; + + D3D8BaseTexture( + D3D8Device* pDevice, + Com&& pBaseTexture, + UINT SubresourceCount) + : D3D8Resource ( pDevice, std::move(pBaseTexture) ) { + m_subresources.resize(SubresourceCount, nullptr); + } + + ~D3D8BaseTexture() { + for (size_t i = 0; i < m_subresources.size(); i++) + if (m_subresources[i] != nullptr) + m_subresources[i] = nullptr; + } + + virtual IUnknown* GetInterface(REFIID riid) final override try { + return D3D8Resource::GetInterface(riid); + } catch (HRESULT err) { + if (riid == __uuidof(IDirect3DBaseTexture8)) + return this; + + throw err; + } + + void STDMETHODCALLTYPE PreLoad() final { + this->GetD3D9()->PreLoad(); + } + + DWORD STDMETHODCALLTYPE SetLOD(DWORD LODNew) final { + return this->GetD3D9()->SetLOD(LODNew); + } + + DWORD STDMETHODCALLTYPE GetLOD() final { + return this->GetD3D9()->GetLOD(); + } + + DWORD STDMETHODCALLTYPE GetLevelCount() final { + return this->GetD3D9()->GetLevelCount(); + } + + protected: + + HRESULT STDMETHODCALLTYPE GetSubresource(UINT Index, SubresourceType8** ppSubresource) { + InitReturnPtr(ppSubresource); + + if (unlikely(Index >= m_subresources.size())) + return D3DERR_INVALIDCALL; + + if (m_subresources[Index] == nullptr) { + try { + Com subresource = LookupSubresource(Index); + + // Cache the subresource + m_subresources[Index] = new SubresourceType(this->m_parent, this, std::move(subresource)); + } catch (HRESULT res) { + return res; + } + } + + *ppSubresource = m_subresources[Index].ref(); + return D3D_OK; + } + + private: + + Com LookupSubresource(UINT Index) { + Com ptr = nullptr; + HRESULT res = D3DERR_INVALIDCALL; + if constexpr (std::is_same_v) { + res = this->GetD3D9()->GetSurfaceLevel(Index, &ptr); + } else if constexpr (std::is_same_v) { + res = this->GetD3D9()->GetVolumeLevel(Index, &ptr); + } else if constexpr (std::is_same_v) { + res = this->GetD3D9()->GetCubeMapSurface(d3d9::D3DCUBEMAP_FACES(Index % CUBE_FACES), Index / CUBE_FACES, &ptr); + } + if (FAILED(res)) + throw res; + return ptr; + } + + std::vector> m_subresources; + + }; + + using D3D8Texture2DBase = D3D8BaseTexture; + class D3D8Texture2D final : public D3D8Texture2DBase { + + public: + + D3D8Texture2D( + D3D8Device* pDevice, + Com&& pTexture) + : D3D8Texture2DBase(pDevice, std::move(pTexture), pTexture->GetLevelCount()) { + } + + D3DRESOURCETYPE STDMETHODCALLTYPE GetType() final { return D3DRTYPE_TEXTURE; } + + HRESULT STDMETHODCALLTYPE GetLevelDesc(UINT Level, D3DSURFACE_DESC* pDesc) { + d3d9::D3DSURFACE_DESC surf; + HRESULT res = GetD3D9()->GetLevelDesc(Level, &surf); + ConvertSurfaceDesc8(&surf, pDesc); + return res; + } + + HRESULT STDMETHODCALLTYPE GetSurfaceLevel(UINT Level, IDirect3DSurface8** ppSurfaceLevel) { + return GetSubresource(Level, ppSurfaceLevel); + } + + HRESULT STDMETHODCALLTYPE LockRect(UINT Level, D3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) { + return GetD3D9()->LockRect(Level, reinterpret_cast(pLockedRect), pRect, Flags); + } + + HRESULT STDMETHODCALLTYPE UnlockRect(UINT Level) { + return GetD3D9()->UnlockRect(Level); + } + + HRESULT STDMETHODCALLTYPE AddDirtyRect(CONST RECT* pDirtyRect) { + return GetD3D9()->AddDirtyRect(pDirtyRect); + } + + }; + + using D3D8Texture3DBase = D3D8BaseTexture; + class D3D8Texture3D final : public D3D8Texture3DBase { + + public: + + D3D8Texture3D( + D3D8Device* pDevice, + Com&& pVolumeTexture) + : D3D8Texture3DBase(pDevice, std::move(pVolumeTexture), pVolumeTexture->GetLevelCount()) {} + + D3DRESOURCETYPE STDMETHODCALLTYPE GetType() final { return D3DRTYPE_VOLUMETEXTURE; } + + HRESULT STDMETHODCALLTYPE GetLevelDesc(UINT Level, D3DVOLUME_DESC *pDesc) { + d3d9::D3DVOLUME_DESC vol; + HRESULT res = GetD3D9()->GetLevelDesc(Level, &vol); + ConvertVolumeDesc8(&vol, pDesc); + return res; + } + + HRESULT STDMETHODCALLTYPE GetVolumeLevel(UINT Level, IDirect3DVolume8** ppVolumeLevel) { + return GetSubresource(Level, ppVolumeLevel); + } + + HRESULT STDMETHODCALLTYPE LockBox(UINT Level, D3DLOCKED_BOX* pLockedBox, CONST D3DBOX* pBox, DWORD Flags) { + return GetD3D9()->LockBox( + Level, + reinterpret_cast(pLockedBox), + reinterpret_cast(pBox), + Flags + ); + } + + HRESULT STDMETHODCALLTYPE UnlockBox(UINT Level) { + return GetD3D9()->UnlockBox(Level); + } + + HRESULT STDMETHODCALLTYPE AddDirtyBox(CONST D3DBOX* pDirtyBox) { + return GetD3D9()->AddDirtyBox(reinterpret_cast(pDirtyBox)); + } + + }; + + using D3D8TextureCubeBase = D3D8BaseTexture; + class D3D8TextureCube final : public D3D8TextureCubeBase { + + public: + + D3D8TextureCube( + D3D8Device* pDevice, + Com&& pTexture) + : D3D8TextureCubeBase(pDevice, std::move(pTexture), pTexture->GetLevelCount() * CUBE_FACES) { + } + + D3DRESOURCETYPE STDMETHODCALLTYPE GetType() final { return D3DRTYPE_CUBETEXTURE; } + + HRESULT STDMETHODCALLTYPE GetLevelDesc(UINT Level, D3DSURFACE_DESC* pDesc) { + d3d9::D3DSURFACE_DESC surf; + HRESULT res = GetD3D9()->GetLevelDesc(Level, &surf); + ConvertSurfaceDesc8(&surf, pDesc); + return res; + } + + HRESULT STDMETHODCALLTYPE GetCubeMapSurface(D3DCUBEMAP_FACES Face, UINT Level, IDirect3DSurface8** ppSurfaceLevel) { + return GetSubresource((Level * CUBE_FACES) + Face, ppSurfaceLevel); + } + + HRESULT STDMETHODCALLTYPE LockRect( + D3DCUBEMAP_FACES Face, + UINT Level, + D3DLOCKED_RECT* pLockedRect, + const RECT* pRect, + DWORD Flags) { + return GetD3D9()->LockRect( + d3d9::D3DCUBEMAP_FACES(Face), + Level, + reinterpret_cast(pLockedRect), + pRect, + Flags); + } + + HRESULT STDMETHODCALLTYPE UnlockRect(D3DCUBEMAP_FACES Face, UINT Level) { + return GetD3D9()->UnlockRect(d3d9::D3DCUBEMAP_FACES(Face), Level); + } + + HRESULT STDMETHODCALLTYPE AddDirtyRect(D3DCUBEMAP_FACES Face, const RECT* pDirtyRect) { + return GetD3D9()->AddDirtyRect(d3d9::D3DCUBEMAP_FACES(Face), pDirtyRect); + } + }; +} \ No newline at end of file diff --git a/src/d3d8/d3d8_volume.h b/src/d3d8/d3d8_volume.h new file mode 100644 index 000000000..7f3f29ea8 --- /dev/null +++ b/src/d3d8/d3d8_volume.h @@ -0,0 +1,40 @@ +#pragma once + +#include "d3d8_subresource.h" +#include "d3d8_d3d9_util.h" + +namespace dxvk { + + using D3D8VolumeBase = D3D8Subresource; + class D3D8Volume final : public D3D8VolumeBase { + + public: + + D3D8Volume( + D3D8Device* pDevice, + IDirect3DVolumeTexture8* pTexture, + Com&& pVolume) + : D3D8VolumeBase(pDevice, std::move(pVolume), pTexture) {} + + HRESULT STDMETHODCALLTYPE GetDesc(D3DVOLUME_DESC* pDesc) { + d3d9::D3DVOLUME_DESC desc; + HRESULT res = GetD3D9()->GetDesc(&desc); + ConvertVolumeDesc8(&desc, pDesc); + return res; + } + + HRESULT STDMETHODCALLTYPE LockBox(D3DLOCKED_BOX* pLockedBox, CONST D3DBOX* pBox, DWORD Flags) final { + return GetD3D9()->LockBox( + reinterpret_cast(pLockedBox), + reinterpret_cast(pBox), + Flags + ); + } + + HRESULT STDMETHODCALLTYPE UnlockBox() final { + return GetD3D9()->UnlockBox(); + } + + }; + +} \ No newline at end of file diff --git a/src/d3d8/d3d8_wrapped_object.h b/src/d3d8/d3d8_wrapped_object.h new file mode 100644 index 000000000..e79fecf71 --- /dev/null +++ b/src/d3d8/d3d8_wrapped_object.h @@ -0,0 +1,68 @@ +#pragma once + +#include "d3d8_include.h" + +namespace dxvk { + + template + class D3D8WrappedObject : public ComObjectClamp { + + public: + + using D3D9 = D3D9Type; + using D3D8 = D3D8Type; + + D3D8WrappedObject(Com&& object) + : m_d3d9(std::move(object)) { + } + + D3D9* GetD3D9() { + return m_d3d9.ptr(); + } + + // For cases where the object may be null. + static D3D9* GetD3D9Nullable(D3D8WrappedObject* self) { + if (unlikely(self == NULL)) { + return NULL; + } + return self->m_d3d9.ptr(); + } + + template + static D3D9* GetD3D9Nullable(Com& self) { + return GetD3D9Nullable(self.ptr()); + } + + virtual IUnknown* GetInterface(REFIID riid) { + if (riid == __uuidof(IUnknown)) + return this; + if (riid == __uuidof(D3D8)) + return this; + + throw E_NOINTERFACE; + } + + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject) final { + if (ppvObject == nullptr) + return E_POINTER; + + *ppvObject = nullptr; + + try { + *ppvObject = ref(this->GetInterface(riid)); + return S_OK; + } catch (HRESULT err) { + Logger::warn("D3D8WrappedObject::QueryInterface: Unknown interface query"); + Logger::warn(str::format(riid)); + return err; + } + } + + + private: + + Com m_d3d9; + + }; + +} \ No newline at end of file diff --git a/src/d3d8/meson.build b/src/d3d8/meson.build new file mode 100644 index 000000000..6a0e0bf51 --- /dev/null +++ b/src/d3d8/meson.build @@ -0,0 +1,42 @@ +d3d8_res = wrc_generator.process('version.rc') + +d3d8_src = [ + 'd3d8_main.cpp', + 'd3d8_interface.cpp', + 'd3d8_device.cpp', + 'd3d8_options.cpp', + 'd3d8_surface.cpp', + 'd3d8_state_block.cpp', + 'd3d8_shader.cpp' +] + +d3d8_ld_args = [] +d3d8_link_depends = [] + +if platform != 'windows' + lib_d3d9 = d3d9_dep + d3d8_ld_args += [ '-Wl,--version-script', join_paths(meson.current_source_dir(), 'd3d8.sym') ] + d3d8_link_depends += files('d3d8.sym') +endif + +d3d8_dll = shared_library(dxvk_name_prefix+'d3d8', d3d8_src, d3d8_res, + dependencies : [ lib_d3d9, util_dep, dxso_dep, dxvk_dep ], + include_directories : dxvk_include_path, + install : true, + vs_module_defs : 'd3d8'+def_spec_ext, + link_args : d3d8_ld_args, + link_depends : [ d3d8_link_depends ], + kwargs : dxvk_so_version, +) + +d3d8_dep = declare_dependency( + link_with : [ d3d8_dll ], + include_directories : [ dxvk_include_path ], +) + +if platform != 'windows' + pkg.generate(d3d8_dll, + filebase: dxvk_pkg_prefix + 'd3d8', + subdirs: 'dxvk', + ) +endif diff --git a/src/d3d8/version.rc b/src/d3d8/version.rc new file mode 100644 index 000000000..9e2b099fe --- /dev/null +++ b/src/d3d8/version.rc @@ -0,0 +1,31 @@ +#include + +// DLL version information. +VS_VERSION_INFO VERSIONINFO +FILEVERSION 10,0,17763,1 +PRODUCTVERSION 10,0,17763,1 +FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +FILEFLAGS 0 +FILEOS VOS_NT_WINDOWS32 +FILETYPE VFT_DLL +FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "080904b0" + BEGIN + VALUE "CompanyName", "DXVK" + VALUE "FileDescription", "Direct3D 8 Runtime" + VALUE "FileVersion", "10.0.17763.1 (WinBuild.160101.0800)" + VALUE "InternalName", "D3D8.dll" + VALUE "LegalCopyright", "zlib/libpng license" + VALUE "OriginalFilename", "D3D8.dll" + VALUE "ProductName", "DXVK" + VALUE "ProductVersion", "10.0.17763.1" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0809, 1200 + END +END diff --git a/src/meson.build b/src/meson.build index 779d9d46f..18d4e6fcd 100644 --- a/src/meson.build +++ b/src/meson.build @@ -31,7 +31,14 @@ if get_option('enable_d3d9') subdir('d3d9') endif +if get_option('enable_d3d8') + if not get_option('enable_d3d9') + error('D3D9 is required for D3D8.') + endif + subdir('d3d8') +endif + # Nothing selected -if not get_option('enable_d3d9') and not get_option('enable_dxgi') +if not get_option('enable_d3d8') and not get_option('enable_d3d9') and not get_option('enable_dxgi') warning('Nothing selected to be built.?') endif diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index 52b2c2ac4..34d88b682 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -698,10 +698,6 @@ namespace dxvk { { R"(\\SWTFU2\.exe$)", {{ { "d3d9.forceSamplerTypeSpecConstants", "True" }, }} }, - /* Scrapland (Remastered) */ - { R"(\\Scrap\.exe$)", {{ - { "d3d9.deferSurfaceCreation", "True" }, - }} }, /* Majesty 2 (Collection) * * Crashes on UMA without a memory limit, * * since the game(s) will allocate all * @@ -732,10 +728,6 @@ namespace dxvk { { R"(\\bionic_commando\.exe$)", {{ { "d3d9.maxFrameRate", "60" }, }} }, - /* Need For Speed 3 modern patch */ - { R"(\\nfs3\.exe$)", {{ - { "d3d9.enableDialogMode", "True" }, - }} }, /* Beyond Good And Evil * * UI breaks at high fps */ { R"(\\BGE\.exe$)", {{ @@ -996,6 +988,133 @@ namespace dxvk { { R"(\\P3R\.exe$)", {{ { "dxgi.syncInterval", "1" }, }} }, + + /**********************************************/ + /* D3D8 GAMES */ + /**********************************************/ + + /* Duke Nukem Forever (2001) */ + { R"(\\DukeForever\.exe$)", {{ + { "d3d9.maxFrameRate", "60" }, + }} }, + /* Indiana Jones and the Emperor's Tomb * + * Fixes intro window being stuck on screen */ + { R"(\\indy\.exe$)", {{ + { "d3d9.enableDialogMode", "True" }, + }} }, + /* Tom Clancy's Splinter Cell * + * Supports shadow buffers */ + { R"(\\splintercell\.exe$)", {{ + { "d3d8.useShadowBuffers", "True" }, + }} }, + /* Anito: Defend a Land Enraged */ + { R"(\\Anito\.exe$)", {{ + { "d3d9.memoryTrackTest", "True" }, + { "d3d9.maxAvailableMemory", "1024" }, + }} }, + /* Red Faction * + * Fixes crashing when starting a new game */ + { R"(\\RF\.exe$)", {{ + { "d3d9.allowDirectBufferMapping", "False" }, + }} }, + /* Commandos 3 * + * The game doesn't use NOOVERWRITE properly * + * and reads from actively modified buffers, * + * which causes graphical glitches at times */ + { R"(\\Commandos3\.exe$)", {{ + { "d3d9.allowDirectBufferMapping", "False" }, + }} }, + /* Motor City Online */ + { R"(\\MCity_d\.exe$)", {{ + { "d3d9.cachedDynamicBuffers", "True" }, + { "d3d8.batching", "True" }, + }} }, + /* Railroad Tycoon 3 */ + { R"(\\RT3\.exe$)", {{ + { "d3d9.maxFrameRate", "60" }, + }} }, + /* Pure Pinball 2.0 REDUX * + * This game reads from undeclared vs inputs * + * but somehow works on native. Let's just * + * change its declaration to make them work. */ + { R"(\\Pure Pinball 2\.0 REDUX\.exe$)", {{ + { "d3d8.forceVsDecl", "0:2,4:2,7:4,9:1,8:1" }, + }} }, + /* Need for Speed III: Hot Pursuit * + (with the "Modern Patch") */ + { R"(\\nfs3\.exe$)", {{ + { "d3d9.enableDialogMode", "True" }, + { "d3d9.cachedDynamicBuffers", "True" }, + { "d3d8.batching", "True" }, + }} }, + /* Need for Speed: High Stakes / Road * + Challenge (with the "Modern Patch") - * + Won't actually render anything in game * + without a memory limit in place */ + { R"(\\nfs4\.exe$)", {{ + { "d3d9.enableDialogMode", "True" }, + { "d3d9.cachedDynamicBuffers", "True" }, + { "d3d9.memoryTrackTest", "True" }, + { "d3d9.maxAvailableMemory", "256" }, + { "d3d8.batching", "True" }, + }} }, + /* Need for Speed: Hot Pursuit 2 */ + { R"(\\NFSHP2\.exe$)", {{ + { "d3d9.cachedDynamicBuffers", "True" }, + }} }, + /* Project I.G.I. 2: Covert Strike */ + { R"(\\igi2\.exe$)", {{ + { "d3d9.cachedDynamicBuffers", "True" }, + }} }, + /* Treasure Planet: Battle at Procyon * + * Declares v5 as color but shader uses v6 */ + { R"(\\TP_Win32\.exe$)", {{ + { "d3d8.forceVsDecl", "0:2,3:2,6:4,7:1" }, + }} }, + /* Scrapland (Remastered) */ + { R"(\\Scrap\.exe$)", {{ + { "d3d9.deferSurfaceCreation", "True" }, + }} }, + /* V-Rally 3 */ + { R"(\\VRally3(Demo)?\.exe$)", {{ + { "d3d9.maxFrameRate", "60" }, + }} }, + /* Soldiers: Heroes Of World War II * + * Fills up all available memory and hangs * + * while loading the main menu otherwise */ + { R"(\\Soldiers\.exe$)", {{ + { "d3d9.memoryTrackTest", "True" }, + { "d3d9.maxAvailableMemory", "512" }, + }} }, + /* Cossacks II: Napoleonic Wars & * + * Battle for Europe */ + { R"(\\Cossacks II.*\\engine\.exe$)", {{ + { "d3d9.maxFrameRate", "60" }, + }} }, + /* Alexander */ + { R"(\\Alexander\\Data\\engine\.exe$)", {{ + { "d3d9.maxFrameRate", "60" }, + }} }, + /* 3DMark2001 (SE) * + * Fixes a drastic performance drop in the * + * "Car Chase - High Detail" benchmark */ + { R"(\\3DMark2001(SE)?\.exe$)", {{ + { "d3d9.allowDirectBufferMapping", "False" }, + }} }, + /* Delta Force: Black Hawk Down */ + { R"(\\dfbhd\.exe$)", {{ + { "d3d9.cachedDynamicBuffers", "True" }, + }} }, + /* X2: The Threat */ + { R"(\\X2\.exe$)", {{ + { "d3d9.cachedDynamicBuffers", "True" }, + }} }, + /* The Lord of the Rings: * + * The Fellowship of the Ring */ + { R"(\\Fellowship\.exe$)", {{ + { "d3d9.maxFrameRate", "60" }, + { "d3d8.placeP8InScratch", "True" }, + }} }, }}; diff --git a/src/vulkan/vulkan_util.h b/src/vulkan/vulkan_util.h index 7fa064dd8..588c92241 100644 --- a/src/vulkan/vulkan_util.h +++ b/src/vulkan/vulkan_util.h @@ -4,6 +4,11 @@ #include "vulkan_loader.h" +#if defined(_MSC_VER) +// Unary minus on unsigned type +#pragma warning( disable : 4146 ) +#endif + namespace dxvk::vk { inline VkImageSubresourceRange makeSubresourceRange( From 38006e9cdaa86c3427be6e4d3f7307cff22ae26a Mon Sep 17 00:00:00 2001 From: Er2 Date: Sun, 7 Jul 2024 14:22:43 +0300 Subject: [PATCH 224/246] [dxvk] Add FreeBSD support --- package-native.sh | 16 ++++++++++++++-- src/util/thread.h | 4 ++++ src/util/util_env.cpp | 15 +++++++++++++++ src/util/util_win32_compat.h | 2 +- 4 files changed, 34 insertions(+), 3 deletions(-) diff --git a/package-native.sh b/package-native.sh index 56915e632..cbd4132e8 100755 --- a/package-native.sh +++ b/package-native.sh @@ -25,6 +25,8 @@ shift 2 opt_nopackage=0 opt_devbuild=0 opt_buildid=false +opt_64_only=0 +opt_32_only=0 CC=${CC:="gcc"} CXX=${CXX:="g++"} @@ -41,6 +43,12 @@ while [ $# -gt 0 ]; do "--build-id") opt_buildid=true ;; + "--64-only") + opt_64_only=1 + ;; + "--32-only") + opt_32_only=1 + ;; *) echo "Unrecognized option: $1" >&2 exit 1 @@ -81,8 +89,12 @@ function package { rm -R "dxvk-native-$DXVK_VERSION" } -build_arch 64 lib -build_arch 32 lib32 +if [ $opt_32_only -eq 0 ]; then + build_arch 64 lib +fi +if [ $opt_64_only -eq 0 ]; then + build_arch 32 lib32 +fi if [ $opt_nopackage -eq 0 ]; then package diff --git a/src/util/thread.h b/src/util/thread.h index 6e25f407d..2c20c02e4 100644 --- a/src/util/thread.h +++ b/src/util/thread.h @@ -316,7 +316,11 @@ namespace dxvk { switch (priority) { default: case ThreadPriority::Normal: policy = SCHED_OTHER; break; +#ifndef __linux__ + case ThreadPriority::Lowest: policy = SCHED_OTHER; break; +#else case ThreadPriority::Lowest: policy = SCHED_IDLE; break; +#endif } ::pthread_setschedparam(this->native_handle(), policy, ¶m); } diff --git a/src/util/util_env.cpp b/src/util/util_env.cpp index 1b0901f66..8441b25b2 100644 --- a/src/util/util_env.cpp +++ b/src/util/util_env.cpp @@ -6,6 +6,10 @@ #ifdef __linux__ #include #include +#elif defined(__FreeBSD__) +#include +#include +#include #endif #include "util_env.h" @@ -85,6 +89,17 @@ namespace dxvk::env { size_t count = readlink("/proc/self/exe", exePath.data(), exePath.size()); return std::string(exePath.begin(), exePath.begin() + count); +#elif defined(__FreeBSD__) + int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, getpid()}; + char exePath[PATH_MAX] = {}; + size_t size = PATH_MAX; + + if (sysctl(mib, 4, exePath, &size, NULL, 0) != 0) { + // throw error here? + return ""; + } + + return std::string(exePath); #endif } diff --git a/src/util/util_win32_compat.h b/src/util/util_win32_compat.h index 74ea6ecea..5b156c6e2 100644 --- a/src/util/util_win32_compat.h +++ b/src/util/util_win32_compat.h @@ -1,6 +1,6 @@ #pragma once -#if defined(__linux__) +#if defined(__unix__) #include #include From 33e7497ef46b126a493397fe23a4ee079506b084 Mon Sep 17 00:00:00 2001 From: Robin Kertels Date: Sun, 7 Jul 2024 11:31:42 +0200 Subject: [PATCH 225/246] [meta] Add Robin Kertels to LICENSE file --- LICENSE | 1 + 1 file changed, 1 insertion(+) diff --git a/LICENSE b/LICENSE index 68b6498ff..35d56c0ba 100644 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,6 @@ Copyright (c) 2017 Philip Rebohle Copyright (c) 2019 Joshua Ashton + Copyright (c) 2019 Robin Kertels Copyright (c) 2023 Jeffrey Ellison zlib/libpng license From 6ffd5acefa038cc3e1ccf80abd156b3955c0767b Mon Sep 17 00:00:00 2001 From: WinterSnowfall Date: Sun, 7 Jul 2024 15:12:40 +0300 Subject: [PATCH 226/246] [util] dxvk.conf cleanup --- dxvk.conf | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/dxvk.conf b/dxvk.conf index 87642979e..822607875 100644 --- a/dxvk.conf +++ b/dxvk.conf @@ -722,16 +722,3 @@ # d3d8.useShadowBuffers = False -# MANAGED Buffer Placement -# -# Remap some DEFAULT pool vertex and index buffers to the MANAGED pool to improve -# performance by avoiding waiting for games that frequently lock (large) buffers. -# -# This implicitly disables direct buffer mapping. Some applications may need this option -# disabled for certain (smaller) buffers to keep from overwriting in-use buffer regions. -# -# Supported values: -# - True/False -# - Any non-negative integer - sets the minimum size in bytes for a buffer to be MANAGED - -# d3d8.managedBufferPlacement = True From 5a0f05ae66b3d39206178be64b0e0689b0fa17c0 Mon Sep 17 00:00:00 2001 From: Robin Kertels Date: Sun, 7 Jul 2024 12:33:42 +0200 Subject: [PATCH 227/246] [util] Disable counting losables for Battlefield 2 & 2142 --- src/util/config/config.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index 34d88b682..a462bb0dc 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -576,15 +576,15 @@ namespace dxvk { { "d3d9.supportDFFormats", "False" }, { "d3d9.deviceLossOnFocusLoss", "True" }, }} }, - /* Battlefield 2 * - * Bad z-pass and ingame GUI loss on alt tab */ - { R"(\\BF2\.exe$)", {{ - { "d3d9.longMad", "True" }, - { "d3d9.deviceLossOnFocusLoss", "True" }, - }} }, - /* Battlefield 2142 - Same GUI issue as BF2 */ - { R"(\\BF2142\.exe$)", {{ + /* Battlefield 2 & Battlefield 2142 * + * Bad z-pass and ingame GUI loss on alt tab * + * Also hang when alt tabbing which seems * + * like a game bug that d3d9 drivers work * + * around. */ + { R"(\\(BF2|BF2142)\.exe$)", {{ + { "d3d9.longMad", "True" }, { "d3d9.deviceLossOnFocusLoss", "True" }, + { "d3d9.countLosableResources", "False"}, }} }, /* SpellForce 2 Series */ { R"(\\SpellForce2.*\.exe$)", {{ From a63a5753d4da4da4c3b0dc9acc69e6ca2de459a9 Mon Sep 17 00:00:00 2001 From: Blisto91 Date: Sun, 7 Jul 2024 13:01:20 +0200 Subject: [PATCH 228/246] [util] Enable longMad for Tomb Raider Legend Since the enablement of invariant position by default the game suffers from white flickers on characters when you use the Next Generation Content option --- src/util/config/config.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index a462bb0dc..2cdf2900c 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -593,10 +593,12 @@ namespace dxvk { /* Tomb Raider: Legend, Anniversary, Underworld * * Read from a buffer created with: * * D3DPOOL_DEFAULT, * - * D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY buffer */ + * D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY buffer. * + * Legend flickers with next gen content option. */ { R"(\\(trl|tra|tru)\.exe$)", {{ { "d3d9.cachedDynamicBuffers", "True" }, { "d3d9.maxFrameRate", "60" }, + { "d3d9.longMad", "True" }, }} }, /* Everquest */ { R"(\\eqgame\.exe$)", {{ From fe9c875fe910812a12b4542d9a6913b6fd191bc9 Mon Sep 17 00:00:00 2001 From: Blisto91 <47954800+Blisto91@users.noreply.github.com> Date: Fri, 28 Jun 2024 18:53:23 +0200 Subject: [PATCH 229/246] [util] Set cachedDynamicBuffers for The Sims 2 and friends Helps very low CPU bound performance especially in Castaway Stories. --- src/util/config/config.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index 2cdf2900c..42ae825d8 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -502,6 +502,7 @@ namespace dxvk { { "d3d9.supportX4R4G4B4", "False" }, { "d3d9.maxAvailableMemory", "2048" }, { "d3d9.memoryTrackTest", "True" }, + { "d3d9.cachedDynamicBuffers", "True" }, }} }, /* Dead Space uses the a NULL render target instead of a 1x1 one if DF24 is NOT supported From 4b0c9c611c097b3066869c11ed6a83f5fdb5ba2f Mon Sep 17 00:00:00 2001 From: niansa/tuxifan Date: Mon, 8 Jul 2024 11:37:02 +0100 Subject: [PATCH 230/246] [meta] Added `d3d8` to list of overrides to create to README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 753b375f7..c0ee2d6ee 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ The most recent development builds can be found [here](https://github.com/doitsu Release builds can be found [here](https://github.com/doitsujin/dxvk/releases). ## How to use -In order to install a DXVK package obtained from the [release](https://github.com/doitsujin/dxvk/releases) page into a given wine prefix, copy or symlink the DLLs into the following directories as follows, then open `winecfg` and manually add DLL overrides for `d3d11`, `d3d10core`, `dxgi`, and `d3d9`. +In order to install a DXVK package obtained from the [release](https://github.com/doitsujin/dxvk/releases) page into a given wine prefix, copy or symlink the DLLs into the following directories as follows, then open `winecfg` and manually add DLL overrides for `d3d11`, `d3d10core`, `dxgi`, `d3d9` and `d3d8`. In a default Wine prefix that would be as follows: ``` From 5f9ca0807113ed6ff49362e9b12d639793b317af Mon Sep 17 00:00:00 2001 From: Blisto91 <47954800+Blisto91@users.noreply.github.com> Date: Sun, 7 Jul 2024 22:18:09 +0200 Subject: [PATCH 231/246] [util] Enable deviceLossOnFocussLoss for Rise of Nations To prevent it from crashing on a alt-tab --- src/util/config/config.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index 42ae825d8..084669390 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -1118,6 +1118,10 @@ namespace dxvk { { "d3d9.maxFrameRate", "60" }, { "d3d8.placeP8InScratch", "True" }, }} }, + /* Rise of Nations + Expansion - alt-tab crash*/ + { R"(\\(nations|patriots)\.exe$)", {{ + { "d3d9.deviceLossOnFocusLoss", "True" }, + }} }, }}; From a7ae5999a962004fe477fd4f0abdf066e3c768d2 Mon Sep 17 00:00:00 2001 From: Robin Kertels Date: Mon, 8 Jul 2024 23:55:44 +0200 Subject: [PATCH 232/246] [d3d8] Slightly clean up CopyRects --- src/d3d8/d3d8_device.cpp | 122 ++++++++++++++++++--------------------- src/d3d9/d3d9_bridge.h | 4 +- 2 files changed, 56 insertions(+), 70 deletions(-) diff --git a/src/d3d8/d3d8_device.cpp b/src/d3d8/d3d8_device.cpp index 61be21e1d..a2b958942 100644 --- a/src/d3d8/d3d8_device.cpp +++ b/src/d3d8/d3d8_device.cpp @@ -562,11 +562,11 @@ namespace dxvk { /** * \brief D3D8 CopyRects implementation - * + * * \details * The following table shows the possible combinations of source * and destination surface pools, and how we handle each of them. - * + * * ┌────────────┬───────────────────────────┬───────────────────────┬───────────────────────┬──────────┐ * │ Src/Dst │ DEFAULT │ MANAGED │ SYSTEMMEM │ SCRATCH │ * ├────────────┼───────────────────────────┼───────────────────────┼───────────────────────┼──────────┤ @@ -587,7 +587,7 @@ namespace dxvk { } // TODO: No format conversion, no stretching, no clipping. - // All src/dest rectangles must fit within the dest surface. + // All src/dest rectangles must fit within the dest surface. Com src = static_cast(pSourceSurface); Com dst = static_cast(pDestinationSurface); @@ -618,8 +618,6 @@ namespace dxvk { pDestPointsArray = &point; } - HRESULT res = D3DERR_INVALIDCALL; - for (UINT i = 0; i < cRects; i++) { RECT srcRect, dstRect; @@ -639,10 +637,10 @@ namespace dxvk { dstRect.bottom = dstRect.top + (srcRect.bottom - srcRect.top); asymmetric = dstRect.left != srcRect.left || dstRect.top != srcRect.top || dstRect.right != srcRect.right || dstRect.bottom != srcRect.bottom; - + stretch = (dstRect.right-dstRect.left) != (srcRect.right-srcRect.left) || (dstRect.bottom-dstRect.top) != (srcRect.bottom-srcRect.top); - + offset = !stretch && asymmetric; } else { dstRect = srcRect; @@ -651,102 +649,103 @@ namespace dxvk { POINT dstPt = { dstRect.left, dstRect.top }; - res = D3DERR_INVALIDCALL; - auto unhandled = [&] { - Logger::debug(str::format("CopyRects: Hit unhandled case from src pool ", srcDesc.Pool, " to dst pool ", dstDesc.Pool)); + Logger::warn(str::format("CopyRects: Hit unhandled case from src pool ", srcDesc.Pool, " to dst pool ", dstDesc.Pool)); return D3DERR_INVALIDCALL; }; + auto logError = [&] (HRESULT res) { + if (FAILED(res)) { + // Only a debug message because some games mess up CopyRects every frame in a way + // that fails on native too but are perfectly fine with it. + Logger::debug(str::format("CopyRects: FAILED to copy from src pool ", srcDesc.Pool, " to dst pool ", dstDesc.Pool)); + } + return res; + }; + switch (dstDesc.Pool) { - + // Dest: DEFAULT - case D3DPOOL_DEFAULT: + case d3d9::D3DPOOL_DEFAULT: switch (srcDesc.Pool) { - case D3DPOOL_DEFAULT: { + case d3d9::D3DPOOL_DEFAULT: { // DEFAULT -> DEFAULT: use StretchRect - res = GetD3D9()->StretchRect( + return logError(GetD3D9()->StretchRect( src->GetD3D9(), &srcRect, dst->GetD3D9(), &dstRect, d3d9::D3DTEXF_NONE - ); - goto done; + )); } - case D3DPOOL_MANAGED: { + case d3d9::D3DPOOL_MANAGED: { // MANAGED -> DEFAULT: UpdateTextureFromBuffer - res = m_bridge->UpdateTextureFromBuffer( + return logError(m_bridge->UpdateTextureFromBuffer( src->GetD3D9(), dst->GetD3D9(), &srcRect, &dstPt - ); - goto done; + )); } - case D3DPOOL_SYSTEMMEM: { + case d3d9::D3DPOOL_SYSTEMMEM: { // SYSTEMMEM -> DEFAULT: use UpdateSurface - res = GetD3D9()->UpdateSurface( + return logError(GetD3D9()->UpdateSurface( src->GetD3D9(), &srcRect, dst->GetD3D9(), &dstPt - ); - goto done; + )); } - case D3DPOOL_SCRATCH: + case d3d9::D3DPOOL_SCRATCH: default: { // TODO: Unhandled case. return unhandled(); } } break; - + // Dest: MANAGED - case D3DPOOL_MANAGED: + case d3d9::D3DPOOL_MANAGED: switch (srcDesc.Pool) { - case D3DPOOL_DEFAULT: { + case d3d9::D3DPOOL_DEFAULT: { // TODO: Copy on GPU (handle MANAGED similarly to SYSTEMMEM for now) - + // Get temporary off-screen surface for stretching. Com pBlitImage = dst->GetBlitImage(); // Stretch the source RT to the temporary surface. - res = GetD3D9()->StretchRect( + HRESULT res = GetD3D9()->StretchRect( src->GetD3D9(), &srcRect, pBlitImage.ptr(), &dstRect, d3d9::D3DTEXF_NONE); + if (FAILED(res)) { - goto done; + return logError(res); } // Now sync the rendertarget data into main memory. - res = GetD3D9()->GetRenderTargetData(pBlitImage.ptr(), dst->GetD3D9()); - goto done; + return logError(GetD3D9()->GetRenderTargetData(pBlitImage.ptr(), dst->GetD3D9())); } - case D3DPOOL_MANAGED: - case D3DPOOL_SYSTEMMEM: { + case d3d9::D3DPOOL_MANAGED: + case d3d9::D3DPOOL_SYSTEMMEM: { // SYSTEMMEM -> MANAGED: LockRect / memcpy - + if (stretch) { - res = D3DERR_INVALIDCALL; - goto done; + return logError(D3DERR_INVALIDCALL); } - res = copyTextureBuffers(src.ptr(), dst.ptr(), srcDesc, dstDesc, srcRect, dstRect); - - goto done; + return logError(copyTextureBuffers(src.ptr(), dst.ptr(), srcDesc, dstDesc, srcRect, dstRect)); } - case D3DPOOL_SCRATCH: + case d3d9::D3DPOOL_SCRATCH: default: { // TODO: Unhandled case. return unhandled(); } } break; - + // DEST: SYSTEMMEM - case D3DPOOL_SYSTEMMEM: { + case d3d9::D3DPOOL_SYSTEMMEM: { // RT (DEFAULT) -> SYSTEMMEM: Use GetRenderTargetData as fast path if possible if ((srcDesc.Usage & D3DUSAGE_RENDERTARGET || m_renderTarget.ptr() == src.ptr())) { @@ -757,44 +756,40 @@ namespace dxvk { && srcDesc.Height == dstDesc.Height && srcDesc.Format == dstDesc.Format && !asymmetric) { - res = GetD3D9()->GetRenderTargetData(src->GetD3D9(), dst->GetD3D9()); - goto done; + return logError(GetD3D9()->GetRenderTargetData(src->GetD3D9(), dst->GetD3D9())); } } switch (srcDesc.Pool) { - case D3DPOOL_DEFAULT: { + case d3d9::D3DPOOL_DEFAULT: { // Get temporary off-screen surface for stretching. Com pBlitImage = dst->GetBlitImage(); // Stretch the source RT to the temporary surface. - res = GetD3D9()->StretchRect( + HRESULT res = GetD3D9()->StretchRect( src->GetD3D9(), &srcRect, pBlitImage.ptr(), &dstRect, d3d9::D3DTEXF_NONE); if (FAILED(res)) { - goto done; + return logError(res); } // Now sync the rendertarget data into main memory. - res = GetD3D9()->GetRenderTargetData(pBlitImage.ptr(), dst->GetD3D9()); - goto done; + return logError(GetD3D9()->GetRenderTargetData(pBlitImage.ptr(), dst->GetD3D9())); } // SYSMEM/MANAGED -> SYSMEM: LockRect / memcpy - case D3DPOOL_MANAGED: - case D3DPOOL_SYSTEMMEM: { + case d3d9::D3DPOOL_MANAGED: + case d3d9::D3DPOOL_SYSTEMMEM: { if (stretch) { - res = D3DERR_INVALIDCALL; - goto done; + return logError(D3DERR_INVALIDCALL); } - res = copyTextureBuffers(src.ptr(), dst.ptr(), srcDesc, dstDesc, srcRect, dstRect); - goto done; + return logError(copyTextureBuffers(src.ptr(), dst.ptr(), srcDesc, dstDesc, srcRect, dstRect)); } - case D3DPOOL_SCRATCH: + case d3d9::D3DPOOL_SCRATCH: default: { // TODO: Unhandled case. return unhandled(); @@ -803,22 +798,15 @@ namespace dxvk { } // DEST: SCRATCH - case D3DPOOL_SCRATCH: + case d3d9::D3DPOOL_SCRATCH: default: { // TODO: Unhandled case. return unhandled(); } } - - done: - if (FAILED(res)) { - Logger::debug(str::format("CopyRects: FAILED to copy from src pool ", srcDesc.Pool, " to dst pool ", dstDesc.Pool)); - return res; - } - } - return res; + return D3DERR_INVALIDCALL; } HRESULT STDMETHODCALLTYPE D3D8Device::UpdateTexture( diff --git a/src/d3d9/d3d9_bridge.h b/src/d3d9/d3d9_bridge.h index 7ed00a750..5887fa678 100644 --- a/src/d3d9/d3d9_bridge.h +++ b/src/d3d9/d3d9_bridge.h @@ -3,8 +3,6 @@ #include #include "../util/config/config.h" -#include "../vulkan/vulkan_loader.h" - /** * The D3D9 bridge allows D3D8 to access DXVK internals. * For Vulkan interop without needing DXVK internals, see d3d9_interop.h. @@ -111,7 +109,7 @@ namespace dxvk { void** ppvObject); const Config* GetConfig() const; - + protected: D3D9InterfaceEx* m_interface; }; From cc87870be12f0e885a9f33ba4e977653617f6a31 Mon Sep 17 00:00:00 2001 From: Robin Kertels Date: Mon, 8 Jul 2024 23:56:09 +0200 Subject: [PATCH 233/246] [d3d9] Relax stretch rect check in D3D8 mode --- src/d3d9/d3d9_device.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/d3d9/d3d9_device.cpp b/src/d3d9/d3d9_device.cpp index dbb6fbba1..31afcd45f 100644 --- a/src/d3d9/d3d9_device.cpp +++ b/src/d3d9/d3d9_device.cpp @@ -1264,7 +1264,7 @@ namespace dxvk { // - both destination and source are depth stencil surfaces // - both destination and source are offscreen plain surfaces. // The only way to get a surface with resource type D3DRTYPE_SURFACE without USAGE_RT or USAGE_DS is CreateOffscreenPlainSurface. - if (unlikely(!dstHasRTUsage && (!dstIsSurface || !srcIsSurface || srcHasRTUsage))) + if (unlikely((!dstHasRTUsage && (!dstIsSurface || !srcIsSurface || srcHasRTUsage)) && !m_isD3D8Compatible)) return D3DERR_INVALIDCALL; } From d456d0b43748baf4f98477a4e8598841b700f863 Mon Sep 17 00:00:00 2001 From: Robin Kertels Date: Tue, 9 Jul 2024 14:32:44 +0200 Subject: [PATCH 234/246] [d3d9] StretchRect: Allow using an offscreen surface as dst when stretching --- src/d3d9/d3d9_device.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/d3d9/d3d9_device.cpp b/src/d3d9/d3d9_device.cpp index 31afcd45f..48dc9ade9 100644 --- a/src/d3d9/d3d9_device.cpp +++ b/src/d3d9/d3d9_device.cpp @@ -1245,6 +1245,7 @@ namespace dxvk { bool stretch = srcCopyExtent != dstCopyExtent; bool dstHasRTUsage = (dstTextureInfo->Desc()->Usage & (D3DUSAGE_RENDERTARGET | D3DUSAGE_DEPTHSTENCIL)) != 0; + bool dstIsSurface = dstTextureInfo->GetType() == D3DRTYPE_SURFACE; if (stretch) { if (unlikely(pSourceSurface == pDestSurface)) return D3DERR_INVALIDCALL; @@ -1252,12 +1253,13 @@ namespace dxvk { if (unlikely(dstIsDepth)) return D3DERR_INVALIDCALL; - // Stretching is only allowed if the destination is either a render target surface or a render target texture - if (unlikely(!dstHasRTUsage)) + // The docs say that stretching is only allowed if the destination is either a render target surface or a render target texture. + // However in practice, using an offscreen plain surface in D3DPOOL_DEFAULT as the destination works fine. + // Using a texture without USAGE_RENDERTARGET as destination however does not. + if (unlikely(!dstIsSurface && !dstHasRTUsage)) return D3DERR_INVALIDCALL; } else { bool srcIsSurface = srcTextureInfo->GetType() == D3DRTYPE_SURFACE; - bool dstIsSurface = dstTextureInfo->GetType() == D3DRTYPE_SURFACE; bool srcHasRTUsage = (srcTextureInfo->Desc()->Usage & (D3DUSAGE_RENDERTARGET | D3DUSAGE_DEPTHSTENCIL)) != 0; // Non-stretching copies are only allowed if: // - the destination is either a render target surface or a render target texture From ff137dac9f24410d2f50a2be9fbae77b89625cac Mon Sep 17 00:00:00 2001 From: WinterSnowfall Date: Sun, 7 Jul 2024 15:36:19 +0300 Subject: [PATCH 235/246] [d3d8] Implement monotonic state block tokens --- src/d3d8/d3d8_device.cpp | 55 +++++++++++++++++++++++++++------------- src/d3d8/d3d8_device.h | 10 +++++--- 2 files changed, 44 insertions(+), 21 deletions(-) diff --git a/src/d3d8/d3d8_device.cpp b/src/d3d8/d3d8_device.cpp index a2b958942..b1de9a8e9 100644 --- a/src/d3d8/d3d8_device.cpp +++ b/src/d3d8/d3d8_device.cpp @@ -63,11 +63,6 @@ namespace dxvk { D3D8Device::~D3D8Device() { if (m_batcher) delete m_batcher; - - // Delete any remaining state blocks. - for (D3D8StateBlock* block : m_stateBlocks) { - delete block; - } } HRESULT STDMETHODCALLTYPE D3D8Device::GetInfo(DWORD DevInfoID, void* pDevInfoStruct, DWORD DevInfoStructSize) { @@ -981,21 +976,37 @@ namespace dxvk { Com pStateBlock9; HRESULT res = GetD3D9()->CreateStateBlock(d3d9::D3DSTATEBLOCKTYPE(Type), &pStateBlock9); - D3D8StateBlock* pStateBlock = new D3D8StateBlock(this, Type, pStateBlock9.ref()); - m_stateBlocks.insert(pStateBlock); - - *pToken = DWORD(reinterpret_cast(pStateBlock)); + m_stateBlocks.emplace(std::piecewise_construct, + std::forward_as_tuple(m_token), + std::forward_as_tuple(this, Type, pStateBlock9.ref())); + *pToken = m_token; + m_token++; return res; } HRESULT STDMETHODCALLTYPE D3D8Device::CaptureStateBlock(DWORD Token) { - return reinterpret_cast(Token)->Capture(); + auto stateBlockIter = m_stateBlocks.find(Token); + + if (unlikely(stateBlockIter == m_stateBlocks.end())) { + Logger::err("Invalid token passed to CaptureStateBlock"); + return D3DERR_INVALIDCALL; + } + + return stateBlockIter->second.Capture(); } HRESULT STDMETHODCALLTYPE D3D8Device::ApplyStateBlock(DWORD Token) { StateChange(); - return reinterpret_cast(Token)->Apply(); + + auto stateBlockIter = m_stateBlocks.find(Token); + + if (unlikely(stateBlockIter == m_stateBlocks.end())) { + Logger::err("Invalid token passed to ApplyStateBlock"); + return D3DERR_INVALIDCALL; + } + + return stateBlockIter->second.Apply(); } HRESULT STDMETHODCALLTYPE D3D8Device::DeleteStateBlock(DWORD Token) { @@ -1003,9 +1014,14 @@ namespace dxvk { if (unlikely(ShouldRecord())) return D3DERR_INVALIDCALL; - D3D8StateBlock* block = reinterpret_cast(Token); - m_stateBlocks.erase(block); - delete block; + auto stateBlockIter = m_stateBlocks.find(Token); + + if (unlikely(stateBlockIter == m_stateBlocks.end())) { + Logger::err("Invalid token passed to DeleteStateBlock"); + return D3DERR_INVALIDCALL; + } + + m_stateBlocks.erase(stateBlockIter); return D3D_OK; } @@ -1014,8 +1030,12 @@ namespace dxvk { if (unlikely(m_recorder != nullptr)) return D3DERR_INVALIDCALL; - m_recorder = new D3D8StateBlock(this); - m_stateBlocks.insert(m_recorder); + auto stateBlockIterPair = m_stateBlocks.emplace(std::piecewise_construct, + std::forward_as_tuple(m_token), + std::forward_as_tuple(this)); + m_recorder = &stateBlockIterPair.first->second; + m_recorderToken = m_token; + m_token++; return GetD3D9()->BeginStateBlock(); } @@ -1029,9 +1049,10 @@ namespace dxvk { m_recorder->SetD3D9(std::move(pStateBlock)); - *pToken = DWORD(reinterpret_cast(m_recorder)); + *pToken = m_recorderToken; m_recorder = nullptr; + m_recorderToken = -1; return res; } diff --git a/src/d3d8/d3d8_device.h b/src/d3d8/d3d8_device.h index 141723638..2e11f5f7d 100644 --- a/src/d3d8/d3d8_device.h +++ b/src/d3d8/d3d8_device.h @@ -14,7 +14,7 @@ #include #include #include -#include +#include namespace dxvk { @@ -416,9 +416,11 @@ namespace dxvk { D3DPRESENT_PARAMETERS m_presentParams; - D3D8StateBlock* m_recorder = nullptr; - std::unordered_set m_stateBlocks; - D3D8Batcher* m_batcher = nullptr; + D3D8StateBlock* m_recorder = nullptr; + DWORD m_recorderToken = -1; + DWORD m_token = 0; + std::unordered_map m_stateBlocks; + D3D8Batcher* m_batcher = nullptr; struct D3D8VBO { Com buffer = nullptr; From d731608d5ec3e1db8c46efd66cd6250dfacebd87 Mon Sep 17 00:00:00 2001 From: WinterSnowfall Date: Wed, 10 Jul 2024 00:51:46 +0300 Subject: [PATCH 236/246] [d3d8] Mimic native token allocations --- src/d3d8/d3d8_device.cpp | 12 +++++++++--- src/d3d8/d3d8_device.h | 2 +- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/d3d8/d3d8_device.cpp b/src/d3d8/d3d8_device.cpp index b1de9a8e9..86ca46bbf 100644 --- a/src/d3d8/d3d8_device.cpp +++ b/src/d3d8/d3d8_device.cpp @@ -976,11 +976,11 @@ namespace dxvk { Com pStateBlock9; HRESULT res = GetD3D9()->CreateStateBlock(d3d9::D3DSTATEBLOCKTYPE(Type), &pStateBlock9); + m_token++; m_stateBlocks.emplace(std::piecewise_construct, std::forward_as_tuple(m_token), std::forward_as_tuple(this, Type, pStateBlock9.ref())); *pToken = m_token; - m_token++; return res; } @@ -1023,6 +1023,12 @@ namespace dxvk { m_stateBlocks.erase(stateBlockIter); + // native apparently does drop the token counter in + // situations where the token being removed is the + // last allocated token, which allows some reuse + if (m_token == Token) + m_token--; + return D3D_OK; } @@ -1030,12 +1036,12 @@ namespace dxvk { if (unlikely(m_recorder != nullptr)) return D3DERR_INVALIDCALL; + m_token++; auto stateBlockIterPair = m_stateBlocks.emplace(std::piecewise_construct, std::forward_as_tuple(m_token), std::forward_as_tuple(this)); m_recorder = &stateBlockIterPair.first->second; m_recorderToken = m_token; - m_token++; return GetD3D9()->BeginStateBlock(); } @@ -1052,7 +1058,7 @@ namespace dxvk { *pToken = m_recorderToken; m_recorder = nullptr; - m_recorderToken = -1; + m_recorderToken = 0; return res; } diff --git a/src/d3d8/d3d8_device.h b/src/d3d8/d3d8_device.h index 2e11f5f7d..eb46b8dfc 100644 --- a/src/d3d8/d3d8_device.h +++ b/src/d3d8/d3d8_device.h @@ -417,7 +417,7 @@ namespace dxvk { D3DPRESENT_PARAMETERS m_presentParams; D3D8StateBlock* m_recorder = nullptr; - DWORD m_recorderToken = -1; + DWORD m_recorderToken = 0; DWORD m_token = 0; std::unordered_map m_stateBlocks; D3D8Batcher* m_batcher = nullptr; From 008afc1c5f287e82a553108790da6e10b1159200 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Wed, 10 Jul 2024 11:55:14 +0200 Subject: [PATCH 237/246] [meta] Release 2.4 --- RELEASE | 2 +- meson.build | 2 +- src/dxvk/dxvk_instance.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/RELEASE b/RELEASE index 2bf1c1ccf..6b4950e3d 100644 --- a/RELEASE +++ b/RELEASE @@ -1 +1 @@ -2.3.1 +2.4 diff --git a/meson.build b/meson.build index c6294126d..0548a48d4 100644 --- a/meson.build +++ b/meson.build @@ -1,4 +1,4 @@ -project('dxvk', ['c', 'cpp'], version : 'v2.3.1', meson_version : '>= 0.58', default_options : [ 'cpp_std=c++17', 'warning_level=2' ]) +project('dxvk', ['c', 'cpp'], version : 'v2.4.0', meson_version : '>= 0.58', default_options : [ 'cpp_std=c++17', 'warning_level=2' ]) pkg = import('pkgconfig') cpu_family = target_machine.cpu_family() diff --git a/src/dxvk/dxvk_instance.cpp b/src/dxvk/dxvk_instance.cpp index e8e7783e5..f47f77786 100644 --- a/src/dxvk/dxvk_instance.cpp +++ b/src/dxvk/dxvk_instance.cpp @@ -182,7 +182,7 @@ namespace dxvk { appInfo.pApplicationName = appName.c_str(); appInfo.applicationVersion = flags.raw(); appInfo.pEngineName = "DXVK"; - appInfo.engineVersion = VK_MAKE_API_VERSION(0, 2, 3, 2); + appInfo.engineVersion = VK_MAKE_API_VERSION(0, 2, 4, 0); appInfo.apiVersion = VK_MAKE_API_VERSION(0, 1, 3, 0); VkInstanceCreateInfo info = { VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO }; From ba47af53da6b0d704a487192f3a0b1c10af4c950 Mon Sep 17 00:00:00 2001 From: Blisto91 <47954800+Blisto91@users.noreply.github.com> Date: Thu, 11 Jul 2024 14:42:06 +0200 Subject: [PATCH 238/246] [meta] Rearrange readme Rearrange the readme so the entries are consistently listed in a given section it makes sense. They are ordered from project info, usage, building and native. --- README.md | 150 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 78 insertions(+), 72 deletions(-) diff --git a/README.md b/README.md index c0ee2d6ee..be79144bc 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ The most recent development builds can be found [here](https://github.com/doitsu Release builds can be found [here](https://github.com/doitsujin/dxvk/releases). ## How to use -In order to install a DXVK package obtained from the [release](https://github.com/doitsujin/dxvk/releases) page into a given wine prefix, copy or symlink the DLLs into the following directories as follows, then open `winecfg` and manually add DLL overrides for `d3d11`, `d3d10core`, `dxgi`, `d3d9` and `d3d8`. +In order to install a DXVK package obtained from the [release](https://github.com/doitsujin/dxvk/releases) page into a given wine prefix, copy or symlink the DLLs into the following directories as follows, then open `winecfg` and manually add DLL overrides for `d3d8`, `d3d9`, `d3d10core`, `d3d11` and `dxgi`. In a default Wine prefix that would be as follows: ``` @@ -32,9 +32,84 @@ In order to remove DXVK from a prefix, remove the DLLs and DLL overrides, and ru Tools such as Steam Play, Lutris, Bottles, Heroic Launcher, etc will automatically handle setup of dxvk on their own when enabled. +#### DLL dependencies +Listed below are the DLL requirements for using DXVK with any single API. + +- d3d8: `d3d8.dll` and `d3d9.dll` +- d3d9: `d3d9.dll` +- d3d10: `d3d10core.dll`, `d3d11.dll` and `dxgi.dll` +- d3d11: `d3d11.dll` and `dxgi.dll` + ### Notes on Vulkan drivers Before reporting an issue, please check the [Wiki](https://github.com/doitsujin/dxvk/wiki/Driver-support) page on the current driver status and make sure you run a recent enough driver version for your hardware. +### Online multi-player games +Manipulation of Direct3D libraries in multi-player games may be considered cheating and can get your account **banned**. This may also apply to single-player games with an embedded or dedicated multiplayer portion. **Use at your own risk.** + +### HUD +The `DXVK_HUD` environment variable controls a HUD which can display the framerate and some stat counters. It accepts a comma-separated list of the following options: +- `devinfo`: Displays the name of the GPU and the driver version. +- `fps`: Shows the current frame rate. +- `frametimes`: Shows a frame time graph. +- `submissions`: Shows the number of command buffers submitted per frame. +- `drawcalls`: Shows the number of draw calls and render passes per frame. +- `pipelines`: Shows the total number of graphics and compute pipelines. +- `descriptors`: Shows the number of descriptor pools and descriptor sets. +- `memory`: Shows the amount of device memory allocated and used. +- `gpuload`: Shows estimated GPU load. May be inaccurate. +- `version`: Shows DXVK version. +- `api`: Shows the D3D feature level used by the application. +- `cs`: Shows worker thread statistics. +- `compiler`: Shows shader compiler activity +- `samplers`: Shows the current number of sampler pairs used *[D3D9 Only]* +- `scale=x`: Scales the HUD by a factor of `x` (e.g. `1.5`) +- `opacity=y`: Adjusts the HUD opacity by a factor of `y` (e.g. `0.5`, `1.0` being fully opaque). + +Additionally, `DXVK_HUD=1` has the same effect as `DXVK_HUD=devinfo,fps`, and `DXVK_HUD=full` enables all available HUD elements. + +### Logs +When used with Wine, DXVK will print log messages to `stderr`. Additionally, standalone log files can optionally be generated by setting the `DXVK_LOG_PATH` variable, where log files in the given directory will be called `app_d3d11.log`, `app_dxgi.log` etc., where `app` is the name of the game executable. + +On Windows, log files will be created in the game's working directory by default, which is usually next to the game executable. + +### Frame rate limit +The `DXVK_FRAME_RATE` environment variable can be used to limit the frame rate. A value of `0` uncaps the frame rate, while any positive value will limit rendering to the given number of frames per second. Alternatively, the configuration file can be used. + +### Device filter +Some applications do not provide a method to select a different GPU. In that case, DXVK can be forced to use a given device: +- `DXVK_FILTER_DEVICE_NAME="Device Name"` Selects devices with a matching Vulkan device name, which can be retrieved with tools such as `vulkaninfo`. Matches on substrings, so "VEGA" or "AMD RADV VEGA10" is supported if the full device name is "AMD RADV VEGA10 (LLVM 9.0.0)", for example. If the substring matches more than one device, the first device matched will be used. + +**Note:** If the device filter is configured incorrectly, it may filter out all devices and applications will be unable to create a D3D device. + +### Debugging +The following environment variables can be used for **debugging** purposes. +- `VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation` Enables Vulkan debug layers. Highly recommended for troubleshooting rendering issues and driver crashes. Requires the Vulkan SDK to be installed on the host system. +- `DXVK_LOG_LEVEL=none|error|warn|info|debug` Controls message logging. +- `DXVK_LOG_PATH=/some/directory` Changes path where log files are stored. Set to `none` to disable log file creation entirely, without disabling logging. +- `DXVK_DEBUG=markers|validation` Enables use of the `VK_EXT_debug_utils` extension for translating performance event markers, or to enable Vulkan validation, respecticely. +- `DXVK_CONFIG_FILE=/xxx/dxvk.conf` Sets path to the configuration file. +- `DXVK_CONFIG="dxgi.hideAmdGpu = True; dxgi.syncInterval = 0"` Can be used to set config variables through the environment instead of a configuration file using the same syntax. `;` is used as a seperator. + +### Graphics Pipeline Library +On drivers which support `VK_EXT_graphics_pipeline_library` Vulkan shaders will be compiled at the time the game loads its D3D shaders, rather than at draw time. This reduces or eliminates shader compile stutter in many games when compared to the previous system. + +In games that load their shaders during loading screens or in the menu, this can lead to prolonged periods of very high CPU utilization, especially on weaker CPUs. For affected games it is recommended to wait for shader compilation to finish before starting the game to avoid stutter and low performance. Shader compiler activity can be monitored with `DXVK_HUD=compiler`. + +This feature largely replaces the state cache. + +**Note:** Games which only load their D3D shaders at draw time (e.g. most Unreal Engine games) will still exhibit some stutter, although it should still be less severe than without this feature. + +### State cache +DXVK caches pipeline state by default, so that shaders can be recompiled ahead of time on subsequent runs of an application, even if the driver's own shader cache got invalidated in the meantime. This cache is enabled by default, and generally reduces stuttering. + +The following environment variables can be used to control the cache: +- `DXVK_STATE_CACHE`: Controls the state cache. The following values are supported: + - `disable`: Disables the cache entirely. + - `reset`: Clears the cache file. +- `DXVK_STATE_CACHE_PATH=/some/directory` Specifies a directory where to put the cache files. Defaults to the current working directory of the application. + +This feature is mostly only relevant on systems without support for `VK_EXT_graphics_pipeline_library` + ## Build instructions In order to pull in all submodules that are needed for building, clone the repository using the following command: @@ -42,8 +117,6 @@ In order to pull in all submodules that are needed for building, clone the repos git clone --recursive https://github.com/doitsujin/dxvk.git ``` - - ### Requirements: - [wine 7.1](https://www.winehq.org/) or newer - [Meson](https://mesonbuild.com/) build system (at least version 0.49) @@ -76,76 +149,9 @@ cd build.w64 ninja install ``` -The D3D9, D3D10, D3D11 and DXGI DLLs will be located in `/your/dxvk/directory/bin`. Setup has to be done manually in this case. +The D3D8, D3D9, D3D10, D3D11 and DXGI DLLs will be located in `/your/dxvk/directory/bin`. -### Online multi-player games -Manipulation of Direct3D libraries in multi-player games may be considered cheating and can get your account **banned**. This may also apply to single-player games with an embedded or dedicated multiplayer portion. **Use at your own risk.** - -### Logs -When used with Wine, DXVK will print log messages to `stderr`. Additionally, standalone log files can optionally be generated by setting the `DXVK_LOG_PATH` variable, where log files in the given directory will be called `app_d3d11.log`, `app_dxgi.log` etc., where `app` is the name of the game executable. - -On Windows, log files will be created in the game's working directory by default, which is usually next to the game executable. - -### HUD -The `DXVK_HUD` environment variable controls a HUD which can display the framerate and some stat counters. It accepts a comma-separated list of the following options: -- `devinfo`: Displays the name of the GPU and the driver version. -- `fps`: Shows the current frame rate. -- `frametimes`: Shows a frame time graph. -- `submissions`: Shows the number of command buffers submitted per frame. -- `drawcalls`: Shows the number of draw calls and render passes per frame. -- `pipelines`: Shows the total number of graphics and compute pipelines. -- `descriptors`: Shows the number of descriptor pools and descriptor sets. -- `memory`: Shows the amount of device memory allocated and used. -- `gpuload`: Shows estimated GPU load. May be inaccurate. -- `version`: Shows DXVK version. -- `api`: Shows the D3D feature level used by the application. -- `cs`: Shows worker thread statistics. -- `compiler`: Shows shader compiler activity -- `samplers`: Shows the current number of sampler pairs used *[D3D9 Only]* -- `scale=x`: Scales the HUD by a factor of `x` (e.g. `1.5`) -- `opacity=y`: Adjusts the HUD opacity by a factor of `y` (e.g. `0.5`, `1.0` being fully opaque). - -Additionally, `DXVK_HUD=1` has the same effect as `DXVK_HUD=devinfo,fps`, and `DXVK_HUD=full` enables all available HUD elements. - -### Frame rate limit -The `DXVK_FRAME_RATE` environment variable can be used to limit the frame rate. A value of `0` uncaps the frame rate, while any positive value will limit rendering to the given number of frames per second. Alternatively, the configuration file can be used. - -### Device filter -Some applications do not provide a method to select a different GPU. In that case, DXVK can be forced to use a given device: -- `DXVK_FILTER_DEVICE_NAME="Device Name"` Selects devices with a matching Vulkan device name, which can be retrieved with tools such as `vulkaninfo`. Matches on substrings, so "VEGA" or "AMD RADV VEGA10" is supported if the full device name is "AMD RADV VEGA10 (LLVM 9.0.0)", for example. If the substring matches more than one device, the first device matched will be used. - -**Note:** If the device filter is configured incorrectly, it may filter out all devices and applications will be unable to create a D3D device. - -### Graphics Pipeline Library -On drivers which support `VK_EXT_graphics_pipeline_library` Vulkan shaders will be compiled at the time the game loads its D3D shaders, rather than at draw time. This reduces or eliminates shader compile stutter in many games when compared to the previous system. - -In games that load their shaders during loading screens or in the menu, this can lead to prolonged periods of very high CPU utilization, especially on weaker CPUs. For affected games it is recommended to wait for shader compilation to finish before starting the game to avoid stutter and low performance. Shader compiler activity can be monitored with `DXVK_HUD=compiler`. - -This feature largely replaces the state cache. - -**Note:** Games which only load their D3D shaders at draw time (e.g. most Unreal Engine games) will still exhibit some stutter, although it should still be less severe than without this feature. - -### State cache -DXVK caches pipeline state by default, so that shaders can be recompiled ahead of time on subsequent runs of an application, even if the driver's own shader cache got invalidated in the meantime. This cache is enabled by default, and generally reduces stuttering. - -The following environment variables can be used to control the cache: -- `DXVK_STATE_CACHE`: Controls the state cache. The following values are supported: - - `disable`: Disables the cache entirely. - - `reset`: Clears the cache file. -- `DXVK_STATE_CACHE_PATH=/some/directory` Specifies a directory where to put the cache files. Defaults to the current working directory of the application. - -This feature is mostly only relevant on systems without support for `VK_EXT_graphics_pipeline_library` - -### Debugging -The following environment variables can be used for **debugging** purposes. -- `VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation` Enables Vulkan debug layers. Highly recommended for troubleshooting rendering issues and driver crashes. Requires the Vulkan SDK to be installed on the host system. -- `DXVK_LOG_LEVEL=none|error|warn|info|debug` Controls message logging. -- `DXVK_LOG_PATH=/some/directory` Changes path where log files are stored. Set to `none` to disable log file creation entirely, without disabling logging. -- `DXVK_DEBUG=markers|validation` Enables use of the `VK_EXT_debug_utils` extension for translating performance event markers, or to enable Vulkan validation, respecticely. -- `DXVK_CONFIG_FILE=/xxx/dxvk.conf` Sets path to the configuration file. -- `DXVK_CONFIG="dxgi.hideAmdGpu = True; dxgi.syncInterval = 0"` Can be used to set config variables through the environment instead of a configuration file using the same syntax. `;` is used as a seperator. - -## Troubleshooting +### Build troubleshooting DXVK requires threading support from your mingw-w64 build environment. If you are missing this, you may see "error: ‘std::cv_status’ has not been declared" or similar threading related errors. From a8dbbcfa31270c9523d4b8bbe2670d1703e7cbd0 Mon Sep 17 00:00:00 2001 From: WinterSnowfall Date: Thu, 11 Jul 2024 14:40:41 +0300 Subject: [PATCH 239/246] [util] Reorder config options by API --- src/util/config/config.cpp | 76 ++++++++++++++++++++------------------ 1 file changed, 40 insertions(+), 36 deletions(-) diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index 084669390..327c9af39 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -17,6 +17,44 @@ namespace dxvk { const static ProfileList g_profiles = {{ + /**********************************************/ + /* D3D12 GAMES (vkd3d-proton with dxvk dxgi) */ + /**********************************************/ + + /* Diablo 4 - Will complain about missing * + * GPU unless dxgi Id match actual GPU Id */ + { R"(\\Diablo IV\.exe$)", {{ + { "dxgi.hideNvidiaGpu", "False" }, + }} }, + /* WILD HEARTS™️ * + * D3D12 title using D3D11 device for * + * media texture creation, whereby a large * + * chunk size only slows down media * + * initialization */ + { R"(\\WILD HEARTS(_Trial)?\.exe$)", {{ + { "dxvk.maxChunkSize", "4" }, + }} }, + /* Ratchet & Clank: Rift Apart - does not allow + * enabling ray tracing if it sees an AMD GPU. */ + { R"(\\RiftApart\.exe$)", {{ + { "dxgi.hideNvidiaGpu", "False" }, + }} }, + /* Metro Exodus Enhanced Edition picks GPU adapters + * by available VRAM, which causes issues on some + * systems with integrated graphics. */ + { R"(\\Metro Exodus Enhanced Edition\\MetroExodus\.exe$)", {{ + { "dxvk.hideIntegratedGraphics", "True" }, + }} }, + /* Persona 3 Reload - disables vsync by default and + * runs into severe frame latency issues on Deck. */ + { R"(\\P3R\.exe$)", {{ + { "dxgi.syncInterval", "1" }, + }} }, + + /**********************************************/ + /* D3D11 GAMES */ + /**********************************************/ + /* Assassin's Creed Syndicate: amdags issues */ { R"(\\ACS\.exe$)", {{ { "dxgi.customVendorId", "10de" }, @@ -949,7 +987,8 @@ namespace dxvk { }} }, /* Battle for Middle-earth 2 and expansion * * Slowdowns in certain scenarios */ - { R"(\\(The Battle for Middle-earth (\(tm\))? II( Demo)?|The Lord of the Rings, The Rise of the Witch-king)\\game\.dat$)", {{ + { R"(\\(The Battle for Middle-earth( \(tm\))? II( Demo)?)" + R"(|The Lord of the Rings, The Rise of the Witch-king)\\game\.dat$)", {{ { "d3d9.cachedDynamicBuffers", "True" }, }} }, /* WRC4 - Audio breaks above 60fps */ @@ -957,41 +996,6 @@ namespace dxvk { { "d3d9.maxFrameRate", "60" }, }} }, - - /**********************************************/ - /* D3D12 GAMES (vkd3d-proton with dxvk dxgi) */ - /**********************************************/ - - /* Diablo 4 - Will complain about missing * - * GPU unless dxgi Id match actual GPU Id */ - { R"(\\Diablo IV\.exe$)", {{ - { "dxgi.hideNvidiaGpu", "False" }, - }} }, - /* WILD HEARTS™️ * - * D3D12 title using D3D11 device for * - * media texture creation, whereby a large * - * chunk size only slows down media * - * initialization */ - { R"(\\WILD HEARTS(_Trial)?\.exe$)", {{ - { "dxvk.maxChunkSize", "4" }, - }} }, - /* Ratchet & Clank: Rift Apart - does not allow - * enabling ray tracing if it sees an AMD GPU. */ - { R"(\\RiftApart\.exe$)", {{ - { "dxgi.hideNvidiaGpu", "False" }, - }} }, - /* Metro Exodus Enhanced Edition picks GPU adapters - * by available VRAM, which causes issues on some - * systems with integrated graphics. */ - { R"(\\Metro Exodus Enhanced Edition\\MetroExodus\.exe$)", {{ - { "dxvk.hideIntegratedGraphics", "True" }, - }} }, - /* Persona 3 Reload - disables vsync by default and - * runs into severe frame latency issues on Deck. */ - { R"(\\P3R\.exe$)", {{ - { "dxgi.syncInterval", "1" }, - }} }, - /**********************************************/ /* D3D8 GAMES */ /**********************************************/ From af1ba1b20548035eda756a1525da07523602f551 Mon Sep 17 00:00:00 2001 From: Robin Kertels Date: Mon, 15 Jul 2024 17:05:58 +0200 Subject: [PATCH 240/246] [d3d9] FF: Fix return type for lighting op --- src/d3d9/d3d9_fixed_function.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/d3d9/d3d9_fixed_function.cpp b/src/d3d9/d3d9_fixed_function.cpp index 4da5cf266..e8972e4b2 100644 --- a/src/d3d9/d3d9_fixed_function.cpp +++ b/src/d3d9/d3d9_fixed_function.cpp @@ -1338,7 +1338,7 @@ namespace dxvk { uint32_t midDot = m_module.opDot(m_floatType, normal, mid); midDot = m_module.opFClamp(m_floatType, midDot, m_module.constf32(0.0f), m_module.constf32(1.0f)); uint32_t doSpec = m_module.opFOrdGreaterThan(bool_t, midDot, m_module.constf32(0.0f)); - doSpec = m_module.opLogicalAnd(bool_t, doSpec, m_module.opFOrdGreaterThan(m_floatType, hitDot, m_module.constf32(0.0f))); + doSpec = m_module.opLogicalAnd(bool_t, doSpec, m_module.opFOrdGreaterThan(bool_t, hitDot, m_module.constf32(0.0f))); uint32_t specularness = m_module.opPow(m_floatType, midDot, m_vs.constants.materialPower); specularness = m_module.opFMul(m_floatType, specularness, atten); From b74725b264700a57c41ad9f8db22f008b315fc4e Mon Sep 17 00:00:00 2001 From: Felix Klinge Date: Mon, 15 Jul 2024 21:08:32 +0200 Subject: [PATCH 241/246] Add Batman Arkham Knight Custom Vendor ID to config to fix the game for Intel GPUs --- src/util/config/config.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index 327c9af39..5183bc255 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -55,6 +55,11 @@ namespace dxvk { /* D3D11 GAMES */ /**********************************************/ + /* Batman Arkham Knight - doesn't like intel vendor id + (refuses to boot if vendor isn't 0x10de or 0x1002) */ + { R"(\\BatmanAK\.exe$)", {{ + { "dxgi.customVendorId", "10de" }, + }} }, /* Assassin's Creed Syndicate: amdags issues */ { R"(\\ACS\.exe$)", {{ { "dxgi.customVendorId", "10de" }, From 2a2d51e0491af3d4d0d545c9f026d9834f8ffbc0 Mon Sep 17 00:00:00 2001 From: Felix Klinge Date: Tue, 16 Jul 2024 14:47:59 +0200 Subject: [PATCH 242/246] changed dxgi.customVendorId to dxgi.hideIntelGpu for Batman Arkham Knight --- src/util/config/config.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index 5183bc255..feacdbc67 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -58,7 +58,7 @@ namespace dxvk { /* Batman Arkham Knight - doesn't like intel vendor id (refuses to boot if vendor isn't 0x10de or 0x1002) */ { R"(\\BatmanAK\.exe$)", {{ - { "dxgi.customVendorId", "10de" }, + { "dxgi.hideIntelGpu", "True" }, }} }, /* Assassin's Creed Syndicate: amdags issues */ { R"(\\ACS\.exe$)", {{ From bac7ae2929da476cd646507722f2e7fb51fb67d4 Mon Sep 17 00:00:00 2001 From: WinterSnowfall Date: Mon, 15 Jul 2024 16:21:17 +0300 Subject: [PATCH 243/246] [util] Disable countLosableResources for Inquisitor --- src/util/config/config.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index feacdbc67..adba71962 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -1131,6 +1131,11 @@ namespace dxvk { { R"(\\(nations|patriots)\.exe$)", {{ { "d3d9.deviceLossOnFocusLoss", "True" }, }} }, + /* Inquisitor (2009) * + * Leaks a resource when alt-tabbing */ + { R"(\\Inquisitor\.exe$)", {{ + { "d3d9.countLosableResources", "False" }, + }} }, }}; From b7d61b70c1429fb7e84853c6c64057b068f534fc Mon Sep 17 00:00:00 2001 From: Robin Kertels Date: Fri, 19 Jul 2024 18:08:08 +0200 Subject: [PATCH 244/246] [d3d9] Use converted format for multiplane formats in CheckFormat --- src/d3d9/d3d9_adapter.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/d3d9/d3d9_adapter.cpp b/src/d3d9/d3d9_adapter.cpp index e82d28c68..d0a91aac2 100644 --- a/src/d3d9/d3d9_adapter.cpp +++ b/src/d3d9/d3d9_adapter.cpp @@ -169,7 +169,11 @@ namespace dxvk { return D3D_OK; // Let's actually ask Vulkan now that we got some quirks out the way! - return CheckDeviceVkFormat(mapping.FormatColor, Usage, RType); + VkFormat format = mapping.FormatColor; + if (unlikely(mapping.ConversionFormatInfo.FormatColor != VK_FORMAT_UNDEFINED)) { + format = mapping.ConversionFormatInfo.FormatColor; + } + return CheckDeviceVkFormat(format, Usage, RType); } From 031a98c232cc4e6fc7aa962a654274e6585daabf Mon Sep 17 00:00:00 2001 From: Robin Kertels Date: Fri, 19 Jul 2024 18:09:43 +0200 Subject: [PATCH 245/246] [d3d9] Allow fourcc formats as the source in CheckFormatConversion The docs say: "The source format must be a FOURCC format or a valid back buffer format." --- src/d3d9/d3d9_adapter.cpp | 3 ++- src/d3d9/d3d9_format.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/d3d9/d3d9_adapter.cpp b/src/d3d9/d3d9_adapter.cpp index d0a91aac2..faf9a0381 100644 --- a/src/d3d9/d3d9_adapter.cpp +++ b/src/d3d9/d3d9_adapter.cpp @@ -251,7 +251,8 @@ namespace dxvk { D3D9Format SourceFormat, D3D9Format TargetFormat) { bool sourceSupported = SourceFormat != D3D9Format::Unknown - && IsSupportedBackBufferFormat(SourceFormat); + && (IsSupportedBackBufferFormat(SourceFormat) + || (IsFourCCFormat(SourceFormat) && !IsVendorFormat(SourceFormat))); bool targetSupported = TargetFormat == D3D9Format::X1R5G5B5 || TargetFormat == D3D9Format::A1R5G5B5 || TargetFormat == D3D9Format::R5G6B5 diff --git a/src/d3d9/d3d9_format.h b/src/d3d9/d3d9_format.h index a8927a9c1..4efbd07df 100644 --- a/src/d3d9/d3d9_format.h +++ b/src/d3d9/d3d9_format.h @@ -230,6 +230,7 @@ namespace dxvk { && format != D3D9Format::MULTI2_ARGB8 && format != D3D9Format::UYVY && format != D3D9Format::R8G8_B8G8 + && format != D3D9Format::YUY2 && format != D3D9Format::G8R8_G8B8 && format != D3D9Format::DXT1 && format != D3D9Format::DXT2 From 10ab85c3baeca3d7ae226361bca4aaa3f63c6fa7 Mon Sep 17 00:00:00 2001 From: Tobias Jakobi Date: Sat, 13 Jul 2024 21:00:27 +0200 Subject: [PATCH 246/246] [d3d8] Remove references to d3d8.useShadowBuffers for now The current D3D8 codebase in DXVK does not implement this feature, i.e. setting this option is a NOP. It was implemented in D8VK at some point, but was removed before the big merge. Remove any references to avoid confusion. --- dxvk.conf | 13 ------------- src/util/config/config.cpp | 5 ----- 2 files changed, 18 deletions(-) diff --git a/dxvk.conf b/dxvk.conf index 822607875..453024feb 100644 --- a/dxvk.conf +++ b/dxvk.conf @@ -709,16 +709,3 @@ # - True/False # d3d9.countLosableResources = True - -# Use NVIDIA Shadow Buffers -# -# Vendor behavior for GeForce3 and GeForce4 cards that allows -# sampling depth textures with non-normalized Z coordinates -# and applies hardware shadow filtering. -# -# Supported values: -# - True/False - -# d3d8.useShadowBuffers = False - - diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index adba71962..7183f842d 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -1014,11 +1014,6 @@ namespace dxvk { { R"(\\indy\.exe$)", {{ { "d3d9.enableDialogMode", "True" }, }} }, - /* Tom Clancy's Splinter Cell * - * Supports shadow buffers */ - { R"(\\splintercell\.exe$)", {{ - { "d3d8.useShadowBuffers", "True" }, - }} }, /* Anito: Defend a Land Enraged */ { R"(\\Anito\.exe$)", {{ { "d3d9.memoryTrackTest", "True" },