narzoul / DDrawCompat

DirectDraw and Direct3D 1-7 compatibility, performance and visual enhancements for Windows Vista, 7, 8, 10 and 11
BSD Zero Clause License
939 stars 70 forks source link

Query on Rogue Spear #212

Closed BEENNath58 closed 12 months ago

BEENNath58 commented 1 year ago

The game has different types of issues, and investigating them is quite difficult, I was intrigued to know how you fixed two of them:

  1. The SW renderer: It works when DDC is used. In normal circumstances it crashes. When DxWnd is forced to apply HEL, the game runs with no mission visuals. When just HAL is disabled (so the game uses RGB or RAMP or just HEL in some way again), I get to see the sky but nothing else. How did you fix it? Gho doubted that it is something with the game using an IDirectDraw interface no longer supported. What did you do there?
  2. Lagged briefing: The briefing screen is lagging unless the game uses non-HAL modes withOUT DDC, with DDC everything is fine. What was the issue there?

Thank you

narzoul commented 1 year ago

I don't remember doing anything specific for the SW renderer. Probably by the time I tested it, it was already fixed by something, so I don't know what it was. The only issue I think it had was that it didn't render correctly above 640x480. It looked like z-buffer corruption or some such to me, but I just assumed it was badly handled by the game itself.

I do remember 2 separate crash problems when starting the game in general, not sure if they were related to SW rendering though, and at the time both were only affecting AMD drivers:

  1. The main menu fonts are mapped into a system memory surface larger than 4k in one dimension, but AMD drivers for some reason restrict the maximum system memory surface size to the same as video memory surfaces, which was 4k on my old GPUs (this shouldn't be a problem with newer GPUs, since they support at least 8k textures). There was some custom fix on the internet that replaced the problematic font with a smaller one I think, which fits into 4k.

  2. I couldn't find traces of this the last time I checked, but I definitely recall the game was using the old DDSURFACEDESC struct for creating z-buffers. With this older interface, the z-buffer format is just specified as a single number with the bit depth, instead of as a pixel format, like with the newer interfaces. The problem was that the game was using DDCAPS.dwZBufferBitDepths to determine the largest supported bit depth, and was trying to use that to create the z-buffer. However, my AMD driver was filling this cap incorrectly, indicating that it supports 16 and 32 bit depth buffers, where in fact it was only 16 and 24 bit ones. So the z-buffer creation failed, the game had no error checking for it and then crashed on dereferencing the null surface pointer. I think this is still an issue on newer drivers too (at least for my RX 480).

The lag in the briefing is also related to text, I think. The game tries to blit every single letter from a system memory surface to the render target back buffer with source color keying. Because color keying is involved, this cannot be hardware accelerated, so the HEL steps in to emulate the copy by locking both surfaces, performing the blit via the CPU, then unlocking both surfaces. Locking and unlocking the back buffer is expensive, I think it's basically equivalent to copying the whole surface from video memory to system memory and then back. And this blit can happen hundreds of times in a short time frame during some transitions in the briefing screens.

DDrawCompat has multiple ways of handling such scenarios more efficiently:

  1. It intercepts lock/unlock operations in the WDDM driver, and replaces them by copying the surface between the video memory surface and a self-allocated system memory surface that serves as the "lock surface". This in itself is not much different than locking the surface, but it also remembers which of the two surfaces still contains valid data, and only does the copy on demand when the requested surface was invalidated by some operation. Technically, this might be possible to do without WDDM hooks I think (maybe I even had such a version in some old release), but there are cases when DirectDraw calls internal (not exported) lock functions, such as when a blit has to be emulated by HEL, and you'd have to be able to predict these in advance accurately and redirect the operation to the system memory copy (after making sure it's up to date).

  2. It detects when a blit requires a nontrivial copy from system to memory video (which cannot be hardware accelerated), gets a temporary video memory texture where it uploads the source rectangle, then changes the source surface and rectangle to this texture. The blit itself might be performed by shaders instead of the pfnBlt driver routine, because newer AMD GPUs have somehow also broken color keyed blits (I received multiple such reports for RX 5xxx series GPUs at least). And I've also noticed strange issues with doing stretched blits on NVIDIA, which defaults to point filtering, but it seems to have some rounding errors that result in one-pixel shifts along some horizontal/vertical lines. Nowadays blitting via shaders seems to be a lot more reliable. You could also do some of this with fixed-function pipeline rendering without shaders, but you know, D3D9On12 doesn't support texture color keying, which would be needed to emulate color keyed blits... And, of course the D3D7 runtime doesn't support shaders at all, so using shaders is pretty much impossible without WDDM hooking, I think.

BEENNath58 commented 1 year ago

Thanks for the report. The game would usually run natively, but now I can't seem to run the game without DDC or DxWnd or dgVoodoo2. Something related to resolution enumeration is causing it (the DxWnd option for resolution - Native emulates the crash again), and the three wrappers fix it.

What I did was trace the oldest ddraw.zip files found across the Issues thread, and the oldest from Aug 19, 2019 has the game working properly. Since I can't seem to compile Detours correctly (I never used it). And 0.21 version doesn't work so it must be sometime in 3 years when it was fixed.

Gho assumes any of the DirectDraw3 interfact (apparently IDirectDraw3?) is missing on Windows but I am skeptical about it. He quotes: "In SW mode the game tries to open a Direct3D3 session and returns an unknown error code 0x887602e9. My guess is that Direct3D3 support was completely forgotten and wiped away from last Windows versions. We probably would need a D3D implementation that includes this legacy interface, but I fear it won't be easy."

But we don't know, since HEL and HAL-less modes render something at least.

Edit: Although you don't manage DxWnd, the last line that appears before crash in SW mode is: BuildGenericEmu: CreateSurface ERROR res=0x88760091(DDERR_INVALIDPIXELFORMAT) at 876

Ring any bells? (I am using Nvidia, not AMD)

narzoul commented 1 year ago

Gho assumes any of the DirectDraw3 interfact (apparently IDirectDraw3?) is missing on Windows but I am skeptical about it.

I never heard of such an issue either, and I don't think that's the case.

returns an unknown error code 0x887602e9

That's hardly unknown, it can even be found by a simple search on Sourcegraph: https://sourcegraph.com/search?q=context:global+0x887602e9&patternType=standard&sm=1&groupBy=repo

From the DX7 SDK:

D3DERR_ZBUFF_NEEDS_SYSTEMMEMORY The requested operation could not be completed because the specified device requires system-memory depth-buffer surfaces. (Software rendering devices require system-memory depth buffers.)

It sounds like maybe DxWnd is removing the DDSCAPS_SYSTEMMEMORY cap when creating the z-buffer surface? At least I don't remember the game having such an issue on my end.

I don't know about the DDERR_INVALIDPIXELFORMAT crash, doesn't look familiar to me.

BEENNath58 commented 1 year ago

Thank you again. I got some better news this time.

Basically DxWnd has a setting named "Clean ZBUFFER HARD" which writes stuffs on top of the ZBuffer. This brings the game back to life, but there are MAJOR ZBUFFER ISSUES:

roguespearbadzbuffer

Now I think I will be correct to assume DDC doesn't use this "write on top of ZBuffer" approach any day. Do you recall doing anything special to DDC when you saw the game renders like this (if it did) or do you utilise any special technique to deal with situations like this with games?

It sounds like maybe DxWnd is removing the DDSCAPS_SYSTEMMEMORY cap when creating the z-buffer surface?

DxWnd has a setting named ddraw:AllowSysMemoryon3DDevices that resembles what it says. This is what Windows 11 actually needs with the ZBuffer setting! BUT Windows 7 doesn't need! Can it mean that Windows 10/11 prohibits the usage of system memory for 3D purposes unless any special parameter is passed?

narzoul commented 1 year ago

Do you recall doing anything special to DDC when you saw the game renders like this (if it did) or do you utilise any special technique to deal with situations like this with games?

No, I never saw this issue, nor did I do anything special with z-buffers for this game apart from fixing the AMD caps bug, which was causing a crash on start. There are some objects that become invisible when looked at from certain camera angles, which is fixed by FaultTolerantHeap if I remember right, but it didn't look as bad as on your screenshot.

DxWnd has a setting named ddraw:AllowSysMemoryon3DDevices that resembles what it says. This is what Windows 11 actually needs with the ZBuffer setting! BUT Windows 7 doesn't need! Can it mean that Windows 10/11 prohibits the usage of system memory for 3D purposes unless any special parameter is passed?

I doubt it. DDrawCompat doesn't touch z-buffer memory type at all (unless using the SoftwareDevice=hal setting), so system memory z-buffers should be working with native ddraw too. Direct3D has pretty strict requirements for software rendering devices, as far as I know, all 3D-related surfaces must be in system memory. In practice, the opposite is true for hardware accelerated devices, although in theory the GPU could support texturing from system memory and such. At least there are caps for it, though I don't know if any hardware ever actually supported it.

Who knows, with the amount of memory management issues in old games, it could be that the game itself sets the z-buffer surface caps incorrectly based on some uninitialized variable or whatever. But I've never seen this particular problem myself.

Oh, and there are z-buffer issues with the software renderer when using resolutions larger than 640x480, but I already mentioned that. Pretty sure it's a game bug, but 640x480 at least always worked for me.