FindImage remembers, in a 256-slot table, each request that resolved to an
image with the same backing, keyed by the request bytes and a generation
that every index registration or removal advances. A repeated request
touches the image and returns the remembered result without walking the page
owner index. SHARPEMU_IMAGE_LOOKUP_MEMO=0 turns it off; [PERF][IMAGE_LOOKUP]
reports hits and misses.
Resource materialization read every descriptor and flattened-table word
through its own guarded TryReadGuestWord call, about 2.6 million per five
seconds in Demon's Souls. The raw reads of each plan are grouped once; at run
time their addresses are resolved, sorted and read as contiguous runs through
the resident guest reader, and the values seed the evaluator cache. A run the
GPU may still own, or one out of bounds or through a null base, falls back to
the word reads. The cache recorder records the prefetched words, and
SHARPEMU_RESOURCE_PREFETCH=0 turns the prefetch off.
VulkanAsyncReadback keeps eight slots: slot zero serves synchronous reads and
the others carry asynchronous downloads. A guest thread that reads GPU-written
memory lets the render thread start the copy and waits for it on its own
thread, then the render thread applies it if the range is unchanged; a
foreign read keeps the source buffer alive until it completes.
Windows the render thread had to read synchronously become hot. When a batch
writes into a hot window, the window is downloaded right after the batch is
submitted, and the next read uses that copy if it is still current. A pending
write to a hot window ends the open batch early. SHARPEMU_ASYNC_GUEST_READBACK=0
keeps the synchronous path; [PERF][ASYNC_READBACK] reports the counts.
A cache key now holds up to four materializations in most-recently-used order,
so alternating descriptor contents reuse their earlier result instead of
re-materializing. The cache report counts stale and unreadable entries and
table refreshes, and ReadingTable tells a reader when the flattened table is
being evaluated.
SharedBackingViews publishes its view snapshot as an alias identity, and a
reader can enter an alias access window once and resolve guest pages to their
backing alias without the views lock until the snapshot changes.
BoundedFillDetector recognizes the AGC range-fill and pattern-fill compute
programs, including thread-dimension dispatches, and a dispatch of one that
targets DCC metadata becomes a DCC fill instead of a compute dispatch.
Refused conversions are reported by the DCC writer trace.
A fill that targets DCC metadata which no GPU write owns is recorded as a GPU
fill and also written to the guest backing directly, so the range never
becomes GPU-dirty and later CPU reads need no readback. Ranges the GPU still
owns or that overlap an image keep the plain buffer fill.
ForEachPossiblyCpuDirtyRange combined a region lookup and a summary read for
every 4 MiB block of mapped memory on each device-address sweep. A bitset of
blocks that have a region and the dirty summary words are now scanned a word
at a time, with the same runs as before.
MayHaveCpuDirtyPages reads the per-block summary and the region masks without
taking the region lock; a true answer still goes through the locked path.
Regions also bump a CPU-dirty epoch whenever a page becomes CPU-dirty.
On Windows hosts with 8 to 64 logical processors the render thread gets the
last physical core of the fastest efficiency class at the highest priority,
and guest threads are kept off that core so spinning job workers cannot take
the renderer's SMT sibling. SHARPEMU_RENDER_CORE=0 turns it off.
Per-draw timestamps are written only with SHARPEMU_PROFILE_GPU_DRAWS=1; by
default each submission gets two timestamps and the overlapping intervals
are merged into a [PERF][GPU_BUSY] line with the busy share and the longest
submission.
Preserve combined mip-tail refresh and export routing while taking upstream stencil and no-pixel-stage handling. Adapt upstream uint flags and wave64 exchange to per-register shader state; retain upstream LDS sizing and phase synchronization.
A thread that blocks on a semaphore between iterations is waiting, like the
condition-variable waits already on the list. A job-wait loop that runs queued
work between timed semaphore waits was otherwise taken for a hang once loading
outlasted the guard window.
The export was a success stub that never wrote its out parameter, so callers
sized buffers from uninitialized memory. The signature is (id, uint64_t* size);
it now answers from the APR file registry like sceKernelAprGetFileStat.
#955 overlaps several open PRs. Resolution:
- GuestPageTracker, GuestBufferCache, ShaderIdentity, SharedBackingViews: upstream's versions
(they carry the same allocation-free invalidation, hashing and lock-free reads as #944/#968).
- RELEASE_MEM: upstream's deferred release flush (supersedes #966).
- CommandStreamQueue.Done: ours (#941 queued frame boundary; upstream only reformatted).
- Flip snapshot pool: ours (locked; snapshots are taken on the command-stream thread and
retired on the render thread), upstream's unlocked pool dropped.
- Gen5SpirvTranslator: our per-register variables and structurer (#923, #975/#976) first,
upstream's guarded in-order blocks as the fallback, then the dispatcher; upstream's
lane-spill slots, bounded move-relative, exec-known-full and PS input sharing kept.
* [Gpu] Astro Bot performance: 14 -> ~60 fps at the title scene
- Defer RELEASE_MEM command-buffer flushes instead of submitting per packet
- Resolve indexed indirect draws and dispatches on the GPU
- Async buffer readbacks on a dedicated transfer/compute queue
- Pool image backings, flip snapshots and tiler resources
- One-frame command-stream run-ahead
- Lock-free backed-range and image page-owner checks on CPU writes
- Scalar value cache with generation-stamped reset; memoized invariant phis
- Shader compiler: structured forward-only CFGs with function-scope
registers, EXEC-full guard elision, bounded V_MOVREL offsets
- Trivial-leaf import dispatch and strchr/strrchr fast paths
- SHARPEMU_GUEST_REFRESH_RATE option
* [Tests] Fix full-suite flaky tests
- Red-zone patcher: when every fixed trampoline candidate is taken, look
for any free host range within relative-jump reach
(PhysicalVirtualMemory.EnumerateFreeHostRanges); the test places its
image in a probed free hole
- Kernel memory tests: probe a free PRT-area address instead of the fixed
0x1026C00000; the reservation-extent test only rejects picks inside
the reservation
- Release checks tolerate another thread reusing the freed range
- Allocation tests measure on a dedicated thread and keep the least
allocating of several rounds (AllocationMeasurement.SteadyState)
* [Gpu] Astro Bot: structured loops, buffer slabs, pad motion
- Shader: emit natural loops as structured SPIR-V loops (OpLoopMerge)
instead of the block dispatcher, and merge SCC-uniform if/else joins
as selects so descriptors picked on a branch stay evaluable per draw.
- Shader: decode V_FFBH_I32 and V_MIN3_F16; read the DS GDS bit from
bit 17 (GFX10) and route GDS append/consume through shared atomics.
- Gpu: sub-allocate small device-local buffers from shared memory slabs.
- Pad: report motion data by default (Astro Bot never imports
scePadSetMotionSensorState), convert acceleration from m/s^2 to G,
and add scePadResetOrientation / scePadSetAngularVelocityDeadbandState.
* [CPU] Stop pinning guest threads to host cores by default
Guest affinity packed Demon's Souls' 13 spinning BPE job workers onto 10 host lanes and put its renderer next to them at equal priority, so the renderer and lock holders waited out whole scheduler quanta. Intro 10.6 -> 44.7 fps, menus 11.6 -> 47.5 fps. SHARPEMU_GUEST_AFFINITY=1 restores pinning.
* [Media] Convert host movie frames to YUV on the decoder thread
The render thread converted every decoded Bink frame from BGRA back to YUV 4:2:0 (6.7 ms per frame). The decoder thread now produces the planes and the presenter uploads them as-is. PS Studios logo 33 -> ~50 fps, attract movie 44 -> 60 fps.
* [Agc] Accept a zero destination in sceAgcDcbWriteData
Demon's Souls emits WRITE_DATA with address 0 into a template and patches it after copying. Returning null made the title patch a relocated null pointer, which wrote a stack-derived value into its label and was then parsed as an unknown NOP marker (GPU FATAL).
* [Loader] Patch short red-zone faulting accesses next to branches
A faulting access shorter than a jump could not be relocated when followed by a branch or a branch target, so a handled fault let Windows overwrite the red zone. Spans may now end with a direct branch (run after the stack pointer is restored) or start up to three instructions earlier. Fixes a Demon's Souls crash after the new-game intro movie.
* [GPU] Astro Bot rendering fixes: compute subgroups, DCC clears, PS inputs, texture MIN_LOD
- Compute: pin non-emulated compute pipelines to a 32-lane subgroup
(VK_EXT_subgroup_size_control) so wave64 groups other than 64
invocations keep lanes 32..63 active on AMD (striped froxel volume).
- Shader translator: 64-bit AND for the wave32 S_*_SAVEEXEC_B64 mask.
- PS inputs: slots that read one VS parameter share one host input;
a smooth/flat slot next to a V_INTERP_MOV slot is rebuilt from the
per-vertex values (black save cards, NaN normals, black props).
- Render targets: drop unwritten color targets of depth passes from both
the attachments and the pixel outputs, keeping locations aligned.
- DCC: materialize pending zero fast clears before shader access so a
later attachment bind does not wipe compute writes.
- Textures: honour the T# MIN_LOD with VK_EXT_image_view_min_lod so
streamed textures never sample unloaded mips (blown-out stadium sky).
- Device: enable VK_KHR_maintenance5 for point lists without PointSize.
* [Audio][GPU] ATRAC9 independent mono streams, BC storage fallback, 32-bit bit counts
- LibAtrac9: decode config header 0x30 streams (PS5 ambisonics), where every
channel is an independent mono stream interleaved frame by frame.
- Images: when the driver refuses storage usage on a block-compressed image,
compute writes go through an uncompressed replacement of the same memory.
- SPIR-V: count 64-bit masks with two 32-bit OpBitCount, which Vulkan requires
without maintenance9.
- Agc: drop the indirect-draw emit debug log; report the draw modifier in the
unsupported indirect draw error.
* [GPU] Partial texture-pool uploads and cheaper per-draw preparation
- A CPU write into a large layered texture (Demon's Souls' 256-layer BC3
pools) re-uploaded the whole image, ~750 ms of GPU detiling in one frame.
Each (mip, layer) piece's guest bytes are hashed at upload; a refresh
uploads only the pieces that changed.
- Device-address preparation touched and checked every registered buffer for
each dispatch. Recency touches now run once per retirement tick, and uploads
only visit buffers in blocks the tracker does not know to be clean; hot
pages stay dirty, so they are still re-uploaded every time.
- Resource materialization cache: when the only changed words are ones the
flattened table alone reads (per-frame inline constants), only the table is
evaluated again.
- A full batch no longer splits an open render pass before 512 draws.
- Lock-free lookups for shader headers, device format properties and the idle
host movie bridge; the SPIR-V dump toggle is read once per program cache.
* Demon soul - fix texture
* Delete runahead tests
* Screcrow fix for nvidia GPU
A RELEASE_MEM that writes a 32-bit label submitted the open batch at once. Without an interrupt
nothing inside the slice waits for the label, and every slice that makes progress already ends
with a submit - including one that stops at a WAIT_REG_MEM on that label - so the label is
written when that batch completes either way.
The per-label submits split Superliminal's frames into ~35 extra command buffers; each ended the
render pass, which stores and reloads every attachment on a tile-based GPU. The title menu went
from 15.6 to 21 fps and Octopath from 13-21 to 18-29 fps, with no hang over 150 s runs.
The image collector runs after every completed guest submission as well as at each flip, and
every run advanced the age it compares against. A title submits dozens of command buffers per
frame, so "unused for 16 ticks" meant a fraction of a frame: every texture sampled once a frame
was deleted and recreated, and Superliminal uploaded 3,591 textures (2.2 GB) every 5 seconds.
Deleting a GPU-written image the cache cannot read back also dropped its contents.
Only a flip now advances the age. Collection after a submission still runs, so memory pressure is
relieved during long stretches without a flip. Superliminal's per-frame uploads went from ~55 to
none, and its intro scene from 12 to 45 fps.
Only forward, nested branches were emitted as structured control flow; every program with a
loop, and every single-block program, went through the loop around a switch on the guest PC,
which keeps all registers live and stops the host compiler from optimizing across blocks.
A block that a later block branches back to - on a wave-uniform condition or with s_branch -
now opens a SPIR-V loop whose body is structured like any range, whose continue evaluates
that branch's condition, and whose iterations share the dispatcher's step guard so a guest
loop that never ends still cannot wedge the GPU queue. A conditional branch from the body to
the block after the latch is a break out of the innermost loop. Any other backward edge, or a
break past an outer loop, keeps the dispatcher as before.
Across eight games, programs left on the dispatcher went from 760 to 104 (Poppy, Silent Hill,
Oregon Trail and Dreaming Sarah to none); every emitted module passes spirv-val and the
frames match. Octopath's 12.5M-invocation instanced draw went from 12.2 to 10.8 ms.
Every mapping change queued behind the GPU worker so it could not race a pending GPU read.
During loading that worker is busy translating shaders, and Demon's Souls' reservations and
batch maps waited seconds for a queue slot (23.6 s over 132 calls, up to 7.1 s each) while
the change itself took about a millisecond.
A change that only places memory where the GPU was never given anything - a kernel-chosen
address, or a fixed range that is empty or reserved and overlaps no registered span - cannot
race a GPU read and has nothing to unregister, so it now runs on the calling thread. Those
paths also skip the unregister request for free and reserved ranges, which are never
registered; waiting on the worker while holding the mapping lock that GPU reads take would
deadlock, so reaching a registered span there throws instead.
The guest context-transfer stub that fiber switches go through keeps the
Windows TEB stack bounds in sync by storing the target stack top and
bottom to gs:[8] and gs:[16]. Unlike the guest-entry and exception stubs,
it did so on every host. On macOS gs addresses the pthread TSD, where
those slots hold errno's address and another key: a fiber switch wrote
its stack bounds over them, and the next failed libc call on the thread
(a timed-out condition wait) crashed reading errno through the pointer.
Demon's Souls died this way within 10-50 s of boot; a hardware
watchpoint on the thread's errno slot stopped in this stub.
The fiber bounds sync now lives next to the other TEB-bound helpers and,
like them, emits nothing off Windows.
The red-zone patcher computes a shifted core for every span: the run of
faultable guest memory accesses that executes with RSP moved below the
red zone. A span over register-only instructions has no such access, so
the core could not be computed and the span was refused as too short.
SHA rewrite sites are exactly that shape (sha256msg1/msg2/rnds2 on
registers), so no SHA instruction was rewritten any more: on hosts
without the SHA extensions every one trapped and was emulated one at a
time. Poppy Playtime logged sha_rewrites=0 with 144 too-short refusals,
hit hundreds of thousands of illegal-instruction traps and stopped making
progress within a minute.
A span with no guest memory access has nothing that must run outside the
shift, and the span builder has already refused any instruction that
touches RSP, so the whole span is the core.