Commit Graph
58 Commits
Author SHA1 Message Date
winstrexandClaude Opus 5.5 97c3687b01 perf(agc): compare snapshot copies on the CPU and evict BDA tables by recency
Each cached snapshot copy keeps a CPU copy of its bytes, so the reuse
check no longer reads the buffer's mapping, which is uncached on devices
without host-cached coherent memory. An address space's table map now
evicts its least recently used table instead of its smallest key.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 13:16:55 -03:00
winstrexandClaude Opus 5.5 619be69796 perf(agc): share snapshot copies and the BDA table of repeated address-based builds
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 13:16:54 -03:00
AlxcBTWandClaude Opus 5.5 48cdce9922 perf(agc): note an address space's in-place reads by reference
Every address-based build copied the cached address space's imported
ranges (about 9000 in Sword of the Sea gameplay) into its batch as pending
reads, only for the copy HLE's reader check to scan them. The space now
builds that list once, and a build that took the space whole notes it by
reference (Recorder::NotePendingReadList); its own ranges follow as before.

The space is immutable while its serial lives, the list equals the space's
part of InPlaceReads in its order, and the batch scans plain reads and lists
in note order, a list by bisection, so every PendingReadOverlaps and
DescribePendingRead answer and reader kind stay the same. A capture trace or
APS5_FLIP_READ_CHECK expands the list as before.

Tests: lists noted by reference answer like the same ranges noted one by one
(open and in-flight scans, kinds, a list noted twice, empty and null lists);
the space's list is shared while the registry stands and rebuilt when a range
is added or removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 14:30:19 +02:00
Adrià 905f197966 Merge pull request #4979 from AlxcBTW/perf/bda-writable-writes-space-table
perf(agc): bind the address space's BDA table for writes inside writable imports
2026-10-14 14:00:09 +02:00
Dean Galvin fd2c300e46 fix(agc): drop the comments from the dissolve for a written V# over a copied read-only range 2026-10-14 18:16:22 +13:00
Dean Galvin e5d65569e5 fix(agc): dissolve the cached address space when a written V# overlaps a read-only V# copied across imports
A read-only V# that crosses two base regions is copied per build while the
space stays. A written V# overlapping that copy would bind the base region's
import while the read-only V# binds the copy of the same bytes. Dissolve the
space in that case (either order of the two), as without this branch, so both
share the one merged region.
2026-10-14 16:10:33 +13:00
alfalfa-starandClaude Opus 5.5 972ce12247 perf(agc): keep the cached address space for a read-only V# across two imports
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 15:47:42 +13:00
Michon c871b5893d test(agc): host import windows, whole-range builds and budget refusals
agc_driver_graphics (BdaResources.cpp), each failing without its change:
- a view in the last window of a range whose first window was imported
  binds through an import of the whole range;
- an address-based build over the budget leaves the range's window in
  place (3 builds, same import);
- two uses across neighbouring cell edges share one import over 10
  rounds;
- a range mapped at the budget is refused at map time and retires
  nothing.
agc_driver_host_import_windows: windows follow their range, and
ClearHostImports drops them.
2026-10-14 14:51:48 +13:00
d275a58814 fix(agc-driver): copy a region an exact join's import covers only in part
After an exact two-piece join, a bind across the join's end got the
joined import from importAllocation and the view failed as "rounded up to
its view range exceeds its GPU owner". UploadFinish now asks importJoined
for a region past the import's end and drops an import that still does
not cover the region, so the region is copied, as on main. The join test
binds across the end of an exact join after a third piece is registered.

Co-authored-by: TideOPL <57710757+TideOPL@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 13:34:31 +13:00
AlxcBTWandClaude Opus 5.5 ca95f74097 perf(agc-driver): join the imports of adjacent ranges of any size that a buffer is bound exactly over
A bound buffer is joined across adjacent registered ranges only when every
one of them is larger than the 8 MiB window. PPSA25344's loading binds 16 MiB
and 4 MiB buffers whose backing was registered in two to six adjacent
pieces with the same access, some of them smaller than the window, so each
use of such a buffer was copied on the CPU: up to 348 GB in the first 300 s
of loading.

A bound buffer that starts at the first crossed range's start and ends at the
last one's end now joins those ranges whatever their size. A heap grown in
pieces matches that only for a binding over exactly its current pieces, so
growth beside a joined import does not re-import it. joinedCovers, used when a
joined import retires, accepts any adjacent run of ranges.

importJoinTests binds exactly over two 64 KiB pieces of a partly filled
window and requires one import of both; a binding across them that starts
inside the first piece stays apart.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 13:34:31 +13:00
Dean 61b8742b6b Merge pull request #4515 from AlxcBTW/perf/host-import-join
perf(agc-driver): join the imports of adjacent large ranges when a buffer is bound across them
2026-10-14 05:55:27 +13:00
AlxcBTWandClaude Opus 5.5 d95634b706 perf(agc): bind the address space's BDA table for writes inside writable imports
A build mapping only the cached address space but writing through V#s got a
table keyed by its writes: a new key for nearly every set of written
addresses, so each such build merged the space's imported ranges, stored them,
then hashed and compared them in BdaResources.

AddressRanges splits only ranges without write permission around the written
intervals. When no written interval overlaps such a range of the space, its
result is the space's own ranges, so the build binds the space's table by the
space serial, as a build without writes does. A write overlapping a range
without write permission keeps the per-writes table.

Test: builds with one and two writes inside a writable import bind the serial
and the ranges of the build without writes, equal to AddressRanges(), and keep
the read-only import without write permission.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 17:43:50 +02:00
AlxcBTWandClaude Opus 5.5 230b5c2683 perf(agc-driver): join the imports of adjacent large ranges when a buffer is bound across them
8 MiB window, so a range larger than the window is always imported alone. A
buffer bound across two such ranges then has no import and is copied whole on
every use: PPSA25344 binds a 368 MiB buffer across a 320.75 MiB and a 47.3 MiB
range and copies it on almost every frame.

When UploadFinish meets such a region, it imports the run of the ranges the
region crosses, provided every one of them is larger than the window, and
retires their own imports. It does this before any region of the build
confirms an import, so the retire moves the epoch ahead of every pointer the
build would use. A range that large is registered whole, not grown in 64 KiB
pieces like a heap, so the joined import is not re-imported as a heap grows; it
stands in refreshImports while exactly its ranges stand. A later join that
reaches a joined import takes it in, so two joins that share a range never
retire each other in turn.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 13:45:21 +02:00
hypermistandClaude Opus 5.5 6bf9ab86ff perf(agc): keep adjacent ranges one import serves as one region of the cached address space
A V# spanning two registered ranges dissolved the shared space into the
build's own regions (about 10k regions copied, sorted and looked up per
build in Killing Floor 3), although the import covering the run binds it
in place.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uzy15wqYTt6iWyxYXEzUWo
2026-10-13 19:03:20 +13:00
Dean 2ffbb75ae7 Merge pull request #4206 from jweidenh/codex/sparse-buffer-hole-initialization
fix(agc): clear interior holes in recycled sparse buffers
2026-10-13 17:48:00 +13:00
Dean Galvin 59db660f7b test(agc): reserve the arena host hole the write-tracking checks query
On Windows the guest arena is reserved while libc loads, around whatever
the process has already mapped in its interval. About one start in forty
has nothing mapped there, so the arena has no host hole:
agc_driver_graphics then fails with "write-watched arena contained no
host exclusion to test" and agc_write_tracking prints that its host-hole
checks were skipped.

agc_write_tracking now starts itself suspended, reserves one allocation
granule in the middle of the child's arena interval before the child's
loader runs, and the child runs the host-hole checks on that range. The
range is a host hole on every start, so the checks no longer depend on
the address layout. The four queries agc_driver_graphics made on an
enumerated hole are among them and are removed there.
2026-10-13 07:47:05 +13:00
TibixDev 2441a4b0d9 fix(agc): retry failed host imports after range replacement
Retain weak identities and the full failed span so a replacement of either owner or a shorter remaining run retries the import. Keep unchanged failures cached across unrelated mapping changes without pinning their allocations. Existing upstream import lifetime, span windows and Windows fallback remain in place. AI-assisted implementation.
2026-10-12 07:34:15 +03:00
jweidenh 8404d62be9 test(agc): reproduce stale bytes in sparse buffer holes 2026-10-11 21:06:02 -04:00
Scramblrrrr 56d08f2547 fix(agc): exclude host arena holes from write-watch proofs 2026-10-11 10:55:48 -03:00
Cesar RodriguezandClaude Opus 5.5 a947303731 perf(agc-driver): join host-import spans only over whole aligned windows
one span. A title that grows a heap 16 KiB at a time then re-imports the
whole chain on every growth: Ratchet & Clank: Rift Apart re-imported about
1.5 TiB while loading its first scene, and NVIDIA refused grown spans with
VK_ERROR_OUT_OF_DEVICE_MEMORY, which leaves those ranges copied per draw.

Ranges are now joined only when a chain covers a whole aligned window (8 MiB
by default, APS5_HOST_IMPORT_WINDOW_MIB on the device); until the window is
complete each range is imported on its own, and a span never crosses a
window edge. Growth therefore re-imports a range once more at most when its
window completes, instead of the chain on every growth. Joining any part of
a window was tried first and still re-imported 2 GB per second in Ratchet,
as each growth re-imported the window's partial span. A single range larger
than the window is imported whole. SetHostImportWindow(0) keeps the
unbounded spans, which is what the existing tests use.

The new importWindowTests (agc_driver_graphics) checks that a full window is
one span, that a partly filled window imports its range alone, that a view
crossing a window edge gets no import, and that completing one window leaves
the other window's import in place; with the window ignored it fails.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 22:35:28 +13:00
Chad b88ad02090 perf(agc): mirror a sparse descriptor heap instead of copying it per dispatch 2026-10-11 09:55:14 +02:00
Adrià c12a19e4de Merge pull request #3237 from Raghav314/perf/agc-cache-fault-only-resources
perf(agc): cache resources whose only completion work is the fault check
2026-10-11 09:50:44 +02:00
Adrià fbcdf1a804 Merge pull request #2992 from CnRJay/fix/agc-sparse-heap-cut
fix(agc): copy a sparse descriptor heap only up to its last backed byte
2026-10-11 09:24:32 +02:00
raghav314andClaude Opus 5.5 024cb1feda perf(agc): cache resources whose only completion work is the fault check
A draw or dispatch whose shaders have guarded descriptor slots (or a rect
list) but no BDA table gets a fault buffer, and with it NeedsCompletion(),
which kept its resources out of the resource cache, and NeverReusable
skipped the cache lookup for it. In PPSA36082 nearly every draw in the hub
is such a build, so the cache served none of them.

Such an object has no table to revalidate, no stores through BDA and no
copied writes, so it is now reusable. Each use of a reused object checks
the fault record when its batch completes (CheckFaults) instead of
running the once-only write-back; draw recipes over such a template
register the same check, and dispatch recipes still skip templates with
completion work. NeverReusable now refuses what noteReusable refuses: a
BDA table, or a fault buffer with APS5_LOOP_GUARD set (its fault resets
the buffer).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 09:15:53 +02:00
Adrià 5beb77f0f6 Merge pull request #3231 from Raghav314/perf/agc-pass-fault-only-draws
perf(agc): continue render passes across draws with only a fault buffer
2026-10-11 09:14:20 +02:00
Adrià 6e52fb8f8c Merge pull request #3158 from XairRrR/perf/agc-host-import-spans
perf(agc-driver): import adjacent registered ranges as one host-import span
2026-10-11 09:14:15 +02:00
ChadandScramblrrrr 86d6624323 fix(agc): copy a sparse descriptor heap only up to its last backed byte
Co-authored-by: Scramblrrrr <Scramblrrrr@users.noreply.github.com>
2026-10-11 04:56:40 +02:00
raghav314andClaude Opus 5.5 70a6dde689 perf(agc): continue render passes across draws with only a fault buffer
A recorded draw counted as reading and writing any guest memory when its
resources needed completion work, which a fault buffer alone (guarded
descriptor slots, rect lists) also needs. Such a draw reaches no guest
memory through it: the buffer is the draw's own, written only on a fault.
Its pass hazards now come from its known ranges and images like any other
draw; address-based draws, draws through the BDA table and draws with
copied writes still count as touching any guest memory.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 07:53:23 +05:30
raghav314andClaude Opus 5.5 aa013f59ac fix(libc): register guest heap blocks without GPU access
AnyPS5's libc heap registered every block with GPU access, so every
address-based build imported or mirrored each malloc block the title had
made. The console's libc.prx maps its heap with protection 3 (CPU read
and write, no GPU bits), so the GPU never sees it there either.

Heap blocks are now registered without GPU access; reallocating keeps the
block's access. The AvPlayer fallback for a title that passes no texture
allocator stands in for that allocator, which hands out memory the GPU
reads, so it allocates through a new GuestHeapAlignForGpu.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 07:02:30 +05:30
Scott SchusterandClaude Opus 5.5 dc48c61050 test(agc): a strided buffer's rounded-up last record stays within its own allocation
When another readable allocation starts right after the buffer's allocation,
the bound range still ends at the first allocation: the bytes past it are
outside the buffer the descriptor's records describe, and binding into the
neighbour would send the range back to a copy on every draw.

AI-assisted (Claude Code).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 01:37:00 +02:00
Scott SchusterandClaude Opus 5.5 b352406f70 fix(agc): bound a strided buffer's rounded-up last record by its allocation
RADV rounds a strided buffer descriptor's record count up, so stride * records
can run up to one stride past the end of the buffer's allocation. The guest
buffer region then no longer fits inside the allocation's host import and is
copied on the CPU, whole, for every draw that binds it.

When a strided descriptor ends less than one stride past the end of the
readable registered allocation that contains its base, the range is bound up
to the allocation's end. RetroArch's Dolphin core binds a 48 MiB stream buffer
with stride 20 (records 0x266667, 12 bytes past the allocation) on every draw.
With this change the buffer is bound in place from its import.

AI-assisted (Claude Code).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 01:37:00 +02:00
XairRrR 70d7aa58e3 perf(agc-driver): import adjacent registered ranges as one host-import span
A host import covered exactly one registered range, so a buffer crossing
two ranges had no import and was copied on the CPU at every use, under
the GPU mutex. Titles that map a GPU heap piece by piece (Uncharted maps
1 MiB direct-memory pieces) hit this for every buffer larger than a
piece.

An import now covers the run of address-adjacent registered ranges that
are readable with the same write and GPU access. Runs never overlap, so
imports do not either; an import is retired when any range of its run
changes, and a refused run is remembered by its base. Callers already
address imports relative to their base.
2026-10-11 01:04:12 +02:00
MathiasDevelopesandClaude Opus 5.5 078b6d886f style(agc): drop the comments the conventions check rejects
Comments are reserved for technical debt (docs/dev/TechnicalDebt.md), so
the ones the previous commit added to the recorder, the snapshot keep and
the tests go.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HZHkb8kUwnHAuBBbHywvJf
2026-10-11 00:33:07 +02:00
MathiasDevelopesandClaude Opus 5.5 4940de5436 fix(agc): count memory shared by a batch's keeps once and test the three keep sites
A ShaderResources from the resource caches is the same object for every
draw or dispatch that uses it, and Recorder::ReusableDrawSnapshot hands
the next draw the same snapshot buffer, so passing their bytes with each
Keep counted one allocation once per use and submitted the batch before
it held the budget. Recorder::KeepBytes counts an owner's bytes once per
batch, and the dispatch, draw and snapshot keeps use it.

The recorded kept bytes test runs a dispatch and a draw that copy a
guest buffer and two draws that snapshot one on the device, and fails
when any of the three sites stops counting or a reused snapshot counts
twice. The BdaResources check pins the copied region it sets up.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HZHkb8kUwnHAuBBbHywvJf
2026-10-11 00:33:07 +02:00
MathiasDevelopesandClaude Sonnet 5.5 00fafd4379 fix(agc): count copied guest buffers and draw snapshots in the batch byte budget
The recorder bounds the bytes a batch keeps alive (KeptBytesBudget) and
waits for older batches past twice that. A recorded dispatch or draw kept
its ShaderResources, and a draw its snapshot buffers, with a size of 0, so
the host-visible copies of guest buffers they own never counted. A title
that binds a 124 MiB writable guest buffer in every draw then held about
ninety copies, 11 GB, until the GPU caught up, and aborted with
VK_ERROR_OUT_OF_HOST_MEMORY from vkAllocateMemory.

GuestBufferMemory reports the bytes of its copied regions, and the
dispatch and draw keeps and the draw snapshots pass their bytes to
Recorder::Keep, so the existing bound applies to them.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-11 00:33:07 +02:00
Dean Galvin 2485915a03 perf(agc): skip the resource-cache key and lookup for address-based draws
A draw whose stages bind a BDA table or a fault buffer builds resources
that need completion work, so noteReusable never keeps them and the shared
resource cache never holds an entry such a draw could hit. Building the
content key and probing the cache for each of them was pure cost. Those
draws now skip both (ShaderResources::NeverReusable) and count as
uncacheable.
2026-10-10 23:02:52 +13:00
raghav314andClaude Opus 5.5 c11a9cb59f fix(agc): address-based builds map only memory mapped for the GPU
The registry records whether a range was mapped or protected with GPU
access (0x10/0x20), and an address-based build leaves out ranges without
it, as the console's GPU page tables do. The main image is registered
without GPU access; the title's own mprotect with GPU bits adds it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 14:34:34 +05:30
Adrià 4fbf6ca5a3 Merge pull request #1960 from Raghav314/fix/agc-import-leaves-pages-unarmed
fix(agc): host imports write-protect only pages a collect already protects
2026-10-10 10:45:04 +02:00
seven 5b9fdd111a fix(agc): count a pixel stage's colour attachments against maxPerStageResources
Vulkan counts the framebuffer colour attachments against maxPerStageResources
for the fragment stage. The draw's attachment count now seeds the fragment
stage's resource total in ShaderResources, so a pixel shader whose buffers and
images fit the limit but whose pipeline exceeds it throws instead of creating
an invalid pipeline. The draw resource cache key carries the attachment count,
so a cached build is never reused by a draw with more attachments.
2026-10-10 12:07:56 +05:30
raghav314andClaude Opus 5.5 c7f4690cd3 fix(agc): host imports write-protect only pages a collect already protects
Importing guest memory collected the write watch over the whole import,
which write-protected every page of it, so pages nothing had collected yet
took a write fault on their first CPU write. The import now collects only
the parts a collect already protects (GuestWriteWatchCollectArmed). The
rest stays unprotected until something collects it, which reports it as
written; a GPU write recorded into such memory (MarkWritten) first collects
the never-collected parts of its 64 KiB blocks
(GuestWriteWatchCollectFresh), so that report is older than the GPU's
results and does not read as a CPU write over them.

Those pages are stamped as written and their CPU pages are noted, but the
block's CPU stamp is left alone, so a GPU label record or a pending storage
image elsewhere in the block does not see a CPU write. StoredOver and
UnchangedSinceCollected read the noted pages whenever a block has them, so
a store to such a page is still seen by a check over that page. The extra
generation step happens only when something was collected; a block whose
write protection fails is unwatched and the write returns no generation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 11:38:28 +05:30
Dean d2612fb586 Merge pull request #1411 from Celegans12/perf/agc-heap-mirror-runs
perf(agc): skip unchanged heap mirror runs and idle mirror sweeps in address-based builds
2026-10-10 08:06:17 +13:00
Dean 05ba7f746a Merge pull request #1410 from Celegans12/perf/agc-imported-heap-mirrors
perf(agc): release a heap mirror once a host import serves its range
2026-10-10 08:06:03 +13:00
Dean 2af96d1863 Merge pull request #1085 from Celegans12/fix/agc-host-heap-budget
fix(agc): throw instead of aborting when a heap mirror cannot be made
2026-10-10 08:05:48 +13:00
Celegans12 4709920641 fix(agc): throw instead of aborting when a heap mirror cannot be made
An address-based build whose heap mirror the device refused, or one past
APS5_HEAP_MIRROR_MIB, stopped the process inside acquireMirror. PPSA26344
hit it at 478 s with 6.8 GiB of heap mirrors beside the host imports.

- a mirror that would go past the budget first sweeps the mirrors whose
  registered range is gone, before its allocation; if it still does not
  fit, the build throws "past the budget"
- the mirror is allocated once: a refusal throws with the range, the
  Vulkan result and the mirror totals, so the draw or dispatch fails like
  any other allocation; nothing is registered for the refused range
- the BDA resource tests run with a 4 MiB mirror budget and a mock memory
  limit: a mirror past the budget is made in the swept mirror's memory,
  and a refused one fails after a single allocation attempt
2026-10-09 14:58:20 +02:00
Celegans12 3f00dc7841 perf(agc): release a heap mirror once a host import serves its range
An address-based build maps every readable registered range: through a
host import when one can be made, else through a heap mirror, a
host-visible copy of the range. A range mirrored while the import budget
was full kept its mirror after a later pass imported it, until the guest
freed the range. The pass then read the range through the import, and the
mirror only held host-visible memory and a second copy of the bytes.

The pass now releases the heap mirror of every range it serves through
an import. Recorded work that bound the mirror keeps it until it
completes. If the import goes away while the range stays registered, the
next pass makes a new mirror from the guest bytes. The [buffers] image
mirrors profile line counts the released mirrors.
2026-10-09 14:58:17 +02:00
Celegans12 e804ce85f7 perf(agc): skip unchanged heap mirror runs and idle mirror sweeps in address-based builds
A build served by the cached address space walked every region of the
space for its mirrors, sorted the heap mirrors, compared each heap
mirror's blocks with the write tracker under its own lock and swept the
whole mirror registry. In an offline replay with 37,000 heap mirrors
that took about 10 ms per build, every build.

The space now keeps its writable mirrors, its sorted heap mirrors and
their adjacent runs from when it is published. Each run is still
flushed and collected; a run with no block stamped since the generation
its last complete check collected skips the per-mirror compare, and that
generation is stored only once the build's blocks were copied. The
registry sweep is skipped while every live mirror's range is held by the
cached address space's lease and no mirror was added, replaced or
dropped since.
2026-10-09 14:58:16 +02:00
Celegans12 52eb4272de fix(agc): copy an imported range once a merge extends it past its import
An address-based build maps each imported registered range through its
host import. UploadPrepare merges a descriptor range or snapshot
overlapping such a range into the range's region, and the region kept
the import even when the merge took it past the import's end, as a V#
crossing into the next registered range does. Descriptor then bound the
import's buffer beyond its size, and the BDA table mapped the whole
region, the next range included, through the import's device address.

A merge that extends a region past its end now drops the import. The
region then takes the import lookup and, with no import covering it,
the copy path, as a range crossing an image mirror's end already does.
Like every copied range, it is read-only to pointer stores.
2026-10-09 12:11:06 +02:00
boykopovar 1b3da3a740 test(agc): update graphics binding and bda range checks 2026-10-08 17:37:40 +03:00
Celegans12 38e89df1a5 fix(agc): bind guest buffer views that start off a DWORD boundary
A V# whose base is not a multiple of 4 bytes from the start of its GPU
owner threw in GuestBufferMemory::Descriptor, because the shader added
the offset below the view only in whole dwords.

Descriptor now binds from the offset alignment below the view and
passes the whole remainder. Such a view's range covers the dwords
around it, and its descriptor region starts and ends on dword
boundaries, so a lone view copied to its own buffer binds the same way.

The buffer specialization records the V# base's low two bits. Raw
vector loads through such a V# join two dwords, glc dwordx2 loads
included, stores merge their bytes into both with compare-exchange
loops, and bounds checks include the second dword. s_buffer_load
ignores the low two bits, as SrtEvaluator does. Formatted accesses
whose element alignment (min(element bytes, 4)) the base breaks throw,
as they fault the wave on RDNA2, and so do atomics.
2026-10-08 13:01:27 +02:00
Dean Galvin 4ad7103e56 test(agc): run agc_driver_graphics_tests under ctest and build it outside Windows
Nothing built the graphics test target, so it is now registered with the other AGC tests. It set
APS5_PIN_WAIT_MS through _putenv_s only, which does not exist outside Windows; it now uses setenv
there. The heap mirror tests need a write-watched guest arena and are skipped with a notice
without one, as the recorder tests already do.
2026-09-30 23:37:40 +13:00