xemu
xemu copied to clipboard
Fatal Frame: Camera blue ring missing
Title
https://xemu.app/titles/54430004/#Fatal-Frame
TC-004
Bug Description
Camera works for everything, but environmental objects no Blue ring over said objects.
Expected Behavior
Camera should light up with a blue ring when over the objects in the picture
xemu Version
v0.8.47
System Information
OS: Windows 11 GPU: GTX 1070 TI CPU: Ryzen 5 2600X Xemu Version: V0.8.47
Additional Context
No response
Steps to reproduce.
- Start a new game.
- Complete the intro tutorial mission (Note there is another camera bug where combat ghost can only be hit while it is standing near the railing over the doorway. In the normal game, it can be hit anywhere in the room.)
- Start night 1, enter the fireplace room, head upstairs and try and use the doorway.
- Walk back downstairs, a cutscene will play directing you to take a picture of the folding divider.
- Attempt to take the picture of it, the camera does not light up.
Capturing some discussion from Discord: it appears that this is more than just a rendering issue, pressing the right trigger does not trigger the expected action even when aiming at a target that is expected to work.
yeah that's right it doesn't work at all, and it seems to happen in both 1 and 2, but we can't test 2 because we can't even get past the title screen
The blue ring works but only flash for 2 frames then stop working until you but the camera away.
https://github.com/user-attachments/assets/59434000-3f76-43d3-a60e-9135abfc55bc
managed to get through the door but took a few tries
the title's a Little Misleading considering some Blue Rings do appear in certain photo spots
Also doesn't work
This appears to have been resolved as of version v0.8.62. All the capture circles are appearing as they would on normal hardware so far halfway into night 1. Will be testing the rest of the game tomorrow to see if any other issues come up.
During my testing, I discovered that if you are using the Microsoft provided video drives for AMD video cards (Currently version 32.0.13031.2000), the blue circle does not appear.
However if you install the latest AMD driver from AMD themselves (Current version 32.0.21001.9024) The circles will appear. So the version of video drivers you have installed do matter.
This bug only affects nivda Gpus
This does not only affect nvidia GPUs. On the Steam Deck (which uses AMD), it also does not work as of 0.8.71 (tested both openGL and vulkan backends).
This bug isn't reproducible on my AMD RX 6600 GPU drivers 25.6.1. I'm on Windows 11 24H2. I tested it using the latest Xemu release 0.8.71.
Bug reproduced v0.8.94 with NVIDIA GeForce RTX 3060 driver 32.0.14.7652 on Windows 11 24H2 with OpenGL and Vulkan backends.
This commit fixes the issue on my nvidia GPU on both opengl and vulkan: https://github.com/jet082/xemu/commit/5c8c93cafe37eef461f68e6ef408d8345a57d8d3
I won't create a PR, since I'll be honest - this was more a product of vibe coding than figuring things out myself. However, I've compiled this and it does fix the issue.
That said, since it's only a few changes to a few lines of code in a single file, perhaps it might direct people more knowledgeable than me to the cause of the problem.
This makes me game playable/beatable (I assume).
Interesting find, thanks for looking into it!
I won't create a PR, since I'll be honest - this was more a product of vibe coding than figuring things out myself. However, I've compiled this and it does fix the issue.
That said, since it's only a few changes to a few lines of code in a single file, perhaps it might direct people more knowledgeable than me to the cause of the problem.
While a couple of the changes look plausible, overall my impression is that this is mostly LLM hallucination that happens to make things work by luck in some specific situations.
- The change to the assert seems plausible, though I don't think anybody has reported that assert firing so it's probably moot as the DMA cap is likely set to something sufficiently large that it'd never come into play (many DirectX configured DMA limits are just set to top of memory)
- Getting an actual timestamp seems like a good idea, though I'm not familiar enough with qemu clocks or with the PGRAPH report behavior to know if that's the appropriate clock. It'd be good to write some tests for this behavior and examine what HW actually does here. I would guess based on the fact that we don't have tons of similar issues that either the reports are rarely used or the timestamp field isn't generally looked at/meaningful.
Other than that, this does not strike me as a correct change unless the model has access to some documentation explaining the report format the the original authors did not.
- If
doneis really a flag indicating that something is completed, setting it to 1 seems like it could be a reasonable thing to do, but I'd be surprised if it wasn't set to 0 because the original author found it to be 0 when testing on HW and never found a case where it was set to anything else. It'd be good to write some tests to see if HW ever sets it to anything. - My suspicion is that replacing the actual report result with the low 32 bits of the timestamp (likely a much much larger number most of the time) just happens to make this game think that the entire ring has passed the depth comparison.
If you're willing to do a bit more digging, it'd be great to see if we could narrow down exactly what "fixes" the behavior.
- If you revert to the original state and just hardcode
resultto some very large number, does that make the game work? - If not, does just changing
doneto1(leaving result alone) make it work?
If neither of those fixes the problem, does writing the result to report_data[0] make it work? I guess it's possible that the original author misinterpreted the report struct and either the LLM found documentation to that effect or randomly decided that the report is probably the most important field and might come first. Generally I believe the majority of the old reverse engineering work was done with a lot of care and tends to be correct, so I'm dubious this is the case.
Actually, the LLM's first suggestion was to set done to 1 and that did nothing.
Then it added the uint64_t timestamp = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); line and that did nothing.
That said - you are correct. Setting result to 4294967295 (the max value for uint32_t) and leaving everything else as-is fixes the issue.
I simply hard-coded the result here:
void pgraph_write_zpass_pixel_cnt_report(NV2AState *d, uint32_t parameter,
uint32_t result)
{
PGRAPHState *pg = &d->pgraph;
uint64_t timestamp = 0x0011223344556677; /* FIXME: Update timestamp?! */
uint32_t done = 0; // FIXME: Check
hwaddr report_dma_len;
uint8_t *report_data =
(uint8_t *)nv_dma_map(d, pg->dma_report, &report_dma_len);
hwaddr offset = GET_MASK(parameter, NV097_GET_REPORT_OFFSET);
assert(offset < report_dma_len);
report_data += offset;
stq_le_p((uint64_t *)&report_data[0], timestamp);
stl_le_p((uint32_t *)&report_data[8], 4294967295);
stl_le_p((uint32_t *)&report_data[12], done);
NV2A_DPRINTF("Report result %d @%" HWADDR_PRIx, 4294967295, offset);
}
Cool, thanks for checking. I suspect that the underlying problem here is due to depth buffer precision and will be fixed by @coldhex 's #2240 .
The change here just lies and says that all pixels passed the depth test, which must cause the game logic to decide to display the blue circle.
It'd be interesting to test the build from that PR so that this issue could be tagged as being fixed there if appropriate.
Edit: I added a test https://github.com/abaire/nxdk_pgraph_tests/blob/main/src/tests/zpass_pixel_count_tests.cpp just to make sure there wasn't something obviously wrong with a simple case. xemu matches HW behavior on M3 mac apart from the timestamp which is optional at the DirectX level and doesn't seem to be causing this issue. https://abaire.github.io/nxdk_pgraph_tests_golden_results/results/ZPass_pixel_count/index.html
Unfortunately, I can confirm that https://github.com/xemu-project/xemu/pull/2240 does not seem to fix this issue.
Very interesting. If you can get a renderdoc capture and a PGRAPH trace (see https://github.com/abaire/nxdk_pgraph_tests/wiki/xemu-debugging-with-RenderDoc ) maybe we'll find that it's doing something unusual during the draw.
The zpass test that I added to validate the current implementation is very basic so it's possible that there's some additional behavior that isn't emulated correctly (though generally I'd expect that interesting things like clipping would result in overcounting rather than undercounting).
It's also possible that #2240 makes things better but does not exactly match HW and the game has low tolerance for deviation.
Thanks again for your help debugging this!
Another hack that alone fixes this is to hardcode gl_PointSize=1.0 in vsh.c https://github.com/xemu-project/xemu/blob/c67843384031e0edaab3db91ec496464b931789b/hw/xbox/nv2a/pgraph/glsl/vsh.c#L396
The zpass pixel count query is used when rendering a single point (a visibility test point?), but gl_PointSize is set to 0.0 by Xemu.
I've bookmarked some EIDs in this RenderDoc capture:
fatal_frame_point_size_issue.7z.zip
The glDrawArrays(1) call renders a single point which has v1 vertex attribute value (0.0, 0.0, 1.0, 1.0). This value lingers from a previous glDrawElements(50) draw as an inline value, but there it doesn't seem to have anything to do with point size. The inline value got stored by:
https://github.com/xemu-project/xemu/blob/c67843384031e0edaab3db91ec496464b931789b/hw/xbox/nv2a/pgraph/gl/vertex.c#L192
How do you find the xemu/hw/xbox/nv2a/pgraph/gl/vertex.c line of code in the emulator I can't find it
Very interesting. If you can get a renderdoc capture and a PGRAPH trace (see https://github.com/abaire/nxdk_pgraph_tests/wiki/xemu-debugging-with-RenderDoc ) maybe we'll find that it's doing something unusual during the draw.
The zpass test that I added to validate the current implementation is very basic so it's possible that there's some additional behavior that isn't emulated correctly (though generally I'd expect that interesting things like clipping would result in overcounting rather than undercounting).
It's also possible that #2240 makes things better but does not exactly match HW and the game has low tolerance for deviation.
Thanks again for your help debugging this!
It SOUNDS like @coldhex provided what you were looking for. If you need me to do anything as well, let me know. Happy to help however I can.
v0.8.101 v0.8.102 gtx 1060 (566.36), i5 12400f, win 10 (21H2) the game is unbeatable right now due to this bug
Another hack that alone fixes this is to hardcode gl_PointSize=1.0 in vsh.c
That's super interesting as we already know xemu's implementation of point size does not match HW (at least on NVIDIA/GL): https://abaire.github.io/xemu-nxdk_pgraph_tests_results/compare/xemu-0.8.66-master-8667193001e4d9f20f9d5237d941f6f0ff3a2672/Linux_x86_64/gl_NVIDIA_Corporation_NVIDIA_GeForce_GTX_1070-PCIe-SSE2%3Agslv_4.00_NVIDIA_via_Cg_compiler/Point_size.html
On M3 macOS it's closer, and setting a guest point size of 0 still (correctly) renders a pixel: https://abaire.github.io/xemu-nxdk_pgraph_tests_results/compare/xemu-0.8.96-master-98a3974290618086de5132d27c8ede736f9d93ea/Darwin_arm64/gl_Apple_Apple_M3_Max%3Agslv_4.10/Point_size.html
It'd be good to get a PGRAPH trace of this so we can see what the game is trying to do wrt. point size and rendering, but it plausible that that's the real issue here.
I'll extend the zpass pixel test in the meantime to verify what happens with very tiny point sizes; excellent find @coldhex !
@abaire let me know if you want it with the hack enable as not possible to get it work without so the capture and pgraph trace doesn't have ring active.
Renderdoc https://drive.google.com/file/d/1ITB-6CpTySo7b_-0KN7DMFsVVEWThpkn/view?usp=sharing
Trace
I don't think the hack will materially change the pgraph trace so that should be fine. I'll check in detail in the next day or two. Thanks for grabbing it!
That's super interesting as we already know xemu's implementation of point size does not match HW (at least on NVIDIA/GL):
Probably why it only effect NVIDIA GPU and not amd.
In that trace I only see point size being set to 1.0 (8, it's 6.3 fixed point).
It could be that the probe is done infrequently and the trace just missed it? We could add a printf around https://github.com/xemu-project/xemu/blob/ff1617d66468abd927f55f7082b3f53610ff26a4/hw/xbox/nv2a/pgraph/glsl/vsh.c#L133 that triggers if the point size is 0 to see if that's the case.
In that trace I only see point size being set to 1.0 (8, it's 6.3 fixed point).
I think in Xemu NV097_SET_POINT_SIZE only affects fixed function vertex shading, but this game draws the point using programmable vertex shading.
Another hack that alone fixes this is to hardcode gl_PointSize=1.0 in vsh.c
That's super interesting as we already know xemu's implementation of point size does not match HW (at least on NVIDIA/GL): https://abaire.github.io/xemu-nxdk_pgraph_tests_results/compare/xemu-0.8.66-master-8667193001e4d9f20f9d5237d941f6f0ff3a2672/Linux_x86_64/gl_NVIDIA_Corporation_NVIDIA_GeForce_GTX_1070-PCIe-SSE2%3Agslv_4.00_NVIDIA_via_Cg_compiler/Point_size.html
On M3 macOS it's closer, and setting a guest point size of 0 still (correctly) renders a pixel: https://abaire.github.io/xemu-nxdk_pgraph_tests_results/compare/xemu-0.8.96-master-98a3974290618086de5132d27c8ede736f9d93ea/Darwin_arm64/gl_Apple_Apple_M3_Max%3Agslv_4.10/Point_size.html
It'd be good to get a PGRAPH trace of this so we can see what the game is trying to do wrt. point size and rendering, but it plausible that that's the real issue here.
I'll extend the zpass pixel test in the meantime to verify what happens with very tiny point sizes; excellent find @coldhex !
Worth noting that this is not sufficient alone to get to where we need to go as per https://github.com/xemu-project/xemu/issues/966#issuecomment-3315885297
Setting result to max value in pgraph_write_zpass_pixel_cnt_report in hw/xbox/nv2a/pgraph/pgraph.c results in working upscaling, whereas setting gl_PointSize=1.0 in vsh.c (alone) does not.
This is a hacky workaround, of course.