Commit Graph
50328 Commits
Author SHA1 Message Date
Henrik RydgårdandClaude Opus 5.5 05de369578 POPS: Upload images the emulator writes to VRAM with the CPU
The PS1 emulator writes an image that comes through the GP0 data port
(rather than by DMA) into VRAM one pixel at a time with the CPU. The
hardware renderers never saw those pixels, so a later copy out of the PS1
VRAM framebuffer read stale texels.

The two row ends of that loop now go to an HLE call, which uploads the
finished rectangle with a new PerformWriteRectFromMemory().

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 16:08:25 +01:00
Henrik Rydgård 5b5465c78b Merge pull request #22941 from hrydgard/more-ui-work
More UI work
2026-11-05 16:08:06 +01:00
Henrik Rydgård f2b7732780 Another hint, misc 2026-11-05 13:55:18 +01:00
Henrik Rydgård bfb3f0f23f Turn off the quick scroll bar from #22796 by default on desktop 2026-11-05 13:39:40 +01:00
Henrik Rydgård 307e43b8de Firmware and savedata screen tweaks 2026-11-05 13:34:48 +01:00
Henrik RydgårdandClaude Opus 5.5 d5bded2dbd Translations: Add the why-disabled setting messages
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 12:25:02 +01:00
Henrik Rydgård fd81cf7e6b Merge pull request #22937 from hrydgard/skinned-minmax
GPU: Skip the min/max depth discard on skinned draws that stay inside the range
2026-11-05 12:22:55 +01:00
Henrik Rydgård 546e00c697 Merge pull request #22934 from hrydgard/vr-vulkan
VR: OpenXR on Vulkan, with single-pass stereo (Quest)
2026-11-05 12:22:43 +01:00
Henrik RydgårdandClaude Opus 5.5 1cb9b1d3ef UI: Allow a custom why-disabled message on views and setting items
View::SetDisabledReason, and SettingItem::DisabledReason for settings
pages. When set, it's shown instead of the message worked out from the
enabled pointer, and it also works for items with enabled callbacks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 11:58:36 +01:00
Henrik RydgårdandClaude Opus 5.5 6f709515e3 UI: Say which setting is in the way when activating a disabled item
Tapping a disabled item, or pressing accept on a focused one, shows an
info message naming the setting that has to be on or off, when the item
is disabled by one (SetEnabledPtr/SetDisabledPtr on a config setting).
Items disabled through a callback show nothing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 11:53:32 +01:00
Henrik RydgårdandClaude Opus 5.5 d5e0bf2ef5 UI: Let disabled items take keyboard focus
They still ignore keys and touches. A focused disabled item keeps its
disabled background and gets a thin outline in the focus color instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 11:48:27 +01:00
Henrik RydgårdandClaude Opus 5.5 e0efa821c7 UI: Tighten the space between a setting's title and its hint
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 11:39:35 +01:00
Henrik RydgårdandClaude Opus 5.5 13d14e9ad2 GPU: Give skinned draws the min/max depth discard only when they reach past the range
Without clip distances, the depth range's min/max is a discard in the fragment shader. Skinned and morphed draws
skip the bounding box test, so they got it whenever a game narrowed the range, though skinned ones are rarely
past it, and the discard costs fill rate. Nearly every draw in Tekken 6 is skinned, and without the discard on
them it ran 25% faster at 4x on a Mali-G57 (OpenGL), and 15% faster on a PowerVR GE8320 (OpenGL).

A skinned draw now gets MinMaxZCheck instead. At flush, its vertices are decoded into RAM, where they go anyway,
and it gets the discard only if their screen Z reaches past the range. Morphed draws are rare and keep it.

On a GPU without clip distances, the whole framedump set renders the same as before. Only 0.6% of the skinned
vertices still get the discard, in ten dumps (Pursuit Force's split depth range among them).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 11:35:42 +01:00
Henrik RydgårdandClaude Opus 5.5 eeb25d0f57 GPU: Remove leftovers of hardware skinning
The vertex decoder always skins now (except for the software renderer's own), so NormalizeVertices never sees
weights to apply, and the GLES shaders have no bone uniforms to look up.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 10:36:41 +01:00
Henrik RydgårdandClaude Opus 5.5 ef3c7985ee UI: Draw setting hints inside their Choice or CheckBox
ClickableItem gets a hint text, drawn small under the item's own content,
so it highlights and clicks along with it instead of being a separate
SettingHint view below. Settings pages attach it with .Hint() on the item
it belongs to, and search matches it as part of that item.

SettingHint stays for the few hints that have no item to go in: custom
settings views that aren't ClickableItems, and cheat comments under a title.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 10:35:23 +01:00
Henrik Rydgård 9f5c502d8f Merge pull request #22936 from hrydgard/cpu-clip
GPU: Clip and cull on the CPU while keeping hardware transform, with CPU-lit clipped triangles
2026-11-05 10:04:43 +01:00
Henrik RydgårdandClaude Opus 5.5 05b5ef163f GPU: Light the triangles the CPU clips, in a second vertex stream
The GE clips lit triangles after lighting, interpolating the lit colors. So that lit draws can take the CPU clip
path too, the CPU now lights the ends of the edges it clips, once per vertex, and the vertices clipping makes take
the interpolated colors. They come in a second vertex stream, 8 bytes a vertex (cpulit0 for color0, cpulit1 for
color1, its alpha set where they are to be used), and the vertex shader uses them in place of its own lighting
there (VS_BIT_CPU_LIT). The original vertices keep the shader's lighting.

Every lit draw gets the stream where the CPU lights the triangles it clips (no clip distances, or a game flagged
CorrectCullAfterClip), with a static buffer of zeroes when nothing is clipped, so whether a draw is clipped
doesn't change its pipeline. The colors take attribute locations 3 and 4, those of the skinning weights, which
are never vertex attributes, to stay within GLES2's eight.

Implemented for Vulkan, OpenGL, D3D11 and Metal. Shader cache versions are bumped (new shader ID bit and vertex
format bit).

Regression risk: a new vertex shader input and second vertex buffer on GPUs without clip distances, every lit
draw on those. Tested on Mali (G52, G71, G78) and PowerVR (BXM, GE8320) phones.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 00:53:12 +01:00
Henrik RydgårdandClaude Opus 5.5 e1ba8145db GPU: Clip and cull draws crossing the Z planes on the CPU, keeping hardware transform
Where the GPU can't clip and cull like the GE (no clip or cull distances, the near plane not on the minimum Z,
or a game flagged CorrectCullAfterClip), a draw whose bounding box crosses a Z plane used to go to software
transform. Mixing that with hardware transform on other passes over the same surface made their depth tests
fail on PowerVR.

Now the CPU decodes such a draw to RAM, clips its triangles at the near plane like the GE (into one triangle or
two), drops those entirely beyond the far plane, and range culls the vertices clipping makes. The vertices,
including the new ones interpolated from the decoded ones, then go through hardware transform like the rest.

Lit draws, flat shading and normal-based texgen aren't linear in the decoded vertices, so they're left to the
clip distance where there is one, and culled only when culled whole. Without clip distances, lit draws still
take software transform.

Regression risk: draws that cross a Z plane on GPUs without clip or cull distances, and the flagged games, now
take a new path, on every backend.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 00:42:26 +01:00
Henrik RydgårdandClaude Opus 5.5 39b7e8dc2f Vulkan, OpenGL, Metal: Let draws bind a second vertex stream
Vulkan pipelines can have a second vertex binding, and draws a second vertex buffer for it. OpenGL input
layout entries can come from a second buffer, with its own stride, given with the draw. Metal gets a second
vertex buffer index. Nothing uses them yet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 00:29:16 +01:00
Henrik Rydgård 0051dca319 Merge pull request #22935 from hrydgard/gpu-misc-fixes
GPU: Fix black frame dump replays, the depth clamp range, and DBZ's culling
2026-11-05 00:20:40 +01:00
Henrik RydgårdandClaude Opus 5.5 1676765456 GE dump: Only draw the zero-normal reset patch on the software renderer
Only the software renderer keeps the sign it resets, and on the hardware renderers the patch made about 70 of
the frame dumps replay black (MX vs. ATV Untamed among them).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 23:52:20 +01:00
Henrik RydgårdandClaude Opus 5.5 48aa69c748 Compat: Clip and cull DBZ Tenkaichi Tag Team after clipping
With clip and cull distances, the GPU drops ground textures on modded maps (#18561): 25551 pixels off the PSP
render on a Pixel 3a, against 1987 with the flag.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 23:06:22 +01:00
Henrik RydgårdandClaude Opus 5.5 cabbea312b GPU: Count only what's drawn in the Z range for the depth clamp
With depth clipping, what's behind the near plane is clipped away, but its screen Z still counted, and behind
the eye it's meaningless. Every draw crossing the near plane then took the fragment shader depth clamp on GPUs
without depth clamp, whose depth doesn't match the other draws' rasterized depth, so equal-depth passes failed
(Monster Hunter 3rd HD's ground on PowerVR GLES). The near plane's Z counts instead, and doesn't need the clamp
at exactly zero, where losing what rounds below is the near clip.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 23:06:12 +01:00
Henrik RydgårdandClaude Opus 5.5 b62bcc9e12 Vulkan: Allow it on Motorola's PowerVR BXM-8-256 phones with any driver
The entry hid the Vulkan option on those (Moto G54 and similar) with drivers before 0x005d9576, for the crash
in #18681. Vulkan is the default there, and works on a G54 with that driver. The list is only used on Mac
now, so it and its check are only built there.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 23:05:36 +01:00
Henrik RydgårdandClaude Opus 5.5 5a5c11f984 Headless: Print the number of pipelines created
Vulkan and Metal pipelines, and OpenGL programs, including those precompiled from the shader cache. Benchmark
times include the compiles, so this shows when two builds differ in how many they need.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 23:05:36 +01:00
Henrik RydgårdandClaude Opus 5.5 77e417e59f CrossSIMD: Make the Store functions const
They only write to the destination.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 23:05:36 +01:00
Henrik Rydgård c68f43c731 Merge pull request #22911 from hrydgard/texcache-index
TextureCache: Find the textures a block transfer overlaps through a flat index
2026-11-04 19:28:05 +01:00
Henrik Rydgård 9366aa7dc2 Merge pull request #22933 from hrydgard/more-ui-work
Fix a bunch of UI bugs
2026-11-04 19:12:03 +01:00
Henrik RydgårdandClaude Opus 5.5 324f608a94 TextureCache: Keep the range index and the map together in a TexCacheMap
The map is only handed out const, and entries only come and go through Insert(), Erase(), Release(), Clear()
and SetBufw(), which keep the index in step. Before, every place that changed cache_ had to update the index
by hand, and one that didn't would leave a dangling pointer for the next block transfer to write through.
A new entry now gets the fields its range needs before it goes in, and the slot of one going to the
secondary cache is erased right away.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 18:59:56 +01:00
Henrik RydgårdandClaude Opus 5.5 3863e2f90f TextureCache: Find the textures a block transfer overlaps through a flat index
Invalidate() runs for every block transfer, and walked the cache_ map from 1 MB before the address,
setting a flag on the few textures that overlap. MX vs. ATV Untamed does about a thousand a second,
each walking about 380 of its 745 textures to find six, and its one 1 MB texture means a tighter bound
on texture size wouldn't help.

Now an array of the textures' address ranges sits next to cache_, sorted by address, with textures
over 64 KB in a short list of their own, so a search only starts 64 KB back. It's updated wherever
an entry enters or leaves cache_ (including the moves to and from secondCache_) or changes its bufw.
It marks the same textures as the map walk did (compared on every call in MX vs. ATV, Wipeout Pure,
God of War: Chains of Olympus and Tekken 6). In MX on a Cortex-A53, Invalidate() went from 889
samples to 27.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 18:42:49 +01:00
Henrik Rydgård 9e972e0dd3 GameInfoCache: Parse the game ID out of save data. nice to display. 2026-11-04 18:29:51 +01:00
Henrik Rydgård 6baa0a9805 Fix issue in game tab settings with increase/decrease size. 2026-11-04 18:19:15 +01:00
Henrik Rydgård d0b271820a Make SasAudio addresses clickable 2026-11-04 18:14:33 +01:00
Henrik Rydgård 029d9582a0 Visual fixes on pause screen 2026-11-04 18:07:50 +01:00
Henrik Rydgård b5f91caae4 Windows app (not headless): Inform the user of bad command line parameters, but proceed. 2026-11-04 18:07:29 +01:00
Henrik Rydgård 3ca6ab6a79 Fix ImDebugger font screwup 2026-11-04 18:07:17 +01:00
Henrik Rydgård 9e78ef5677 Misc UI and cmdline parse work 2026-11-04 17:58:18 +01:00
Henrik RydgårdandClaude Opus 5.5 c5a51c084c VR: Don't reject a saved Vulkan backend before OpenXR is up
The config is validated before OpenXR is initialized, and VulkanMayBeAvailable
said no in VR until it was, so a saved Vulkan setting was always replaced by
the default ("Backend 3 not enabled"). Only say no once OpenXR is up without
Vulkan support. app-android checks again after initializing it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 14:21:24 +01:00
Henrik Rydgård 3bc60270ea Merge pull request #22931 from hrydgard/parseuri-hardware-tests
sceParseUri: Rewrite the parser against hardware, and implement sceUriUnescape
2026-11-04 14:20:10 +01:00
Henrik Rydgård 148e9c82d2 Merge pull request #22930 from hrydgard/vblank-timing
sceDisplay/sceCtrl: Charge the vblank interrupt's and ctrl peeks' CPU time, as measured on hardware
2026-11-04 14:19:29 +01:00
Henrik RydgårdandClaude Opus 5.5 5a1d143ac2 VR: Restart in-process when changing the backend
A graphics restart recreates the activity, but on VR devices onDestroy
called System.exit, so the app just quit. The XR instance (created for the
old activity), the GL session (entered once per process) and the controller
input (bound to the first session) all outlived the activity.

Now the native shutdown takes OpenXR down (ShutdownVR: renderer, session,
instance), the new activity sets it up again, and the exit is skipped for
restarts. Input is created per session, which also keeps the controllers
working when Vulkan recreates its session with the graphics context. The
passthrough layer is destroyed with the renderer.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 12:49:24 +01:00
Henrik RydgårdandClaude Opus 5.5 249b653962 VR: Fix inverted depth and GL gamma, keep the flat screen upright
- Depth was inverted on both backends: the VR shader replaced Z with the
  game's clip-space Z after the viewport transform, skipping its Z scale.
  Since the viewport transform moved into the shader, that's wrong. Replace
  it before the transform instead, as libretro already did.
- GL eye buffers are sRGB, and GLES encoded our sRGB colors again (too
  bright). Disable the encoding with GL_EXT_sRGB_write_control.
- Keep the flat screen upright instead of tilting it to the head's pitch at
  recenter time.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 12:48:32 +01:00
Henrik RydgårdandClaude Opus 5.5 abe2203657 VR: Port the skyplane hack and VRForce to Vulkan
The Vulkan version edits the frame's steps in PreprocessSteps, like the GL
one does at replay: the first pass that doesn't sample another framebuffer
clears to the fog color, and indexed draws without depth test are dropped.

The shader generator test also builds the vertex shaders with the VR
uniforms now, for Vulkan and GLSL.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 12:47:22 +01:00
Henrik RydgårdandClaude Opus 5.5 a81e6d61c9 VR: Run OpenXR on Vulkan, with both eyes in one multiview pass
The XR swapchain (one image layer per eye) replaces the main swapchain as a
VulkanPresentation, and the backbuffer pass is multiview in VR. The emu
thread begins each XR frame before NativeFrame, the render thread ends it
after submitting. VR uniforms (lens projection, right eye view, HUD scale)
follow the base uniform block on Vulkan, recomputed for every draw since
they depend on the draw being 2D and on the head pose. VR now defaults to
Vulkan.

On the Quest the swapchain is plain UNORM: a mutable sRGB one viewed as
UNORM showed snow along edges in its compositor. Poses that aren't valid
(no tracking yet) become identity, so layers aren't rejected.

See docs/vr-openxr-vulkan.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 12:46:39 +01:00
Henrik Rydgård 7b2860ec42 Add icon for VR settings 2026-11-04 11:41:53 +01:00
Henrik Rydgård ac31417b71 Merge pull request #22928 from hrydgard/softgpu-zero-normals
SoftGPU: Light zero normals by the sign of the last Bezier patch
2026-11-04 11:31:21 +01:00
Henrik RydgårdandClaude Opus 5 96cda92669 sceParseUri: rewrite the parser against hardware, and implement sceUriUnescape
Nothing tested sceParseUri, and it showed. Measured on a real PSP by the new
net/parseuri tests:

- A URI without "//" was rejected outright, so mailto:, urn:, about: and even
  file:///tmp/x all failed. They parse fine; that is what noSlash is for.
- A fragment ended up appended to the path, because the code looked for "#"
  inside the query rather than the whole remainder.
- An empty path came back as "/", because Url insists on one.
- Ports take at most five digits and at most the value 65536, which wraps to
  zero; anything longer or larger is refused. A host runs while the characters
  are alphanumeric, dot or dash, so an underscore or a bracketed IPv6 address
  falls through to the path.
- The three error codes are real and specific - 80433060 bad URI, 804331FE no
  output pointer, 80433022 work area too small - where everything returned -1.
  Leaving out either area is a size query that succeeds.

So sceUriParse no longer goes through the Url class, which is built for HTTP and
has nowhere to put a fragment.

sceUriEscape kept the wrong two characters: hardware leaves "*" alone and escapes
"~", which is the other way round from RFC 3986. It also fills what it can before
reporting a buffer too small, and leaves the length output alone when it does.

sceUriUnescape was a null entry, so calling it got "library not linked". It is
implemented, including the odd positive return value - one per plain character
and five per escape - that the hardware hands back.

Also lists the two exports libparse_uri.prx has that we did not know about, so a
game calling one is named in the log rather than appearing as a bare NID.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-11-04 11:11:20 +01:00
Henrik RydgårdandClaude Opus 5.5 201e1be9ef SoftGPU: Light zero normals by the sign of the last Bezier patch
The GE keeps the sign of the z of the last Bezier patch's normal at its last
corner (flipped by patch facing, 0 when sideways or degenerate), and lights a
normal that is exactly zero as that sign times (1,1,1) in model space
(ppsspp-re geprobe exp226-253). Normals the tessellation generates don't take
it. Nayuta no Kiseki and Zettai Zetsumei Toshi 3 rely on it.

Frame dump playback draws a flat +z patch after INIT, as the PSP replayer does
before each replay, so both start from +1.

A run's chunks read the sign and the generated-normals flag, so changing either
waits for the run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 11:07:23 +01:00
Henrik Rydgård c0479ac6f2 Merge pull request #22926 from hrydgard/libretro-depth-raster
libretro: Add the Lens flare occlusion (depth raster) option
2026-11-04 11:02:45 +01:00
Henrik RydgårdandClaude Opus 5.5 7af0b9d432 test.py: threads/scheduling/alarmcosts passes now
The test checks ranges instead of rounding on hardware's noise.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-11-04 10:41:00 +01:00