xemu-project / xemu

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

Pirates: The Legend of Black Kat - FMVs are rendered as side-by-side duplicates #652

Open abaire opened 2 years ago

abaire commented 2 years ago

Title

https://xemu.app/titles/45410015/#Pirates-The-Legend-of-Black-Kat

Bug Description

The intro FMVs are duplicated side-by-side:

Screenshot_20220113_083642 Screenshot_20220113_083651

Expected Behavior

On hardware the videos are not duplicated: ship_xbox westwood_xbox

xemu Version

Latest master

Version: 0.6.2-51-g3fd15218c7 Branch: master Commit: 3fd15218c76cedd89496db637c7ef5369ef35a63 Date: Thu Jan 13 04:44:48 PM UTC 2022

System Information

OS: Kubuntu 21.04 - 5.11.0-38-generic CPU: Core i7-6700K @ 4GHz GPU: NVIDIA GeForce GTX 1070 GPU Driver: 4.6.0 NVIDIA 470.74

Additional Context

I started writing up a more detailed description of the problem here: https://gist.github.com/abaire/8345ff167c4d89d4ff4a3a78e87b9ae4

abaire commented 2 years ago

I think this is an interaction w/ anti-aliasing.

According to VLC the source for the video is 640x480 mpeg2.

I see the surface being created with that size but w/ AA set to NV097_SET_SURFACE_FORMAT_ANTI_ALIASING_CENTER_CORNER_2:

nv2a: Target: [COLOR @ 2dd0000] (ln) aa:1 clip:x=0,w=640,y=0,h=480
nv2a: Adding surface region at [2dd0000: 3028000)
nv2a: Create: [COLOR @ 2dd0000 (1280x480)] (ln) aa:1, clip:x=0,w=640,y=0,h=480
nv2a: [RAM->GPU] COLOR (lin) surface @ 2dd0000 (w=1280,h=480,p=5120,bpp=4)

...

nv2a: Target: [COLOR @ 2dd0000] (ln) aa:1 clip:x=0,w=640,y=0,h=480
nv2a:  Match: [COLOR @ 2dd0000 (1280x480)] (ln) aa:1, clip:x=0,w=640,y=0,h=480
nv2a:    Hit: [COLOR @ 2dd0000 (1280x480)] (ln) aa:1, clip:x=0,w=640,y=0,h=480

If I disable the width doubling in pgraph_apply_anti_aliasing_factor the videos render correctly (though obviously it breaks other things).

I'm guessing the problem is that anti-aliasing isn't actually being applied anywhere. As far as I can see the video frames are decoded someplace and then uploaded via pgraph_upload_surface_data without any obvious pgraph class interactions. Initially I assumed this would be something related to PVPE but I don't see any interactions going on there.

abaire commented 2 years ago

I think the problem is that a surface's shape is set based on whatever the anti-aliasing flag happens to be set to when it is first created, and it stays stuck in that state forever. Based on the observed behavior, it seems like the SET_SURFACE_FORMAT setting is intended to apply retroactively, so the cache needs to be broken if the values no longer match up.

abaire commented 2 years ago

Copying over some later findings from when I dug further into fixing this:

Test HW Results

Updating to capture the context:

The problem is that the game does 3D rendering to the framebuffer with anti-aliasing enabled, then switches to doing direct CPU writes. xemu creates a SurfaceBinding for the 0x97-based renders, then erroneously continues to use it for the final blit in nv2a_get_framebuffer_surface. In hardware, only AvSetDisplayMode should have an effect on the interpretation of the framebuffer.

abaire commented 2 years ago

xemu 0.7.55 results of the tests:

Screenshot_20220630_100141 Screenshot_20220630_100159 Screenshot_20220630_100203 Screenshot_20220630_100208 Screenshot_20220630_100214

theboy181 commented 2 years ago

This is caused by 0x1021 - Should be 0x1121 This is used for resolution modes (i think) and helps set AA in games.

I have some awkward notes on finding this, but have not been able to narrow down what its actually doing..

Pirates Video Fix (MOD) 00 2A 00 00 00 C7 05 30 5B 20 00 21 10 00 00 89 = 00 2A 00 00 00 C7 05 30 5B 20 00 11 21 00 00 89

abaire commented 2 years ago

There's no problem with the game code, and the behavior is more easily identifiable by looking at the pgraph commands than by reversing the game code :)

The game sets up anti-aliased rendering to the framebuffer for 3D for a draw, then later writes decompressed FMV frames directly to the framebuffer via the CPU. xemu has a bug that causes it to assume that if a 3d surface was set up pointing at the framebuffer than its (host) GL contents contain the framebuffer data and can be displayed. It has a further bug where it assumes that VRAM will be in the same format as the surface configuration, so when it sees that the guest VRAM has been modified, it reads it in as anti-aliased data, so when it's finally rendered the pitch is double what it should be.

The draft PR fixes the xemu bug, but there is a remaining issue with how xemu grabs the framebuffer in a UI thread unrelated to the guest.