diff --git a/board/batocera/patches/sdl2/sdl2_frametime_log.patch b/board/batocera/patches/sdl2/sdl2_frametime_log.patch new file mode 100644 index 00000000000..7a0c9accbd8 --- /dev/null +++ b/board/batocera/patches/sdl2/sdl2_frametime_log.patch @@ -0,0 +1,80 @@ +--- a/src/video/SDL_egl.c ++++ b/src/video/SDL_egl.c +@@ -37,6 +37,54 @@ + #include "SDL_egl_c.h" + #include "SDL_loadso.h" + #include "SDL_hints.h" ++#include ++#include ++ ++/* frametime logger (env FRAMETIME_LOG). Wraps the eglSwapBuffers function ++ * pointer itself instead of the generic SDL_EGL_SwapBuffers() helper: on ++ * this board (and any Wayland/KMSDRM backend), the per-backend SwapWindow ++ * implementations (Wayland_GLES_SwapWindow, KMSDRM_GLES_SwapWindow) call ++ * _this->egl_data->eglSwapBuffers() directly and never go through ++ * SDL_EGL_SwapBuffers() - a hook there is dead code for those backends. ++ * The function pointer is the true single choke point for every backend ++ * (SDL_EGL_SwapBuffers() itself also calls through this same pointer, so ++ * wrapping it here covers that path too without double-counting). */ ++static EGLBoolean (EGLAPIENTRY *FT_real_eglSwapBuffers)(EGLDisplay, EGLSurface); ++static FILE *FT_log; ++static EGLBoolean EGLAPIENTRY FT_log_eglSwapBuffers(EGLDisplay dpy, EGLSurface surf) ++{ ++ struct timespec ts_; ++ clock_gettime(CLOCK_MONOTONIC, &ts_); ++ fprintf(FT_log, "%lld\n", ++ (long long)(ts_.tv_sec * 1000000LL + ts_.tv_nsec / 1000)); ++ return FT_real_eglSwapBuffers(dpy, surf); ++} ++ ++/* FRAMETIME_LOG is set on the *frontend* (emulationstation) process so that ++ * the game it forks/execs inherits it - but emulationstation itself also ++ * links this same libSDL2 and does its own swaps (still-live background ++ * redraws even with a game "in front"), so it would otherwise open and ++ * write into the same log file as the game it launched, corrupting it with ++ * interleaved/out-of-order timestamps from two independent processes ++ * (confirmed live: ES held an open FD on the game's FRAMETIME_LOG path, ++ * producing multi-hundred-second jumps in the CSV). Exclude the frontend ++ * itself by process name so only the actual emulator process logs. */ ++static int FT_skip_self(void) ++{ ++ static int cached = -1; ++ if (cached < 0) { ++ char comm[16] = {0}; ++ FILE *cf = fopen("/proc/self/comm", "r"); ++ if (cf) { ++ if (!fgets(comm, sizeof(comm), cf)) { ++ comm[0] = '\0'; ++ } ++ fclose(cf); ++ } ++ cached = (SDL_strncmp(comm, "emulationstatio", 15) == 0); ++ } ++ return cached; ++} + + #ifdef EGL_KHR_create_context + /* EGL_OPENGL_ES3_BIT_KHR was added in version 13 of the extension. */ +@@ -479,6 +527,22 @@ + _this->egl_data = NULL; + return -1; + } ++ ++ /* frametime logger: substitute logging wrapper if requested. Guarded by ++ * a pointer-identity check so a second LoadLibraryOnly() call (video ++ * reinit) doesn't double-wrap. Skipped entirely for the emulationstation ++ * frontend itself (see FT_skip_self() above). */ ++ if (!FT_skip_self()) { ++ const char *ft_path = SDL_getenv("FRAMETIME_LOG"); ++ if (ft_path && !FT_log) { ++ FT_log = fopen(ft_path, "w"); ++ if (FT_log) setvbuf(FT_log, NULL, _IOLBF, 0); ++ } ++ if (FT_log && _this->egl_data->eglSwapBuffers != FT_log_eglSwapBuffers) { ++ FT_real_eglSwapBuffers = _this->egl_data->eglSwapBuffers; ++ _this->egl_data->eglSwapBuffers = FT_log_eglSwapBuffers; ++ } ++ } + return 0; + } + diff --git a/board/batocera/rockchip/bsp-odroidm1/linux-defconfig.config b/board/batocera/rockchip/bsp-odroidm1/linux-defconfig.config index 971ccdcb0d9..b6a5e3cb5af 100644 --- a/board/batocera/rockchip/bsp-odroidm1/linux-defconfig.config +++ b/board/batocera/rockchip/bsp-odroidm1/linux-defconfig.config @@ -6203,7 +6203,7 @@ CONFIG_TINYDRM_ST7735R=m # CONFIG_DRM_PL111 is not set # CONFIG_DRM_XEN_FRONTEND is not set # CONFIG_DRM_LIMA is not set -CONFIG_DRM_PANFROST=m +CONFIG_DRM_PANFROST=y # CONFIG_DRM_TIDSS is not set CONFIG_DRM_GUD=m # CONFIG_DRM_SSD130X is not set @@ -6231,9 +6231,9 @@ CONFIG_MALI_DEVFREQ=y # CONFIG_MALI_CORESTACK is not set CONFIG_MALI_PWRSOFT_765=y # CONFIG_MALI_KUTF is not set -CONFIG_MALI_BIFROST=y -CONFIG_MALI_PLATFORM_NAME="rk" -CONFIG_MALI_REAL_HW=y +# CONFIG_MALI_BIFROST is not set +# CONFIG_MALI_PLATFORM_NAME is not set +# CONFIG_MALI_REAL_HW is not set # CONFIG_MALI_BIFROST_NO_MALI is not set # CONFIG_MALI_IS_FPGA is not set diff --git a/board/batocera/rockchip/bsp-odroidm1/local.mk.example b/board/batocera/rockchip/bsp-odroidm1/local.mk.example new file mode 100644 index 00000000000..9ca9dfe90ff --- /dev/null +++ b/board/batocera/rockchip/bsp-odroidm1/local.mk.example @@ -0,0 +1,34 @@ +# Copy (or symlink) this file to output/rk3568-odroidm1/local.mk to build +# the kernel straight from a live local checkout instead of downloading +# Pyohwan/linux's panfrost-enable branch - skips buildroot's +# download/extract steps entirely. Edit the kernel source, then +# `make linux-rebuild all`, no commit/push needed per iteration. +# +# Requires the matching docker mount: pass KERNEL_SRCDIR= on every `make rk3568-odroidm1-build` +# invocation (see docker/docker.mk's KERNEL_SRCDIR opt-in) - the container +# only sees the checkout at /kernel-src if that variable is set, e.g.: +# +# make rk3568-odroidm1-build BATCH_MODE=1 KERNEL_SRCDIR=/mnt/2026_990pro_2tb_1_data/git_projects/linux-odroidm1 +# +# Why this exists: buildroot's git-download cache keys its dl/ tarball +# filename off BR2_LINUX_KERNEL_CUSTOM_REPO_VERSION's literal string, not +# off the commit it resolves to. A branch name never changes that string, +# so pushing new commits to an already-downloaded branch is invisible to +# buildroot forever - this cost two full iterations of the odroid-m1- +# panfrost debugging cycle silently rebuilding the same stale commit +# (see Joplin "B트랙 buildroot 커널 캐시 오염" note, 2026-08-08). This +# OVERRIDE_SRCDIR mechanism is buildroot's own documented answer for +# "actively editing source, rebuilding often" (manual: "Using Buildroot +# during development") - it bypasses the download cache instead of +# fighting it. +# +# Caveat: while this is active, the working tree - not git history - is +# what's actually being built. Check `git status`/`git stash list` in the +# kernel repo before trusting what a given image contains, and commit +# anything worth keeping. Once a build is verified good enough to archive, +# delete/rename this file and go back to pinning +# BR2_LINUX_KERNEL_CUSTOM_REPO_VERSION to that commit's full hash in the +# .board file instead - the two mechanisms are meant to be swapped between, +# not run at the same time. +LINUX_OVERRIDE_SRCDIR = /kernel-src diff --git a/buildroot b/buildroot index a7564de8bd1..1e45e38e3c1 160000 --- a/buildroot +++ b/buildroot @@ -1 +1 @@ -Subproject commit a7564de8bd1b9e2a477d02abf7c73414d422b848 +Subproject commit 1e45e38e3c14a5a68a0e50c2b9ecf3460337f981 diff --git a/configs/batocera-rk3568-odroidm1.board b/configs/batocera-rk3568-odroidm1.board index e739f2e1baa..24f90ad6d4e 100644 --- a/configs/batocera-rk3568-odroidm1.board +++ b/configs/batocera-rk3568-odroidm1.board @@ -1,10 +1,30 @@ include batocera-board.common +# Build machine job cap - not a board property, but MAKE_JLEVEL passed on +# the make command line doesn't reach this .config (add-defconfig +# plumbing doesn't apply to plain -build), and hand-editing the +# generated _defconfig doesn't stick (gets regenerated from this file). +# BR2_JLEVEL=0 (auto = nproc+1=17) pushed a 16-core/15GB host into ~8GB of +# swap compiling LLVM+Mesa3D for the Panfrost experiment - that pairing is +# the actual risk (LLVM's own build parallelism stacks on top of buildroot's). +# Bumped 4->8 (2026-08-08): during a kernel-only rebuild (no LLVM/Mesa3D in +# the dependency chain), `docker stats` showed the build container using +# only ~2.7GiB/15GiB even at JLEVEL=4 - plenty of headroom for a plain C +# compile. Watch `docker stats`/`free -h` again next time LLVM+Mesa3D are +# actually in the build (fresh from-scratch image, not just this kernel +# iteration loop) and drop back toward 4 if swap grows the same way. +BR2_JLEVEL=8 + # Target definition BR2_aarch64=y BR2_cortex_a55=y BR2_ARM_FPU_NEON_FP_ARMV8=y BR2_PACKAGE_BATOCERA_TARGET_RK3568=y +# This dedicated target's own board identity, distinct from +# BR2_PACKAGE_UBOOT_ODROID_M1 (also set by the shared 11-board +# batocera-rk3568.board target, which builds an odroid-m1 image variant +# too - not a valid "is this our dedicated target" proxy). +BR2_PACKAGE_BATOCERA_TARGET_BSP_ODROIDM1=y BR2_TARGET_OPTIMIZATION="-pipe -fsigned-char" BR2_TARGET_GENERIC_GETTY_BAUDRATE_115200=y # NOT rk3568/patches: its ffmpeg/mpv patches add v4l2-request HEVC support @@ -18,11 +38,34 @@ BR2_TARGET_GENERIC_GETTY_BAUDRATE_115200=y BR2_GLOBAL_PATCH_DIR="$(BR2_EXTERNAL_BATOCERA_PATH)/board/batocera/patches $(BR2_EXTERNAL_BATOCERA_PATH)/board/batocera/rockchip/patches $(BR2_EXTERNAL_BATOCERA_PATH)/board/batocera/rockchip/bsp-odroidm1/patches" BR2_ROOTFS_OVERLAY="$(BR2_EXTERNAL_BATOCERA_PATH)/board/batocera/fsoverlay $(BR2_EXTERNAL_BATOCERA_PATH)/board/batocera/rockchip/fsoverlay $(BR2_EXTERNAL_BATOCERA_PATH)/board/batocera/rockchip/rk3568/fsoverlay $(BR2_EXTERNAL_BATOCERA_PATH)/board/batocera/rockchip/bsp-odroidm1/fsoverlay" -# Kernel - our hardkernel BSP fork (HDMI+DSI dual livelock fix), not mainline +# Kernel - our hardkernel BSP fork (HDMI+DSI dual livelock fix), not mainline. +# odroid-m1-panfrost branch (this file) tracks the panfrost-enable kernel +# branch specifically - NOT fix-hdmi-dsi-dual-livelock, which track A (blob) +# still uses directly. panfrost-enable carries a DTS fix (interrupt-names +# case + a separate, ungated GPU OPP table) that panfrost's probe needs but +# the vendor-kbase-oriented base tree doesn't provide - see rk356x.dtsi gpu +# node history. This pointer was accidentally left on the shared base +# branch for the first panfrost build attempt (the DTS gap was masked by a +# different bug at the time); real-hardware dmesg is what surfaced it. BR2_LINUX_KERNEL=y BR2_LINUX_KERNEL_CUSTOM_GIT=y BR2_LINUX_KERNEL_CUSTOM_REPO_URL="https://github.com/Pyohwan/linux.git" -BR2_LINUX_KERNEL_CUSTOM_REPO_VERSION="fix-hdmi-dsi-dual-livelock" +# Pinned to a commit hash, not the branch name, on purpose: buildroot's git +# download cache keys its dl/ tarball filename off this literal string, not +# off the commit it resolves to. With a branch name, pushing new commits to +# panfrost-enable is invisible to buildroot forever once the first tarball +# for that name exists - v2/v3/v4 all silently rebuilt v2's tree this way +# (dl/linux/git's checked-out HEAD stayed at 1139cde0 through two more real +# pushes). Bump this hash by hand after every commit meant to actually +# change what gets built; a branch name is only safe once iteration is done. +# +# For active same-day DTS iteration instead, skip this pointer entirely: +# use board/batocera/rockchip/bsp-odroidm1/local.mk.example (copy to +# output/rk3568-odroidm1/local.mk, build with KERNEL_SRCDIR=) to build straight from the working tree via buildroot's +# LINUX_OVERRIDE_SRCDIR - no commit/push/hash-bump per iteration. Switch +# back to a hash pin here once a build is verified good enough to archive. +BR2_LINUX_KERNEL_CUSTOM_REPO_VERSION="13fcfde92b934f0dca1905fe98d976ff003cf369" BR2_KERNEL_HEADERS_6_1=y BR2_LINUX_KERNEL_USE_CUSTOM_CONFIG=y BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE="$(BR2_EXTERNAL_BATOCERA_PATH)/board/batocera/rockchip/bsp-odroidm1/linux-defconfig.config" @@ -47,10 +90,58 @@ BR2_PACKAGE_HOST_DTC=y BR2_PACKAGE_BATOCERA_KODI21=y BR2_TARGET_ROOTFS_SQUASHFS4_ZSTD=y -# GPU - Mali-G52 blob (hardkernel). The kernel also builds Panfrost as an -# unused module (matches the real device: loaded, refcount 0) but we use -# the proprietary blob, matching what's verified working on hardware. -BR2_PACKAGE_MALI_G52_ODROIDM1=y +# GPU - Panfrost (Mesa gallium), replacing the Mali-G52 blob for the +# Panfrost A/B experiment (branch odroid-m1-panfrost / kernel branch +# panfrost-enable). Static DTS check confirmed a clean match against +# panfrost's of_match_table before this switch: compatible = "arm,mali- +# bifrost" (drivers/gpu/drm/panfrost/panfrost_drv.c dt_match), clock +# lookup is unnamed/positional (devm_clk_get(dev, NULL) grabs the first +# listed clock, which is the SCMI-backed "clk_mali" - no clk-scmi -517 +# risk since CONFIG_COMMON_CLK_SCMI is already =y), and the "mali-supply" +# regulator property matches panfrost's default_supplies[] = {"mali", +# NULL} exactly. Board dtsi override (rk3568-evb.dtsi &gpu) only adds +# mali-supply + status=okay, no custom kbase-only properties - this DTS +# was effectively already mainline-shaped. Kernel side: CONFIG_MALI_ +# BIFROST unset, CONFIG_DRM_PANFROST=y (see linux-defconfig.config). +# BR2_PACKAGE_MALI_G52_ODROIDM1=y + +# (TRX/Tomb-Raider auto-select used to be gated on !BR2_PACKAGE_MALI_ +# G52_ODROIDM1 specifically as a stand-in for "this board has no desktop +# GL" - flipped back on and hard-stopped the build once that package was +# disabled here. Fixed at the real root cause instead: batocera-system/ +# Config.in's TRX select now checks BR2_PACKAGE_HAS_LIBGL directly, which +# is false for both the blob and Panfrost/GLES-only Mesa on this board.) + +# Mesa3D with the panfrost gallium driver takes over as the HAS_LIBEGL/ +# HAS_LIBGLES/HAS_LIBGBM provider (mali-g52-odroidm1's Config.in used to +# do this via BR2_PACKAGE_PROVIDES_LIBEGL=mali-g52-odroidm1 etc.). +BR2_PACKAGE_MESA3D=y +BR2_PACKAGE_MESA3D_LLVM=y +BR2_PACKAGE_MESA3D_GALLIUM_DRIVER_PANFROST=y +BR2_PACKAGE_MESA3D_OPENGL_EGL=y +BR2_PACKAGE_MESA3D_OPENGL_ES=y + +# PanVK (Mesa's own Panfrost Vulkan driver) - the blob (Track A) has zero +# Vulkan support at all, so this is a B-only capability, not an A/B +# comparison axis. The GLES-only-comparison rationale that used to be here +# was based on an unverified "PanVK is non-conformant on G52" assumption - +# checked Mesa 26.1.5's docs/features.txt directly: Vulkan 1.0 is listed as +# fully DONE on panvk with no Bifrost/Valhall gen qualifier (only some 1.1+ +# extensions are gen-gated to "panvk/v10+" i.e. Valhall-only), so G52 +# (Bifrost) gets a real Vulkan 1.0 implementation, not a token/broken one. +# Setting BR2_PACKAGE_BATOCERA_VULKAN=y directly (rather than going through +# BR2_PACKAGE_BATOCERA_PANFROST_MESA3D, which this board doesn't use since +# it sets granular MESA3D_* flags above instead of that meta-select) is +# what actually flips every emulator .mk's `ifeq ($(BR2_PACKAGE_BATOCERA_ +# VULKAN),y)` build flag (flycast/beetle-psx/ppsspp/azahar/ps2/dolphin/ +# retroarch --enable-vulkan/vkquake*) - this is additive, not a replacement +# for the existing GLES path: standalone emulators expose Vulkan as a +# runtime-selectable gfxbackend alongside OpenGL, and retroarch cores built +# with USE_VULKAN=ON still keep their GL renderer, Vulkan just becomes an +# additional selectable video driver. Existing GL-path fps numbers already +# recorded in Joplin remain valid as the default/baseline. +BR2_PACKAGE_BATOCERA_VULKAN=y +BR2_PACKAGE_MESA3D_VULKAN_DRIVER_PANFROST=y # No compositor (labwc/Wayland): the vendor Mali blob's Wayland-client EGL # platform is broken (confirmed live on hardware - eglGetPlatformDisplayEXT @@ -105,6 +196,12 @@ BR2_PACKAGE_LIBRETRO_FBNEO_KOREAN=y # instead of full Mesa. Needs kronos.mk patched to not require full mesa3d # on this board (unclear yet whether it actually needs anything beyond # headers for the FORCE_GLES path) before this can be turned back on. +# NOTE (odroid-m1-panfrost branch only): this specific blocker (mesa3d- +# headers vs full mesa3d hard-stop) no longer applies here now that the +# board runs full BR2_PACKAGE_MESA3D for panfrost - kronos could likely +# just be turned on as-is on this branch. Left off anyway to keep the +# Panfrost A/B diff scoped to GPU driver only; worth revisiting once +# panfrost itself is confirmed working on hardware. # BR2_PACKAGE_LIBRETRO_KRONOS=y # NDS: upstream only selects libretro-melonds-ds when BR2_PACKAGE_ @@ -116,6 +213,84 @@ BR2_PACKAGE_LIBRETRO_FBNEO_KOREAN=y # a Software render mode that doesn't need GL at all, worth trying. BR2_PACKAGE_LIBRETRO_MELONDS_DS=y +# Vulkan-capable systems/cores, added alongside PanVK above. All additive +# to the existing GLES matrix (see BR2_PACKAGE_BATOCERA_VULKAN note above) - +# none of these replace an already-tested GL path, they're either brand new +# systems or extra selectable renderers on top of what's already recorded. +# +# PCSX2 (PS2) - never tested on this board before (no PS2 core at all +# previously - libretro-play/libretro-ps2 below are the only alternatives, +# both also new). Explicitly supports aarch64 in its own Config.in +# (depends on BR2_x86_64 || BR2_aarch64) and needs BR2_PACKAGE_XORG7, which +# this board already has on. Vulkan is PCSX2's modern primary renderer. +BR2_PACKAGE_PCSX2=y + +# Dolphin standalone (GameCube/Wii) - distinct from libretro-dolphin above +# (which is what this session's A/B GameCube headline case used, GL only). +# Standalone Dolphin has its own Vulkan gfxbackend (dolphinGenerator.py +# already wires system.config.get("gfxbackend")=="Vulkan"), a completely +# different render path from the libretro GL backend, worth retrying here. +BR2_PACKAGE_DOLPHIN_EMU=y + +# ParaLLEl-RDP N64 core - Vulkan-capable N64 renderer, not one of the 4 +# renderers (mupen64plus-next/GLideN64/glide64mk2/Rice) already tested +# this session, all of which were GL/GLES only. +BR2_PACKAGE_LIBRETRO_PARALLEL_N64=y + +# Dedicated Vulkan-only Dreamcast core (separate from libretro-flycast, +# which is already on and GL-tested this session at 44fps/Gunbird 2) - +# tried and reverted. libretro-flycastvl.mk only has ARM platform/ARCH +# branches wired up for specific boards (RPi4/5, RK3326, H3/H5/H6, ...); +# this board (rk3568/odroidm1) isn't one of them, so it silently falls +# through to the x86_64 default LIBRETRO_PLATFORM, which drags in xbyak's +# x86 JIT code unconditionally - confirmed via a real build failure +# ("fatal error: cpuid.h: No such file or directory" compiling +# core/hw/aica/dsp_x64.cpp). This is a genuinely stale, unmaintained fork +# (pinned commit from Oct 2021) that would need real upstream-style ARM +# platform wiring to fix properly, and libretro-flycast above already +# covers Dreamcast/Vulkan once built with BATOCERA_VULKAN=y - not worth +# patching a 5-year-stale duplicate for. Left off. +# BR2_PACKAGE_LIBRETRO_FLYCASTVL is not set + +# libretro PPSSPP core - distinct build from the standalone PPSSPP already +# tested this session (GL, 30fps min/Tekken 6). Standalone's own Vulkan +# gfxbackend is covered by BATOCERA_VULKAN above; this is an independent +# second PSP core to compare against. +BR2_PACKAGE_LIBRETRO_PPSSPP=y + +# libretro Azahar (3DS) core - no 3DS system was buildable on this board +# before at all (standalone azahar was never enabled either). +BR2_PACKAGE_LIBRETRO_AZAHAR=y + +# Play! - additional PS2 core option alongside PCSX2 above. +BR2_PACKAGE_LIBRETRO_PLAY=y + +# libretro-ps2 tried and reverted: its own CMakeLists.txt/BuildParameters. +# cmake can't identify our cross-compile target at all ("Unsupported +# architecture: unknown", confirmed via a real build failure) - a deeper +# upstream CMake arch-detection issue, not a quick one-line fix like the +# other cores above. PS2 is already covered twice over by PCSX2 and +# libretro-play, so not worth chasing a third path for. +# BR2_PACKAGE_LIBRETRO_PS2 is not set + +# vkquake/vkquake3 need no explicit line here - batocera-system's Config.in +# auto-selects both once BR2_PACKAGE_BATOCERA_VULKAN=y is set (vkquake3 is +# only excluded on JH7110 targets, which this board isn't; it falls back to +# GL4ES for the bits that aren't pure Vulkan since HAS_LIBGL is false here). +# These are native Vulkan-only ports with no GL fallback at all - impossible +# on the blob (Track A) by construction, not just untested. +# +# vkquake2 is the one exception: it builds ref_gl.so *and* ref_vk.so side +# by side (real dual-renderer engine, not vestigial), so it genuinely needs +# desktop GLU/GLX - not available here (Panfrost only provides GLES/EGL, +# same reason TRX/dolphin-libretro are excluded above). A first build +# attempt confirmed a hard buildroot stop over this ("libgl is in the +# dependency chain of libglu... Stop.") - a defconfig "is not set" line +# can't override batocera-system's forced `select`, so fixed at the real +# root cause instead: added "&& BR2_PACKAGE_HAS_LIBGL" to vkquake2's +# auto-select condition in batocera-system/Config.in (this was a latent +# gap there - nothing GLES-only-with-Vulkan existed to expose it before). + # Firmware BR2_PACKAGE_FIRMWARE_ARMBIAN=y diff --git a/docker/docker.mk b/docker/docker.mk index fa44d61c299..68b05667fbb 100644 --- a/docker/docker.mk +++ b/docker/docker.mk @@ -23,6 +23,21 @@ else UID := $(shell id -u) GID := $(shell id -g) +# Opt-in extra bind mount for iterative package development: point +# KERNEL_SRCDIR at a local git checkout and it shows up in the container +# at /kernel-src, ready for a _OVERRIDE_SRCDIR=/kernel-src in local.mk +# (see output//local.mk - BR2_PACKAGE_OVERRIDE_FILE's default +# location). This is buildroot's own documented mechanism for "edit +# source, rebuild, no commit/push needed" - see buildroot manual's "Using +# Buildroot during development". Without this mount, only a version +# string that buildroot has ALREADY cached under is enough to make it +# silently keep reusing a stale download() forever, even after new +# commits land upstream - this bit the odroid-m1-panfrost kernel branch +# for two full iterations (see Joplin "buildroot 커널 캐시 오염" note). +ifdef KERNEL_SRCDIR +DOCKER_OPTS += -v $(KERNEL_SRCDIR):/kernel-src +endif + define RUN_DOCKER $(DOCKER) run -t --init --rm \ -v $(PROJECT_DIR):/build \ diff --git a/package/batocera/core/batocera-configgen/configgen/configgen/emulatorlauncher.py b/package/batocera/core/batocera-configgen/configgen/configgen/emulatorlauncher.py index ceaf8fe2a40..9c3f0d8f330 100644 --- a/package/batocera/core/batocera-configgen/configgen/configgen/emulatorlauncher.py +++ b/package/batocera/core/batocera-configgen/configgen/configgen/emulatorlauncher.py @@ -207,6 +207,20 @@ def start_rom(args: argparse.Namespace, maxnbplayers: int, rom: Path, original_r if not generator.hasInternalMangoHUDCall(): cmd.array.insert(0, "mangohud") + # Mesa's own on-screen graph (fps/frametime) via the gallium + # driver, set unconditionally alongside whatever each + # generator's own show_fps wiring already does (native + # counters, e.g. retroarch's fps_show or dolphin's + # ShowFPS - left untouched, one central env var here beats + # touching every generator individually). Harmless no-op + # on processes that never load a gallium driver (any + # non-GL system, or the vendor Mali blob) or on older + # Mesa without HUD support - the process just never draws + # it, so this is naturally a fallback to each generator's + # existing fps display instead of an either/or choice. + if system.config.show_fps: + cmd.env["GALLIUM_HUD"] = "fps" + # generate the gun help try: default_gun_help_dir = Path("/var/run/batocera-overlays") diff --git a/package/batocera/core/batocera-configgen/configgen/configgen/generators/dolphin/dolphinGenerator.py b/package/batocera/core/batocera-configgen/configgen/configgen/generators/dolphin/dolphinGenerator.py index 1705db84009..8b466e4e2e9 100644 --- a/package/batocera/core/batocera-configgen/configgen/configgen/generators/dolphin/dolphinGenerator.py +++ b/package/batocera/core/batocera-configgen/configgen/configgen/generators/dolphin/dolphinGenerator.py @@ -429,12 +429,25 @@ def generate(self, system, rom, playersControllers, metadata, guns, wheels, game if state_filename := system.config.get('state_filename'): commandArray.extend(["--save_state", state_filename]) + # check if we're running wayland or X11 - boards with neither (no + # compositor, direct KMS/DRM like odroid-m1) need eglfs instead, + # otherwise Qt defaults into xcb with no X server to talk to and + # aborts outright (same failure class as standalone duckstation-qt/ + # pcsx2-qt, see duckstationGenerator.py/pcsx2Generator.py) + if environ.get("WAYLAND_DISPLAY"): + qt_qpa_platform = "wayland" + elif environ.get("DISPLAY"): + qt_qpa_platform = "xcb" + else: + qt_qpa_platform = "eglfs" + return Command.Command( array=commandArray, env={ "XDG_CONFIG_HOME": CONFIGS, "XDG_DATA_HOME": SAVES, - "XDG_CACHE_HOME": CACHE + "XDG_CACHE_HOME": CACHE, + "QT_QPA_PLATFORM": qt_qpa_platform } ) diff --git a/package/batocera/core/batocera-configgen/configgen/configgen/generators/libretro/libretroControllers.py b/package/batocera/core/batocera-configgen/configgen/configgen/generators/libretro/libretroControllers.py index a30b3467a84..e9971babcb3 100755 --- a/package/batocera/core/batocera-configgen/configgen/configgen/generators/libretro/libretroControllers.py +++ b/package/batocera/core/batocera-configgen/configgen/configgen/generators/libretro/libretroControllers.py @@ -89,6 +89,15 @@ def cleanControllerConfig(retroconfig: UnixSettings, controllers: Controllers, / def writeHotKeyConfig(retroconfig: UnixSettings, controllers: Controllers, /) -> None: if controllers and 'hotkey' in controllers[0].inputs and controllers[0].inputs['hotkey'].type == 'button': retroconfig.save('input_enable_hotkey_btn', controllers[0].inputs['hotkey'].id) + # Without this, input_enable_hotkey_btn alone arms the hotkey modifier + # but no joypad action is bound to it, so Hotkey+Start (the batocera + # keyboard convention for force-quitting a game, see es_input.cfg's + # keyboard hotkey=select binding) does nothing on a gamepad - only the + # keyboard input_exit_emulator="escape" above ever quits a running + # libretro core. Confirmed live on hardware (ODROID-M1, SHAKS S6b pad): + # Hotkey+Start did not exit FBNeo until this was added. + if controllers and 'start' in controllers[0].inputs and controllers[0].inputs['start'].type == 'button': + retroconfig.save('input_exit_emulator_btn', controllers[0].inputs['start'].id) # Write a configuration for a specified controller def writeControllerConfig( diff --git a/package/batocera/core/batocera-configgen/configgen/configgen/generators/pcsx2/pcsx2Generator.py b/package/batocera/core/batocera-configgen/configgen/configgen/generators/pcsx2/pcsx2Generator.py index 71fabacf7e3..7db84ee111f 100644 --- a/package/batocera/core/batocera-configgen/configgen/configgen/generators/pcsx2/pcsx2Generator.py +++ b/package/batocera/core/batocera-configgen/configgen/configgen/generators/pcsx2/pcsx2Generator.py @@ -4,6 +4,7 @@ import re import shutil import time +from os import environ from pathlib import Path from typing import TYPE_CHECKING, ClassVar, Final @@ -120,8 +121,21 @@ def generate(self, system, rom, playersControllers, metadata, guns, wheels, game if not re.search(r'^flags\s*:.*\ssse4_1\W', cpuinfo.read(), re.MULTILINE): _logger.warning("CPU does not support SSE4.1 which is required by pcsx2. The emulator will likely crash with SIGILL (illegal instruction).") + # check if we're running wayland or X11 - boards with neither (no + # compositor, direct KMS/DRM like odroid-m1) need eglfs instead, + # otherwise Qt defaults into xcb with no X server to talk to and + # aborts outright (same failure class as standalone duckstation-qt/ + # melonDS, see duckstationGenerator.py) + if environ.get("WAYLAND_DISPLAY"): + qt_qpa_platform = "wayland" + elif environ.get("DISPLAY"): + qt_qpa_platform = "xcb" + else: + qt_qpa_platform = "eglfs" + envcmd: dict[str, str | Path] = { - "XDG_CONFIG_HOME": CONFIGS + "XDG_CONFIG_HOME": CONFIGS, + "QT_QPA_PLATFORM": qt_qpa_platform } # wheels won't work correctly when SDL_GAMECONTROLLERCONFIG is set. excluding wheels from SDL_GAMECONTROLLERCONFIG doesn't fix too. diff --git a/package/batocera/core/batocera-configgen/configs/configgen-defaults-rk3568.yml b/package/batocera/core/batocera-configgen/configs/configgen-defaults-rk3568.yml index 24e13533f1d..6db8b8f0236 100644 --- a/package/batocera/core/batocera-configgen/configs/configgen-defaults-rk3568.yml +++ b/package/batocera/core/batocera-configgen/configs/configgen-defaults-rk3568.yml @@ -39,8 +39,8 @@ systemsp: options: hud_support: false # flycast core, same issue as dreamcast above ps2: - emulator: libretro - core: play + emulator: pcsx2 + core: pcsx2 flash: emulator: lightspark core: lightspark diff --git a/package/batocera/core/batocera-system/Config.in b/package/batocera/core/batocera-system/Config.in index f155f0ce28e..71c15dcaa15 100644 --- a/package/batocera/core/batocera-system/Config.in +++ b/package/batocera/core/batocera-system/Config.in @@ -348,7 +348,7 @@ config BR2_PACKAGE_BATOCERA_SYSTEM select BR2_PACKAGE_ROCKNIX_JOYPAD if (BR2_PACKAGE_BATOCERA_TARGET_RK3326 || \ BR2_PACKAGE_BATOCERA_TARGET_RK3568 || \ BR2_PACKAGE_BATOCERA_TARGET_H700) && \ - !BR2_PACKAGE_MALI_G52_ODROIDM1 + !BR2_PACKAGE_BATOCERA_TARGET_BSP_ODROIDM1 select BR2_PACKAGE_UPOWER # required by some cemuhook servers @@ -797,6 +797,15 @@ config BR2_PACKAGE_BATOCERA_TARGET_JH7110 endchoice +# Board identity for the ODROID-M1 dedicated target (configs/batocera-rk3568-odroidm1.board), +# as opposed to BR2_PACKAGE_UBOOT_ODROID_M1, which the shared 11-board rk3568 +# target (configs/batocera-rk3568.board) also selects - that symbol is a +# bootloader package flag, not a board identity, and using it as a "not this +# board" proxy silently affects the other 10 boards on the shared target too. +config BR2_PACKAGE_BATOCERA_TARGET_BSP_ODROIDM1 + bool "odroidm1 dedicated bsp target" + default n + config BR2_PACKAGE_BATOCERA_EXTRAS bool "batocera.linux tools packages selection" @@ -1202,7 +1211,7 @@ config BR2_PACKAGE_BATOCERA_PORTS_SYSTEMS # Quake 2 select BR2_PACKAGE_LIBRETRO_VITAQUAKE2 if BR2_PACKAGE_BATOCERA_GLES3 - select BR2_PACKAGE_VKQUAKE2 if BR2_PACKAGE_BATOCERA_VULKAN && BR2_PACKAGE_XORG7 + select BR2_PACKAGE_VKQUAKE2 if BR2_PACKAGE_BATOCERA_VULKAN && BR2_PACKAGE_XORG7 && BR2_PACKAGE_HAS_LIBGL select BR2_PACKAGE_YQUAKE2 # ALL select BR2_PACKAGE_YQUAKE2_XATRIX if BR2_PACKAGE_YQUAKE2 # Mission Packs select BR2_PACKAGE_YQUAKE2_ROGUE if BR2_PACKAGE_YQUAKE2 @@ -1225,7 +1234,7 @@ config BR2_PACKAGE_BATOCERA_PORTS_SYSTEMS select BR2_PACKAGE_LIBRETRO_BOOM3 if BR2_PACKAGE_BATOCERA_TARGET_X86_64_ANY # Requires RA with OpenGL # Tomb Raider 1, 2 & 3 - select BR2_PACKAGE_TRX if BR2_PACKAGE_XORG7 && !BR2_PACKAGE_MALI_G52_ODROIDM1 + select BR2_PACKAGE_TRX if BR2_PACKAGE_XORG7 && BR2_PACKAGE_HAS_LIBGL # Quake 3 select BR2_PACKAGE_IOQUAKE3 if !BR2_PACKAGE_BATOCERA_TARGET_BCM2835 && \ @@ -2260,7 +2269,7 @@ config BR2_PACKAGE_BATOCERA_SDL3 select BR2_PACKAGE_SDL3_TTF select BR2_PACKAGE_SDL3_OPENGLES if BR2_PACKAGE_HAS_LIBGLES select BR2_PACKAGE_SDL3_OPENGL if BR2_PACKAGE_HAS_LIBGL - select BR2_PACKAGE_SDL3_X11 if BR2_PACKAGE_XORG7 && !BR2_PACKAGE_MALI_G52_ODROIDM1 + select BR2_PACKAGE_SDL3_X11 if BR2_PACKAGE_XORG7 && !BR2_PACKAGE_BATOCERA_TARGET_BSP_ODROIDM1 select BR2_PACKAGE_SDL3_KMSDRM if BR2_PACKAGE_HAS_LIBGBM && !BR2_PACKAGE_BATOCERA_RPI_VCORE select BR2_PACKAGE_SDL3_WAYLAND if BR2_PACKAGE_BATOCERA_WAYLAND select BR2_PACKAGE_BATOCERA_HAS_SDL diff --git a/package/batocera/core/batocera-system/batocera.conf b/package/batocera/core/batocera-system/batocera.conf index 3738057e763..54ba78de5e9 100644 --- a/package/batocera/core/batocera-system/batocera.conf +++ b/package/batocera/core/batocera-system/batocera.conf @@ -210,7 +210,7 @@ controllers.gamecon.args=map=1 ## Set the keyboard layout (fr,de,us,es). ## To view all available layouts, i.e. English, via terminal type: ## sed '/! layout/,/^$/!d;/English/!d' < /usr/share/X11/xkb/rules/evdev.lst -#system.kblayout=de +system.kblayout=kr ## Setting a keyboard variant is optional ## To view all variants for German language variants, via terminal type: @@ -219,7 +219,7 @@ controllers.gamecon.args=map=1 ## Set the local time zone ## To view all available time zones, run: ls /usr/share/zoneinfo/ -#system.timezone=Europe/Paris +system.timezone=Asia/Seoul # ------------ G - Updates ------------ # diff --git a/package/batocera/core/batocera-system/sysconfigs/rk3568/batocera.conf.Hardkernel_ODROID_M1 b/package/batocera/core/batocera-system/sysconfigs/rk3568/batocera.conf.Hardkernel_ODROID_M1 index bf2284aabe0..df96eef9599 100644 --- a/package/batocera/core/batocera-system/sysconfigs/rk3568/batocera.conf.Hardkernel_ODROID_M1 +++ b/package/batocera/core/batocera-system/sysconfigs/rk3568/batocera.conf.Hardkernel_ODROID_M1 @@ -39,6 +39,15 @@ system.cpu.governor=interactive # powermode_launch_hooks.sh), defeating the point of a single global # knob. See Joplin "ODROID-M1 retro OS 빌드 계획" for the underlying # measurements if per-system defaults are ever wanted again. +# +# global.powermode itself is intentionally left unset here (reverted after +# briefly trying a "highperformance" factory default) - ES's Game Settings +# > Power Mode menu displayed "auto" (no user override saved) even while +# that factory default was silently active underneath, which is misleading: +# the menu should reflect what's actually applied, not hide a source-level +# override the user never chose. Leaving it unset means "auto" in the menu +# genuinely means auto (batocera-power-mode's set_default fallback), and +# the user picks "High Performance" there explicitly if they want it. # Default core for the fbneo system: dsno/Zansword's Korean-patched # fork (package/batocera/emulators/retroarch/libretro/ diff --git a/package/batocera/emulationstation/batocera-emulationstation/controllers/es_input.cfg b/package/batocera/emulationstation/batocera-emulationstation/controllers/es_input.cfg index 5349649e806..de1d620ccdf 100644 --- a/package/batocera/emulationstation/batocera-emulationstation/controllers/es_input.cfg +++ b/package/batocera/emulationstation/batocera-emulationstation/controllers/es_input.cfg @@ -3694,7 +3694,16 @@ - + + diff --git a/package/batocera/emulators/pcsx2/Config.in b/package/batocera/emulators/pcsx2/Config.in index 314191b0e97..dfac3b7e46e 100644 --- a/package/batocera/emulators/pcsx2/Config.in +++ b/package/batocera/emulators/pcsx2/Config.in @@ -24,9 +24,9 @@ config BR2_PACKAGE_PCSX2 select BR2_PACKAGE_SHADERC select BR2_PACKAGE_VULKAN_HEADERS if BR2_PACKAGE_BATOCERA_VULKAN select BR2_PACKAGE_VULKAN_LOADER if BR2_PACKAGE_BATOCERA_VULKAN - select BR2_PACKAGE_WEBP - select BR2_PACKAGE_WXWIDGETS - select BR2_PACKAGE_YAML_CPP + select BR2_PACKAGE_WEBP + select BR2_PACKAGE_WXWIDGETS if BR2_PACKAGE_HAS_LIBGL + select BR2_PACKAGE_YAML_CPP select BR2_PACKAGE_ZLIB help PCSX2 is a free and open-source PlayStation 2 (PS2) emulator. diff --git a/package/batocera/emulators/pcsx2/pcsx2.mk b/package/batocera/emulators/pcsx2/pcsx2.mk index 3bb6e9bdd6a..cb17adb011d 100644 --- a/package/batocera/emulators/pcsx2/pcsx2.mk +++ b/package/batocera/emulators/pcsx2/pcsx2.mk @@ -24,7 +24,11 @@ PCSX2_SUPPORTS_IN_SOURCE_BUILD = NO PCSX2_DEPENDENCIES += alsa-lib ecm fmt freetype host-clang host-libcurl kddockwidgets PCSX2_DEPENDENCIES += libaio libbacktrace libcurl libgtk3 libpcap libpng libsamplerate PCSX2_DEPENDENCIES += libsoundtouch plutosvg portaudio qt6base qt6svg qt6tools -PCSX2_DEPENDENCIES += rapidyaml shaderc sdl3 webp wxwidgets xorgproto yaml-cpp zlib +PCSX2_DEPENDENCIES += rapidyaml shaderc sdl3 webp xorgproto yaml-cpp zlib + +ifeq ($(BR2_PACKAGE_HAS_LIBGL),y) +PCSX2_DEPENDENCIES += wxwidgets +endif # Use clang for performance PCSX2_CONF_OPTS += -DCMAKE_C_COMPILER=$(HOST_DIR)/bin/clang diff --git a/package/batocera/emulators/ppsspp/ppsspp.mk b/package/batocera/emulators/ppsspp/ppsspp.mk index bd784aeed81..5190a9d41d0 100644 --- a/package/batocera/emulators/ppsspp/ppsspp.mk +++ b/package/batocera/emulators/ppsspp/ppsspp.mk @@ -83,7 +83,7 @@ ifeq ($(BR2_PACKAGE_BATOCERA_VULKAN),y) PPSSPP_TARGET_CFLAGS += -DEGL_NO_X11=1 -DMESA_EGL_NO_X11_HEADERS=1 endif # rpi4/5 vulkan support - ifeq ($(BR2_PACKAGE_BATOCERA_TARGET_BCM2711)$(BR2_PACKAGE_BATOCERA_TARGET_BCM2712),y) + ifeq ($(BR2_PACKAGE_BATOCERA_TARGET_BCM2711)$(BR2_PACKAGE_BATOCERA_TARGET_BCM2712)$(BR2_PACKAGE_BATOCERA_TARGET_RK3568),y) PPSSPP_CONF_OPTS += -DARM_NO_VULKAN=OFF else ifeq ($(BR2_arm)$(BR2_aarch64),y) PPSSPP_CONF_OPTS += -DARM_NO_VULKAN=ON diff --git a/package/batocera/emulators/retroarch/libretro/libretro-parallel-n64/libretro-parallel-n64.mk b/package/batocera/emulators/retroarch/libretro/libretro-parallel-n64/libretro-parallel-n64.mk index 3e253dbdb12..60010374c01 100644 --- a/package/batocera/emulators/retroarch/libretro/libretro-parallel-n64/libretro-parallel-n64.mk +++ b/package/batocera/emulators/retroarch/libretro/libretro-parallel-n64/libretro-parallel-n64.mk @@ -32,6 +32,21 @@ LIBRETRO_PARALLEL_N64_PLATFORM=rpi2 else ifeq ($(BR2_PACKAGE_BATOCERA_TARGET_XU4),y) LIBRETRO_PARALLEL_N64_PLATFORM=odroid LIBRETRO_PARALLEL_N64_BOARD=ODROID-XU4 +else ifeq ($(BR2_PACKAGE_BATOCERA_TARGET_RK3568),y) +# Not in upstream's own per-board platform table (odroidm1/rk3568 is a +# batocera-only target). Falling through to the generic "else" branch +# below would leave $(ARCH) unset, and the upstream Makefile's fallback +# `ARCH = $(shell uname -m)` then picks up the *build host's* arch +# (x86_64, since this runs inside the cross-compile docker container) - +# confirmed via a real build failure (xbyak's x86-only cpuid.h pulled in, +# "-msse"/"-msse2" rejected by aarch64-buildroot-linux-gnu-g++). Reusing +# the H5 platform's CPUFLAGS (-march=armv8-a+crc, cortex-a53 tuning, no +# crypto/dotprod extensions) rather than adding new SoC-specific flags - +# it's a strict baseline armv8-a subset with no extension-gated +# instructions, so it's safe to run on RK3568's Cortex-A55 cores too even +# though the tuning target isn't an exact match. +LIBRETRO_PARALLEL_N64_EXTRA_ARGS+=WITH_DYNAREC=aarch64 +LIBRETRO_PARALLEL_N64_PLATFORM=h5 else ifeq ($(BR2_PACKAGE_BATOCERA_TARGET_RK3576),y) LIBRETRO_PARALLEL_N64_EXTRA_ARGS+=WITH_DYNAREC=aarch64 LIBRETRO_PARALLEL_N64_PLATFORM=rk3576