feat(ps1): Phase 2 GPU/GTE capture hooks (#418, #419)#581
Conversation
Add CaptureBuffer, RipperHooks, GpuCommandParser, and GTE matrix dedupe. Wire capture arming to the worker thread; stub core emits sample primitives when armed. Unit tests cover GP0 decoding, matrix hashing, and capture flow. Co-authored-by: Cursor <cursoragent@cursor.com>
|
Warning Rate limit exceeded
You’ve run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (6)
📝 WalkthroughWalkthroughThis PR implements Phase 2 of PS1 Runtime Geometry Extraction: a complete GPU primitive and GTE matrix capture infrastructure. It adds data structures for GPU commands, a GP0 parser, matrix deduplication, a per-frame capture buffer, emulator hook interfaces, and worker/manager integration to arm, record, and finalize frame capture with CSV output. ChangesPS1 Capture Pipeline Implementation
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related issues
Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6e498c2cf8
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Parse E1–E6 as single-word drawing-environment commands, fix gouraud triangle/quad vertex layout per psx-spx, and add 0xC0 to VRAM copy skip sizes. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
Actionable comments posted: 8
🧹 Nitpick comments (1)
src/PS1/runtime/CaptureBuffer.h (1)
18-19: ⚡ Quick winUse const reference parameters for non-trivial types.
addMatrixandaddPrimtake their arguments by value, whileaddDrawModecorrectly usesconst &.MatrixRecordandPrimRecordare non-trivial types (containing matrices, vertices, colors, etc.), so passing by const reference is both more efficient and consistent with C++ best practices.♻️ Proposed fix for consistent pass-by-reference
- uint32_t addMatrix(MatrixRecord matrix); - void addPrim(PrimRecord prim); + uint32_t addMatrix(const MatrixRecord &matrix); + void addPrim(const PrimRecord &prim); void addDrawMode(const DrawModeRecord &mode);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/PS1/runtime/CaptureBuffer.h` around lines 18 - 19, Change the addMatrix and addPrim parameter types to take const references instead of by-value: replace MatrixRecord matrix with const MatrixRecord& and PrimRecord prim with const PrimRecord& in the CaptureBuffer.h declarations (and update the corresponding definitions/implementations in the .cpp to match). Keep addMatrix's return type (uint32_t) unchanged, and scan/adjust all call sites to ensure they still compile with the new const-reference signatures (no ownership/modify semantics assumed); this aligns with addDrawMode's const& usage and avoids expensive copies of MatrixRecord and PrimRecord.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/PS1/runtime/CaptureBuffer.cpp`:
- Around line 29-31: The current dedup uses only
m_matrixIndexByHash.constFind(matrix.hash) and returns found.value() on a hash
match, risking collision-based mis-association; change the map to store a
list/vector of matrix IDs for each hash (e.g. m_matrixIndexByHash: hash ->
std::vector<MatrixId>), then when you get the bucket for matrix.hash iterate its
IDs, fetch each candidate matrix and compare full equality (use the existing
matrix equality routine or implement a strict CompareMatrices function) and only
return the candidate ID when a true equality match is found; if none match,
insert the new matrix ID into the vector for that hash so future lookups
consider it.
In `@src/PS1/runtime/GpuCommandParser.cpp`:
- Around line 145-146: The textured-quad length check wrongly requires 13 words
causing valid 0x2C..0x2F commands to be rejected; in the GpuCommandParser code
path that handles textured quads (the branch that sets error = "textured quad:
packet too short"), change the guard that uses (index + 13 > wordCount) to
require 9 words instead (e.g., (index + 9 > wordCount)) to match the actual
number of consumed words for those packets; apply the same fix to the second
occurrence noted around the other check (lines that currently use
+13/packet-too-short for 0x2C..0x2F) so both checks use +9 and keep the existing
error message and command-range handling intact.
- Around line 28-34: The function decodeUvTpageClut currently ignores the tpage
parameter (it discards it with (void)tpage), so PrimRecord::tpage never gets
set; modify decodeUvTpageClut to extract and assign tpage from the incoming word
according to the PS1 GP0 TPAGE bit layout and adjust the clut extraction if
necessary so clut and tpage use the correct, non-overlapping bit ranges;
specifically edit decodeUvTpageClut (and any related masks/shifts) to assign
tpage from the appropriate shifted/masked bits of word instead of discarding it
so textured primitives populate PrimRecord::tpage.
- Around line 324-329: The opcode range 0x34..0x37 is missing and so those
commands are being skipped; add an explicit branch for cmd >= 0x34 && cmd <=
0x37 and invoke the appropriate triangle parser (e.g. call parseGouraudTri or
the correct triangle-parsing function used for this variant) with the same
arguments (words, wordCount, index, prim, result.error) so these opcodes produce
primitives like the 0x30..0x33 case; place this new else-if alongside the
existing parseGouraudTri/parseGouraudQuad/parseSprite branches to ensure all
opcode ranges are handled.
In `@src/PS1/runtime/PS1RipManager.cpp`:
- Around line 64-66: Replace the custom breadcrumb categories used for
capture/export with the mandated Sentry categories: change the
SentryReporter::addBreadcrumb calls around the capture flow (the one immediately
before emit frameCaptured(captureId) and the similar calls around lines 201-205)
to use "ui.action" for arm/disarm/toolbar/menu-like capture actions and
"file.export" for frame-save/export operations while preserving the existing
breadcrumb message/payload (e.g., keep "frame_saved:%1" or "capture" text);
locate the SentryReporter::addBreadcrumb invocations in PS1RipManager.cpp and
update their first argument category strings accordingly so they conform to the
coding guidelines.
- Around line 197-213: The manager clears m_captureArmed during
stop/session-stop paths but never updates the worker, so the worker can remain
armed; ensure the worker is explicitly notified when clearing the flag by either
calling PS1RipManager::armCapture(false) from the stop/session-stop code or
invoking the worker update the same way armCapture does (use
QMetaObject::invokeMethod on m_worker to call m_worker->setCaptureArmed(false)
with Qt::QueuedConnection). Update the stop/session-stop code paths to reference
m_worker and setCaptureArmed to propagate the disarm reliably.
- Around line 208-211: In armCapture() and captureFrame(), avoid capturing this
in lambdas queued to m_worker; instead copy m_worker to a local variable (e.g.,
auto worker = m_worker) and capture that pointer by value in the lambda, then
call worker->setCaptureArmed(armed) and worker->captureFrame() respectively;
this ensures the lambda dereferences a stable worker pointer even if the
PS1RipManager is destroyed before the queued call runs.
In `@src/PS1/runtime/PS1RipWorker.cpp`:
- Around line 165-173: QDir().mkpath(dir); building csvPath and creating QFile
currently emits frameCaptureReady regardless of write outcome; change the flow
in PS1RipWorker::(the function using csvPath) so that you only emit
frameCaptureReady(captureId, prims.size()) after confirming the file was
successfully opened and written (i.e., file.open(...) returned true and
file.write(...) succeeded and file.close() completed); if any step fails, do not
emit frameCaptureReady and instead log an error including captureId and csvPath
so failures are visible.
---
Nitpick comments:
In `@src/PS1/runtime/CaptureBuffer.h`:
- Around line 18-19: Change the addMatrix and addPrim parameter types to take
const references instead of by-value: replace MatrixRecord matrix with const
MatrixRecord& and PrimRecord prim with const PrimRecord& in the CaptureBuffer.h
declarations (and update the corresponding definitions/implementations in the
.cpp to match). Keep addMatrix's return type (uint32_t) unchanged, and
scan/adjust all call sites to ensure they still compile with the new
const-reference signatures (no ownership/modify semantics assumed); this aligns
with addDrawMode's const& usage and avoids expensive copies of MatrixRecord and
PrimRecord.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 40d1a1cb-d87b-4921-b882-f9fbef12ed08
📒 Files selected for processing (23)
plugins/ps1core_stub/CMakeLists.txtplugins/ps1core_stub/StubCaptureSynth.cppplugins/ps1core_stub/StubCaptureSynth.hplugins/ps1core_stub/StubEmuCore.cppsrc/PS1/CMakeLists.txtsrc/PS1/PS1_RIP_DESIGN.mdsrc/PS1/runtime/CaptureBuffer.cppsrc/PS1/runtime/CaptureBuffer.hsrc/PS1/runtime/CaptureBuffer_test.cppsrc/PS1/runtime/CaptureTypes.hsrc/PS1/runtime/EmuHooks.hsrc/PS1/runtime/GpuCommandParser.cppsrc/PS1/runtime/GpuCommandParser.hsrc/PS1/runtime/GpuCommandParser_test.cppsrc/PS1/runtime/GteCapture.cppsrc/PS1/runtime/GteCapture.hsrc/PS1/runtime/PS1RipManager.cppsrc/PS1/runtime/PS1RipManager_test.cppsrc/PS1/runtime/PS1RipWorker.cppsrc/PS1/runtime/PS1RipWorker.hsrc/PS1/runtime/RipperHooks.cppsrc/PS1/runtime/RipperHooks.htests/CMakeLists.txt
Verify matrix equality on hash hits, decode TPAGE from texcoord words, fix textured quad/gouraud packet sizes, parse 0x34–0x37 opcodes, sync worker capture armed on stop, use standard Sentry categories, and only emit frameCaptureReady after a successful CSV write. Co-authored-by: Cursor <cursoragent@cursor.com>
|



Summary
CaptureTypes,GpuCommandParser,CaptureBuffer, andRipperHooksfor GP0 primitive and GTE matrix capture (PS1: GPU GP0/GP1 command interception layer #418, PS1: GTE matrix-stack capture for world/view/projection inversion #419).armCapture/captureFrameto the worker thread with an atomic armed flag; disarmed hooks are no-ops.captureFrame.Test plan
UnitTests --gtest_filter="GpuCommandParser*:CaptureBuffer*:GteCapture*:PS1RipManager*"(Linux, offscreen)unit-tests-linuxwithENABLE_PS1_RIP=ONCloses #418, #419.
Made with Cursor
Summary by CodeRabbit
Release Notes
New Features
Tests