Commit Graph
6037 Commits
Author SHA1 Message Date
Henrik Rydgård c44a12363f Prototype the new ChoiceGroup component 2026-10-24 01:52:34 +02:00
Henrik RydgårdandClaude Opus 5.5 2ed5aa264c UI: Let round rects keep some corners square
A RoundCorners mask picks the corners to round. A square corner is drawn
from the corner image's edge profiles, split along the diagonal, so its
antialiasing matches the straight edges and it needs no extra draw call.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-24 01:52:34 +02:00
Henrik RydgårdandClaude Opus 5.5 1fb8c3090b UI: Fade out the scroll bob when the scroll view is left alone
It stays for a second after the last activity, then fades out over half
a second. Any scrolling, touch, bob drag, or the mouse over the bob's
strip brings it back within a tenth of a second. It also shows briefly
when the view appears, so it's clear that it scrolls.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-24 01:52:34 +02:00
Henrik RydgårdandClaude Opus 5.5 110fb7fd6a UI: Rounded rectangles from precomputed atlas corners
UIAtlas generates an antialiased white corner for each radius in
ROUND_RECT_RADII (8, 16, 24, 32 UI units) and each style (filled, and
thin/medium/thick outlines from ROUND_RECT_THICKNESS) at the atlas DPI.
DrawBuffer::DrawRoundRect mirrors the corner into all four corners and
stretches the corner's last texel row/column along the edges, so a
round rect is 9 quads (8 for outlines).

Other radii use the nearest corner, scaled. The LogoScreen temporarily
draws some for testing.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-24 01:52:34 +02:00
Henrik Rydgård 01508a9f38 Merge pull request #22743 from hrydgard/text-drop-shadow
TextDrawer: Soft drop shadow flag, shared with the UI atlas
2026-10-23 13:54:42 +02:00
Henrik RydgårdandClaude Opus 5.5 e86113afa3 Fix the Windows build of draw_text.cpp
CommonWindows.h defines WIN32_LEAN_AND_MEAN, which leaves out what the
Direct2D/DirectWrite headers in draw_text_uwp.h need. Include
<windows.h> with just NOMINMAX instead, and keep std::max out of
DropShadow.h so it doesn't depend on which order windows.h comes in.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-23 13:04:18 +02:00
Henrik RydgårdandClaude Opus 5.5 dea182f1d8 Use one UI drop shadow for text and atlas images, fix Windows build
Text and the SVG button images are mixed on screen, so they now share
UIDropShadowParams (3 UI units of blur, 0.66 intensity, no offset, the
atlas's existing look) instead of the text having its own.

draw_text.cpp included <windows.h> directly, whose min/max macros broke
the std::max calls added here. Include CommonWindows.h instead.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-23 12:32:25 +02:00
Henrik RydgårdandClaude Opus 5.5 fcfac13643 Share the drop shadow code between the UI atlas and the text drawer
CompositeDropShadow in Common/Render/DropShadow.cpp blurs a premultiplied
RGBA image's alpha (with an optional offset) and composites the image
over it. The atlas's AddDropShadow and TextDrawer::AddDropShadow are now
thin wrappers that pick the parameters.

The blur scatters only the non-transparent texels, which makes atlas
shadows 3-7x faster than the old gather-with-clamping version. Atlas
output is unchanged apart from rounding (alpha differs by at most 1).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-23 11:56:24 +02:00
Henrik RydgårdandClaude Opus 5.5 352c0da492 TextDrawer: Add FLAG_DROP_SHADOW for a soft drop shadow under text
The backend rasterizes coverage (or color, for emoji) as usual, then a
shared CPU pass pads the bitmap, blurs an offset copy of the coverage
and composites the text over it as RGBA8888. Blur and offset scale with
the font size and with DPI. The flag is part of the string cache key,
and the text is still aligned by its own size, with the quad grown to
fit the shadow.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-23 11:51:18 +02:00
Henrik RydgårdandClaude Opus 5.5 05273d9da3 UI atlas: Rasterize each SVG image separately, in parallel
nanosvg walks every scanline of the target for each visible shape, so
rasterizing the whole SVG and cropping the images out of it cost
shapes * canvas area. Each image is now rasterized into a buffer the size
of its bounding box, from a list of copies of its own shapes, so the
images can be rasterized on the thread pool.

Atlas generation on a Redmi 9A (32-bit) goes from about 165 ms to about
95 ms, and on a desktop from about 10 ms to 6 ms. Parsing the SVG is now
the largest part. The atlas layout is unchanged; a few edge pixels differ
by at most 2/255 from the different subpixel offsets.

Also copy whole rows in Image::copyfrom, and fix a leak of the SVG file
data when parsing fails.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-23 10:16:31 +02:00
Henrik RydgårdandClaude Opus 5.5 f795f7e07f UI: A press and release in one event is a press only for the top screen
Key ups go to every screen, so keys don't get stuck, and that included
events that are a press and a release at once, like iOS's back swipe. So
with settings open from the pause menu, a back swipe made the pause
screen try to close too. It isn't on top, so finishDialog refused, but
the screen had already stopped taking input, and once settings closed it
was left on top, ignoring everything.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 23:16:47 +02: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årdandClaude Opus 5.5 04ae3fea5b External display setting: Show the game, or mirror the screen
Graphics > "External display" on iOS and Android. "Show the game" (the
default) is the TV mode, "Mirror the screen" leaves the TV to the system.
A change applies right away. It's disabled on backends that can't draw to
a second swapchain.

On Android 12 and up, a second screen that's part of the device (like a
dual-screen handheld's) isn't used, so the game doesn't move there by
itself.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 22:31:07 +02:00
Henrik RydgårdandClaude Opus 5.5 5fbd134a7d Android: Show the game on a second display, with the controls on the phone
A presentation display (HDMI or USB-C video out, some wireless displays,
or the simulated ones in Developer options) gets a Presentation with a
SurfaceView, whose surface becomes a Game output, when the backend is
Vulkan. Otherwise Android keeps mirroring.

The surface mustn't be touched after surfaceDestroyed returns, so the
output is removed with the new Native_RemoveOutputAndWait(), which
returns once the render thread has let go of it (or right away if the
graphics are down). The UI drawn on an output now leaves out the main
swapchain's pre-rotation, as the game already did.

"In menus on %1" gets the device name from Settings.Global.DEVICE_NAME.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 22:31:07 +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 3b1432d851 Outputs: Begin the UI frame after binding, and scissor to the output
UIContext::BeginFrame sets the scissor, which on Vulkan needs a bound
render target, and sets it to the primary's size. In portrait, that cut
the TV's menu message off past the phone's width.

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 014f6dceea Outputs: Name the device in the menu message
"In menus on Henrik's iPhone" if the platform gives us the name the user
gave the device, otherwise "In menus on your iPhone" (or Mac). iOS only
gives the name with an entitlement Apple has to grant, so for now it's
the model there.

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 3ca76c674c Outputs: Say the menus are on the device, instead of showing black
When no screen on the stack shows anything on an output (we're in the
menus), the background screen draws "Menus are on your device" there.
UI drawn to an output gets the primary's DPI scale, over the whole output.

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 d491fc90cf Secondary outputs, and --game-window on Mac to show the game in a second window
The ScreenManager keeps a list of secondary outputs, each with its own
swapchain, recreated after a lost device. Platform code adds, resizes and
removes them from any thread through Native_AddOutput() and friends, which
take effect at the start of the next frame.

Each frame, every output is drawn by the topmost screen that claims it
(Screen::renderOutput). EmuScreen claims Game outputs: it presents the
game there, laid out for that output, and leaves it out of the backbuffer,
where the touch controls and the menus stay. Not in non-buffered mode,
which draws straight into the backbuffer.

--game-window opens such an output on Mac, with Metal. It's a smoke test
for multiple swapchains, and a stand-in for an AirPlay display.

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 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 5d8db2a22d UI: Give views a pointer to their parent group
Set by ViewGroup when a view is added, inserted or swapped in. Lets a view
find the scroll view it's in, for example.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 15:34:51 +02:00
Henrik RydgårdandClaude Opus 5.5 ab2328b53d Net: Stricter inet_pton, reject empty labels in direct DNS lookups
- inet_pton's IPv6 half is rewritten as a plain parser of the groups before
  and after "::". It no longer rejects a valid trailing "::" after seven
  groups or accepts nine groups around a "::", writes bytes in network order
  without assuming the host's, and only touches the output on success.
- The IPv4 half rejects empty octets ("1..2.3"), and an unsupported family
  returns -1 like the POSIX one, instead of success.
- An empty label ("a..b", ".example.com") ended the encoded name early, so
  the direct lookup asked for something else and cached it under the name
  that was asked for.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 11:51:17 +02:00
Henrik RydgårdandClaude Opus 5.5 66fb9f7ab4 Net: Harden the direct DNS lookup and inet_pton
- inet_pton could write outside its output for IPv6 strings with more than
  eight colons or two "::". Only reachable through AF_INET today, but it's a
  public function. Added a unit test.
- The direct DNS lookup took any datagram that arrived within its timeout as
  the answer, and cached it until restart. The socket is now connected, so the
  OS drops packets from anyone else, the query has a random ID, and replies
  with a different ID, without the response bit, truncated or with an error
  code are rejected.
- Packet fields are read and written with memcpy instead of through unaligned
  pointers, which can fault on 32-bit ARM.
- The lookup cache has a lock. Only the emu thread uses it so far.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 11:45:43 +02:00
Henrik Rydgård 55f10e5590 Merge pull request #22712 from hrydgard/mapped-volume-keys
Input: Let mapped volume keys reach the menus, and consume them in game
2026-10-22 11:37:47 +02:00
Henrik Rydgård 4932e0466d Merge pull request #22721 from hrydgard/settings-bugfixes
Assorted UI bugfixes
2026-10-22 11:31:42 +02:00
Henrik Rydgård b1611a3b47 Merge pull request #22719 from hrydgard/text-measure-cache
TextDrawer: Cache wrapped text, hash the string caches
2026-10-22 11:31:07 +02:00
Henrik RydgårdandClaude Opus 5.5 0c8c447284 Settings: Fix a blank page when reopening settings on the Search tab
The saved tab was selected before the Search tab was added, so the index
was out of range and no tab was created. Select it after adding Search, and
create the current tab anyway if the saved one no longer exists (Search is
hidden on small screens).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 10:55:30 +02:00
Henrik Rydgård 390862a396 Temporary debug mode buildfix for name collision in aemu_postoffice 2026-10-22 10:54:12 +02:00
Henrik RydgårdandClaude Opus 5.5 02b688e4f3 TextDrawer: Cache wrapped text, hash the string caches
MeasureString was slow mostly because of wrapping: DrawStringRect and
MeasureStringRect re-ran the word wrapper every frame, and it measures
every prefix of every word. Wrapped strings are now cached. The caches
are unordered_maps with an xxhash of the key, and lookups reuse a key
buffer so a hit doesn't allocate. UIContext also skips switching fonts
when measuring in the current style. In a Mac debug build, 200 cached
measures go from 0.26 to 0.06 ms and 10 wrapped paragraphs from 2.1 to
0.01 ms per frame.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 10:11:42 +02:00
Henrik RydgårdandClaude Opus 5.5 c51bcd458c Input: Let the key mapping dialog see volume keys, mapped or not
The volume key filter in NativeKey left unmapped volume keys to the OS, so
they never reached the dialog and couldn't be mapped. A new InputMode::AllKeys
lets a screen ask for them; the dialog consumes them so the volume doesn't
change while mapping.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 00:01:34 +02:00
Henrik Rydgård 1adf3c553d Merge pull request #22549 from Kethen/aemu_postoffice_enet
UDP(ENet) mode for adhocctl & aemu_postoffice relay
2026-10-21 23:51:28 +02:00
Katharine Chui 880158cfdb initial enet mode for adhocctl & aemu_postoffice relay 2026-10-21 14:19:54 +02:00
Henrik RydgårdandClaude Opus 5.5 cdf9d38dca iOS: Feed the gyroscope into tilt input
Same as Android: rotation rates go to NativeGyroscope, and SYSPROP_HAS_GYRO shows the "Use gyroscope" option.

Accelerometer data is now scaled from g to m/s^2, like Android's. Tilt normalizes it, but the gyro fusion only trusts the accelerometer when it reads about 9.81, so in g it would never correct drift.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-21 14:00:33 +02:00
Henrik RydgårdandClaude Opus 5.5 fbf0cc2df4 Android: Detect the accelerometer and gyro instead of assuming them
SYSPROP_HAS_ACCELEROMETER was true for every phone and tablet, whether or
not it had the sensor. The activity now tells the native side what the
sensor manager found, which also answers the new SYSPROP_HAS_GYRO. VR
headsets still report neither. Every other platform returns false for the
gyro for now.

The "Use gyroscope" checkbox uses the new property instead of waiting for
gyro data to show up.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-21 12:20:20 +02:00
Henrik RydgårdandClaude Opus 5.5 f84e63f753 Tilt: Steady the tilt input with the gyroscope where there is one
Tilt used the raw accelerometer as the gravity vector, which jitters with
sensor noise and jumps when the device is shaken. Now, when gyro data is
coming in, a complementary filter integrates the gyro rates and only pulls
the result toward the accelerometer with a 0.5 s time constant, trusting
the accelerometer less the further it reads from 1g. The fused vector goes
through the same ProcessTilt path, so calibration and sensitivity work as
before. Without a gyro, nothing changes.

The fusion lives in TiltEventProcessor behind NativeGyroscope(), so other
sources (DualSense) can feed it too. Android registers TYPE_GYROSCOPE next
to the accelerometer. A "Use gyroscope" checkbox (on by default, per game)
appears in tilt setup once gyro data has been seen.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-21 10:45:37 +02:00
Henrik Rydgård a51fe51055 Merge pull request #22690 from hrydgard/me-core
Media Engine part 1: Address space and core (off by default)
2026-10-21 08:37:22 +02:00
Henrik RydgårdandClaude Opus 5.5 8e2bb1faed 32-bit: Keep the memory view range reserved, and mask guest addresses to it
Find4GBBase found 256 MB of free address space for the views and then
unmapped it, so the gaps between the views went back to the system. On a
32-bit Android device, a running game had malloc heaps between VRAM and RAM
and GPU driver buffers just past RAM, and masked guest addresses (bits 30-31
cleared) reach a whole GB above base, mostly heap. A stray guest access
landed in those silently instead of faulting, and another thread could even
map something where a view was about to go, which the view's MAP_FIXED then
replaced. That fits the 32-bit-only crashes deep in system libraries and in
our own heap structures.

Keep the range reserved as PROT_NONE and map the views over it, putting the
reservation back when a view is released. Mask out bits 28-29 too on 32-bit
(no valid address has them), so accesses stay inside the reservation.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-20 23:21:11 +02:00
Henrik RydgårdandClaude Opus 5.5 41851007a2 Memory: Give the Media Engine an address space of its own
The ME sees the main CPU's RAM, but has its own eDRAM at 0 (with
0x80000000 and 0xA0000000 mirrors) where the main CPU has its scratchpad,
its own memory where the main CPU has VRAM, and both CPUs share the boot
page at 0xBFC00000.

- The ME's own memory is views in the main arena flagged MV_ME_ONLY, which
  only Memory::CreateMESpace() maps, into a second reservation along with
  the shared views at their same arena positions. It gets its own block
  shadow.
- The eDRAM (MV_EDRAM) is the ME's half of the eDRAM chip, the GE's VRAM
  being the other: 2MB on the PSP-1000 (whose resident ME image says so),
  4MB on the others (from mcidclan's libmecore, not hardware-checked).
- At VRAM's addresses, through 0x44000000 and 0xA4000000 alike, the ME
  doesn't see VRAM but memory of its own (pspautotest me/vram, on a
  PSP-1000): where homebrew keeps the VME's buffers. Its size isn't known;
  2MB with VRAM's mirrors here, and no depth layout on 16-bit accesses.
- 0xA0000000 reaches physical memory uncached, so RAM is mapped at
  0xA8000000 too.
- The boot page is a view too, which the main CPU reaches through a new
  MMIO device (in savestates from MMIO section version 2). The ME runs
  code from it, uncached, so the main CPU's writes invalidate code there.
  On masked platforms it isn't mapped, and the device uses a plain
  buffer.
- AddressSpace has IsValidAddress etc, with the ME's rules when edramSize
  is set, and the interpreters and the IR interpreter check and fetch
  instructions through their state's space. The IR compiler's checks still
  use the main CPU's, until the ME gets a JIT.
- On Darwin, MemArena can hold several view reservations.

Masked platforms (iOS, 32-bit) don't get an ME space yet.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-20 22:22:40 +02:00
Henrik RydgårdandClaude Opus 5.5 135e752105 Windows: Let the language ini's Font override use system fonts again
- Fixes #22675

Since 3aa4714ce0 desktop Windows draws text with DirectWrite, which looked
up every font in a collection of only our bundled fonts, so an override
like "Font = 微软雅黑" silently fell back to Roboto. Fall back to the
system font collection when the family isn't one of ours.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-20 16:40:37 +02:00
Henrik Rydgård aba3a99888 Merge pull request #22679 from hrydgard/exit-on-game-exit
Android frontends: --pause-menu-exit exits when the game ends, Args extra with URIs, Copy deep link
2026-10-20 13:25:53 +02:00
Henrik RydgårdandClaude Opus 5.5 55f02684f5 Android: Enable "Copy deep link" on the game screen, copying the content URI
PPSSPP has access to the URI, so a frontend or adb can launch the game with it,
which isn't true of a plain path on modern Android.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-20 12:42:07 +02:00
Henrik Rydgård 58f2eae3b4 Merge pull request #22677 from hrydgard/texreplace-fake-mip
Texture replacement fixes: fake mipmap levels, case-insensitive packs, cache sizing
2026-10-20 12:35:41 +02:00