From 7619122b70d6d7603abed01f5f475c87f7af5106 Mon Sep 17 00:00:00 2001 From: Jimmy Chang Date: Tue, 11 Aug 2026 01:37:59 +0800 Subject: [PATCH] Correct the drawing surface density on non-HiDPI displays MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The layer being rendered into can be left at a 2x contents scale while the window reports a 1x backing scale, making the real drawing surface twice the size the window system reports. Everything is then drawn into the lower-left quarter of the window, and only resizing the window or dragging it to another display — either of which rebuilds the surface — restores it. GLFW pushes the window's backing scale onto the layer only when the content scale changes, so a window that opens with the wrong scale and stays on one display is never corrected. Compare the layer's scale against the window's backing scale each frame, correct it when they diverge, and re-read the framebuffer size when it does. This also keeps the surface right when the window moves between displays of differing density. --- CMakeLists.txt | 1 + engine/render/r_main.cpp | 1 + engine/system/sys_video.h | 1 + engine/system/win/sys_macos.mm | 33 ++++++++++++++++++++++++++ engine/system/win/sys_platform_macos.h | 12 ++++++++++ engine/system/win/sys_video.cpp | 23 ++++++++++++++++++ 6 files changed, 71 insertions(+) create mode 100644 engine/system/win/sys_platform_macos.h diff --git a/CMakeLists.txt b/CMakeLists.txt index b992d62e..53dce986 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -80,6 +80,7 @@ set (SIMPLEGRAPHIC_PLATFORM_SOURCES) if (APPLE) set (SIMPLEGRAPHIC_PLATFORM_SOURCES "engine/system/win/sys_macos.mm" + "engine/system/win/sys_platform_macos.h" ) endif() diff --git a/engine/render/r_main.cpp b/engine/render/r_main.cpp index 1d46715f..268944ad 100644 --- a/engine/render/r_main.cpp +++ b/engine/render/r_main.cpp @@ -1164,6 +1164,7 @@ void r_renderer_c::BeginFrame() ImGui_ImplGlfw_NewFrame(); ImGui::NewFrame(); { + sys->video->SyncSurfaceScale(); auto& vid = sys->video->vid; int wNew = VirtualScreenWidth(); int hNew = VirtualScreenHeight(); diff --git a/engine/system/sys_video.h b/engine/system/sys_video.h index 73b3dd75..adce88d2 100644 --- a/engine/system/sys_video.h +++ b/engine/system/sys_video.h @@ -50,6 +50,7 @@ class sys_IVideo { virtual void SetForeground() = 0; // Activate the window if shown virtual bool IsActive() = 0; // Get activated status virtual void FramebufferSizeChanged(int width, int height) = 0; // Respond to framebuffer size change + virtual void SyncSurfaceScale() = 0; // Keep the drawing surface density matched to the display virtual void SizeChanged(int width, int height, bool max) = 0; // Respond to window size change virtual void PosChanged(int x, int y) = 0; // Respond to window position change virtual void GetMinSize(int &width, int &height) = 0; // Get minimum window size diff --git a/engine/system/win/sys_macos.mm b/engine/system/win/sys_macos.mm index 04136de1..8295fb4d 100644 --- a/engine/system/win/sys_macos.mm +++ b/engine/system/win/sys_macos.mm @@ -1,6 +1,13 @@ #include #include #include +#include + +#define GLFW_EXPOSE_NATIVE_COCOA +#include +#include + +#include "sys_platform_macos.h" const char* PlatformOpenURL(const char* textUrl) { @@ -10,3 +17,29 @@ CFRelease(url); return nullptr; } + +bool Platform_SyncLayerScale(GLFWwindow* window) +{ + // The layer the renderer draws into can be left at a pixel density that + // doesn't match the display the window is on, which makes the drawing + // surface larger than the size the window system reports. Everything then + // renders into a fraction of the window. + NSWindow* w = glfwGetCocoaWindow(window); + if (!w) { + return false; + } + NSView* v = [w contentView]; + CALayer* layer = [v layer]; + if (!layer) { + return false; + } + CGFloat want = [w backingScaleFactor]; + if (want <= 0.0 || fabs([layer contentsScale] - want) < 0.01) { + return false; + } + [layer setContentsScale:want]; + for (CALayer* sub in [layer sublayers]) { + [sub setContentsScale:want]; + } + return true; +} diff --git a/engine/system/win/sys_platform_macos.h b/engine/system/win/sys_platform_macos.h new file mode 100644 index 00000000..7a91806a --- /dev/null +++ b/engine/system/win/sys_platform_macos.h @@ -0,0 +1,12 @@ +// SimpleGraphic Engine +// +// macOS platform helpers +// + +#pragma once + +struct GLFWwindow; + +// Keep the layer being rendered into at the same pixel density as the display +// the window is on. Returns true when it had to be corrected. +bool Platform_SyncLayerScale(GLFWwindow* window); diff --git a/engine/system/win/sys_video.cpp b/engine/system/win/sys_video.cpp index 6f61bffb..0d4e5f6b 100644 --- a/engine/system/win/sys_video.cpp +++ b/engine/system/win/sys_video.cpp @@ -8,6 +8,9 @@ #include #include "sys_local.h" +#ifdef __APPLE__ +#include "sys_platform_macos.h" +#endif #include "core.h" #include @@ -32,6 +35,7 @@ class sys_video_c : public sys_IVideo { void SetForeground(); bool IsActive(); void FramebufferSizeChanged(int width, int height); + void SyncSurfaceScale(); void SizeChanged(int width, int height, bool max); void PosChanged(int x, int y); void GetMinSize(int& width, int& height); @@ -677,6 +681,25 @@ void sys_video_c::FramebufferSizeChanged(int width, int height) } } +void sys_video_c::SyncSurfaceScale() +{ +#ifdef __APPLE__ + if (!wnd) { + return; + } + if (Platform_SyncLayerScale(wnd)) { + // The surface was rebuilt at the correct density; re-read the size + // that describes it. + int fbW = 0, fbH = 0; + glfwGetFramebufferSize(wnd, &fbW, &fbH); + if (fbW > 0 && fbH > 0) { + vid.fbSize[0] = fbW; + vid.fbSize[1] = fbH; + } + } +#endif +} + void sys_video_c::SizeChanged(int width, int height, bool max) { // Avoid persisting an invalid window size from being minimized.