narzoul / DDrawCompat

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

Tropico - graphical artifacts in hardware mode #357

Open yeeevan opened 1 week ago

yeeevan commented 1 week ago

Tropico has graphical artifacts when "3D Mode" is set to "Hardware" in settings: image

Steps to reproduce:

  1. start tutorial (game only allows to change settings while in-game)
  2. press the book icon at bottom right to open the menu (under tropico logo, above money)
  3. go to settings and change "3D Mode" to "Hardware"

OS: win 10 21H2 GPU: nvidia gtx 1660 ti

log where i started the game hardware mode on from the start, got into tutorial and then left: https://icedrive.net/s/1Rxh7jf4R6BiWv2wzbCSiiWY7CGG (with loglevel=debug it's too big for github)

narzoul commented 1 week ago

It's a bug in the game. @elishacloud already covered it here: https://github.com/elishacloud/dxwrapper/discussions/109

But I was still curious why it could have worked when the game was released, because the pitch requirements are nothing new and should have existed back then too. The game also largely uses managed textures in hardware accelerated mode, where the DX runtime itself creates the system memory backing surface, and it would surely apply its own pitch rules there. At least for runtime-managed textures. I don't know about driver-managed ones, but I'm not sure if any drivers ever supported that anyway. The docs state that pitch should always be a multiple of 8 bytes for system memory surfaces, and that seems to be correct for runtime-managed textures too.

More puzzling is that hardware mode works correctly with WineD3D, which seems to follow the same pitch rules as native ddraw, and also uses a multiple of 8 bytes for managed textures. After a lot of debugging, I finally found what "fixes" it. WineD3D exposes the D3DPTEXTURECAPS_POW2 flag in its texture capabilities, meaning it can only create textures that have power-of-two sizes. It probably doesn't enforce this and only does it for compatibility reasons, but I haven't tested it further. Anyway, the game respects this, and only uses power-of-two textures in this case, which coincidentally makes the pitch work out to be what the game naively expects.

So it seems that an easier way to fix the problem is to simply expose this capability (or rather restriction) to the game. Either way, some new configuration setting is probably needed, as I'd rather not make this the default behavior. I have no idea if it breaks any other games.

Here's a quick fix without a config setting for now: ddraw.zip (diff.txt compared to v0.5.4)

yeeevan commented 1 week ago

Yep, works now. Thank you! (you can close, or keep open until you make it a config option) fixed

elishacloud commented 1 week ago

After a lot of debugging, I finally found what "fixes" it. WineD3D exposes the D3DPTEXTURECAPS_POW2 flag in its texture capabilities, meaning it can only create textures that have power-of-two sizes.

Good catch. Thanks for the note on this. It seems like most modern hardware require textures to be to the power of 2 internally even though this flag is not set. Since I think most video cards require this I wonder if it would be bad to always include the flag.

narzoul commented 1 week ago

It seems like most modern hardware require textures to be to the power of 2 internally even though this flag is not set.

What makes you think that? I think it's the opposite, most modern hardware don't have any restrictions like that on texture sizes (except for a maximum size), that's why the flag is not usually set. Wine has some comments claiming that the flag is always set by Direct3D7 on Windows, irrespective of hardware caps, but it's not the case on my system. Maybe it was true a long time ago, like before WDDM or some such. But I haven't seen any modern GPU yet that couldn't handle non-pow2 textures.

elishacloud commented 1 week ago

What makes you think that? I think it's the opposite, most modern hardware don't have any restrictions like that on texture sizes (except for a maximum size), that's why the flag is not usually set.

Maybe I am wrong here, but what I mean is that while DirectX allows you to create a texture that is not power of 2 it actually internally creates it as power of 2 anyways. You can see that from this game (Tropico) where it creates a texture at a specific size but internally (you can tell by the stride/pitch) the texture is actually created at a power of 2. So modern GPUs claim to create textures at non-power of 2 sizes, but in reality they actually create them at power of 2 sizes.

You can test this because if you create a texture size 127 width and then lock the texture you can see that the pitch/stride is actually the same as if it was 128 width.

Edit: maybe I am getting power of 2 mixed up with 32/64 bit byte aligned?

narzoul commented 1 week ago

Edit: maybe I am getting power of 2 mixed up with 32/64 bit byte aligned?

Yes, that's more like it. Although on dedicated GPUs the alignment seems to be much higher than that. I tested all 3 of my GPUs at texture widths 1, 65, 129 and 257, with 8 and 16 bit formats. None of them rounded up the pitch to a power of 2. They use width multiplied by bytesperpixel, rounded up to some alignment. The alignments in my case were:

Intel HD 770: 4 bytes NVIDIA RTX 3060Ti: 128 bytes AMD Radeon RX 480: 256 bytes

You can see that from this game (Tropico) where it creates a texture at a specific size but internally (you can tell by the stride/pitch) the texture is actually created at a power of 2.

Tropico's case is different from the above, because it uses managed textures, so the alignment is always 8 bytes (since the locked surface is technically a system memory resource, not a video memory one). For example, on a 9 width, 16 bpp texture, I had a pitch of 24 on NVIDIA.

elishacloud commented 1 week ago

Thanks for your detailed response!

BEENNath58 commented 5 days ago

D3DPTEXTURECAPS_POW2 flag in its texture capabilities

Being on this topic, I found it as useful to mention that the "pink tyres" in Grand Prix 2 gets fixes as soon as you expose it being able to use non-power-2 textures. However, I don't recall if DDrawCompat, which apparently allows non-pow-2 textures, stop the pink tyre issue