Skip to content

fix(gum): create the VFPU context in whichever gum function runs first - #207

Merged
sajattack merged 1 commit into
overdrivenpotato:masterfrom
ItsNoHax:fix-gum-cold-vfpu-context
Sep 2, 2026
Merged

sajattack merged 1 commit into
overdrivenpotato:masterfrom
ItsNoHax:fix-gum-cold-vfpu-context

Conversation

@ItsNoHax

@ItsNoHax ItsNoHax commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

Fixes #189. Rebased onto master after #206; the overlap in ci/tests is resolved below.

The bug

VFPU_CONTEXT is created by sceGumLoadIdentity and sceGumLoadMatrix only. Every other gum
function reaches it through get_context_unchecked, which resolves a None context with
core::intrinsics::unreachable. Opening with any of them is undefined behaviour, and on a
debug build it traps:

E CPU: CPU exception: break instruction hit at 0881ba7c [ci_tests.text+17a7c]
psp::sys::gum::get_context_unchecked [ci_tests.text+17a0c]
sceGumMatrixMode [ci_tests.text+17b4c]

That is the report's sceGumMatrixMode failure exactly. It is easy to hit because
sceGumMatrixMode is what most setup code calls first — as in the issue's tutorial, which selects
a matrix mode before loading anything into it.

The fix

get_context_unchecked becomes get_context, which creates the context on demand, and the two
functions that were doing that inline now go through it too. Creating a Context is a zeroed
matrix set with an empty saved mask, so doing it from any entry point is the same work
sceGumLoadIdentity already did — the choice of which function got to do it was arbitrary.

Tests

One case added to the gum tests from #206: open with sceGumMatrixMode on a cold context, then
build a translation and read it back with sceGumStoreMatrix to show the context it created is
usable. No rendering, so it needs no GU.

Only the first gum call in a process is cold, so there is one case to be had and it runs before the
rest of the module. That also lets reset drop the leading sceGumLoadIdentity it was carrying
purely to warm the context up — and the comment on it, which pointed at this issue and is now
false. With reset opening on a mode switch, the cold path is exercised by the suite's ordinary
first call rather than by a special case.

Before (master, 13d5bf3) the run stops at the first reset and never reaches a final token, so CI
fails on the missing FINAL_SUCCESS:

STARTING_TESTS
[PASS]: (blank_screenshot) Equal!
[PASS]: (embedded_graphics_triangle) Equal!

After — all 51 cases pass, including #206's four, which confirms the simplified reset still sets
the matrix up the way they expect:

[PASS]: (gum_cold_matrix_mode) (1.0, 2.0, 3.0) == (1.0, 2.0, 3.0)
[PASS]: (gum_push_pop_restores_translation) (1.0, 2.0, 3.0) == (1.0, 2.0, 3.0)
[PASS]: (gum_push_pop_nested_restores_translation) (1.0, 2.0, 3.0) == (1.0, 2.0, 3.0)
[PASS]: (gum_push_pop_survives_matrix_write) (1.0, 2.0, 3.0) == (1.0, 2.0, 3.0)
[PASS]: (gum_push_leaves_current_matrix_alone) (1.0, 2.0, 3.0) == (1.0, 2.0, 3.0)
...
FINAL_SUCCESS

Run locally with the same invocation as .github/workflows/pipeline.yml. cargo fmt is clean for
both psp and ci/tests.

@sajattack
sajattack force-pushed the fix-gum-cold-vfpu-context branch from 06745f3 to f1c949a Compare August 30, 2026 23:43
@sajattack

Copy link
Copy Markdown
Collaborator

@ItsNoHax plz resolve conflicts.

Only `sceGumLoadIdentity` and `sceGumLoadMatrix` created `VFPU_CONTEXT`. Every
other gum function went through `get_context_unchecked`, which resolved a `None`
context with `core::intrinsics::unreachable`. Opening with any of them -- most
naturally `sceGumMatrixMode`, which is the first call in most setup code -- is
therefore undefined behaviour, and in practice traps:

    E CPU: CPU exception: break instruction hit at 0881ba7c
    psp::sys::gum::get_context_unchecked
    sceGumMatrixMode

Replace the helper with `get_context`, which creates the context on demand, and
use it everywhere including the two functions that previously did this inline.
The context is a zeroed matrix set with no saved registers, so creating it from
any entry point is the same work `sceGumLoadIdentity` was already doing.

Adds a case to the gum tests that opens with `sceGumMatrixMode` and reads the
resulting matrix back. Only the first gum call in a process is cold, so it runs
before the rest of the module. `reset` loses the `sceGumLoadIdentity` it carried
purely to warm the context up, along with the comment pointing at this issue.
Against master the run stops at the first `reset` with no final token; with this
change the suite ends FINAL_SUCCESS.

Fixes overdrivenpotato#189.
@ItsNoHax
ItsNoHax force-pushed the fix-gum-cold-vfpu-context branch from f1c949a to fc6ace5 Compare September 2, 2026 16:10
@ItsNoHax

ItsNoHax commented Sep 2, 2026

Copy link
Copy Markdown
Contributor Author

done @sajattack

@sajattack
sajattack merged commit 6098b80 into overdrivenpotato:master Sep 2, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

VFPU_CONTEXT might not be initialized for further gum calls

2 participants