* [ShaderCompiler] Pass guest LDS size to SPIR-V compilation
* [SpirvTranslator] Synchronize wave64 half masks through LDS exchange
* [Fiber] Avoid formatting unused names and disabled traces
* [Shader] Build materialization cache ranges without temporary copies
* [ShaderCompiler] Intern scalar graphs without string keys or hit allocations
* [Media] Vectorize movie YUV conversion without changing pixels
* [Shader] Reuse scratch storage for resource read recording
* [Tests] Initialize presenter state for GPU binding validation
* [Vulkan] Keep temporary descriptor writes off the managed heap
* [GPU] Reuse bounded descriptor preparation scratch arrays
* [ShaderCompiler] Persist versioned scalar analysis across launches
* [GPU] Remove duplicated image layouts from texture bindings
* [ShaderCompiler] Bind cached graph payloads to their input keys
* [Vulkan] Load content-addressed driver cache shards on demand
* [Tests] Keep presenter allocation fixture independent of diagnostics
* WIP: Yotei runtime descriptors, compile-cost fixes and Codex memory work before PR #1000
Snapshot before merging upstream PR #1000: runtime image descriptors (global table),
format-store and fault-flush compile fixes, runtime buffer strides, stencil plane
fixes, Codex's image memory, D16 and residency work. Tests: all pass except the
two known failures (DescriptorReloadedInsideLoop_SharesOneSampler,
RuntimeFormattedLoad_ReadsItsElementThroughThePageTable).
* [Cpu] Claim guest thread stack and TLS regions atomically
TryMapGuestThreadRegion looked for a free slot and mapped it in two steps, and Map
reuses a region that already exists. Two host threads claiming a nested-callback stack
at the same time therefore got the same stack, and Map zeroed the first one's frames.
In Ghost of Yotei two render threads ran the AGC command-buffer-full callback on that
shared stack at once: one returned through the other's entry stub onto the other
thread's host stack, abandoning managed frames. The runtime then failed fast with
"attempted to call a UnmanagedCallersOnly method from managed code" or an access
violation, one to four minutes after the intro.
The slot search and the mapping now run under one lock shared by the stack and TLS
claims.
* [Gpu] Preserve downloaded buffers and batch resource descriptor reads
* [Vulkan] Forget driver cache identities of destroyed shader modules
The remaining change of the updated #1006 head (e6659681) that the integration branch did not carry.
* [Vk] Measure GPU busy time from per-submission timestamps
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.
* [HLE] Reserve a host core for the render thread
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.
* [Vk] Spin briefly before blocking on a GPU wait
A GPU wait polls the timeline for SHARPEMU_GPU_WAIT_SPIN_US microseconds
(150 by default) before it sleeps, which saves the wake-up latency when the
work completes soon.
* [GpuMemory] Answer CPU-dirty range queries without the tracker lock
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.
* [Gpu] Skip the device-address sweep while no page became CPU-dirty
PrepareBda remembers the CPU-dirty epoch and the mapping it swept last and
skips the full sweep of mapped memory until either changes.
* [GpuMemory] Scan possibly CPU-dirty blocks 64 at a time
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.
* [Gpu] Skip syncing a written buffer range that is already GPU-owned
SynchronizeBuffer returns early when a written range lies entirely in the
GPU-modified ranges, uses the lock-free CPU-dirty query for reads, and the
render profile splits buffer sync time into written, texel and upload parts.
* [Gpu] Version the GPU-modified buffer ranges
A written range bumps GpuModifiedVersion only when it adds bytes to the
GPU-modified set, so readers can cache a clean verdict for a page until the
GPU writes something new.
* [Gpu] Track the extent of DCC metadata and answer overlap queries
A color target records the size of its DCC metadata, and the image cache can
tell whether a guest range overlaps registered DCC metadata.
* [Diagnostics] Trace the GPU writers of DCC metadata and of a chosen address
SHARPEMU_TRACE_DCC_WRITERS=1 writes the shader program and a call stack for
GPU buffer writes that overlap DCC metadata; SHARPEMU_TRACE_WRITER_ADDRESS
does the same for writes that cover one guest address.
* [Gpu] Fill DCC metadata on the GPU and in guest memory at once
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.
* [Gpu] Turn AGC fill compute shaders into buffer fills
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.
* [HLE] Resolve backing aliases for readers that hold an access window
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.
* [Diagnostics] Trace shader resource reads that wait for GPU-written memory
SHARPEMU_TRACE_GPU_READS=1 writes the shader, stage, address and table phase
of a resource read that has to synchronize GPU-written memory, once per shader
and page.
* [ShaderCompiler] Keep recent materialization variants per draw key
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.
* [VideoOut] Read resource words through cached clean pages and the backing alias
TryReadGuestWord and TryReadResidentGuestBytes remember the last 64 pages
found free of GPU-dirty bytes, keyed by the GPU-modified version, and read
them through the backing alias inside the alias access window that each
command stream slice now holds. GPU-dirty checks are byte-precise, and a
resident read refuses unreadable ranges before it asks the page tracker.
* [Gpu] Read GPU-written guest memory back asynchronously and ahead of time
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.
* [ShaderCompiler] Prefetch contiguous resource words in one resident read
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.
* [Gpu] Reuse image lookups that found the same backing
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.
* [ShaderCompiler] Look up fused programs without the registry lock
Every shader header lookup and decode asked the fused-program registry under
its lock. The registry now publishes an immutable dictionary that readers
take with a volatile read, and a registration copies it under the lock.
* [ShaderCompiler] Decode fused programs whose halves are far apart
The fused decoder joined the continuation at its address distance from the
entry and refused halves more than 4 GiB apart or in the wrong order, which
Poppy Playtime now hits for a vertex shader (invalid-fused-layout). Such a
continuation is placed right after the entry instead, and each of its
instructions keeps its real offset from the program address in AddressOffset;
S_GETPC_B64 in the scalar graph, the SPIR-V and the MSL translators uses that
offset, and the persisted scalar analysis key includes it.
* [Gpu] Collect an image only once it is old enough in frames
Since images age by frame, clamping the age to the current frame made every
image of the first frame collectable during the first 16 frames, and a
submission that ended no frame could delete a texture the frame still used.
Without memory pressure nothing is collected until the full age has passed;
under pressure the clamp still lets collection relieve memory early.
* [Tests] Cover the deferred global barrier of an attachment-only scope
A global barrier still ends a scope whose draws store to memory. In a scope
whose draws only write attachments it waits for the scope to end, and a later
memory-writing draw ends the scope and records it.
* [Agc] Make the number of suspend points in flight configurable
CommandStreamQueue takes the number of frame boundaries that may wait behind
the graphics commands. The default stays one, so the guest CPU can run one
frame ahead, and SHARPEMU_SUSPEND_POINTS_IN_FLIGHT=0 makes a suspend point
wait until its own boundary is processed, for diagnosis. Tests cover both
modes, including a label that the CPU rewrites after the suspend point.
* [Gpu] Throttle the device-address fault and 3D slice range logs
Little Nightmares wrote 195 thousand "Accessed non-GPU cached memory" lines in
one session and Silent Hill 6800 slice range lines. Both now print the first
occurrences and then only at power-of-two counts, with the count.
* [Docs] Document the new environment variables
Adds SHARPEMU_ASYNC_GUEST_READBACK, SHARPEMU_GPU_WAIT_SPIN_US,
SHARPEMU_IMAGE_LOOKUP_MEMO, SHARPEMU_RESOURCE_PREFETCH,
SHARPEMU_SUSPEND_POINTS_IN_FLIGHT, SHARPEMU_RENDER_CORE,
SHARPEMU_TRACE_DCC_WRITERS, SHARPEMU_TRACE_GPU_READS,
SHARPEMU_TRACE_WRITER_ADDRESS and SHARPEMU_PROFILE_GPU_DRAWS, and corrects the
group counts to the rows the tables hold.
* [GpuCommands] Write the 64-bit label of a compute-done release with an interrupt
RELEASE_MEM with event 0x2F and a 64-bit data selection was accepted only
without an interrupt. It is now handled like the other end-of-pipe events:
the label is written and the interrupt is raised after it.
* [GpuCommands] Let a wait see its own queue's preceding label store
A memory wait that directly follows a 4- or 8-byte store of its queue to the
same address, with only waits or inert NOPs and markers between them, compares
against the stored value when guest memory no longer holds it. Dead Cells
writes a label and waits for it while the CPU already rewrites it for the next
frame. The forwarded value lives only within one slice, and any draw, dispatch
or transfer in between drops it.
* [ShaderCompiler] Key fused programs by the underlying guest memory
Each native guest thread wraps the guest memory in its own tracking wrapper,
so a fused program registered while one thread created the shader was not
found by the render context. The registry now unwraps ICpuMemoryWrapper
before it looks up the per-memory table.
* [Agc] Implement the legacy sceAgcFuseShaderHalves export
The older NID (nApJjpKNBl4) shares the fusing path but keeps the front half's
user data pointer, takes the larger value of the resource allocation fields
of both halves, takes the user SGPR count and the ES component flag from the
front half, and copies the checksum register of the matching stage.
* [ShaderCompiler] Decode S_CMOV_B64 as a conditional select
S_CMOV_B64 is decoded as S_CSELECT_B64 with the old destination pair as the
false source, so it reuses the SCC, register-pair and EXEC handling of the
translators and of the scalar value graph.
* [Gpu] Merge the stencil replacement references of a face
Vulkan has one stencil reference per face for the comparison and every
replace operation. Replacements with the test value and with the operation
value are now merged into that reference over the written bits, and refused
only when they conflict on bits the comparison also reads. A replacement
with an operation value of zero over the written bits becomes a zero
operation, which ASTRO's PLAYROOM uses to clear one stencil bit.
* [Json] Add the sce::Json::Value copy constructor and keep shadow values on copies
The copy constructor (fSb2oQTNrgA) shares the assignment path. Assignment
and reads now carry the value a constructor or setter recorded in the object
heap, so a copy keeps it after the source is destroyed.
* [Font] Implement sceFontTextSourceInit
Fills the 0x60-byte text source with the text range, the cursor, the parser
callback and its object, and the text size, and leaves the memory after it
untouched.
* [AudioIn] Answer sceAudioInGetSilentState without an input backend
No AudioIn port can be opened until an input backend exists, so the export
returns an AudioIn error code (0x80260101) instead of being an unresolved
import.
* [PhtreadSpecificSemanticsTests] Fixes for computed shaders
* [Gpu] Return idle buffer memory and bound retired memory under pressure
- Small-buffer slab chunks track their live blocks; completely idle chunks go back to
Vulkan under memory pressure instead of staying allocated until device teardown.
- Buffers removed from lookup still hold GPU memory until their retirement completes.
The cache now counts them, collects on registered plus retired bytes, and waits for
completion once retired memory passes 256 MiB.
- The tiler's pooled scratch buffers are released under pressure, and image collection
waits for completion when outstanding scratch is over budget.
* fix(shader): preserve wave64 ballot masks on Vulkan
* test(shader): cover subgroup lane bits in wave mask predicates
* [Pad] Give each new touchpad contact its own touch id
The pad assigns a new id (1 to 127, then back to 1) to every finger contact and keeps it
while the finger stays down. SharpEmu reported the finger slot instead, so lifting the
finger and touching elsewhere read as one contact jumping across the pad. Games that tell
strokes apart by the id (Ghost of Yotei's name drawing) never saw a new stroke.
* [Gpu] Stop device-address faults from churning the buffer cache
Shaders reach guest memory through pointers, and a page they touch outside the cache
faults and is registered afterwards. In Ghost of Yotei that path re-created tens of
thousands of buffers every few minutes:
- Faulted ranges outside the GPU mappings were never touched again, so they aged out
while still in use and faulted again. They are now kept and touched each tick, and
forgotten once their guest mapping is gone.
- A fault inside a guest mapping now brings in its aligned 2 MiB window, clipped to the
mapping, instead of one 16 KiB page.
- Faults on memory the guest never mapped (bad pointers) no longer create buffers: the
shader reads zeros there, as on the console. They were 98% of the faults and about
2.6 GiB of junk buffers; the cache now holds ~0.5 GiB in the same scene.
- The per-fault log line is gone; it wrote hundreds of thousands of lines per session.
* [Vulkan] Save and load driver pipeline caches past 2 GiB
Ghost of Yotei's driver cache grows past 2 GiB. Saving went through a .NET array, so
every save failed and new pipelines were lost at the next launch; loading had the same
limit. Both now stream the unchanged file format from and to native memory in chunks,
so existing caches still load.
* [Np] Treat NO_ENTITLEMENT for unowned add-on content as an expected result
sceNpEntitlementAccessGetAddcontEntitlementInfo returns NO_ENTITLEMENT when the user does
not own the add-on, as on the console. The import log reported it as a warning.
* [Vulkan] Log only slow pipeline creations
Every pipeline creation was logged, cache hits included, which wrote thousands of
0.0 ms lines per session. Only creations of 500 ms or more are logged now.
* [Shader] Treat a phi that merges one value as that value when planning table reads
* [Test] Read the formatted-load V# from GPU memory so the load goes through the page table
* [Repo] Add license headers to new files and build pipebench with central package versions
* [Test] Add the missing copyright line to the large dispatcher tests
* [Repo] Remove game-specific RenderDoc capture scripts
* [Repo] Remove the pipebench tool
* [Kernel] Implement sceKernelAioPollRequest
* [ShaderCompiler] Return the entry header address of a registered fused program
An overload of TryGetFusedProgramParts also gives the entry header, so a
caller can register the same fused program on another memory.
* [Vulkan] Keep saving a pipeline cache that outgrows its limit
A driver cache over 256 MiB was refused on every save, so it was never
written again and every launch compiled the same pipelines. Such a cache
is now written as an empty file once, and the next launch starts a fresh
one. A save is skipped when the driver reports the size that was loaded
or last saved, and a failed or deferred save waits for the next interval
instead of running again on every new pipeline.
* [Vulkan] Write only the cache shards that gained pipelines
A shard remembers the driver size it was loaded or saved with and is not
written again while that size holds. Shard saves are logged as one
summary line, and a shard load only when it is slow.
* [Vulkan] Remove the single-file cache once the shard mode takes over
The shard mode never reads the single cache file of the default path, so
it is deleted instead of staying on disk.
* [Gpu] Record compiled compute programs in a shader prewarm list
When the pipeline host gives a list, each compute permutation the game
compiles is appended at once to shader-prewarm.bin: the guest code the
decoder read (once per program), the fused parts, the compute input, the
system registers, the resource specialization and the push data start.
Records carry a checksum, so a cut tail is dropped at the next open.
TryCompilePrewarm rebuilds the compile request from a record through the
same layout and request code as the runtime, so it produces the same
SPIR-V.
* [Vulkan] Prewarm the recorded compute pipelines after a translator or driver change
A first run after a translator change compiled every compute pipeline
one at a time while the render thread waited (Demon's Souls: 282
pipelines, 144 s). When the translator assemblies or the driver differ
from shader-prewarm.stamp, background threads at below-normal priority
translate the listed programs again and create their pipelines, so the
driver has them before the game asks. With the same build and driver the
launch does no prewarm work. The log reports the prewarm result and how
many runtime compute modules it produced.
* [Vulkan] Keep the pipeline cache in memory on Windows
The GPU driver keeps its own shader cache on disk, and the shader prewarm
list rebuilds it after an update, so Windows no longer writes the driver
cache file by default. SHARPEMU_VK_PIPELINE_CACHE=file keeps one; Linux
keeps the file and macOS keeps its shards.
* [Docs] Document the pipeline cache modes and the shader prewarm list
Adds SHARPEMU_SHADER_PREWARM, SHARPEMU_VK_PIPELINE_CACHE_SHARDS and
SHARPEMU_VK_PIPELINE_CACHE_LEGACY_SHARDS, and describes the new cache
file default.
* [DeS] Fixes for page faults and stack bounds issues in the emulator and audio fixes
* [ShaderPrewarm] Hold the game until the prewarm finishes and resume interrupted runs
The prewarm compiled thousands of compute pipelines on background threads
while the now faster boot ran beside it, so logos, Bink movies and the first
scenes raced the compile and dropped frames.
- Hold the guest at its first splash hide, GPU submission or CPU flip until
the prewarm is decided and done; the render thread keeps presenting the
splash. SHARPEMU_SHADER_PREWARM_WAIT=0 opts out.
- Stamp only the shader translators, the compiler type and the driver, so a
Libs-only build no longer triggers a full prewarm.
- Record finished pipelines in shader-prewarm.progress so an interrupted
prewarm resumes with the remaining ones.
* [AudioOut2] Block a blocking push only until a queue slot frees
A blocking sceAudioOut2ContextPush waited only when the host stream hit its
128 KiB cap, so a title rendering faster than real time kept about 680 ms
queued and every sound played that far behind (GTA V: queue=2 context,
queued_ms avg=678). The push now waits until the device holds the cushion
plus QueueDepth-1 grains, the level GetQueueLevel already reports as full;
the host cap stays as the limit for non-blocking bursts. GTA V now holds
about 41 ms with no underruns.
* [ShaderCompiler] Share the device-address formatted load decoder per component count
Every BUFFER_LOAD_FORMAT through a device address inlined the whole page-table
walk, format conversion and bounds check. Shaders with many such loads grew to
several megabytes of SPIR-V; 128 D16 loads added about 4600 words each.
Emit one DontInline function per component count, taking the address, size,
offset, descriptor and caller PC. Fault recording, formats, swizzles, EXEC
and out-of-range values are unchanged.
Offline A/B on three extracted shaders (RTX 5070, driver cache disabled):
18.6 s -> 2.9 s, 12.9 s -> 2.8 s, 15.9 s -> 3.2 s.
* [Gpu] Never copy a linear image readback past its guest range
The readback storage is sized from the guest range, but the copy follows the
linear layout computed for the format, whose padded rows can need more
bytes. A 513x513 R8 image declared as 0x40401 bytes was copied with
768-byte rows (0x60201 bytes): the copy wrote past its slot in the
download ring, and at the end of the ring into freed memory, which lost
the device.
Plan the readback only when the copy fits the guest range (and the
partial-level readback only when it fits its covered bytes), with one
warning naming the image. A linear download that would still exceed its
destination now fails explicitly instead of corrupting GPU memory.
* [Gpu] Draw merged LS/HS tessellation through a native tessellator
The guest hull runs unchanged in a cooperative wave64 compute workgroup and
computes its own control points and tessellation factors. A generated
control stage feeds those factors to Vulkan's tessellator, and the guest
domain shader is translated to a tessellation evaluation stage that reads
the guest control-point ring.
- VGT_TF_PARAM gives the domain, spacing and winding; VGT_LS_HS_CONFIG
gives the patches per group and the control-point counts.
- Factors are clamped by the guest's minimum and maximum levels; zero or
NaN outer factors still cull the patch. Isoline factors are swapped
into Vulkan's order.
- The SPI_SHADER_USER_DATA_ADDR_HS pair is carried separately from the LS
scalars and moved to s0:s1 before the guest code runs.
- Patches are drawn one hull group at a time, because the guest rings
are reused by relative patch id.
- A configuration the bridge cannot express (power-of-two partitioning,
an undefined domain, an empty hull group) skips the draw with one
warning instead of stopping the emulator.
* [VideoOut] Verify a guest page once for clean resource reads
Resource materialization reads a few hundred guest words per draw through
the clean readers, and each read re-checked the whole GPU ownership of its
bytes: GPU-dirty pages and bytes, then the image cache, which built a new
list (twice per word) under its lock. In Ghost of Yotei that was about 550
million reads and 90 GB of allocations per minute in game.
- The image cache answers whether an unsynchronized GPU image covers the
bytes without building the list; the list is built only when one does.
- The clean-page memo, already used by plain reads, now serves clean reads
too. A page is verified clean once (no GPU-dirty bytes or pages, no
GPU-modified image) and trusted until a buffer or an image is written by
the GPU, which advances the buffer cache's or the images' write version.
GPU-dirty pages are still checked on every read.
Measured in game: one materialization went from 0.33 ms to 0.17 ms.
* [Gpu] Keep the guest's face culling on tessellated patches
A patch list stands for legacy rectangle lists, which are never culled, so
every patch-list draw dropped its culling. Tessellated draws use the patch
topology too, and their output rasterizes as ordinary triangles: back
faces were drawn. Only a patch list without tessellation stages is a
rectangle list now.
* [ShaderCompiler] Give the domain shader its patch ID in v8
The ES ABI passes u, v, the patch within its hull group and the patch ID in
v5..v8. v8 was never written. Each native draw covers one hull group, so
the patch ID is the group's first patch plus the native primitive ID.
* [Gpu] Run tessellation hull groups together in their own off-chip slots
The hardware gives each hull threadgroup in flight its own off-chip buffer
and its own run of the factor ring, so groups run together and one draw
consumes them. Every group used slot zero instead, which serialized the
draw into one dispatch, one draw and full barriers per group.
- sceAgcDriverSetHsOffchipParam (VGT_HS_OFFCHIP_PARAM) is kept: buffer
count and granularity. Ghost of Yotei sets 256 buffers of 8K dwords.
- Hull workgroup k runs group k: its first patch, its patch count, s2 at
slot k and s4 at group k's run of the factor ring.
- The domain shader finds its group from the native primitive ID: the
off-chip slot in s4, the patch within the group in v7, the patch ID in
v8.
- One dispatch and one draw cover as many groups as the off-chip buffers
and the factor ring hold. Without the guest's configuration, one group
runs at a time as before.
* [Tests] Restore two upstream tests the branch dropped without a behaviour change
DepthState_PlayroomReplacementClearsOnlyTheWrittenBit and
FormattedUntypedLoad_SpecializesOnTheDescriptorFormat were removed by
earlier branch work although the code still behaves as they expect; both
pass unchanged.
* [Tests] Check the AudioOut2 port volume and reserved bytes again
The layout change dropped the checks of the -1 volume at +4 and the zeroed
bytes at +8, which the export still writes.
* [Repo] Remove the branch's debugging switches
The capture-after-seconds RenderDoc request, the crash-capture directory and
first-chance fault traces, the unsupported shader dump, the AGC allocation
failure traces and the control-flow trace served earlier investigations
only. The configuration switches (direct memory size, image cache budget,
forcing the shader dispatcher) stay.
* [PlayGo] Do not reject chunk IDs the playgo.pgm numbering only assumes
playgo.pgm is read for its chunk count only; the IDs 0..count-1 are
derived, yet they were marked authoritative and any other ID returned
BAD_CHUNK_ID. The list is still reported, but like other metadata that
is not understood, it no longer rejects an ID outside it.
* [Vulkan] Bound the bindless sampler binding by the device's sampler limits
The persistent heap always declared 4096 update-after-bind samplers and
left them out of the pool-wide descriptor budget it split between sampled
and storage images. On a device with fewer samplers per stage, or a tight
update-after-bind total (MoltenVK, some Intel drivers), creating the heap
could fail.
The sampler binding now takes the smallest of the device's per-stage and
per-set sampler limits (at most 4096), and its share comes out of the
total before the images split the rest.
* [Gpu] Copy linear render targets with the guest's own pitch
A linear render target's builder lays it out with the guest's pitch, which
need not be aligned like a texture's: a 513x513 R8 target is 513 bytes per
row. Transfers recomputed the texture's aligned linear layout (768-byte
rows), so its uploads read shifted rows and its readbacks did not fit the
guest range and were skipped.
A single-level, non-volume linear image whose description holds a
consistent slice layout now transfers with that layout. Textures, which
carry no layout of their own, keep the format's linear layout, and the
readback guard still refuses a copy that does not fit.
* [Gpu] Free images and retry once when an image allocation runs out of memory
The image budget comes from the device's reported memory, so an allocation
can still fail when the driver or another process holds more than
expected. That was fatal.
Running out of device or host memory while allocating an image's backing
is now recoverable: the cache retires every image collection allows, waits
for the retired ones to be destroyed, returns unused pool memory and tries
once more. A second failure stays fatal. Tests make an allocation fail
through an internal hook on GpuDeviceInfo.
* [Vulkan] Size descriptor pools for the sets that need them
The branch had raised every descriptor pool to 65536 storage buffers,
262144 sampled images and 8192 storage images so the largest shader's set
would fit, and every pool paid for that on every device.
Pools are back to upstream's ordinary size (8192/8192/1024/1024). When a
set does not fit one, the next pool is sized for four sets of its kind,
and a retired pool is reused only if it can hold the set. MaxImages stays
the planner's bound on distinct image descriptors per shader.
* [Vulkan] Blend with a second source from the MRT1 export
Blend factors 15-18 (SRC1 color and alpha) were mapped to Vulkan's Src1
factors without enabling dualSrcBlend or giving the fragment shader a
second output, so the blend read an undefined value and validation
reported every such pipeline.
When the device supports dualSrcBlend, it is enabled, and a draw whose
target 0 blends with an SRC1 factor (and binds no other target, as on the
hardware) gets a second fragment output fed by the MRT1 export, declared at
location 0 with index 1. The translator accepts that one extra output and
keys the compiled program by it.
* [Tests] Skip device tests without a Vulkan device, as upstream does
The branch had turned the device gates into assertions, so every device test
failed on runners without a GPU. SHARPEMU_TEST_REQUIRE_DEVICE=1 still makes a
missing device a failure.
* [Gpu] Wait for pending image readbacks before uploading over them
A tiled image retired under pressure publishes its contents only when its
readback tick completes. Found again before then, it was rebuilt from the
guest bytes it supersedes. Uploads now wait for the readbacks into their range.
* [Shader] Unpack compressed 16-bit pixel exports by their export format
Compressed exports were always unpacked as half floats. UNORM16, SNORM16,
UINT16 and SINT16 exports (G-buffer normals among them) now follow the
SPI_SHADER_COL_FORMAT of their target, which also keys the compiled program.
* [Agc] Emit direct draws for every draw modifier
sceAgcDcbDrawIndex and sceAgcDcbDrawIndexAuto returned without writing the
draw for any modifier other than 0x40000000, so whole passes vanished
without a trace.
* [Agc] Emit addressed occlusion counter events
sceAgcDcbEventWrite refused every event with an address, so the 0x38/0x39
counter events never reached the command stream. Their results are now
published through the ordered buffer fill path instead of CPU memory only.
* [Gpu] Execute target-less draws whose pixel shader writes memory
A draw without color or depth targets was dropped before compilation even
when its pixel shader stores to buffers or images. Yotei fills its visibility
buffer this way; the dropped pass left most of the scene black.
* [Shader] Keep EXEC guards after V_CMPX with a legacy scalar destination
GFX10 V_CMPX in VOP3 or SDWA form changes EXEC and ignores its scalar
destination. The EXEC-full analysis took it as a write of that destination,
kept EXEC full and elided the guards of the following vector writes.
Ported from voidborn-astra/sharperemu 8499b905.
* [Vulkan] Use unsigned operations for scalar pair bit instructions
Ported from voidborn-astra/sharperemu c0cff157.
* [Gpu] Invalidate image copies for raw buffer writes
Ported from voidborn-astra/sharperemu caa4149d.
* [Gpu] Retain GPU image contents during collection
A GPU-written image whose readback a dirty buffer overlap blocks was cleared
and deleted, losing its contents. It is kept instead; same-page CPU writes are
resolved by hash before deciding.
Ported from voidborn-astra/sharperemu 3b5eb2f3.
* [Shader] Read the selected lane for V_READLANE in graphics stages
Graphics stages took the reading lane's own value instead of the selected
lane. A graphics wave is one group of up to 32 host lanes; the selected lane
of that group is now read with a subgroup shuffle.
* [Tests] Cover Yotei's wave64 march and waterfall loop shapes
A step counter compared into EXEC with lanes leaving through VCC, and a
waterfall over an SDWA compare into an SGPR pair. Both already end every lane
in both halves of the wave.
* [Vulkan] Make depth bounds dynamic pipeline state
The depth bounds were part of the graphics pipeline key. Ghost of Yotei moves
them per draw, so every value created a new pipeline: about 80 per frame and
thousands kept for the session. They are now set with vkCmdSetDepthBounds per
draw; the key bytes they used stay reserved.
* [Shader] Read absent graphics lanes as the reading lane
A host subgroup of a graphics stage can be partly empty, where a PS5 wave
still has dead lanes. DPP, V_READLANE and V_PERMLANE reads of an absent lane
returned undefined values: Ghost of Yotei's wave minimum then never reached its
exit and its pixel shaders ran until the step guard. An absent lane now reads
the calling lane's value, which keeps min/max/and/or reductions exact.
* [Memory] Skip the ownership transition for pages already CPU-dirty
A CPU write to pages that are all CPU-dirty re-ran ChangeState, which scans the
whole 4 MiB region and republishes the same summary, for every managed guest
store. CPU and GPU dirty states are exclusive, so nothing can change there.
* [GPU] Keep the previous frame's images through mid-frame collection
Collection runs before every draw while image memory is under pressure. It
evicted every image not used yet in the current frame, which the rest of the
frame samples again, so Ghost of Yotei recreated and uploaded about 7000
images per frame. Below the critical level the mid-frame pass now leaves
them alone; frame-end collection and allocation-time reclaim are unchanged.
* [GPU] Copy the newer GPU contents between same-layout image aliases
One surface read through two formats of the same layout is two cache
images over the same guest bytes. Before one is used as a texture, storage
image or target, it copies the newest GPU contents of its same-layout
aliases on the GPU, ordered by write sequence, as unified memory shows
them. Depth keeps its reinterpretation path.
* fix(kernel): provide read-only entropy device descriptors
(cherry picked from commit 2ad0eca819ba8456b42177d4e682915d99fb7639)
* fix(net): track epoll descriptor creation and destruction
(cherry picked from commit e1c5658ed5f9579756111c89943b136fb995182b)
* fix(pthread): implement relative timed mutex acquisition
(cherry picked from commit c75f28f489a7fa3f78d6baae533e49c33dc10cb7)
* fix(net): implement level-triggered socket epoll waits
(cherry picked from commit 2f27f0078888007861f71afc5615e5c9a7246532)
* fix(kernel): release timed-out event flag waiters
(cherry picked from commit 4daa8a34e4c0534d3a0ebcc79f7a15597aaed330)
* fix(system-service): report empty event polls
(cherry picked from commit 65fb9aae7a7ee994ce8ab5b70f8fb00df2f7c742)
* fix(videoout): preserve decoded frame presentation order
(cherry picked from commit 66526f0eedc4da4253a6650b5563c257ff5c278d)
* fix(ajm): decode ATRAC9 jobs from split buffers
(cherry picked from commit e8c9925d86eb886d8734913a0ba7c10c125cc209)
* fix(videodec): finish queued frames before decoder deletion
(cherry picked from commit eae19e7c170998af3f4cf865759536fb33886e28)
* fix(net): confirm socket errors before reporting epoll error readiness
(cherry picked from commit 95128f67d446b4f659692584d241bdc85a5a9af7)
* fix(net): format guest Ethernet addresses
(cherry picked from commit 08cca779b5c000b46322ce16eb75b9739f312175)
* fix(net): keep one sceNetEtherNtostr export
Both branches added sceNetEtherNtostr. Keep the version that reports an
unreadable source address as a fault, the errno a guest read fault gives.
* fix(net): distinguish pending accepts from socket hangups
(cherry picked from commit 28ad0c0a9d989ad8f368d471354ede295ce9acf7)
* fix(net): avoid querying payload bytes on listening sockets
(cherry picked from commit 2aac142b2fb58b4b398bb1f2dfdf96cd94a388f1)
* fix(audio): avoid pacing empty nonblocking context pushes
A nonblocking push returns at once. The grain it claims still counts in the
queue level when no host backend is attached; only a blocking push waits
for its slot.
(cherry picked from commit 9b2db6d814f89b44cf46da11c3f8ced474ace063,
adapted to keep the queued grain accounted)
* chore(net): remove an investigation trace call
(network part of 8d0bd2fc; its PlayGo trace only exists in the skipped d342dd1c)
* fix(videodec2): preserve the picture-info size prefix
Without a picture only the ready flag at offset 8 is cleared; the caller's
size prefix and the other fields are preserved. A decoded picture still
fills the whole output info, pitch included.
(cherry picked from commit 050e9c824ad4a1ac2d9a40a6d6dd9ba1f63fd4fb,
adapted to keep the complete output info for decoded pictures)
* fix(videodec): preserve adjacent picture-info fields
Width and height are 32-bit fields with the pitch between them.
(cherry picked from commit 88a79715; the complete output info this branch
writes for a decoded picture already used 32-bit fields, so only the
dimensions helper and its test are added)
* fix(shader): report descriptor accesses that fall back to the null descriptor
A control-dependent image or sampler descriptor with no plan-time source
reads the null descriptor, so its texels read as zero. The plan records
each such access and the host warns once per shader with its PC, so the
loss is visible instead of silent.
* fix(shader): read control-dependent descriptors at run time before the null fallback
A sampled float access whose image or sampler descriptor has no plan-time
source read the null descriptor even when the runtime descriptor table
could serve it, because the null fallback ran before the runtime path was
tried. Such an access now reads its descriptors at run time, as the
hardware reads them from memory; the null fallback stays for accesses the
table cannot serve yet.
* fix(gpu): keep invalid runtime image descriptors on the null texture
A shader reading its descriptors at run time reports every image it does not
find. One with a null base or a format without a host equivalent stopped
the emulator when the host tried to create it. It now stays out of the
table, so the shader keeps reading the null texture, as the hardware does
for such a descriptor.
* fix(gpu): translate equivalent stencil replacement states
(cherry picked from commit a6e7922dbede9548fe5ef8795d29387de98b1bf8)
* fix(gpu): accept GFX10 image resource-level bit
(cherry picked from commit 1cbafc0a52e44592e34c5ae708d98573f6d2e9d6)
* fix(gpu): preserve addressed texture view alignment
(cherry picked from commit 936d873f70922337b7e9557cf4e579f4ed73d471)
* test: keep only the tests that 1cbafc0a and 936d873f add
Resolving their test conflicts kept test blocks from later #1057 commits
that depend on code not merged yet; only each commit's own test remains.
* fix(gpu): read disjoint descriptor bytes through backing aliases
GPU ownership is tracked in bytes but page protection covers whole pages:
CPU-owned bytes on a page that also holds GPU-owned bytes are read through
the backing alias, without bringing the neighbouring GPU bytes back.
(cherry picked from commit 7c8f0b97; this branch's clean word reader, which
first synchronises GPU-owned words, is kept, and the test keeps the
resident-range assertions)
* fix(gpu): read indirect tessellation draw arguments on the CPU
Non-indexed indirect draws now leave their arguments to the GPU (#1036), but the
merged tessellation path counts patches from the vertex count on the CPU: an indirect
tessellation draw arrived with the placeholder count of one and drew no patch.
* fix(gpu): write a replaced stencil plane back before dropping its proxy
A depth buffer whose stencil plane has another size at the same address drops the
other buffer's stencil proxy. When the GPU had written that plane, deleting the
proxy stopped the emulator (Ghost of Tsushima). The plane now reaches guest memory
first, as when a depth buffer changes planes, and stencil write-backs register as
publishing downloads so an upload of the range waits for them.
* feat(shader): read image-load descriptors at run time
An ImageLoad whose descriptor has no plan-time source looks its words up in the
runtime descriptor table instead of reading the null descriptor. The key carries the
numeric class the descriptor format selects, so the shader fetches through the uint,
sint or float heap array the host view matches.
- Only active lanes record misses; an inactive lane may hold anything.
- A missed descriptor over unbacked memory stays out of the table, and an entry whose
memory the title freed leaves it when its image is retired.
- An entry keeps the texture it resolved to until the cache retires its image, so a
draw no longer looks every entry up again; each draw still acquires its views.
- A plan that reads descriptors at run time on a host without the bindless heap is a
rejected plan, not a fatal layout error.
* fix(shader): resolve descriptors from empty scalar buffers
Descriptor words read from an empty scalar buffer are zero whatever the offset, so
the access reads the null descriptor; materialization rechecks that the buffer is
still empty. Ghost of Tsushima's pixel shaders read image descriptors this way.
(cherry picked from commit 5d03a71aacdead6adfbc9f46b5eeb777168e759a; on this branch
an access the runtime descriptor table can read keeps that path, which also serves a
buffer that is not empty, so the proof applies to the other accesses only. Its tests
use a depth-compare sample accordingly, the indirect-image case stays a runtime read,
and the planning-dump fixture adds one to its descriptor as f73663e8 does upstream)
* fix(shader): preserve scalar sources in immediate comparisons
(cherry picked from commit 9f4e325df34d9c075d9625da9994ecccfa9d1ac3)
* fix(shader): count unsigned leading bits in scalar instructions
(cherry picked from commit 09e9e1de7a8de12d92de39d51eed744ff4416fa6)
* fix(shader): bind signed integer storage images
(cherry picked from commit 1c134311d66ea6690a4549dff933017e1e478c5a)
* fix(shader): view only 32-bit signed storage writes as unsigned
A write-only signed storage image went through an unsigned view for every signed
format while no signed storage binding existed. With signed bindings (1c134311),
only the 32-bit format keeps that view, where the bits are the same; a 16-bit signed
image stores through its own signed view again.
* feat(shader): read adjusted-sample descriptors at run time
The IMAGE_SAMPLE_*_A forms carry their adjustment in the sampler's reserved dword-3
bits. Their descriptors without a plan-time source now go through the runtime table
like a plain sample, with those bits masked out of the sampler key so each
adjustment does not register a sampler of its own. A Ghost of Tsushima pixel shader
reads the image of such a sample from a material record that the plan cannot prove.
* fix(shader): qualify single-mode sample interpolants
(cherry picked from commit 3026bb288124f001f828f550755a2e4117c67ec0)
* fix(gpu): preserve sample masks and decode signed locations
(cherry picked from commit 3ab11b99d98482694037365c4e3865839544d891)
* fix(gpu): distinguish attachment sample counts in pipeline keys
(cherry picked from commit 51dd915d42f2ec877df970b66e0f7d2fc57b1300)
* fix(gpu): preserve EQAA depth and overrasterization controls
(cherry picked from commit 1087b9fba3914d3afda21041897c223dda3f324d)
* fix(gpu): retain scan mode control for sample iteration
(cherry picked from commit c8614eae54bc66d18ee60ac594280b9659b1fdd4)
* test(gpu): verify captured mixed-sample register decoding
(cherry picked from commit 943fc08e17f11dbe0995849813900a666781d42c)
* feat(shader): support explicit pixel interpolation samples
(cherry picked from commit fa66d55425a6108760f5dc1f7d6d5e9f62c2b8d9)
* fix(shader): honor eligible early depth testing in Vulkan
(cherry picked from commit 9b34fa404eaca555170e9c5fb18622a907b70179)
* fix(vulkan): preserve pixel sample-mask exports
(cherry picked from commit 5fbe6a3b38c1724c8c66fcbb6eb37aacdde4a981)
* fix(vulkan): preserve pixel depth exports
(cherry picked from commit bad8e123e566cf0a1a835fd5fa8736625ffa9581)
* fix(gpu): separate pixel iteration rate from interpolation inputs
(cherry picked from commit 9e0bdd4cea8105243347772328ec69a0a77e7b11)
* fix(vulkan): interpolate fixed custom samples with explicit offsets
(cherry picked from commit 8e7796636bed4fad5d5c65ce04a35b970fc369be)
* fix(vulkan): gate fragment execution with post-depth sample coverage
(cherry picked from commit 4c981e8edfae91b0a9ecdc336a2813ec841941f6)
* fix(gpu): apply sample exclusion to early fragment execution
(cherry picked from commit 0f5b5a71c54983db73d5d461088f3285cf13641a)
* fix(vulkan): preserve per-sample custom interpolation coordinates
(cherry picked from commit bfb5d7618b7ec994f2197c8f50d8b717f3c6bca6)
* fix(gpu): preserve late depth updates for shader coverage changes
(cherry picked from commit 4f8bcb6d142a7352a58d02328d798b390f244ee5)
* fix(gpu): key shader programs by custom sample positions
(cherry picked from commit dd36fcea9b83fd90ac1aea5af770cfdebbe5f987)
* fix(vulkan): preserve two-sample depth with single-sample color
(cherry picked from commit 042e5c4d19c5b104f0c20b0a38d87cb349d9241d)
* fix(gpu): reject unrepresented two-sample coverage masks
(cherry picked from commit b8fe958ac343b049295392a61c99419115020e63)
* fix(gpu): preserve forced shader depth ordering
(cherry picked from commit 58a4b282c89b2f35d04fe46a3f702c990f5164f0)
* fix(gpu): use late depth for unforced shader depth exports
(cherry picked from commit 29fe184bf1d241af6e8bfa521020d38d2eaba589)
* fix(shader): reject unsupported multisample sampling modules
(cherry picked from commit e77451e273d12e9c11355004654d592235e6aa63)
* fix(gpu): keep runtime descriptors that describe no view out of the table
A T# read at run time holds whatever the lane computed; the hardware reads one that
describes no valid view as the null texture. Ghost of Tsushima samples through such a
descriptor (image type 0, base level past the last level) before choosing another
one. The runtime path now asks for the texture without stopping on it: the T# checks
of the texture builder go through one rejection, fatal on the planned path as before
and a failure for the runtime one, which keeps that descriptor out of the table.
* perf(loader): sample repeated unresolved import warnings
(cherry picked from commit 842c3eea5228f017f26941e2f5ad9f69e678c002)
* perf(shader): read buffer-table descriptors at run time when the access allows it
A descriptor read from a table in a buffer, indexed by a lane value, was planned as
a dense table: materialization takes every record the buffer holds and each draw
finds, refreshes and binds all of them. A Ghost of Tsushima material table made that
about 2000 images per draw, nearly all of the render thread's time. An access the
runtime descriptor table can read now takes that path and binds only the records the
shader reads; a storage, compare or cube access keeps the dense table.
* fix(shader): read sample 0 for samples of a multisample image
Vulkan cannot sample a multisample image, and e77451e2 stopped such a module from
compiling, which stopped the emulator at a Ghost of Tsushima gameplay pixel shader.
A sample instruction on a 2D multisample image now fetches sample 0 of the texel its
normalized coordinate falls in, as shadPS4 lowers it, and the image is bound without
a sampler. A gather or an arrayed multisample sample is still refused.
* fix(gpu): free images and retry once when a buffer allocation runs out of memory
Images already retried after freeing what can go; a buffer allocation that found
every memory type full stopped the emulator (Ghost of Tsushima gameplay). The buffer
allocation now reports the failure, the buffer cache asks the image cache to free its
images and wait for retired ones, and tries once more before stopping.
* fix(vulkan): initialize format buffer scratch for runtime resources
* fix(shader): invalidate stale prewarm resource plans
* chore(sync): carry upstream Unity compatibility fixes
Apply upstream/main's post-revert Unity compatibility changes while preserving the Yotei-specific AudioOut2 state layout. The upstream rendering reversion is intentionally not imported because this branch still depends on those rendering changes.
* fix(shader): bind incompatible-only indirect image tables as typed nulls
An indirect image table whose candidates all have a dimension the MIMG
instruction cannot address (Demon's Souls 0x9D1D8F5B7F200DE7: every
candidate 2D, instruction MSAA) failed with "no typed candidate". Keep
the instruction's declared image type and bind every candidate as a
typed null instead.
* debug(gpu): dump the whole command buffer on an unknown packet
Temporary diagnostic for a tester's Yōtei crash ("packet opcode is
unknown", header 0x00000000). The old dump showed only 24 dwords around
the bad packet, too few to re-parse the stream and find which earlier
packet is misaligned. Dump from the start of the buffer instead.
* perf(gpu): copy CPU-write-hot pages once per memory visibility point
Every draw with a read-only device-address range re-copied the range's
CPU-write-hot pages: they stay writable, so they read as dirty forever.
In Ghost of Yotei that was ~47% of the render thread (thousands of
draws per frame re-copying the same pages, each copy ending the render
pass behind a full barrier).
The guest only guarantees CPU writes are visible at memory visibility
points (a submission slice, a satisfied wait), the rule the PrepareBda
sweep already follows. Each buffer now remembers the range whose hot
pages it copied in the current visibility generation and skips them
there; pages that fault dirty are still copied on every draw. A
command-processor write invalidates only the buffers holding the
written bytes instead of starting a new generation.
SHARPEMU_BDA_RESYNC_HOT_EVERY_DRAW=1 restores the previous behaviour.
[PERF][BDA_SYNC] reports visibility points, processor writes and how
many syncs skipped hot pages.
* perf(vulkan): stop retaining per-draw texture bindings until GPU completion
Ghost of Yotei binds ~300 images per draw, and each one allocated a
TextureResource of ~1.3 KB that stayed referenced until its submission
retired. At under one frame per second every binding outlived gen0, so
nearly every GC was a gen1 collection (299 gen1 vs 35 gen0 in 15 s).
- Drop TextureResource.Resolution: it was written in one place and never
read, and duplicated the ImageRequest (about half the object).
- Keep only the host movie planes, which own a staging buffer recycled
at completion; the other bindings die with the draw.
Measured in game: gen1 collections fell to 9 per 15 s.
* perf(images): widen the image lookup memo to 4096 slots
The direct-mapped memo had 256 slots, while a Yotei draw binds ~300
images and a frame thousands, so slots evicted each other and only 42%
of lookups hit. With 4096 slots (about 2.5 MB) 90% hit.
* fix(agc): allocate command buffer space atomically
Guest threads run HLE exports concurrently. Two exports appending to one
command buffer both read its cursor before either stored it back, so the
second packet overwrote the first. A tester's Yotei DCB showed a
PopMarker NOP written over the first two dwords of a SET_SH_REG 0x228
packet; the stream then hit its trailing zero dword ("packet opcode is
unknown, header 0x00000000") about 20 s into the game.
The cursor update is now atomic per buffer through striped locks; the
lock is released around the guest's buffer-full callback. A one-time
"[AGC][WARN] Two threads append to one command buffer" line reports the
race when the same buffer is contended.
* fix(gpu): stop executing queued GPU work once guest memory is torn down
Closing the window raced the runtime teardown: the runtime detached the
guest GPU memory registry while the presenter's shutdown drain was still
executing queued draws, so the first device-address draw died with
"A device-address program needs the guest GPU memory registry" and the
end-of-run reports were lost. The drain now drops the remaining work
once the registry is gone.
Also report the audio backend a port actually opened instead of a
hardcoded "coreaudio" in the AudioOut output trace.
* fix(runtime): release guest GPU memory only after the presenter's caches
Runtime teardown disposed the guest GPU memory while the presenter was
still shutting down on its own thread: its image and buffer caches still
held page watches, so PageGuard.Dispose failed fast with "Cannot release
the page guard while page state is active". Wait (bounded) until the
presenter detaches its stores, and keep the memory mapped if it does not.
* perf(buffers): stop read-aheads of windows the GPU keeps rewriting
Eager readbacks copied a 512 KiB window after every batch that wrote a
hot window. In Yōtei's loading phase and cinematic the GPU rewrites those
windows before the CPU reads them: of ~2,900 read-aheads per 10 s, 3 were
used and ~2,800 were discarded as stale. Count consecutive wasted
read-aheads per window, skip a window after two, and give every window a
new chance every 256 batches.
* perf(buffers): read only the requested pages from mapped device memory
A guest read of GPU-written memory served from a unified (ReBAR) buffer
copied the whole 512 KiB readback window out of device-local memory,
which the CPU reads uncached across the bus. In Yōtei's loading phase
that memmove was 30% of the render thread. Mapped reads now copy a
16 KiB window around the request; copy-command readbacks keep the large
window. Loading phase, same 10 s window: QueueRelay 2447 -> 947 ms,
frames 205 -> 252.
* diag(gpu): report how tessellated draws split into batches
Adds [PERF][TESSELLATION] to the render profile: draws, instances, patches, batches per draw and the off-chip buffer count. Each batch prepares and binds every stage again, so batches per draw is the multiplier on tessellation cost.
* perf(gpu): run all instances of a tessellated draw in shared batches
DrawTessellation looped over instances on the CPU: every instance took
its own hull dispatch, a full binding preparation of the hull, domain
and pixel stages, and its own draw. Yōtei's cinematic issues up to
~1,900 instances per tessellated draw, ~15,000 iterations per 10 s, and
tessellation was half of the render thread.
Number patches across all instances instead and batch that numbering by
the factor-ring and off-chip capacity, as the hardware distributes the
patches of an instanced draw. A new tessellation data word carries the
patches per instance; each hull thread derives its instance ID (v5), its
patch ID (v0) and its index-buffer vertex, and the domain stage its patch
ID (v8), from the shared number. Zero patches per instance keeps the old
values exactly.
Yōtei: one batch per tessellated draw, cinematic 0.6-0.7 -> 1.1-1.3 FPS.
* perf(memory): let CPU-write-hot pages cool down once per frame
A page the CPU rewrites right after three uploads turns hot: it stays
writable and dirty and every sync copies it whole. Nothing ever cooled a
page down, so a ring the game fills a little each frame ended up hot in
full. In Yōtei's cinematic two 28-32 MiB buffers were copied whole at
every device-address sync, ~60 GB per 10 s.
Once per presented frame, hot pages now lose their heat: the next sync
copies them once more and protects them again, and only pages the CPU
keeps rewriting within a frame turn hot again. SHARPEMU_KEEP_CPU_WRITE_HEAT=1
restores the old behaviour.
Yōtei cinematic: uploads ~60 GB -> ~0.6 GB per 10 s, 1.1 -> 1.5-1.6 FPS.
* diag(images): trace lost GPU image contents and name guest passes
SHARPEMU_TRACE_IMAGE_DROPS=1 reports every time an image loses GPU-written
contents that never reached guest memory: the code site, the image, the
range of a buffer write that caused it and how much of the image it
covers, the image's dirty flags, and the guest render pass decoded from
its push markers (one stack per queue). Evictions report whether they
published the contents first.
A renderdoc.capture file next to the executable now requests a RenderDoc
capture at the next guest flip, for unattended runs.
* fix(images): keep an image being resolved out of collection
ResolveOverlap can allocate a replacement (GrowImage, a depth overlap
copy), and an allocation under memory pressure collects images that
were not used this tick. The image being resolved is the copy source of
that replacement: when it was collected, the copy read a freed slot and
the process failed fast with "The slot is not allocated" (seen twice in
Yōtei with a 4 GB image cache budget). Touch it before resolving.
* fix(images): publish GPU-only image contents before an overlap drops them
Memory keeps its bytes when another image or a buffer write reuses part
of it. When an overlapping image request or a partial buffer write
dropped an image whose GPU contents had never reached guest memory, the
contents were simply lost; a later image over those bytes started from
stale guest memory. Yōtei's cloud tables alias a 2048x2048 RG16F and a
512x512 RGBA16F at one address, and several post-process targets are
partly overwritten by compute buffer writes (7-82 % coverage).
Before such a drop, copy the image into the buffer over its range on the
GPU (the existing image-to-buffer path) and let the buffer own the bytes,
as after any GPU write. Full overwrites still drop directly.
SHARPEMU_IMAGE_DROP_WITHOUT_PUBLISH=1 restores the old behaviour.
* perf(gpu): poll the timeline at most every 0.5 ms in per-draw housekeeping
The completed-submission collection before every draw read the timeline
semaphore twice per draw (vkGetSemaphoreCounterValue), about 12 % of the
command thread in Ghost of Yotei. The per-draw path now polls at most once
per interval and uses the last value otherwise; real waits still read the
timeline at once. SHARPEMU_TIMELINE_POLL_US overrides the interval.
* perf(gpu): look up known samplers without taking the store lock
Every sampler of every draw took the store lock, and the async compute
queue contended for it (3.7 % of the command thread in Monitor slow paths).
Hits now read a concurrent dictionary; only a miss locks to create.
* perf(buffers): read one tracker page per mapped readback
Mapped readbacks copy uncached device-local memory at about 300 MB/s, and
most want a descriptor word or a packet dword. The window drops from 16 KiB
to one 4 KiB tracker page. SHARPEMU_MAPPED_READBACK_KB overrides it.
* perf(images): reuse per-thread level scratch when resolving color targets
Every draw resolved its color targets with two fresh 16-entry arrays that are
only copied into the image description; they were about 10 % of the managed
allocation volume in Ghost of Yotei. One cleared pair per thread replaces them.
* fix(agc): leave the command buffer alone in Push/PopMarker
Ghost of Yotei 1.512 calls the marker exports from a second guest thread on a
DCB that the main thread fills with inline-written packets. Appending a marker
NOP read a stale cursor and overwrote the game's packets: a tester's dump shows
a PopMarker NOP over the first two dwords of a SET_SH_REG, whose stray value
dword then failed as an unknown packet (header 0x00000000). Markers are
tool-only NOPs on PS5, so by default they now return the current cursor
without writing. SHARPEMU_AGC_MARKERS=1 restores the packets for the pass
names that traces decode.
* diag(shader): say why resource materialization failed
Two testers of Ghost of Yotei 1.512 stop on 'could not be materialized ...
reason=Other' for one pixel shader, and Other named no step. Each failure
site now records what failed and the values it saw (the source and dword that
could not be evaluated, the guest address that could not be read, the bad
count, stride or descriptor words); the fatal message appends it as detail=.
Return values and the fatal/reject policy are unchanged.
* fix(agc): preserve marker reservations for the DCB writer
Restore packet reservation and capacity callbacks for markers. Suppress markers from another known guest writer under the allocation gate so they cannot overwrite unpublished inline packets. Scope writer identities to canonical memory across native thread wrappers.
* fix(gpu): preserve partial images before capturing buffer bindings
* fix(audio): report the channel count in AudioOut2 port state
* Revert "debug(gpu): dump the whole command buffer on an unknown packet"
The full dump identified the tester's crash: a PopMarker from a second guest
thread overwrote a SET_SH_REG (fixed by the marker reservation change). Back to
the short dump around the bad packet.
* fix(shader): probe record-aligned material offsets past the indirect image cap
Ghost of Yotei 1.512 stops on 'indirect image 0x494: 72559 unbounded selector
probes exceed 65536' for a 2502-record material table with a 116-byte stride.
Without a proven selector set every multiple of gcd(stride, 2^32) is probed,
because selector * stride + offset wraps modulo 2^32; reaching an offset that
is not a record start needs a selector of at least 2^32 / stride. Tables under
the cap keep the exhaustive enumeration; past it, the record-aligned offsets
are probed and a warning names the image once.
* fix(gpu): track guest pages written through device addresses
Shaders that store to memory through a runtime V# or a flat/global
pointer reach guest memory through the device-address page table, not
through a bound buffer. The buffer cache only learned about GPU writes
from bound writable bindings, so these stores were invisible to it: the
page stayed "clean", the host never downloaded the GPU copy, and the
next CPU upload or eviction silently overwrote the GPU result with stale
guest bytes (or the CPU kept reading stale bytes). Ghost of Yotei's
GPU-driven passes (producer computes filling per-object data and V#
tables) write almost exclusively this way.
The translator now reports the page of every device-address store and
atomic in a second bitmap that follows the fault bitmap in the fault
buffer. Non-pixel invocations merge bits of one bitmap word and publish
it when the word changes or the invocation ends; pixel invocations,
which a discard can end early, publish at once. The fault processor
compacts that bitmap with the same pass as the fault bitmap and, once
the scan completes, GuestBufferCache.NoteDeviceAddressWrites marks the
pages GPU-written exactly as a written binding would: GPU-dirty in the
page tracker, recorded in the GPU-modified ranges, the owning buffer's
write tick bumped, and overlapping image memory copies invalidated.
A page the CPU dirtied before the write is reported cannot be owned by
both sides; it stays CPU-owned and the conflict is logged once per power
of two. On Yotei these conflicts remain (dozens per run, mostly the
producer input pages at 0x50CB...), so mixed CPU/GPU pages can still
lose bytes; resolving them needs a byte-level merge and is left for a
follow-up.
Diagnostics added alongside:
- SHARPEMU_TRACE_COHERENCE=1 checks, every fourth presented frame with
the GPU idle, that pages the tracker calls clean hold the same bytes
in guest memory and in the mapped buffer, and reports each divergence
(SHARPEMU_TRACE_COHERENCE_MB sets the slice checked per pass). With
SHARPEMU_TRACE_GPU_MEMORY_ADDRESS=coherence the first diverging page
is traced. On Yotei the detector went from hundreds of divergences to
zero with this fix.
- Guest buffers are named "guest 0x<address>+0x<size>" when Vulkan debug
labels are enabled, so RenderDoc captures show which guest range a
binding is.
* fix(gpu): merge shader and CPU writes that share a guest page
On the console the CPU and the GPU write one memory, so a 4 KiB page
can hold bytes from both. The page tracker gives each page a single
owner. When a shader stored through a device address into a page the
CPU had dirtied before the store was reported, the page stayed
CPU-owned and the next upload copied the whole guest page, stale
where the shader wrote, over the GPU copy. The guest never saw those
shader bytes either. Ghost of Yotei hits this dozens of times per run
(producer input pages at 0x50CB..., per-draw rings at 0x2002...).
SharedPageShadows keeps, per such page, the bytes both copies last
agreed on and does a three-way merge:
- Uploads of a shadowed page copy only the byte runs the CPU changed
since the shadow; the shader's bytes in the buffer stay.
- When a device-address write is reported on a CPU-dirty shadowed
page (once every upload staged for it has executed), the bytes the
GPU changed since the shadow are written into guest memory where
the CPU left them alone. A byte both changed keeps the CPU value,
which the next upload carries to the GPU.
- Downloads refresh the shadow, since both copies agree afterwards.
A shader-written page gets its shadow when the CPU first touches it:
that access downloads the page, so guest memory matches the GPU copy
and no extra read of uncached mapped memory is needed. Pages only the
GPU uses cost one dictionary entry. A page the CPU dirtied before its
first shader write was reported is shadowed from the mapped GPU copy;
shader bytes an upload already overwrote there are lost once, and the
log says so. At most 16384 pages (64 MiB) are shadowed.
On Yotei: 0 unresolved conflicts (8 one-time losses), 0 coherence
divergences, no throughput loss in an A/B run (about 5.6k vs 6.2k
draws/s over the same cinematic window). The in-game darkening is
unchanged, so these lost writes were not its cause.
* fix(gpu): expose the partial last word of byte-addressed buffers
Storage buffers are declared to shaders as uint[], and Vulkan's runtime
array length counts only whole words. A buffer whose size is not a
multiple of four therefore hid its last one to three bytes: byte,
short and formatted loads there read zero, and stores were dropped.
Ghost of Yotei's occluder producer reads a one-byte-per-instance state
buffer of 95 bytes. The last three instances were the ones to produce,
so the shader saw them as inactive, wrote no matrices, and the depth
prepass drew the object with the previous frame's stale matrices:
exploding vertices that cover the scene with black bands and holes.
Byte-addressed and formatted bindings now get a view rounded up to the
next word. The number of padding bytes travels in the two spare bits of
the runtime stride entry (strides use 14 bits), and the shader keeps
the guest byte bound for those bindings: byte assembly may read the
padded word, but an element is loaded or stored only when all its bytes
are inside the guest range, and whole-word accesses keep the old bound.
The padded view must stay inside the cache buffer, which spans whole
pages, so this never reaches past its VkBuffer.
The device test reads and stores the tail of 41- to 102-byte buffers,
aligned and unaligned, formatted and byte loads, with and without the
zero-out-of-bounds read path, and checks that bytes past the guest
range stay hidden and untouched; 13 of its 17 cases fail without the
padding.
* fix(gpu): only wait for shared page uploads that copy bytes
A shadowed page's merge waits until every upload staged for it has
executed, since until then the GPU copy lacks the staged CPU bytes. The
staged tick was advanced for every upload that visited the page, also
when filtering removed all of its bytes because the CPU changed none.
Such an upload copies nothing, yet merges waited for its tick, and a
page re-staged every frame could keep shader writes out of guest memory
indefinitely.
The tick now advances only for runs that survive filtering and for a
whole-page copy into a new owner. The tests cover an eliminated upload
followed by a merge and a real pending upload that still blocks it.
* fix(gpu): keep shared page shadows when a buffer grows
When a request outgrows a cached buffer, the cache creates a larger one
and copies the old buffer's GPU contents into it. Shadows of shared
pages still named the old buffer as owner, so the next upload into the
replacement treated the page as unknown and copied the whole guest
page: stale where shaders had written and not yet been reported, it
overwrote those bytes in the GPU copy.
The shadows (and the pages waiting for their first CPU access) now
follow the copy to the replacement, and a merge waits for the copy's
tick, since the replacement holds the old bytes only once it executes.
The device test grows a buffer between an unreported shader store and
the next upload and checks that both shader writes and the CPU write
survive.
* diag(gpu): watch guest ranges per frame and force full barriers
Two opt-in diagnostics used to trace Ghost of Yotei's stale occluder
matrices back to the producer that never wrote them:
- SHARPEMU_WATCH_BLOCKS=addr:len[,addr:len...] logs, after every
presented frame, each watched range whose bytes changed in guest
memory or in the GPU copy (both contents, dirty state, submitted and
completed ticks), and every cache event touching a watched range:
device-address write reports, uploads, writable bindings, CPU write
faults, host DMA writes, fills and copies.
- SHARPEMU_DIAG_BARRIER_EVERY_CALL=1 records a full memory barrier
before every draw and dispatch, to rule missing guest barriers in or
out. It is very slow.
Both are off by default and cost one branch when off.
* fix(gpu): read and write whole words that start inside a buffer range
The previous fix padded only byte-addressed and formatted bindings and
kept the floor word count as the bound for whole-word accesses. The
hardware range-checks a buffer access by its first byte (stride 0 or
swizzle off: offset >= num_records is out of range), so a dword load or
store that starts inside a range ending mid-word is performed whole.
A dword-addressed buffer of 95 bytes therefore returned zero for its
last word, bytes 92..94 included, where the console returns the data;
whether the read got zero depended on the driver's robust access
granularity, not on the guest descriptor.
Every runtime-stride view is now padded to whole words, and a word is
in range when it is inside the padded view, which is exactly the
hardware bound. Byte and formatted elements keep the exact byte limit
carried in the runtime stride entry.
The new device test loads dwords from 93- to 96-byte buffers with both
read paths; three of its five cases fail without the change.
* fix(shader): track both words of binary64 vector results
(cherry picked from commit 2e339ac4143061e76bfefb4e065b04eba4647f77)
* fix(shader): lower half-word logical shifts on Metal
(cherry picked from commit fa9776ec2b71a30c6a833562d8e342f5718a612f)
* feat(gpu): pack narrow color channels without changing encoded bytes
(cherry picked from commit e98fe0b4e68cd5e070124c97d583911561f5fd00)
* feat(gpu): transfer narrow sRGB images through expanded backing
(cherry picked from commit 9a5f4e08aaf101ebd68ca18d43efac17ec94112c)
* fix(gpu): retain explicit pitch for linear color target transfers
(cherry picked from commit 88695316e2119b18cb095ae7ad00e1c17ea1f6fb)
* fix(shader): decode explicit-LOD comparison gathers
(cherry picked from commit a629cc3949cba63b52cfbdc0b41199a3fc1cba79)
* fix(gpu): decode narrow sRGB color target outputs
(cherry picked from commit 66aa64a01fc4ad984d53fa248b742481d3b54e61)
* fix(agc): accept the shader register Ghost of Yotei 2.002 writes at 0x192
Ghost of Yotei Complete Edition 2.002 applies a shader register table at
startup that writes 1 to SH offset 0x192 (0x2D92), just past the LS block.
gfx10 documents no register there and no shader state depends on it, but
the table writer had no entry for it and stopped the emulator right after
the splash screen. Ignore it like the other pace and control registers.
* diag(agc): log the values written to the undocumented shader register 0x192
The register is still not applied, but the first value and every change
are logged, so a rendering difference in Ghost of Yotei 2.002 can be
traced back to what the title wrote there.
* perf(shader): look runtime descriptors up through one shared function
Reading buffer-table descriptors at run time (90e35ba8) inlined the hash and
the eight probes of the runtime descriptor table at every image and sampler
access. A Ghost of Yotei pixel shader with 56 such accesses grew from 419 KB
to 2,051 KB of SPIR-V and took about 6.5 s to compile instead of 0.5 s.
The image and sampler lookups are now one DontInline function each, called
with the key and the lane's EXEC state, as the device buffer format decoder
already is; the same shader is 507 KB and still binds only the records it
reads.
* fix(shader): read format bytes in a buffer's last partial dword on the device
A format load through a device-side V# assembled each component byte from
its aligned dword, and that dword read zero unless all four of its bytes were
below num_records. The hardware range-checks the element, so the last one to
three elements of a byte buffer whose size is not a multiple of four read 0
instead of memory.
Ghost of Yotei's culling updater reads each object's 8_SINT visibility state,
skips the store when the new state equals the one it read, and counts the
element. Its 19-byte state buffers read 0 for elements 16..18, so the store
was skipped while the element was counted: counts[] exceeded the instance
slots the producer wrote, and the extra instances drew as black blocks.
The dword is now read when any of its bytes is inside the buffer; the existing
per-component check still zeroes a component past num_records.
* fix(loader): prove jump table bounds and tables over the control-flow graph
The red-zone patcher refused every short faultable site of a function whose
jump tables it could not resolve. The resolver only accepted a lea-only window
between a 32-bit compare and the table load, so Ghost of Tsushima's render
thread kept unprotected accesses in a leaf function that holds a pointer in the
red zone, and a host exception dispatch clobbered it.
Resolve each table by walking the function's control-flow graph backwards from
the load: every path must pass the fallthrough of ja after an unsigned compare
of the selector, reaching the load through register moves and zero-extensions
only, and must define the table register with lea of the same RIP-relative
table. The graph includes the resolved table edges, and resolution repeats until
a round reproduces its own targets. Tsushima's indirect-branch refusals drop
from 55 to 8.
* fix(agc): accept the user-config register Ghost of Yotei 2.002 writes at 0x191
The 2.002 build writes 0 to user-config offset 0x191 from a register table,
right after shader register 0x192. No gfx10-12 register lives there. Accept
it and log value changes, like 0x192.
* fix(images): keep a mapped mip chain whole when a texture starts past mip 0
A sampled texture whose descriptor starts past mip 0 was always made an image
of only the levels from there, for streamed textures whose larger mips are not
mapped. A pass that samples a lower mip of an image the GPU rendered (Demon's
Souls' fog and bloom chains) then got a separate image of the same memory,
filled from stale guest bytes and traded with the render target every frame:
white blocks across the fog and 0.2 fps.
The image cache now keeps the resident-levels form only when the chain is not
fully mapped; otherwise the texture is the whole chain viewed from its base
level, the same image as every other view of that memory.
* fix(np): report the package's real trophies from sceNpTrophy2GetTrophyInfoArray
b5b3d691 treated rdx as the first trophy id and always returned the requested
limit as the count. The call is (context, handle, offset, limit, details[],
data[], *count): offset indexes into the package and the count is how many
trophies exist from there, capped by limit. Hades asks for 50 and then walked
past its own on-stack arrays, crashing in a job thread before the first frame.
Read sce_sys/trophy2/trophy00.ucp (unencrypted container with tropconf.json and
tropmeta_*.json) and fill SceNpTrophy2Details (1312 bytes) and SceNpTrophy2Data
(32 bytes) for the trophies that really exist, all locked. Layouts and the
package format follow KytyPS5's NpTrophy2 implementation.
Co-authored-by: Felipe <54341095+Pipe530@users.noreply.github.com>
Co-authored-by: nmzik <Nmzik@mail.ru>
* chore(audio): mark the YoteiPC-derived ATRAC9 code as GPL-2.0-only
The multichannel block ordering in Atrac9DecodeState follows YoteiPC's at9_api.c,
which its author publishes under GPL-2.0 (no "or later" grant). The file now says
GPL-2.0-only with the source and author named, and the license text is added for REUSE.
LibAtrac9 itself stays MIT.
---------
Co-authored-by: ParantezTech <12572227+par274@users.noreply.github.com>
Co-authored-by: kuba <xnetcat.dev@gmail.com>
Co-authored-by: MathiasYG <06mathias6@gmail.com>
Co-authored-by: voidborn_astra <41128975+voidborn-astra@users.noreply.github.com>
Co-authored-by: Felipe <54341095+Pipe530@users.noreply.github.com>
Co-authored-by: nmzik <Nmzik@mail.ru>
* [SpirvTranslator] Load dword-aligned 32-bit formatted buffer components as words
A formatted untyped buffer load decodes the descriptor format at run time,
reading every component byte by byte through the generic layout select
chain. For dword-aligned 32-bit data formats (32, 32_32, 32_32_32,
32_32_32_32) with a raw-bit number format (UINT, SINT, FLOAT) a component is
one buffer word; that branch loads it directly and applies the same swizzle
and bounds rules. The descriptor is uniform, so the branch does not diverge.
UE GPU-skinning vertex shaders fetch bone data this way; Silent Hill: The
Short Message's menu goes from 1.3 to 2.0 FPS.
(cherry picked from commit 20540cd153855964d817daf065de3e243fd547bd)
* [SpirvTranslator] Convert f16 with the native pack/unpack when the host measured it exact
Every packed-f16 (VOP3P) lane widens its operands to f32 and rounds the
result back, and both conversions were the branchless integer sequences in
EmitHalfToFloat / EmitFloatToHalf: about twenty scalar ops each, because
GLSL UnpackHalf2x16 / PackHalf2x16 have no guaranteed rounding or subnormal
behaviour without float-controls execution modes. A device capability bit
does not settle it either - MoltenVK on Apple silicon reports
shaderRoundingModeRTEFloat16 and shaderSignedZeroInfNanPreserveFloat16 yet
not shaderDenormPreserveFloat16 - so only a measurement can.
ShaderCompileRequest.NativeHalfConversionExact, which the host sets only
after measuring the two ext instructions against these sequences on the
running device, swaps both helpers for the single ext instruction the
driver lowers to one hardware convert. It defaults to false, so an
unmeasured device keeps the exact emulation.
Silent Hill f2 volumetric fog dispatch: 30.7 -> 21.9 ms per dispatch in
replay, output unchanged.
(cherry picked from commit 10f76eeac0d4306172e41ecff86b737f6a3c9634)
(cherry picked from commit 7f18cc96ae58ac5fd47da6562c4ba3abe22ccb4a)
(cherry picked from commit 972f658033f8339effe42c5d305af87bcbab93f0)
* [Vk] Probe f16 conversion at device setup and report the verdict
A one-time compute pass at device setup measures the device's GLSL
UnpackHalf2x16 / PackHalf2x16 against the translator's own f16 conversion
before any guest shader is compiled, and only a clean measurement lets the
translator use them (ShaderCompileRequest.NativeHalfConversionExact). The
vectors cover all 65536 f16 bit patterns, every f16 value widened to f32,
the midpoint between adjacent f16 values and that midpoint one f32 ulp
either way, the overflow edge, signed zeroes, infinities, NaNs and f32
subnormals, and each is measured in both directions: 643200 comparisons.
Every finite value, zero and infinity must be bit-exact, sign included. A
NaN only has to stay a NaN, because neither side defines its payload: the
emulation picks sign | 0x7C00 | 0x200 narrowing and carries the f16 payload
widening, while the measured hardware convert canonicalizes to a positive
quiet NaN. On an Apple M4 that is the only disagreement - 2314 of 643200,
every one a negative NaN whose sign the hardware cleared - and f16
subnormals convert exactly despite shaderDenormPreserveFloat16 being false.
The probe never fails device setup: any error leaves the exact emulation in
place and says so on the reported line. SHARPEMU_NATIVE_HALF=0 forces the
emulation, =1 takes native without measuring. The result is one line:
[VK][F16] native half conversion exact=True mismatches=0 tested=643200
The flag reaches the translator through IShaderPipelineHost, so it is
process-constant and cannot mix two translations of one program; the saved
driver pipeline cache keys its entries by the module it was given, and the
Metal host does not probe and keeps the emulation. Offline replays run the
same probe, so a case translates the way the game would.
(cherry picked from commit 6a343683d890b4c6d123c84fdacb41e718ec4c19)
(cherry picked from commit 65ca663aa2bfb1fcc69709c73ab6189b07ead3c1)
(cherry picked from commit 003b1216bd311e5c5fb1d48ca01a66efa3fc51de)
* [SpirvTranslator] Read an unaligned buffer word as a funnel shift of two words
The byte-wise decode of a formatted buffer load read each byte of a component
from its own bounds-checked word load: four loads, four range tests and a byte
assembly for a value that spans at most two words. One funnel shift of those
two words is the same value, including out of range, where a word reads zero on
both paths and so contributes zero bytes. Every sub-dword buffer load goes
through the same helper.
The decode is the branch a formatted load takes when its address is not dword
aligned, so its size also sets the register pressure of the aligned branch next
to it. Silent Hill: The Short Message's volumetric fog dispatch
(0xD5DF9DC46C4E35EA, 8 formatted loads in its light loop) goes from 8.68 to
7.28 ms in offline replay, and the module from 394 to 360 KiB. An experiment
that dropped the byte-wise branch entirely measured 7.35 ms, so the funnel
shift is worth all of what the branch cost.
(cherry picked from commit 0c996ffab38144d795517e68cb2ca9d8d48e8988)
* [SpirvTranslator] Load a guest buffer word unchecked where the device zeroes out-of-range reads
Every guest buffer word is read through a range test, an address clamp and a
zero select: three operations whose only purpose is to produce the zero the
hardware returns past the end of a V#. A device with robustBufferAccess2 that
honours it returns that zero itself, so the three are pure overhead there -
dropping them measured about 12% of Silent Hill's volumetric fog dispatch, 482
checked words in one program.
ShaderCompileRequest.ZeroOutOfBoundsBufferReads turns them off and defaults to
false: an out-of-range read is undefined without the promise, so only a host
that measured the device may set it. It is set from a device probe, never from
a capability bit, because MoltenVK runs on Metal, which has no hardware bounds
checking.
(cherry picked from commit 2ba626a0ac7157f93f348cfe7573ff1092762d8c)
* [Vk] Probe out-of-range buffer reads at device setup and report the verdict
A one-time compute pass binds 1 KiB of a 4 KiB allocation that carries a
sentinel everywhere and reads all 4 KiB back through the descriptor: the words
inside the range must still read their sentinel and the words past it must read
zero. Only a clean measurement lets the translator drop the range test, address
clamp and zero select it puts around every guest buffer word
(ShaderCompileRequest.ZeroOutOfBoundsBufferReads).
The probe only runs where the device enabled robustBufferAccess2, because an
out-of-range read is undefined without it and can fault the device; everywhere
else it reports the reason and the checks stay. On this Apple M4 through
MoltenVK the feature is not supported at all, so nothing changes:
[VK][OOB] out-of-range buffer reads zero=False mismatches=unprobed tested=0
note=robustBufferAccess2 is not enabled on this device
SHARPEMU_ZERO_OOB_READS=0 keeps the checks, =1 drops them without measuring.
The flag reaches the translator through IShaderPipelineHost, so it is
process-constant and cannot mix two translations of one program.
(cherry picked from commit e81d924349042acc1122a66d4c767268bd6ef2a4)
* shader(decode): fill MIMG float-atomic and SOP1 opcode gaps
Gen5ShaderTranslator's opcode name tables were missing several real
RDNA2 opcodes that Ghost of Yotei's shaders use: MIMG 0x1D-0x1F
(ImageAtomicFcmpswap/Fmin/Fmax) and SOP1 0x15/0x1B/0x1C/0x1E/0x34/0x3B
(SFlbitI32B32, SBitset0/1 B32/B64, SAbsI32, SBitreplicateB64B32).
Without names these decoded as unknown instructions and aborted
translation before the pipeline ever reached GPU submission.
* shader(resources): accept float-numeric-class atomic image descriptors
ResourceMaterializer rejected any atomic image descriptor whose guest
format wasn't exactly Format32Uint, but real atomic-float images
(ImageAtomicFmin/Fmax) arrive with a float-numeric-class format. Relax
the check to also accept formats whose SampledNumericClass is Float,
and default the null-descriptor's NumericClass to the image's own
class for atomics instead of always Float.
Also surface the actual materialization failure reason in
ShaderProgramCache's error message instead of a generic string, so
each subsequent blocking bug could be identified from the log without
extra instrumentation.
* shader(bindings): add a dedicated atomic-float image binding range
BindingLayout only had a single Atomic-Uint binding range (32-36), so
any atomic image with a float numeric class had nowhere to bind and
ForImage() returned null for it. Add AtomicFloatBinding (37-40),
bump ImageBindingCount 36->41, and split Describe()'s atomic range
into its Uint and Float sub-ranges to match.
Also add an Atomic flag to ShaderImageShape (TargetRegisterWords.cs)
so downstream view-format resolution can distinguish atomic images
from ordinary sampled/storage ones.
* spirv: emit ImageAtomicFmin/Fmax and DsSwizzleB32, fill SOP1 ALU gaps
Vulkan has no native float image-atomic-min/max instruction, so emit
them the way KytyPS5 does: OpImageTexelPointer (typed uint) ->
OpAtomicLoad -> compare-and-swap loop (OpPhi/OpFOrdLessThan/OpSelect
on the bitcast float value) -> OpAtomicCompareExchange, returning the
pre-exchange value from EmitAtomicWordUpdate (now non-void, valid SSA
at the loop's merge block).
Add DsSwizzleB32 (bit-mode/quad-mode lane shuffle via
GroupNonUniformShuffle) to the LDS-opcode switch and to
UsesSubgroupShuffle(), and add the SFlbitI32B32/SAbsI32/SBitset0B32/
SBitset1B32 SOP1 cases so the newly-decoded opcodes from
Gen5ShaderTranslator actually translate to SPIR-V instead of hitting
the generic "unsupported instruction" fallback.
* spirv: declare atomic image OpTypeImage with a Uint sampled type
DeclareImageClass previously derived the SPIR-V OpTypeImage's sampled
type purely from the binding's logical NumericClass, so an
atomic-float image (logical class Float, backed by our new
compare-and-swap emission on a uint texel pointer) still declared its
OpTypeImage as float-sampled. That violates
VUID-StandaloneSpirv-Image-04965: the R32ui image format and a float
sampled type disagree on numeric class. Force the sampled type (and
format, already correct as R32ui) to Uint whenever the image is
atomic, regardless of its logical numeric class.
* gpu: back atomic-float images with a real Uint VkImageView
The SPIR-V side now declares atomic images as Uint (OpTypeImage), but
the actual VkImageView bound at dispatch time was still created with
the descriptor's own float-numeric-class format, tripping
VUID-vkCmdDispatch-format-07753 (the shader's declared sampled type
must match the bound view's format numeric class).
Fix both resolution paths:
- VulkanVideoPresenter.Descriptors.cs: NumericClassOf() now forces
Uint for any atomic image (this covers the null-descriptor
fallback path), and ShapeOf() forwards the Atomic flag.
- ImageRequestBuilders.Texture.cs: the real, non-null descriptor path
resolves its view format independently from the descriptor's raw
GuestPixelFormat and was untouched by the above. Extend the
existing Sint-storage override (which already forces such images to
Format.R32Uint) to also cover shape.Atomic.
Confirmed via a fresh Vulkan-validation-layer run against Ghost of
Yotei that VUID-vkCmdDispatch-format-07753 no longer appears.
* fix(shader): reconcile atomic SPIR-V emitters with upstream
* spirv: fix ImageGetResinfo querying a non-existent sampled-image combo
ImageGetResinfo never goes through UsesSampler() (it only matches
ImageSample*/ImageGather* opcodes), so TryResolveLayoutImage always
resolves imageObject as the raw OpTypeImage value for this
instruction, never a sampled-image combo. The size-query code still
called OpImage on it whenever resource.IsStorage was false, expecting
to extract a combo that was never built, which spirv-val rejects:
"Expected Sample Image to be of type OpTypeSampleImage". Just use
imageObject directly in both branches.
This surfaced only after the earlier atomic-image fixes let Ghost of
Yotei's shader compilation get this far; it is an unrelated,
pre-existing bug in the base PR.
* gpu: remap compute workgroup axes past the Z-dimension device limit
Ghost of Yotei dispatches a compute shader with NUM_THREAD (1,1,256).
Vulkan only guarantees maxComputeWorkGroupSize >= (1024, 1024, 64):
the Z axis has a much tighter guaranteed limit than X/Y, and this ran
into it for real: "LocalSize Z (256) exceeds device limit
maxComputeWorkGroupSize[2] (64)" at vkCreateComputePipelines, even
though the NVIDIA RTX 5070 here allows up to 1024 total invocations.
Fix by remapping logical GNM axes onto physical SPIR-V/dispatch axes
at shader-compile time: the largest NUM_THREAD axis always lands on
physical X (most headroom), the smallest on physical Z (tightest
limit). Gen5SpirvTranslator.ComputeWorkgroupAxisOrder computes this
permutation purely from the three thread-group sizes, so:
- The translator uses it to declare the physical LocalSize execution
mode and to read gl_LocalInvocationId/gl_WorkGroupId's components
back into the correct logical VGPR/system-register slots, so guest
shader code (which only ever sees logical TID/WorkGroupId values)
is unaffected by the remap.
- RenderExecutor.Compute.cs recomputes the identical permutation from
the same thread-group sizes (input.ThreadsX/Y/Z) to reorder
vkCmdDispatch's group counts the same way, with no new metadata
plumbing between the shader compiler and the dispatch path.
* shader(resources): drop redundant ImageHandle ScalarBufferWord guard; add opt-in resource-tracker diagnostics
GetHandleSource had a blanket rejection of any ImageHandle whose dword
was a ScalarBufferWord, predating ValidateSource/RuntimeValueValidator
being able to generically resolve such dwords at bind time (the same
mechanism MaterializationSources already uses for buffer descriptors).
Drop the redundant, narrower guard and let ValidateSource be the
single source of truth, so legitimately resolvable image descriptors
loaded from a scalar buffer (not just the two hardcoded heap/direct
shapes TryMakeIndirectImage/TryMakeDirectImage recognize) aren't
rejected outright.
This did not resolve Ghost of Yotei's current blocker: its compute
shader at hash=0xCECFBCC210CB2372 pc=0x59C resolves an image
descriptor's byte offset through a loop-carried Phi (a hash-table/
linear-probe style bindless material lookup) that is not loop-
invariant, so it genuinely cannot be pre-evaluated as a single
per-dispatch runtime value the way MakeRuntimeBufferSource/
RuntimeValueEvaluator expect. That needs real GPU-side dynamic
descriptor indexing support, which doesn't exist yet in this
resource-tracking pass.
Also add SHARPEMU_RESOURCE_TRACKER_DIAG=1, gating verbose stderr
tracing in TryMakeIndirectImage's rejection paths and
RuntimeValueValidator's node-validation failures. This is what
localized the loop-carried Phi as the actual root cause; keeping it
opt-in and off by default costs nothing at runtime.
* shader(resources): degrade control-dependent bindless image/sampler descriptors to null instead of failing compilation
Ghost of Yotei's material lookup resolves an image descriptor's
offset through a loop-carried phi (a hash-table/linear-probe bindless
scheme) that RuntimeValueValidator correctly can't treat as a single
compile-time source - this resource tracker has no GPU-side dynamic
descriptor indexing to fall back on. Track *why* validation failed
(RuntimeValueValidator.ControlDependent, set when the failure traces
to a non-invariant phi) and, for image/sampler handles specifically,
degrade to a null descriptor instead of hard-failing shader
recompilation. Buffer handles and any other failure reason still fail
as before.
This ports KytyPS5's fix for the identical case
(feat/shader-control-dependent-descriptor, "shader: fall back to a
null image when its descriptor is control-dependent") rather than
attempting full dynamic descriptor indexing support, which doesn't
exist in either codebase.
Also add SHARPEMU_RESOURCE_TRACKER_DIAG tracing in
RuntimeValueEvaluator's evaluation failure paths (per-source-dword,
flattened table reads, and raw memory reads), used to localize the
next blocker down this same path.
* gpu: keep image shader types compatible with Vulkan views
* shader: decode and emit VOP1 0x39 (VFfbhU32, find-first-bit-high)
Another decode gap in the same family as the earlier MIMG/SOP1 fixes:
VOP1 0x39 was missing from the opcode name table, aborting
translation of Ghost of Yotei's next compute shader before it reached
GPU submission. Ported the fix straight from our own tree's
Gen5ShaderTranslator.cs/Gen5SpirvTranslator.Alu.cs, which already
decodes and emits it: reverse the bits then use the GLSL FindILsb
extended instruction, since RDNA reports the leading-zero count with
the architecturally required 0xFFFFFFFF sentinel for a zero source.
* shader(resources): treat a null-based SRT raw read as zero instead of faulting
A bindless pointer the guest leaves unbound for a dispatch resolves
its address-handle dwords to zero; EvaluateRawRead's raw-address
branch (as opposed to the buffer-descriptor branch) then dereferenced
that near-null address directly, and the guest memory reader
correctly refused it as unmapped, failing the whole materialization
with a generic "reason=Other". Ghost of Yotei's next compute shader
hit exactly this: a flattened SRT table read through a null-resolved
48-bit pointer plus a small immediate offset landed at address 0x14.
Treat any raw-address read below a small guest null-page threshold
(0x10000) as reading zero, so the value (and whatever descriptor it
feeds) collapses to null instead of faulting materialization. Ported
directly from KytyPS5's identical fix for the identical symptom
("shader: treat a null-based SRT constant read as zero").
* shader: decode/emit VOPC 64-bit compares and the 64-bit LDS atomics
Ghost of Yotei's cs 0x80003AF600 emits V_CMPX_NE_U64 (VOPC 0xF5) and DS_ADD_U64/DS_OR_B64 (0x40/0x4A), which the decoder refused. The VOPC 0xE0-0xE7 / 0xF0-0xF7 forms are the same V_CMP/V_CMPX U64 operations the VOP3 table already names and the emitter already lowers, so mirror them into the two-operand VOPC table.
The 64-bit LDS atomics sit just ahead of DS_WRITE_B64. LDS is modelled as an array of dwords, so each is lowered to the two 32-bit atomics that make it up. Neither opcode returns the old value, which makes the split exact: only the final memory state is observable. OR is bitwise-independent, so two 32-bit ORs are the 64-bit OR; ADD folds exactly one carry per low-dword wrap into the high dword, so the high word converges on the true wrap count regardless of lane interleaving. The RTN variants cannot be split without losing the returned pair's atomicity, so they stay undecoded and keep failing loudly.
* shader: decode and emit VOP3-encoded 64-bit VOPC compares (0xE0-E7, 0xF0-F7)
Ghost of Yotei's material-lookup shader issues V_CMP_*_U64/V_CMPX_*_U64
through the VOP3 encoding (opcode < 0x100, the same numbering VOPC
uses), which DecodeVop3's own opcode table never listed - it only
handled VOP3-native opcodes >= 0x101. Ported the fix straight from our
own tree, which already special-cases this exact sub-range (see the
"VOP3-encoded 64-bit VOPC" comment there): decode 0xE0-E7/0xF0-F7 to
the VCmp*U64/VCmpx*U64 names, and emit them as a 64-bit integer
compare (or a constant true/false for the F/T pseudo-compares),
reusing the existing generic VCmpx exec-mask handling.
* shader: implement GFX10 double-precision conversions and arithmetic
Decode VOP1 0x04/0x0F/0x16/0x2F/0x30/0x34 and their VOP3-encoded forms (V_CVT_F64_I32, V_CVT_F64_U32, V_CVT_F32_F64, V_RCP_F64, V_RSQ_F64, V_SQRT_F64) and emit them as real SPIR-V doubles. A 64-bit result is split across the destination register pair: the low word goes through the common destination store and the high word is stored alongside it, both under the EXEC guard. V_MUL_F64 (VOP3 0x165) and V_FMA_F64 (VOP3 0x14C) are added as well.
Source absolute/negate modifiers and the VOP3 output modifier and clamp are honoured. FP64 inline constants and literals are rejected explicitly rather than read as the wrong bits, following the gfx10 expansion rules. The shaderFloat64 device feature is enabled only when the physical device offers it, and the Float64 capability is declared only for modules that use a double.
Ghost of Yotei compute shader 0x8000393600 (hash 0xB90E2024732C6111) now translates and compiles instead of stopping at decode.
* shader: decode and emit VOP2 V_PK_FMAC_F16
V_PK_FMAC_F16 (VOP2 0x3C) reads the destination register as the addend for both packed f16 lanes and writes D = S0 * S1 + D back as a packed pair. Reuse the existing single-rounding fused-f16 path per lane and merge the two halves, so the result matches the hardware's f16 fused behaviour.
Ghost of Yotei compute shader 0x8000393600 reaches this opcode after the decoder is past the f64 gap.
* shader: decode and emit VOP3 V_MAD_I32_I24
V_MAD_I32_I24 (VOP3 0x142) differs from the already-handled V_MAD_U32_U24 only in that both multiplicands are signed 24-bit values. Sign-extend each operand from bit 23, multiply with 32-bit wrap and add the third source.
Ghost of Yotei compute shader 0x8000393600 reaches this opcode after the decoder is past the f64 gap.
* shader(resources): address runtime-fetched buffer descriptors via the device-address page table
A V# whose four dwords are all read from a runtime scalar-memory address (a descriptor-array entry indexed by a loop counter, as in Ghost of Yotei's cs 0x80003F2A00 at pc=0x1B4) cannot be bound ahead of time, so resource tracking used to fail the whole shader. Its dword reads are not control-dependent in any other way: they are real guest memory loads emitted at runtime through the device-address page table.
Detect that case in ResourceTracker and mark the access RuntimeBufferDescriptor instead of building a dense buffer resource. The SPIR-V backend then builds the guest address from the descriptor's runtime base (SGPR pair), the MUBUF offset/soffset/vector offset and, when indexed, the descriptor's stride, and stores/loads through the existing physical-storage-buffer path. num_records/stride bound the access so a null or empty descriptor binds nothing, matching the static path's range check. Formatted, typed and atomic accesses keep failing loudly because their descriptor format cannot be reconstructed from the raw words.
This completes the former d8959888aafd2552 dispatch.
* shader(resources): recognize runtime V# descriptors built from SRT reads and loop phis
The first runtime-descriptor detection only accepted four direct raw ScalarAddressWord dwords. Ghost of Yotei's cs 0x8000289300 at pc=0x78C builds its V# from a loop-carried phi that merges two SBufferLoadDwordx4 reads of the SRT buffer at a dynamic offset, so the access was still rejected even though the descriptor is a real runtime value.
Make the detection generic: a BufferHandle is a runtime descriptor when each dword is a plan-time-known value (constant, user data, shader base, flattened table word) or a guest-memory read, recursing through phis, selects and operations, and at least one leaf is a raw read. A cycle through a phi is accepted the same way the read planner treats cyclic phis. Anything else keeps hard-failing.
The raw BufferLoadUshort at pc=0x78C now lowers through the device-address page table. The formatted BufferLoadFormatX at pc=0x18F4 correctly escalates to BoundedCandidateTable, which is still unimplemented and is now the next required feature.
* shader(resources): plan bounded runtime V# candidate tables
* shader(resources): materialize bounded runtime V# candidate tables
* shader(resources): address a runtime scalar buffer V# through the device page table
* shader(resources): model the guest V# separately from its Vulkan lowering; real 64-bit LDS atomics
Replace MemoryAccessInfo.RuntimeBufferDescriptor with a GuestBufferDescriptor that carries the V#'s provenance (Static or Runtime) and, for static descriptors, the dense-buffer index. The backend now chooses an explicit BufferLoweringStrategy from the descriptor and the access shape via GuestBufferDescriptor.ChooseStrategy, instead of hard-coding a single path: NativeBinding for compile-time descriptors, PhysicalStorageBuffer for a runtime descriptor used by a raw dword/subword access, and BoundedCandidateTable for a runtime descriptor whose format/type cannot be reconstructed from the raw words. The last one is not implemented and fails loudly with a precise reason, which is now a single, discoverable place rather than a boolean scattered through the translator.
DS_ADD_U64/DS_OR_B64 are now emitted as true 64-bit workgroup atomics when the device exposes shaderSharedInt64Atomics (queried through VkPhysicalDeviceShaderAtomicInt64Features, declared with the Int64Atomics capability, addressed through a 64-bit LDS view). The previous two-32-bit split is kept only as a documented fallback for devices without the feature; it is not atomic as a pair. Device setup reports the capability once.
* shader/image: accept 11_11_10 UNORM sampling and 8-bit sRGB render targets
The unified image format 30 is 11_11_10_UNORM, a packed 11/11/10 normalized float format the rendering side already knows as GuestPixelFormat.Bits11_11_10UNorm (mapped to VK_FORMAT_B10G11R11_UFLOAT_PACK32). It was missing from the shader-side sampled-format table, so cs 0x8000265D00 failed specialization with 'sampled image descriptor 7 uses unsupported format 30'.
Bits8Srgb and Bits8_8Srgb also had RenderTargetBytesPerElement = 0 while their element sizes are 1 and 2 bytes, which made TryGetTextureSize reject a RenderTarget-tiled 8-bit sRGB surface (tile mode 27) that the guest creates. Their render-target footprint is the same as the matching UNORM formats, and the host view already substitutes R8/R8G8 UNORM, so give them the real byte counts.
* shader: allow inline constants and literals as VOP3P packed-f16 operands
The VOP3P packed-f16 path only accepted register operands and refused constants, so cs 0x80003AF600 stopped at V_PK_FMA_F16 with src[128]. An inline constant or literal is a single f32 value rather than a packed register pair, so there is no half to select: both lanes take the exactly-converted f16 of that constant, with the lane's negate modifier still applied. Keeps the register path unchanged.
* shader: support Yotei f16 shifts bitsets and safe LDS atomics
* shader(vulkan): dispatch bounded runtime V# candidate tables
* shader: support additional LDS operations
* shader: support RDNA LDS store encoding
* shader: handle LDS operands and RDNA buffer cache op
* shader: extend strict GFX9 buffer and ALU decoding
* [VideoOut] added output mode events and profiling fixes
* [Gpu] fixed image, render, and dispatch handling
* shader: track SDWA and runtime buffer values
* [Shader] expanded Gen5 resource and interpolation support
* gpu: reconcile PR893 metadata and indirect dispatch
* gpu(commands): consume unknown NOP marker ids instead of failing the submission
Demon's Souls emits markers 0x6EC and 0x80C, which the interpreter treated as
fatal. The packets carry no work for the ids without a known side effect
(flips, user-data marker context), so they are consumed and each id is
reported once (command_stream.marker_ignored). voidborn, which gets much
further on this title, likewise ignores NOP marker payloads it has no
handler for.
* kernel: implement sceKernelGetOperationMode (PS5 base console, no submode)
The import was unresolved, so callers polling it (Demon's Souls does it at very
high frequency) got an unresolved-import result instead of the mode. Behaviour
matches KytyPS5 KernelGetOperationMode: *mode = 2, *submode = 0.
* loader: protect red-zone functions with enclosing patch spans
The red-zone patcher only built a trampoline span forward from the faulting
instruction, so it gave up whenever the next instruction was a branch target,
touched RSP, or changed control flow. Those refusals never reached FailedSites,
and the summary line reported sites=11472/11472 failed=0 while 777 faultable
instructions inside red-zone functions stayed unprotected.
Demon's Souls died on one of them. A GPU write-tracking fault at 0x800AD91F8
dispatched on the guest stack with RSP unshifted, which let Windows overwrite
the 128 bytes below RSP; the function reloaded its pointer from [rsp-0x10],
got 0, and faulted writing through it. The fault census confirms the same RSP
in the faulting frame as in the crash report.
Add TryBuildEnclosingPatchSpan: when the forward search refuses, take the
span's bytes from before the faulting instruction so the span ends at that
instruction and the one after it is never rewritten, which keeps every existing
branch into it valid. Two invariants make this safe and both are exercised by
the new tests:
- the RSP shift brackets only the faultable core, so a relocated
RSP-relative access still reads the slot it meant to;
- a span may not start before the end of the previous site, because a
backward span can otherwise reach instructions an earlier site already
replaced with its own jump.
Patch() also refuses overlapping sites outright rather than corrupting guest
code, and the scan now reports unrelocatable sites by category.
Unprotected sites drop from 777 to 270. The title runs past the main menu into
its opening cinematic and stops on an unrelated shader resource error.
The fiber tests move into a shared collection: they mutate the same static
FiberExports state as FiberExportsTests, and xUnit runs separate classes in
parallel, which made them fail intermittently.
* cpu: count guest faults and make access-violation reports readable
Guest code is commonly mapped PAGE_EXECUTE, which no host read can satisfy, so
IsReadableHostRange refused it and the whole call-site section of the access
violation report was skipped: every crash in such a title printed "Could not
read code at RIP" and nothing else. Read those windows through the guest memory
view instead, and print the faulting region so a future refusal explains
itself.
Add a census of the recoverable faults taken on guest threads, by guest RIP and
by the handler that resolved them. Windows has no alternate exception-dispatch
stack, so each of those faults overwrites the 128 bytes below RSP; knowing which
guest instructions fault is what turns a red-zone corruption from a hypothesis
into a measurement. Counters are allocated once per RIP and are inert unless
SHARPEMU_FAULT_CENSUS is set, since the handler is hot.
Also fix the guest context transfer frame: it published the stack bounds in the
wrong order, storing the low address in NT_TIB.StackBase and the high one in
StackLimit on every fiber switch.
Note: these two files also carried uncommitted work from earlier in this
worktree (guest stack sizing, the 22-qword transfer frame, import and exception
paths); that work is included here rather than separated, because the hunks
interleave.
* gpu: stop handing image ownership to a buffer that was only bound
Binding a formatted writable buffer over an image's memory invalidated every
image covering that range. The shader only declares that it may write there, so
this fired on every bind of every draw, and when the range belonged to a render
target the image was refreshed from guest memory the GPU never wrote - its
tiled backing read as linear, over the frame that had just been rendered.
A Demon's Souls capture showed 412 buffer-dirty refreshes and 207 GB of image
traffic. Suppressing the two bind-time call sites removed the banding entirely
and took the frame time from 7471 ms to 4272 ms, with GPU occupancy going from
29% to 45%; buffer-dirty refreshes dropped to zero.
Writes that actually happened still invalidate, from GuestBufferCache's fill and
DMA-copy paths, which is what the image ownership tests cover. The declared-write
case needs a reconciliation after the submission, once the GPU-modified ranges
are known; that does not exist yet, so
SHARPEMU_SPECULATIVE_IMAGE_INVALIDATION=1 restores the old behaviour meanwhile.
Note: these two files also carried uncommitted work from earlier in this
worktree.
* shader: name the instruction behind an undefined descriptor dword
A descriptor dword that resolves to an undefined value fails the resource plan
with nothing to act on. Undefined nodes are interned per type, so one shared
node stands for every instruction the builder could not model, and the builder
has about twenty places that produce one: the message names a category where it
should name an opcode.
Record the instruction the builder is translating, and under
SHARPEMU_RESOURCE_TRACKER_DIAG intern undefined nodes per instruction as well.
Revisiting a block reaches the same instruction, so values stay as stable across
visits as before, and the rejection can walk the failing dword back to the
undefined leaves it rests on and name them. Without the diagnostic the interning
and the message are unchanged.
A first attempt recorded every undefined created while an instruction was in
flight, which listed 1410 instructions for one shader because many are created
legitimately, for SCC among others. Only the leaves the failing value actually
depends on are reported.
Test counts are unchanged at 614 passed and 95 pre-existing failures.
* shader: carry over the Gen5 translation work from the port
Pre-existing work in this worktree, recorded here rather than authored in this
session: VOP3B carry/borrow paths for VSubCoCiU32 and VSubrevCoCiU32, the high
opcode bit for MIMG and the ray-tracing names it selects, DS/GDS flags, the
wave32 EXEC mask, and the scalar value graph and runtime evaluator changes that
go with them, across the Vulkan and Metal back ends.
* gpu: carry over the AGC and VideoOut work from the port
Pre-existing work in this worktree, recorded here rather than authored in this
session: tracked image caching in place of unused snapshots, depth/stencil
handling, snapshot reuse for feedback loops, read-only depth, the Vulkan
feedback target resolver, the GuestDepth and draw recording paths, GPU barriers
and image cache tracking, the storage resolver, BDA and GPU memory tracking in
VideoOut, and the submission and indirect dispatch corrections.
* hle: carry over the library export work from the port
Pre-existing work in this worktree, recorded here rather than authored in this
session: an HTTP connect timeout, an offline MAC, NP signalling context, the
asynchronous NP stubs and entitlement key, NP WebAPI context deletion, a CFW
stub, and the added aliases for sceImeKeyboardGetInfo, unlink, sched_yield,
scePadResetOrientation, scePadIsRemoteController and the solo scheduling
attributes, along with the Ampr file registry and kernel compatibility exports.
* cpu: carry over the guest execution and memory work from the port
Pre-existing work in this worktree, recorded here rather than authored in this
session: the guest stack size the pthread attribute asks for and its cap, the
PthreadYield fallback, the multi-thread stall watchdog, import and handoff
instrumentation, the host view and guest space owner changes, and the physical
virtual memory and runtime adjustments that go with them.
* base: verified pre-rebase gameplay baseline
* fix: materialize indirect shader image resources
* gpu,shader: Yotei bring-up changes (GPT session, unreviewed)
Work from a separate GPT session aimed at Ghost of Yotei, committed as-is so
it can be reviewed block by block. It clearly improves Demon's Souls rendering
(the scene becomes visible), but review found problems to fix before a PR:
- S_BITREPLICATE_B64_B32 copies the dword into both halves; the ISA doubles
each bit (bit i -> bits 2i, 2i+1). Wrong in the translator and the graph.
- FindMatchingMipLevel no longer matches 2D array views of 3D volumes; the
test expectation was flipped from 0 to -1 instead of keeping the case.
- VectorCompareResourceTests lost five passing tests, replaced by
compile-only smoke tests.
- 11_11_10 UNorm is mapped to a packed-float Vulkan format.
- Unknown BufferLoadFormat descriptors now read zeros / null images silently.
- Undefined scalar values are interned per instruction for every title.
Also adds 16-bit integer VOPC compares, V_RSQ_F16, IMAGE_GATHER4_C_LZ,
IMAGE_SAMPLE_A decode, Yotei base entitlements, richer image-creation
failure messages and flip-source diagnostics.
* diagnostics(renderdoc): capture automatically after N guest flips
SHARPEMU_RENDERDOC_CAPTURE_AFTER_FLIPS=N requests one capture once N guest
flip boundaries have been seen, so an unattended run can capture without the
F12 key. The count is kept here because the flip image version is per display
buffer, not a global frame index.
* fix(import-guard): recognize polling progress boundaries (#895)
* port shader GDS byte reads and float atomics
* port kernel debug exception exports
* Revert "port kernel debug exception exports"
This reverts commit 71d605e82c82a4a921862bfc865a104305cf746d.
* Revert "port shader GDS byte reads and float atomics"
This reverts commit 8f4e7664d7fc81a28ba5c47c86cbc03dba61d47b.
* [usbd] implement event timeout
* [usbd] add initialization stubs
* [share] add feature and overlay exports
* [kernel] implement pthread cond signalto
* [font] add renderer and kerning exports
* [agc] implement ACB rewind exports
* [share] add running status export
* [stubs] add game service exports
* [kernel] implement high resolution timer events
* [gpu] support polygon primitive rendering
* chore: bump version to 0.0.4
* [kernel] implement debug exception exports
* [vulkan] adopt synchronization2 submission and barriers
* docs: update upstream README
* Revert "[vulkan] adopt synchronization2 submission and barriers"
This reverts commit 7e44f90d624c05ccac3ba8d14f94bfa63c225b1d.
* Reapply "[vulkan] adopt synchronization2 submission and barriers"
This reverts commit 66b456dd2a4562b081970404034ab3cc3135f66a.
* Revert "[kernel] implement high resolution timer events"
This reverts commit faf105d9a0f93120f9954ff30890701683d79a67.
* Reapply "[kernel] implement high resolution timer events"
This reverts commit 9858506307cfdc43e875180f21727bea936ac888.
* Revert "[kernel] implement pthread cond signalto"
This reverts commit 83ad3b0ac313b01c7d1493e685423a5a419b8766.
* Reapply "[kernel] implement pthread cond signalto"
This reverts commit 4bc31dabf7885687dc3777aefede6b4122f936a4.
* [ampr] fall back to executable-local index cache
* [ampr] probe cache directory writability
* [media] keep host movies progressing when Vulkan bindings lag
* [media] release stalled host movies independently of rendering
* [vulkan] reuse pipeline caches across emulator builds
* [media] propagate stalled movie EOF to the guest
* [ampr] return guest path ids from resolve exports
* Revert "[vulkan] reuse pipeline caches across emulator builds"
This reverts commit f0f8e21382b402b510645acb66cf122ad765cd4a.
* [vulkan] reuse pipeline caches across emulator builds
* Revert "[vulkan] reuse pipeline caches across emulator builds"
This reverts commit fd667b6320b74be1e5d71b37a3c29fc11af713bb.
* fix(media): honor full Bink movie duration
* fix(vulkan): schedule wave64 barriers uniformly
* fix(vulkan): emit wave64 loop merge before branch
* fix(vulkan): predicate wave64 image stores
* Revert "fix(vulkan): predicate wave64 image stores"
This reverts commit 5842298743822d1b128e8b4dcb3f9d0a8fd03c35.
* Revert "fix(vulkan): emit wave64 loop merge before branch"
This reverts commit d2b98e0a8439e41c8faf04335320f202740fe7f9.
* Revert "fix(vulkan): schedule wave64 barriers uniformly"
This reverts commit 976d5a5957080de6568417c9d12235570b1503ef.
* baseline: preserve good Demon's Souls rendering
* Revert "[vulkan] adopt synchronization2 submission and barriers"
This reverts commit 7e44f90d624c05ccac3ba8d14f94bfa63c225b1d.
* fix(ampr): keep APR resolve ids unique on path-hash collisions
The APR resolve exports now return the 31-bit FNV hash of the guest path
(upstream 5c347d2c). Demon's Souls ships 13 same-hash file pairs under
/app0/ alone, and it resolves 32 colliding paths before the main menu.
Colliding files shared one id and read each other's bytes, and the
first level load after the menu never finished.
Hand out the hash only while it names one host file, keep it in an
authoritative table that later alias publishes cannot poison, and fall
back to a collision-safe Register() handle otherwise. Log both the
fallback and reads of unknown ids, which previously failed silently.
* fix(media): size the Bink watchdog to the movie, restore Gen4 export
c77746bd reverted the duration-based Bink watchdog to a fixed 90 s, and
MediaFramePlayback also capped every movie (AvPlayer too) at 120 s.
Demon's Souls ships a 171 s intro, 117-138 s attract movies and 756 s
credits, which both bounds cut short. Arm the watchdog for the movie
length plus 60 s (minimum 90 s, no upper bound) and drop the fixed
playback cap, as in the pre-rebase baseline.
Also keep cfwBSQyr5Ys exported for Gen4 as the baseline did.
* chore: trim added comments, keep upstream's cfwBSQyr5Ys target
Restore upstream's APR ABI comment verbatim, keep each added comment to
one line, and leave sceKernelDebugWriteCppExceptionInfo Gen5-only as
upstream declares it.
* fix(vulkan): query image levels only when the result is read
ImageGetResinfo emitted OpImageQueryLevels for every sampled image even
when the dmask never reads component 3. On a 3D image, the unused query
crashes NVIDIA's pipeline compiler inside vkCreateComputePipelines:
Demon's Souls compute shader 0xC553F37E46481A6B (dmask 3) takes the
process down right after the first frame.
* fix(shader): reconcile branch resource planning with upstream contracts
- Keep upstream's 42-binding image ABI. A float min/max image atomic
binds as the uint atomic image its compare-exchange loop already uses.
- A null-page read turns into zero only after a null base pointer fails
to read, not for every address below 0x10000.
- An empty V# reads as zero; overrunning a bound buffer stays a failure.
- A wave-indexed image table reads its mask through the clean reader,
like every other slot its descriptor depends on.
- A buffer load through a lane-varying address stays undefined, and a
constant exec_lo write keeps s_cbranch_execz decidable.
- An image descriptor from a scalar buffer must be one contiguous
record, and a vector access through a draw-evaluable V# keeps its
explicit binding instead of the runtime-descriptor path.
* fix(shader): drop the null-page read rule, encode GDS at bit 17 in tests
KytyPS5 does not turn a null SRT pointer read into zero: an unmapped
read fails materialization, which FailedResourceReadsRemainFatal pins.
Remove the rule.
GFX10 DS encodes GDS at bit 17 with OP at 25:18; bit 16 is GFX9's
position (LLVM DS_Real_gfx10). The decoder already reads bit 17, so the
DataShare tests set GDS there, and the LDS wave-counter tests clear it.
* fix(vulkan): keep bind-time image invalidation coherent with GPU contents
Binding a formatted writable buffer over an image marks the image
buffer-modified, so its next use refreshes it from the buffer. The
buffer was only synchronized from the image for read-only views, so
over a render target a writable view refreshed the image from stale
guest bytes. That is the Demon's Souls banding 48cd723a worked around
by skipping the invalidation altogether.
Synchronize writable formatted views from the image as well, and at bind
time invalidate every covered image except one whose only current copy
is still on the GPU and could not be copied into the buffer. Drop the
SHARPEMU_SPECULATIVE_IMAGE_INVALIDATION switch.
* test(shader): expect the header immediate in the indirect selector offset
The materializer reads the selector word at selector * stride +
SelectorOffset, so the offset must include the material read's
immediate. With the header immediate 4 and the +4 in the address
arithmetic it is 8; the test expected 4 since it was written.
* chore: remove session diagnostics
Drop the ad-hoc instrumentation added while bringing Demon's Souls and
Ghost of Yotei up:
- SHARPEMU_RESOURCE_TRACKER_DIAG traces in the resource tracker, dense
image tables, runtime evaluator and validator
- unconditional materialize_snapshot_probe warnings and the
SHARPEMU_TRACE_INDIRECT_IMAGES wave-table trace
- the guest fault census (SHARPEMU_FAULT_CENSUS / SHARPEMU_FAULT_WATCH)
- red-zone patcher address trace, unprotected-site file and short-jump
estimate (SHARPEMU_REDZONE_TRACE / SHARPEMU_REDZONE_SITES_FILE)
- RenderDoc capture after N flips and the flip-source refresh traces
The red-zone coverage counters and the readable access-violation report
stay.
* fix(kernel): sparse virtual reservations and HLE compatibility fixes
- sceKernelReserveVirtualRange: keep very large non-fixed reservations
in the guest mapping table without one contiguous host placeholder.
- Event flags: read names and write handles through the kernel
compatibility helpers so read-only image pages work.
- sceAjmInitialize: only validate the 32-bit reserved argument.
- Pthread: only free opaque objects the compatibility layer allocated.
- Route libc memset to the HLE implementation (int 45h path in LLE).
* fix(gpu): tiling, color targets, VideoOut pipelines and primitive stage masks
- Tile geometry and tiler shader fixes for detiling.
- Color target request fixes for 3D slice ranges.
- VideoOut descriptor, draw recording and pipeline fixes.
- Accept the compact primitive-shader stage masks (0x2000, 0x2030)
and report the rejected state when draws are skipped.
* fix(shader): Little Nightmares III decoding and translation
- Decode S_SCRATCH_STORE_DWORDX2 (SMRD op 0x16) as a scalar scratch
store instead of a load.
- Raise the decoder instruction limit to the maximum shader size.
- Stop linear decoding at S_SETPC_B64 and treat the tail as
unreachable; find fused GS pairs across the full 1 MiB object and
resolve continuation image descriptors from its entry SGPRs.
- Translate S_SETPC_B64 in linearised fused programs as a marker.
- Implement S_MUL_HI_I32 and V_DOT2C_F32_F16.
* fix(shader): Astro Bot debugger stubs and wave32 primitive pipelines
- S_CBRANCH_CDBG* only fire under an attached GPU debugger; do not let
them extend the decoded program past S_ENDPGM into debugger stubs
that load descriptors from devkit-only addresses.
- Stop decoding after an unconditional backward S_BRANCH with no
pending forward target instead of reading the "sl00" metadata
trailer as instructions.
- Ignore the GS_W32_EN/VS_W32_EN wave-size bits when matching
primitive-shader stage masks; graphics stages always compile as
wave32, so these draws were skipped for no reason.
* fix(kernel): keep sparse reservations out of the macOS graphics window
macOS reserves 0x10_0000_0000..0x70_0000_0000 for graphics memory, so a
sparse reservation starting at 0x40_0000_0000 could not be mapped there
(LargeVirtualReservationFallsBackToSparseGuestRange failed on osx-x64).
Start the sparse aperture at 0x70_0000_0000 on macOS.
* fix(shader): declare guest 1D textures as 1D images
Since #903 the image request builder binds guest 1D textures through
1D Vulkan views, but the SPIR-V translator still declared them as 2D.
Sampling a 1D view through a 2D image type is invalid, and on NVIDIA
Astro Bot's final composite pass wrote black to the flip buffer.
Declare Dim1D/Dim1DArray as SPIR-V 1D images with Sampled1D/Image1D,
matching upstream.
* revert(gpu): fail on written device-address ranges that cannot be planned
The Little Nightmares III work skipped unplannable written ranges on the
host and allowed those stores unconditionally in the shader. Stores whose
pages were not already in the BDA page table were then dropped silently.
Restore upstream's fatal and remove UnplannableWrittenMemoryIndices, so
each such shader is reported and fixed on its own.
* fix(shader): route FLAT accesses through the LDS and scratch apertures
GFX10 sends a FLAT access to LDS or scratch when bits 63:48 of its
address match the shared or private aperture (SH_MEM_BASES), and to
global memory otherwise. src_shared_base/src_private_base decoded to 0,
so an LDS pointer became a device address the host could not plan
(Little Nightmares traversal stack, shader 0x9669394374AAE5BC).
- Give the apertures distinct high dwords no guest address can reach,
consistent for 32-bit (high dword) and 64-bit (address) reads.
- Keep aperture identity in the scalar value graph (MemoryAperture).
- The device-address planner classifies a FLAT access whose high dword
derives from an aperture and gives it no device range; plain device
pointers keep the range and the fatal.
- The SPIR-V backend routes each lane by bits 63:48 to LDS, scratch or
the page-table global path.
* fix(shader): use the PS5 apertures and prove clamped FLAT addresses local
Little Nightmares builds its BVH traversal-stack addresses without
src_shared_base: v_med3_u32(0x80000000, 0x70000000, sp) clamps the high
dword, and the pop path reads the 0x7xxxxxxx entries with ds_read and
the 0x8xxxxxxx ones with scratch_load. These are the PS5 shared and
private apertures, so:
- SharedApertureHigh = 0x70000000, PrivateApertureHigh = 0x80000000;
the backend routes a FLAT lane by the top nibble of its high dword.
- Model v_med3_u32/v_med3_i32 in the scalar value graph.
- The planner bounds the high dword (Phi/Select/UMin/UMax, cycles
ignored since each yields one of its inputs) and classifies it local
when the bounds stay inside the apertures.
- Record the EXEC mask of each global/flat access: a select on that
same mask is a lane-masked register write whose inactive arm never
reaches a lane that accesses memory.
Little Nightmares shaders 0x9669..., 0xE9F1... and 0x392B... now plan
without the removed unplanned-write skip.
* feat(shader): emulate the GFX10 BVH node test for image_bvh_intersect_ray
Follows AMD GPURT's RT IP 1.1 fallback: triangle nodes 0/1 with barycentric
swizzle, FP16/FP32 box slab test with grow, NaN rule and sorting network.
Nodes are read through the device-address page table.
* fix(gpu): move a depth surface sampled as color into a color image
A texture whose format reads neither the depth nor the stencil plane
reinterprets the memory; Little Nightmares samples a D32S8 surface as BGRA8.
* fix(shader): make s_barrier order buffer and image memory within the workgroup
Guest code publishes stores to its workgroup with s_waitcnt vmcnt(0) +
s_barrier; the host barrier only ordered shared memory.
* fix(gpu): match sampler border color and filtering to the sampled view
Border colors were always integer, which Vulkan leaves undefined on float
views (an opaque-white border can read back as black). The numeric class
now comes from the bound view formats; integer views also drop filtering.
* fix(shader): decode the VOP1 and VOP3 half-precision transcendentals
Silent Hill's pixel shaders use v_log_f16; add rcp, sqrt, log, exp,
floor, ceil, trunc, rndne, fract, sin and cos for f16 in both encodings.
* fix(shader): decode v_min3_f16, v_max3_f16 and v_med3_f16
Silent Hill's pixel shaders use v_min3_f16; the draw was skipped.
* fix(agc): give a fused shader the larger user SGPR count of its halves
sceAgcFuseShaderHalves kept the back half's SPI_SHADER_PGM_RSRC2_GS, so a
front half reading its vertex tables from user SGPRs 26-29 saw only 12
(Silent Hill: front 30, back 12) and the draw faulted on null tables.
A vertex program without input semantics no longer needs the tables, and
the null-table and PRT-mapping messages name the registers involved.
* fix(shader): read buffers past the binding budget through their device descriptors
A Silent Hill compute shader binds more than 32 distinct buffers. Past the
budget an untyped access reads its V# from its SGPRs at run time, as the
hardware does, instead of failing the whole shader.
* fix(gpu): treat a compute constant fill of DCC metadata as a fast clear
Unreal fast-clears DCC render targets with a compute shader that stores one
dword from a constant buffer into every metadata record (0x40404040 clears
to 0,0,0,1). The fill only reached plain memory, so Silent Hill's
translucency layer stayed 0 and its composite multiplied the scene by 0.
The exact instruction sequence is recognised and routed to the existing
DCC clear tracking.
* fix(gpu): invalidate every image under a bound writable formatted buffer
The bound-write path skipped images whose only current copy was on the GPU.
Such an image kept stale contents after the guest rewrote it through the
buffer: Dead Cells lost its background and Demon's Souls showed wrong
surfaces. Bisected to ed3de27b; upstream invalidates unconditionally, and
52fbe275 already synchronizes writable views from the image, which fixed the
banding the exception was meant to hide.
---------
Co-authored-by: ParantezTech <12572227+par274@users.noreply.github.com>
Co-authored-by: Mathias <06mathias6@gmail.com>
* refactor(videoout): isolate host buffer code
Move pooled host-visible Vulkan buffer management into a dedicated presenter partial.
The new location makes ownership clear.
Execution behavior does not change.
* refactor(videoout): isolate draw state code
Move guest draw-state conversion into a dedicated presenter partial.
This change only improves code organization.
Keep runtime behavior unchanged.
* refactor(videoout): isolate texture format support code
Move Vulkan image-format capability and render-target mapping into the texture-format presenter partial.
This move changes code ownership only.
Runtime behavior stays the same.
* refactor(videoout): isolate presentation image code
Move presentation-target selection and offscreen presentation-image creation into a dedicated presenter partial.
The new location makes ownership clear.
Execution behavior does not change.
* refactor(videoout): isolate HDR presentation code
Move HDR presentation resources, pipelines, and command recording into a dedicated presenter partial.
This change only improves code organization.
Keep runtime behavior unchanged.
* refactor(videoout): isolate draw execution code
Move offscreen draw, clear, guest-image write, and render-pass execution into a dedicated presenter partial.
This move changes code ownership only.
Runtime behavior stays the same.
* refactor(videoout): isolate guest submission code
Move guest GPU batching, submission, retirement, and visibility waits into a dedicated presenter partial.
The new location makes ownership clear.
Execution behavior does not change.
* refactor(videoout): isolate render loop code
Move presenter work waiting, queue draining, frame acquisition, and work dispatch into a dedicated partial.
This change only improves code organization.
Keep runtime behavior unchanged.
* refactor(videoout): isolate draw recording code
Move translated draw recording, image transitions, draw tracing, and resource retirement into a dedicated presenter partial.
This move changes code ownership only.
Runtime behavior stays the same.
* refactor(videoout): isolate guest work queue code
Move guest GPU work submission, scheduling, backpressure, and completion into a dedicated partial.
The new location makes ownership clear.
Execution behavior does not change.
* refactor(videoout): place storage image contracts code with its owner
Move SPIR-V storage image contract parsing and validation into the compute storage-image partial.
This change only improves code organization.
Keep runtime behavior unchanged.
* refactor(videoout): place ordered guest flips code with its owner
Move ordered guest flip capture and completion into the presentation-frame partial.
This move changes code ownership only.
Runtime behavior stays the same.
* refactor(videoout): place frame lifetime code with its owner
Move presentation frame waits and flip-version retirement into the presentation-frame partial.
The new location makes ownership clear.
Execution behavior does not change.
* refactor(videoout): place texture cache resolution code with its owner
Move cached texture resolution and CPU refresh into the texture-cache partial.
This change only improves code organization.
Keep runtime behavior unchanged.
* refactor(videoout): place barycentric pipeline code with its owner
Move the fullscreen barycentric shaders and pipeline creation into the pipeline partial.
This move changes code ownership only.
Runtime behavior stays the same.
* refactor(videoout): place swapchain draw resources code with its owner
Move presentation render-pass, image-view, and framebuffer setup into the swapchain partial.
The new location makes ownership clear.
Execution behavior does not change.
* refactor(videoout): place feedback snapshot creation code with its owner
Move color feedback snapshot creation beside its pool lifecycle and telemetry.
This change only improves code organization.
Keep runtime behavior unchanged.
* refactor(videoout): place texture format conversion code with its owner
Move real-format conversion and its transfer scratch images into the texture-format partial.
This move changes code ownership only.
Runtime behavior stays the same.
* refactor(videoout): place guest image diagnostics code with its owner
Move guest image tracing and readback diagnostics beside the guest image registry.
The new location makes ownership clear.
Execution behavior does not change.
* refactor(videoout): place guest submission execution code with its owner
Move ordered actions, cache operations, pooled-data returns, and guest-work descriptions beside guest submission lifetime.
This change only improves code organization.
Keep runtime behavior unchanged.
* refactor(agc): isolate initialization exports code
Move AGC initialization and versioned register-default materialization into a focused partial.
This move changes code ownership only.
Runtime behavior stays the same.
* refactor(agc): isolate driver resource exports code
Move AGC driver resource-registration exports and their private machinery into a focused partial.
The new location makes ownership clear.
Execution behavior does not change.
* refactor(agc): isolate event exports code
Move AGC event signaling exports and their private machinery into a focused partial.
This change only improves code organization.
Keep runtime behavior unchanged.
* refactor(agc): isolate shader state exports code
Move AGC shader-pipeline state exports and their private machinery into a focused partial.
This move changes code ownership only.
Runtime behavior stays the same.
* refactor(agc): isolate packet patching exports code
Move AGC command-packet patching exports and their private machinery into a focused partial.
The new location makes ownership clear.
Execution behavior does not change.
* refactor(agc): isolate control flow exports code
Move AGC command-buffer control-flow exports into a focused partial.
This change only improves code organization.
Keep runtime behavior unchanged.
* refactor(agc): isolate register packet exports code
Move AGC register-programming packet exports and their private machinery into a focused partial.
This move changes code ownership only.
Runtime behavior stays the same.
* refactor(agc): isolate draw packet exports code
Move AGC draw-command packet exports and their private machinery into a focused partial.
The new location makes ownership clear.
Execution behavior does not change.
* refactor(agc): isolate DMA packet exports code
Move AGC DMA packet exports into a focused partial.
This change only improves code organization.
Keep runtime behavior unchanged.
* refactor(agc): isolate compute fast paths code
Move CPU semantic replacements for recognized compute kernels into a focused partial.
This move changes code ownership only.
Runtime behavior stays the same.
* refactor(agc): isolate compute dispatch code
Move translated compute dispatch handling into a focused partial.
The new location makes ownership clear.
Execution behavior does not change.
* refactor(agc): isolate draw translation code
Move translated guest draw construction into a focused partial.
This change only improves code organization.
Keep runtime behavior unchanged.
* refactor(agc): isolate draw state code
Move AGC render-state decoding into a focused partial.
This move changes code ownership only.
Runtime behavior stays the same.
* refactor(agc): isolate submitted geometry code
Move submitted index and vertex retention into a focused partial.
The new location makes ownership clear.
Execution behavior does not change.
* refactor(agc): isolate texture transport code
Move guest texture transport into a focused partial.
This change only improves code organization.
Keep runtime behavior unchanged.
* refactor(agc): isolate PM4 decoding code
Move submitted PM4 stream decoding into a focused partial.
This move changes code ownership only.
Runtime behavior stays the same.
* refactor(agc): isolate GPU wait lifecycle code
Move submitted GPU wait suspension and resumption into a focused partial.
The new location makes ownership clear.
Execution behavior does not change.
* refactor(agc): isolate orphan preamble recovery code
Move orphan-preamble recovery into a focused partial.
This change only improves code organization.
Keep runtime behavior unchanged.
* refactor(agc): isolate submission runtime code
Move submitted command execution into a focused partial.
This move changes code ownership only.
Runtime behavior stays the same.
* refactor(agc): isolate draw diagnostics code
Move translated-draw diagnostics into a focused partial.
The new location makes ownership clear.
Execution behavior does not change.
* refactor(agc): isolate command packet code
Move general command-buffer packet builders into a focused partial.
This change only improves code organization.
Keep runtime behavior unchanged.
* refactor(agc): place multi-DCB export in submissions
Place the multi-DCB submission handler beside the queue parser and submission lifecycle.
This move changes code ownership only.
Runtime behavior stays the same.
* refactor(agc): place driver configuration exports with its owner
Place tessellation driver configuration handlers beside the other driver resources.
The new location makes ownership clear.
Execution behavior does not change.
* refactor(agc): place software present path in events
Place software presentation beside the AGC flip and event handlers.
This change only improves code organization.
Keep runtime behavior unchanged.
* refactor(agc): place color-control decode in draw state
Place the CB color-mode and hardware-resolve helpers with the other draw-state code.
This move changes code ownership only.
Runtime behavior stays the same.
* refactor(agc): place translated draw buffers in draw translation
Place the translated draw buffer builders with the draw translation pipeline.
The new location makes ownership clear.
Execution behavior does not change.
* refactor(agc): place wait packet helpers in command packets
Place WAIT_REG_MEM encoders and command-packet trace counters with their only consumers.
This change only improves code organization.
Keep runtime behavior unchanged.
* refactor(agc): place draw translation helpers with its owner
Place pixel-input translation helpers and their trace state with the draw translation pipeline.
This move changes code ownership only.
Runtime behavior stays the same.
* refactor(agc): place frame packet trace setting in pm4
Place the frame-packet trace setting with its PM4 consumers.
The new location makes ownership clear.
Execution behavior does not change.
* refactor(agc): place guest memory transfer helpers in submissions
Place guest memory copy and fill helpers with their submitted-command consumers.
This change only improves code organization.
Keep runtime behavior unchanged.
* refactor(agc): place packet identity helper in packet patching
Place packet identity decoding with its packet-patching consumers.
This move changes code ownership only.
Runtime behavior stays the same.
* refactor(agc): place duplicate target trace state in draw state
Place the duplicate-target trace counter with its draw-state consumer.
The new location makes ownership clear.
Execution behavior does not change.
* style(renderer): remove extra spacing from split files
Remove excess blank lines left by the AGC and Vulkan partial extractions.
This cleanup changes formatting only.
Runtime behavior stays the same.
* refactor(agc): keep rebased state in the split renderer
Keep color-metadata state with draw translation after the file split. Restore the complete PM4 helper set. Retain frame publication and metadata tracking that arrived during the rebase.
* fix(gpu): keep acquire memory cache ranges
Keep acquire memory operations separate when their ranges or cache semantics differ.
Merge only matching operations that overlap or touch.
Submit the exact operations as one backend work item to limit queue growth without widening invalidation ranges.
Add focused tests for the changed behavior.
* fix(vulkan): scope guest cache barriers to tracked resources
Use exact buffer and depth-image ranges for ranged guest cache operations. Use a global barrier when the range or image layout is unknown, when no resource matches, or when the operation covers all memory. Record cached texture spans and test each fallback.
* fix(cpu): handle guest image write faults in native code
Keep tracked-page write faults inside the Windows VEH path.
Restore page access, queue dirty pages, and defer managed tracker work until safe execution.
This prevents CLR fail-fast crashes and avoids full tracked-page scans.
Add focused tests for the changed behavior.
* fix(agc): track constant fills as managed writes
Call the managed-writer tracker after the constant-fill fast path writes guest memory. Do not protect a range that the managed memory layer already reports. This prevents an immediate write fault on the same pages.
* fix(cpu): make exception memory reads safe
Probe diagnostic addresses before reading them.
Use ReadProcessMemory for unstable data and keep direct reads only for executable bytes that are active during dispatch.
This prevents the crash reporter from causing a second access violation while it reports a guest fault.
Add focused tests for the changed behavior.
* fix(loader): protect the guest red zone on Windows
Patch faultable memory instructions in System V red-zone functions to run through nearby trampolines.
The trampoline moves RSP while Windows handles a write fault, so the handler cannot overwrite live guest values below the stack pointer.
Add focused tests for the changed behavior.
* fix(audio): correct AJM job argument and size handling
Read the gapless result pointer from the fifth ABI argument and keep reset as the fourth argument.
Use the split-job base size before adding buffer descriptors.
This keeps batch cursors and result writes aligned with AJM.
Add focused tests for the changed behavior.
* fix(vulkan): use all attachments for framebuffer identity
Clamp the framebuffer extent to every color and depth attachment.
Include the color format and view in the depth-framebuffer cache key.
This prevents invalid reuse when one guest image changes attachment views or formats.
Add focused tests for the changed behavior.
* fix(videoout): align captures with complete guest frames
Start a capture at a guest-frame boundary and keep it active across presenter ticks.
End the capture after the next guest flip completes.
Discard interrupted or timed-out captures so later requests can continue.
* fix(shader): keep forward branches after program end markers
Allow a forward scalar branch to target reachable code after S_ENDPGM.
Preserve instruction boundaries and reaching definitions across the program-end marker.
Add a regression test for this control-flow form.
* fix(shader): apply fragment export validity
Apply the EXP.VM flag when a fragment export is conditionally valid.
Suppress invalid color and depth exports in both shader backends.
Add translation and output-mask tests.
* fix(vulkan): declare graphics subgroup operations
Enable subgroup capabilities only for graphics modules that use subgroup instructions.
Declare the required SPIR-V capabilities and execution modes.
Add interface validation tests for vertex and fragment stages.
* fix(vulkan): use flat fragment subgroup inputs
Decorate integer fragment subgroup built-ins with Flat so generated SPIR-V satisfies Vulkan interface validation.
Add focused tests for the changed behavior.
* fix(storage): return usable storage capacity
Add the temporary-data capacity export and saved-data search alias, then report total and free blocks using the platform ABI.
Add focused tests for the changed behavior.
* fix(videoout): send capture requests to guest-frame control
Send the capture hotkey request to the guest-frame capture state machine.
Do not start a host-present capture directly from the window event.
This keeps one request aligned with one complete guest frame.
* fix(videoout): apply metadata-backed image clear patterns
Track metadata fills and constant-buffer fills with their full 16-byte pattern and physical image size.
Decode clear values by target format, including repeated half-float texels.
Apply pending clears when the guest image becomes available.
Add tests for tiled allocation sizes, metadata state, and clear patterns.
* chore(videoout): trace selected image synchronization paths
Add address-filtered traces for compute destinations, guest-image overlaps, and sampled-image binds.
Keep the traces off unless the related environment option is set.
Use the output to compare image synchronization without broad logging.
* fix(vulkan): require a compatible wave size for graphics subgroups
Enable graphics subgroup lowering only when the device reports a compatible native wave size.
Pass the selected policy into shader translation and shader cache keys.
Add policy and interface tests.
* fix(agc): preserve composite depth target extents
Decode attachment width and height from composite depth binding packets and carry them separately from independent context-register state.
Detach irreconcilably mismatched depth attachments instead of shrinking color passes, with focused coverage for packet parsing and fallback behavior.
* fix(cpu): preserve full-width guest callback results
Return the complete 64-bit value from direct and worker-backed guest calls instead of truncating it to 32 bits.
Add fourth-argument support for guest callbacks and cover both ABI behaviors in the native return smoke test.
* fix(shader): key translated programs by checksum
Include programmed shader checksums in decode and compiled-pipeline cache identities so replaced code at a reused guest address cannot resolve to stale translations.
Propagate checksums through compute, graphics, and submitted-geometry translation and cover address reuse with a regression test.
* fix(agc): skip rectangle draws without parameter exports
Track enabled shader parameter exports and reject only non-indexed rectangle draws that have no vertex inputs or parameter exports but still request interpolated pixel inputs.
Treat the rejection as intentional and cover the guarded and preserved draw shapes with focused tests.
* diagnostics(agc): trace video draw chains
Track decoded video planes through recent render-target dependencies and report texture payload summaries, generations, blend state, and viewport data for targeted analysis.
Keep the trace flag, rectangle-skip visibility, and flip-bounded diagnostic state together in this commit.
* fix(agc): snapshot rotating video buffers per draw
Recognize registered decoded-video buffer addresses independently of tracing and bypass address-only upload and texture-content cache shortcuts for those ranges.
This preserves the texels associated with queued draws when the decoder later reuses a rotating guest buffer.
* fix(video): queue decoded frames and honor seek state
Start video decoding with playback, buffer decoded frames on a bounded worker queue, and distinguish pending data from end of stream instead of blocking guest polling.
Preserve pause state across jumps, seek audio and video decoders to the requested timestamp, offset reported times, and deliver the completion warning through the event callback ABI.
* fix(video): load replacement-backed sources through callbacks
Read replacement-backed sources through the guest open, size, offset-read, and close callbacks, materializing a temporary host file for decoding.
Fall back to the ordinary resolved path when the optional replacement interface declines a source, and clean up owned temporary files when sources or players are released.
* fix(video): report decoded stream metadata
Preserve display aspect ratio, color range, primaries, transfer characteristics, frame pitch, and crop information when reporting decoded video streams and frames.
Use the stream's fallback frame rate when its average rate is unavailable, and cover the extended ABI field layout with focused tests.
* fix(video): report extended audio stream format
Populate channel count and sample rate in extended stream details so clients configure decoded PCM output correctly. Add ABI coverage for the audio union layout and duration.
* fix(shader): preserve full LDS single-address offsets
Combine both encoded offset bytes for single-address data-share operations while retaining independent scaled offsets for paired forms. Apply the correction to both shader backends and add decoder and generated-module regression coverage.
* fix(shader): preserve depth comparison filter semantics
Decode the sampler comparison function instead of assuming one operation, and evaluate linear LOD-zero depth filtering per texel before interpolation. Add focused generated-module coverage for comparison functions and the four-tap filtering path.
* fix(agc): create native interpolant mappings
Implement dbOlWdppb4o as sceAgcCreateInterpolantMapping2 and emit packed SPI_PS_INPUT_CNTL register pairs from matched shader semantics. Fill unused entries with identity mappings and cover identity, matched, default, flat, custom, and f16 modes.
* fix(agc): construct primitive state registers
Build context and user-config primitive state independently, merge optional hull-stage state, and select the correct output primitive source. Cover hull handling and derived versus shader-owned output primitives.
* fix(agc): emit native indirect register packets
Emit and parse native indirect context, shader, and user-config register packets. Patch native address and count fields without clobbering control bits, retain legacy packet compatibility, and apply flagged depth-size entries to the correct state.
* fix(agc,video): support guest depth-stencil state
Decode guest stencil attachment, clear, mask, reference, compare, and operation state. Create compatible Vulkan depth-stencil resources and preserve their aspects through render passes, pipeline caching, feedback copies, and barriers.
* chore(diagnostics): add focused guest graphics tracing
Add canonical draw-state summaries with optional descriptor detail filters. Improve guest instruction sampling with thread selection and per-window accounting, and trace register-default handoff provenance.
* fix(video): publish cache identity for depth aliases
Reuse the exact sampled-texture content identity when a depth-backed image resolves successfully. Publish its sampler and write generation so later submissions avoid redundant guest-memory snapshots that the presenter would discard.
* chore(diagnostics): profile guest thread execution
* chore(diagnostics): profile graphics command processing
* chore(diagnostics): profile texture preparation
* chore(diagnostics): profile shader evaluation
* chore(diagnostics): profile address synchronization
* chore(diagnostics): unify performance profiling switches
* fix(video): disable face culling for rectangle primitives
Keep expanded rectangle draws visible regardless of triangle winding. Preserve culling and all other raster settings for other primitive types.
Add regression coverage for each culling combination and unchanged non-rectangle raster state.
* fix(agc): keep depth clears separate from color targets
Remove the color-clear request triggered by the depth-clear control bit. Keep the existing depth, stencil, and color-metadata clear paths unchanged.
* fix(shader): honor pixel input counts and mapped locations
Read only the programmed pixel input controls and use identity mappings beyond that range. Share duplicate-location resolution between both shader backends.
Test input-count bounds, zero-input state, identity fallback, duplicate mappings, and location limits.
* chore(diagnostics): trace interpolant packet and draw mappings
Add opt-in packet fingerprints, register pairs, and consumed draw mappings through SHARPEMU_TRACE_AGC_INTERPOLANT_FLOW.
Limit packet output and unique draw records to 1024 entries. Stop packet scans after the trace limit and keep diagnostic state bounded.
* feat(memory): add GPU memory tracking foundations
Track mapped ranges and page access state.
Keep unresolved faults on the existing error path.
Report memory counters once when shutdown starts.
* feat(memory): map direct memory through shared backing
Create portable host views over a persistent guest backing object.
Route direct and flexible mappings, protection, queries, unmaps, and aliases through one owner.
Preserve private-memory behavior and cover lifecycle, overlap, and failure paths.
* feat(gpu): schedule submissions with timeline completion
Route graphics and compute work through one queue and scheduler. Replace per-submit fences with timeline ticks and preserve batch accounting on every submission path.
Add synchronous worker relay, ordered completion callbacks, and shutdown tests. Keep optional video frame polling nonblocking to prevent a relay deadlock.
* fix(gui): drain pending logs before closing the file
Save all queued output at shutdown without the display batch limit.
Keep console output when file logging is disabled. Add tests for complete and batched drains.
* fix(cpu): bypass managed handlers for debug output
* fix(vulkan): correct image barriers and release texture resources
Include attachment reads and select the source stage for presentation uploads.
Release cached textures at shutdown and retain ownership of sampled image views.
* fix(kernel): service guest exceptions during mutex waits
Deliver pending exceptions outside the mutex lock and resume the wait.
Test delivery without granting ownership and isolate the shared scheduler state.
* feat(gpu): replace buffer snapshots with a tracked buffer cache
Keep guest buffers in device memory and upload changed ranges on use.
Recover CPU access through GPU downloads and preserve guest page permissions.
Manage buffer lifetime through submission completion and remove the old writeback path.
Add buffer, tracking, fault recovery, and descriptor synchronization tests.
* fix(vulkan): use cached buffers for GPU-written geometry
Read current vertex and index data from the buffer cache instead of stale CPU snapshots.
Preserve buffer offsets and complete allocation merges before resolving draw handles.
Add tests for index routing, vertex bindings, and overlapping geometry allocations.
* feat(diagnostics): trace GPU submissions and guest memory reads
Keep a bounded submission history and report memory mappings when buffer reads fail.
Trace scalar loads for the selected shader and reset operation details for each recording.
Add tests for submission history and memory-range reports.
* feat(gpu): replace image snapshots with a tracked image cache
* fix(shaders): bound scalar load memory ranges
* fix(shaders): preserve image bindings across branch paths
* fix(videoout): use the active presentation target
* feat(diagnostics): report image cache and presenter costs
* perf(agc): skip unused context updates during geometry capture
* perf(gpu): skip image uploads before full color clears
* perf(gpu): wake the presenter when submissions complete
* perf(agc): reuse retained vertex data before capture
* feat(diagnostics): report image upload preparation costs
* feat(diagnostics): report vertex snapshot and prepass costs
* feat(gpu): replace command processing and label handling
* test(gpu): use asynchronous waits in the image lock test
* fix(memory): finish GPU work before taking mapping locks
* perf(memory): avoid temporary segments for single-mapping reads
* perf(shader): cache scalar analysis query results
* perf(shader): cache scalar load bounds and batch component reads
* feat(diagnostics): report draw and scalar evaluation costs
* fix(videoout): widen the performance overlay and align it right
* test(videoout): isolate flip state and limit port cleanup
* refactor: remove unused fields and unreachable branches
* fix(kernel): use a private lock for event waiter completion
* feat(gpu): replace draw and compute translation
* fix(memory): select the first available guest address
* fix(gpu): invalidate tracked memory across partial ranges
* perf(gpu): stream repeatedly written buffer pages
* fix(shader): preserve image array coordinates
* fix(shader): order shared memory access across wave lanes
* perf(gpu): cache decoded shader instruction metadata
* fix(gpu): preserve command failures when error logging fails
* feat(profiling): add detailed render and submission timings
* feat(profiling): add optional presentation frame history
* fix(gpu): use storage images for stencil writes
* fix(gpu): support compressed image sampling on macOS
* fix(gpu): check graphics subgroup stage support
* fix(cpu): preserve pending waits during signal delivery
* fix(memory): avoid reserved graphics addresses on macOS
* fix(cpu): preserve vector registers during macOS signal recovery
* fix(loader): protect guest memory accesses on macOS
* fix(avplayer): preserve the playback clock during video catch-up
* fix(kernel): write the complete local time conversion result
* feat(http): parse URI components into guest memory
* feat(profiling): separate GPU wait and submission cleanup time
* feat(profiling): add GPU command completion intervals
* fix(profiling): use the configured shader dump directory
* feat(diagnostics): add Windows crash dump capture
* fix(gui): save console placement and hide it on Options
* fix(renderdoc): use F12 for capture and show the shortcut
* feat(gui): add crash dump controls and enable app0 writes by default
* feat(gui): add overlay modes, corner controls, and GPU usage
* fix(cpu): remove unsafe SSE4a instruction rewrites
* fix(cpu): preserve call width and registers in TLS load patches
* fix(loader): copy TLS initialization bytes after relocation
* fix(memory): extend retained reservations through free ranges
* fix(memory): retry faults after backing view restoration
* perf(memory): use binary search for mapped ranges
* perf(memory): track direct allocations and free ranges
* perf(gpu): reuse completed drains when no work is pending
* fix(gpu): identify image upload failures
* feat(diagnostics): profile guest memory operations
* feat(gui): add performance profiling options
* fix(cpu): allow concurrent native fault callbacks
* fix(cpu): respect instruction boundaries in TLS patches
* fix(cli): preserve the assembly path during relaunch
* fix(cpu): recognize time reads in import loop checks
* fix(diagnostics): capture native thread state safely
* fix(agc): validate shader headers and direct register exports
* fix(gpu): apply bounded embedded vertex offsets
* test(shader): verify unsigned difference instructions
* fix(shader): separate global memory source and destination registers
* fix(debugger): restrict frontend requests and validate tokens
* feat(gui): add the community link
* fix(shader): preserve buffer descriptors across branches
Keep incoming register values for alternate resource paths. Resolve each vertex fetch once and preserve shared bindings. Add branch regression tests and a gated invalid-address trace.
* fix(diagnostics): handle Windows debugger breaks during crash capture
Identify debugger breaks from the system module and thread entry. Resume only verified first-chance breaks and preserve application exception handling. Add regression tests for repeated breaks and crash dumps.
* fix(memory): recover late writes after page protection is removed
Track write-access restoration for each watched page. Retry a late write only when the mapping and host permissions still allow it. Reject repeated faults and stale mapping evidence. Add page-watch and native fault regression tests.
* feat(gpu): replace shader and pipeline caches
Resolve shader resources with a cached value graph. Share binding layouts between Vulkan and Metal. Keep descriptors until each GPU submission completes. Add shader access to device addresses and global data share memory. Correct typed buffer formats. Remove the replaced code paths.
* fix(shader): add signed 24-bit multiplication
Decode signed 24-bit multiplication in both instruction formats. Sign-extend the low 24 bits of each input. Store the low 32 bits of the product. Preserve the destination when its lane is inactive. Add Vulkan and Metal translation support and regression tests.
* fix(shader): add signed 16-bit packing
Decode signed 16-bit packing in the extended instruction format. Clamp each signed input to the 16-bit range and pack both results into one register. Add Vulkan and Metal translation support. Preserve inactive destinations and add regression tests.
* fix(shader): add 64-bit shared-memory reads
Decode single and paired 64-bit reads. Load local or global shared memory with the required offsets. Preserve the source address when destination registers overlap it. Keep inactive destination registers unchanged. Add Vulkan and Metal translation support and regression tests.
* fix(shader): add paired 64-bit shared-memory writes
Decode both paired-write forms. Write independent source-register pairs to local or global shared memory. Apply eight-byte or 512-byte offset scales. Use only the first source pair when offsets are equal. Preserve memory for inactive lanes and add regression tests.
* fix(shader): add thread-indexed local-memory writes
Decode the thread-indexed write instruction. Form each address from the low 16 bits of M0, the byte offset, and four times the lane index. Preserve memory for inactive lanes. Enable the required Vulkan lane input and add regression tests for both shader backends.
* fix(shader): handle traps without an installed handler
Decode the shader trap instruction and retain its eight-bit trap ID. Treat the instruction as a no-op under the current no-handler model on Vulkan and Metal. Preserve shader state and continue execution. Keep unknown-opcode checks and add regression tests.
* perf(gpu): avoid allocations in small image queries
Store up to 16 image identifiers inline and use overflow storage for larger queries. Replace the capturing owner callback with an indexed loop. Preserve query order, duplicate filtering, and snapshots during image removal. Add allocation and query regression tests.
* perf(avplayer): reuse private video frame buffers
Recycle a delivered frame only after a new frame is available. Keep reusable storage separate for each playback and drain queued frames before reporting completion. Reuse the conversion buffer after its pending bytes are consumed. Add frame-lifetime and storage-reuse tests.
* perf(agc): copy register packet payloads in bulk
Copy separate register ranges through the guest memory interface. Keep forward word copies for overlapping ranges or when bulk copying fails. Leave null-source payloads unchanged for the caller to fill.
* perf(memory): avoid segment lists for single-mapping transfers
Write and copy through the backing alias when each range fits one mapping. Preserve range checks, locking, and overlap handling. Keep the existing path for transfers across mappings.
* perf(memory): allow independent protection updates
Use address-range locks for transient protection updates. Keep mapping changes and disposal exclusive. Preserve lock order, release locks on failure, and reject protection requests after disposal.
* perf(cpu): wake ready threads with queue notifications
Replace ready-thread polling with notifications under the scheduler lock. Notify the dispatcher when work is queued, an executor is released, or shutdown starts. Finish slice accounting before releasing executor ownership.
* fix(videoout): validate output settings and report refresh codes
Use refresh-rate codes in output status. Check configuration arguments and reject unavailable output modes. Preserve validation errors and output buffer boundaries.
* fix(videoout): share vblank counts across waits and events
Use one display clock per port for vblank status, waits, and events. Report counters for the latest vblank boundary. Wake the event worker when subscriptions change or shutdown starts.
* fix(videoout): apply per-port flip mode timing
Use each port refresh rate to pace flips. Apply window margins and keep ASAP flips unrestricted. Let multiple-flip requests share the next vblank boundary after readiness without applying the flip rate. Cancel CPU waits when the port closes.
* test(agc): cover register copies across backing views
Verify register payload bytes, the returned packet address, and the command-buffer cursor when the source and destination span separate backing views.
* feat(diagnostics): report slow window event polls
Record poll totals and the four longest calls per report interval. Require frame tracing and an active render scope. Preserve event handling and omit event types when no event is returned.
* feat(diagnostics): profile register packet writes
Measure import dispatch, packet setup, and payload copies. Count copy paths and emitter results. Require performance profiling and frame tracing, and report cumulative inclusive times.
* feat(diagnostics): profile shader resource resolution
Measure resource resolution phases, managed allocations, and source activity. Require performance profiling and frame tracing. Report cumulative inclusive times with invariant decimal formatting.
* feat(diagnostics): save shader planning inputs
Save decoded instructions and resource graphs before descriptor validation. Record planning failures under the existing shader dump switch and address filter. Report file errors without replacing the shader rejection.
* feat(diagnostics): save shader variant inputs and modules
Save cache comparisons before translation and pair each variant with its compiled module. Capture indirect image failures under the existing shader dump controls. Create failure callbacks only when the dump gate matches.
* perf(memory): avoid copies in dirty-page range checks
Test intersecting page-mask words directly instead of creating a masked copy. Preserve half-open range bounds and existing region validation.
* feat(diagnostics): profile buffer uploads
Report buffer upload ranges, byte counts, and slow synchronization calls under frame tracing. Associate device-address uploads with shader program hashes. Read hot-page byte counts under the region lock. Keep counters bounded and report decimals in a fixed format.
* feat(diagnostics): trace guest thread handoffs
Record bounded thread transitions, mutex handoffs, and semaphore signals under frame tracing. Preserve thread identities and wake results. Reset traces at session start and write them at shutdown. Keep trace output failures from interrupting native cleanup.
* feat(diagnostics): trace command submission flow
Record bounded submission, queue, and presenter wait events under frame tracing. Correlate records by queue and submission identifiers. Reset the trace at session start and close it at shutdown. Keep trace output failures from interrupting cleanup.
* feat(diagnostics): profile memory faults and protection
Record fault, protection, and lock timings under frame tracing. Separate tracker and image-cache activity. Prepare thread-local state before guest execution. Report cumulative measurements without changing memory protection or lock ownership.
* feat(gui): expose strict shader mode and enable it by default
Add the strict compute switch to global and per-game debug options. Stop on shader rejections unless skip mode is explicitly selected. Preserve disabled overrides and apply the saved choice at launch. Document the default and skip behavior.
* fix(shader): implement unsigned vector xor-add
Decode V_XAD_U32 and apply XOR followed by wrapping addition in Vulkan, Metal, and resource evaluation. Add tests for literal sources, overlapping destinations, and inactive execution lanes.
* fix(gpu): replace incompatible image backing types
Replace single-row image backings when a view requires a different dimensional type. Preserve cached subresources, current contents, and deferred release. Copy between 1D and 2D images through a GPU buffer. Add cache and binding regression tests.
* fix(shader): add thread-indexed local-memory reads
Decode the thread-indexed read instruction. Form each address from the low 16 bits of M0, the byte offset, and four times the lane index. Preserve inactive destination registers. Add Vulkan and Metal translation support and regression tests.
* test(stdio): synchronize concurrent file operations
Run blocking file operations on dedicated threads. Check that close and seek block before releasing the read. Join workers before cleanup and identify failed synchronization checkpoints.
* fix(cpu): read Linux diagnostic memory safely
Use process_vm_readv to read host memory outside the allocation tracker. Reject incomplete reads and test access across a protected page.
* fix(cpu): defer native callback allocation
Create the import callback thunk when it is first needed. Reject native execution outside an x64 process and declare that requirement in backend tests.
* fix(memory): use address-space protection granularity
Keep the native page-size requirement and let test address spaces declare their granularity. Use one host-memory backend for tracker allocation and queries. Keep the test fatal handler active through cleanup.
* test(cpu): isolate POSIX signal recovery state
Prevent signal recovery tests from overlapping other test collections. Restore the previous backend after each test.
* test(metal): resolve fixture buffer descriptors
Materialize buffer descriptors before compiling compute fixtures. Check the resolved formats and update the matching shader snapshots without changing expected device results.
* test(memory): require supported native page protection
Declare the 4 KiB host page requirement for image tracking and backing-view fault tests. Skip these tests on unsupported hosts without changing production checks.
* fix(memory): isolate write-tracker warm-up pages
Use a dedicated host mapping for protection warm-up. Release the mapping after removing its tracker entry. Check native page permissions in the tracker tests.
* fix(memory): track POSIX backing-view query state
Share view mapping and protection records across host memory queries. Preserve committed state for inaccessible pages and round partial-page changes to host pages. Update protection records without allocating during faults.
* test(memory): use valid portable guest ranges
Probe free addresses within the guest range instead of relying on fixed or host-selected addresses. Respect guest and host alignment. Allocate output storage independently of fixed guest image addresses.
* fix(memory): preserve execute permissions during writes
Request writable protection without execute permission for non-executable private memory. Add a regression check that rejects unnecessary executable protection. Restore the original protection after the write.
* fix(cpu): resolve Linux TLS through libpthread
Load Linux thread-local storage functions from libpthread.so.0. Resolve the native TLS lookup from the same library to support glibc versions before the libc merge. Keep macOS imports unchanged.
* fix(kernel): connect sockets without thread-pool waits
Use synchronous TCP connection handling for the blocking guest call. Remove the fixed task wait that could reject connections under thread-pool load. Add tests for occupied workers, refused connections, and descriptor lifetime.
* fix(gpu): prepare device memory and guard inactive loads
Keep active buffers resident and upload read-only device-address ranges before use. Preserve pending faults when the output buffer is full. Prevent inactive lanes from loading global memory.
* feat(gpu): trace device-address memory faults
Select the first fault page when memory tracing is set to auto. Record the shader, instruction, and address for a fault. Trace buffer registration, preparation, and collection for the selected page.
* feat(gpu): trace buffer readback callers
Record buffer readback callers, ranges, byte counts, and elapsed time. Bound the collected ranges and report calls beyond the limit. Enable these details only when both performance profile switches are set. Add tests for caller separation, report gates, and counter limits.
* perf(kernel): skip occupied ranges during address search
Check kernel reservations before asking the backing store to reserve an address. Keep aligned gaps and reusable reservation hints. Add tests for occupied ranges, alignment, and address overflow.
* fix(gpu): correct shader resource selection and data sharing
Support data-share swizzles on Vulkan and Metal. Bound indirect resource selectors from wave-mask inputs. Resolve sampled image variants with their resource types and include selector state in program identity. Add compiler and device regression tests.
* refactor(gpu): centralize guest buffer ownership and lifetime
Track buffer identity, page ownership, recency, and retirement in one registry. Separate upload preparation, download planning, merge bounds, and collection policy. Preserve GPU synchronization and add registry and device regression tests.
* fix(shader): support unsigned 16-bit packing
Clamp each unsigned input to 16 bits. Pack the first input into the low half and the second input into the high half on Vulkan, Metal, and the resource evaluator.
* fix(memory): reject overlapping fixed allocations
Reject committed pages when an exact allocation shares a host reservation. Keep adjacent allocations valid and preserve existing memory regions.
* fix(kernel): implement AIO waits and correct array arguments
Return recorded completion states for single-request waits. Use the correct argument order for array waits and polls. Validate request IDs and guest memory accesses.
* fix(shader): implement scalar signed absolute value
Decode S_ABS_I32 and emit it on Vulkan and Metal. Preserve the minimum signed value and update the scalar condition flag. Match these results in resource evaluation.
* fix(shader): implement three-input vector XOR
Decode V_XOR3_B32 and combine all three inputs on Vulkan and Metal. Apply the same operation during resource evaluation.
* fix(shader): implement scalar bit clear
Decode S_BITSET0_B32 and clear the selected destination bit on Vulkan and Metal. Preserve the condition flag and retain the destination input for scalar bit updates.
* fix(gpu): keep depth export format from enabling pixel shaders
Use shader control flags to detect depth and coverage effects. Do not activate a pixel shader for a depth-only draw because an export format remains set.
* feat(runtime): trace failed imported data writes
Report whether a failed relocation target is readable and list its tracked memory regions. Keep the details under the existing data-rebind logging switch.
* feat(kernel): trace guest-tracked mutex self-locks
Include adaptive mutex self-lock failures in the existing fast-path trace. Keep the lock behavior and return codes unchanged.
* feat(savedata): trace failed mount requests
Report the directory and mount mode when a save mount is missing or busy. Use the existing save-data logging switch.
* feat(gpu): trace pixel shader draw inputs
Include the pixel shader address, hash, and user data in the existing AGC trace.
* feat(gpu): trace pixel register writes
Log pixel shader register writes when AGC tracing is enabled. Include the packet or table address and the shader address.
* feat(gpu): report sources of invalid shader resources
Include shader and descriptor details when a buffer starts in unmapped memory or a sampler has an invalid anisotropy ratio. Keep the existing validation checks.
* fix(kernel): correct AMPR event filter identifiers
Use the required filter identifiers for AMPR and AMPR system events. Add a regression test for both values.
* fix(gui): add missing Azerbaijani option text
Add labels and descriptions for crash dumps, performance profiling, strict shader checks, and the performance display.