From 0cecb00564e07592ffa49c98e1fdfe2db9243e61 Mon Sep 17 00:00:00 2001 From: Matthew Stanley <1379tech@gmail.com> Date: Wed, 14 Oct 2026 10:44:19 -0700 Subject: [PATCH] feat: send drawable size to cores for adaptive presentation --- CMakeLists.txt | 6 ++++++ README.md | 2 +- docs/RELEASES.md | 8 ++++++++ scripts/build-local.ps1 | 35 +++++++---------------------------- scripts/build-local.sh | 33 ++++----------------------------- src/hub/hub_main.cpp | 6 +++--- src/hub/hub_play.cpp | 13 ++++++++++++- src/hub/hub_play.hpp | 3 ++- third_party/Retro-Runtime | 2 +- 9 files changed, 44 insertions(+), 64 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8ccc358..5c700b9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -353,6 +353,12 @@ else() set(RETCOMM_LINK_ACCESSORY_DATA FALSE) message(STATUS "retro-hub: Retro-Runtime link predates accessory data; the VRU is off") endif() +# Older Runtime checkouts still build; newer ones negotiate the wire tail. +file(STRINGS "${RETCOMM_RUNTIME_DIR}/corelink/link_protocol.hpp" _rr_presentation + REGEX "link_has_presentation_size") +if(_rr_presentation) + target_compile_definitions(retcomm_core PUBLIC RETCOMM_LINK_PRESENTATION_SIZE=1) +endif() # The hub starts the runner from its own directory, so in the build tree the # runner must land there too, not under third_party/ (where, until this line, a # stale pre-split copy beside the hub was being run instead). diff --git a/README.md b/README.md index 4443fe6..3862ed4 100644 --- a/README.md +++ b/README.md @@ -203,7 +203,7 @@ scripts/build-local.ps1 [-Debug] [-Out DIR] [-Build DIR] [-Runtime DIR] [-Jobs N ``` Builds `retro-hub` and a `retro-core-runner` (the submodule's, or one built -from `--runtime`) into a flat prefix, `out/local//`, laid out like the +from `--runtime`, for both shared link and runner) into a flat prefix, `out/local//`, laid out like the bare hub archive plus the runner and the title-app kit (`packaging/title/`; on Windows also the portable stub). SDL3 is found, or built from source into `.cache/sdl3` (Homebrew on macOS, vcpkg on Windows). It checks the result diff --git a/docs/RELEASES.md b/docs/RELEASES.md index 6a16c5e..f6c515b 100644 --- a/docs/RELEASES.md +++ b/docs/RELEASES.md @@ -52,6 +52,14 @@ ROM file names, and saved as `/boxart/libretro/.png`; later launches reuse it. `--boot` skips the page, plays at once and exits when the player closes the game (what every revision before 3 did). +The bundled Runtime now speaks CoreLink 2.3 / rcore draft 9. Each frame grant +carries the window's available drawable width and height in physical pixels, +before aspect letterboxing. Cores may use the optional `presentation_size` +callback for adaptive projection; fixed-aspect cores keep their own policy. +Resize and display-density changes reach the next grant; minimized windows +report unavailable dimensions. An older 2.x runner receives the original +grant prefix, so it still plays without the new presentation information. + The session itself (menu, input, fault screen) is described in Retro-Runtime's [`docs/CORE_LINK.md`](https://github.com/RetroPortingToolKit/Retro-Runtime/blob/main/docs/CORE_LINK.md), "Direct mode". diff --git a/scripts/build-local.ps1 b/scripts/build-local.ps1 index e3a6dce..02bba9c 100644 --- a/scripts/build-local.ps1 +++ b/scripts/build-local.ps1 @@ -45,9 +45,8 @@ this machine, and print its path last, as RETRO_HUB=<absolute path>. -Debug Debug build (default: Release) -Out DIR the flat prefix (default: <repo>\out\local\windows-<arch>\) -Build DIR CMake build directory (default: <repo>\build-local[-debug]) - -Runtime DIR build the runner from this Retro-Runtime checkout (its - scripts\build-local.ps1 when it has one) instead of the - submodule's + -Runtime DIR build the shared link and runner from this Retro-Runtime + checkout instead of the submodule's -Jobs N parallel build jobs (default: all CPUs) -ReleaseVersion V build as release V (-DRETCOMM_VERSION, -DRETCOMM_RELEASE_BUILD): @@ -119,6 +118,7 @@ $cmakeArgs = @("-S", $Root, "-B", $Build, "-G", $generator, "-DVCPKG_TARGET_TRIPLET=$Triplet", "-DCMAKE_BUILD_TYPE=$Config", "-DRETCOMM_COMMIT=$commit") +if ($Runtime) { $cmakeArgs += "-DRETCOMM_RUNTIME_DIR=$Runtime" } if ($ReleaseVersion) { $cmakeArgs += @("-DRETCOMM_VERSION=$ReleaseVersion", "-DRETCOMM_RELEASE_BUILD=ON") } if ($generator -like "Visual Studio*") { $cmakeArgs += @("-A", $(if ($Arch -eq "arm64") { "ARM64" } else { "x64" })) } Say "$Platform, $Config, build $Build" @@ -133,31 +133,10 @@ if (-not (Test-Path (Join-Path $StageBin "retro-hub.exe"))) { Fail "retro-hub.ex # ---- the runner: the submodule's, or -Runtime's -------------------------------------- $RunnerSrc = Join-Path $StageBin "retro-core-runner.exe" $RunnerFrom = "the Retro-Runtime submodule" -if ($Runtime) { - $rtOut = Join-Path $Build "runtime-out" - $rtBuild = Join-Path $Build "runtime-build" - $rtScript = Join-Path $Runtime "scripts\build-local.ps1" - if (Test-Path $rtScript) { - Say "building the runner with $rtScript" - $rtArgs = @("-Out", $rtOut, "-Build", $rtBuild, "-Jobs", "$Jobs") - if ($Debug) { $rtArgs += "-Debug" } - $lines = @(& $rtScript @rtArgs) - if ($LASTEXITCODE -ne 0) { Fail "$rtScript failed" } - $last = [string]($lines | Select-Object -Last 1) - if (-not $last.StartsWith("RETRO_CORE_RUNNER=")) { Fail "$rtScript printed no RETRO_CORE_RUNNER= line" } - $RunnerSrc = $last.Substring("RETRO_CORE_RUNNER=".Length) - } else { - Say "building the runner from $Runtime with cmake (it has no scripts\build-local.ps1)" - Invoke-Checked cmake @("-S", $Runtime, "-B", $rtBuild, "-G", $generator, - "-DCMAKE_TOOLCHAIN_FILE=$Vcpkg\scripts\buildsystems\vcpkg.cmake", - "-DVCPKG_TARGET_TRIPLET=$Triplet", "-DCMAKE_BUILD_TYPE=$Config") - Invoke-Checked cmake @("--build", $rtBuild, "--config", $Config, "--parallel", "$Jobs", "--target", "retro-core-runner") - if (Test-Path $rtOut) { Remove-Item -Recurse -Force $rtOut } - Invoke-Checked cmake @("--install", $rtBuild, "--config", $Config, "--prefix", $rtOut) - $RunnerSrc = Join-Path $rtOut "bin\retro-core-runner.exe" - } - $RunnerFrom = $Runtime -} +# CMake built the runner and shared link from the same selected checkout, +# using the hub's SDL3 target. A separate runner build could silently lose GL. +if ($Runtime) { $RunnerFrom = $Runtime } + if (-not (Test-Path $RunnerSrc)) { Fail "no retro-core-runner.exe at $RunnerSrc" } # ---- the flat prefix ---------------------------------------------------------------- diff --git a/scripts/build-local.sh b/scripts/build-local.sh index 56e615e..197c73d 100755 --- a/scripts/build-local.sh +++ b/scripts/build-local.sh @@ -40,9 +40,8 @@ machine, and print its path last, as RETRO_HUB=<absolute path>. --debug Debug build (default: Release) --out DIR the flat prefix (default: <repo>/out/local/<platform>/) --build DIR CMake build directory (default: <repo>/build-local[-debug]) - --runtime DIR build the runner from this Retro-Runtime checkout (its - scripts/build-local.sh when it has one) instead of the - submodule's + --runtime DIR build the shared link and runner from this Retro-Runtime + checkout instead of the submodule's --jobs N parallel build jobs (default: all CPUs) --release-version V build as release V (-DRETCOMM_VERSION, -DRETCOMM_RELEASE_BUILD): @@ -136,6 +135,7 @@ cmake_args=( "-DCMAKE_BUILD_TYPE=${CONFIG}" "-DRETCOMM_COMMIT=${COMMIT}" ) +[[ -z "${RUNTIME}" ]] || cmake_args+=("-DRETCOMM_RUNTIME_DIR=${RUNTIME}") [[ -z "${RELEASE_VERSION}" ]] || cmake_args+=("-DRETCOMM_VERSION=${RELEASE_VERSION}" -DRETCOMM_RELEASE_BUILD=ON) command -v ninja >/dev/null && cmake_args+=(-G Ninja) [[ -z "${CMAKE_PREFIX_PATH:-}" ]] || cmake_args+=("-DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH}") @@ -166,33 +166,8 @@ cmake --install "${BUILD}" --prefix "${STAGE}" >/dev/null RUNNER_SRC="${STAGE}/bin/retro-core-runner" RUNNER_FROM="the Retro-Runtime submodule ($(git -C "${ROOT}/third_party/Retro-Runtime" rev-parse --short HEAD 2>/dev/null || echo '?'))" RUNNER_LIBS=() +# CMake built both the link and runner against the same SDL3 target. if [[ -n "${RUNTIME}" ]]; then - RT_OUT="${BUILD}/runtime-out" RT_BUILD="${BUILD}/runtime-build" - if [[ -x "${RUNTIME}/scripts/build-local.sh" ]]; then - say "building the runner with ${RUNTIME}/scripts/build-local.sh" - rt_args=(--out "${RT_OUT}" --build "${RT_BUILD}" --jobs "${JOBS}") - [[ "${CONFIG}" == Debug ]] && rt_args+=(--debug) - rt_report="$("${RUNTIME}/scripts/build-local.sh" "${rt_args[@]}")" || die "${RUNTIME}/scripts/build-local.sh failed" - RUNNER_SRC="$(tail -1 <<<"${rt_report}" | sed -n 's/^RETRO_CORE_RUNNER=//p')" - [[ -x "${RUNNER_SRC}" ]] || die "${RUNTIME}/scripts/build-local.sh printed no RETRO_CORE_RUNNER= line" - shopt -s nullglob - RUNNER_LIBS=("$(dirname "${RUNNER_SRC}")"/libSDL3*) - shopt -u nullglob - else - say "building the runner from ${RUNTIME} with cmake (it has no scripts/build-local.sh)" - rt_cmake=(-S "${RUNTIME}" -B "${RT_BUILD}" "-DCMAKE_BUILD_TYPE=${CONFIG}") - command -v ninja >/dev/null && rt_cmake+=(-G Ninja) - [[ -z "${CMAKE_PREFIX_PATH:-}" ]] || rt_cmake+=("-DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH}") - case "${OS}" in - linux) rt_cmake+=("-DCMAKE_INSTALL_RPATH=\$ORIGIN") ;; - macos) rt_cmake+=("-DCMAKE_INSTALL_RPATH=@loader_path") ;; - esac - cmake "${rt_cmake[@]}" >&2 - cmake --build "${RT_BUILD}" --parallel "${JOBS}" --target retro-core-runner >&2 - rm -rf "${RT_OUT}" - cmake --install "${RT_BUILD}" --prefix "${RT_OUT}" >/dev/null - RUNNER_SRC="${RT_OUT}/bin/retro-core-runner" - fi RUNNER_FROM="${RUNTIME} ($(git -C "${RUNTIME}" rev-parse --short HEAD 2>/dev/null || echo '?'))" fi [[ -x "${RUNNER_SRC}" ]] || die "no retro-core-runner at ${RUNNER_SRC}" diff --git a/src/hub/hub_main.cpp b/src/hub/hub_main.cpp index b98560b..a4875e0 100644 --- a/src/hub/hub_main.cpp +++ b/src/hub/hub_main.cpp @@ -18457,7 +18457,7 @@ int run_direct_home(SDL_Window* window, UiScale& ui, const Theme& th, const Dire if (core_settings_capture_event(hub, e)) continue; ImGui_ImplSDL3_ProcessEvent(&e); } - if (play) play->tick(); + if (play) play->tick(window); ImGui_ImplOpenGL3_NewFrame(); ImGui_ImplSDL3_NewFrame(); @@ -18608,7 +18608,7 @@ int run_direct_play(SDL_Window* window, UiScale& ui, const DirectPlay& d, const SDL_SetWindowFullscreen(window, !fs_now); } } - play.tick(); + play.tick(window); ImGui_ImplOpenGL3_NewFrame(); ImGui_ImplSDL3_NewFrame(); retcomm::hub::overlay::pad_nav_frame(pad_capture_active()); @@ -19226,7 +19226,7 @@ int main(int argc, char** argv) { } } } - if (play) play->tick(); + if (play) play->tick(window); #endif if (!play_title_id.empty()) { if (!play_requested && !setup_pending) { diff --git a/src/hub/hub_play.cpp b/src/hub/hub_play.cpp index 1afdc22..cceea76 100644 --- a/src/hub/hub_play.cpp +++ b/src/hub/hub_play.cpp @@ -385,7 +385,11 @@ void PlaySession::grant(std::uint64_t) { p.connected = connected; } } +#if defined(RETCOMM_LINK_PRESENTATION_SIZE) + link_.grant(pads, presentation_width_, presentation_height_); +#else link_.grant(pads); +#endif } // Turbo: grant again as soon as a frame is done, for most of a hub frame, and @@ -565,7 +569,14 @@ void PlaySession::upload_frame() { picture_.upload(link_.frame_pixels(), f->width, f->height, f->aspect_num, f->aspect_den); } -void PlaySession::tick() { +void PlaySession::tick(SDL_Window* window) { + // Physical drawable pixels before Picture's aspect letterbox. Sample after + // events, before any grant (including turbo), rather than last frame's UI. + int width = 0, height = 0; + if (window && !(SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED)) + SDL_GetWindowSizeInPixels(window, &width, &height); + presentation_width_ = width > 0 && height > 0 ? static_cast<std::uint32_t>(width) : 0; + presentation_height_ = width > 0 && height > 0 ? static_cast<std::uint32_t>(height) : 0; link_.pump(0); const bool ready = link_.state() == corelink::LinkState::Ready; if (ready) poll_host_combos(); diff --git a/src/hub/hub_play.hpp b/src/hub/hub_play.hpp index 9fab4a7..0290e15 100644 --- a/src/hub/hub_play.hpp +++ b/src/hub/hub_play.hpp @@ -105,7 +105,7 @@ class PlaySession { // the session's own (menu toggle) and should not reach ImGui. bool handle_event(const SDL_Event& e); // Once per hub frame: link messages, pacing and grants, audio, saves. - void tick(); + void tick(SDL_Window* window); // Inside an ImGui frame: the picture behind everything, then whichever // overlay applies (loading, quick menu, fault). void draw(); @@ -154,6 +154,7 @@ class PlaySession { corelink::CoreLink link_; PlayArgs args_; + std::uint32_t presentation_width_ = 0, presentation_height_ = 0; bool menu_open_ = false; bool menu_focus_ = false; // the menu just opened: put the nav ring on it bool netplay_ = false; diff --git a/third_party/Retro-Runtime b/third_party/Retro-Runtime index c932598..c6209ca 160000 --- a/third_party/Retro-Runtime +++ b/third_party/Retro-Runtime @@ -1 +1 @@ -Subproject commit c93259883cd6f79991c4d5b3631687b46e67d323 +Subproject commit c6209ca026b99f70943504beeb8b41d6eb57cd5f