Skip to content
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

Back buffer reference counting #21

Closed
CookiePLMonster opened this issue May 6, 2017 · 10 comments
Closed

Back buffer reference counting #21

CookiePLMonster opened this issue May 6, 2017 · 10 comments

Comments

@CookiePLMonster
Copy link
Contributor

CookiePLMonster commented May 6, 2017

When investigating on why both d3d8to9 and ENB's d3d8 wrapper flat out crash with a dx8 thrash driver in Need for Speed III (from THIS PATCH) I noticed a couple issues, but the most important fact is that this code loves to do stuff like:

device->GetBackBuffer(0, &surface);
surface->Release();
global_myBackbuffer = surface;

Since d3d8to9 creates a new wrapper class in GetBackBuffer, the call to Release destroys it and any attempts to use this surface fail miserably.

Should this be considered severe API misuse and not handled, or is this something to fix on d3d8to9 side? I mean, this is obviously not how you should be managing reference counters but this doesn't seem to crash with a real dx8 device at all. Maybe device wrapper should account for such odd behaviour, store its backbuffer surface wrappers in an array and return those on demand? This should probably match real device behaviour more closely.

EDIT:
Said game also seems to crash consistently inside Direct3DDevice8::SetCurrentTexturePalette but that seems like a different issue. What's worse, it instantiates itself with D3DCREATE_MULTITHREADED and who knows how thread unsafe d3d8to9 device wrapper really is.

@crosire
Copy link
Owner

crosire commented May 6, 2017

While it's a bad idea it's not technically wrong to use the API like that, since the device has a guaranteed backbuffer instance for its entire lifetime. So yes, I guess we should cache the backbuffer in the device wrapper. I don't think we need to handle more than the first backbuffer though. It's rare more than one exists.

As for thread-safety: Most calls are implicitly handled by the critical section the D3D9 runtime acquires, since the same flag is passed on to it as well. However, some calls that modify states in d3d8to9, like Direct3DDevice8::SetRenderTarget are certainly not thread-safe.

@CookiePLMonster
Copy link
Contributor Author

CookiePLMonster commented May 6, 2017

I don't know what the D3D8 limit is, but D3D9 limit is 3 so it shouldn't be much more effort than handling only the first backbuffer.

Then there are swap chains, though... maybe d3d8 device should store handles to its implicit swap chains and use them for D3D8Device::GetBackBuffer and the others?

EDIT:
This thrash driver really is a clusterfuck though - seems to have crashes in more than just this and SetCurrentTexturePalette. Weird stuff.

@elishacloud
Copy link
Contributor

CookiePLMonster, are you sure you put the right link down? I downloaded the 'nfs3_modern_patch.7z' file from this link and followed the steps to install the patch and it runs nfs3.exe using Direct3D9. d3d8.dll does not get loaded at all. So I cannot reproduce this issue.

Also this code seems ok to me with the native Microsoft Direct3D8 driver:

device->GetBackBuffer(0, &surface);
surface->Release();
global_myBackbuffer = surface;

The surface should have at least 1 reference before GetBackBuffer is called. So when GetBackBuffer is called the count jumps to 2. Therefore when they call Release the count should go back to 1. I think the issue is in d3d8to9 where it does not add a reference when GetBackBuffer is called.

Maybe we should do something like this in GetBackBuffer:

*ppBackBuffer = new Direct3DSurface8(this, SurfaceInterface);
(*ppBackBuffer)->AddRef();

I see the same issue in many other functions. Here are the functions I see the same issue with:

  • Direct3DDevice8::GetBackBuffer()
  • Direct3DDevice8::GetTexture()
  • Direct3DDevice8::GetStreamSource()
  • Direct3DDevice8::GetIndices()
  • Direct3DSwapChain8::GetBackBuffer()
  • Direct3DTexture8::GetSurfaceLevel()
  • Direct3DCubeTexture8::GetCubeMapSurface()
  • Direct3DVolumeTexture8::GetVolumeLevel()

@CookiePLMonster
Copy link
Contributor Author

CookiePLMonster commented Jul 1, 2017 via email

@elishacloud
Copy link
Contributor

Ok, I found it. If you open 'nfs3.ini' and change ThrashDriver=nglide to ThrashDriver=dx8 it will use Direct3D8 and d3d8.dll.

@elishacloud
Copy link
Contributor

Calling AddRef() for each get surface and texture request as described in my comments above fixed the reference count issue.

The palette issue appears to be that they are calling SetCurrentTexturePalette with NULL palette peFlags. This was allowed in IDirect3D8, see here. But as far as I can tell SetCurrentTexturePalette will crash in IDirect3D9 unless the peFlags are set to PC_NOCOLLAPSE. This site also seems to indicate that you must set peFlags to PC_NOCOLLAPSE.

Adding this line solved the crash in SetCurrentTexturePalette:

PaletteNumber = (PaletteNumber & 0xFFF) | (PC_NOCOLLAPSE << 12);

Note the line should be added to both of these functions:

  • Direct3DDevice8::SetPaletteEntries
  • Direct3DDevice8::SetCurrentTexturePalette

Code changes to fix these issues are posted here 7d64f1a.

@CookiePLMonster
Copy link
Contributor Author

CookiePLMonster commented Jul 2, 2017 via email

@crosire
Copy link
Owner

crosire commented Jul 2, 2017

Don't just AddRef, that will create a memory leak. Caching is the right way to go here.

@elishacloud
Copy link
Contributor

elishacloud commented Jul 5, 2017

Ok, I added caching to fix the ref counting issue. Take a look at the code here and let me know what you think. I tested this with Need for Speed III along with 9 other Direct3D8 games.

Updated link with latest check-in that simplifies the caching function.

@elishacloud
Copy link
Contributor

I have made a few changes and updates. You might want to just look at the branch I created for this here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants