Skip to content

Commit 2ac3523

Browse files
committed
feat(macOS): true dynamic-resolution window resize (recreate EGL pbuffer)
The Zink/KosmicKrisp path rendered into a fixed-size pbuffer set at startup and ReadWindowPosAndSize pinned the engine view size to it, so resizing only scaled a fixed image. Save the EGLConfig and add ResizeEGLSurfaceIfNeeded(): on the window-resize path it recreates the pbuffer at the window's current pixel size and updates g_pbufW/H, so winSize binds to the new size and the engine renders at the new resolution. The Metal drawable + IOSurface already follow g_pbufW/H per present. Verified: window resize -> pbuffer recreated, viewport follows, drawable 1:1, no crash.
1 parent 9fb4bd7 commit 2ac3523

1 file changed

Lines changed: 54 additions & 0 deletions

File tree

rts/Rendering/GlobalRendering.cpp

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
static EGLDisplay g_eglDisplay = EGL_NO_DISPLAY;
1919
static EGLContext g_eglContext = EGL_NO_CONTEXT;
2020
static EGLSurface g_eglSurface = EGL_NO_SURFACE;
21+
static EGLConfig g_eglConfig = nullptr; // saved so the pbuffer can be recreated on resize
2122
static void* g_metalLayer = nullptr; // CAMetalLayer attached to the window's NSView
2223
static int g_pbufW = 1280; // pbuffer (default framebuffer) dimensions
2324
static int g_pbufH = 720;
@@ -162,6 +163,7 @@ static bool InitEGLContext(SDL_Window* window, int major, int minor) {
162163
(int)cfgOk, numConfigs, eglGetError());
163164
if (!cfgOk || numConfigs == 0)
164165
return false;
166+
g_eglConfig = eglConfig; // remember for pbuffer recreation on window resize
165167

166168
void* nativeView = GetNSViewFromSDLWindow(window);
167169
g_metalLayer = nativeView; // CAMetalLayer for the manual present path
@@ -314,6 +316,52 @@ static void DestroyEGLContext() {
314316
g_eglContext = EGL_NO_CONTEXT;
315317
g_eglSurface = EGL_NO_SURFACE;
316318
}
319+
320+
// Recreate the pbuffer (the engine's default framebuffer) at the window's
321+
// current pixel size so the window resizes at true resolution instead of
322+
// scaling a fixed-size buffer. Called from ReadWindowPosAndSize on resize,
323+
// before winSize is bound to g_pbufW. No-op if the size is unchanged or the
324+
// EGL context is not up yet. The CAMetalLayer drawable auto-follows because
325+
// the present path sizes it from g_pbufW/g_pbufH each frame.
326+
static void ResizeEGLSurfaceIfNeeded(SDL_Window* window) {
327+
if (g_eglDisplay == EGL_NO_DISPLAY || g_eglContext == EGL_NO_CONTEXT || g_eglConfig == nullptr)
328+
return;
329+
330+
int winW = 0, winH = 0;
331+
SDL_GetWindowSize(window, &winW, &winH);
332+
if (winW <= 0 || winH <= 0)
333+
return;
334+
335+
const bool noRetina = (getenv("SPRING_MAC_NO_RETINA") != nullptr);
336+
const double bsf = noRetina ? 1.0 : GetBackingScaleFactor(window);
337+
const int pxW = std::max(1, int(std::lround(double(winW) * bsf)));
338+
const int pxH = std::max(1, int(std::lround(double(winH) * bsf)));
339+
340+
if (pxW == g_pbufW && pxH == g_pbufH)
341+
return; // size unchanged
342+
343+
EGLint pbAttribs[] = { EGL_WIDTH, pxW, EGL_HEIGHT, pxH, EGL_NONE };
344+
EGLSurface newSurface = eglCreatePbufferSurface(g_eglDisplay, g_eglConfig, pbAttribs);
345+
if (newSurface == EGL_NO_SURFACE) {
346+
fprintf(stderr, "[EGL] resize: eglCreatePbufferSurface %dx%d failed (0x%x); keeping %dx%d\n",
347+
pxW, pxH, eglGetError(), g_pbufW, g_pbufH);
348+
return; // keep the old surface rather than lose the context
349+
}
350+
351+
if (!eglMakeCurrent(g_eglDisplay, newSurface, newSurface, g_eglContext)) {
352+
fprintf(stderr, "[EGL] resize: eglMakeCurrent failed (0x%x); reverting\n", eglGetError());
353+
eglMakeCurrent(g_eglDisplay, g_eglSurface, g_eglSurface, g_eglContext);
354+
eglDestroySurface(g_eglDisplay, newSurface);
355+
return;
356+
}
357+
358+
if (g_eglSurface != EGL_NO_SURFACE)
359+
eglDestroySurface(g_eglDisplay, g_eglSurface);
360+
g_eglSurface = newSurface;
361+
g_pbufW = pxW;
362+
g_pbufH = pxH;
363+
fprintf(stderr, "[EGL] resize: pbuffer -> %dx%d px (window %dx%d * %.2f)\n", pxW, pxH, winW, winH, bsf);
364+
}
317365
#endif // __APPLE__ && !HEADLESS
318366

319367
#include "GlobalRenderingInfo.h"
@@ -2249,6 +2297,12 @@ void CGlobalRendering::ReadWindowPosAndSize()
22492297
SDL_GetWindowSize(sdlWindow, &winSizeX, &winSizeY);
22502298
#if defined(__APPLE__) && !defined(HEADLESS)
22512299
{
2300+
// True dynamic-resolution resize: recreate the pbuffer FBO at the
2301+
// window's current pixel size, so winSize below binds to the NEW size
2302+
// and the engine renders at the new resolution (rather than scaling a
2303+
// fixed startup buffer). No-op if unchanged or pre-EGL-init.
2304+
ResizeEGLSurfaceIfNeeded(sdlWindow);
2305+
22522306
// Engine contract: winSize/viewSize must match the pbuffer FBO the
22532307
// engine actually renders into — *not* the CAMetalLayer drawableSize.
22542308
// They are equal by accident at full Retina (drawable == backing ==

0 commit comments

Comments
 (0)