Skip to content

docs(libSceAgcDriver): copy kernel count record is read after the lease finish - #13599

Open
MarcZX10 wants to merge 1 commit into
boykopovar:mainfrom
MarcZX10:docs/debt-D12-1
Open

MarcZX10 wants to merge 1 commit into
boykopovar:mainfrom
MarcZX10:docs/debt-D12-1

Conversation

@MarcZX10

Copy link
Copy Markdown
Contributor

What

Documentation correction in docs/dev/debt/libSceAgcDriver.md, entry "CPU reads behind an address-based build" (second half of the file, audited at upstream/main 50843f45d95b60e5772f4396431f9a39b8709d72).

The entry said the count record of the copy kernel HLE is a CPU read that does not finish the batches recorded with a lease. The indexed copy branch of BufferCopy.cpp calls Graphics::FinishLeaseWork() before it reads that record, so the claim is removed. The other reads listed in the entry are unchanged. The entry is also shortened to under 1000 characters.

One commit, docs(libSceAgcDriver): copy kernel count record is read after the lease finish.

Overlap: tools/pr_overlap.py lists open PRs that change docs/dev/debt/libSceAgcDriver.md (for example #13471, #13474, #13484, #13499, #13373 and others). Each of them changes other entries. None edits the corrected entry (checked against the diff of each). #12840 shows the entry only as unchanged context. Permalinks in the commit body are pinned to the audited SHA.

Tested

Docs only. Not built, no runtime tests.

Run on branch docs/debt-D12-1 (head 256fc34e23a533ef86aba3fe9e93033a990b29c3, base 50843f45d95b60e5772f4396431f9a39b8709d72):

  • python3 tools/check_conventions.py --base upstream/main: "All conventions checks passed."
  • git diff --check upstream/main: clean.
  • python3 tools/env_reference.py --check: fails with the same single line as on a clean upstream/main run (identical output, the known baseline failure of APS5_PAD_SPEAKER_FALLBACK_DB).

NOT RUN:

  • Build and runtime tests: not applicable, the change touches only a Markdown debt document.
  • tools/pr_precheck.py test_quality_heuristics: reported as failed for docs-only PRs (harness limitation per RULES.md, the check points at a .md file). Raw failure line: /usr/bin/python3: can't open file '/marc/agent-work/D12/work/precheck/head-256fc34e23a5-693d8407/tools/pr_test_quality.py': [Errno 2] No such file or directory
  • Windows, macOS, real devices, hardware GPU, console: not applicable to a documentation change.

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
  • Fixes: none
  • Unblocks: none
  • GPUs tested: n/a
  • AI-assisted: yes

…se finish

Old claim (CPU reads behind an address-based build, libSceAgcDriver.md): the
count record of the copy kernel HLE is one of the CPU reads that do not finish
the batches recorded with a lease, so it can see guest memory from before such
a build ran, and it is guarded only by its shape check.

What the code does at upstream/main 50843f4:
the indexed copy branch calls Graphics::FinishLeaseWork() before it checks the
record (accessibility, snapshot and flush checks) and before it reads the count,
source and destination offsets from it.

https://github.com/boykopovar/AnyPS5/blob/50843f45d95b60e5772f4396431f9a39b8709d72/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Memory/BufferCopy.cpp#L197
https://github.com/boykopovar/AnyPS5/blob/50843f45d95b60e5772f4396431f9a39b8709d72/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Memory/BufferCopy.cpp#L207-L209

The claim is removed from that entry. The other reads listed there are unchanged:
Pm4::ReadDrawArguments and Pm4::ReadDrawCount are not preceded by a lease finish
(Draw.cpp), and the awaitsTitle and orderReleased checks in Submission.cpp do not
call one either. The entry is also shortened to under 1000 characters, the wording of those two checks only, with no change of meaning.
@oneandonlydean

Copy link
Copy Markdown
Collaborator

@MarcZX10 reviewed at 256fc34e23. The indexed branch is as you say: Graphics::FinishLeaseWork() runs at BufferCopy.cpp line 197, before the 12-byte copy record is checked and read.

The other branch of copyBuffer reads the 8-byte count record (count and period, lines 218-229, traced as "count record") without a lease finish. Neither copyBuffer nor its caller in Dispatch.cpp calls one before that read; in the driver, the only calls are line 197 and IndirectDispatch.cpp. So that count record can still see guest memory from before a lease-recorded build ran, which is what the removed sentence described.

Could the entry keep that read, named as the non-indexed count record, and drop only the indexed copy record? The shortened wording of the Submission.cpp checks is fine as it is.

@github-actions

github-actions Bot commented Oct 30, 2026 •

Copy link
Copy Markdown

Some comparisons could not be completed because a pull request conflicts with the base branch. Review these shared files manually before concluding that there is no overlap.

Pull request Shared files not compared
PR 2434 docs/dev/debt/libSceAgcDriver.md
PR 4448 docs/dev/debt/libSceAgcDriver.md
PR 5372 docs/dev/debt/libSceAgcDriver.md
PR 5925 docs/dev/debt/libSceAgcDriver.md
PR 6146 docs/dev/debt/libSceAgcDriver.md
PR 6185 docs/dev/debt/libSceAgcDriver.md
PR 6186 docs/dev/debt/libSceAgcDriver.md
PR 6194 docs/dev/debt/libSceAgcDriver.md
PR 6223 docs/dev/debt/libSceAgcDriver.md
PR 6273 docs/dev/debt/libSceAgcDriver.md
PR 6280 docs/dev/debt/libSceAgcDriver.md
PR 6412 docs/dev/debt/libSceAgcDriver.md
PR 6481 docs/dev/debt/libSceAgcDriver.md
PR 6548 docs/dev/debt/libSceAgcDriver.md
PR 6549 docs/dev/debt/libSceAgcDriver.md
PR 6743 docs/dev/debt/libSceAgcDriver.md
PR 6804 docs/dev/debt/libSceAgcDriver.md
PR 6808 docs/dev/debt/libSceAgcDriver.md
PR 6867 docs/dev/debt/libSceAgcDriver.md
PR 6926 docs/dev/debt/libSceAgcDriver.md
PR 6973 docs/dev/debt/libSceAgcDriver.md
PR 6987 docs/dev/debt/libSceAgcDriver.md
PR 6989 docs/dev/debt/libSceAgcDriver.md
PR 6995 docs/dev/debt/libSceAgcDriver.md
PR 7002 docs/dev/debt/libSceAgcDriver.md
PR 7085 docs/dev/debt/libSceAgcDriver.md
PR 7256 docs/dev/debt/libSceAgcDriver.md
PR 7449 docs/dev/debt/libSceAgcDriver.md
PR 7604 docs/dev/debt/libSceAgcDriver.md
PR 7642 docs/dev/debt/libSceAgcDriver.md
PR 7668 docs/dev/debt/libSceAgcDriver.md
PR 7978 docs/dev/debt/libSceAgcDriver.md
PR 8592 docs/dev/debt/libSceAgcDriver.md
PR 8707 docs/dev/debt/libSceAgcDriver.md
PR 9020 docs/dev/debt/libSceAgcDriver.md
PR 9335 docs/dev/debt/libSceAgcDriver.md
PR 9356 docs/dev/debt/libSceAgcDriver.md
PR 9387 docs/dev/debt/libSceAgcDriver.md
PR 9508 docs/dev/debt/libSceAgcDriver.md
PR 9592 docs/dev/debt/libSceAgcDriver.md
PR 9672 docs/dev/debt/libSceAgcDriver.md
PR 9673 docs/dev/debt/libSceAgcDriver.md
PR 9674 docs/dev/debt/libSceAgcDriver.md
PR 9675 docs/dev/debt/libSceAgcDriver.md
PR 9700 docs/dev/debt/libSceAgcDriver.md
PR 9742 docs/dev/debt/libSceAgcDriver.md
PR 9748 docs/dev/debt/libSceAgcDriver.md
PR 9891 docs/dev/debt/libSceAgcDriver.md
PR 9892 docs/dev/debt/libSceAgcDriver.md
PR 9893 docs/dev/debt/libSceAgcDriver.md
PR 9895 docs/dev/debt/libSceAgcDriver.md
PR 9898 docs/dev/debt/libSceAgcDriver.md
PR 9915 docs/dev/debt/libSceAgcDriver.md
PR 9950 docs/dev/debt/libSceAgcDriver.md
PR 10049 docs/dev/debt/libSceAgcDriver.md
PR 10064 docs/dev/debt/libSceAgcDriver.md
PR 10193 docs/dev/debt/libSceAgcDriver.md
PR 10233 docs/dev/debt/libSceAgcDriver.md
PR 10256 docs/dev/debt/libSceAgcDriver.md
PR 10351 docs/dev/debt/libSceAgcDriver.md
PR 10359 docs/dev/debt/libSceAgcDriver.md
PR 10469 docs/dev/debt/libSceAgcDriver.md
PR 10472 docs/dev/debt/libSceAgcDriver.md
PR 10492 docs/dev/debt/libSceAgcDriver.md
PR 10497 docs/dev/debt/libSceAgcDriver.md
PR 10743 docs/dev/debt/libSceAgcDriver.md
PR 10779 docs/dev/debt/libSceAgcDriver.md
PR 11104 docs/dev/debt/libSceAgcDriver.md
PR 11346 docs/dev/debt/libSceAgcDriver.md
PR 11551 docs/dev/debt/libSceAgcDriver.md
PR 11557 docs/dev/debt/libSceAgcDriver.md
PR 11587 docs/dev/debt/libSceAgcDriver.md
PR 11825 docs/dev/debt/libSceAgcDriver.md
PR 11995 docs/dev/debt/libSceAgcDriver.md
PR 11998 docs/dev/debt/libSceAgcDriver.md
PR 12120 docs/dev/debt/libSceAgcDriver.md
PR 12452 docs/dev/debt/libSceAgcDriver.md
PR 12565 docs/dev/debt/libSceAgcDriver.md
PR 12567 docs/dev/debt/libSceAgcDriver.md
PR 12568 docs/dev/debt/libSceAgcDriver.md
PR 12764 docs/dev/debt/libSceAgcDriver.md
PR 12792 docs/dev/debt/libSceAgcDriver.md
PR 12840 docs/dev/debt/libSceAgcDriver.md
PR 12855 docs/dev/debt/libSceAgcDriver.md
PR 12904 docs/dev/debt/libSceAgcDriver.md
PR 13021 docs/dev/debt/libSceAgcDriver.md
PR 13023 docs/dev/debt/libSceAgcDriver.md
PR 13146 docs/dev/debt/libSceAgcDriver.md
PR 13147 docs/dev/debt/libSceAgcDriver.md
PR 13151 docs/dev/debt/libSceAgcDriver.md
PR 13248 docs/dev/debt/libSceAgcDriver.md
PR 13474 docs/dev/debt/libSceAgcDriver.md
PR 13793 docs/dev/debt/libSceAgcDriver.md
PR 13809 docs/dev/debt/libSceAgcDriver.md
PR 13858 docs/dev/debt/libSceAgcDriver.md
PR 13863 docs/dev/debt/libSceAgcDriver.md
PR 13920 docs/dev/debt/libSceAgcDriver.md
PR 13928 docs/dev/debt/libSceAgcDriver.md
PR 13929 docs/dev/debt/libSceAgcDriver.md
PR 13963 docs/dev/debt/libSceAgcDriver.md
PR 13991 docs/dev/debt/libSceAgcDriver.md
PR 13994 docs/dev/debt/libSceAgcDriver.md
PR 14005 docs/dev/debt/libSceAgcDriver.md
PR 14018 docs/dev/debt/libSceAgcDriver.md
PR 14085 docs/dev/debt/libSceAgcDriver.md
PR 14212 docs/dev/debt/libSceAgcDriver.md
PR 14220 docs/dev/debt/libSceAgcDriver.md
PR 14243 docs/dev/debt/libSceAgcDriver.md
PR 14248 docs/dev/debt/libSceAgcDriver.md
PR 14265 docs/dev/debt/libSceAgcDriver.md
PR 14296 docs/dev/debt/libSceAgcDriver.md
PR 14302 docs/dev/debt/libSceAgcDriver.md
PR 14311 docs/dev/debt/libSceAgcDriver.md
PR 14344 docs/dev/debt/libSceAgcDriver.md
PR 14561 docs/dev/debt/libSceAgcDriver.md
PR 14621 docs/dev/debt/libSceAgcDriver.md
PR 14628 docs/dev/debt/libSceAgcDriver.md
PR 14815 docs/dev/debt/libSceAgcDriver.md
PR 14909 docs/dev/debt/libSceAgcDriver.md
PR 14943 docs/dev/debt/libSceAgcDriver.md
PR 14944 docs/dev/debt/libSceAgcDriver.md
PR 14950 docs/dev/debt/libSceAgcDriver.md
PR 14963 docs/dev/debt/libSceAgcDriver.md
PR 14964 docs/dev/debt/libSceAgcDriver.md
PR 14976 docs/dev/debt/libSceAgcDriver.md
PR 14982 docs/dev/debt/libSceAgcDriver.md
PR 15058 docs/dev/debt/libSceAgcDriver.md

@github-actions github-actions Bot added the docs label Oct 31, 2026
@adriadam10 adriadam10 self-assigned this Oct 31, 2026

@MarcZX10 MarcZX10 left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Reviewed — reply answered, the count record is read without a lease finish

oneandonlydean's 2026-10-30 reply shows that the 8-byte copy count record is read without FinishLeaseWork, and the code at this head confirms it, with one correction to the reply's call list.

What I ran

Live head 256fc34e23a533ef86aba3fe9e93033a990b29c3 (matches the batch file). Read the comment with gh api, the check rollup with gh pr view --json statusCheckRollup, the base doc diff with git diff 73f4f7e… 256fc34… -- docs/dev/debt/libSceAgcDriver.md, and the sources with git -C /marc/AnyPS5 show 256fc34…:<path>. Nothing was built or run.

Results

Check Result Note
eligibility SUCCESS hosted
conventions SUCCESS hosted
intake, labels SUCCESS hosted
linux, windows, relinker, relinker-asan, write-watch, register-context SKIPPED hosted, doc-only change

Findings

The reply (2026-10-30T19:32:01Z) asks to restore the count-record read to the entry. The code supports the claim.

  • FinishLeaseWork() is called in copyBuffer only in the indexed branch (core/libs/prx/libSceAgcDriver/Execution/src/Driver/Memory/BufferCopy.cpp line 196-197).
  • The non-indexed branch (lines 218-229) reads the 8-byte count and period with no lease finish. copyBuffer is called from Dispatch.cpp line 162 only when indirectArguments == 0.
  • The base doc (73f4f7e) named "the count record of the copy kernel HLE" in the CPU-reads entry; doc line 181 at head no longer does.

Two corrections:

  • The reply says the only driver calls to FinishLeaseWork are line 197 and IndirectDispatch.cpp. VulkanDevice.cpp line 5182 also calls it, in the indirect-argument read, not on the copyBuffer path.
  • If the old "guarded only by its shape check" wording returns, note that lines 222-226 check accessibility, pending writes and labels before the read. The shape check is at line 232, after it.

Sources

  • } else if (indexed) {
    Graphics::FinishLeaseWork();
    const bool recordAccessible = GuestMemory::Accessible(reinterpret_cast<const void*>(record), 12);
    bool published = false;
    if (!recordAccessible || Graphics::Recorder::SnapshotWriteOverlaps(record, 12) || Graphics::StorageTexture::FlushPending(record, 12, nullptr, "indexed copy record", Graphics::PublishScope::Whole, &published) || published || labelPending(record, 12)) {
    if (traceCopy()) traceCopyPending(recordAccessible ? "copy record" : "inaccessible copy record", record, 12, source, destination, 0, localDevice);
    countCopy(CopyRecordPending, 0, started, locked);
    return false;
    }
    std::uint32_t count = 0, sourceOffset = 0, destinationOffset = 0;
    std::memcpy(&count, reinterpret_cast<const void*>(record), 4);
    std::memcpy(&sourceOffset, reinterpret_cast<const void*>(record + 4), 4);
    std::memcpy(&destinationOffset, reinterpret_cast<const void*>(record + 8), 4);
    const auto range = IndexedCopyRange(userData, groups[0], count, sourceOffset, destinationOffset);
    if (!range || (record < range->destination + range->bytes && range->destination < record + 12)) {
    countCopy(CopyShape, range ? range->bytes : 0, started, locked);
    return false;
    }
    source = range->source;
    destination = range->destination;
    bytes = range->bytes;
    } else {
    const bool recordAccessible = GuestMemory::Accessible(reinterpret_cast<const void*>(record), 8);
    bool published = false;
    if (!recordAccessible || Graphics::Recorder::SnapshotWriteOverlaps(record, 8) || Graphics::StorageTexture::FlushPending(record, 8, nullptr, "buffer copy count", Graphics::PublishScope::Whole, &published) || published || labelPending(record, 8)) {
    if (traceCopy()) traceCopyPending(recordAccessible ? "count record" : "inaccessible count record", record, 8, source, destination, 0, localDevice);
    countCopy(CopyRecordPending, 0, started, locked);
    return false;
    }
    std::uint32_t count = 0, period = 0;
    std::memcpy(&count, reinterpret_cast<const void*>(record), 4);
    std::memcpy(&period, reinterpret_cast<const void*>(record + 4), 4);
  • if (!partialLastGroup && indirectArguments == 0 && copyBuffer(queue, submission.queue, packet, std::span(snapshot.code).subspan(codeOffset), userData, compute, localDevice, address)) {
  • hold.drawWritersScanned += Graphics::DrawCopiedWriters()->size();
    hold.maxWriters = std::max(hold.maxWriters, state->copiedWriters->size());
    }
    timer.phase(PhaseDecide);
    if (outcome.cpuReason == 0) return;
    const auto readStart = std::chrono::steady_clock::now();
    Graphics::FinishLeaseWork();
  • - [CPU reads behind an address-based build](../../../core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/Draw.cpp) (libSceAgcDriver) - an address-based store (FLAT/GLOBAL, or through a descriptor the recompiler cannot resolve to a static buffer) is not recorded as a pending write: its batch holds a lease on the registered allocations instead, and the flush hook does not wait for it. A DISPATCH_INDIRECT whose arguments are read on the CPU first finishes the batches recorded with a lease (`Graphics::FinishLeaseWork`); these reads do not, so they can see guest memory from before such a build ran: the indirect draw arguments and count of a draw resolved on the CPU (`Pm4::ReadDrawArguments`, `Pm4::ReadDrawCount`), and the checks in [Submission.cpp](../../../core/libs/prx/libSceAgcDriver/Execution/src/Driver/Queues/Submission.cpp) on whether a queue awaits a title-only label (`awaitsTitle`, `orderReleased`). No test covers them and no title has been seen to read such memory there.

Not verified

  • Whether the count record can see stale guest memory on hardware, or whether any title reads it there; the reply's trace is code reading only.
  • Nothing was built or run.

@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Nov 2, 2026

This branch has not been deployed

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

Labels

docs needs rebase Conflicts with main, rebase needed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants