Repository navigation
Conversation
|
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.
|
|
Tested on Minecraft Dungeons II (PPSA16064, UE). Windows 11 (26200), NVIDIA GeForce RTX 5070 (driver 610.88), WinLibs GCC 15.2, Release. Build: With
Verify run ( Rendering. Correct. We took screenshots every 3 s over 90 s of gameplay in both runs: no missing or wrong textures, no flicker, and frames match the base build. First boot with an empty shader cache. It reached gameplay on the first try with these switches and Summary line (second run) |
CnRJay
left a comment
There was a problem hiding this comment.
Stacked on #6946, #5656 and #6632, so this should be reviewed after those land. Notes on the numbers and the design.
- The gain over #6946 is p50 12.2 to 10.9 ms, but
gpu_busy_ms_per_presentgoes from 2.157 to 3.438 (+59%), and p99/max are slightly worse (15.49 to 15.92 ms, 17.89 to 18.81 ms). This moves per-draw descriptor work from the CPU to the GPU. On a 4090 at 1080p that is free; on a card that is already GPU-bound it is a regression. Please run the same pair on a mid-range card (RX 6600 or RTX 3060 class) and in a scene where GPU time is the limit. - One run each in a menu again; no gameplay scene and no repeat to show the spread.
- Known limit: the heap holds every texture it placed until its sweep frees the slot, so heap textures escape the texture cache's budget, and a scene with more than 4096 distinct textures of one kind stops with "the global image heap is full". That is a hard stop on a path meant to be a speed-up. It needs either eviction or a fall back to the per-draw path when the heap fills, and a test for it.
- Known limit: a pre-rasterization layout with an empty set 1 against a heap layout with vertex-visible bindings is a validation error (
VUID-VkGraphicsPipelineCreateInfo-pLibraries-06758), listed in debt. A validation error that gives undefined behaviour on some drivers is not debt; the combination should be rejected or avoided. - 5.7k lines and 98 files including the stacked PRs. Once the stack lands, check whether the heap and the "space reuse" part (which also depends on
APS5_DRAW_PIPELINE) can be two PRs.
|
@CnRJay on splitting the heap from the space reuse: I tried the heap in smaller steps first, and it did not help on its own.
The GPU time and the other points are a separate reply; the Minecraft Dungeons II numbers from @TideOPL are on that side too. LLM assistance used. |
|
@TideOPL thanks, this is very useful. It is the first run on a title that is not Hades II, and also the first on Windows, at 4K, and on a GPU that is not a 4090. Draw pipeline. That matches what the PR moves. Here queue 0 spends about 53 ms per frame in Heap. That is a real regression on this title. Thanks for the verify lines too: 0 wrong slots and 0 differing constants means it is slow but correct. In your slower run, GPU busy goes from about 45 to 60.5 ms per present, and Abort in the base mode ( LLM assistance used. |
|
@TideOPL's run shows the heap 12 to 23% slower on Minecraft Dungeons II at 4K (GPU busy 45 to 60.5 ms per present), correct but slow, and @Raghav314 agrees it's a real regression on a GPU-bound title. @CnRJay's asks stand: a mid-range card and a scene where GPU time is the limit, an eviction or a fall back to the per-draw path when the heap holds 4096 textures, and |
|
@TideOPL I reproduced the Minecraft Dungeons II slowdown with my own dump (PPSA16064 01.100, Linux, RTX 4090, 4K output), at a fixed spot in Squid Coast, and pushed three commits that fix it. With
The first two rows are local builds with extra timing marks, the last is these commits as pushed. GPU time was never the difference; the heap made queue 0's draws slower on the CPU, and the compute queue waited on them (
Hades II with both switches on the same build: 909 presents in 10 s (899 before), p99 13.4 ms (15.9), GPU busy 2.07 ms per present (3.44). Your other points (the trace files and the base-mode abort on LLM assistance used. |
|
@Raghav314 Coordination for native-MSAA buffer-read follow-up #7002, dependent on #6995 and #6498: tested fork. It adds Your heap change edits the adjacent BDA eligibility condition and in-place read lookup. If yours lands first, I will retain the handoff before your new early-return condition when rebasing our follow-up. Our fixture matrices have 81 passes across explicit RTX5080, Radeon Graphics and llvmpipe source pins, with 27 targeted refusal and alias-order control failures. The combined GPU-heap/space-reuse path remains untested here. Please flag any ordering constraint I have missed; exact evidence is maintained in #6480. AI-assisted: Codex; author responsible. |
|
007 First Light (PPSA11386 v01.018.000), 4K output. The setup, base and draw-pipeline numbers are in my comment on #6946: #6946 (comment) Head ddb3938; the commits pushed since (
Cold boot with an empty shader cache and no pipeline journal, switches off:
I can rerun with the current head, or with timing marks if you add them. AI-assisted: yes (Claude Code) |
There was a problem hiding this comment.
Tested fab8dc6 on Linux, RTX 5080 (driver 615.78.08), Ryzen 7 7800X3D, stacked as in the body on main 2fab88d, all runs with APS5_PIPELINED_DRAWS=32 APS5_PIPELINE_DISPATCH=1 APS5_DRAW_PIPELINE=1.
Hades II (PPSA36082) crashes on a first boot with an empty shader cache when APS5_GPU_HEAP=1 is set: SIGSEGV at 17 to 30 s in 4 of 4 runs, before the title screen. The core shows the title calling memcpy with a NULL destination (corrected, see my later comment). The same lib set with the heap off reaches the first area of a new game at 340 s (2 of 2), as do the base and 6946 (2 of 2 each). Warm, the heap runs reach gameplay (3 of 3).
Warm numbers, frames in 10 s, two traces each:
| 6946 | 6947 with the heap | |
|---|---|---|
| Astro's Playroom (PPSA01325), first level | 138, 135 | 84, 83 |
| Hades II, first area | 1122, 1054 | 1192, 1194 |
Captures match and the earlier Playroom flicker is gone (burst of consecutive frames). APS5_VERIFY_GPU_HEAP=1 in Playroom: up to 9,200,002 image slots checked, 0 hold another texture; Hades II: 8 GPU-loaded constant words, 0 differing. Playroom cold-boots into the level. The PR's tests pass on the RTX 5080, a Radeon 610M (RADV) and lavapipe.
…its render pass recordDraw passed every address-based draw to the pass hazard tracker as both an unknown reader and an unknown writer of guest memory. An unknown writer conflicts with any earlier access in the pass, so no address-based draw ever continued the previous draw's pass: each one ended the pass and began a new one, which loads and stores the attachments again. At Resident Evil Village's menu about half of all draws are address-based, and about 590 render passes begin for about 850 draws per frame. A build whose programs only read through the BDA table now reports unknown reads only. The tracker already orders unknown reads behind any recorded range write in the pass and keeps later range writes behind them, so such a draw continues a pass whose earlier draws wrote no guest ranges. Builds with stores through the table, copied writes, or an address lease without a table keep both flags. APS5_NO_ADDRESS_READ_PASSES=1 restores the old behaviour. Render targets reach guest memory only by explicit write-backs, so a read through the table sees the same bytes whether or not the pass breaks. Raghav314's boykopovar#6947 makes the same change inside its GPU descriptor heap, with the read-only fact from boykopovar#6946's recompiler field; this one keeps it in the driver, without a shader cache format change. The idea also comes from fable-void's 5d57298 (wip/new-gpu-path), which orders address-based builds by their known access ranges; this takes the read-only half without his run-time pre-pass bounds.
|
@Raghav314, a question before we build on this. We'd like to add run-time image tables on top of your heap. A T# that a shader loads from a table, at a key it computes, would be looked up on the GPU in a device-side key table: the T# words, an image interface id and a sampler class give the heap slot and view mode. A miss ring is drained after each batch. That replaces the CPU snapshotting and resolving every table record per draw. Resident Evil Village (PPSA01556) snapshots tables of 32,768 records at its menu, about 8 ms each. Its menu's draws are all indirect, which Are image tables, or indirect draws in GPU mode, already on your list? If they are, we'll wait for yours rather than overlap. If not, we'd stack this as a follow-up with |
|
@Raghav314, Windows notes for this stack, from a Windows build of current main merged with this PR's head fab8dc6 (WinLibs GCC, as the Windows job uses): https://github.com/EagleBearerYS/AnyPS5/actions/runs/39008923574
The other driver tests set variables with
Those are the only Windows failures the build and the suite show. |
|
@fma-rc thanks for the runs. I can't reproduce the cold-boot crash here (RTX 4090, 615.78.08): first boots with an empty Hades II needs
On Astro's Playroom (138 to 84 with the heap): a 70:10 trace pair ( LLM assistance used. |
|
@Raghav314, in case they help with the Windows notes above: tested fixes for all nine thread_locals are on EagleBearerYS/AnyPS5
Both move the scratch into |
|
@EagleBearerYS thanks, I took your three commits as they are: bdeb1ad is on #6946 (d63a804) and all three are on this PR (27d2b5e). On Linux the Image tables and indirect draws in GPU mode are not on my list, so go ahead with them as a follow-up with LLM assistance used. |
|
Thanks! We'll do image tables and indirect draws in GPU mode as a follow-up with |
|
@Amoenus thanks for checking. I don't see an ordering constraint from this side. The The early return LLM assistance used. |
|
@Raghav314 I have to correct my earlier description of the core. The title does not call an unmapped address: at Playroom 70:10 traces on the same lib set, same scene in the captures (one no-heap run whose window landed at 420 s in another scene is left out). Per frame, from
So both the recording on queue 0 and the GPU time per frame grow with the heap. I can attach the traces if they help. |
|
Rebase handoff — rebased onto current
Verified on Linux (CachyOS, kernel 7.2.9, GCC 16.2.1, Ninja, Release,
To pick it up: Head is |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…+ builds ResultKeyMemo declared std::atomic<std::shared_ptr<...>>. libc++ has no atomic<shared_ptr> specialization, so with Apple clang the recompiler headers stop at std::atomic's trivially-copyable static assertion (request_serializer_tests in the macOS relinker build). The memo now uses AtomicSharedPtr from libc, which is that specialization where the library provides it and a locked shared_ptr with the same load and store otherwise. The header joins the shader disk cache key's sources, and request_serializer_tests gets core/libs on its include path.
…nts for vertex stages A stage compiled with gpuResolvedImages names its images and samplers by heap slot through its metadata instead of per-draw descriptors. A vertex stage compiled with gpuConstants reads its SRT constants through a BDA range cache on the GPU. The prepared invocation can be built from a moved request and the extension key words are kept per thread. Disk cache format 39. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Eligible fragment, vertex and compute stages bind one global image and sampler heap and a ring of per-draw data instead of per-draw descriptor sets; slots the CPU does not know are resolved by a compute pass before the batch. Vertex stages of heap draws read GPU constants, address-based draws with read-only BDA reuse their address space within an epoch, and the pipeline journal describes the heap layouts. APS5_VERIFY_GPU_HEAP checks slots, rebuilds 1 in N heap stages on the CPU path and compares GPU-read constants. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t are address-based anyway Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…U stays off the heap Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The GPU heap of upstream PR 6947 (Raghav314, ddb3938) added thread_local objects with destructors to libSceAgcDriver.prx: the heap fragment memo, MaterializeHeapStage's reads, ranges, values and walk memo, and writeHeapStage's key and patched words. On Windows their destructors run after emulated TLS has freed them, which prx_thread_local_destructors reports. They now live in HostThreadLocal.
agc_driver_heap_slots_tests calls setenv, which MinGW does not declare, so the Windows build stopped at BindlessHeapSlots.cpp. It now sets APS5_HEAP_SLOT_CAP the way the other driver tests set variables: _putenv_s on Windows, setenv elsewhere.
A heap stage's template was keyed by its full descriptors, so a draw whose constant buffer or texture moved built a new template and a new set of resources. The template is now keyed by the descriptors' shape (as the binding plan is), and each draw carries its own descriptor words for the bindings that differ from the template's. Vertex stages and ring entries carry them too. A draw whose words equal the template's carries none and skips Populate. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A misaligned guest buffer of a heap stage was staged. It is now bound in place, with its offset patched into the stage's shader data, as on the per-draw path. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Only fragment and vertex stages are built in GPU mode. Compute targets, pipeline layouts and dispatch recording are back to what main has, and the heap's set layouts and counters are vertex and fragment only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every draw whose descriptors differed from its template copied the whole stage result to swap in its words, on the finisher thread. Readers now take a draw's descriptor words and vertex attributes through one accessor, so no result is copied. The heap content key also covers the samplers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A heap stage's variant was prepared at its first draw, so a cold boot linked and compiled pipelines on the committer thread. The stall let a game's staging allocator run dry, and Hades II crashed at boot. Heap variants are now prepared at registration and their pipeline libraries prebuilt first. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A heap stage's per-draw user data and flattened SRT were applied only where the stage data is written to the heap. Every other reader of the shader data and SRT bindings (data hashing, the unchanged-data check, validation) saw the template draw's words. The per-draw words now carry both bindings whenever they differ from the template's, also on a ring vertex hit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
APS5_VERIFY_GPU_HEAP now also checks, on sampled draws, that every binding read through the per-draw accessor equals the patched stage result, and reports each mismatch as it happens. With APS5_VERIFY_DRAW_ENTRIES or APS5_VERIFY_DRAW_DATA_HITS, a ring vertex hit's reused words are compared with a fresh build. A direct heap stage whose full capture could not read every descriptor is counted as not comparable instead of as differing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The range cache was turned on for every stage whenever APS5_GPU_HEAP is set, but compute stages are not on the heap anymore. In Astro's Playroom it made three compute shaders slower (one went from 1.7 ms to 4.0 ms a frame), which was the whole GPU gap between heap on and heap off. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A heap stage takes its image and sampler slots on the committer, after
the draw already chose GPU-mode stages, so a full heap aborted the title
("the global image heap is full"). When taking a new slot leaves fewer
than 1024 image slots of a kind (64 samplers) free after a sweep, the
heap is refused for the rest of the run: new draws build per-draw stages
and lookups skip GPU-mode variants, while the draws already queued use
the margin. The worker only reads one flag.
Tested with APS5_HEAP_SLOT_CAP=1 (new heap draw sample tests, plain and
pipelined) and in Astro's Playroom with APS5_HEAP_SLOT_CAP=256.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s words The image table, sampler table and table map of a stage were bound and checked from the program's bindings, so a heap stage would use its template's table words. Today a stage with tables keys its heap template on all of its descriptors, so the template's words are the draw's own, but nothing kept it that way. They now come from DrawGuestWords, like the other bindings, also in the table reuse check and in revalidation. A ring vertex hit reuses the inserting draw's template across draws whose user words differ, so a vertex stage with tables is no longer kept as a ring variant. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The heap keeps a reference to every texture it has placed, and the sampled texture cache keeps an entry whose texture has any other holder. So with APS5_GPU_HEAP=1 the cache passed over every heap texture until it was a quarter over budget, and the textures it then dropped stayed alive until the heap swept, which happened only every 1024 new slots. A title that peaks near the card's memory without the heap ran out of device memory with it. A texture now counts its heap slots. The cache does not count them as holders, and dropping such a texture marks a heap sweep due, which the next present runs. The sweep frees a slot when the heap's slots are the texture's only holders, so a texture in two slots is freed in one sweep. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…free up The committer swept every heap slot on every 1024th new slot. It now only marks a sweep due there, and the next present runs it, together with a sampler sweep. The sweep a full kind needs to place a texture stays on the committer. A refused heap is taken again when a sweep leaves twice the margin free in every image kind and in the sampler heap, so a title that fills the heap once, for example while loading, gets it back. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ShaderResources::BindHeap bound the heap (set 1) and the data set (set 2, with its four dynamic offsets) in one vkCmdBindDescriptorSets call. Lavapipe's handle_descriptor_sets hands a call's remaining dynamic offsets to apply_dynamic_offsets for every set in the call, which makes a new set (lvp_descriptor_set_create zeroes it) and copies the old one into it, so the whole heap set was zeroed and copied on every bind. The heap set is now bound alone with no dynamic offsets, and the data set with its four offsets in a second call on the same pipeline layout, so the heap set stays bound. On lavapipe a loop of 2,000 GPU-mode direct draws went from 8,944 to 958 us per draw. Drivers that do not copy sets for dynamic offsets see the same bindings as before.
…es_back on Windows The comes-back mode of agc_driver_heap_slots_tests, registered in f2a7557, had no Windows PATH entry, unlike the executable's own registration in the agc test list. On Windows it exited 0xc0000135 (a DLL not found) before running.
0cc74c2 to
f901322
Compare
…xp/lane-a-v4 at 046eb32
…, and argument-validation parity (#564) * feat(libc): implement ilogb and ilogbf (cherry picked from commit 0edfefa) * feat(libc): implement clearerr_unlocked, fileno_unlocked and fgetln Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 22c584a) * feat(libc): implement bounded error message APIs (cherry picked from commit 14acf7f) * fix(libkernel): answer F_GETLK with F_UNLCK on Linux and macOS fcntl threw for F_GETLK with an F_UNLCK request on the hosts that use POSIX locks, as Linux's F_GETLK rejects that type. FreeBSD 11.4's kern_fcntl accepts it and lf_getlock reports the first overlapping lock that lf_blocks lets conflict: another owner's lock, one of the two being a write lock. For an F_UNLCK request that is exactly another owner's write lock, the same set an F_RDLCK request finds, so the host is asked with F_RDLCK and the reply is written back as for the other types. (cherry picked from commit 41ec785) * test(libkernel): cover F_GETLK against another process's record locks The fcntl tests only ever held locks in the guest's own process, where FreeBSD's lf_blocks never reports a conflict, so the host lock path that answers F_GETLK was not exercised. A forked process now holds a read lock, a write lock and a write lock to the end of the file through the host. F_GETLK with F_UNLCK reports only the write locks, F_RDLCK the same and F_WRLCK the read lock, each with the owner's pid and SEEK_SET; F_SETLK over the read lock fails with EAGAIN, and nothing is reported once the owner exits. (cherry picked from commit 9ece094) * fix(libkernel): measure the host stack reserve from the frame address MeasureStackReserve took the distance from the stack top to a local variable. In an ANYPS5_ENABLE_ASAN build that local lives on AddressSanitizer's fake stack (detect_stack_use_after_return is on by default on Linux), away from the thread's stack, so the reserve could come out negative. scePthreadCreate then asked the guest arena for a stack of nearly 2^64 bytes and threw 'guest address space arena exhausted'. The frame address is on the real stack in every build. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit e88b7de) * fix(libkernel): leave a blocked SIGALRM expiry pending An ITIMER_REAL or alarm expiry while SIGALRM is blocked is marked on the pending set of sigtimedwait instead of throwing, as kern_psignal leaves a blocked signal pending. sigwait, sigwaitinfo and sigtimedwait take it, repeated expiries merge into one pending signal, and unblocking it runs a guest handler on the unblocking thread or, with the default disposition, exits the process with status 142 as FreeBSD's default action does. The expiry rechecks the mask after marking the signal, so an unblock that races it does not leave SIGALRM pending while unblocked, and TakePending clears the bit with the value it read so one signal is taken once. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 1bc029d) * fix(libkernel): accept an uninitialized attribute in scePthreadCondInit scePthreadCondInit, and pthread_cond_init through it, threw when attr pointed to a null attribute: a zeroed one that was never initialized or one that scePthreadCondattrDestroy has set to null. FreeBSD 11.4 libthr (thr_cond.c, cond_init) takes a null attribute as the default one and creates a CLOCK_REALTIME condition variable. A PS5 (firmware 13.60) takes a zeroed attribute as the default one in scePthreadMutexInit (boykopovar#4158). Take it as the default here too, as scePthreadMutexInit and scePthreadCreate already do. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 111d817) * fix(libkernel): treat a null deadline as an untimed lock in the timed pthread locks pthread_rwlock_timedrdlock, pthread_rwlock_timedwrlock and pthread_mutex_timedlock threw for a null abstime. FreeBSD's libthr passes a null abstime on to the untimed kernel lock, so these calls then wait like pthread_rwlock_rdlock, pthread_rwlock_wrlock and pthread_mutex_lock. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 676ee71) * fix(libkernel): return EINVAL/EFAULT from getrusage instead of throwing FreeBSD's getrusage rejects an unknown `who` with EINVAL before it copies anything out and returns EFAULT when the result can't be written. The HLE threw std::invalid_argument for both, which ends the title. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 01c8ceb) * fix(libkernel): return EFAULT from sceKernelRead/sceKernelWrite for a null buffer read and write already fail with EFAULT for a null buffer and a non-zero length and treat a zero-length null buffer as empty. The sceKernel entry points threw std::invalid_argument for both. They now do the same as read and write. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit b437dc5) * fix(libkernel): return EINVAL from the scePthreadAttr stack and guard size calls scePthreadAttrGetstack, Getstackaddr, Getguardsize, Setguardsize, Setstack and Setstackaddr threw for a null attribute, a null pointer argument or a stack below the 16 KiB minimum. FreeBSD's libthr returns EINVAL for those, as the pthread_attr_* wrappers here and the other scePthreadAttr calls already do. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 19b236a) * fix(libkernel): return EINVAL from read and write for a byte count above SSIZE_MAX FreeBSD's read, write, pread and pwrite reject a byte count above IOSIZE_MAX with EINVAL before they look up the descriptor. write threw std::invalid_argument for a negative count, and the others passed the count on to the host, which reports EFAULT on Linux. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 28acd09) * fix(libkernel): return ENOTSUP from pthread_attr_setschedparam and setschedpolicy like libthr FreeBSD's libthr rejects a null sched_param and, under SCHED_FIFO or SCHED_RR, a priority outside sched_get_priority_min..max with ENOTSUP, and a policy outside SCHED_FIFO..SCHED_RR with ENOTSUP. The wrappers returned EINVAL for the null parameter and stored any priority or policy. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit b693ef2) * fix(libkernel): return 0 from sceKernelMprotect and sceKernelMtypeprotect for a zero length mprotect already returns 0 for a zero length. The sceKernel entry points passed it to DoMprotect, which throws for an empty range. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit cbf006c) * fix(libkernel): return EFAULT from sceKernelWriteThrottlingStatus for a null status (cherry picked from commit a295b77) * fix(libkernel): return EFAULT from clock_gettime for a null result pointer clock_gettime threw invalid_argument when the result pointer was null, before looking at the clock id. FreeBSD 11.4's sys_clock_gettime reads the clock through kern_clock_gettime, which fails with EINVAL for an unknown clock, and only then copies the result out, so a null pointer fails with EFAULT. Read the clock into a local value first, then fail with -1 and EFAULT when the pointer is null. A clock that is not supported still throws before the pointer is looked at. Test the null pointer for the supported clocks. Written with AI assistance (Claude). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> (cherry picked from commit c17c6a5) * fix(libkernel): take NOTE_LOWAT on socket read and write events kevent threw for NOTE_LOWAT. On a socket's EVFILT_READ it now sets the low-water mark as FreeBSD 11's filt_soread does: the event fires once the buffered bytes reach it, or at end of file or on a listening socket, and a later change of the knote replaces it. EVFILT_WRITE takes a mark of 1, where the host's writability stands in for a free byte; other write marks and marks below 1 still throw. A pipe ignores the mark, as filt_piperead does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit e1d4ccb) * fix(libkernel): reject connects on listening sockets first (cherry picked from commit 00ca965) * fix(libkernel): reject PTHREAD_PROCESS_SHARED in pshared setters as the PS5 does On a PS5 (firmware 12.70), of the values 0, 1, 2 and -1, pthread_spin_init, pthread_barrierattr_setpshared and pthread_mutexattr_setpshared accept only PTHREAD_PROCESS_PRIVATE (0); 1, 2 and -1 return EINVAL and leave the attribute unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit db504f6) * fix(relinker): decode near branches with an operand-size prefix as AMD does 66 E8, 66 E9 and 66 0F 80-8F without REX.W take a 16-bit displacement on AMD processors, the console included. Every near relative branch with that prefix truncates its target to 16 bits, so the decoder reports no branch target for it and stubs refuse to move it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016TFMtUC4L4wMS4eeEqaNSR (cherry picked from commit 206aaa5) * fix(relinker): keep the Windows TLS rewrite from moving truncated branches A short TLS access followed by a near branch with an operand-size prefix decided the branch was relative from its flow kind and opcode alone, then re-encoded it to the fall-through address or wrote its rel32 over the prefix and opcode. Such a branch now counts as one that cannot move. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016TFMtUC4L4wMS4eeEqaNSR (cherry picked from commit aed1ffb) * fix(relinker): move a neighbour without an FS or GS prefix with a 256-bit store first A 256-bit store is 4 bytes, so its stub window takes a neighbouring instruction to make room for the jump. The window took the following instruction first even when it was a guest TLS access (an FS or GS prefix). The macOS writer cannot rewrite a TLS access inside a stub and refuses the module with "AMD-only instruction is also a guest TLS access". The window now tries the following and the preceding instruction without a segment prefix first, and falls back to the old order only when neither works, so outputs that can keep the access in the stub do not change. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> (cherry picked from commit 513b2e1) * test(relinker): keep an FS or GS access out of the window of a 256-bit store Covers a thread-pointer read (FS and GS form) after the store with a movable instruction before it, and the fallback to the following access when there is no other neighbour. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> (cherry picked from commit cf9e5a2) * feat(libSceJobManager): implement compute sequences and compute dispatches (cherry picked from commit 051a610) * feat(agc): count the render passes recorded draws begin and continue DrawPassTotals returns, since the process started, how many recorded draws began a render pass of their own or continued the previous draw's, and how many flushed the queued DCC key stores or label stores before themselves, for address-based draws and for the others. A draw is address-based when its resources hold the allocation registry lease, whether they were built for it or found in the draw cache. The totals are kept with the [draws] line's (APS5_PROFILE_DRAW), so a test or a harness can tell whether an address-based draw kept the open pass without reading the 10-second report. (cherry picked from commit 2d4512e) * perf(agc): let a draw that only reads through the BDA table continue its render pass recordDraw passed every address-based draw to the pass hazard tracker as both an unknown reader and an unknown writer of guest memory. An unknown writer conflicts with any earlier access in the pass, so no address-based draw ever continued the previous draw's pass: each one ended the pass and began a new one, which loads and stores the attachments again. At Resident Evil Village's menu about half of all draws are address-based, and about 590 render passes begin for about 850 draws per frame. A build whose programs only read through the BDA table now reports unknown reads only. The tracker already orders unknown reads behind any recorded range write in the pass and keeps later range writes behind them, so such a draw continues a pass whose earlier draws wrote no guest ranges. Builds with stores through the table, copied writes, or an address lease without a table keep both flags. APS5_NO_ADDRESS_READ_PASSES=1 restores the old behaviour. Render targets reach guest memory only by explicit write-backs, so a read through the table sees the same bytes whether or not the pass breaks. Raghav314's boykopovar#6947 makes the same change inside its GPU descriptor heap, with the read-only fact from boykopovar#6946's recompiler field; this one keeps it in the driver, without a shader cache format change. The idea also comes from fable-void's 5d57298 (wip/new-gpu-path), which orders address-based builds by their known access ranges; this takes the read-only half without his run-time pre-pass bounds. (cherry picked from commit a939efe) * test(agc): check that draws reading through the BDA table share a render pass agc_driver_address_passes draws a triangle twice onto a guest block, with pixel shaders that global_load_dword or global_store_dword through s[2:3], and reads DrawPassTotals for the address-based draws: - --readers: two draws reading through the table share one render pass; - --store-after-read: a draw storing through the table after one reading through it begins its own pass; - --read-sees-store: a draw reading through the table what the previous draw stored sees the store, in a pass of its own. Taken from c44c9a6 (perf/agc-bounded-address-passes) without its --store-beside-reads case, which needs that commit's store bounds. (cherry picked from commit b3e94a5) * fix(agc): let the storage image cache budget follow the heap's use like the sampled one StorageTextureBudget gave the storage image cache a quarter of the VK_EXT_memory_budget budget of the largest device-local heap (capped at TextureCacheBudget, never below 512 MiB) whatever else the heap held, while SampledTextureBudget takes what that budget leaves after the heap's other use. On a 10 GB card the storage cache kept 2024-2170 MiB into a level load whose depth and render target allocations then failed with VK_ERROR_OUT_OF_DEVICE_MEMORY with the heap at its reported budget where a run logged it. The storage image budget is now what the reported budget leaves after the heap's use other than the cached storage images, the unused part of the sampled textures' 2 GiB floor and an eighth of the budget; the caps at a quarter of the budget and at TextureCacheBudget and the 512 MiB floor stay. The sampled budget still reserves TextureCacheBudget for the storage cache, at least the storage cap, and the storage budget reserves the sampled textures alive, so neither counts bytes the other may take; while the sampled textures alive stay within their floor the sampled budget falls first, then the storage budget, and above it both fall together. The budget line also prints when the storage image budget moves by more than a tenth, with its previous value, and StorageTextureCacheBudget is exported for the execution test. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> (cherry picked from commit 39c2366) * fix(agc): decode COLOR_8_8 UINT as R8G8_UINT (cherry picked from commit bb39eba) * perf(agc): release settle-retired imports after the overlap block of a pin wait The Windows release of imports retired during a settle read belongs after the whole overlapsImport block in the not-holding-the-lock branch of WaitForLeasesAndImports, as in the original change, not inside it. It was placed inside while main was merged in. (cherry picked from commit fd301c7) * fix(recompiler): drop a sample-adjust S# dword 3 that sets only reserved bits An image_sample_*_a sampler whose dword 3 is built only from values inside the reserved bits 12-29 was refused with "GetSamplerResource dword 3 is not a valid runtime value" when the value was not an OR at the top. The reserved bits of a sample-adjust S# are already dropped term by term from an OR (canonicalizeSampleAdjustDword3); a dword 3 whose possible bits all lie in that mask, such as a shifted field with nothing else, is now dropped whole the same way. PPSA01317 builds such a dword 3 from a wave ballot shifted to bit 12 for image_sample_a in a pixel shader. A plain image_sample with the same S# is still refused. AI-assisted: yes (cherry picked from commit 6e5d708) * perf(shader): fold a select nested in a select on the same condition select(c, a, select(c, b, d)) is select(c, a, d), and select(c, select(c, a, b), d) is select(c, a, d). The recompiler leaves these behind when a register is written more than once under the same lane mask and when the f32 NaN selection tests the same source twice, and nothing folded them. The constant folder now takes the inner arm the outer condition already decides, then folds a select whose arms became equal. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 227d1e4) * fix(shader): read low-half 16-bit multiply factors as 24-bit fields On AMD's Windows driver 32.0.21045.5002 (RX 6700 XT), a product of two values the driver knows fit in 16 bits, reduced to 16 bits and stored with nothing else merged in, keeps all 32 bits of the product. v_mul_lo_u16 and v_pk_mul_lo_u16 extracted 16-bit factors, so into a register whose high half was zero they wrote 0xfffe0001 for 0xffff * 0xffff. A factor taken from the low half of its source is now read as the low 24 bits of the register; the low 16 bits of the product depend only on the low 16 bits of the factors. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit ffd468e) * fix(libkernel): recognize listening sockets on macOS hosts macOS does not support getsockopt with SO_ACCEPTCONN; it fails with ENOPROTOOPT. So on macOS the guest's getsockopt of SO_ACCEPTCONN failed, connect did not see that a stream socket was listening (an address of another family failed with EAFNOSUPPORT instead of EOPNOTSUPP), and the read event of a listening socket reported FIONREAD's 0 instead of the waiting connection and did not ignore its low watermark. On macOS the listening state is read from the SO_ACCEPTCONN flag of the socket options that proc_pidfdinfo reports, and the three places that ask go through NativeListening. Other hosts keep getsockopt. (cherry picked from commit 6f2a61a) * fix(libkernel): set the open file limit at load on macOS getrlimit(RLIMIT_NOFILE) reports the host's limits, and libkernel raised the soft limit to the hard one at load only on Linux. On macOS the soft limit stayed below an unlimited hard limit (1048576 and infinity in a shell here, 256 and infinity by default), so the two differed. XPSemu (PPSA97358) then raises the soft limit to the hard one in QEMU's os_setup_limits, and setrlimit refuses the change. macOS fails with EMFILE at kern.maxfilesperproc (10240 here) whatever the soft limit, even an unlimited one. At load, libkernel now sets both limits to the smaller of the hard limit and kern.maxfilesperproc, so getrlimit reports the descriptor count after which opening fails. The Linux path still raises the soft limit to the hard one. (cherry picked from commit 7391082) * test(libkernel): check the macOS open file limit against EMFILE guest_getrlimit failed on macOS because the host's soft and hard RLIMIT_NOFILE differed. On macOS it now also checks that the limit does not exceed kern.maxfilesperproc, and, as on Windows, that guest pipes fail with EMFILE within 64 descriptors of the reported limit. The pipe loop moves into a helper that both use. (cherry picked from commit 82af3e0) * fix(libc): keep errno when strtol converts nothing on macOS macOS's strtoll and strtoull set errno to EINVAL when the subject has no digits, as in "", "words" or " +". The guest's integer conversions pass that to the title, while guest_atoi, guest_wide_conversion and guest_math expect errno unchanged, as glibc leaves it. So atoi("") returned 0 with errno 22 on macOS, and all three tests failed there. A ParseInteger helper in SonyCrt.hpp calls the host's conversion and, on macOS, restores the caller's errno when nothing was converted and the base is valid. An overflow still sets ERANGE, and an invalid base still sets EINVAL. strtol, strtoul, strtoll, strtoull, atoi, atol, atoll, _Stoull and the wcsto* integer conversions in Strings.cpp use it. On other hosts the helper calls the conversion unchanged. (cherry picked from commit 839acfd) * fix(libc): keep errno in strtoimax, strtoumax and _Stoul on macOS The conversions in RuntimeSupport.cpp call the host's strtoimax, strtoumax and strtoull directly, so they set EINVAL on macOS for a subject without digits too. guest_math, once the strtol cases pass, failed on strtoimax(""). They now go through ParseInteger. (cherry picked from commit 8469126) * docs(debt): record the macOS RLIMIT_NOFILE at load in libkernel.md (cherry picked from commit 720ab86) * fix(agc): drop committed conflict markers in DepthSurface.cpp An earlier sync left an empty-vs-theirs conflict block in the file; keep the storage view members it carried, which later code calls. --------- Co-authored-by: BIGFERRY999 <bigferry24@gmail.com> Co-authored-by: T3cm0 <srodriguez7604@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: lorkydey <57268822+Lorkydey@users.noreply.github.com> Co-authored-by: Alexej Szpevák <szpevakalexej@gmail.com> Co-authored-by: milescoviello <kitslayer29@gmail.com> Co-authored-by: LaunchDoesntCode <launchogu@protonmail.com> Co-authored-by: Lindamea1 <322051987+Lindamea1@users.noreply.github.com> Co-authored-by: pushkarreddyy <212133856+pushkarreddyy@users.noreply.github.com> Co-authored-by: mattogepeto <mattogepeto@gmail.com> Co-authored-by: MarcZX100 <MarcZX100@users.noreply.github.com> Co-authored-by: ionut27001 <194206737+ionut27001@users.noreply.github.com> Co-authored-by: oskarlgs <oskarlgs@gmail.com> Co-authored-by: Morten Holm <mohoholm@gmail.com> Co-authored-by: fma-rc <188743521+fma-rc@users.noreply.github.com> Co-authored-by: Yunus Shaikh <75296019+EagleBearerYS@users.noreply.github.com> Co-authored-by: Jan Šlégl <slegl.jan@gmail.com> Co-authored-by: Maz <mazenshaar@hotmail.com> Co-authored-by: Dean Galvin <dean.galvin@live.com> Co-authored-by: Gjon Gojcevic <gjonn@hotmail.com> Co-authored-by: thuanquang <60096168+thuanquang@users.noreply.github.com> Co-authored-by: mugurc <mceker@gmail.com> Co-authored-by: David <David.Samir.WD@gmail.com>
What
An opt-in GPU descriptor heap,
APS5_GPU_HEAP=1, so eligible stages stop rebuilding their descriptors on the CPU for every draw. Stacked on the draw pipeline PR (#6946) and #5656 (#6632 is inmainnow); only the last 23 commits are this PR.HeapLookupResolve.comp) submitted before the batch.APS5_DRAW_PIPELINEis also set.APS5_VERIFY_GPU_HEAP=1checks heap slots against the bound textures, builds 1 in N GPU-mode stages again on the CPU path and compares, compares GPU-read constants with the CPU walk, and checks that every binding a heap stage's readers see equals the patched stage result. WithAPS5_VERIFY_DRAW_ENTRIES=1, ring vertex hits are compared with a fresh build.Update (rebased on
main48d61f5)userDataRegisterReadsa second time at the end of the invocation; perf(agc): data-only draw hits compare only the user words a stage reads #5656 already encodes it, so the second copy is gone.EncodeResult), so the journal format goes to 5 (4 is perf(agc): data-only draw hits compare only the user words a stage reads #5656's). The disk cache format is 40.main48d61f5, the linux job of currentbuild.ymlrun locally: configure with SPIRV tools on, test registration check, build, libs, the whole ctest (1537) and conventions. All pass exceptagc_driver_bda_device, which fails on plainmaintoo on this machine.Update (rebased on
maine5f18ea)main. The space reuse check usesmain's read-only rule from perf(agc): let a draw that only reads through the BDA table continue its render pass #7184 (SpirvStoresThroughAddresses); this branch's own rule, from the dropped straight-line SRT walk commit, is gone. Where a render pass may continue,main's condition stands unchanged.main's BDA anchor ranges (perf(shader): look up the BDA range of a user-data pointer once at program entry #5859) stay out of the BDA range cache that GPU constants use.docs/dev/ENVIRONMENT.mdrows forAPS5_GPU_HEAP,APS5_HEAP_SLOT_CAPandAPS5_VERIFY_GPU_HEAP(and in perf(agc): draw finisher pipeline (APS5_DRAW_PIPELINE) #6946 for its switches), sincemainchecks them now.maine5f18ea, the linux job of currentbuild.ymlrun locally: configure with SPIRV tools on, test registration check, build, libs, the whole ctest (1510) and conventions. All pass exceptagc_driver_bda_device, which fails on plainmaintoo on this machine (NVIDIA Xid 109, a context switch timeout, during the test).Update (rebased on
main47a266c)memcpyateboot+0x28fba9wrote to a null destination. Heap variants are now prepared and prebuilt at registration. Hades II cold boot (emptyANYPS5_SHADER_CACHE_DIRand NVIDIA shader cache) now reaches the menu, with no shader prepared at use.FMallocBinned3 Attempt to free an unrecognized pointer, once, in Squid Coast): most likely because only the heap write path applied a draw's own user data and flattened SRT, while data hashing, the unchanged-data check and validation saw the template draw's. Every reader now sees the draw's words. Found with the newAPS5_VERIFY_GPU_HEAPcheck (64+ mismatches before, 0 after); Squid Coast did not crash in three runs since.APS5_HEAP_SLOT_CAP=1(plain and pipelined); Astro's Playroom withAPS5_HEAP_SLOT_CAP=256andAPS5_VERIFY_GPU_HEAP=4fell back once, kept 155-158 presents in 10 s and 0 of 200004 checked slots held another texture.DrawGuestWords, like its other bindings. A vertex stage with tables is no longer kept as a ring variant, since ring hits reuse the inserting draw's template.APS5_GPU_HEAPwas set, compute included, although compute is off the heap. WithAPS5_PROFILE_GPU=1the draws took the same GPU time with the heap on and off (11.95 vs 11.89 ms a frame); the gap was three compute shaders, one of them 4.02 ms a frame against 1.71 ms. The range cache is now only in vertex and fragment stages; that shader is at 1.70 ms and GPU busy is 30.7-31.3 ms per present (34.3-34.6 before, 31.7-33.1 with the heap off).Same machine, both switches,
APS5_PIPELINED_DRAWS=32 APS5_PIPELINE_DISPATCH=1, one run each, compared with the same switches before today's commits (not with the heap off):APS5_TRACE_SUMMARY_EVERY=10): 861 / 856 / 869 presents in 10 s, p50 11.4-11.6 ms (832-833 at a488793), GPU busy 2.05 ms per present. This was a cold boot; no shader was prepared at use.APS5_DRAW_PIPELINE=1only): 156 / 157 / 156 presents, p50 63.5-64.0 ms, so parity; GPU busy is 34.5 ms per present with the heap and 31.9 ms without, which does not cost frames in this CPU-bound scene. With the range cache fix (last commit): 161-164 presents, p50 59.8-61.3 ms, GPU busy 30.7-31.3 ms.PPSA36082
The base is the draw pipeline PR (ecfd43302) with
APS5_DRAW_PIPELINE=1. In the level UI/menu, addingAPS5_GPU_HEAP=1takes the frame time p50 from 12.2 to 10.9 ms (807 to 899 presents in 10 s). Against #5656 + #6632 alone (469 presents, p50 21.1 ms) the two switches together are 1.92x. The picture is unchanged.Level UI/menu standing still, same machine,
APS5_HOST_IMPORT_MIB=6784 APS5_PIPELINED_DRAWS=32 APS5_PIPELINE_DISPATCH=1(the #6632 spin at its default, 150 us on this 16-thread CPU), noAPS5_PROFILE_*switch,APS5_TRACE_EVENTS=1048576so the traces are whole, shader cache warmed for each build, one run each. The runs are from before the rebase onto currentmain(on f474fb7, same commits otherwise); the rebase had no conflicts.APS5_DRAW_PIPELINE=1:APS5_GPU_HEAP=1 APS5_DRAW_PIPELINE=1:aps5-trace-c05527b38c16-1070518.json.gz - Main
aps5-trace-ecfd43302cbd-1070858.json.gz - PR 'A'
aps5-trace-b23fdf65f521-1071243.json.gz - PR 'A' + B
PPSA16064 (Minecraft Dungeons II)
@TideOPL measured this PR slower than the draw pipeline alone in Squid Coast. Reproduced locally (01.100, 4K output, same machine as below) and fixed by the last three commits. Same standing spot in Squid Coast,
APS5_PIPELINED_DRAWS=32 APS5_PIPELINE_DISPATCH=1, warm shader cache, trace70:10:APS5_DRAW_PIPELINE=1: 151.7 / 153.0 / 155.9 presents in 10 s over three runs, p50 63.5-64.6 ms.+ APS5_GPU_HEAP=1before the fixes: 129.5 presents, p50 76.4 ms.+ APS5_GPU_HEAP=1at a488793: 152.9 presents, p50 64.5 ms, p99 76.8 ms; GPU busy 36.3 ms per present in all of them.With the fixes, Hades II in the level UI with both switches: 909 presents in 10 s (899 before), p99 13.4 ms (15.9), GPU busy 2.07 ms per present (3.44).
Tested
Linux (CachyOS), x86-64, GCC, 7800X3D / RTX 4090, NVIDIA 615.78.08.
main47a266c, the linux job of currentbuild.ymlrun locally: configure with SPIRV tools on, test registration check, build, libs, the whole ctest (1428) and conventions. All pass. The draw pipeline PR alone on the samemain: 1391, all pass.agc_driver_store_over_untrackedhas been flaky before; it passed in both runs.Earlier, on
mainf474fb7 (before the rebase) with #5656, #6632 and the draw pipeline PR:The linux job of current
build.ymlrun locally on this branch: configure with SPIRV tools on, test registration check, build, libs, and theagc_*andshader_*scope (686 tests): all pass exceptagc_driver_store_over_untracked, which fails the same way onmain. The whole ctest (1313) passed the same way on this change before the last review fixes.With
APS5_GPU_HEAP=1 APS5_DRAW_PIPELINE=1 APS5_PIPELINED_DRAWS=32 APS5_PIPELINE_DISPATCH=1, theagc_*andshader_*scope: the same, plusagc_driver_draw_cache_relocation(the draw pipeline PR's known limit).APS5_GPU_HEAPwithoutAPS5_DRAW_PIPELINEpassed with onlyagc_driver_store_over_untrackedfailing before the last review fixes.The draw pipeline PR's switched tests and eight sampling tests also run with both switches and
APS5_VERIFY_GPU_HEAP=1(*_gpu_heap).New tests:
agc_driver_heap_draw_sample_heap,_nogpl,_nogpl_heap,agc_driver_heap_compute_sample_heap,_heap_pipelined,agc_driver_bindless_images_heap(a draw and compute stages that sample a texture write the expected texels, with and without pipeline libraries; with the switch the heap takes the sampling fragment and compute stages, while a compute stage without samplers and a sampling vertex stage whose constants are read on the CPU stay off it) andagc_driver_heap_slots_tests(a held slot is not reused, a freed slot moves its generation and its lookup key is revived for the new texture).Cold boot: Hades II boots with a cold Vulkan pipeline cache and a fresh NVIDIA shader disk cache, with and without a pipeline journal, and with an empty
ANYPS5_SHADER_CACHE_DIR; a frozen journal replays all 54 GPU-mode pipelines (0 rejected).At a488793 plus the test update: the
agc_*andshader_*scope passes exceptagc_driver_store_over_untracked(fails onmaintoo), and with both switches alsoagc_driver_draw_cache_relocation; conventions pass.Known limits
APS5_DRAW_PIPELINE=1alone in GTA V (@MichonGoddijn231849, 105.5 vs 126.8 presents) and in Astro's Playroom (@fma-rc). Playroom is at parity on the new head here; GTA V needs a retest.VUID-VkGraphicsPipelineCreateInfo-pLibraries-06758; listed in the technical debt file).Why this is one PR
Getting this to work took several days of builds and Hades II runs: several parts on their own made the frame rate worse, and only the combination below gave the gain.
The heap only helps when the CPU stops walking the stages it serves, so its pieces cannot land one at a time:
Testing other titles
The heap changes how most textured draws bind their images, so it needs testing on more games than Hades II, on NVIDIA and AMD alike. Please post:
APS5_TRACE_SECONDS=70:10 APS5_TRACE_EVENTS=1048576) withAPS5_DRAW_PIPELINE=1and withAPS5_GPU_HEAP=1 APS5_DRAW_PIPELINE=1, both withAPS5_PIPELINED_DRAWS=32 APS5_PIPELINE_DISPATCH=1and nothing else changed;APS5_VERIFY_GPU_HEAP=1, the last[heap-slots]and[gpu-heap]lines in the log (any slot holding another texture or any differing constant is a bug), and any Vulkan validation error that names a heap set;Checklist
main; no other open PR implements the same functionsNotImplemented_nid_no_patch); silent stubs are listed under Silent stubs in their library's technical debt file.mdfiles or images added to the repository (attach them to this PR)