Framebuffer-as-texture passthrough for 1P stage clear + VS results (#81)#154
Merged
Framebuffer-as-texture passthrough for 1P stage clear + VS results (#81)#154
Conversation
SSB64 uses an N64 hardware-RDP idiom on the 1P stage-clear screen and the VS match -> results-screen photo wipe: memcpy from gSYSchedulerCurrentFramebuffer into a CPU buffer (a wallpaper sprite or the segment-1 photo heap) and sample it as a texture by a downstream sprite/mesh. On the port the GPU rasterizer never writes to gSYSchedulerCurrentFramebuffer, so both copies read zeros and the result is a solid-black photo. Issue #81 was the open VS-side bug; issue #57 was the earlier 1P-side fix done via CPU readback + downsample + byteswap into the wallpaper bitmap. Replaces both with a single GPU passthrough: register the consumer's CPU range as a mirror of an internal snapshot FB (mGameFb -> snapshot via libultraship's CopyFramebuffer once at capture time), and let Fast3D's ImportTexture bind the snapshot FB directly via SelectTextureFb when the consumer's gsDPSetTextureImage hits the registered range -- no CPU readback, no per-pixel byteswap, no texture-cache eviction, full mCurDimensions resolution. Components: * libultraship (submodule bump): RegisterFbTexture sub-rect API + per-stripe UV slicing in the ImportTexture hook + per-tile-slot UV transform applied in GfxSpTri1's VBO emit + Metal CopyFramebuffer standalone path for between-frames calls. Generalizes Kenix3#1046 (which only handled single-tile substitution) to the common case of N>=1 LoadBlock cycles required by N64's 4 KB TMEM. * decomp (submodule bump): replaces the gSYSchedulerCurrentFramebuffer memcpy in sc1pstageclear.c (per-stripe registration of the wallpaper sprite's bitmap array) and lbtransition.c (single-range registration of the 300x220 photo heap; libultraship slices the 44 5-row LoadBlocks the runtime sprite renderer emits). * port/bridge/framebuffer_capture.{h,cpp}: rewrite around the new snapshot-FB + register-range model. port_capture_set_force_render_to_fb unchanged; old port_capture_game_framebuffer / port_get_captured_framebuffer CPU-readback API removed (no remaining consumers). * port/bridge/lbreloc_bridge.cpp: hook port_capture_release_all into lbRelocInitSetup so bump-reset heap reuse can't leave stale pointer-keyed registrations aliasing fresh assets onto the snapshot FB. Same shape as portResetStructFixups / portResetPackedDisplayListCache. Closes #81. Supersedes the issue #57 CPU-readback fix. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.
Summary
RegisterFbTexture(base, sizeBytes, fbId, u0, v0, u1, v1)) that handles N64's universal multi-tile sampling pattern (TMEM is 4 KB so any FB-sized region must be tiled into N LoadBlock cycles). The hook derives the per-stripe UV slice automatically from the offset + the prior LoadBlock'sline_size_bytes/size_bytesmetadata.mCurDimensions(full GPU resolution) instead of being downsampled to native 320×240, with no per-pixel byteswap or texture-cache eviction.What changed
libultraship (submodule bump →
JRickey/libultraship#ssb64@a51ece10)Interpreter::RegisterFbTexture(base, sizeBytes, fbId, u0, v0, u1, v1)+UnregisterFbTexture+ClearFbTextures. Sub-rect (rather than 1:1) substitution generalizes Kenix3#1046.mFbUvTransform[i]. Reset to identity on miss so a previous hit's slice never leaks.GfxSpTri1applies the per-tile-slot UV transform when emitting per-vertex normalized UVs and the clamp-coord pair into the VBO. Identity = no behavior change for non-FB textures (every other draw in the suite).gfx_metal.cpp::CopyFramebuffer: standalone path for between-frames callers —EndFramenulls every FB's command buffer/encoder, so the existing path silently no-op'd when invoked from a game-thread task. Standalone allocates its own command buffer + blit encoder +waitUntilCompleted, modeled afterReadFramebufferToCPU.decomp (submodule bump →
JRickey/ssb-decomp-re#port-patches@73d6c0d07)sc1PStageClearCopyFramebufToWallpaper: replaces the per-pixel memcpy + byteswap +portTextureCacheDeleteRangewith a per-stripeport_capture_register_fb_for_subrectloop over the wallpaper sprite's bitmap array.lbTransitionSetupTransition: replaces the bottom-upgSYSchedulerCurrentFramebuffermemcpy with oneregister_fb_for_subrect(photo_heap, 132 KB, ...)covering the 300×220 photo region; libultraship slices the 44 5-row LoadBlocks the runtime sprite renderer emits.port (this PR)
port/bridge/framebuffer_capture.{h,cpp}rewritten around snapshot-FB + register-range model. Old CPU-readback API (port_capture_game_framebuffer/port_get_captured_framebuffer) removed — no remaining consumers.port/bridge/lbreloc_bridge.cpp::lbRelocInitSetup: hookport_capture_release_allso bump-reset heap reuse can't leave stale pointer-keyed registrations aliasing fresh assets onto the snapshot FB. Same shape asportResetStructFixups/portResetPackedDisplayListCache.Test plan
syUtilsRandIntRange(11))Closes #81.
🤖 Generated with Claude Code