Commit Graph

6138 Commits

Author SHA1 Message Date
Adrià Franch 50256b0ac4 fix(shader): VOP3 op_sel on v_alignbit/alignbyte_b32 and 16-bit max3/min3/med3
op_sel[1:0] on v_alignbit_b32/v_alignbyte_b32 selects the byte of src2 that
holds the shift, op_sel[2] is ignored and op_sel[3] writes the low 16 result
bits to the high half with the low half zeroed (measured on gfx1035).
v_alignbyte_b32 now uses src2[1:0] only, as the ISA says; values >= 4 gave
a wrong result before. v_max3/min3_{i16,u16} and v_med3_u16 take op_sel like
v_med3_i16 already does.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-04 12:41:07 +02:00
Adrià Franch 89eb938803 fix(shader): VOP3P integer clamp on v_pk_mad/add/sub_{i16,u16}
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-04 12:33:38 +02:00
Michon 34c57ca65d feat(agc): render into one depth slice of a 3D color target
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-04 12:24:09 +02:00
Michon d7d7c142f4 feat(agc): 4 KiB standard color targets and color views of one array slice
Color targets with the SW_4KB_S swizzle (CB_COLOR_ATTRIB3 tile mode 5) are laid out in 4 KiB
blocks with the standard element order the texture detiler uses, and are resident through the
same storage images as 64 KiB targets. A CB_COLOR_VIEW selecting one slice (SLICE_START equal to
SLICE_MAX) renders into that slice: the target starts one slice size per slice after the surface,
the slice size being the surface's mip chain as a texture of the same tile mode lays it out. Views
of several slices are still rejected.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-04 12:24:05 +02:00
SP4C3B4R-8 48ebf3195e fix(libSceAgcDriver): skip depth and stencil tests whose plane is absent
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 12:17:38 +02:00
Adrià Franch 1233dd815d fix(shader): clamp f32 results to +0 for NaN and -0
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-04 12:16:07 +02:00
Adrià Franch 1a5507698b fix(shader): choose v_div_scale_f32/v_div_fmas_f32 NaNs on the bits and refuse s_denorm_mode
v_div_scale_f32 returns a NaN source unchanged instead of scaling it,
and v_div_fmas_f32 quiets the first NaN input or returns the default
NaN 0xffc00000, so results no longer depend on the driver's NaNs. The
test compares NaN results bit for bit. s_denorm_mode throws: the
recompiler does not pin a denormal mode, so no value matches what it
models.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-04 12:05:33 +02:00
Adrià Franch 11bc279945 feat(shader): implement v_div_scale_f32, v_div_fmas_f32, v_div_fixup_f32 and s_denorm_mode
These make up the precise f32 division macro compilers emit for a / b. No
reference emulator implements them and the ISA pseudocode disagrees with the
hardware, so the rules were measured on an RDNA2 GPU:
- div_scale decides from the exponent fields only: 2^64 or 2^-64 scaling
  and VCC for differences >= 96 or <= -96, a denormal or huge (>= 253)
  denominator and a numerator exponent <= 24; a zero input gives 0xffc00000.
- div_fmas scales by 2^64 when the exponent of S2 is >= 128, else 2^-64,
  before the single rounding (the ISA says a fixed 2^32).
- div_fixup follows the ISA, and a NaN quotient with finite inputs becomes
  +-inf.
s_denorm_mode is accepted and ignored; listed in TechnicalDebt.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-04 12:05:33 +02:00
Adrià Franch 7f291b7658 feat(shader): implement the float and 64-bit inc/dec buffer atomics
buffer_atomic_fcmpswap, fcmpswap_x2, fmin_x2, fmax_x2, inc_x2 and
dec_x2 are compare-and-swap loops on the dword or qword. fcmpswap
compares as floats (-0 equals +0, NaN equals nothing) and fmin/fmax are
IEEE minNum/maxNum with -0 < +0, a quieted result for a signalling NaN
and the other operand for a quiet one, all with integer ops so the
driver's float controls do not matter. The existing 32-bit fmin/fmax
kept the old value whenever a NaN or two zeros were involved; it now
uses the same rule.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-04 12:01:15 +02:00
Adrià Franch ae36aafcb2 feat(shader): implement buffer_atomic_inc/dec and the 64-bit integer buffer atomics
buffer_atomic_inc, buffer_atomic_dec, buffer_atomic_cmpswap_x2 and
buffer_atomic_{add,sub,smin,umin,smax,umax,and,xor}_x2 threw in the decoder.
The 64-bit ones use native 64-bit SPIR-V atomics like the existing
swap_x2/or_x2; inc and dec wrap at the data operand through the existing
compare-exchange helpers.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-04 11:59:54 +02:00
Dean 2cf901bcff
Merge pull request #401 from MichonGoddijn231849/perf/buffer-pool-buckets
perf(agc): keep the buffer pool's free slots by key and retain 4096
2026-10-04 22:56:48 +13:00
Dean 0bd5766246
Merge pull request #394 from MichonGoddijn231849/feat/mipmapped-3d-textures
feat(agc): mipmapped 3D textures
2026-10-04 22:56:31 +13:00
Dean 1104cfce5b
Merge pull request #404 from MichonGoddijn231849/perf/color-swizzle-tables
perf(agc): build the color target swizzle tables once per element size
2026-10-04 22:56:14 +13:00
Dean 0623b69325
Merge pull request #376 from MichonGoddijn231849/feat/depth-bounds-bias
feat(agc): depth bounds test and depth bias
2026-10-04 22:55:57 +13:00
Dean bd753903e3
Merge pull request #294 from MichonGoddijn231849/split/color-target-gaps
fix(agc): color targets with gaps between written MRT slots, depth array slices
2026-10-04 22:55:40 +13:00
Dean 97822deb0b
Merge pull request #446 from adriadam10/feat/vop3-f64-basic
feat(shader): implement basic f64 VALU ops
2026-10-04 22:55:23 +13:00
Dean ce8b0838ee
Merge pull request #471 from adriadam10/perf/single-front-end
perf(shader): build a shader's front end once on first use
2026-10-04 22:55:06 +13:00
Dean 6a4bbade0d
Merge pull request #435 from adriadam10/feat/vop3-mad-i64-i32
feat(shader): implement v_mad_i64_i32
2026-10-04 22:54:49 +13:00
Dean 13cbbfa7f7
Merge pull request #325 from adriadam10/feat/relative-indexing
feat(shader): implement v_movrelsd_b32, v_movrelsd_2_b32, v_swap_b32 and v_swaprel_b32
2026-10-04 22:54:32 +13:00
Dean f4071f2469
Merge pull request #331 from adriadam10/feat/format-d16-stores
feat(shader): implement the D16 format buffer stores
2026-10-04 22:54:11 +13:00
Dean 1f4e030374
Merge pull request #330 from adriadam10/fix/formatted-store-conversion
fix(shader): convert formatted buffer stores to the element format
2026-10-04 22:53:54 +13:00
Dean 598833214b
Merge pull request #329 from adriadam10/feat/format-d16
feat(shader): implement the D16 format buffer loads
2026-10-04 22:53:37 +13:00
Dean 5ce9600d87
Merge pull request #319 from adriadam10/feat/d16-memory
feat(shader): implement the D16 byte/short loads and high-half stores
2026-10-04 22:53:20 +13:00
Michon 527c8e46a8 fix(shader): v_mov_b32 SDWA writes byte destinations
v_mov_b32 accepted SDWA destination selectors for the words only; a byte
destination (dst_sel BYTE_0..3) threw 'VOP1 SDWA destination selector is not
supported'. The write itself already handles bytes and every dst_unused mode
(TranslationContext::writeRawU32). GTA V aborted during boot on such a move
(dst_sel BYTE_0, dst_unused UNUSED_PRESERVE) in one of its eboot compute
shaders. The error now names the selector, opcode and pc. The SDWA destination
execution test covers BYTE_0 and BYTE_2 with UNUSED_PRESERVE and BYTE_1 with
UNUSED_PAD.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-04 11:44:10 +02:00
Adrià Franch fc00228938 docs: add architecture overview with Mermaid diagrams
Two diagrams in docs/dev/ARCHITECTURE.md: how the relinker and the NID-patched system libraries produce a native program, and the graphics path from command buffers through the shader recompiler to Vulkan. README only gains a link.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-04 11:20:38 +02:00
Michon 66a46259d1 fix(agc): pad tiled 3D mip levels from their size rounded up, as addrlib does
addrlib sizes a non-tail level with ShiftCeil; the layout used the image's
rounded-down extent, so volumes with a non-power-of-two width or height (33x20x12
in the new test) got a different layout.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-04 10:27:32 +02:00
Michon efde918a62 feat(graphics): mipmapped 3D textures
3D textures with more than one mip level were refused. Each slab of
blockDepth slices of a thick (SW_4KB_S / SW_64KB_S) volume now holds the
whole mip chain, smallest level first and level 0 last, as 2D chains are
laid out. A level's depth halves per level, so the texture loops skip the
slices a level does not have. Thin 3D (D/R 64KB_X) chains use the 2D mip
layout per depth slice. Thick mip tails still throw.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-04 10:27:32 +02:00
Dean 9f9a8f5a53
Merge pull request #340 from QDarkRex/fix/relinker-tls-fs-disp32
fix(relinker): load Windows guest TLS values at signed displacements
2026-10-04 20:39:50 +13:00
Adrià Franch e1b7ecc1c5 feat(shader): implement v_mad_i64_i32
Decode VOP3b 0x177 and translate it next to v_mad_u64_u32: the high half of
the product comes from SMulHi, and the carry-out is the sign of the exact
sum (bit 64 of the sign-extended product plus the sign-extended addend), as
measured on RDNA2 hardware.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-04 09:17:12 +02:00
Dean 8a64778581
Merge pull request #486 from RomainROCH/codex/fix-executable-segments
fix(relinker): validate all executable segments and preserve live imports
2026-10-04 20:09:49 +13:00
Dean 3237b6b548
Merge pull request #484 from RomainROCH/codex/fix-elf-string-bounds
fix(relinker): bound dynamic strings by the declared table size
2026-10-04 20:09:33 +13:00
QDarkRex ab5c339039 fix(relinker): reject guest TLS loads from unmodelled control block fields
Loads at fs:1 through fs:0x27 read tcb_dtv and tcb_thread on the FreeBSD layout, which the Windows thread control block does not model, so they returned zero. Accept only the TLS block, fs:0 and the stack guard at fs:0x28, and fail the relink for the rest.
2026-10-04 14:09:19 +07:00
Dean a75b2c9550
Merge pull request #316 from adriadam10/fix/wave32-subgroup-size
fix(agc): run wave32 compute programs on 32-wide subgroups
2026-10-04 20:09:16 +13:00
Dean 3357199fce
Merge pull request #243 from adriadam10/feat/audio-in-capture
feat(libSceAudioIn): capture from the host's recording device
2026-10-04 19:55:15 +13:00
Adrià Franch fe26911cbb fix(shader): build f32/f64 conversion NaNs from the bits
OpFConvert leaves NaN payloads to the driver; NVIDIA returns its own.
v_cvt_f64_f32 now shifts the f32 payload left by 29 and v_cvt_f32_f64
keeps the top 23 payload bits, both quieted, as RDNA does (60/60 NaN
conversions measured on RDNA2). F64NanConversions covers both with the
hardware rows.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-04 08:52:14 +02:00
Adrià Franch 6ea9750bf3 refactor(shader): read f64 operands through readF64Bits and record the v_fma_f64 subnormal gap
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-04 08:52:14 +02:00
Adrià Franch 6f20ed3778 fix(shader): compute v_fma_f64 exactly instead of relying on the driver
The Vulkan precision table defines GLSL.std.450 Fma as "inherited from
OpFMul followed by OpFAdd", so a driver may round twice. RADV fuses it,
the lavapipe in CI (Mesa 25.2.8, LLVM 20) does not, and F64Arithmetic
failed there on the fma rounding-error row.

v_fma_f64 is now emulated with operations Vulkan requires to be
correctly rounded: the operands are scaled to exponent 0, the product is
split exactly (Dekker), added to the addend with TwoSum, the low parts
are summed with round-to-odd and one final round-to-nearest gives the
fused result (Boldo and Melquiond). The result is scaled back by two
exact powers of two. Inf/NaN, zero factors and subnormal inputs take the
mul + add path, an addend 2^60 larger than the product is returned as
is. Matches the hardware on all 13253 fma results captured for #446 and
an exact model on 300000 random inputs incl. ties.

NaN results of add, mul and fract are also chosen on the bits (first NaN
input quieted, otherwise the default NaN) since Vulkan does not specify
which NaN an operation returns.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-04 08:52:14 +02:00
Adrià Franch b0afa04c77 feat(shader): implement basic f64 VALU ops
Decode and translate v_add/mul/fma/min/max/ldexp_f64, v_trunc/ceil/floor/
rndne/fract_f64, v_frexp_mant/exp_i32_f64 and the f64 <-> f32/i32/u32
conversions. f64 values stay as their raw bits (U64) in the IR and are
bitcast to double in SPIR-V, which needs the Float64 capability: the
device enables shaderFloat64 when it also preserves signed zero, inf and
NaN for 64-bit floats, and the shader declares SignedZeroInfNanPreserve 64.

Semantics measured on RDNA2 hardware (8209 inputs per op): literals are
the high dword, the 1/(2*pi) inline constant is the exact double,
omod is ignored, clamp maps NaN to +0, min/max quiet a
signaling NaN and drop a quiet one, NaN results are quiet, and the
integer conversions saturate with NaN -> 0. Truncation, rounding and
ldexp are done on the bits so they do not depend on the driver: lavapipe
returned wrong results for f64 RoundEven and Trunc, and GLSL Ldexp is
undefined past +-1024. Fma is NoContraction so RADV keeps it fused.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-04 08:52:14 +02:00
Dean Galvin b6298bf738 fix(agc): keep one storage image for a surface viewed past its last mip
The descriptors of one surface share its storage image through the
surface key, which includes the level count. A view past MAX_MIP
counts the extra levels, so its storage image was a second image of
the same memory, and the two flushed and re-uploaded each other every
frame (PPSA21564 writes level 6 of each 1920x1080 bloom chain after
building levels 1 to 5 through MAX_MIP 5 views).

A surface once viewed past its allocated levels now keeps a single
image holding the extended chain, and its other descriptors look that
image up; the allocated-level image it replaces is evicted with its
results stored. The extra levels lie in the allocation (the decode
proved that), so levels 0 to MAX_MIP keep their layout and the views
of them read the same texels.
2026-10-04 19:52:04 +13:00
Dean Galvin 3d3e687971 fix(agc): address texture views that start past the surface's last mip
PPSA21564 ends each of its two 1920x1080 RGBA16F bloom downsample
chains (MAX_MIP 5) with a compute pass that stores level 6 through a
view of levels 6..6, every frame from the PS logo on. The descriptor
decode threw for such a view.

GFX10 uses MAX_MIP only to place the levels: a level's address comes
from the levels below it, and a level in the mip tail is a fixed slot of
the tail block counted from the first tail level. A level past MAX_MIP
is addressed the same way, so when the chain extended to the view's last
level keeps every allocated level, the tail and the surface size
unchanged, the view reaches that slot inside the surface's allocation.
addrlib (GFX10, Navi10 with 16 pipes) computes the same 0x1640000-byte
layout for 6, 7, 9 and 11 levels of this surface, with level 6 in the
tail at (0, 32). The view then decodes with the extended chain over the
same guest bytes. Views whose extra levels would move the allocated
ones, such as linear chains or chains without a tail, still throw.
2026-10-04 19:52:04 +13:00
Dean b0a4a991cb
Merge pull request #334 from adriadam10/feat/ds-permute
feat(shader): implement ds_permute_b32
2026-10-04 19:42:21 +13:00
Dean dda274b5e7
Merge pull request #434 from adriadam10/feat/relinker-sha256-memory
feat(relinker): lower SHA-256 instructions with a memory operand
2026-10-04 19:42:10 +13:00
Dean ec720a351c
Merge pull request #437 from adriadam10/feat/vop3-qsad-mqsad
feat(shader): implement v_qsad_pk_u16_u8, v_mqsad_pk_u16_u8 and v_mqsad_u32_u8
2026-10-04 19:41:53 +13:00
Adrià Franch 7e5ce945e1 feat(libSceAudioIn): implement sceAudioInClose and throw for the unverified 0x10 format
Close releases the port and its host device; Input and Close on a port
that is capturing return BUSY. Format codes follow the PS5 SDK bindings
(SharpProspero) and KytyPS5: 1/2 S16 mono/stereo, 0x11/0x12 float.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-04 08:39:33 +02:00
Adrià Franch 29aa4f3cdf feat(libSceAudioIn): capture from the host's recording device
sceAudioInOpen validates the port and opens the default SDL recording
device in the requested format; sceAudioInInput returns one grain of
captured samples, padded with silence when the device falls behind and
keeping at most four grains of latency. Without a recording device a
port reports DEVICE_NONE in sceAudioInGetSilentState and delivers
silence at its sample rate.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-04 08:37:09 +02:00
Adrià Franch eff7af93d4 fix(shader): translate s_setreg_imm32_b32 only for round to nearest even in MODE
Any other field or value throws. The existing s_setreg_b32 no-op is
recorded under Functional technical debt instead of silent stubs.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-04 08:28:52 +02:00
Adrià Franch f994cc6ab7 feat(shader): implement s_cmovk_i32, s_version and s_setreg_imm32_b32
s_cmovk_i32 writes the sign-extended 16-bit immediate when SCC is set.
s_version is a no-op marker. s_setreg_imm32_b32 takes its value from a
literal dword and is ignored like s_setreg_b32; both are now listed as
silent stubs in TechnicalDebt.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-04 08:26:58 +02:00
Dean Galvin be3f06bfe8 feat(libSceAudioPropagation): render silence for propagated sources
sceAudioPropagationSourceRender takes the system, an array of RenderInfo
descriptors and their count: PPSA21564 and PPSA21567 pass one RenderInfo
{0x010107d6, 0x30} per call, with the source handle at +0x10, its output
buffer at +0x18, the buffer size in bytes at +0x20 and the word 2 at
+0x28, once per audio grain for each propagated source.

Without acoustic propagation a source contributes nothing, so each
output buffer is zeroed for its size. Every RenderInfo is checked first
(descriptor, a live source of this system, a non-empty output), and a
count of 0 or a format word other than 2 throws, as neither has been
observed.
2026-10-04 19:26:06 +13:00
Dean cd61f15e55
Merge pull request #470 from adriadam10/perf/validate-program-flat-index
perf(shader): index ValidateProgram by value and block id
2026-10-04 19:24:04 +13:00
Adrià Franch 01bae4dd79 fix(shader): track the multi-register destinations of the quad SAD ops in vertex fetch analysis
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-04 08:23:37 +02:00