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.