-
Notifications
You must be signed in to change notification settings - Fork 8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[d3d8] RefCount refactoring #122
Changes from all commits
49b2da3
1705b7a
eeb22bc
e26d11c
ad97fea
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -151,14 +151,15 @@ namespace dxvk { | |
UINT iBackBuffer, | ||
D3DBACKBUFFER_TYPE Type, | ||
IDirect3DSurface8** ppBackBuffer) { | ||
InitReturnPtr(ppBackBuffer); | ||
|
||
if (iBackBuffer >= m_backBuffers.size() || m_backBuffers[iBackBuffer] == nullptr) { | ||
Com<d3d9::IDirect3DSurface9> pSurface9; | ||
HRESULT res = GetD3D9()->GetBackBuffer(0, iBackBuffer, (d3d9::D3DBACKBUFFER_TYPE)Type, &pSurface9); | ||
|
||
if (FAILED(res)) return res; | ||
|
||
m_backBuffers[iBackBuffer] = ref(new D3D8Surface(this, std::move(pSurface9))); | ||
m_backBuffers[iBackBuffer] = new D3D8Surface(this, std::move(pSurface9)); | ||
*ppBackBuffer = m_backBuffers[iBackBuffer].ref(); | ||
AlpyneDreams marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
return res; | ||
|
@@ -368,30 +369,76 @@ namespace dxvk { | |
|
||
if (pRenderTarget != NULL) { | ||
D3D8Surface* surf = static_cast<D3D8Surface*>(pRenderTarget); | ||
res = GetD3D9()->SetRenderTarget(0, surf->GetD3D9()); | ||
|
||
if (FAILED(res)) return res; | ||
D3DSURFACE_DESC rtDesc; | ||
surf->GetDesc(&rtDesc); | ||
|
||
if (unlikely(!(rtDesc.Usage & D3DUSAGE_RENDERTARGET))) | ||
return D3DERR_INVALIDCALL; | ||
|
||
if(likely(m_renderTarget.ptr() != surf)) { | ||
res = GetD3D9()->SetRenderTarget(0, surf->GetD3D9()); | ||
|
||
if (FAILED(res)) return res; | ||
|
||
D3D8Surface* pRenderTargetSwap = nullptr; | ||
bool isRTSwap = m_renderTargetPrev.ptr() == surf; | ||
|
||
if(unlikely(isRTSwap)) | ||
// keep a temporary ref on the prev RT to prevent its release | ||
pRenderTargetSwap = m_renderTargetPrev.ref(); | ||
|
||
m_renderTarget = ref(surf); | ||
m_renderTargetPrev = m_renderTarget; | ||
m_renderTarget = surf; | ||
|
||
if(unlikely(isRTSwap && pRenderTargetSwap)) | ||
pRenderTargetSwap->Release(); | ||
} | ||
} | ||
|
||
// SetDepthStencilSurface is a separate call | ||
D3D8Surface* zStencil = static_cast<D3D8Surface*>(pNewZStencil); | ||
res = GetD3D9()->SetDepthStencilSurface(D3D8Surface::GetD3D9Nullable(zStencil)); | ||
|
||
if (FAILED(res)) return res; | ||
if(pNewZStencil != NULL) { | ||
D3DSURFACE_DESC zsDesc; | ||
zStencil->GetDesc(&zsDesc); | ||
|
||
m_depthStencil = ref(zStencil); | ||
return res; | ||
if (unlikely(!(zsDesc.Usage & D3DUSAGE_DEPTHSTENCIL))) | ||
return D3DERR_INVALIDCALL; | ||
} | ||
|
||
if(likely(m_depthStencil.ptr() != zStencil)) { | ||
res = GetD3D9()->SetDepthStencilSurface(D3D8Surface::GetD3D9Nullable(zStencil)); | ||
|
||
if (FAILED(res)) return res; | ||
|
||
D3D8Surface* pDepthStencilSwap = nullptr; | ||
bool isDSSwap = m_depthStencilPrev.ptr() == zStencil; | ||
|
||
if(unlikely(isDSSwap)) | ||
// keep a temporary ref on the prev DS to prevent its release | ||
pDepthStencilSwap = m_depthStencilPrev.ref(); | ||
|
||
m_depthStencilPrev = m_depthStencil; | ||
m_depthStencil = zStencil; | ||
|
||
if(unlikely(isDSSwap && pDepthStencilSwap)) | ||
pDepthStencilSwap->Release(); | ||
} | ||
|
||
return D3D_OK; | ||
} | ||
|
||
HRESULT STDMETHODCALLTYPE GetRenderTarget(IDirect3DSurface8** ppRenderTarget) { | ||
InitReturnPtr(ppRenderTarget); | ||
|
||
if (unlikely(m_renderTarget == nullptr)) { | ||
Com<d3d9::IDirect3DSurface9> pRT9 = nullptr; | ||
HRESULT res = GetD3D9()->GetRenderTarget(0, &pRT9); // use RT index 0 | ||
|
||
m_renderTarget = ref(new D3D8Surface(this, std::move(pRT9))); | ||
if(FAILED(res)) return res; | ||
|
||
m_renderTarget = new D3D8Surface(this, std::move(pRT9)); | ||
|
||
*ppRenderTarget = m_renderTarget.ref(); | ||
return res; | ||
|
@@ -402,12 +449,15 @@ namespace dxvk { | |
} | ||
|
||
HRESULT STDMETHODCALLTYPE GetDepthStencilSurface(IDirect3DSurface8** ppZStencilSurface) { | ||
InitReturnPtr(ppZStencilSurface); | ||
|
||
if (unlikely(m_depthStencil == nullptr)) { | ||
Com<d3d9::IDirect3DSurface9> pStencil9 = nullptr; | ||
HRESULT res = GetD3D9()->GetDepthStencilSurface(&pStencil9); | ||
|
||
m_depthStencil = ref(new D3D8Surface(this, std::move(pStencil9))); | ||
if(FAILED(res)) return res; | ||
|
||
m_depthStencil = new D3D8Surface(this, std::move(pStencil9)); | ||
|
||
*ppZStencilSurface = m_depthStencil.ref(); | ||
return res; | ||
|
@@ -567,7 +617,10 @@ namespace dxvk { | |
|
||
D3D8Texture2D* tex = static_cast<D3D8Texture2D*>(pTexture); | ||
|
||
m_textures[Stage] = ref(tex); | ||
if(unlikely(m_textures[Stage].ptr() == tex)) | ||
return D3D_OK; | ||
|
||
m_textures[Stage] = tex; | ||
|
||
return GetD3D9()->SetTexture(Stage, D3D8Texture2D::GetD3D9Nullable(tex)); | ||
} | ||
|
@@ -769,8 +822,11 @@ namespace dxvk { | |
HRESULT STDMETHODCALLTYPE GetIndices( | ||
IDirect3DIndexBuffer8** ppIndexData, | ||
UINT* pBaseVertexIndex) { | ||
InitReturnPtr(ppIndexData); | ||
|
||
*ppIndexData = m_indices.ptr(); | ||
*pBaseVertexIndex = m_baseVertexIndex; | ||
|
||
return D3D_OK; | ||
} | ||
|
||
|
@@ -829,9 +885,10 @@ namespace dxvk { | |
for (UINT i = 0; i < m_presentParams.BackBufferCount; i++) { | ||
m_backBuffers.push_back(nullptr); | ||
} | ||
m_frontBuffer = nullptr; | ||
m_renderTarget = nullptr; | ||
m_renderTargetPrev = nullptr; | ||
m_depthStencil = nullptr; | ||
m_depthStencilPrev = nullptr; | ||
} | ||
|
||
friend d3d9::IDirect3DPixelShader9* getPixelShaderPtr(D3D8DeviceEx* device, DWORD Handle); | ||
|
@@ -849,23 +906,24 @@ namespace dxvk { | |
D3D8StateBlock* m_recorder = nullptr; | ||
|
||
struct D3D8VBO { | ||
Com<D3D8VertexBuffer> buffer = nullptr; | ||
UINT stride = 0; | ||
Com<D3D8VertexBuffer, false> buffer = nullptr; | ||
UINT stride = 0; | ||
}; | ||
|
||
// Remember to fill() these in the constructor! | ||
std::array<Com<IDirect3DBaseTexture8>, d8caps::MAX_TEXTURE_STAGES> m_textures; | ||
std::array<Com<D3D8Texture2D, false>, d8caps::MAX_TEXTURE_STAGES> m_textures; | ||
std::array<D3D8VBO, d8caps::MAX_STREAMS> m_streams; | ||
|
||
Com<D3D8IndexBuffer> m_indices; | ||
INT m_baseVertexIndex = 0; | ||
Com<D3D8IndexBuffer, false> m_indices; | ||
INT m_baseVertexIndex = 0; | ||
|
||
// TODO: Which of these should be a private ref | ||
std::vector<Com<D3D8Surface, false>> m_backBuffers; | ||
Com<D3D8Surface> m_frontBuffer; | ||
|
||
Com<D3D8Surface> m_renderTarget; | ||
Com<D3D8Surface, false> m_renderTarget; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think these should all be private.
Similar deal for the depth-stencils, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Notice the latter is calling AddRef() directly, not incRef, so it is increasing the public refcount even on private COM objects. Again, the main idea here is to keep the cached objects around as long as we need them, not according to what a game might decide by messing with the public refcount, while trying to match native public refcounts with privateComObject.ref(). As far as I can see from all the native traces I have compared against those captured with this PR, they now behave identically.
If I do not add a temporary ref here and release it after the swap is finalized, the game will crash. This is because the object that is about to be set is equal to the prev object, and the first operation, namely:
will 0 the private reference on m_depthStencilPrev, causing the object's destruction (which we don't want, because this is essentially equal to zStencil in case of a swap). So to keep the object around while I'm swapping it I am indeed increasing its public ref by 1 and releasing it when done. I've used SAFE_RELEASE, because it also nulls the "swap" pointer after calling Release(), but simply calling Release in case of a not null pointer works as well. It's just that SAFE_RELEASE already has the needed logic in place. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Okay, I see, I always assumed ref called .incRef rather than ->AddRef. I am a little concerned about private refcounts purely because there may be cases where we forget to manually increase the refcount when a game expects a certain thing, but we forget to do it. I think it could probably be solved another way, but if this increases compatability then it might be worth it. |
||
Com<D3D8Surface, false> m_renderTargetPrev; | ||
Com<D3D8Surface, false> m_depthStencil; | ||
Com<D3D8Surface, false> m_depthStencilPrev; | ||
|
||
std::vector<D3D8VertexShaderInfo> m_vertexShaders; | ||
std::vector<d3d9::IDirect3DPixelShader9*> m_pixelShaders; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't be needed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One is now a private COM while the other is a public one so I get an error when I don't use .ptr() here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's interesting, because Com<T, B> should have == implemented for any B
But if the compiler is being dumb about it then I suppose leave it like that.