Commit Graph
5864 Commits
Author SHA1 Message Date
Henrik Rydgård e46bd61e2e When taking a screenshot, clear all notifications except achievements. 2026-10-11 17:40:01 +02:00
Henrik RydgårdandClaude Opus 5.5 9f1278b5d6 iOS: Link MoltenVK statically in sideload builds too, raise target to 15.0
Sideload builds used to dlopen a bundled libMoltenVK.dylib, which was
only worth having while they could still fall back to OpenGL. Now both
variants link the static MoltenVK from ios/MoltenVK and call Vulkan
directly. PPSSPP_VK_STATIC replaces the IOS_APP_STORE checks in the
Vulkan loader (the iOS libretro core still loads at runtime).

The static MoltenVK needs iOS 15.0, so that's now the deployment target
for all iOS builds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 16:13:28 +02:00
Henrik RydgårdandClaude Opus 5.5 5704b972e7 iOS: Remove OpenGL support from the app
The app now always uses the Metal view controller with Vulkan (MoltenVK),
for both the App Store and sideload builds. The GL view controller is gone,
and Common/GPU/OpenGL (except GLFeatures, used by the shader generators) and
GPU/GLES are no longer compiled for it. PPSSPP_API_ANY_GL is now off for
all of iOS except the libretro core, which still uses GLES.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 16:13:28 +02:00
Henrik Rydgård f70df399ab Merge pull request #22494 from hrydgard/jit-shadow-block-map
JIT shadow block map: Keep JIT block entries and replacement hooks out of PSP memory
2026-10-11 12:46:42 +02:00
Henrik Rydgård 7572ac05ca Screenshot code cleanup, fix issue where the notification would be in a second fast screenshot
Fixes #22497
2026-10-11 10:56:43 +02:00
Henrik RydgårdandClaude Opus 5.5 6d122f416e Keep JIT block entries and replacement hooks out of PSP memory
The JITs used to find a block by overwriting its first instruction with an "emuhack" holding
the offset into the code space, and replacement functions installed their own emuhack the same
way. Anything that read code (savestates, the debugger, memory dumps, cheats, the function
hasher, games reading their own code) had to translate them back.

Blocks now live in a block shadow: one u32 per guest instruction, indexed by the PC masked with
BLOCK_SHADOW_MASK (1GB on 64-bit, folding the kernel and uncached mirrors; 256MB on 32-bit, iOS
and Switch, where address space is short), with the low bits cleared so a misaligned PC can't
read across two entries. Only the ranges under memory views are committed, so a bad PC still
faults in the dispatcher, and the shadow is reserved before the memory map so it can't land in
a gap of the 4GB window. Entries hold full 32-bit offsets, lifting the 16MB limit. The
dispatchers on every backend, and the IR interpreter's loop, look blocks up there.

A hooked instruction without a compiled block gets the JIT's hook value in its entry: the offset
of the dispatcher's compile path (1 for the interpreters, the IR arena keeping that offset
empty), so dispatching there compiles a block, and the frontends substitute the hook. Compiling
replaces the entry with the block's, and destroying the block puts the hook value back. The hook
value is rewritten when the JIT changes or regenerates its dispatcher.

Hooks live in a map keyed by the masked address, and are dropped when the game overwrites the
hooked instruction, or when an icache invalidation finds the function's hash (immediates masked,
as when matching it) changed. Those checks run when queued invalidations are processed, since
cheats and the debugger queue them before writing. Installing and removing hooks invalidates
deferred, as it happens in HLE calls that can run inside a block. The cache instruction goes
through MIPSState, so it drops changed hooks too.

With clean memory, the save/clear/restore around savestates, memory reads and dumps goes away,
as do the source-range invalidations in the memcpy replacements, sceDmac and the cheats.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-11 10:33:30 +02:00
Henrik RydgårdandClaude Opus 5.5 6cf4872e78 Android: Hand microphone input over on the CPU thread, like the other hosts
The Oboe input callback used to call Microphone::addAudioData directly,
writing PSP memory from the audio thread. Now it only pushes samples
into a SampleRing, and pollRecording drains it on the CPU thread.
Starting and stopping still go through Java, which asks for permission.

Every host with a microphone polls now, so sceUsbMic drops what only the
push path needed: QueueBuf's recursive mutex, restarting the mic on a
device change, and the startMic parameter nobody read. Also removes the
unused System_AudioRecordingState and Microphone::isNeedInput.

The Java microphone_command branch didn't return true, so every command
logged "processCommand failed".

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-10 18:49:21 +02:00
Henrik RydgårdandClaude Opus 5.5 1e5a54e7b5 Move the microphone's lock-free sample ring to Common/Audio/SampleRing.h
Android is about to need the same one.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-10 18:49:21 +02:00
Henrik RydgårdandClaude Opus 5.5 b4ec29111b Microphone: Let the host say whether it wants polling, rather than checking for SDL
The CPU-thread polling added for SDL was keyed off defined(SDL), which
headless and Core are built with too, hence the extra headless check.
Ask the host instead, with SYSPROP_MICROPHONE_NEEDS_POLLING, so other
hosts can hand their input over the same way.

startMic now sends the same startRecording command for every non-Win32
host, taking the rate from curSampleRate like the SDL path did. The
Android path read it from param, which leaked, and which is null when
coming from onMicDeviceChange.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 17:20:48 +02:00
Henrik Rydgård 7b716e6f33 Merge pull request #22459 from chriopter/sdl-microphone
SDL: Add microphone capture and device selection
2026-10-10 17:01:04 +02:00
Henrik Rydgård bd3e55a104 Merge pull request #22487 from hrydgard/android-lifecycle-fixes
Android lifecycle fixes, file sharing support (unused)
2026-10-10 16:43:58 +02:00
Henrik RydgårdandClaude Opus 5.5 77a6abfe6e Add System_ShareFile, for handing a file to another app
Offers a file through the system share sheet, for things like sending a
zipped savestate to a friend. Android for now (SYSPROP_SUPPORTS_SHARE_FILE):
content:// paths from a picked folder are shared as they are, and plain paths
through a FileProvider. Either way the receiving app can read just that file.
With no MIME type given, it's guessed from the extension.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-10 15:59:42 +02:00
Henrik Rydgård 6779143827 Merge pull request #22486 from hrydgard/softgpu-x86-opt
SoftGPU: Do an optimization pass on x86-64 too
2026-10-10 15:48:04 +02:00
Henrik RydgårdandClaude Opus 5.5 34da53de5a Android GL: Support PresentMode::IMMEDIATE with eglSwapInterval
Now that EGL is ours rather than GLSurfaceView's, the swap interval can be set,
so turning vsync off (and fast-forward without skipping flips) works on Android
GL too. Not in VR, where OpenXR presents, and not with a config whose minimum
swap interval is 1.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-10 15:45:27 +02:00
Henrik RydgårdandClaude Opus 5.5 16a5046124 SoftGPU: Read bilinear CLUT and 8888 texels in vector lanes
The four-pixel bilinear sampler read its sixteen texels one at a time,
each with its CLUT lookup and index transform, and loaded them as
vectors afterwards. For CLUT4 and CLUT8 with a 32-bit palette and no
index shift (by far the most common), and for 8888, the texels are now
gathered in lanes: the index bytes, the nibble, the mask and start, then
the palette entries. CrossSIMD gets Vec4S32::Gather and GatherU8 for it,
one load per lane. LocoRoco executes 5.5% fewer instructions, Burnout 3%.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-10 15:01:49 +02:00
Henrik RydgårdandClaude Opus 5.5 f02936575d SoftGPU: Multiply small lane values with 16-bit multiplies
SSE2 has no 32-bit lane multiply, so Vec4S32::Mul is emulated with two
pmuludq and shuffles, and the bilinear sampler spent a tenth of its time
there. The texel row offsets, the texture functions, fog and blending
only multiply values that fit 16 bits, which Mul16 does in one pmaddwd.
The bilinear lerp of two channels packed as 16-bit halves uses the new
MulHalves (pmullw), which multiplies both at once.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-10 13:44:48 +02:00
Henrik RydgårdandClaude Opus 5.5 ce47944216 SoftGPU: Gather a span's UV plane values in registers
GEUVSpan stored the four 64-bit plane values, and the reciprocal table's
entries, to arrays one by one and loaded them as vectors, a store
forwarding stall each time, which took most of its time on x86.
CrossSIMD gets LoadS64Low (the low halves of four 64-bit values) and
LoadPairs (four pairs of 32-bit values split into two vectors) to do it
without memory. Wipeout runs about 2% faster.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-10 13:44:48 +02:00
Henrik RydgårdandClaude Opus 5.5 1752a2e53b SoftGPU: Transform a vertex's screen position in vector lanes, without stalls
ReadVertex took the GE's viewport arithmetic one coordinate at a time,
with a reciprocal of w for each, and stored the position, the UVs and
the whole vertex in pieces that were then reloaded with wider loads,
each a store forwarding stall. x, y and z now go through one GERecip and
the four-lane multiply and add (the scalar path stays for non-finite
input and zero or denormal w), the position is loaded as a vector, and
the vertex is written straight into its destination.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-10 13:44:48 +02:00
Henrik RydgårdandClaude Opus 5.5 834c3f00ee Android: Replace OpenSL ES audio with Oboe
Oboe uses AAudio on Android 8.1+ and falls back to OpenSL ES on older
devices. Output and microphone input both go through it. The mic now gets
resampled by Oboe from the device's rate to whatever the game asks for.

New: when a stream is disconnected (headphones unplugged, Bluetooth device
going away), it's reopened on the new default device instead of going
silent.

Oboe 1.11.0 is added as a submodule, built through ext/oboe-build for CMake
and as a module in Android.mk.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-10 12:13:58 +02:00
Henrik RydgårdandClaude Opus 5.5 1fe8071875 Vulkan: Apply the texture LOD bias in the shader where samplers ignore it
MoltenVK doesn't support a sampler mipLodBias (the portability subset's
samplerMipLodBias is false), so the PSP's LOD bias did nothing on Mac and
iOS, and biased textures came out a mip level blurrier than on OpenGL and
the PSP: MLB's signs and crowd, Burnout Legends, MediEvil, Ridge Racer 2.
Query the portability subset, and where the bias is missing, pass the auto
mode bias to texture() from a uniform instead.

VK_ENABLE_BETA_EXTENSIONS, which declares the portability subset, is now
defined for App Store iOS builds too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 01:04:23 +02:00
Henrik Rydgård 0ac523c4b5 Merge pull request #22475 from hrydgard/cars-brightness
Tantalus games: Fix brightness, fix vertical stripes
2026-10-09 19:22:00 +02:00
Henrik Rydgård 324e6c3954 Merge pull request #22476 from hrydgard/community-security-fixes
Two security fixes by acts-1631, and more found by Claude
2026-10-09 18:55:57 +02:00
Henrik RydgårdandClaude Opus 5.5 e705d62f0b GL: Only set GL_DEPTH_STENCIL_TEXTURE_MODE where stencil texturing exists
It needs GL 4.3 or GLES 3.1. On Apple's GL 4.1 every depth texture bind (Cars
Race-O-Rama's depal from depth, for one) raised GL_INVALID_ENUM, which then
showed up at whatever checked glGetError next. Without the mode a
depth-stencil texture samples depth, so nothing else changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 18:11:59 +02:00
Henrik RydgårdandClaude Opus 5.5 4be43ce23a GL: Forget the cached framebuffer bindings after deleting framebuffers
GLRFramebuffer's destructor binds the default framebuffer, while the queue
runner's cache still had the previous one as bound. A following readback from
that framebuffer skipped the bind and read the default framebuffer instead.
Cars Race-O-Rama and MX vs ATV Reflex recreate framebuffers every few frames,
so their luminance readback came back as zeros on about half the frames and
their brightness adaptation went wrong on GL. Now GL reads the same values as
Vulkan.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 17:22:53 +02:00
Henrik RydgårdandClaude Opus 5.5 4202368b1e Vulkan: Look up multisampled framebuffers through a CLUT by their first sample
With MSAA, a framebuffer textured as CLUT indices was read resolved, so the
indices along edges were averages of the samples and looked up wrong colors.
Dragon Ball Z: Tenkaichi Tag Team draws its outlines from a CLUT32 lookup of
its framebuffer, and they came out noisy and miscolored. Copy each pixel's
first sample with texelFetch into a temporary buffer and look up that.

This needs the multisampled color images to be sampled, and transitioned
back to attachment layout before rendering to them again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 15:49:15 +02:00
Henrik RydgårdandClaude Opus 5.5 6e0d6f4357 Savestates: Terminate fixed-size name strings on load
Add DoCString() for standalone fixed-size strings, and
TerminateLoadedCString() for names inside structs that are loaded whole.
These all get used as C strings (GetName(), logs, the debugger), and are
always terminated when created. Module names are copied from the ELF with
strncpy, so terminate those on creation too.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-09 15:32:06 +02:00
Henrik Rydgård 831acb099e Allow specifying a cap on the number of threads used in ParallelLoop 2026-10-09 14:33:58 +02:00
Henrik Rydgård bdf05ae874 Merge pull request #22464 from hrydgard/softgpu-spans
SoftGPU: Render in 4x1 spans instead of 2x2 quads, like the PSP. Optimize span functions with templating
2026-10-09 11:38:52 +02:00
Henrik RydgårdandClaude Opus 5.5 d9b36b7e46 CrossSIMD: Keep the C++ fallback's integer ops defined, like the SIMD ones
The fallback (which RISC-V uses) did its integer arithmetic in plain int,
where signed overflow and left shifts of negative values are undefined,
and converted out of range floats with a cast, which is undefined too:
RISC-V gives INT_MAX for NaN where SSE gives INT_MIN and NEON 0. With GCC
on RISC-V that broke GEUVSpanCore in the GEMath unit test. Now the
arithmetic wraps through uint32_t, like the SIMD instructions, and an out
of range or NaN conversion gives INT_MIN, like SSE.

Also skip GERowSum's zero terms before computing their shift, as their
exponent can be INT_MIN. Found with -fsanitize=undefined on the fallback.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 11:12:48 +02:00
Henrik Rydgård bd8f37bf0f Merge pull request #22465 from hrydgard/hw-ge-blends
HW backends: Improve accuracy of blending
2026-10-09 11:05:28 +02:00
Henrik RydgårdandClaude Opus 5.5 770dc0fe43 SoftGPU: Early depth test of staged spans in vector lanes
CheckDepthTestPassed4 only has an SSE path, so on ARM it compared lane by
lane. Adds Vec4S32::LoadU16 for the depth values.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-09 10:34:39 +02:00
Henrik RydgårdandClaude Opus 5.5 d24d420057 SoftGPU: Sample a span's four pixels in vector lanes
When the four pixels share their mip level (always, for a triangle's
span), their texel coordinates, clamping or wrapping and texel offsets
are computed in vector lanes, and only the texel and palette reads go
one by one. Adds Vec4S32::Min.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-09 10:34:39 +02:00
Henrik RydgårdandClaude Opus 5.5 d0baab5614 Compute FIXA/FIXB blends like the GE
The GE's blend terms are floor((2c+1)(2f+1)/1024), which truncates, while the
hardware multiplies by f/255 and rounds, so every pass of a fixed color blend
chain came out up to a level brighter. God Eater Burst's bloom is many such
passes and ended up far too bright.

For additive FIXA/FIXB blends that aren't just 0 and 0xFFFFFF, the shader
now computes the src term exactly and lowers it by 0.5 - (2B+1)/1024, and the
blend constant is the GE's dst factor (2B+1)/512, so the hardware's rounding
of the sum lands on the GE's floor (except where the src term would go below
zero). Blend constants are passed as floats to make that possible.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 09:09:36 +02:00
Henrik RydgårdandClaude Opus 5.5 49674c3f25 GEMath: Add GEDot3 and GENormalize4 on Vec4F32
GEDot3 forms one term per lane and sums across the lanes, about 1.5-2x
as fast as the scalar GEDot. Adds Vec4S32::GetLane.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-09 09:07:12 +02:00
Henrik RydgårdandClaude Opus 5.5 a9d8b6d3c5 GEMath: GERowSum4 takes its left operands as one vector
They're truncated to float24 with one AND, and their exponents, fractions
and signs worked out for all terms at once, each term then broadcasting its
lane (new CrossSIMD Vec4S32::SplatLane). The term count becomes a template
parameter so the lanes are constants. About 75-83 M transforms/s on an M1,
from 68; results unchanged.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-09 09:07:12 +02:00
Henrik RydgårdandClaude Opus 5.5 c4ce2e3635 SoftGPU: Use GERowSum4 for the transform's row sums
The four clip space rows of a vertex, the texture matrix's three, the world
normal's three, the vector to a light, and each row of the combined matrix
now take one GERowSum4 call. Same results (pspautotests, frametests and
the probe regression set unchanged).

GERowSum4 scales its result by adding to the exponent instead of
multiplying, and CrossSIMD gets a Vec4S32::Max for the largest lsb. About
68 M transforms/s against 26 for four scalar GERowSums on an M1 (a pure
NEON version with per-lane shifts did about 79); replaying the frametest
dumps gets only about 1% faster, up to 11% on Taiko no Tatsujin.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-09 02:57:09 +02:00
Chriopter 1670f5d1f9 SDL: Add microphone recording and device selection. 2026-10-09 00:18:31 +02:00
Henrik Rydgård c7a2cb6f63 Merge pull request #22458 from hrydgard/android-small-fixes
Android: Small fixes from a Claude code review
2026-10-08 21:17:00 +02:00
Henrik Rydgård f0e0368a77 Merge pull request #22456 from hrydgard/spline-simd
SIMD-optimize splines and beziers
2026-10-08 20:27:34 +02:00
Henrik RydgårdandClaude Fable 5.1 ce19265949 Don't save log levels set from the command line
--loglevel, -d and -v are applied to the channels directly, and the next
config save wrote them to [Log], permanently replacing the user's levels.
The other command line options avoid this with DoNotSaveSetting.

The debugger had the same problem and a flag for it. Generalize that one
(NotifyChannelsChangedByDebugger -> DoNotSaveChannels) and set it here too.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-08 20:11:23 +02:00
Henrik Rydgård 0e3dfcef9d Merge pull request #22454 from hrydgard/android-activity-lifecycle
More Android activity lifecycle fixes
2026-10-08 20:10:14 +02:00
Henrik RydgårdandClaude Opus 5.5 08c1a6907a Don't chmod existing parent directories in CreateFullPath
CreateDir's "repair" of existing directories ran for every component
CreateFullPath walked through, so creating any directory under the
user's home made the home directory itself group-writable (775). That
breaks zsh's compinit ("insecure directories") among other things.

Only repair the permissions of the target directory itself, when it
already exists.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-08 19:13:18 +02:00
Henrik RydgårdandClaude Opus 5.5 802b52d875 CrossSIMD: Add an arithmetic Vec4S32::Shr and Vec4S32FromBits
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-08 19:00:19 +02:00
Henrik RydgårdandClaude Fable 5.1 8b829b3097 Android: Share one TextRenderer class ref between TextDrawers
Each TextDrawerAndroid took a global ref to the TextRenderer class and
never released it (the release had been commented out as crashing), plus
leaked the local ref it was made from. A TextDrawer is created every time
graphics come up, so that was one of each per resume.

Look the class up once and keep it. Also initialize the method IDs, which
IsReady() read uninitialized if the class was missing, and don't walk into
a null array if renderText throws.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-08 18:54:46 +02:00
Henrik RydgårdandClaude Fable 5.1 2f6bb8d55e Android: Don't leak the activity on recreate, and hand it over safely
registerCallbacks took a new global ref to the activity on every onCreate
without releasing the previous one, and unregisterCallbacks only runs on a
real shutdown. Since rotation and resizing recreate the activity, each of
those leaked the old one, view hierarchy and all.

Releasing it means other threads can no longer use the raw global ref,
which had been copied into AndroidStorage and TextDrawerAndroid and was
read from the emu thread and IO threads. They now take a local ref under a
mutex (Android_GetActivity, and the equivalent inside AndroidStorage for
the shortcut activity's sake), so a call in flight keeps its activity
alive while the UI thread swaps in the new one.

The "no activity" bail is a null check on that local ref rather than a
separate HasActivity() test beforehand. Between such a test and the call
the UI thread can swap the activity out, and we'd hand null to a Java
method that wants a Context. The one remaining HasActivity() caller never
passes the activity on, so it has nothing to race with.

The ContentUri class ref and method IDs are now only looked up once,
instead of leaking a class ref per activity.

Headless and the unit test executable link Common but not app-android.cpp, so
they get a stub for this the same way they already do for getEnv and findClass.
Without it the Android CI build fails at link time, where a normal gradle build
is perfectly happy.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01152igCdjTNhT29qvphPyPC
2026-10-08 18:54:46 +02:00
Henrik RydgårdandClaude Fable 5.1 35c586b27b Android: Stop leaking JNI local references on native threads
Local refs are only freed automatically when a native method returns to
Java. The content URI functions mostly run on threads we attached
ourselves, where nothing ever freed the strings they created - two per
file open, one per exists check and so on. From Android 8 that's a slow
leak; before it the table has 512 slots and overflowing it aborts.

Add JNILocalFrame (PushLocalFrame/PopLocalFrame) and use it in all of
AndroidStorage.cpp, in the camera device list, and free the name string
in findClass.

Also: Android_GetFileInfo never released its GetStringUTFChars buffer, a
native leak on every call, and Android_ListContentUri called
GetArrayLength on a null array if the Java side returned none.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-08 18:54:46 +02:00
Henrik RydgårdandClaude Opus 5.5 84b674f4d8 CrossSIMD: Add MulAdd and Vec4S32FromF32Round
MulAdd is fused on ARM64 and LoongArch, which always have it. Rounding to
nearest is a single instruction on every ISA except ARM32.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-08 18:12:10 +02:00
Henrik RydgårdandClaude Opus 5.5 c23cd3c64f CrossSIMD: Add RecipSqrt, Select and Vec4F32FromBits
RecipSqrt is exact (sqrt and divide) except on ARM32, which has no vector
divide and refines vrsqrte with two Newton steps.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-08 18:11:07 +02:00
Henrik RydgårdandClaude Fable 5.1 7f751c7ba1 Vulkan: Don't recreate the swapchain over a pre-transform we chose ourselves
InitSwapchain deliberately refuses the 270 degree pre-transform. The driver
then reports VK_SUBOPTIMAL_KHR on every present, which we counted towards
recreating the swapchain - and the new one got the same treatment, so in
reverse landscape we rebuilt it every single frame.

Remember that the mismatch is ours and stop treating SUBOPTIMAL as a reason
to recreate when it is. Any other cause still counts.

Split out of the Android rotation work, where it was found: it's a
platform-independent Vulkan fix and stands on its own.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01152igCdjTNhT29qvphPyPC
2026-10-08 17:15:09 +02:00
Henrik RydgårdandClaude Opus 5 812d8f4c09 Android GL: Delete the JavaGL path, leaving only native EGL
The EGL path from the previous commit is now the only one, so GLSurfaceView
and everything that existed to serve it are gone:

- NativeRenderer.java and NativeGLSurfaceView.java, deleted.
- The javaGL branches in PpssppActivity. GL and Vulkan now share one
  lifecycle: a plain SurfaceView, startRenderLoopThread() when the surface
  arrives, joinRenderLoopThread() when it goes away.
- The "androidJavaGL" queryConfig, and the AndroidNativeEGL setting and its
  developer-tools checkbox.
- displayInit/displayRender, and with them the "Second time, joining the
  emuthread and starting it up again" dance - restarting the emu thread from
  inside a JNI callback because a lost context was only observable as a
  second onSurfaceCreated.
- GraphicsContext::NotifyContextLost, which existed solely because that join
  drained the render queue on a thread with no current context. The render
  thread now joins the emu thread with the context still current, so the
  drain is real work and nothing has to be skipped.
- g_emuThread, now that nothing starts an emu thread outside the render loop.

Three things displayInit/displayRender did that nothing else did, moved
rather than dropped:

- EnterVR and the per-frame StartVRRender/UpdateVRInput/FinishVRRender hooks
  now live in RenderLoopThread's GL branch. Java's GL thread could simply
  return early and wait for the next vsync when the VR session was inactive;
  this loop drives itself, so that case sleeps 16ms instead of spinning.
  Not tested - no headset here.
- RECREATE_VIEWS after the emu thread starts, as before. Vulkan never did
  this, so it's a candidate for removal when the two paths converge.
- The thin3d error callback, which surfaces GL errors as OSD messages. That
  one moved into AndroidEGLGraphicsContext::InitSurface, next to the rest of
  the context's own setup, matching WindowsGLContext.

Tested on a nubia Z2464N (Unisoc UMS9632, Mali-G57, Android 15): cold start,
UI, booting a homebrew, backgrounding and resuming both in the menu and
in-game, and a density change, which destroys and recreates the activity the
way a rotation does. Every cycle tears the EGL context down and builds a new
one, and the renderer comes back with FBOs and textures intact. The config
ladder's first choice, RGBA8888 D24S8, is what the Java chooser landed on
here too.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01152igCdjTNhT29qvphPyPC
2026-10-08 15:22:25 +02:00