mirror of
https://github.com/mihawk-99/PS5_RetroArch.git
synced 2026-10-21 16:12:27 -05:00
The console run now reaches a frame. In order: video_vulkan selected, the console's
khr_display WSI, a device, a swapchain at 3840x2160, four textures, a vertex buffer,
the stock shader reflected, both stages compiled by the driver's own compiler,
vkCreateGraphicsPipelines returning 0, vulkan_init handing back a live pointer, the
input driver opening the pad, and runloop_iterate reaching vulkan_frame -> vkQueueSubmit.
Nothing is on screen yet: the first frame's command buffer already has a render pass
open when the backbuffer pass begins, and Mesa's common CmdBeginRenderPass2 asserts.
Three of the four fixes are port patches, all of them requests that driver refuses:
0016 its pipeline check accepts only VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, and its
draw path refuses a non-indexed draw whose first vertex is not zero; the two
quads are now six-vertex lists, the second triangle reached through the
binding's own offset. This is what turned VK_ERROR_UNKNOWN into a pipeline.
0017 it refuses any sampler that is not clamp-to-edge and leaves the output handle
untouched, while CommonResources destroys every handle that is not
VK_NULL_HANDLE - sixteen refused samplers were destroyed as if real and the
driver asserted. The array is cleared before it is filled.
0018 the khr_display context reported quit from the frontend's signal-handler
state, so the runloop ended on its first iteration and rarch_main returned 0
with no frame drawn. A console title has no terminal and no SIGTERM sender.
The fourth is this port's own CRT: _start went from _init_env straight to the static
constructors and never cleared the BSS, whose tail is 0xc7040 bytes of a segment that
is 0x104b4 bytes in the file. tooling/native/ps5-pie.ld marks __bss_start/__bss_end
and _start clears that range; every run prints the check that says so. Whether the
loader also zeroed it was never measured - the invariant is what this asserts.
Two instruments, both because nothing else could be read:
src/main.cpp sends stderr to /app0/trace.txt and makes it unbuffered. The driver's
__assert prints the expression, file and line to stderr and then aborts, and the
console's libc buffers that stream, so every assertion arrived as a bare `abort is
called(system)`. The trace now carries the assert text, which is what identified the
render-pass state as the current blocker.
tools/apply-runtime-probes.py carries thirty-nine probes, and tests/test_frontend.py
checks the set itself: an insert that ends with its own anchor duplicates the line it
is inserted before, which broke the build three times in this round. Every probe must
carry a marker in its own text, a note and a trailing newline.
Evidence: bash tools/run-title.sh with the probe set applied (captures under klog/,
probes reverted before this commit); the trace's own lines quoted above;
bash tools/verify.sh PASS with a probe-free image (`strings build/llvm-pie.elf |
grep -c 'probe PIPE2|probe VDRV|...' ` = 0 after deleting build/ra/obj, which is what
a revert needs); 20 unit tests including the probe-set checks.
Next: mark the span between the offscreen-passes call and the backbuffer pass - the
first-frame history/feedback clear and the menu texture upload - and fix whichever of
the two leaves a render pass open.
108 lines
3.5 KiB
C++
108 lines
3.5 KiB
C++
/*
|
|
* ps5-native-app-boilerplate - Native application startup.
|
|
* Copyright (C) 2026 BlackBearReloaded
|
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
*
|
|
* Initializes the platform runtime, runs static constructors and main, and
|
|
* hands normal process termination back to the platform runtime.
|
|
*/
|
|
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
|
|
using Destructor = void (*)();
|
|
using Initializer = void (*)();
|
|
|
|
extern "C"
|
|
{
|
|
void _init_env(void *process_parameters);
|
|
int atexit(Destructor callback);
|
|
[[noreturn]] void exit(int status);
|
|
int application_main(int argc, char **argv, char **envp) __asm__("main");
|
|
|
|
/* Provided by tooling/native/ps5-pie.ld: the range app_crt.cpp clears before
|
|
* anything can read a zero-initialised static. */
|
|
extern char __bss_start[];
|
|
extern char __bss_end[];
|
|
|
|
extern Initializer __preinit_array_start[] __attribute__((weak));
|
|
extern Initializer __preinit_array_end[] __attribute__((weak));
|
|
extern Initializer __init_array_start[] __attribute__((weak));
|
|
extern Initializer __init_array_end[] __attribute__((weak));
|
|
extern Initializer __fini_array_start[] __attribute__((weak));
|
|
extern Initializer __fini_array_end[] __attribute__((weak));
|
|
}
|
|
|
|
namespace
|
|
{
|
|
/* The console's loader does not zero the BSS, and the tail of this image's
|
|
* writable segment is BSS: the file image is 0x104b4 bytes where the segment is
|
|
* 0xc7040, so about three quarters of a megabyte of zero-initialised objects -
|
|
* RetroArch's, the Vulkan driver's and the shader compiler's - started with
|
|
* whatever the previous owner of that memory left behind.
|
|
*
|
|
* Found through one of those objects: RetroArch's signal-handler counter, whose
|
|
* value the display context reads to decide whether the user asked to quit, read
|
|
* -285230512 before any signal had been delivered. The runloop ended on its first
|
|
* iteration and rarch_main returned 0 without drawing a frame. Now the range the
|
|
* linker script marks is cleared before anything can read it. */
|
|
void zero_bss() noexcept
|
|
{
|
|
for (char *at = __bss_start; at != __bss_end; at++)
|
|
*at = 0;
|
|
}
|
|
|
|
void run_forward(Initializer *first, Initializer *last) noexcept
|
|
{
|
|
if (first == nullptr || last == nullptr)
|
|
return;
|
|
while (first != last)
|
|
(*first++)();
|
|
}
|
|
|
|
void run_reverse(Initializer *first, Initializer *last) noexcept
|
|
{
|
|
if (first == nullptr || last == nullptr)
|
|
return;
|
|
while (last != first)
|
|
(*--last)();
|
|
}
|
|
} // namespace
|
|
|
|
extern "C" __attribute__((weak)) void catchReturnFromMain(int status)
|
|
{
|
|
(void)status;
|
|
}
|
|
|
|
extern "C" void _init()
|
|
{
|
|
run_forward(__preinit_array_start, __preinit_array_end);
|
|
run_forward(__init_array_start, __init_array_end);
|
|
}
|
|
|
|
extern "C" void _fini()
|
|
{
|
|
run_reverse(__fini_array_start, __fini_array_end);
|
|
}
|
|
|
|
extern "C" [[noreturn]] __attribute__((visibility("default"))) void
|
|
_start(void *process_parameters, Destructor loader_teardown)
|
|
{
|
|
const int argc = *static_cast<const int *>(process_parameters);
|
|
auto *parameters = static_cast<std::uint8_t *>(process_parameters);
|
|
auto **argv = reinterpret_cast<char **>(parameters + sizeof(std::uint64_t));
|
|
|
|
/* Before anything else: every static this program will read lives in the BSS
|
|
* the loader left alone, and _init_env may well be the first code to use one. */
|
|
zero_bss();
|
|
|
|
_init_env(process_parameters);
|
|
if (loader_teardown != nullptr)
|
|
(void)atexit(loader_teardown);
|
|
(void)atexit(_fini);
|
|
_init();
|
|
const int status = application_main(argc, argv, nullptr);
|
|
catchReturnFromMain(status);
|
|
exit(status);
|
|
}
|