Commit Graph
1369 Commits
Author SHA1 Message Date
Henrik RydgårdandClaude Opus 5.5 7103341202 GL: Halve the defragmented push buffer when the big allocation fails
When a frame spilled over into several push buffers, Defragment freed
them and asserted if one block of their summed size couldn't be
allocated. On 32-bit devices that contiguous allocation fails where the
pieces fit, which Play reports from 32-bit ARM show at the start of a
frame. Halve the size until it fits; a smaller buffer just spills into
more buffers again, as it would have before defragmenting.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 02:05:35 +01:00
Henrik Rydgård 176f56da8d Implement support for setting two common driver flags for Turnip (custom Adreno driver)
enable_tp_ubwc_flag_hint and storage_8bit are now available as check
boxes on the driver setup screen.

Fixes #22808
2026-10-27 12:16:25 +01:00
Henrik RydgårdandClaude Opus 5.5 89d7d69551 Metal: Tell 16-bit format support by GPU family, not by platform
The simulator check didn't compile on macOS, where the define doesn't exist.
The simulator's device reports only the Apple1 and Apple2 families, so require
Apple3 instead, which every device iOS 15 runs on has. Also silence the BC
format availability warnings in DataFormatToMetal, since GetDataFormatSupport
already checks for iOS 16.4.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-26 10:34:29 +01:00
Henrik RydgårdandClaude Opus 5.5 8c8ecc8c32 Metal: Check the 16-bit texture formats through the context
The iOS simulator reports an Apple GPU but doesn't have the packed 16-bit
formats, so creating one tripped Metal's validation and aborted the app.
thin3d now maps them and only reports them as supported on an Apple GPU outside
the simulator, and GPU_Metal asks GetDataFormatSupport like the Vulkan backend
does. Without them, textures decode to 8888.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-26 09:50:10 +01:00
Henrik RydgårdandClaude Opus 5.5 178aaadf1a iOS: Build and run on the simulator
Adds an arm64 simulator platform to the iOS toolchain. The simulator build
renders with Metal only: it doesn't link MoltenVK, and loads Vulkan at
runtime like the libretro core, so Vulkan reports itself unavailable and
the config falls back to Metal.

b-ios-simulator.sh builds the App Store variant and runs it on the booted
simulator, or on one of a given device type, for App Store screenshots of
devices we don't have.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-25 22:48:02 +01:00
Craig Carnell 28ac694bf6 Add libretro VR support 2026-10-24 17:32:15 +01:00
Henrik RydgårdandClaude Opus 5.5 da33eab776 Outputs: Defensive checks, and don't wait for a Metal drawable twice in a frame
- Metal: when a layer has no drawable to give (it can take a second to
  say so), don't ask again in the same frame for every pass to it.
- Swapchains other than the primary are created, resized and destroyed
  between frames (Metal asserts it), and destroyed with the last
  reference to them (ReleaseAssertLast, Metal and Vulkan).
- BindFramebufferSamplesAsTexture asserts it isn't given a swapchain,
  like BindFramebufferAsTexture.
- An offscreen Metal swapchain can't be resized, asserted.
- VulkanRenderManager asserts that no additional swapchain outlives it.
- Native_RemoveOutputAndWait asserts it isn't called on the thread that
  handles the requests, where it would always time out.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 23:08:52 +02:00
Henrik RydgårdandClaude Opus 5.5 771e40e297 Vulkan: Recreate an additional swapchain that stays suboptimal
Only out of date and a lost surface recreated one, so a TV whose mode
changed without a resize (MoltenVK reports SUBOPTIMAL when the layer's
size no longer matches) kept a stretched picture. Like the main
swapchain, it's now recreated after a few SUBOPTIMAL presents in a row.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 23:03:16 +02:00
Henrik Rydgård 79cedbc13f Merge pull request #22734 from hrydgard/vulkan-stale-extent-workaround
Android Vulkan: Bring back the stale surface extent workaround
2026-10-22 21:52:20 +02:00
Henrik RydgårdandClaude Opus 5.5 f8e7efc8b0 Android Vulkan: Check the surface extent against the latest window size
g_display lags behind surfaceChanged until the render thread applies the
resize, and a recreate after a suboptimal present can run in between.
The check then swapped a correct extent for a frame. Android now sets the
draw size callback to the latest size from backbufferResize, and the
check uses that.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 20:49:08 +02:00
Henrik RydgårdandClaude Opus 5.5 82e6ac86f0 Vulkan: Balance the debug label of a swapchain pass that has no image
A swapchain pass closes its step label and opens one in presentCmd, but
only if it gets an image. With the main window minimized it doesn't, and
the end of the step closed a label that wasn't open, in presentCmd if an
extra swapchain's pass had moved recording there.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 20:41:35 +02:00
Henrik RydgårdandClaude Opus 5.5 ea5e39c440 Android Vulkan: Bring back the stale surface extent workaround
e32e558aab dropped it, assuming the stale extent came from our own resize
race. A Redmi 9A (PowerVR, Android 11) shows it doesn't: after a rotation,
vkGetPhysicalDeviceSurfaceCapabilitiesKHR returns the new transform with the
old extent, even when queried after surfaceChanged and ApplyResize have
given us the new size, and the wrong swapchain then stays until the next
rotation.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 20:29:21 +02:00
Henrik RydgårdandClaude Opus 5.5 8e8cf5dbb4 Vulkan: Multiple swapchains
Swapchains other than the main one are VKRSwapchains, owned by the
render manager, each with its own surface, image views, framebuffers
and depth buffer, in the main swapchain's format so the backbuffer render
pass and pipelines work for all of them. A render step with no
framebuffer can name one.

They don't pace the frame: acquiring doesn't wait, and the pass is
skipped if there's no image. The frame's last submit waits on and
signals the semaphores of every swapchain acquired that frame, and each
is presented after the main one. When one goes out of date, it's
recreated at the start of the next frame.

The main swapchain's path is unchanged; frametests on Vulkan match.
VulkanContext::CreateSurface is the old per-platform surface code, now
also used for the extra ones.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 20:11:36 +02:00
Henrik Rydgård 47e67cb122 Merge pull request #22728 from hrydgard/multi-swapchain-3
Multi-swapchain (multi-window rendering) support, for Metal
2026-10-22 20:09:32 +02:00
Henrik RydgårdandClaude Opus 5.5 b46e386f12 Vulkan: Enable the portability subset features MoltenVK has
We queried VkPhysicalDevicePortabilitySubsetFeaturesKHR but never enabled
it at device creation, so to the validation layers we were using image
view swizzles (and sampler LOD bias) without having them. Enable whatever
the device offers.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 18:46:43 +02:00
Henrik RydgårdandClaude Opus 5.5 db6cc41b9d Vulkan: Ask for portability drivers on Mac
Through the Khronos loader (like the Vulkan SDK's, needed for the
validation layers), MoltenVK is only listed if the instance enables
VK_KHR_portability_enumeration. Without it, instance creation failed with
VK_ERROR_INCOMPATIBLE_DRIVER and we fell back to OpenGL. Loading MoltenVK
directly, as the app normally does, works as before.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 18:46:43 +02:00
Henrik RydgårdandClaude Opus 5.5 56c76ace35 thin3d: Swapchain objects, and multiple swapchains on Metal
The backbuffer is now a Swapchain, from GetPrimarySwapchain(), and
callers bind, measure and read it back through that instead of nullptr.
nullptr still means it too. Backends with one swapchain map the primary
to their old backbuffer path.

On Metal, CreateSwapchain() adds more, each with its own layer, depth
buffer and drawable. Passes merge per swapchain, and Present() presents
every drawable acquired that frame. The backbuffer is just the first
of them. Nothing creates a second one yet.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 18:42:53 +02:00
Henrik RydgårdandClaude Opus 5.5 ae9a0c47e7 Metal: MSAA
Like the Vulkan backend: a multisampled framebuffer has multisampled
color and depth textures that are drawn to and kept between passes, and
each pass resolves them into the ordinary ones (depth and stencil to
sample 0), which textures, copies and readbacks use.
BindFramebufferSamplesAsTexture binds the samples, for the shared code's
sample 0 copies. Copies between multisampled framebuffers copy the
samples too, both depth and stencil, since Metal can't copy one plane of
a multisampled texture.

Also skips empty draws, which Metal doesn't allow.

With 4x MSAA, 1012 of the 1061 frame dumps are identical to Vulkan's.
Most of the rest differ at edges because programmable blending blends
each sample with its own color. In Resistance: Retribution, the sample 0
copy for a palette lookup matches the render without MSAA here, and
Vulkan's doesn't.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-19 12:48:12 +02:00
Henrik RydgårdandClaude Opus 5.5 c3f5a4f534 Metal: Cleanups from a review of the backend
- Remove what nothing used: the vertex texture and sampler commands,
  the byte-upload setters, and some unused render manager members.
- thin3d pushes its uniform buffer once for both shader stages.
- Build the thin3d depth/stencil cache key from bytes, so memcmp
  doesn't compare padding.
- Only create the upload command buffer when there are uploads.
- Compare copy destinations by their base texture when merging passes,
  like the textures the passes read.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-19 12:07:52 +02:00
Henrik RydgårdandClaude Opus 5.5 987a904cff Metal: Fail backbuffer readbacks until something draws to it
Headless renders to an offscreen backbuffer that nothing ever draws to,
so the GE debugger's screenshot read uninitialized memory. Fail the
readback instead, like headless Vulkan (which has no backbuffer), so the
debugger falls back to the displayed PSP framebuffer.

All the GE debugger buffers (screenshot, render color/depth/stencil,
texture, CLUT) now read back the same as Vulkan's.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-19 12:02:22 +02:00
Henrik RydgårdandClaude Opus 5.5 b2e6113f21 Metal: Hardware texture scaling
Runs the same compute shaders as the Vulkan backend, translated to MSL,
in the upload command buffer: levels decode unscaled into the push
buffer and get scaled into a view of the level. Push constants go in a
buffer of their own. Multipass shaders (NNEDI3) use scratch textures
that live for the frame.

Shaders with a constant buffer (only the smiley test shader) aren't
supported.

All the texture shaders give the same result as Vulkan's on LocoRoco
and God of War savestates.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-19 12:02:22 +02:00
Henrik RydgårdandClaude Opus 5.5 fb2311099a Metal: Leave room after push buffer allocations
The vertex decoder can write past the end of its output, so a draw
decoded into the last bytes of a push buffer block wrote into unmapped
memory and crashed, now and then, in DrawEngineMetal::Flush. Keep 256
bytes free after each allocation, like the Vulkan backend's push pool.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-19 12:02:22 +02:00
Henrik RydgårdandClaude Opus 5.5 51d775cb97 Metal: Compile shaders and pipelines in the background, and cache them
Pipeline states are created on compile threads, along with the shader
functions they need, which compile once on first use. Draws record a
handle to the pipeline state, and the render thread waits for it if it
gets there first, as with Vulkan, so the emu thread never waits for a
compile. Translation to MSL stays synchronous, as recording needs the
texture masks it produces.

Each game's shader IDs and pipeline keys are saved to a
<discID>.mtlshadercache, and recreated while the game boots. macOS's own
Metal cache then makes them quick: Wipeout Pure's 36 pipelines took
25 ms, or 260 ms with an empty Metal cache, instead of stalls during
play.

Cold Wipeout Pure, 5 emulated seconds from a savestate: 3.0 s before,
1.6 s with the background compiles alone.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-19 12:02:22 +02:00
Henrik RydgårdandClaude Opus 5.5 6a668eed27 Metal: Use programmable blending for shader blending
On Apple GPUs, the fragment shader reads the framebuffer directly
([[color(0)]]) instead of blending against a copy of it. The Vulkan GLSL
generator path for this was already there apart from the input
declaration: SPIRV-Cross turns the subpassInput into framebuffer fetch.
The Vulkan backend never sets the fetch bit.

Reading the framebuffer as it is also sees earlier triangles of the same
draw, like the PSP does, where the copy only had what was there before
the draw. Of the dumps in ppsspp-framedumps whose output changes, all
ten with a PSP capture got closer to it, Tenchu's from about 10000
differing pixels to 900. Ridge Racer runs about 6% faster.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-19 12:02:22 +02:00
Henrik RydgårdandClaude Opus 5.5 9514e02037 Metal: Encode on a render thread, and merge render passes
The render manager now records commands on the emu thread, keeping all
of its decisions there (lazy passes, clear merging, redundant state),
and they're encoded on a render thread, or inline when
bRenderMultiThreading is off, as in the Vulkan backend.

Before encoding, passes to the same framebuffer are merged across
independent work, by the rules of VulkanQueueRunner's render pass merge.
God of War alternates with block transfers to other framebuffers, and
went from about 41 to 18 render passes a frame, which more than halved
its GPU time.

Passes where nothing tests or writes depth or stencil leave the
attachment unloaded and unstored, like the Vulkan backend, which
doesn't attach depth then.

On the eight benchmark states, Metal now uses 4-13% less CPU than
Vulkan, with wall times from 6% faster to 4% slower.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-19 12:02:22 +02:00
Henrik RydgårdandClaude Opus 5.5 51a90c8336 Metal: Cut ARC traffic and redundant bindings from the draw path
Draws use an unretained encoder pointer and pipeline struct pointers
instead of passing ObjC objects around (each return goes through
autorelease and retain), and only bind the texture slots the fragment
shader uses. Brings the emu thread's retain/release work down to 0.6%,
and instruction counts 2-11% below Vulkan's on the benchmark states.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-19 12:02:22 +02:00
Henrik RydgårdandClaude Opus 5.5 330be6c29a Metal: Don't let command buffers retain resources
Resources that the GPU may still use are now released through the render
manager's per-frame list, freed once the frame's work completes, instead
of command buffers retaining everything that's bound.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-19 12:02:22 +02:00
Henrik RydgårdandClaude Opus 5.5 042e320559 Metal: Hook up the backend on iOS
The view controller creates a Metal context when that's the configured
backend, and the backbuffer gets its size from the layer's bounds, as the
drawable size isn't set up yet when the surface is created.

Also pass --options the process was launched with (from Xcode or
devicectl) on to the command line parser, so --graphics=metal works.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-19 12:02:22 +02:00
Henrik RydgårdandClaude Opus 5.5 4793d21e5d Metal: Add the GPU backend
GPU/Metal, modelled on GPU/Vulkan since the shaders are the same Vulkan
GLSL, translated to MSL. Drawing goes through the render manager's state
setters, so it shares encoders with thin3d.

thin3d fixes found along the way:
- Binding the framebuffer that's already the target continues the pass,
  applying clears and ignoring DontCare, like VulkanRenderManager.
- Separate depth or stencil copies, through a buffer, so the stencil
  survives a framebuffer resize (Need for Speed Shift).
- Stencil export, so stencil uploads take one pass.

Out of the 1061 dumps in ppsspp-framedumps, 1058 render identically to
Vulkan (two of the others are bad dumps), as do the eight benchmark
savestates.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-19 12:02:22 +02:00
Henrik RydgårdandClaude Opus 5.5 8d7ce57fa8 Metal: Hook up the backend on Mac, with the null GPU for now
Selectable with --graphics=metal, the backend choice in settings and the
Mac menu bar. SDL creates a Metal view for it, and headless renders
offscreen. Games run on the null GPU until there's a GPU/Metal.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-19 12:02:22 +02:00
Henrik RydgårdandClaude Opus 5.5 16ed2ad55a Metal: Add a thin3d backend (WIP, not hooked up yet)
thin3d on Metal, plus a small render manager that owns frames, command
buffers, lazily started render passes and a per-frame push buffer.

Shaders are the same Vulkan GLSL the Vulkan backend gets, translated to
MSL through glslang and SPIRV-Cross, with the Vulkan binding numbers as
Metal buffer/texture indices. flip_vert_y makes Metal's clip space match
Vulkan's, so everything else follows the Vulkan conventions.

Adds GPUBackend::METAL (4) and builds the files on Apple platforms. Not
yet selectable: the SDL window setup, the UI choice and the GPU core
come next.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-19 12:02:22 +02:00
Henrik RydgårdandClaude Opus 5.5 e666ab49de Remove leftovers from hardware tessellation
Nothing reads from textures or storage buffers in vertex shaders since
hardware tessellation was removed. Drop what was left for it: the
vertexTexture flag of VulkanTexture::EndCreate (always false), the
STORAGE_BUFFER_VERTEX binding type, GL's maxVertexTextureUnits, and the
tesselationShaderSupported cap, which nothing read (and which was always
false, since the feature was never enabled).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-19 12:02:10 +02:00
Henrik RydgårdandClaude Opus 5.5 d3f33d96d3 Create the GPU on the main thread during boot
The loader thread now only loads the game. PSP_InitUpdate creates the GPU on
the thread that owns the graphics device, so device loss can't race the
creation, and the device-lost flag and the GPU init on libretro's separate
path are gone. Vulkan loads its shader cache without waiting for the
pipelines, and PSP_InitUpdate keeps the boot in Booting until they're
created. Exiting during boot joins the loader thread in PSP_Shutdown.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-19 09:26:15 +02:00
Henrik RydgårdandClaude Opus 5.5 e32e558aab Android Vulkan: Drop the stale surface extent workaround
The transposed extents f7ec86c5d5 worked around came from the size race
the previous commit fixed: with the size now applied between frames, the
workaround didn't fire once in 38 rotations on the Galaxy S8+ that hit it
in about one in four.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-18 01:47:07 +02:00
Henrik RydgårdandClaude Opus 5.5 074b66b49b thin3d: Add Framebuffer::HasDepthStencil, check it before depth/stencil copies
ResizeFramebufFBO now asks the framebuffer whether it has a depth/stencil
buffer instead of guessing from the z address and stride.

Vulkan CopyFramebuffer gets the debug asserts and bail-out BlitFramebuffer
already had, and GL copies and blits get the same, so a copy out of a
framebuffer without depth/stencil asserts where it's queued instead of in
the queue runner.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 17:44:09 +02:00
Henrik RydgårdandClaude Opus 5.5 7080085571 GLES: Apply the texture LOD bias in the shader
GL_TEXTURE_LOD_BIAS is desktop GL only, and the queue runner skipped it on
GLES, but GLES still reported sampler LOD bias as supported, so the bias was
dropped and textures used the wrong mip levels: Super Monkey Ball Adventure's
intro came out blurred on the Adreno 840. Report it unsupported, so the shader
applies it (GPU_USE_SHADER_LOD_BIAS), and give the GL backend the uniform that
Vulkan has in its uniform block.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 01:33:32 +02:00
Henrik RydgårdandClaude Opus 5.5 168149e3f2 GL: Use 0..1 clip space depth where clip control is available
With the default -1..1, window depth 0 comes from z = -w, where the
rasterizer's rounding is an ulp of 1.0. Puyo Puyo Fever 2's ground at z = 0
got depths just above 0 with depth clamp on, and the sprites on it that test
GEQUAL lost pixels (910 px off the PSP on an Adreno 840, now 0). Use
glClipControl (GL 4.5, ARB/EXT_clip_control) as in Vulkan. Not in libretro,
where the frontend shares the context.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 01:33:32 +02:00
Henrik RydgårdandClaude Opus 5.5 e801f5a420 GL: Set GL_UNPACK_ALIGNMENT to 1, texture uploads are tightly packed
With the default of 4, the driver reads past the end of any upload whose rows
aren't a multiple of 4 bytes, like a 16-bit texture of odd width. The Adreno
840 crashed in glTexImage2D on one in SOCOM Fireteam Bravo 3.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 01:33:32 +02:00
Henrik RydgårdandClaude Opus 5.5 e3577fad74 GL: Actually enable depth clamp on GLES
GLES drivers with GL_EXT_depth_clamp reported depth clamp as supported, but
glEnable(GL_DEPTH_CLAMP) was compiled out with USING_GLES2. The shaders then
relied on clamping that never happened, and geometry outside the depth range
was clipped: on the Adreno 840, Hokuto no Ken's title screen came out black and
Hot Pixel's background was missing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 01:33:32 +02:00
Henrik RydgårdandClaude Opus 5.5 60100c4254 - Fixes #13676
Vulkan: Batch Jackass' tiled in-place color grade

Jackass: The Game color grades the framebuffer in 32x64 tiles, one channel
per pass, texturing from the framebuffer it renders to. Each of the 225
draws became a depal pass plus a pass drawing it back, 450 render passes
per frame. A new queue hack, enabled by the TiledDepalHack compat flag,
sorts these pairs into waves of non-overlapping tiles and runs each wave
as one depal pass and one draw pass, leaving 6.

Output is byte-identical to before on both dumps from the issue, at 1x
and 3x on desktop, and at 1x on both phones below.

Time saved per post-processed frame, from replaying the dumps from the
issue with headless --bench (each replay renders that frame once):

  Redmi 9A (PowerVR GE8320):  ~145ms at 1x, ~180ms at 2x
  Unisoc UMS9632 (Mali-G57):  ~55ms at 1x and 2x

Either is more than a whole 33ms frame, so on those GPUs this pass alone
ruled out 30fps.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 17:08:58 +02:00
libretroadmin b71fbaeb5c libretro: fix OOB, UAF, data races and control flow in core glue
System_AudioPushSamples converts straight into the output buffer: the
block loop never advanced the source pointer and copied numSamples*2
int16s out of a 2048-sample stack buffer, a stack OOB read for any push
over 1024 frames. Realloc failure drops samples instead of crashing and
log_cb is NULL-checked. retro_serialize refuses to write a state larger
than the frontend's buffer, retro_get_memory_data/size return nothing
until PSP memory is mapped, deferred unserialize no longer leaks the
previous buffer, and the cheat entry points stop leaking a CWCheatEngine
per call.

The emu thread acknowledges a pause with a CAS so it cannot clobber a
concurrent quit request. EmuThreadStop stops and joins from any live
state, so unloading while paused no longer leaves a thread running over
freed state. EmuThreadPause pumps queued frames until the thread parks,
via a new non-blocking GLRenderManager::HasQueuedThreadWork() check,
instead of draining one frame, which deadlocked when the thread was
blocked on a readback sync in the next frame. retro_run resumes a paused
thread and pauses it before check_variables() rewrites g_Config,
retro_reset stops it before PSP_Shutdown(), and the sleep_ms(4) after
resume is gone.
2026-10-13 13:50:05 +02:00
Henrik Rydgård 0af0d86470 Merge pull request #22542 from hrydgard/faster-build
Faster CMake builds: libc++ transitive includes, less glslang/SPIRV-Cross, include cleanup
2026-10-13 12:57:43 +02:00
Henrik RydgårdandClaude Opus 5.5 a361dfc490 Drop unused Config.h and SmallDataConvert.h includes
Config.h is edited about 100 times a year and each edit recompiled 198
files; now 172. SmallDataConvert.h pulls in arm_neon.h and was included by
headers that don't use it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 12:13:42 +02:00
LibretroAdmin 664f6ff5ad D3D11: work on a context that refuses ID3D11DeviceContext1
D3D11DrawContext takes its base context from the deviceContext
argument, and the blend state is bound through the base context, which
accepts an ID3D11BlendState1 as is. ID3D11DeviceContext1 is optional:
RetroArch's threaded video hands v1 cores a deferred-context proxy that
answers ID3D11DeviceContext only, and the core now starts on it.
2026-10-13 02:23:48 +02:00
Henrik RydgårdandClaude Opus 5.5 0886063fba Vulkan: Pre-rotate reverse landscape again
The 270 degree pre-rotation was disabled in e6b7817cc6 for #15773 (UI
disappearing in reverse landscape on a Poco X3 Pro), which left reverse
landscape to the compositor, and every present returning SUBOPTIMAL. Try
it again: rotation is handled in place now, and the stale extent after a
rotation is fixed, so it needs retesting on Adreno.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 18:59:06 +02:00
Henrik RydgårdandClaude Opus 5.5 f7ec86c5d5 Android Vulkan: Don't trust a stale surface extent after a rotation
Since rotation is handled in place, the swapchain is recreated right
after surfaceChanged, and the surface can still report the previous
orientation's extent. On a Galaxy S8+ this happened in about one in four
rotations, leaving a portrait swapchain on a landscape window or the
other way around, squashed into half the screen until the next rotation.

If the extent is exactly the window size transposed, use the window size.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 17:22:41 +02:00
Henrik RydgårdandClaude Opus 5.5 1467ef24a7 GL: Report readback failure, and convert from the format actually read
CopyFramebufferToMemory always returned true, even when glReadPixels
failed, and then copied out whatever the readback buffer held from before.
Depth was labeled D24_S8 while GL reads it as float, and the conversion
ignored the source format, so only a D32F destination came out right.

Record the format and the result of each read, convert from that, and
return it. That also fixes the image readback reusing the aspect of an
earlier framebuffer readback.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-12 12:15:56 +02:00
Henrik RydgårdandClaude Opus 5.5 f04d99e952 Vulkan: Reject a delayed readback of the backbuffer
The backbuffer is a different image every frame, so there's no earlier copy
of it to return, and the lookup fell through to the sync readback buffer.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-12 12:15:56 +02:00
Henrik RydgårdandClaude Opus 5.5 b287b0fab5 GL: Fix the pre-4.5 sub-rectangle texture readback
The repack loop used the texture height instead of the row, and the pack
row length was the rect's right edge while glGetTexImage writes whole rows.
Only reachable from the texture debugger.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-12 12:15:56 +02:00
Henrik RydgårdandClaude Opus 5.5 d15e094ed6 Vulkan: Key delayed readbacks by position and aspect too
The OLD_DATA_OK readback cache was keyed by framebuffer and size only, so
two equal-size rects of one framebuffer shared a buffer and each read got
whichever was copied last. Add x, y and the aspect to the key.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-12 12:15:56 +02:00