Fix double-scaling by locking page layout to logical presentation

Pages computed layout from SDL_GetWindowSize() (real window
coordinates) while SDL_SetRenderLogicalPresentation() independently
remapped every draw call into a 1920x1080 space. The two coordinate
systems only agreed when the window happened to be exactly 1920x1080;
at any other size, layout coordinates were transformed a second time,
so content shrank/grew quadratically toward the top-left on resize or
on non-1080p monitors. Commenting the call out masked the bug by
making rendering 1:1 with the window, but then nothing scaled: fonts
and margins stayed fixed-pixel while backgrounds cover-fit, and HiDPI
displays drew at point resolution and relied on blurry compositor
upsampling.

Restore SDL_SetRenderLogicalPresentation(1920x1080, LETTERBOX) as the
single source of scaling, and route all twelve layout sites through a
new radiantwave::getLayoutSize() helper (src/logical_size.h) that
returns the renderer's logical size, so pages always lay out against
the design resolution regardless of window or monitor geometry. Add
SDL_WINDOW_HIGH_PIXEL_DENSITY so HiDPI displays render at native pixel
density instead of being upscaled by the OS. Cached per-page textures
become resolution-independent since layout no longer depends on window
size.
This commit is contained in:
NeilClack
2026-08-21 09:29:23 -05:00
parent 8e930ef8d9
commit 3c9de0bee2
14 changed files with 47 additions and 18 deletions
+2 -1
View File
@@ -1,5 +1,6 @@
#include "audio_settings_page.h" #include "audio_settings_page.h"
#include "app_state.h" #include "app_state.h"
#include "logical_size.h"
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
#include <algorithm> #include <algorithm>
#include <cctype> #include <cctype>
@@ -477,7 +478,7 @@ void AudioSettingsPage::renderModal(SDL_Renderer* renderer, float fw, float fh,
void AudioSettingsPage::render(SDL_Renderer* renderer) { void AudioSettingsPage::render(SDL_Renderer* renderer) {
int winW, winH; int winW, winH;
SDL_GetWindowSize(SDL_GetRenderWindow(renderer), &winW, &winH); radiantwave::getLayoutSize(renderer, &winW, &winH);
float fw = static_cast<float>(winW); float fw = static_cast<float>(winW);
float fh = static_cast<float>(winH); float fh = static_cast<float>(winH);
+2 -1
View File
@@ -1,6 +1,7 @@
#include "date_format_page.h" #include "date_format_page.h"
#include "app_state.h" #include "app_state.h"
#include "date_format.h" #include "date_format.h"
#include "logical_size.h"
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
#include <algorithm> #include <algorithm>
#include <ctime> #include <ctime>
@@ -131,7 +132,7 @@ void DateFormatPage::update(float dt)
void DateFormatPage::render(SDL_Renderer* renderer) void DateFormatPage::render(SDL_Renderer* renderer)
{ {
int winW, winH; int winW, winH;
SDL_GetWindowSize(SDL_GetRenderWindow(renderer), &winW, &winH); radiantwave::getLayoutSize(renderer, &winW, &winH);
float fw = static_cast<float>(winW); float fw = static_cast<float>(winW);
float fh = static_cast<float>(winH); float fh = static_cast<float>(winH);
+2 -1
View File
@@ -3,6 +3,7 @@
#include "db/db_keys.h" #include "db/db_keys.h"
#include "provisioning.h" #include "provisioning.h"
#include "validation.h" #include "validation.h"
#include "logical_size.h"
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
namespace { namespace {
@@ -164,7 +165,7 @@ void EmailAddressPage::exitPage(AppState& state)
void EmailAddressPage::render(SDL_Renderer* renderer) void EmailAddressPage::render(SDL_Renderer* renderer)
{ {
int winW, winH; int winW, winH;
SDL_GetWindowSize(SDL_GetRenderWindow(renderer), &winW, &winH); radiantwave::getLayoutSize(renderer, &winW, &winH);
float fw = static_cast<float>(winW); float fw = static_cast<float>(winW);
float fh = static_cast<float>(winH); float fh = static_cast<float>(winH);
+2 -1
View File
@@ -8,6 +8,7 @@
#include "audio/audio_downloader.h" #include "audio/audio_downloader.h"
#include "db/db_keys.h" #include "db/db_keys.h"
#include "network/proclamation_sync.h" #include "network/proclamation_sync.h"
#include "logical_size.h"
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
#include <SDL3_image/SDL_image.h> #include <SDL3_image/SDL_image.h>
#include <ctime> #include <ctime>
@@ -114,7 +115,7 @@ void Home::update(float dt)
void Home::render(SDL_Renderer *renderer) void Home::render(SDL_Renderer *renderer)
{ {
int winW, winH; int winW, winH;
SDL_GetWindowSize(SDL_GetRenderWindow(renderer), &winW, &winH); radiantwave::getLayoutSize(renderer, &winW, &winH);
float fw = static_cast<float>(winW); float fw = static_cast<float>(winW);
float fh = static_cast<float>(winH); float fh = static_cast<float>(winH);
+2 -1
View File
@@ -1,6 +1,7 @@
#include "license_blocked_page.h" #include "license_blocked_page.h"
#include "app_state.h" #include "app_state.h"
#include "home.h" #include "home.h"
#include "logical_size.h"
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
void LicenseBlockedPage::onPush(AppState& state) void LicenseBlockedPage::onPush(AppState& state)
@@ -71,7 +72,7 @@ void LicenseBlockedPage::update(float dt)
void LicenseBlockedPage::render(SDL_Renderer* renderer) void LicenseBlockedPage::render(SDL_Renderer* renderer)
{ {
int winW, winH; int winW, winH;
SDL_GetWindowSize(SDL_GetRenderWindow(renderer), &winW, &winH); radiantwave::getLayoutSize(renderer, &winW, &winH);
float fw = static_cast<float>(winW); float fw = static_cast<float>(winW);
float fh = static_cast<float>(winH); float fh = static_cast<float>(winH);
+2 -1
View File
@@ -4,6 +4,7 @@
#include "network/license_service.h" #include "network/license_service.h"
#include "provisioning.h" #include "provisioning.h"
#include "validation.h" #include "validation.h"
#include "logical_size.h"
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
void LicenseKeyPage::onPush(AppState& state) void LicenseKeyPage::onPush(AppState& state)
@@ -172,7 +173,7 @@ void LicenseKeyPage::exitPage(AppState& state)
void LicenseKeyPage::render(SDL_Renderer* renderer) void LicenseKeyPage::render(SDL_Renderer* renderer)
{ {
int winW, winH; int winW, winH;
SDL_GetWindowSize(SDL_GetRenderWindow(renderer), &winW, &winH); radiantwave::getLayoutSize(renderer, &winW, &winH);
float fw = static_cast<float>(winW); float fw = static_cast<float>(winW);
float fh = static_cast<float>(winH); float fh = static_cast<float>(winH);
+2 -1
View File
@@ -1,6 +1,7 @@
#include "log_viewer_page.h" #include "log_viewer_page.h"
#include "app_state.h" #include "app_state.h"
#include "timezone_helper.h" #include "timezone_helper.h"
#include "logical_size.h"
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
#include <algorithm> #include <algorithm>
#include <sstream> #include <sstream>
@@ -162,7 +163,7 @@ void LogViewerPage::update(float dt) {
void LogViewerPage::render(SDL_Renderer* renderer) { void LogViewerPage::render(SDL_Renderer* renderer) {
int winW, winH; int winW, winH;
SDL_GetWindowSize(SDL_GetRenderWindow(renderer), &winW, &winH); radiantwave::getLayoutSize(renderer, &winW, &winH);
float fw = static_cast<float>(winW); float fw = static_cast<float>(winW);
float fh = static_cast<float>(winH); float fh = static_cast<float>(winH);
+17
View File
@@ -0,0 +1,17 @@
#pragma once
#include <SDL3/SDL.h>
namespace radiantwave {
inline void getLayoutSize(SDL_Renderer* renderer, int* w, int* h)
{
SDL_RendererLogicalPresentation mode = SDL_LOGICAL_PRESENTATION_DISABLED;
if (!SDL_GetRenderLogicalPresentation(renderer, w, h, &mode) ||
mode == SDL_LOGICAL_PRESENTATION_DISABLED || *w <= 0 || *h <= 0)
{
SDL_GetWindowSize(SDL_GetRenderWindow(renderer), w, h);
}
}
} // namespace radiantwave
+6 -6
View File
@@ -62,12 +62,12 @@ SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[]) {
// Lock the renderer to a 1920x1080 logical coordinate space. All page // Lock the renderer to a 1920x1080 logical coordinate space. All page
// layout code already targets this resolution, so everything renders at // layout code already targets this resolution, so everything renders at
// the correct size on any display — the GPU scales to native pixels. // the correct size on any display — the GPU scales to native pixels.
// if (!SDL_SetRenderLogicalPresentation(state->renderer, 1920, 1080, if (!SDL_SetRenderLogicalPresentation(state->renderer, 1920, 1080,
// SDL_LOGICAL_PRESENTATION_STRETCH)) { SDL_LOGICAL_PRESENTATION_STRETCH)) {
// radiantwave::logger::Logger::instance().warning("Graphics", radiantwave::logger::Logger::instance().warning(
// "Could not set logical render size; layout may be wrong on HiDPI "Graphics", "Could not set logical render size; layout may be "
// displays"); "wrong on HiDPI displays");
// } }
SDL_HideCursor(); SDL_HideCursor();
+2 -1
View File
@@ -1,6 +1,7 @@
#include "proclamation_options_page.h" #include "proclamation_options_page.h"
#include "app_state.h" #include "app_state.h"
#include "network/proclamation_sync.h" #include "network/proclamation_sync.h"
#include "logical_size.h"
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
#include <algorithm> #include <algorithm>
#include <cctype> #include <cctype>
@@ -552,7 +553,7 @@ void ProclamationOptionsPage::update(float dt)
void ProclamationOptionsPage::render(SDL_Renderer* renderer) void ProclamationOptionsPage::render(SDL_Renderer* renderer)
{ {
int winW, winH; int winW, winH;
SDL_GetWindowSize(SDL_GetRenderWindow(renderer), &winW, &winH); radiantwave::getLayoutSize(renderer, &winW, &winH);
float fw = static_cast<float>(winW); float fw = static_cast<float>(winW);
float fh = static_cast<float>(winH); float fh = static_cast<float>(winH);
+2 -1
View File
@@ -5,6 +5,7 @@
#include "proclamation_options_page.h" #include "proclamation_options_page.h"
#include "scramble/fibonacci_scrambler.h" #include "scramble/fibonacci_scrambler.h"
#include "utf8_utils.h" #include "utf8_utils.h"
#include "logical_size.h"
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
#include <SDL3_ttf/SDL_ttf.h> #include <SDL3_ttf/SDL_ttf.h>
@@ -35,7 +36,7 @@ void ScrollerPage::onPush(AppState &state)
int winW = 0; int winW = 0;
int winH = 0; int winH = 0;
SDL_GetWindowSize(state.window, &winW, &winH); radiantwave::getLayoutSize(state.renderer, &winW, &winH);
screenW_ = static_cast<float>(winW); screenW_ = static_cast<float>(winW);
screenH_ = static_cast<float>(winH); screenH_ = static_cast<float>(winH);
// Lines are capped at the full screen width; the widest Fibonacci lines are // Lines are capped at the full screen width; the widest Fibonacci lines are
+2 -1
View File
@@ -10,6 +10,7 @@
#include "email_address_page.h" #include "email_address_page.h"
#include "license_key_page.h" #include "license_key_page.h"
#include "date_format_page.h" #include "date_format_page.h"
#include "logical_size.h"
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
#include <SDL3_image/SDL_image.h> #include <SDL3_image/SDL_image.h>
#include <algorithm> #include <algorithm>
@@ -128,7 +129,7 @@ void Settings::update(float dt)
void Settings::render(SDL_Renderer *renderer) void Settings::render(SDL_Renderer *renderer)
{ {
int winW, winH; int winW, winH;
SDL_GetWindowSize(SDL_GetRenderWindow(renderer), &winW, &winH); radiantwave::getLayoutSize(renderer, &winW, &winH);
float fw = static_cast<float>(winW); float fw = static_cast<float>(winW);
float fh = static_cast<float>(winH); float fh = static_cast<float>(winH);
+2 -1
View File
@@ -1,5 +1,6 @@
#include "timezone_page.h" #include "timezone_page.h"
#include "app_state.h" #include "app_state.h"
#include "logical_size.h"
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
#include <algorithm> #include <algorithm>
#include <ctime> #include <ctime>
@@ -199,7 +200,7 @@ void TimeZonePage::update(float dt) {
void TimeZonePage::render(SDL_Renderer* renderer) { void TimeZonePage::render(SDL_Renderer* renderer) {
int winW, winH; int winW, winH;
SDL_GetWindowSize(SDL_GetRenderWindow(renderer), &winW, &winH); radiantwave::getLayoutSize(renderer, &winW, &winH);
float fw = static_cast<float>(winW); float fw = static_cast<float>(winW);
float fh = static_cast<float>(winH); float fh = static_cast<float>(winH);
+2 -1
View File
@@ -1,6 +1,7 @@
#include "wifi_setup_page.h" #include "wifi_setup_page.h"
#include "app_state.h" #include "app_state.h"
#include "provisioning.h" #include "provisioning.h"
#include "logical_size.h"
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
#include <algorithm> #include <algorithm>
#include <exception> #include <exception>
@@ -301,7 +302,7 @@ void WiFiSetupPage::update(float dt) {
void WiFiSetupPage::render(SDL_Renderer* renderer) { void WiFiSetupPage::render(SDL_Renderer* renderer) {
int winW, winH; int winW, winH;
SDL_GetWindowSize(SDL_GetRenderWindow(renderer), &winW, &winH); radiantwave::getLayoutSize(renderer, &winW, &winH);
float fw = static_cast<float>(winW); float fw = static_cast<float>(winW);
float fh = static_cast<float>(winH); float fh = static_cast<float>(winH);