From 94a12ec56ba2398c164a2dd61b77d67957774010 Mon Sep 17 00:00:00 2001 From: Jon-Luke Biddle Date: Thu, 5 Nov 2026 04:59:19 -0600 Subject: [PATCH 1/2] bugfix(dx8): Use the display format for the fullscreen back buffer when a device reset does not last In fullscreen the back buffer is promoted to D3DFMT_A8R8G8B8 while the display mode is D3DFMT_X8R8G8B8. Because the formats differ, every IDirect3DDevice8::Reset() sets the display mode again. On systems where a display mode change loses the device (seen on Windows 11 with multiple monitors and GPUs), the device is lost again by its own Reset() before a single frame is presented, so the game resets forever while the screen keeps switching modes. When the previous fullscreen Reset() did not last until a frame was presented, Reset_Device now switches the back buffer to the display format, which lets Reset() restore the device without a display mode change. Destination alpha effects such as the soft water edge are then unavailable for the rest of the session. --- .../Source/WWVegas/WW3D2/dx8wrapper.cpp | 53 ++++++++++++++++++- .../Source/WWVegas/WW3D2/dx8wrapper.h | 3 ++ 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/Core/Libraries/Source/WWVegas/WW3D2/dx8wrapper.cpp b/Core/Libraries/Source/WWVegas/WW3D2/dx8wrapper.cpp index 5597ef73649..099d261359e 100644 --- a/Core/Libraries/Source/WWVegas/WW3D2/dx8wrapper.cpp +++ b/Core/Libraries/Source/WWVegas/WW3D2/dx8wrapper.cpp @@ -120,6 +120,8 @@ int DX8Wrapper::TextureBitDepth = DEFAULT_TEXTURE_BIT_DEPTH; bool DX8Wrapper::IsWindowed = false; D3DFORMAT DX8Wrapper::DisplayFormat = D3DFMT_UNKNOWN; D3DMULTISAMPLE_TYPE DX8Wrapper::MultiSampleAntiAliasing = DEFAULT_MSAA; +bool DX8Wrapper::FullscreenBackBufferUsesDisplayFormat = false; +unsigned DX8Wrapper::FullscreenResetsWithoutPresent = 0; // shader system additions KJM v DWORD DX8Wrapper::Vertex_Shader = 0; @@ -631,9 +633,24 @@ bool DX8Wrapper::Reset_Device(bool reload_assets) HRESULT hr=_Get_D3D_Device8()->TestCooperativeLevel(); if (hr != D3DERR_DEVICELOST ) - { DX8CALL_HRES(Reset(&_PresentParameters),hr) + { + // TheSuperHackers @bugfix coconutbird 05/11/2026 A fullscreen back buffer with destination alpha does not + // match the display format, so every Reset() sets the display mode again. On systems where a display mode + // change loses the device, the device is lost again by its own Reset() before it presents a single frame, + // and the game resets forever while the screen keeps switching modes. When the previous Reset() did not + // last until a frame was presented, use the display format for the back buffer, so that Reset() can + // restore the device without changing the display mode. + if (!IsWindowed && FullscreenResetsWithoutPresent != 0 && _PresentParameters.BackBufferFormat != DisplayFormat) + { + Use_Display_Format_For_Fullscreen_Back_Buffer(); + } + + DX8CALL_HRES(Reset(&_PresentParameters),hr) if (hr != D3D_OK) return false; //reset failed. + + if (!IsWindowed) + ++FullscreenResetsWithoutPresent; } else return false; //device is lost and can't be reset. @@ -1485,7 +1502,9 @@ bool DX8Wrapper::Find_Color_And_Z_Mode(int resx,int resy,int bitdepth,D3DFORMAT *set_backbuffer=*set_colorbuffer = format_table[format_index]; } - if (bitdepth==32 && *set_colorbuffer == D3DFMT_X8R8G8B8 && D3DInterface->CheckDeviceType(0,D3DDEVTYPE_HAL,*set_colorbuffer,D3DFMT_A8R8G8B8, TRUE) == D3D_OK) + // TheSuperHackers @bugfix coconutbird 05/11/2026 Keep the display format once Reset_Device found that a back buffer + // with destination alpha cannot be kept in fullscreen. + if (bitdepth==32 && *set_colorbuffer == D3DFMT_X8R8G8B8 && !FullscreenBackBufferUsesDisplayFormat && D3DInterface->CheckDeviceType(0,D3DDEVTYPE_HAL,*set_colorbuffer,D3DFMT_A8R8G8B8, TRUE) == D3D_OK) { //promote 32-bit modes to include destination alpha when supported *set_backbuffer = D3DFMT_A8R8G8B8; @@ -1624,6 +1643,34 @@ bool DX8Wrapper::Test_Z_Mode(D3DFORMAT colorbuffer,D3DFORMAT backbuffer, D3DFORM return true; } +void DX8Wrapper::Use_Display_Format_For_Fullscreen_Back_Buffer() +{ + WWDEBUG_SAY(("DX8Wrapper::Reset_Device did not last until a frame was presented. Using the display format for the fullscreen back buffer.")); + FullscreenBackBufferUsesDisplayFormat = true; + _PresentParameters.BackBufferFormat = DisplayFormat; + + D3DFORMAT zmode; + if (Find_Z_Mode(DisplayFormat, DisplayFormat, &zmode)) + { + _PresentParameters.AutoDepthStencilFormat = zmode; + } + + if (_PresentParameters.MultiSampleType != D3DMULTISAMPLE_NONE) + { + const HRESULT hrBack = D3DInterface->CheckDeviceMultiSampleType(CurRenderDevice, D3DDEVTYPE_HAL, + _PresentParameters.BackBufferFormat, FALSE, _PresentParameters.MultiSampleType); + const HRESULT hrDepth = D3DInterface->CheckDeviceMultiSampleType(CurRenderDevice, D3DDEVTYPE_HAL, + _PresentParameters.AutoDepthStencilFormat, FALSE, _PresentParameters.MultiSampleType); + + if (FAILED(hrBack) || FAILED(hrDepth)) + { + WWDEBUG_SAY(("Requested MSAA Mode Not Supported")); + MultiSampleAntiAliasing = D3DMULTISAMPLE_NONE; + _PresentParameters.MultiSampleType = D3DMULTISAMPLE_NONE; + } + } +} + void DX8Wrapper::Reset_Statistics() { @@ -1692,6 +1739,7 @@ void DX8Wrapper::End_Scene(bool flip_frames) #endif IsDeviceLost=false; FrameCount++; + FullscreenResetsWithoutPresent = 0; } else { IsDeviceLost=true; @@ -1760,6 +1808,7 @@ void DX8Wrapper::Flip_To_Primary() if (SUCCEEDED(hr)) { IsDeviceLost=false; FrameCount++; + FullscreenResetsWithoutPresent = 0; WWDEBUG_SAY(("Flip to primary succeeded %ld", FrameCount)); } else { diff --git a/Core/Libraries/Source/WWVegas/WW3D2/dx8wrapper.h b/Core/Libraries/Source/WWVegas/WW3D2/dx8wrapper.h index 2e929450484..35cf2f7caad 100644 --- a/Core/Libraries/Source/WWVegas/WW3D2/dx8wrapper.h +++ b/Core/Libraries/Source/WWVegas/WW3D2/dx8wrapper.h @@ -624,6 +624,7 @@ class DX8Wrapper static bool Find_Color_Mode(D3DFORMAT colorbuffer, int resx, int resy, UINT *mode); static bool Find_Z_Mode(D3DFORMAT colorbuffer,D3DFORMAT backbuffer, D3DFORMAT *zmode); static bool Test_Z_Mode(D3DFORMAT colorbuffer,D3DFORMAT backbuffer, D3DFORMAT zmode); + static void Use_Display_Format_For_Fullscreen_Back_Buffer(); static void Compute_Caps(WW3DFormat display_format); /* @@ -651,6 +652,8 @@ class DX8Wrapper static bool IsWindowed; static D3DFORMAT DisplayFormat; static D3DMULTISAMPLE_TYPE MultiSampleAntiAliasing; + static bool FullscreenBackBufferUsesDisplayFormat; + static unsigned FullscreenResetsWithoutPresent; // shader system updates KJM v From da9500c63c89dc046198336e5fd3bac043c86943 Mon Sep 17 00:00:00 2001 From: Jon-Luke Biddle Date: Fri, 6 Nov 2026 06:59:47 -0600 Subject: [PATCH 2/2] bugfix(dx8): Keep the back buffer format when no depth buffer format matches the display format --- Core/Libraries/Source/WWVegas/WW3D2/dx8wrapper.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/Core/Libraries/Source/WWVegas/WW3D2/dx8wrapper.cpp b/Core/Libraries/Source/WWVegas/WW3D2/dx8wrapper.cpp index 099d261359e..a7c8b25c1ed 100644 --- a/Core/Libraries/Source/WWVegas/WW3D2/dx8wrapper.cpp +++ b/Core/Libraries/Source/WWVegas/WW3D2/dx8wrapper.cpp @@ -1645,16 +1645,18 @@ bool DX8Wrapper::Test_Z_Mode(D3DFORMAT colorbuffer,D3DFORMAT backbuffer, D3DFORM void DX8Wrapper::Use_Display_Format_For_Fullscreen_Back_Buffer() { - WWDEBUG_SAY(("DX8Wrapper::Reset_Device did not last until a frame was presented. Using the display format for the fullscreen back buffer.")); - FullscreenBackBufferUsesDisplayFormat = true; - _PresentParameters.BackBufferFormat = DisplayFormat; - D3DFORMAT zmode; - if (Find_Z_Mode(DisplayFormat, DisplayFormat, &zmode)) + if (!Find_Z_Mode(DisplayFormat, DisplayFormat, &zmode)) { - _PresentParameters.AutoDepthStencilFormat = zmode; + WWDEBUG_SAY(("DX8Wrapper::Reset_Device did not last until a frame was presented, but no depth buffer format matches the display format. Keeping the fullscreen back buffer format.")); + return; } + WWDEBUG_SAY(("DX8Wrapper::Reset_Device did not last until a frame was presented. Using the display format for the fullscreen back buffer.")); + FullscreenBackBufferUsesDisplayFormat = true; + _PresentParameters.BackBufferFormat = DisplayFormat; + _PresentParameters.AutoDepthStencilFormat = zmode; + if (_PresentParameters.MultiSampleType != D3DMULTISAMPLE_NONE) { const HRESULT hrBack = D3DInterface->CheckDeviceMultiSampleType(CurRenderDevice, D3DDEVTYPE_HAL,