xemu-project / xemu

Original Xbox Emulator for Windows, macOS, and Linux (Active Development)
https://xemu.app
Other
2.79k stars 280 forks source link

The Elder Scrolls III: Morrowind: GOTY Edition: water is bugged, shows up in a grid pattern #725

Open nalabrie opened 2 years ago

nalabrie commented 2 years ago

Title

https://xemu.app/titles/42530005/#The-Elder-Scrolls-III-Morrowind-Game-of-the-Year-Edition

Bug Description

Any water in the game (lakes, ponds, etc.) is not rendered properly. The water is visible but broken up into a grid pattern. I assume the water was programmed this way but the grid itself wasn't supposed to be visible. Halo CE's water was programmed as a repeating grid, for example.

Steps to reproduce: Any source of water has this issue but here is how to get to water the quickest

  1. Start a new game
  2. Skip the cutscene with the Start button
  3. Name your character
  4. Follow the guard to leave the ship
  5. You are now surrounded by water

morrowind-bugged-water

Expected Behavior

I don't have my real Xbox to get a screenshot so I used my PC version. The water should look the same except without the grid. The shimmering effect (shimmering works, sorry) and shadows are also missing on xemu but I do not remember if the effect was also missing on real Xbox hardware. It could have been cut for performance reasons. I would need someone else to test this or find real Xbox footage. The video below is from my PC version.

EDIT: This YouTube video (not mine) shows the same water for a few seconds on a real Xbox https://youtu.be/nhP49PlN6HY?t=102 more water is visible for longer at 19:24

https://user-images.githubusercontent.com/29314595/152713903-e79970e4-3a0c-4cd7-804b-175cd51c9eb4.mp4

xemu Version

0.6.2-65-g517e4b3414

System Information

OS: Windows 10 CPU: AMD Ryzen 5 3600X GPU: NVIDIA GeForce GTX 1070 GPU Driver: 511.23

Additional Context

No response

abaire commented 2 years ago

Screenshot from hardware

morrowind_water

nv2a log output from more or less the same frame on xemu morrowind_water.txt

abaire commented 2 years ago

The water texture is very interesting and has some radial gaps in the lower left quadrant. If I mess with the shader to exclude that quadrant, the problem goes away, but presumably the output is not correct. It's unclear to me if the texture is corrupted or if it's an intentional env mapping effect. If I move the character around, I'll get a different texture so it seems likely it's being generated dynamically.

t1

After moving ashore: t1_2

Also, even within a single draw call there are multiple water "tiles", possibly an edge addressing/wrapping issue? Because it's a dot-product operation with stage0, it's also possible that there's an issue with the tex0 texture (which has a suspicious black bar on the left).

abaire commented 2 years ago

The textures captured via nv2a-trace from hardware don't seem to have this corruption:

command49--tex_0color-a

abaire commented 2 years ago

I'm wrong, my capture died before the composition; there are similar frames on HW:

command10003--color-a

abaire commented 2 years ago

But I do see that the tex0 is pretty substantially different between xemu and HW. Looks like maybe this is used for the ripple effect on the water:

xemu: t0

hw: command26201--tex_0color-a

I think this could explain the problem it if it's being used to map into the env texture

abaire commented 2 years ago

Looks like the alpha channel on the source textures in xemu is dramatically different from what I think is the same texture in hardware and more or less matches the final output (alpha seems compressed into the top and left sides).

t0 is generated a few draw calls before it is first used. For that generation, it looks like there is a noise texture that gets loaded into all 4 texture units. When I look at the noise texture it is again very dramatically different between xemu and HW:

xemu: noise_xemu

hw: noise_hw

I'm assuming this texture is generated somehow and not loaded, since the format is basic 0x06 (A8R8G8B8). I do notice that NV097_SET_TEXTURE_SET_BUMP_ENV_MAT is used during this draw which is interesting, but I don't think it can explain the input textures being different and so far I don't see the noise texture as an output of a nearby stage...

I also don't see the texture offset being referenced before it is used in the env/bump map generator pass.

Dumping the raw texture memory as it creates the GL texture in upload_gl_texture gives the same (seemingly wrong) output so it's not some trivial translation problem. unswizzled.bin.zip

abaire commented 2 years ago

I tried forcibly replacing the seemingly broken texture with the one from hardware in upload_gl_texture but that doesn't have any effect. The texture is also not recreated each frame, so I suspect that one of the outputs of the shader stages that seem to be applying the noise effect is piped back in for the next frame. This probably also explains the bad texture I see in my dump; if the noise generator is introducing compounding errors it's likely I just captured my frame after the texture was destroyed.

Will need to look into rendering to a texture (looks like SET_SURFACE_COLOR_OFFSET gets set to the address of the texture output by the shader and then is used by subsequent draws) to see if there's some interesting issue there.

abaire commented 2 years ago

Test written for basic texture rendering, no issue there.

When I look at the output of the vertex shader, I see something curious about the specific numbers:

-0.9916992 | -1.0083008 | -0.5151515 | 1
1.0083008 | -1.0083008 | -0.5151515 | 1
1.0083008 | 0.9916992 | -0.5151515 | 1
-0.9916992 | 0.9916992 | -0.5151515 | 1

I wonder if these small offsets introduce the compounding error that causes the noise texture to completely degenerate over time.

abaire commented 2 years ago

Confirmed that the behavior between xemu and hw differs in this case:

Test

HW: xbox_Rounding

xemu (the render target is initialized to magenta): Screenshot_20220214_144613

abaire commented 2 years ago

It also looks like the render target behavior differs between macOS/M1 and linux/nvidia (on m1 the blue texture fails to render entirely)

The rounding issue affects both render targets and basic geometry and could be responsible for all kinds of subtle issues.

abaire commented 2 years ago

The critical threshold for rounding is 0.5625 (0.5 + 1/16). Anything at that value or higher will be treated as the next pixel. Anything below will be treated as the previous pixel.

A trivial attempt at fixing by doing trunc(oPos + sign(oPos) * vec4(1.0 - 0.5625)) fixes the grid pattern, but introduces gaps in the geometry at borders between BEGIN_END calls.

abaire commented 2 years ago

Moving some of my findings from the PR back into the bug:


It looks like there is differing behavior between cases where we are rendering to the framebuffer versus to a texture.

When rendering direct to the framebuffer, it appears to be correct that we should adjust the coordinate by flooring the (screenspace) output of the xbox vertex shader + 0.5625 to match hardware rounding behavior.

When rendering to a texture, it seems like even without this PR (and with any offset values I've tried) there is texture smearing, which makes me think that it's related to the way xemu handles render targets (it looks like it's being filtered rather than faithfully copied).


When creating the initial texture from the surface, the result is correct (no smearing). When doing the first draw (rendering into the surface target) the smearing is introduced. So I think we can rule out the s2t_rndr, but I'm still left wondering why we don't get consistently shifted results (e.g., if I don't render back into the surface at the same hwaddr as the texture, I get a pixel-aligned image).

Triticum0 commented 2 years ago

This also affects Jurassic Park: Operation Genesis

NZJenkins commented 2 years ago

Not relevant to rounding issues but the effect is described here: https://developer.download.nvidia.com/assets/gamedev/docs/gdc2002_ProcTextureAnim.pdf

ArtemZaryanov commented 11 months ago

The problem is still present on the latest version of Xemu.

ghyujkilop commented 2 months ago

Still there (tested with 0.7.132)