Fixes crashes in Armored Core Verdict and Armored Core V.
RPCS3 HDD reads were finishing much faster than on PS3, so emulating
their timing gives the audio worker time to free old sound banks before
the new ones exhaust the heap.
### Remove
- gui: Remove Debug "Approximate PPU Vector NaN Handling"
- gui: Remove I/O "Load SDL GameController Mappings"
- gui: Remove Advanced "VBlank NTSC Fixup", enable by default
- gui: Remove Debug "Accurate DFMA"
### Move
- gui: Move Advanced "Enable SPU Events Busy Loop" to Debug
- gui: Move Advanced "Set DAZ and FTZ" to Debug
- gui: Move Audio Device to the left column in Audio
### Group
- gui: Add "Savestates" section in Advanced
### Order
- gui: Alphabetically order the Advanced Tab
- gui: Alphabetically order the I/O tab
### Visibility
- gui: Hide formats list when Audio Format is not Manual
- gui: Hide 3D settings if GPU "Enable 3D Support" is disabled
- gui: Hide RCAS if GPU "Output Scaling" is not FSR1
Fixes#18381
### Bug:
`gs_frame::show()` went through `QWindow::show()`. On desktop platforms
`QPlatformIntegration::defaultWindowState()` is `Qt::WindowNoState`, so
that always routes through `showNormal()`, which pushes
`Qt::WindowNoState` to the native window before the startup visibility
is applied. On a window that is already fullscreen this is a real "leave
fullscreen" request immediately followed by an "enter fullscreen" one,
and a window manager is free to resolve that pair as windowed.
`QXcbWindow::setWindowState()` returns early when the requested state
matches the current one, so the first call of a session is a no-op, but
a later one on a fullscreen window is not, and
`QWaylandWindow::setWindowState()` forwards unconditionally.
Only the OpenGL frame runs into this. `gl_gs_frame` shows itself from
its constructor so that wayland-egl has a mapped surface before the
context is created (c7b2a21f), so by the time `gs_frame::flip()` shows
the frame again on the first flip, the window is already fullscreen.
Vulkan and Null only ever show the frame on that first flip, which is
why they are fine. It also explains why this never reproduced on
Windows: `ShowWindow()` applies both states synchronously and the second
one wins.
### TO BE TESTED ON LINUX
Not runtime tested yet: it needs a run on X11 and on Wayland with an
OpenGL game and "Start games in fullscreen mode" enabled.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 5.5 <[email protected]>
Display an error message when booting a game from an encrypted ISO file
/ BD Drive in case:
- no decryption key file found
- wrong decryption key file provided (e.g. with key of another game)
Further, code improved to skip a key file for an ISO file recognized as
decrypted (avoid to decrypt (to corrupt) the ISO file with a wrongly
provided key).
ZPASS_COUNT is an observable side-effect of Z-buffer access, despite Z-TEST and Z-WRITE being disabled. Technically, this is a pure Z-READ operation.
Co-authored-by: kd-11 <[email protected]>
save_patches always emitted the address element as fmt::format("0x%.8x"),
but for move_file and hide_file that element is a VFS path: load() keeps the
text in original_offset and skips u32 validation for those two types.
Round-tripping such a patch through save_patches replaced the source path
with 0x00000000.
The entry stays syntactically valid and keeps loading, so the patch is still
listed and can still be toggled in the Patch Manager while silently matching
no file. Re-downloading does not repair it either, since append_patches
discards an incoming patch whose Patch Version is not strictly greater.
Gate the emit on patch_type_uses_hex_offset, the predicate that already
existed for this and was used only on the load side. The numeric branch
keeps using offset rather than original_offset on purpose: an address
modifier is folded into offset at load time and the flat form emitted here
has nowhere to put it.
Co-Authored-By: Claude Opus 5 <[email protected]>
save_patches opened the target with fs::rewrite (write + create + trunc),
then discarded the result of file.write() and returned true unconditionally.
A write that fails part way leaves a truncated file, load() rejects the
whole file on a parse error, and import_patches refuses to write when
load() fails, so every later import returns -1 with no in-app recovery.
75e66ab5e made the patch config and the pad config atomic but missed this
function; save_config, 70 lines up in the same file, already writes through
fs::pending_file and checks the result. Do the same here.
Co-Authored-By: Claude Opus 5 <[email protected]>
Adreno waves are 64 lanes, and a workgroup narrower than the wave does not pack
together with its neighbours -- it occupies a whole wave and masks the surplus
lanes off. At 32 that idled half of every wave on every dispatch, and these
kernels run on every texture upload of a kilobyte or more, plus every deswizzle
and detile.
Nothing argues the other way here. The kernels carry no shared memory and no
barriers, so group size is a scheduling hint with no LDS or synchronisation
cost to trade against.