Repository navigation
Fix Evo's visor, camera shaking, overscan drawing, black TV screens, terminal trails, dome reflections; render to RDRAM only when needed - #7
Open
filipverlee wants to merge 7 commits into
Conversation
render_rig_instance_xlu pushed joint 1 a second time before joint 3, so rig part 9 (Evo's red visor, and Dan's part 9) got the joint 1 offset twice and floated a head-height above the model in the intro cockpit. Push joint 3 from the joint 1 level restored by the preceding pop and drop the now-unneeded pop, keeping the stack balanced. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01421MLZmp5pdkDs5MnD9xmK
draw_rectangle left the scissor at full screen and draw_intro_logo_model always used the full frame, so in 4:3 the 3D geometry drawn afterwards (and the title-screen ship) spilled into the 8px overscan border. The original restores 8, 8, w - 8, h - 8; use set_default_frame_scissor, which keeps the full frame in widescreen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01421MLZmp5pdkDs5MnD9xmK
With SSSV_PATCH_DISABLE_LOD/DISABLE_FAR_AWAY_CULL the visibility classifiers returned VISIBILITY_VISIBLE for everything in a loaded cell. The game acts on those results beyond rendering, and the camera never settles: at rest it cycles +1.5, +1.6, +1.8, -3.9 every 6-7 frames (139 direction reversals in 900 frames at the level 1 start), which shows as big camera jumps while moving. On the original (Mupen64Plus) the camera is still at rest. Split the overrides: DISABLE_LOD now only forces gLodDetailState = 0 for objects the game itself classified as visible, and the far-away cull override (unchanged behaviour) is off by default. Same spot after the change: 0 reversals, camera at rest. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01421MLZmp5pdkDs5MnD9xmK
RT64 writes every frame back to RDRAM by default. That readback scales with the output resolution: at 2x on an Intel Iris Plus 640 the gameplay attract demo ran at 24.9 fps with 357 of 870 frames over 40 ms. The game only reads rendered frames in two places: the title/attract screen grab for the transition into the game, and the previous frame copied into the pause-menu thumbnail (gInitialisationState 1-3). Emit gEXSetRenderToRAM(0) for gameplay frames, except while the pause menu is being entered, and gEXSetRenderToRAM(1) for intro/title frames. Same demo afterwards: 29.8 fps, 87 frames over 40 ms (same as turning render-to-RAM off globally). Title transition and pause thumbnail checked on screen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01421MLZmp5pdkDs5MnD9xmK
The TV screen model draws its static as a white quad with prim alpha 0x80 and G_AC_DITHER, in 2-cycle mode with G_CC_PASS2 / G_CC_PRIMITIVE. The alpha compare uses the first cycle's alpha, which is COMBINED there. On hardware that still holds the previous pixel's output, so about half the pixels pass. RT64 starts COMBINED at 0, every pixel fails and the monitors in Something Fishy stay black. Rewrite that combine command in the loaded model to use PRIMITIVE in both cycles, only when segment 1 holds the TV model. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Owner
|
First of all, thank you so much for working on this! |
Author
I try to get it running on a Linux box for myself and the kiddos ;) and it would be stupid to not share my results. So hopefully everyone can enjoy the results. |
render_terminal_background_frame cleared the frame to opaque black every frame in phase 1. The original only draws a translucent black rectangle over the previous frame there, so the glyph text, DNA and animal leave fading trails behind them, which is the noise effect on the stats screen. Only clear in widescreen, where the uncleared frame also keeps a native-width copy of the stat text visible. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
render_ship_window_projection_replay is how the game draws reflections in the dome glass (e.g. the glass walls in Snow Joke): near a reflecting wall it sets gCameraVisibilityMask[6] and replays the frame's object lists under a mirror matrix multiplied into the projection, before the world geometry, so the mirrored objects show through the translucent glass. The recomp had replaced it with an empty function and removed the matching mirrored visibility checks. Restore both. The translucent object list now starts with a projection reload (the XLU state barrier), which would drop the mirror matrix, so the replay runs that list from the end of the barrier and loads the world modelview itself. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Three fixes and one performance change, compared against the original game in Mupen64Plus-Next.
Evo's visor floating above his head (
modelview_stack_patches.c)In the new-game cockpit cutscene, Evo's red visor floats a head-height above him (and a small piece floats above Dan).
render_rig_instance_xlupushes joint 1 a second time before joint 3, so rig part 9 gets the joint 1 offset twice. For Evo in that scene, joint 1 is about (4, 0, 144) and joint 3 about (-4, 0, 80), which puts the visor around z=368 instead of z=224, next to the head (part 8, at joint 1 + joint 2).The fix pushes joint 3 from the joint 1 level that the previous pop already restored, and drops one pop so the stack stays balanced. The matrix stack depth when part 9 is drawn goes from 5 to 4, and the caller's depth is unchanged. The visor now sits on Evo's face, as on the original.
3D geometry drawing into the 8px border in 4:3 (
graphics_patches.c)draw_rectangleleft the scissor at full screen after drawing. The original restores8, 8, w - 8, h - 8, so geometry drawn after any rectangle spilled into the overscan border.draw_intro_logo_modelalways used the full frame, so the title-screen ship flew over the border. The original uses8, 8, 312, 232.Both now call
set_default_frame_scissor, which gives the TV-safe rect in 4:3 and the full frame in widescreen. Widescreen keeps its previous behaviour.Camera shaking in levels (
lod_patches.c,sssv_patch_types.h)With
SSSV_PATCH_DISABLE_LOD/SSSV_PATCH_DISABLE_FAR_AWAY_CULLon, both visibility classifiers returnVISIBILITY_VISIBLEfor everything in a loaded cell. The game acts on those results beyond drawing, and the camera never settles. I logged the camera eye (gCameraEyeWorldX/Z) once per frame at the level 1 start. At rest it cycles +1.5, +1.6, +1.8, −3.9 every 6–7 frames, which gave 139 direction reversals in 900 frames. While moving, this shows up as large camera jumps. In Mupen64Plus (camera RAM read through RetroArch'sREAD_CORE_RAM), the camera is completely still at the same spot.The fix splits the two overrides:
DISABLE_LODnow only forcesgLodDetailState = 0for objects the game itself classified as visible, so the extra detail stays.DISABLE_FAR_AWAY_CULLkeeps its old behaviour, but is now off by default.At the same spot afterwards: 0 reversals, and the camera stays at rest, also after walking and turning.
The same override also caused the black levels from #3 (only bubbles, stars and the HUD visible). It showed up with aspect ratio Expand. Sting in the Tail is black from the start with the v0.2.0
lod_patches.cand renders normally with this change. Something Fishy stays visible while swimming to the monitor and over the wall. Details: #3 (comment)Fixes #3
Performance: only render to RDRAM when the game reads the frame back (
graphics_patches.c)RT64 writes every frame back to RDRAM by default, and that readback scales with the output resolution. At 2x on an Intel Iris Plus 640, the gameplay attract demo ran at 24.9 fps, with 357 of 870 frames slower than 40 ms. That shows up as uneven frame pacing and a juddering camera.
The game reads rendered frames back in only two places:
func_80298F1C);func_803925D0whilegInitialisationStatecounts 1–3).The patch emits
gEXSetRenderToRAM(0)for gameplay frames, except while the pause menu is being entered, andgEXSetRenderToRAM(1)for intro/title frames. On the same demo afterwards: 29.8 fps, with 87 frames over 40 ms. That's the same as switching render-to-RAM off globally. I checked the title transition (tile break-up) and the pause thumbnail on screen.Black TV screens in Something Fishy (
graphics_patches.c)On the original, the wall monitors in Something Fishy show TV static. In the recomp they're black. The TV screen model (
D_01017AA0) draws a black quad and then a white quad with prim alpha 0x80 andG_AC_DITHER(from the low bits of itsSetOtherMode_Lrender-mode word), in 2-cycle mode withG_CC_PASS2, G_CC_PRIMITIVE. The alpha compare uses the first cycle's alpha, which isCOMBINEDthere. On hardware that still holds the previous pixel's output (about 0x80), so about half the pixels pass the dither compare at random. RT64 startsCOMBINEDat 0, so every pixel fails and only the black quad is left.load_segmentsnow rewrites that one combine command in the loaded model toG_CC_PRIMITIVE, G_CC_PRIMITIVE. It only does this when segment 1 holds the TV model: the command must match the original and must follow the dithered render mode. Afterwards the monitors show animated static (about half the pixels white, most of them changing every frame), like the original in Mupen64Plus-Next.Missing trails on the species terminal screen (
overlay_context_patches.c)On the species/stats terminal (touching a TV screen, or the new-animal cutscene), the original leaves fading trails behind the scrolling glyph text, the DNA helix and the animal. That's the "noise" from #4. In phase 1,
render_terminal_background_framecleared the frame to opaque black every frame. The original only draws its translucent black rectangle (alpha 100) over the previous frame there. The clear now only happens in widescreen. Without it, a native-width copy of the stat text stays visible next to the widened layout. In 4:3 (Original and 2x) the screen now looks like the original in Mupen64Plus-Next. Widescreen is unchanged.Reflections in the dome glass (
ship_window_patches.c,lod_patches.c,graphics_patches.c)In Snow Joke the glass walls at the edge of the dome reflect the player (#4). Despite its name,
render_ship_window_projection_replayis how the game draws this. Near a reflecting wall, the camera code setsgCameraVisibilityMask[6](bit 0: z mirror, bit 1: x mirror, plus the plane offset). The game then replays the frame's opaque and translucent object lists under a mirror matrix multiplied into the projection, before the world geometry is drawn, so the mirrored objects only show through the translucent glass. The classifiers also count an object as visible when its mirror image is. Commit 108d986 had replaced the replay with an empty function and removed those visibility checks.Both are restored as in the decomp. The mirror matrices come from the existing
mtx_patches.cbuilders, which give the same values as the original. One adjustment: the translucent object list now starts with a projection reload (SSSV_PATCH_SHIP_WINDOW_XLU_STATE_BARRIER), which would drop the mirror matrix. Sosetup_world_perspective_6AB090records where that state ends, and the replay runs the list from there and loads the world modelview itself.Checked at the reporter's spot in Snow Joke (start, go right to the glass), in 4:3 and Expand, against Mupen64Plus-Next with ParaLLEl-RDP and with GlideN64 (both show the reflection). Level 1 and Something Fishy look unchanged. Not checked: the x-mirror case (I don't know a wall that uses it) and frame interpolation above 30 fps with the replayed objects.
Testing
validate.yml(patches built with libdragon's mips64-elf-gcc), US 1.0 ROM.D_803F2AA3), in 4:3 at Original/2x and in Expand, and compared against Mupen64Plus-Next with the same RAM write.render_rig_instance_xlu(pose banks 0 and 2) were not checked on screen.🤖 Generated with Claude Code
https://claude.ai/code/session_01421MLZmp5pdkDs5MnD9xmK