From c2e9a9fa3c6074f8e750f451a4cc7a7d015d999a Mon Sep 17 00:00:00 2001 From: Tin Tin Hamans <5984296+tintinhamans@users.noreply.github.com> Date: Sat, 17 Oct 2026 19:54:15 +0200 Subject: [PATCH 1/2] fix(dx8wrapper): Release the cached textures at shutdown (#3543) (cherry picked from commit 78ba6ea6ff69879c813605d9e83239b068e38bf1) --- Core/Libraries/Source/WWVegas/WW3D2/dx8wrapper.cpp | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/Core/Libraries/Source/WWVegas/WW3D2/dx8wrapper.cpp b/Core/Libraries/Source/WWVegas/WW3D2/dx8wrapper.cpp index c406c783654..7c92a74a8b2 100644 --- a/Core/Libraries/Source/WWVegas/WW3D2/dx8wrapper.cpp +++ b/Core/Libraries/Source/WWVegas/WW3D2/dx8wrapper.cpp @@ -345,16 +345,12 @@ void DX8Wrapper::Shutdown() } - if (CurrentCaps) + for (size_t i = 0; i < ARRAY_SIZE(Textures); ++i) { - int max = CurrentCaps->Get_Max_Textures_Per_Pass(); - for (int i = 0; i < max; i++) + if (Textures[i]) { - if (Textures[i]) - { - Textures[i]->Release(); - Textures[i] = nullptr; - } + Textures[i]->Release(); + Textures[i] = nullptr; } } From 2e42b5ef4a0caa54b50e83d41df64215601394fb Mon Sep 17 00:00:00 2001 From: tintinhamans <5984296+tintinhamans@users.noreply.github.com> Date: Sun, 18 Oct 2026 00:32:01 +0200 Subject: [PATCH 2/2] fix(dx8): Unbind device resources before Reset --- .../Source/WWVegas/WW3D2/dx8wrapper.cpp | 138 ++++++++++-------- .../Source/WWVegas/WW3D2/dx8wrapper.h | 2 + 2 files changed, 78 insertions(+), 62 deletions(-) diff --git a/Core/Libraries/Source/WWVegas/WW3D2/dx8wrapper.cpp b/Core/Libraries/Source/WWVegas/WW3D2/dx8wrapper.cpp index 7c92a74a8b2..83a9fac9ece 100644 --- a/Core/Libraries/Source/WWVegas/WW3D2/dx8wrapper.cpp +++ b/Core/Libraries/Source/WWVegas/WW3D2/dx8wrapper.cpp @@ -156,6 +156,7 @@ bool DX8Wrapper::IsRenderToTexture = false; unsigned DX8Wrapper::_MainThreadID = 0; bool DX8Wrapper::CurrentDX8LightEnables[4]; bool DX8Wrapper::IsDeviceLost; +bool DX8Wrapper::DeviceResetFailed = false; int DX8Wrapper::ZBias; float DX8Wrapper::ZNear; float DX8Wrapper::ZFar; @@ -432,7 +433,7 @@ void DX8Wrapper::Invalidate_Cached_Render_States() } //Need to explicitly set texture to null, otherwise app will not be able to //set it to null because of redundant state checker. MW - if (_Get_D3D_Device8()) + if (_Get_D3D_Device8() && !DeviceResetFailed) _Get_D3D_Device8()->SetTexture(a, nullptr); if (Textures[a] != nullptr) { Textures[a]->Release(); @@ -486,6 +487,7 @@ void DX8Wrapper::Do_Onetime_Device_Dependent_Shutdowns() bool DX8Wrapper::Create_Device() { WWASSERT(D3DDevice == nullptr); // for now, once you've created a device, you're stuck with it! + DeviceResetFailed = false; D3DCAPS8 caps; if @@ -606,83 +608,95 @@ bool DX8Wrapper::Reset_Device(bool reload_assets) { WWDEBUG_SAY(("Resetting device.")); DX8_THREAD_ASSERT(); - if ((IsInitted) && (D3DDevice != nullptr)) { - // Release all non-MANAGED stuff - WW3D::_Invalidate_Textures(); + if (!IsInitted || D3DDevice == nullptr) { + WWDEBUG_SAY(("Device reset failed")); + return false; + } - for (unsigned i = 0;i < MAX_VERTEX_STREAMS;++i) - { - Set_Vertex_Buffer(nullptr, i); - } - Set_Index_Buffer(nullptr, 0); - if (m_pCleanupHook) { - m_pCleanupHook->ReleaseResources(); - } - DynamicVBAccessClass::_Deinit(); - DynamicIBAccessClass::_Deinit(); - DX8TextureManagerClass::Release_Textures(); - SHD_SHUTDOWN_SHADERS; + // Release all non-MANAGED stuff + WW3D::_Invalidate_Textures(); - // Reset frame count to reflect the flipping chain being reset by Reset() - FrameCount = 0; + for (unsigned i = 0;i < MAX_VERTEX_STREAMS;++i) + { + Set_Vertex_Buffer(nullptr, i); + } + Set_Index_Buffer(nullptr, 0); + if (m_pCleanupHook) { + m_pCleanupHook->ReleaseResources(); + } + DynamicVBAccessClass::_Deinit(); + DynamicIBAccessClass::_Deinit(); + DX8TextureManagerClass::Release_Textures(); + SHD_SHUTDOWN_SHADERS; - memset(Vertex_Shader_Constants, 0, sizeof(Vector4) * MAX_VERTEX_SHADER_CONSTANTS); - memset(Pixel_Shader_Constants, 0, sizeof(Vector4) * MAX_PIXEL_SHADER_CONSTANTS); + // Reset frame count to reflect the flipping chain being reset by Reset() + FrameCount = 0; - // Set_Vertex_Buffer/Set_Index_Buffer are deferred, so unbind on the device itself. - // Resources still bound during Reset() crash the runtime in DestroyResource. - for (unsigned i = 0; i < MAX_VERTEX_STREAMS; ++i) - { - DX8CALL(SetStreamSource(i, nullptr, 0)); - } - DX8CALL(SetIndices(nullptr, 0)); - Invalidate_Cached_Render_States(); + memset(Vertex_Shader_Constants, 0, sizeof(Vector4) * MAX_VERTEX_SHADER_CONSTANTS); + memset(Pixel_Shader_Constants, 0, sizeof(Vector4) * MAX_PIXEL_SHADER_CONSTANTS); - // Rebind the back buffer and release all surface - // references before Reset(), otherwise it crashes in DestroyResource. - Set_Render_Target((IDirect3DSurface8*)nullptr); + HRESULT hr = _Get_D3D_Device8()->TestCooperativeLevel(); + if (hr == D3DERR_DEVICELOST) { + return false; //device is lost and can't be reset. + } - HRESULT hr = _Get_D3D_Device8()->TestCooperativeLevel(); - if (hr != D3DERR_DEVICELOST) - { - // TheSuperHackers @bugfix xezon 13/06/2025 Front load the system dbghelp.dll to prevent - // the graphics driver from potentially loading the old game dbghelp.dll and then crashing the game process. - DbgHelpGuard dbgHelpGuard; - DX8CALL_HRES(Reset(&_PresentParameters), hr) - if (hr != D3D_OK) - return false; //reset failed. - } - else - return false; //device is lost and can't be reset. + // TheSuperHackers @bugfix arcticdolphin 09/10/2026 Reset() fails while resources are still bound + if (!DeviceResetFailed) { + Unbind_Device_Buffers(); + } + Invalidate_Cached_Render_States(); + Set_Render_Target((IDirect3DSurface8*)nullptr); - if (reload_assets) - { - DX8TextureManagerClass::Recreate_Textures(); - if (m_pCleanupHook) { - m_pCleanupHook->ReAcquireResources(); - } + { + // TheSuperHackers @bugfix xezon 13/06/2025 Front load the system dbghelp.dll to prevent + // the graphics driver from potentially loading the old game dbghelp.dll and then crashing the game process. + DbgHelpGuard dbgHelpGuard; + hr = _Get_D3D_Device8()->Reset(&_PresentParameters); + Increment_DX8_CallCount(); + } + DeviceResetFailed = (hr != D3D_OK); + if (DeviceResetFailed) { + Non_Fatal_Log_DX8_ErrorCode(hr, __FILE__, __LINE__); + return false; //reset failed. + } + + if (reload_assets) + { + DX8TextureManagerClass::Recreate_Textures(); + if (m_pCleanupHook) { + m_pCleanupHook->ReAcquireResources(); } - Invalidate_Cached_Render_States(); - Set_Default_Global_Render_States(); - SHD_INIT_SHADERS; - WWDEBUG_SAY(("Device reset completed")); - return true; } - WWDEBUG_SAY(("Device reset failed")); - return false; + Invalidate_Cached_Render_States(); + Set_Default_Global_Render_States(); + SHD_INIT_SHADERS; + WWDEBUG_SAY(("Device reset completed")); + return true; +} + +// Set_Vertex_Buffer and Set_Index_Buffer are deferred +void DX8Wrapper::Unbind_Device_Buffers() +{ + for (unsigned stream = 0; stream < MAX_VERTEX_STREAMS; ++stream) + { + DX8CALL(SetStreamSource(stream, nullptr, 0)); + } + DX8CALL(SetIndices(nullptr, 0)); } void DX8Wrapper::Release_Device() { if (D3DDevice) { - for (int a = 0;a < MAX_TEXTURE_STAGES;++a) - { //release references to any textures that were used in last rendering call - DX8CALL(SetTexture(a, nullptr)); - } + if (!DeviceResetFailed) + { + for (int a = 0;a < MAX_TEXTURE_STAGES;++a) + { //release references to any textures that were used in last rendering call + DX8CALL(SetTexture(a, nullptr)); + } - DX8CALL(SetStreamSource(0, nullptr, 0)); //release reference count on last rendered vertex buffer - DX8CALL(SetIndices(nullptr, 0)); //release reference count on last rendered index buffer + Unbind_Device_Buffers(); + } /* diff --git a/Core/Libraries/Source/WWVegas/WW3D2/dx8wrapper.h b/Core/Libraries/Source/WWVegas/WW3D2/dx8wrapper.h index 8a6f3d26495..f47dfe5fb1b 100644 --- a/Core/Libraries/Source/WWVegas/WW3D2/dx8wrapper.h +++ b/Core/Libraries/Source/WWVegas/WW3D2/dx8wrapper.h @@ -572,6 +572,7 @@ class DX8Wrapper static bool Create_Device(); static void Release_Device(); + static void Unbind_Device_Buffers(); static void Reset_Statistics(); static void Enumerate_Devices(); @@ -634,6 +635,7 @@ class DX8Wrapper static bool IsInitted; static bool IsDeviceLost; + static bool DeviceResetFailed; ///< Until a Reset() succeeds the device only takes TestCooperativeLevel, Reset and Release static void * Hwnd; static unsigned _MainThreadID;