Skip to content

feat(agc): trace shader failures through draw submission batches - #5431

Merged
adriadam10 merged 1 commit into
boykopovar:mainfrom
sktharunbalaji13-cmd:feat/shader-draw-submission-correlation-5166-fresh
Oct 24, 2026
Merged

adriadam10 merged 1 commit into
boykopovar:mainfrom
sktharunbalaji13-cmd:feat/shader-draw-submission-correlation-5166-fresh

Conversation

@sktharunbalaji13-cmd

@sktharunbalaji13-cmd sktharunbalaji13-cmd commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

What

Add opt-in diagnostic correlation across draw attempts, shader selection and preparation failures, actual draw-command recording, and recorder batch submission in the AGC driver.

Each draw attempt receives a correlation token. Relevant failure and recording events preserve that identity. Recorder batches collect a deduplicated set of the tokens associated with commands recorded in the batch, allowing one batch to represent multiple draws. Submission-attempt diagnostics are distinguished from confirmation records emitted only after successful queue submission and finalized required recorder bookkeeping. Diagnostic formatting and logging are bounded and best-effort to prevent trace failures from masking original exceptions or blocking otherwise valid submission.

Tested

Clean PR branch \ eat/shader-draw-submission-correlation-5166-fresh, rebased on current \main\ (\80249ea54).

  • Windows local: CMake/Ninja configure succeeded; \core/libs/libs/libSceAgcDriver.prx\ built successfully; NID patching reported \OK.
  • \ctest -R agc_capture_trace\ passed 8/8 tests.
  • \ctest -R agc_driver_draw_shader_failure\ passed 6/6 tests.
  • Total 14/14 tests in \libSceAgcDriver\ pass 100%.

Checklist

  • Based on current \main; no other open PR implements the same functions
  • One topic per PR; follow-ups go in a new PR
  • No comments in code except technical debt
  • Unimplemented paths throw (\NotImplemented_nid_no_patch); silent stubs are listed under Silent stubs in their library's technical debt file
  • No notes, investigation .md\ files or images added to the repository (attach them to this PR)
  • New third-party code is a submodule built from source
  • Generic behaviour, not specific to one title
  • Depends on: none
  • Unblocks: none (diagnostics-only change; no title-specific trace evidence)
  • GPUs tested: n/a; no GPU tests ran for this tracing-only change
  • AI-assisted: yes

@MarcZX10

Copy link
Copy Markdown
Contributor

I inspected the failing conventions run. It reports 39 errors: inline comments in CaptureTrace.hpp are disallowed unless the PR also documents the associated technical debt under docs/dev/TechnicalDebt.md or docs/dev/debt/, and the PR description is missing the required What, Tested, and Checklist sections. Please address those findings and rerun the check; the current PR body has validation details, but they need to be placed under the required headings so the repository check can recognize them.

I also compared this with #5238. Both PRs change Execution/src/Driver/Draw/Draw.cpp and the libSceAgcDriver CMake target, while tracing different points in the draw path. Could you clarify how the two tracing mechanisms are intended to compose and whether one can reuse the other, so they do not add duplicate or conflicting instrumentation?

@sktharunbalaji13-cmd

Copy link
Copy Markdown
Contributor Author

Thank you for the review. The conventions findings are addressed in commit 6b5c7fbda03e9b9469e786b34d98a87dde438383: the non-technical-debt diagnostic comments were removed, the PR body now uses the required ### What, ### Tested, and ### Checklist sections, and tools/check_conventions.py passes locally against base dacd78525e537a779682032c0ace7f7af120af05. The GitHub conventions run subsequently passed.

On composition with #5238: they are intended to be complementary. PR #5238 gives per-draw shader address information (stage, role, code/entry address, queue, submission context), which helps identify the selected program. This PR assigns a correlation token to each draw attempt and tracks whether the draw reaches selection, actual command recording, submission attempt, and successful queue submission; it also associates recorded draws with a deduplicated set of tokens per recorder batch. In practice, #5238 helps answer "which shader was selected?" while this PR helps answer "did that draw attempt survive setup and get recorded/submitted?"

Both PRs touch the same Draw.cpp and CMake target, so their merge order will matter. If #5238 lands first, this PR should adapt to its address-formatting and per-draw structures rather than duplicating them. I avoided copying or renaming its address-formatting implementation; the remaining overlap is in the shared Draw/composition points where both may hook logging. They do not need to emit duplicate records for the same event, but some reconciliation at those shared points is likely during integration.

No runtime/game validation is claimed yet, and the platform build jobs remain skipped for this PR.

@MarcZX10

Copy link
Copy Markdown
Contributor

Thanks, that addresses both points. The distinction between identifying the selected shader and tracking a draw attempt through recording/submission is clear, and your note about reconciling the shared Draw hooks if #5238 lands first gives a workable integration plan. I verified the new conventions check passes; the platform build jobs remain skipped for this path. No runtime validation is claimed.

@CnRJay CnRJay left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checked the cost with the trace off: the token is 0 and NoteDrawTraceToken returns at once, so the disabled path stays cheap. Two points about the enabled path and the scope:

  • The dedup in NoteDrawTraceToken is quadratic per batch. It runs std::find over open->drawTraceTokens for every recorded draw. A batch holding a few thousand draws (common in a busy frame) then does millions of comparisons on the queue worker, under the recorder's open-batch state, while the trace measures that same worker. That distorts the timings the correlation is meant to explain. The tokens come from a counter, so a draw's commands are recorded contiguously and the token can only repeat back to back. Comparing against back() gives the same dedup in O(1). If tokens can interleave, an unordered_set beside the vector also works.
  • The draw-path refactor would be better split out. Moving per-stage materialization into Driver::materializeDrawStage (27 parameters, several of them out-parameters) changes the hottest function in the driver as part of a diagnostics PR. It may well be behaviour-neutral, but reviewers have to verify that across ~200 lines, and it conflicts with every other open PR that touches Driver::draw (#5238 already). Keeping the token as one extra argument through the existing code, and leaving the restructure to its own refactor(agc) PR, would make both easier to review.

Minor: the catch (...) {} in NoteDrawTraceToken hides allocation failures. In a diagnostics path, losing a token silently makes the trace look complete when it isn't. Letting it throw, or counting the drops in the trace, is safer.

@sktharunbalaji13-cmd

Copy link
Copy Markdown
Contributor Author

Thank you for the detailed review. I’ve addressed the three points in commit 2c3502d01f902b3cd6de2a67f658d68b35bf1389.

A. Token deduplication

NoteDrawTraceToken now uses an accompanying std::unordered_set beside the existing ordered token vector. A token is pushed only when the set insert succeeds, so dedup is O(1) average instead of O(batch size) per recorded draw, duplicates still cannot repeat in the vector, and ordering is unchanged. The side set is also rolled back if the vector append throws, so a failed append does not leave later recording unable to reintroduce that token.

B. Scope of the draw-path change

The PR mainly adds parameters rather than introducing a broad new draw refactor. The existing Driver::materializeDrawStage helper was already present upstream; PR #5431 threaded packetOffset and the correlation token through it and through Driver::draw so materialization/decode failures can preserve correlation identity. The materialization calls and try/catch wrappers are the instrumentation path, and they keep the original early-return behavior. If this second parameter pass is still judged too broad after review, I can split it into a refactor(agc) preparatory change, but it currently supports the request/materialization failure records described in the PR.

C. Silent allocation failures

The previous empty catch now emits draw-token-note-failed token=... through the best-effort logging path, so dropped correlation data is observable rather than indistinguishable from a complete trace. As before, this cannot throw past the call site, append to the reordered token list before success, or block QueueSubmit/draw success.

Validation

  • Local Windows Release build: passed, including core/libs/libs/libSceAgcDriver.prx and NID patching.
  • CaptureTrace focused tests: 8/8 passed.
  • git diff --check: passed.
  • Conventions check: passed.
  • GitHub checks on the new head: conventions, eligibility, and report passed; platform jobs remained skipped, as in the earlier runs.

#5238 overlap

I compared the overlap with PR #5238. It remains complementary: #5238 exposes per-draw shader address/context details, while this PR adds token lifecycle and submission-batch correlation. If #5238 lands first, this PR should adapt to its address formatting rather than duplicating it. No changes were made to #5238's branch.

No claim of GPU/game-runtime validation is made.

@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Oct 14, 2026
@adriadam10 adriadam10 self-assigned this Oct 14, 2026

@adriadam10 adriadam10 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the quick follow-ups on the dedup and the empty catch. Blocking points:

  • Conflicts with main in code, not just adjacency: Execution/src/Driver/Draw/Draw.cpp and Draw/DrawCapture.cpp (main's 73a808e changed how draw stages use their shared compiled result). Driver::materializeDrawStage has to be redone on top of that code, and the draw tests rerun after the rebase.
  • Split the refactor out (as @CnRJay asked): moving per-stage materialization into Driver::materializeDrawStage (27 parameters, several out-parameters) rewrites the hottest function in the driver, and it is the part that conflicts with #5238, #5364, #5441, #5609 and #5656. Send that as its own refactor(agc) PR that is behaviour-neutral and has its own tests; then keep this PR to the token passed as one extra argument through the existing code.
  • Evidence for the claim: "Unblocks PPSA02225 #4461" is not backed by anything in the PR. Show a trace from that title that reveals a failure that is not visible today, or drop the claim.

Welcome back once it is rebased and the refactor is separated.

@adriadam10 adriadam10 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The merge queue's Linux Build run on the current head (run 39157472408, PR merged onto main) fails four of this PR's own tests, deterministically:

  • agc_driver_draw_shader_failure_successful, agc_driver_draw_shader_failure_correlation_disabled, agc_driver_draw_shader_failure_materialization, agc_driver_draw_shader_failure_materialization_correlation_disabled.
  • The log shows [gpu] driver failure: AGC driver: guest memory has no write permission (the colour-target writability check at Graphics/src/Draw.cpp:2566) and AGC graphics: the submission failure lost the original shader-request error. The first test and the request_correlation_disabled one pass, so it looks tied to submitting further draws into the same ColorMemory after an earlier one in the process.

Please rebase on current main, run ctest -R agc_driver_draw_shader_failure (Linux/lavapipe if you can, or check the Windows run too) and make the tests independent of each other, e.g. a fresh writable colour buffer per case, or find why the buffer loses write permission. (agc_driver_depth_bias_per_face also fails in that run but is not caused by this PR.)

@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Oct 18, 2026
@sktharunbalaji13-cmd
sktharunbalaji13-cmd force-pushed the feat/shader-draw-submission-correlation-5166-fresh branch from a5cdfa9 to 7c2f861 Compare October 19, 2026 04:37
@sktharunbalaji13-cmd

Copy link
Copy Markdown
Contributor Author

Rebased on current \main\ (\676d423968) and resolved the test failures:

  • Root cause of \guest memory has no write permission\ on Linux: In \DrawPacket, register \

@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 19, 2026

@adriadam10 adriadam10 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Windows Build of the current head fails to compile (run 39202586561):

  • Execution/src/Driver/Draw/DrawCapture.cpp:105: error: binding reference of type 'ShaderRecompiler::PreparedShaderInvocation&' to 'const ShaderRecompiler::PreparedShaderInvocation' discards qualifiers. The PR declares const auto invocation = [&] {...}(); (line 76) and passes it to CaptureInvocation(..., PreparedShaderInvocation& invocation) (ShaderRegistry.hpp:221); on main it is auto invocation. Dropping the const fixes it.
  • With that fixed and the branch rebased on current main, agc_driver_depth_bias_per_face fails on lavapipe (the driver CI's Linux job uses) and passes on main. Please rebase, rerun the libSceAgcDriver tests on lavapipe and check whether your changes to the draw/recorder path affect it.

Please push both and the Build will be re-run.

@adriadam10 adriadam10 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No push since our last comment, and the head no longer applies to main:

  • Windows build still fails to compile: Execution/src/Driver/Draw/DrawCapture.cpp:76 declares const auto invocation, and :105 passes it to CaptureInvocation(..., PreparedShaderInvocation&). On main it is auto invocation; dropping the const fixes it (run 39202586561).
  • Real conflicts with main, not adjacency: Execution/src/Driver/Draw/Draw.cpp (9 hunks), Execution/include/Driver/Driver.hpp, Execution/include/VulkanDevice.hpp, Execution/src/Driver/Draw/DrawCapture.cpp, Graphics/src/Draw.cpp and Graphics/include/Draw.hpp. main has since moved the draw path into DrawTail/finishDraw and changed materializeDrawStage's signature, so the correlation token has to be threaded through that new structure. That is a design decision for you, so we are not rebasing it for you.
  • After the rebase, agc_driver_depth_bias_per_face failed on lavapipe and passes on main; please rerun the libSceAgcDriver tests on lavapipe and check whether your draw/recorder changes affect it.

Please rebase on current main, fix the above and push; the Build will then run again.

@sktharunbalaji13-cmd
sktharunbalaji13-cmd force-pushed the feat/shader-draw-submission-correlation-5166-fresh branch from 7c2f861 to 3b4c930 Compare October 24, 2026 05:56
@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 24, 2026
@sktharunbalaji13-cmd
sktharunbalaji13-cmd force-pushed the feat/shader-draw-submission-correlation-5166-fresh branch from 3b4c930 to b21fa22 Compare October 24, 2026 08:02
@sktharunbalaji13-cmd

Copy link
Copy Markdown
Contributor Author

Rebased on current main (b0f57be3c) and pushed. All three points are addressed, and the rebase did turn up one real regression that I found and fixed.

1. Windows compile error

Execution/src/Driver/Draw/DrawCapture.cpp:77 now declares auto invocation, matching main. That is the discards qualifiers error at the CaptureInvocation(..., PreparedShaderInvocation&) call.

2. The rebase, including DrawTail/finishDraw

All 20 conflict hunks across the six files are resolved by keeping main's structure and re-applying the correlation token on top, never by picking a side.

Execution/src/Driver/Draw/Draw.cpp — the token is produced in draw() and carried to finishDraw on the tail:

struct DrawTail {
    ...
    std::uint64_t traceToken = 0;
    VulkanDevice::DrawTraceContext trace{};
};

draw() sets tail.traceToken, tail.trace and tail.packetOffset before either handing the tail to the draw finisher or calling finishDraw(tail, timing), and finishDraw aliases them at the top. That way the recording and submission records describe the draw the finisher actually processes, including across the DrawFinisher hand-off, instead of a token captured on the calling thread. materializeDrawStage keeps main's meshBarycentrics parameter and gains packetOffset/traceToken; all three call sites pass them.

Graphics/src/Draw.cpp — main split Draw() into prepareDraw() + recordPreparedDraw(), so the trace rides on PreparedDraw:

struct PreparedDraw {
    ...
    const DrawTraceContext* trace = nullptr;
};

Draw() and DrawWithRecipe() take the trace pointer; recordPreparedDraw forwards prepared.trace into recordDraw and its recordDrawCommands calls.

Everything main added that the PR does not touch is kept: WriteOnlyPasses(), the PixelFunctionLdsLayouts block, DrawInputsInPlace(), labelPacketQueues(), finishDraw().

3. agc_driver_depth_bias_per_face — found it, it was my rebase

The first rebase silently dropped main's face-split block in recordPreparedDraw:

if (pipeline->SplitsFaces()) {
    pipeline->ContinueBackFaces(commands, state);
    recordDrawCommands(...);
}

Without it the pipeline continued into back faces but never recorded the draw, so the back-facing triangle was never rasterised — exactly what the test asserts. recordDraw still had its copy, which is why it was easy to miss. Restored (with the trace token forwarded), and the test now passes.

How that was isolated, on Windows with mingw-w64 g++ 15.2.0 and Ninja, Release:

  • A clean main worktree (a5a2c946a) passes agc_driver_depth_bias_per_face 3/3; the PR head failed 3/3. Only two AGC files differ between a5a2c946a and b0f57be3c, neither in the draw path, so the baseline is valid.
  • Reverting only the PR's Graphics/src/Draw.cpp changes made it pass, which pointed at that file.
  • Restoring the SplitsFaces() block made it pass with the rest of the PR in place.

Test results on the final head

  • core/libs/libs/libSceAgcDriver.prx builds and links.
  • ctest -R agc_capture_trace — 8/8 pass.
  • ctest -R agc_driver_draw_shader_failure — 6/6 pass, including the four that used to fail deterministically.
  • agc_driver_depth_bias_per_face — passes.
  • python3 tools/check_conventions.py --base upstream/main — passes.

I could not run lavapipe: there is no Vulkan SDK and no Linux host on this machine, so every GPU result above is Windows on the real driver. I am not claiming a lavapipe result — that still needs the CI run.

@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Oct 24, 2026
@adriadam10
adriadam10 force-pushed the feat/shader-draw-submission-correlation-5166-fresh branch from b21fa22 to ea65a95 Compare October 24, 2026 17:18
@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 24, 2026
@adriadam10
adriadam10 merged commit 2dc8560 into boykopovar:main Oct 24, 2026
10 checks passed
@adriadam10

Copy link
Copy Markdown
Collaborator

main moved under this head again (conflicts in Draw.cpp on both the Execution and Graphics sides). Rebased it ourselves: kept main's results.reserve, writablePrograms and DepthCopySourceScope, re-applied the correlation token on top, and threaded packetOffset through precheckDraw for the depth-copy Driver::draw call. Build and ctest pass on the rebased tree and conventions pass; the queue's full Build is the Windows/Linux gate; merged.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants