PPSSPP reported its current internal resolution as its maximum geometry, so
any increase made RetroArch restart the video driver (runloop.c compares
max_width/max_height). RetroArch keeps the cached Vulkan device across that
restart and calls neither context_destroy nor context_reset, so PPSSPP's render
thread kept the freed vk_t's interface and called a null function (issue 63:
signal 11, rip=0, [rsp] inside the core). The maximum is now always 10x
(4800x2720, the largest option and the PS5 default profile), so a change is a
geometry update; with hardware rendering RetroArch creates no textures from
the maximum, and default users already had it.
Evidence (python3 tools/evidence.py compare evidence/: 146 replayed, 0 failed):
- evidence/ppsspp-resolution-crash-before: build a2c7800a, tests/pad/
ppsspp-resolution-change.txt (2880x1632 to 3840x2176 at 30 s): the second
video_vulkan init, then the reported crash, [rsp]=0x000000040084aabb.
- evidence/ppsspp-resolution-change: build 191ff52c, the same script:
SET_SYSTEM_AV_INFO 3840x2160 and NotifyDisplayResized 3840x2160 with one
video_vulkan init; the 45 s screenshot and the 55 s STOP, no crash.
Gates: tools/verify.sh, all five (format, unit 215, build, integration 215,
evidence 141), before the evidence was added.
Co-Authored-By: Claude <noreply@anthropic.com>
Every game title in tracked text (test fixtures, comments, patch notes,
console evidence, the v1.0.0-beta.1 note's alt text) is now a made-up
stand-in that keeps the original's shape: its punctuation (apostrophes,
"&", " - "), region tags and, where a test depends on it, its sort order
and how a title relates to its alternate name. Evidence folders and the
Flycast pad script named after games are renamed. Only the committed
handoff/PPSA99002/eboot.bin still holds a title, inside the binary.
releases/v2.0.0-beta-2.md: PS5SX2, frame generation, RetroAchievements,
NetPlay, the WebUI in 16 languages, the library and frontend changes,
the systems tested on a Base PS5, upgrading from v1.0.0-beta.1, source
and credits. It opens with the anti-piracy banner and points, names no
game and shows no game picture: three WebUI screenshots in
assets/releases/v2.0.0-beta-2/.
Gates: format, unit (196 tests), build, evidence (127 replayed, 0
failed), check-manifest and check-notices: PASS.
Use SDK f674976 for blocking heap contention and live direct-memory availability. Trim unused PPSSPP caches under pressure, drain retired GPU objects and retry framebuffer allocation once; close content on unrecoverable failure. Report CPU/GPU/free/largest memory around state loads and failures.
Verified: all five tools/verify.sh gates, 87 tests including 108 injected GPU failures, all 16 core builds; SDK 318 checks plus memory scanner tests. Console evidence: evidence/ppsspp-memory-recovery. Balanced and 6x/8x pass repeated state round trips; prior 10x/8x crash now recovers first load, then safely closes content after device OOM on second load. Build 8b635e9b installed; user settings/state unchanged.
PPSSPP took the physical device's API version (1.4 on RADV) as the version it
could use. It treated render pass 2 as core and never enabled the extension,
while RetroArch had created the instance for 1.1, so vkCreateRenderPass2 was
null and PPSSPP's MSAA path faulted 3.6 s into God of War: Ghost of Sparta.
The port patch caps the device's version at the instance's in
VulkanContext::CreateDevice, and the libretro context asks for 1.1, the version
RetroArch creates anyway. Nothing changes on ps5vk (1.1 and 1.1).
docs/PHASE_LOG: the first console runs on RADV. The menu, PPSSPP, Dolphin
(Wind Waker) and LRPS2 (GTA San Andreas) render, at full speed once the shader
cache is filled, beside ps5vk's runs of the same content.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The title and its cores build with ../PS5_PayloadSDK at a pinned
revision (tools/setup-native-dependencies.sh): the v0.42 release with
the fork's headers and platform layer installed over it. The builder
links SDK archives by name (APP_SDK_ARCHIVES); the core stamps include
the fork's revision.
- The machine context: the fork's sys/_ucontext.h is the console's
layout, so the crash reporter, the sampler and the Dolphin, PPSSPP
and LRPS2 ports read uc_mcontext's fields; the six-word offset each
carried is gone.
- The libc gaps: src/ps5_directory.cpp and most of src/libc_shims.c are
the platform's ps5_* functions; src/platform_wraps.c binds the
title's --wrap links to them and tools/core-imports.py a core's
imports. The directory fixture test moved to the fork.
- Executable code and guest memory: Dolphin's, PPSSPP's and LRPS2's JIT
code is the platform's executable direct memory near each core, at
upstream cache sizes (Dolphin 128 + 64 MiB, LRPS2 305 MiB), and their
arenas are the platform's direct-memory objects, views and ranges.
- A profiled run reports the platform's live counters after its memory
line; the platform probe test aid gains the huge and full tests.
- docs/PLATFORM_FIRMWARE_ANALYSIS.md: the 10.01 modules' export tables
against the SDK, and where the platform layer lives.
- tools/run_clang_tidy.sh tidies each C++ test with the flags its
harness builds it with (the app-source pass still lacks the build's
include paths; a separate task).
Console (docs/PHASE_LOG.md): Dolphin Profiles 0, 1 and 9 and Rogue
Leader, PPSSPP God of War and LRPS2's BIOS run with no crash; Dolphin
has 58 MiB more flexible memory in game and LRPS2 160 MiB more, with
the caches at upstream sizes; the platform's counters return to zero
when content closes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Fast-forward sets PSP_CoreParameter().fastForward with frameskip 3 while
held, so __DisplayFlip no longer throttles with sleeps that overshoot on
the console; FrameTiming's skip-flip path is off on PS5 (FIFO present).
Tag Force save state, fast-forward held: 1199 flips in 10 s.
- Close Content then reopening: the loader waits up to 2 s for core threads
(counted by the thread trampoline) before unmapping a core, and PPSSPP's
MemArena re-reserves its fixed 4 GiB range on the second load.
- Yu-Gi-Oh! GX Tag Force: the console libc statvfs faults; cores now import
ps5_statvfs instead.
- Audio logs the switch to non-blocking (fast-forward) and back, once each.
- README: PPSSPP as a hardware-rendered Vulkan core, JIT, 120 Hz output,
save states and fast-forward; first-person wording.
verify.sh: PASS (format unit build integration evidence)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The core now starts with the settings God of War: Ghost of Sparta is tuned
against: Vulkan, JIT, 10x internal resolution, 16x anisotropy, Auto Max
Quality texture filtering, hardware transform, software skinning, high
spline/Bezier quality, full-resolution effects, no frameskip and no
speedhacks (MSAA waits for the driver's renderpass2 and depth/stencil
resolve). An existing PPSSPP.opt would override them, so it is set aside
once as PPSSPP.opt.before-ps5-profile, guarded by a marker; nothing is
deleted.
FFmpeg 3.0.2 is cross-built with --disable-asm. I turned the assembly off
while ruling it out as the cause of the menu's blue banding (it is not);
the C paths decode the game's intro videos correctly, and I have not
measured the assembly build against them yet.
The exported patch again carries libretro/ps5_gl_stubs.cpp: a plain git diff
leaves untracked files out, which verify's clean build caught.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The core is rebuilt from the v1.20.4 release as one patch
(patches/ppsspp/ps5-port.patch; tools/build-ppsspp.sh, PPSSPP_DEV=1 builds
the tree as it stands). Vulkan through the frontend's device (no GL, no
GLEW), the x86-64 JIT, and FFmpeg 3.0.2 cross-built from the tree's own
submodule for video. In the core: executable memory is mapped read-write
then made executable (the console's working order), guest memory is an
anonymous shared object mapped into a 4 GiB reserved range at
0x6_0000_0000 (outside the GPU's window at high word 2), the fault
handler reads the console's machine context six words past the SDK
header's, and the libretro surface path is VK_KHR_display.
In the title, what a core needs that the console did not give it:
- core thread creation and thread-specific data go through title frames
(src/core_threads_ps5.cpp): libkernel resolves the caller to a module
and faults on the core's anonymous mapping. Core threads get a 2 MiB
minimum stack (glslang's parser overflowed the default).
- a core allocates from an overflow heap in direct memory first
(src/overflow_heap.c, dlmalloc 2.8.6 MIT, 32-byte aligned as the
target's default new alignment): PPSSPP filled libc's small heap, after
which libkernel's own condition variables and sceAgcInit failed.
- gmtime_r and arc4random shims (src/libc_shims.c).
- a crash report in trace.txt (rip, rsp, return address, stack words).
- patch 0087: the log file is line-buffered, so a fault keeps its lines.
Console: GoW boots, loads, plays its intro video and menu and renders its
real-time intro cutscene correctly at 60 FPS content; the menu's
background still renders wrong (driver depth sampling is the lead).
Driver ../PS5_Vulkan 8d61168. verify.sh PASS (patch count 193 -> 194).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PPSSPP dies in ThreadManager::Init on the console, on its first worker thread.
This commit records what the crash is and what it is not, so the next attempt
starts from here.
The console's own dump: SIGSEGV on a worker thread, fault address 0x70, rip
0x8000407fb, backtrace 0x80004055c, 0x800040638, 0x20185ea6a, 0x800074d0.
/system/common/lib/libkernel.sprx is readable over FTP and can now be
symbolized: SharpProspero's NidEncoder documents that a NID is the first eight
bytes of SHA1(name || salt), byte-reversed and base-64-encoded with a custom
alphabet, and it validates against seven documented pairs (puts=YQ0navp+YIc,
malloc=gQX+4GDQjpM, ...). Computing NIDs for the SDK's 4,328 kernel/libc symbols
names 1,111 of the module's 1,270 exports, which turns every console backtrace
inside libkernel into function names.
Naming the frames gives the mechanism. 0x8000407fb is
pthread_get_specificarray_np+0x3b (NID td+DYvbbDfk), reached from the thread
creation path pthread_create_name_np. Disassembling it shows a frame-pointer
walk: take a return address two frames up, look it up in a table of 0x98-byte
range records, then read [result+0x70] with no null check - rbx in the register
dump is 0x8000711d0, libkernel's __stack_chk_guard, which confirms the reading.
The address that could not be resolved, 0x20185ea6a, is the third backtrace
frame: a return address inside this title's core, which the loader mapped
anonymously. The core is not a registered module, so the lookup misses and the
dereference faults.
src/thread_probe.cpp is the opt-in diagnostic (/app0/thread-test.txt) that
tested every case buildable in the title - 32 concurrent std::threads,
thread_local in each, a heap std::mutex and std::condition_variable used by 1 and
32 threads, a thread entered on an anonymous page, a thread entered on a core
export, a thread calling core code, a libkernel call whose immediate caller is an
anonymous page, and pthread_getspecific from an anonymous page. All of them pass
({"stage":"done","created":32,"ran":32,"joined":32,"tls_ok":32} and successors).
PPSSPP still dies at the same instruction, so the trigger is not any of those
alone.
patches/ppsspp/0005-ps5-thread-name.patch stands on its own: PS5 defines
__FreeBSD__, so PPSSPP took its BSD branch and every worker called
pthread_set_name_np, which no shipped core had ever used.
patches/ppsspp/0006-diagnose-thread-pool.patch is the temporary instrumentation
that named the failing step ("creating 0", "created 0", then death).
Gates: bash tools/verify.sh format unit - PASS, 74 tests.
Next: remove PPSSPP's thread_local uses (the plan's A2 step, now motivated by the
emutls path being exactly the family in the backtrace), or make libkernel's
lookup succeed for core addresses.
Track A's console step. The loader diagnostic now passes for PPSSPP —
passed=true, cycles=8, exports=25, api=1 at identity 73d2aad8…, with
"ran 110 initializers" and "symbols=19430 relocations=24812
mapped_bytes=18923520" — so an 18.1 MB C++ core with 392 imports loads,
initialises and unloads eight times over.
Three blockers fell on the way, each found on the console rather than by reading
code:
- Unresolvable imports. The first run failed on gai_strerror, the next on
glGetString: the loader requires every import to have an address, so a symbol
the console leaves null is fatal for a core even though the title tolerates it.
The four libScePosixForWebKit symbols (gai_strerror, getaddrinfo, freeaddrinfo,
isatty) were never exercised by any shipped core and are now implemented in
tooling/ppsspp/ps5-libc-shims.cpp; the 83 OpenGL symbols are excluded by
patches/ppsspp/0003-no-opengl.patch, which drops Common/GPU/OpenGL, GPU/GLES,
the two libretro GL contexts and PS5's PPSSPP_API_ANY_GL. The core now has zero
gl*/egl* imports and the title's table falls from 497 bindings to 410, all of
them resolving from libkernel_web or libSceLibcInternal.
- The thread pool. ThreadManager::Init multiplies its two arguments
(numRealCores * numLogicalCoresPerCpu); the platform branch had set both from
sysconf, asking for 256 compute threads and 512 in total. It now takes the
physical core count from CPUID leaf 0x80000008 and SMT siblings from the online
count: 16 and 32, which is right for this console.
- The runtime assets. PPSSPP resolves them as <system>/PPSSPP and warned "Core
system files missing". tools/build-ppsspp.sh now stages the pinned source's
assets/ — the complete tree, 193 files, 22 MiB — rather than the 187-file
partial copy CMake leaves in the build directory (it omits cheats.json,
knownfuncs.ini and four others), and tools/build-title.sh publishes it to
dist/PPSA99169/system/PPSSPP, verified on the console at
/data/homebrew/PPSA99169/system/PPSSPP including flash0, debugger, shaders and
lang. The tree's digest is in build.json (ppsspp_assets_sha256 223c78b2…).
Also in this commit: patches/ppsspp/0004-software-backend-default.patch makes the
libretro software context the PS5 default for ppsspp_backend (the Vulkan renderer
cannot come up against a driver with no combined depth/stencil format, and the
stalled Vulkan attempt is why earlier runs showed no progress past core init), and
the CPU fix above.
RetroArch is relinked against the sibling's rung-1.0 driver: PS5_Vulkan d4e73ff,
embedded libps5vk.ps5.a d41f934b…, title identity 735f7eb1…. All five gates pass
(32 captures replayed).
Open, and the next step: the title takes a SIGSEGV on a worker thread during
ThreadManager::Init (fault address 0x70, frames inside libkernel.sprx per
klog/run-PPSA99169-002106.log), so no PSP frame has been presented. Rung 1.0 also
does not yet give PPSSPP's Vulkan renderer the six things it needs: a combined
depth/stencil format, attachments smaller than 3840x2160, cull mode, per-channel
colour write masks, stencil test with dynamic stencil state, and dynamic blend
constants.
Console evidence: evidence/ppsspp-native/; replay with
bash tools/verify.sh format unit integration evidence.
Track A of the PPSSPP implementation plan: PPSSPP comes up on its software GPU
core with no PS5_Vulkan dependency, and the Vulkan renderer follows once the
driver subset lands. This commit is the first step — the platform branch and the
cross build — and claims nothing about the console.
tools/build-ppsspp.sh fetches pinned PPSSPP f293b10fb2d9dc0c2bc10281444ee3d3e932e6ad
with its 29 submodules, resets the tree, applies patches/ppsspp/0001-platform-ps5.patch
and 0002-cross-build-ps5.patch with --fuzz=0, configures the tree directly with
tooling/ppsspp/ps5-toolchain.cmake (a wrapper project cannot work: upstream resolves
module paths and sources through CMAKE_SOURCE_DIR), builds the libretro target with
the SDK's prospero-clang wrappers, and runs tools/check-core.py.
Result: "core ABI PASS: ppsspp_libretro.so; 25 exports; kernel_web;
sha256=038001216b59698c28268860349e0b27e8e8cb940f98dbb12650a90d063d71c6",
18,485,448 bytes. readelf on that artifact: zero PT_TLS segments, three 16 KiB load
segments (flags 5/4/6, no writable-executable), NEEDED limited to libkernel_web.sprx,
libSceLibcInternal.sprx and libScePosixForWebKit.sprx. Reports in
build/cores/ppsspp/{abi.json,build.json}; the latter records the revision, the 29
submodule SHAs and seven port-input hashes.
thread_local needed no source patch: a probe compiled and linked with
prospero-clang++ and tooling/native/ps5-core.ld showed zero PT_TLS and one import,
__emutls_get_address, because the SDK uses -femulated-tls. Two flag shims were
required (-Dstatic_assert=_Static_assert for the 1990 __ISO_C_VISIBLE that PPSSPP's
own -D_XOPEN_SOURCE=700 forces on this libc, and -DZSTD_TRACE=0 as the frontend
build already uses), and four libc entry points vendored code calls are declared
but not exported by the SDK stubs — swab, nl_langinfo, tmpfile, tmpnam — so
tooling/ppsspp/ps5-libc-shims.cpp implements them in the core.
Import audit: all 479 undefined symbols but localtime_r resolve from an SDK stub or
from the libc++/libc++abi/libunwind archives the title links; localtime_r is the
existing rtime_localtime alias. The five shipped cores plus PPSSPP generate a
497-binding table for six cores with no TLS or type conflict; the five-core table was
regenerated afterwards, so PPSSPP is not yet in the title's import surface or in
core_names.
Gates: bash tools/verify.sh format unit — PASS, 74 tests, 19s. Not proven: the title
link with ppsspp in core_names, console load/init/teardown, and any game. No console
was contacted, nothing was uploaded or launched.