summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms/cocoa/qcocoaglcontext.mm
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/platforms/cocoa/qcocoaglcontext.mm')
-rw-r--r--src/plugins/platforms/cocoa/qcocoaglcontext.mm192
1 files changed, 69 insertions, 123 deletions
diff --git a/src/plugins/platforms/cocoa/qcocoaglcontext.mm b/src/plugins/platforms/cocoa/qcocoaglcontext.mm
index 6db4bdb9fd..a65311175f 100644
--- a/src/plugins/platforms/cocoa/qcocoaglcontext.mm
+++ b/src/plugins/platforms/cocoa/qcocoaglcontext.mm
@@ -1,53 +1,18 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include <AppKit/AppKit.h>
#include "qcocoaglcontext.h"
#include "qcocoawindow.h"
#include "qcocoahelpers.h"
#include "qcocoascreen.h"
+#include <QtCore/private/qcore_mac_p.h>
+
#include <qdebug.h>
-#include <QtPlatformHeaders/qcocoanativecontext.h>
#include <dlfcn.h>
-#import <AppKit/AppKit.h>
-
static inline QByteArray getGlString(GLenum param)
{
if (const GLubyte *s = glGetString(param))
@@ -65,30 +30,23 @@ QCocoaGLContext::QCocoaGLContext(QOpenGLContext *context)
{
}
-void QCocoaGLContext::initialize()
+QCocoaGLContext::QCocoaGLContext(NSOpenGLContext *nativeContext)
+ : QPlatformOpenGLContext()
{
- QVariant nativeHandle = context()->nativeHandle();
- if (!nativeHandle.isNull()) {
- if (!nativeHandle.canConvert<QCocoaNativeContext>()) {
- qCWarning(lcQpaOpenGLContext, "QOpenGLContext native handle must be a QCocoaNativeContext");
- return;
- }
- m_context = nativeHandle.value<QCocoaNativeContext>().context();
- if (!m_context) {
- qCWarning(lcQpaOpenGLContext, "QCocoaNativeContext's NSOpenGLContext cannot be null");
- return;
- }
-
- [m_context retain];
-
- // Note: We have no way of knowing whether the NSOpenGLContext was created with the
- // share context as reported by the QOpenGLContext, but we just have to trust that
- // it was. It's okey, as the only thing we're using it for is to report isShared().
- if (QPlatformOpenGLContext *shareContext = context()->shareHandle())
- m_shareContext = static_cast<QCocoaGLContext *>(shareContext)->nativeContext();
+ m_context = [nativeContext retain];
+}
- updateSurfaceFormat();
- return;
+void QCocoaGLContext::initialize()
+{
+ if (m_context) {
+ // Note: We have no way of knowing whether the NSOpenGLContext was created with the
+ // share context as reported by the QOpenGLContext, but we just have to trust that
+ // it was. It's okey, as the only thing we're using it for is to report isShared().
+ if (QPlatformOpenGLContext *shareContext = context()->shareHandle())
+ m_shareContext = static_cast<QCocoaGLContext *>(shareContext)->nativeContext();
+
+ updateSurfaceFormat();
+ return;
}
// ----------- Default case, we own the NSOpenGLContext -----------
@@ -139,23 +97,20 @@ void QCocoaGLContext::initialize()
return;
}
- // The native handle should reflect the underlying context, even if we created it
- context()->setNativeHandle(QVariant::fromValue<QCocoaNativeContext>(m_context));
-
// --------------------- Set NSOpenGLContext properties ---------------------
const GLint interval = m_format.swapInterval() >= 0 ? m_format.swapInterval() : 1;
- [m_context setValues:&interval forParameter:NSOpenGLCPSwapInterval];
+ [m_context setValues:&interval forParameter:NSOpenGLContextParameterSwapInterval];
if (m_format.alphaBufferSize() > 0) {
int zeroOpacity = 0;
- [m_context setValues:&zeroOpacity forParameter:NSOpenGLCPSurfaceOpacity];
+ [m_context setValues:&zeroOpacity forParameter:NSOpenGLContextParameterSurfaceOpacity];
}
// OpenGL surfaces can be ordered either above(default) or below the NSWindow
// FIXME: Promote to QSurfaceFormat option or property
const GLint order = qt_mac_resolveOption(1, "QT_MAC_OPENGL_SURFACE_ORDER");
- [m_context setValues:&order forParameter:NSOpenGLCPSurfaceOrder];
+ [m_context setValues:&order forParameter:NSOpenGLContextParameterSurfaceOrder];
updateSurfaceFormat();
@@ -342,7 +297,7 @@ void QCocoaGLContext::updateSurfaceFormat()
return value;
};
- m_format.setSwapInterval(glContextParameter(NSOpenGLCPSwapInterval));
+ m_format.setSwapInterval(glContextParameter(NSOpenGLContextParameterSwapInterval));
if (oldContext)
[oldContext makeCurrentContext];
@@ -357,6 +312,8 @@ QCocoaGLContext::~QCocoaGLContext()
bool QCocoaGLContext::makeCurrent(QPlatformSurface *surface)
{
+ QMacAutoReleasePool pool;
+
qCDebug(lcQpaOpenGLContext) << "Making" << this << "current"
<< "in" << QThread::currentThread() << "for" << surface;
@@ -390,7 +347,7 @@ bool QCocoaGLContext::setDrawable(QPlatformSurface *surface)
// Clear the current drawable and reset the active window, so that GL
// commands that don't target a specific FBO will not end up stomping
// on the previously set drawable.
- qCDebug(lcQpaOpenGLContext) << "Clearing current drawable" << m_context.view << "for" << m_context;
+ qCDebug(lcQpaOpenGLContext) << "Clearing current drawable" << QT_IGNORE_DEPRECATIONS(m_context.view) << "for" << m_context;
[m_context clearDrawable];
return true;
}
@@ -399,10 +356,16 @@ bool QCocoaGLContext::setDrawable(QPlatformSurface *surface)
auto *cocoaWindow = static_cast<QCocoaWindow *>(surface);
QNSView *view = qnsview_cast(cocoaWindow->view());
- if (view == m_context.view)
+ if (view == QT_IGNORE_DEPRECATIONS(m_context.view))
return true;
- prepareDrawable(cocoaWindow);
+ // We generally want high-DPI GL surfaces, unless the user has explicitly disabled them.
+ // According to the documentation, layer-backed views ignore wantsBestResolutionOpenGLSurface
+ // and configure their own backing surface at an appropriate resolution, but in some cases
+ // we've seen this fail (plugin views embedded in surface-backed hosts), so we do it anyways.
+ QT_IGNORE_DEPRECATIONS(view.wantsBestResolutionOpenGLSurface) = qt_mac_resolveOption(YES,
+ cocoaWindow->window(), "_q_mac_wantsBestResolutionOpenGLSurface",
+ "QT_MAC_WANTS_BEST_RESOLUTION_OPENGL_SURFACE");
// Setting the drawable may happen on a separate thread as a result of
// a call to makeCurrent, so we need to set up the observers before we
@@ -412,68 +375,49 @@ bool QCocoaGLContext::setDrawable(QPlatformSurface *surface)
auto updateCallback = [this, view]() {
Q_ASSERT(QThread::currentThread() == qApp->thread());
- if (m_context.view != view)
+ if (QT_IGNORE_DEPRECATIONS(m_context.view) != view)
return;
m_needsUpdate = true;
};
m_updateObservers.clear();
- if (view.layer) {
- m_updateObservers.append(QMacNotificationObserver(view, NSViewFrameDidChangeNotification, updateCallback));
- m_updateObservers.append(QMacNotificationObserver(view.window, NSWindowDidChangeScreenNotification, updateCallback));
- } else {
- m_updateObservers.append(QMacNotificationObserver(view, NSViewGlobalFrameDidChangeNotification, updateCallback));
- }
+ m_updateObservers.append(QMacNotificationObserver(view, NSViewFrameDidChangeNotification, updateCallback));
+ m_updateObservers.append(QMacNotificationObserver(view.window, NSWindowDidChangeScreenNotification, updateCallback));
m_updateObservers.append(QMacNotificationObserver([NSApplication sharedApplication],
NSApplicationDidChangeScreenParametersNotification, updateCallback));
+ m_updateObservers.append(QMacNotificationObserver(view,
+ QCocoaWindowWillReleaseQNSViewNotification, [this, view] {
+ if (QT_IGNORE_DEPRECATIONS(m_context.view) != view)
+ return;
+ qCDebug(lcQpaOpenGLContext) << view << "about to be released."
+ << "Clearing current drawable for" << m_context;
+ [m_context clearDrawable];
+ }));
+
// If any of the observers fire at this point it's fine. We check the
// view association (atomically) in the update callback, and skip the
// update if we haven't associated yet. Setting the drawable below will
// have the same effect as an update.
// Now we are ready to associate the view with the context
- m_context.view = view;
- if (m_context.view != view) {
+ QT_IGNORE_DEPRECATIONS(m_context.view) = view;
+ if (QT_IGNORE_DEPRECATIONS(m_context.view) != view) {
qCInfo(lcQpaOpenGLContext) << "Failed to set" << view << "as drawable for" << m_context;
m_updateObservers.clear();
return false;
}
- qCInfo(lcQpaOpenGLContext) << "Set drawable for" << m_context << "to" << m_context.view;
+ qCInfo(lcQpaOpenGLContext) << "Set drawable for" << m_context << "to" << QT_IGNORE_DEPRECATIONS(m_context.view);
return true;
}
-void QCocoaGLContext::prepareDrawable(QCocoaWindow *platformWindow)
-{
- // We generally want high-DPI GL surfaces, unless the user has explicitly disabled them
- bool prefersBestResolutionOpenGLSurface = qt_mac_resolveOption(YES,
- platformWindow->window(), "_q_mac_wantsBestResolutionOpenGLSurface",
- "QT_MAC_WANTS_BEST_RESOLUTION_OPENGL_SURFACE");
-
- auto *view = platformWindow->view();
-
- // The only case we have to opt out ourselves is when using the Apple software renderer
- // in combination with surface-backed views, as these together do not support high-DPI.
- if (prefersBestResolutionOpenGLSurface) {
- int rendererID = 0;
- [m_context getValues:&rendererID forParameter:NSOpenGLContextParameterCurrentRendererID];
- bool isSoftwareRenderer = (rendererID & kCGLRendererIDMatchingMask) == kCGLRendererGenericFloatID;
- if (isSoftwareRenderer && !view.layer) {
- qCInfo(lcQpaOpenGLContext) << "Disabling high resolution GL surface due to software renderer";
- prefersBestResolutionOpenGLSurface = false;
- }
- }
-
- view.wantsBestResolutionOpenGLSurface = prefersBestResolutionOpenGLSurface;
-}
-
// NSOpenGLContext is not re-entrant. Even when using separate contexts per thread,
// view, and window, calls into the API will still deadlock. For more information
// see https://openradar.appspot.com/37064579
-static QMutex s_reentrancyMutex;
+Q_CONSTINIT static QMutex s_reentrancyMutex;
void QCocoaGLContext::update()
{
@@ -483,12 +427,14 @@ void QCocoaGLContext::update()
QMacAutoReleasePool pool;
QMutexLocker locker(&s_reentrancyMutex);
- qCInfo(lcQpaOpenGLContext) << "Updating" << m_context << "for" << m_context.view;
+ qCInfo(lcQpaOpenGLContext) << "Updating" << m_context << "for" << QT_IGNORE_DEPRECATIONS(m_context.view);
[m_context update];
}
void QCocoaGLContext::swapBuffers(QPlatformSurface *surface)
{
+ QMacAutoReleasePool pool;
+
qCDebug(lcQpaOpenGLContext) << "Swapping" << m_context
<< "in" << QThread::currentThread() << "to" << surface;
@@ -501,19 +447,17 @@ void QCocoaGLContext::swapBuffers(QPlatformSurface *surface)
return;
}
- if (m_context.view.layer) {
- // Flushing an NSOpenGLContext will hit the screen immediately, ignoring
- // any Core Animation transactions in place. This may result in major
- // visual artifacts if the flush happens out of sync with the size
- // of the layer, view, and window reflected by other parts of the UI,
- // e.g. if the application flushes in the resize event or a timer during
- // window resizing, instead of in the expose event.
- auto *cocoaWindow = static_cast<QCocoaWindow *>(surface);
- if (cocoaWindow->geometry().size() != cocoaWindow->m_exposedRect.size()) {
- qCInfo(lcQpaOpenGLContext) << "Window exposed size does not match geometry (yet)."
- << "Skipping flush to avoid visual artifacts.";
- return;
- }
+ // Flushing an NSOpenGLContext will hit the screen immediately, ignoring
+ // any Core Animation transactions in place. This may result in major
+ // visual artifacts if the flush happens out of sync with the size
+ // of the layer, view, and window reflected by other parts of the UI,
+ // e.g. if the application flushes in the resize event or a timer during
+ // window resizing, instead of in the expose event.
+ auto *cocoaWindow = static_cast<QCocoaWindow *>(surface);
+ if (cocoaWindow->geometry().size() != cocoaWindow->m_exposedRect.size()) {
+ qCInfo(lcQpaOpenGLContext) << "Window exposed size does not match geometry (yet)."
+ << "Skipping flush to avoid visual artifacts.";
+ return;
}
QMutexLocker locker(&s_reentrancyMutex);
@@ -522,6 +466,8 @@ void QCocoaGLContext::swapBuffers(QPlatformSurface *surface)
void QCocoaGLContext::doneCurrent()
{
+ QMacAutoReleasePool pool;
+
qCDebug(lcQpaOpenGLContext) << "Clearing current context"
<< [NSOpenGLContext currentContext] << "in" << QThread::currentThread();
@@ -554,7 +500,7 @@ NSOpenGLContext *QCocoaGLContext::nativeContext() const
QFunctionPointer QCocoaGLContext::getProcAddress(const char *procName)
{
- return (QFunctionPointer)dlsym(RTLD_DEFAULT, procName);
+ return (QFunctionPointer)dlsym(RTLD_NEXT, procName);
}
#ifndef QT_NO_DEBUG_STREAM