From ea8b0a2d3290697c65fea102420e09f7e5917062 Mon Sep 17 00:00:00 2001 From: reuk Date: Mon, 29 Nov 2021 16:49:38 +0000 Subject: [PATCH] OpenGL: Ensure that GL views display at the correct scale on macOS 12 Previously, we were using the window's top-left position to determine the scale to use for the OpenGLContext. However, on macOS the backingScaleFactor of the window is not strictly related to the top-left corner of the window, so the OpenGL view's scale could end up differing from the backing scale factor when slowly moving a window between displays with different backing scale factors. On macOS, we now use the backing scale factor of the window's screen (as maintained by AppKit), rather than trying to work out the correct display and scale ourselves. --- .../juce_opengl/opengl/juce_OpenGLContext.cpp | 70 +++++++++++-------- 1 file changed, 39 insertions(+), 31 deletions(-) diff --git a/modules/juce_opengl/opengl/juce_OpenGLContext.cpp b/modules/juce_opengl/opengl/juce_OpenGLContext.cpp index c9c02f442a..60c121577d 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLContext.cpp +++ b/modules/juce_opengl/opengl/juce_OpenGLContext.cpp @@ -161,13 +161,6 @@ public: void resume() { - #if JUCE_MAC - initialDisplayTopLeft = Desktop::getInstance().getDisplays() - .getDisplayForRect (component.getTopLevelComponent()->getScreenBounds()) - ->totalArea - .getTopLeft(); - #endif - if (renderThread != nullptr) renderThread->addJob (this, false); } @@ -314,23 +307,26 @@ public: if (auto* peer = component.getPeer()) { - auto localBounds = component.getLocalBounds(); - const auto currentDisplay = Desktop::getInstance().getDisplays().getDisplayForRect (component.getTopLevelComponent()->getScreenBounds()); - - if (currentDisplay != lastDisplay) + #if JUCE_MAC + const auto displayScale = [this] { - #if JUCE_MAC - if (cvDisplayLinkWrapper != nullptr) + if (auto* wrapper = cvDisplayLinkWrapper.get()) { - cvDisplayLinkWrapper->updateActiveDisplay (currentDisplay->totalArea.getTopLeft()); - nativeContext->setNominalVideoRefreshPeriodS (cvDisplayLinkWrapper->getNominalVideoRefreshPeriodS()); + if (auto* screen = wrapper->updateAndReturnActiveDisplay()) + { + nativeContext->setNominalVideoRefreshPeriodS (wrapper->getNominalVideoRefreshPeriodS()); + + return [screen backingScaleFactor]; + } } - #endif - lastDisplay = currentDisplay; - } - const auto displayScale = currentDisplay->scale; + return scale; + }(); + #else + const auto displayScale = Desktop::getInstance().getDisplays().getDisplayForRect (component.getTopLevelComponent()->getScreenBounds())->scale; + #endif + auto localBounds = component.getLocalBounds(); auto newArea = peer->getComponent().getLocalArea (&component, localBounds).withZeroOrigin() * displayScale; #if JUCE_WINDOWS && JUCE_WIN_PER_MONITOR_DPI_AWARE @@ -590,7 +586,7 @@ public: #if JUCE_MAC cvDisplayLinkWrapper = std::make_unique (*this); - cvDisplayLinkWrapper->updateActiveDisplay (initialDisplayTopLeft); + cvDisplayLinkWrapper->updateAndReturnActiveDisplay(); nativeContext->setNominalVideoRefreshPeriodS (cvDisplayLinkWrapper->getNominalVideoRefreshPeriodS()); #endif @@ -709,13 +705,9 @@ public: OpenGLFrameBuffer cachedImageFrameBuffer; RectangleList validArea; Rectangle viewportArea, lastScreenBounds; - const Displays::Display* lastDisplay = nullptr; double scale = 1.0; AffineTransform transform; GLuint vertexArrayObject = 0; - #if JUCE_MAC - Point initialDisplayTopLeft; - #endif StringArray associatedObjectNames; ReferenceCountedArray associatedObjects; @@ -752,15 +744,30 @@ public: return 0.0; } - void updateActiveDisplay (Point topLeftOfDisplay) + NSScreen* getCurrentScreen() const { - CGDirectDisplayID displayID; - uint32_t numDisplays = 0; - constexpr uint32_t maxNumDisplays = 1; - CGGetDisplaysWithPoint (convertToCGPoint (topLeftOfDisplay), maxNumDisplays, &displayID, &numDisplays); + if (auto* peer = cachedImage.component.getPeer()) + if (auto* view = static_cast (peer->getNativeHandle())) + if (auto* window = [view window]) + return [window screen]; - if (numDisplays == 1) - CVDisplayLinkSetCurrentCGDisplay (displayLink, displayID); + return nullptr; + } + + /* If updated, returns the new screen, or nullptr otherwise. */ + NSScreen* updateAndReturnActiveDisplay() + { + auto* oldScreen = std::exchange (currentScreen, getCurrentScreen()); + + if (oldScreen == currentScreen) + return nullptr; + + for (NSScreen* screen in [NSScreen screens]) + if (screen == currentScreen) + if (NSNumber* number = [[screen deviceDescription] objectForKey: @"NSScreenNumber"]) + CVDisplayLinkSetCurrentCGDisplay (displayLink, [number unsignedIntValue]); + + return currentScreen; } ~CVDisplayLinkWrapper() @@ -783,6 +790,7 @@ public: CachedImage& cachedImage; const bool continuousRepaint; CVDisplayLinkRef displayLink; + NSScreen* currentScreen = nullptr; }; std::unique_ptr cvDisplayLinkWrapper;