Skip to content

Commit

Permalink
Change shader object lifetimes to better match D3D8 behavior (#158)
Browse files Browse the repository at this point in the history
  • Loading branch information
nv-ajaus authored Jun 15, 2023
1 parent aaddbe5 commit cc71fd3
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 2 deletions.
6 changes: 6 additions & 0 deletions source/d3d8to9.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#pragma once

#include <vector>
#include <unordered_set>
#include "d3d8.hpp"
#include "interface_query.hpp"

Expand Down Expand Up @@ -160,6 +161,7 @@ class Direct3DDevice8 : public IDirect3DDevice8

private:
void ApplyClipPlanes();
void ReleaseShaders();

Direct3D8 *const D3D;
IDirect3DDevice9 *const ProxyInterface;
Expand All @@ -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<DWORD> PixelShaderHandles, VertexShaderHandles;
unsigned int VertexShaderAndDeclarationCount = 0;
};

class Direct3DSwapChain8 : public IDirect3DSwapChain8, public AddressLookupTableObject
Expand Down
58 changes: 56 additions & 2 deletions source/d3d8to9_device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -1419,6 +1434,7 @@ HRESULT STDMETHODCALLTYPE Direct3DDevice8::CreateVertexShader(const DWORD *pDecl
ShaderInfo = new VertexShaderInfo();

hr = ProxyInterface->CreateVertexShader(static_cast<const DWORD *>(Assembly->GetBufferPointer()), &ShaderInfo->Shader);
VertexShaderAndDeclarationCount++;

Assembly->Release();
}
Expand All @@ -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<DWORD>(ShaderInfo) & 1) == 0);
const DWORD ShaderMagic = reinterpret_cast<DWORD>(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
Expand Down Expand Up @@ -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<VertexShaderInfo *>(HandleMagic);

if (ShaderInfo->Shader != nullptr)
if (ShaderInfo->Shader != nullptr)
{
ShaderInfo->Shader->Release();
VertexShaderAndDeclarationCount--;
}
if (ShaderInfo->Declaration != nullptr)
{
ShaderInfo->Declaration->Release();
VertexShaderAndDeclarationCount--;
}

delete ShaderInfo;

Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -2083,6 +2120,8 @@ HRESULT STDMETHODCALLTYPE Direct3DDevice8::DeletePixelShader(DWORD Handle)

reinterpret_cast<IDirect3DPixelShader9 *>(Handle)->Release();

PixelShaderHandles.erase(Handle);

return D3D_OK;
}
HRESULT STDMETHODCALLTYPE Direct3DDevice8::SetPixelShaderConstant(DWORD Register, const void *pConstantData, DWORD ConstantCount)
Expand Down Expand Up @@ -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;
}

0 comments on commit cc71fd3

Please sign in to comment.