Homebrew commonly ships its unstripped ELF next to the EBOOT, which is already
how the symbol loader turns z_un_08841f98 into a function name. That same ELF
carries a DWARF .debug_line section, so the addresses can be mapped to source
files and lines too - and a backtrace stops being four hex numbers:
08841f98 move sp,fp mesh.zig:163
0883afa4 li v0,0x0 MenuState.zig:821
088260d8 andi at,v0,0xFFFF State.zig:40
0882a27c andi at,v0,0xFFFF engine.zig:468
Surfaced in three places: per frame in hle.backtrace, in the "hit" object that
cpu.breakpoint.hit and cpu.stepping share, and appended to the disassembly
window's status bar. The breakpoint case keys on the pc rather than the address,
since for a memory breakpoint the useful source location is the instruction that
did the access, not the data it touched.
Storage is a plain sorted table of absolute addresses per module. SymbolMap
keeps module-relative addresses because its .ppsym files are meant to be
reloaded by a different game that pulls in the same module; none of this is ever
written anywhere - it's regenerated from the ELF each boot - so there'd be
nothing for relative addresses to buy. Each module owns its own rows and file
names outright and is keyed the way SymbolMap::UnloadModule is, so unloading one
module drops its lines and nobody else's.
The subtle part is end-of-sequence markers. Without them a lookup for an address
in a gap - a compilation unit built without debug info - confidently reports the
last line of an unrelated file. A prototype run over one test binary
mis-attributed 70 of its 349 functions that way, so sequence ends are recorded
as rows with line 0 and a lookup landing on one reports nothing instead.
DWARF 2 through 4 are decoded (psp-gcc emits 2, Zig 4). Version 5 re-encoded the
file table, so those units are skipped with a warning rather than mis-parsed -
nothing targeting the PSP produces it today.
Scope, since it's narrower than it sounds: PRX conversion strips every .debug
section. I checked all 437 pspautotests .prx and CrossCraft's own app.prx -
none have any. Of 24 installed homebrew EBOOTs, zero carry debug info; CrossCraft
only does because it ships app.elf separately. So this helps someone developing
homebrew, and does nothing at all for a commercial game.
Costs about 1.2 MB for a large Zig binary (98383 rows, 438 files) and nothing
for anything without debug info. Follows bAutoSaveLoadSymbols like the symbols
do.
pspautotests 314/314, UnitTest 55/55, CoreUWP builds.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GZq8ZtJmFY7bkX5FVkr3P9
Adds Core/MIPS/InterpreterDispatch.cpp, the checked-in output of
GenerateInterpreterDispatch() (see the previous commit), and hooks it
into RunUntilFast() in MIPSTables.cpp in place of the old
MIPSGetInstruction()-based table walk + indirect call through
instr->interpret. The checked-with-breakpoints/memchecks path
(RunUntilWithChecks) is untouched for now, since it inspects
MIPSInstruction flags directly and correctness there matters most.
Also fixes a real crash in headless.cpp found while testing this:
cmdLineOptions.gpuBackend.value() would throw when unset (e.g. with
--graphics=software), now uses value_or().
Verified with `test.py -g --cpu=interpreter --graphics=software`:
313/314 pass; the one failure (cpu/fpu/fpu) is a pre-existing
interpreter-vs-JIT denormal (flush-to-zero) difference, confirmed to
fail identically with the old table-walking dispatch, so unrelated to
this change.
Adds Tools/update-dispatcher.py to regenerate InterpreterDispatch.cpp
from a built PPSSPPHeadless binary whenever the MIPSTables.cpp tables
change.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019SKhm9wKEQzRUsx9mTrrtQ
libretro/libretro_vulkan.cpp got PPSSPP's libretro core working with
RetroArch's Vulkan integration by globally monkey-patching PPSSPP's Vulkan
loader function pointers (vkCreateInstance, vkCreateDevice,
vkCreateSwapchainKHR, vkAcquireNextImageKHR, vkQueuePresentKHR,
vkQueueSubmit, etc.) so the unmodified VulkanContext class would end up
wrapping RetroArch's already-existing VkInstance/VkDevice instead of
creating its own, and so a fake VkSwapchainKHR (a self-managed array of
images synced against RetroArch's retro_hw_render_interface_vulkan
callbacks) could stand in for the real swapchain that libretro's Vulkan
model doesn't have. Flagged in-code as "a wacky wrapper".
Replaces that with first-class support in VulkanContext for the two things
libretro actually needs:
- Adopting an externally-created instance/device instead of faking
vkCreateInstance/vkCreateDevice: VulkanContext::CreateInstanceExternal()
adopts RetroArch's VkInstance; CreateDevice() gained optional
extraDeviceExtensions/extraRequiredFeatures params so RetroArch's
requirements get merged into a real vkCreateDevice() call;
ownsInstance_/ownsDevice_ flags (the latter set via
SetDeviceExternallyOwned()) mean DestroyInstance()/DestroyDevice() skip
the real vkDestroy* calls when something else owns the object, without
needing to intercept anything. VulkanLoader gained
VulkanLoadFromGetInstanceProcAddr() for bootstrapping from a
host-supplied proc-addr getter instead of dlopen/dlsym-ing the loader
ourselves - vkGetDeviceProcAddr is resolved via the real instance handle
(not NULL), since per the Vulkan spec it's not one of the handful of
commands queryable with a NULL instance.
- A pluggable presentation backend (Common/GPU/Vulkan/VulkanPresentation.h)
for hosts with no real VK_KHR_swapchain, replacing the fake-swapchain-
handle trick. VulkanContext::GetPresentation() is null by default, so
every existing platform's real-swapchain code path is untouched;
libretro/LibretroVulkanPresentation implements this interface directly
against retro_hw_render_interface_vulkan, as real class state instead of
file-scope globals. Several pieces of state that are normally only
populated as a side effect of ReinitSurface()/InitSwapchain() - the
graphics queue/queue family index (ChooseQueue() is entangled with
real-surface presentation-support checks), the swapchain format, and
the available present modes - needed presentation-aware fallbacks since
libretro never calls that real-surface path at all.
libretro/LibretroVulkanContext.cpp now drives VulkanContext's real, public
API directly - no more hijacked function pointers, no more fake surface or
swapchain. libretro/libretro_vulkan.cpp is deleted.
Verified with a full build+run in RetroArch (not just compile-time
checks): the libretro Makefile doesn't track header dependencies
(cl.exe doesn't support -MMD/-MP, and Makefile.common never sets up an
equivalent), so a `make clean` full rebuild is required after any header
change to avoid linking stale object code from before the change - several
of the fixes above were initially masked by exactly that.
Adds a new GLProfiler class for GPU-side timestamp profiling, similar to
VulkanProfiler. Uses GL_EXT_disjoint_timer_query (GLES) or GL_ARB_timer_query
(desktop GL).
Features:
- Scoped Begin()/End() profiling with printf-style naming
- Automatic GPU disjoint detection (frequency changes invalidate results)
- Millisecond timing output via INFO_LOG
- Pre-allocated query pool (1024 queries max per frame)
- Nested scope support with indented output
Signed-off-by: Chris Healy <cphealy@gmail.com>
extend max pdp block size, due to user report of the following in
dissidia:
07:53:752 MatchingEven I[SCENET]: HLE\sceNetAdhocMatching.cpp:1188 EventLoop[1]: Matching Event [1=HELLO][00:02:02:02:02:02] OptSize=4696
gcc9 and the version of msvc19 that the libretro builders use don't
have _mm_storeu_si32. Using _m_cvtsi128_si32 followed by memcpy is
picked up by compilers that support _mm_storeu_si32 as being the same
and they generate the same assembly, so this is no harm to them.