diff --git a/source/d3d8to9.hpp b/source/d3d8to9.hpp index 9a7b5df..c0971f3 100644 --- a/source/d3d8to9.hpp +++ b/source/d3d8to9.hpp @@ -6,6 +6,7 @@ #pragma once #include +#include #include "d3d8.hpp" #include "interface_query.hpp" @@ -160,6 +161,7 @@ class Direct3DDevice8 : public IDirect3DDevice8 private: void ApplyClipPlanes(); + void ReleaseShaders(); Direct3D8 *const D3D; IDirect3DDevice9 *const ProxyInterface; @@ -172,6 +174,10 @@ class Direct3DDevice8 : public IDirect3DDevice8 static constexpr size_t MAX_CLIP_PLANES = 6; float StoredClipPlanes[MAX_CLIP_PLANES][4] = {}; DWORD ClipPlaneRenderState = 0; + + // Store Shader Handles so they can be destroyed later to mirror D3D8 behavior + std::unordered_set PixelShaderHandles, VertexShaderHandles; + unsigned int VertexShaderAndDeclarationCount = 0; }; class Direct3DSwapChain8 : public IDirect3DSwapChain8, public AddressLookupTableObject diff --git a/source/d3d8to9_device.cpp b/source/d3d8to9_device.cpp index dca8214..20b100c 100644 --- a/source/d3d8to9_device.cpp +++ b/source/d3d8to9_device.cpp @@ -49,10 +49,21 @@ ULONG STDMETHODCALLTYPE Direct3DDevice8::AddRef() { return ProxyInterface->AddRef(); } + ULONG STDMETHODCALLTYPE Direct3DDevice8::Release() { ULONG LastRefCount = ProxyInterface->Release(); + // Shaders are destroyed alongside the device that created them in D3D8 but not in D3D9 + // so we Release all the shaders when the device releases to mirror that behaviour + if (LastRefCount !=0 && LastRefCount == (VertexShaderAndDeclarationCount + PixelShaderHandles.size())) + { + ProxyInterface->AddRef(); + ReleaseShaders(); + LastRefCount = ProxyInterface->Release(); + assert(LastRefCount == 0); + } + if (LastRefCount == 0) delete this; @@ -196,6 +207,10 @@ HRESULT STDMETHODCALLTYPE Direct3DDevice8::Reset(D3DPRESENT_PARAMETERS8 *pPresen } } + // Shaders are destroyed alongside the device that created them in D3D8 but not in D3D9 + // so we Release all the shaders when the device releases to mirror that behaviour + ReleaseShaders(); + return ProxyInterface->Reset(&PresentParams); } HRESULT STDMETHODCALLTYPE Direct3DDevice8::Present(const RECT *pSourceRect, const RECT *pDestRect, HWND hDestWindowOverride, const RGNDATA *pDirtyRegion) @@ -1419,6 +1434,7 @@ HRESULT STDMETHODCALLTYPE Direct3DDevice8::CreateVertexShader(const DWORD *pDecl ShaderInfo = new VertexShaderInfo(); hr = ProxyInterface->CreateVertexShader(static_cast(Assembly->GetBufferPointer()), &ShaderInfo->Shader); + VertexShaderAndDeclarationCount++; Assembly->Release(); } @@ -1436,19 +1452,28 @@ HRESULT STDMETHODCALLTYPE Direct3DDevice8::CreateVertexShader(const DWORD *pDecl if (SUCCEEDED(hr)) { + // Store the shader handle before it's bit-manipulated + + // Since 'Shader' is at least 8 byte aligned, we can safely shift it to right and end up not overwriting the top bit assert((reinterpret_cast(ShaderInfo) & 1) == 0); const DWORD ShaderMagic = reinterpret_cast(ShaderInfo) >> 1; *pHandle = ShaderMagic | 0x80000000; + + VertexShaderHandles.insert(*pHandle); + VertexShaderAndDeclarationCount++; } else { #ifndef D3D8TO9NOLOG LOG << "> 'IDirect3DDevice9::CreateVertexDeclaration' failed with error code " << std::hex << hr << std::dec << "!" << std::endl; #endif - if (ShaderInfo->Shader != nullptr) + if (ShaderInfo->Shader != nullptr) + { ShaderInfo->Shader->Release(); + VertexShaderAndDeclarationCount--; + } } } else @@ -1509,16 +1534,24 @@ HRESULT STDMETHODCALLTYPE Direct3DDevice8::DeleteVertexShader(DWORD Handle) if ((Handle & 0x80000000) == 0) return D3DERR_INVALIDCALL; + VertexShaderHandles.erase(Handle); + if (CurrentVertexShaderHandle == Handle) SetVertexShader(0); const DWORD HandleMagic = Handle << 1; VertexShaderInfo *const ShaderInfo = reinterpret_cast(HandleMagic); - if (ShaderInfo->Shader != nullptr) + if (ShaderInfo->Shader != nullptr) + { ShaderInfo->Shader->Release(); + VertexShaderAndDeclarationCount--; + } if (ShaderInfo->Declaration != nullptr) + { ShaderInfo->Declaration->Release(); + VertexShaderAndDeclarationCount--; + } delete ShaderInfo; @@ -2055,6 +2088,10 @@ HRESULT STDMETHODCALLTYPE Direct3DDevice8::CreatePixelShader(const DWORD *pFunct LOG << "> 'IDirect3DDevice9::CreatePixelShader' failed with error code " << std::hex << hr << std::dec << "!" << std::endl; #endif } + else + { + PixelShaderHandles.insert(*pHandle); + } return hr; } @@ -2083,6 +2120,8 @@ HRESULT STDMETHODCALLTYPE Direct3DDevice8::DeletePixelShader(DWORD Handle) reinterpret_cast(Handle)->Release(); + PixelShaderHandles.erase(Handle); + return D3D_OK; } HRESULT STDMETHODCALLTYPE Direct3DDevice8::SetPixelShaderConstant(DWORD Register, const void *pConstantData, DWORD ConstantCount) @@ -2134,3 +2173,18 @@ void Direct3DDevice8::ApplyClipPlanes() index++; } } + +void Direct3DDevice8::ReleaseShaders() +{ + for (auto Handle : PixelShaderHandles) + { + DeletePixelShader(Handle); + } + PixelShaderHandles.clear(); + for (auto Handle : VertexShaderHandles) + { + DeleteVertexShader(Handle); + } + VertexShaderHandles.clear(); + VertexShaderAndDeclarationCount = 0; +}