A storage image refreshed over memory where other images still had results
pending stored those results first, then uploaded. When the image's DCC keys
are a clear code its upload is a GPU clear that reads none of that memory, so
those stores were dead for it. Unreal's render graph does this whenever it
hands transient memory to a new texture it fast-clears.
In write-watched memory the results now stay pending. Anything that reads the
memory before the image is written stores them as before. Once the image's own
results cover their 64 KiB units they are dropped, as its write-back stores
every byte of those units; an image written again since keeps its newer
results, and a unit stored after the hand-over no longer counts as a CPU store
against the image's write. The path is chosen from the keys read before any
flush, and only when no flush is needed. APS5_NO_CLEAR_HAND_OVER=1 stores the
results first as before.
While a trimmed import waits for the next refresh it pins no registered
range, so the guest can unmap its kept side and map new pages at the
same address without waiting for it. The new pages must be imported
again: the GPU has to read their bytes, not the old pages the trimmed
memory still pins.
agc_trimmed_import imports 32 MiB of singly mapped direct memory whole
and then cuts it: a 16 KiB page made inaccessible nearer its start or
nearer its end, an unmap of a 2 MiB resident run inside it, an unmap of
its first 2 MiB run, and an unmap of the first 1440 KiB of that run. It
checks that the larger side keeps the import's memory, or, when the
unmap replaced pages of the kept side's run, that it is imported again;
that the smaller side is imported apart from it; and that the GPU reads
the guest's bytes through each import while the write watch collects CPU
stores and reports none nobody made. Each unmapped span is then mapped
again at the same address, and the whole range must be served by a new
import that matches the new pages and the kept side.
The re-import bench only moved the start of the range. It now runs three
patterns, each on a fresh 1126 MiB range: the head or the tail loses GPU
access 1440 KiB at a time, or a 16 KiB page inside the kept side loses
all access (256 MiB in, then 1440 KiB above the one before). For every
cut it prints whether the kept side was trimmed or imported again, the
cut and import times and, for a cut inside, the import of the smaller
side. After each pattern it checks that the kept side still matches the
guest's bytes and write watch. The bench is registered on Windows only,
the one platform it runs on.
releaseTrimmed freed the trimmed import's memory at once when the
calling thread did not hold GpuMutex. It now hands the memory to the
active recorder whenever there is one, as retireImport does for the
imports importRunAt and refreshImports retire, so the memory outlives
the open batch and every batch submitted before it.
A protect or unmap strictly inside a plain host import still retired the
whole import, and the next access imported both sides of the cut again.
The larger side is now kept as a trimmed import, as a cut at the head or
the tail already is, and only the smaller side is imported again. Equal
sides keep the tail.
The summary line names the CPU, its thread count, physical memory and,
with param.json, the title ID, name and content version.
APS5_TRACE_BENCH=1 with APS5_TRACE_SUMMARY_EVERY rewrites
aps5-bench-<commit>-<pid>.json.gz at each interval with the time between
every two presents, so reports can be recomputed and checked from raw
frame times. param.json's contentVersion is read when present.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The over-limit row was a fixed 0xffffffff in X, which is a valid count
on devices that report that X limit (RX 9070 XT). Take the first axis
whose limit is below 0xffffffff and use limit + 1 there; skip when no
count can exceed the limits.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
recordDispatch now reads its indirect offset through HostImportOffset,
which adds the import's lead. The unchecked path passes that offset to
vkCmdDispatchIndirect; the clamp kernel reads from import.address plus
the guest offset, since import.address already includes the lead.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Backing only the pages a pointer touches splits a zero-filled run into
one section per backed block. Each becomes a Mach-O segment, and dyld's
bind and rebase opcodes address at most 16 segments, so a module with
pointer slots on many separate zero-filled pages converted on main but
failed with --macos. When the segments would pass that limit, adjacent
sections with the same protection are merged again, smallest gap first.
Reported by jotitadev in the review.
#6503 took request serialization 36 for its float32 flush flag, so this branch's sample order moves to 37. The version relabels in ShaderMemory.cpp and RequestSerializerTests.cpp drop both trailing flags and the sample order.
Fresh outputs whose names differ only in case, or that the volume treats as the same name otherwise, were not detected outside Windows. On default macOS APFS, for example, Ä/ä, Ω/ω, Ж/ж and canonically equivalent NFC/NFD names refer to the same file, so one output silently overwrote another.
Outside Windows, create the first name in an identity directory next to the outputs and check whether the second name resolves to it. This lets the filesystem decide instead of folding text, so case-sensitive volumes still report distinct names.
drem, dremf, remainderl and remquol now set EDOM themselves for an
infinite x or a zero y, as fmodl does. Apple's libm never sets errno and
MinGW skips it for long double, so the host could not be relied on.
Guest long is 64 bits, but the host long is 32 bits on Windows. The
lrint, lround and scalbln exports now take and return std::int64_t,
round through llrint and llround, and clamp scalbln exponents to the int
range, where the result is already infinite or zero. The test uses
INT64_MAX instead of LONG_MAX and covers remainderl, dremf and a 2^40
lrint result.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A DMA_DATA whose destination is nowhere writes no memory, so the draws ahead of it have nothing to order against. RADV issues one per draw, and each drained the pipeline and idled the queue worker until the committer caught up.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
The checked release tested that the range was allocated, released the pool lock and then
unmapped a snapshot of its views. Two concurrent checked releases of one range both passed the
test and took the same snapshot; the second then unmapped views the first had already removed
and terminated on "unmap address is not registered" instead of returning ENOENT.
A release now claims its pages in the pool under the pool lock before it looks at the views,
and frees them, dropping the claim, after the unmap. A checked release of pages another release
has claimed returns ENOENT as if they were already free; a plain release waits for that release
to finish. The pages stay allocated while claimed, so they still cannot be reallocated during
the unmap's lease wait.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Primitive type 19 (DI_PT_QUADLIST, 0x13) is used by Unity and other engines for UI quads, sprites and font glyphs. Because Vulkan lacks native quad-list primitive topology, quad draws are expanded on the CPU into indexed triangle lists with 6 indices per 4-vertex quad, respecting the first/last provoking vertex configuration and discarding incomplete trailing vertices. Adds unit tests and documents the functional debt.
dl_iterate_phdr takes the loader lock. Calling it with the base cache
mutex held inverts the order against a thread inside dlopen whose
constructor throws and reaches FindFrameFast. Probe first, then lock
only for the lookup and the insert; racing misses compute the same
bases and emplace keeps the first.
Co-Authored-By: Claude Opus 5.5 <[email protected]>