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.mm585
1 files changed, 282 insertions, 303 deletions
diff --git a/src/plugins/platforms/cocoa/qcocoaglcontext.mm b/src/plugins/platforms/cocoa/qcocoaglcontext.mm
index 7ffe0003d3..069429796e 100644
--- a/src/plugins/platforms/cocoa/qcocoaglcontext.mm
+++ b/src/plugins/platforms/cocoa/qcocoaglcontext.mm
@@ -47,8 +47,6 @@
#import <AppKit/AppKit.h>
-QT_BEGIN_NAMESPACE
-
static inline QByteArray getGlString(GLenum param)
{
if (const GLubyte *s = glGetString(param))
@@ -56,109 +54,66 @@ static inline QByteArray getGlString(GLenum param)
return QByteArray();
}
-#if !defined(GL_CONTEXT_FLAGS)
-#define GL_CONTEXT_FLAGS 0x821E
-#endif
-
-#if !defined(GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT)
-#define GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT 0x0001
-#endif
-
-#if !defined(GL_CONTEXT_PROFILE_MASK)
-#define GL_CONTEXT_PROFILE_MASK 0x9126
-#endif
-
-#if !defined(GL_CONTEXT_CORE_PROFILE_BIT)
-#define GL_CONTEXT_CORE_PROFILE_BIT 0x00000001
-#endif
-
-#if !defined(GL_CONTEXT_COMPATIBILITY_PROFILE_BIT)
-#define GL_CONTEXT_COMPATIBILITY_PROFILE_BIT 0x00000002
-#endif
-
-static void updateFormatFromContext(QSurfaceFormat *format)
+@implementation NSOpenGLPixelFormat (QtHelpers)
+- (GLint)qt_getAttribute:(NSOpenGLPixelFormatAttribute)attribute
{
- Q_ASSERT(format);
-
- // Update the version, profile, and context bit of the format
- int major = 0, minor = 0;
- QByteArray versionString(getGlString(GL_VERSION));
- if (QPlatformOpenGLContext::parseOpenGLVersion(versionString, major, minor)) {
- format->setMajorVersion(major);
- format->setMinorVersion(minor);
- }
-
- format->setProfile(QSurfaceFormat::NoProfile);
-
- Q_ASSERT(format->renderableType() == QSurfaceFormat::OpenGL);
- if (format->version() < qMakePair(3, 0)) {
- format->setOption(QSurfaceFormat::DeprecatedFunctions);
- return;
- }
-
- // Version 3.0 onwards - check if it includes deprecated functionality
- GLint value = 0;
- glGetIntegerv(GL_CONTEXT_FLAGS, &value);
- if (!(value & GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT))
- format->setOption(QSurfaceFormat::DeprecatedFunctions);
-
- // Debug context option not supported on OS X
-
- if (format->version() < qMakePair(3, 2))
- return;
-
- // Version 3.2 and newer have a profile
- value = 0;
- glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &value);
+ int value = 0;
+ [self getValues:&value forAttribute:attribute forVirtualScreen:0];
+ return value;
+}
+@end
- if (value & GL_CONTEXT_CORE_PROFILE_BIT)
- format->setProfile(QSurfaceFormat::CoreProfile);
- else if (value & GL_CONTEXT_COMPATIBILITY_PROFILE_BIT)
- format->setProfile(QSurfaceFormat::CompatibilityProfile);
+@implementation NSOpenGLContext (QtHelpers)
+- (GLint)qt_getParameter:(NSOpenGLContextParameter)parameter
+{
+ int value = 0;
+ [self getValues:&value forParameter:parameter];
+ return value;
}
+@end
+
+QT_BEGIN_NAMESPACE
- // NSOpenGLContext is not re-entrant (https://openradar.appspot.com/37064579)
-static QMutex s_contextMutex;
+Q_LOGGING_CATEGORY(lcQpaOpenGLContext, "qt.qpa.openglcontext", QtWarningMsg);
-QCocoaGLContext::QCocoaGLContext(const QSurfaceFormat &format, QPlatformOpenGLContext *share,
- const QVariant &nativeHandle)
- : m_context(nil),
- m_shareContext(nil),
- m_format(format),
- m_didCheckForSoftwareContext(false)
+QCocoaGLContext::QCocoaGLContext(QOpenGLContext *context)
+ : QPlatformOpenGLContext(), m_format(context->format())
{
+ QVariant nativeHandle = context->nativeHandle();
if (!nativeHandle.isNull()) {
if (!nativeHandle.canConvert<QCocoaNativeContext>()) {
- qWarning("QCocoaGLContext: Requires a QCocoaNativeContext");
+ qCWarning(lcQpaOpenGLContext, "QOpenGLContext native handle must be a QCocoaNativeContext");
return;
}
- QCocoaNativeContext handle = nativeHandle.value<QCocoaNativeContext>();
- NSOpenGLContext *context = handle.context();
- if (!context) {
- qWarning("QCocoaGLContext: No NSOpenGLContext given");
+ m_context = nativeHandle.value<QCocoaNativeContext>().context();
+ if (!m_context) {
+ qCWarning(lcQpaOpenGLContext, "QCocoaNativeContext's NSOpenGLContext can not be null");
return;
}
- m_context = context;
+
[m_context retain];
- m_shareContext = share ? static_cast<QCocoaGLContext *>(share)->nsOpenGLContext() : nil;
- // OpenGL surfaces can be ordered either above(default) or below the NSWindow.
- const GLint order = qt_mac_resolveOption(1, "QT_MAC_OPENGL_SURFACE_ORDER");
- [m_context setValues:&order forParameter:NSOpenGLCPSurfaceOrder];
+
+ // 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;
}
- // we only support OpenGL contexts under Cocoa
+ // ----------- Default case, we own the NSOpenGLContext -----------
+
+ // We only support OpenGL contexts under Cocoa
if (m_format.renderableType() == QSurfaceFormat::DefaultRenderableType)
m_format.setRenderableType(QSurfaceFormat::OpenGL);
if (m_format.renderableType() != QSurfaceFormat::OpenGL)
return;
- QMacAutoReleasePool pool; // For the SG Canvas render thread
+ if (QPlatformOpenGLContext *shareContext = context->shareHandle()) {
+ m_shareContext = static_cast<QCocoaGLContext *>(shareContext)->nativeContext();
- m_shareContext = share ? static_cast<QCocoaGLContext *>(share)->nsOpenGLContext() : nil;
-
- if (m_shareContext) {
// Allow sharing between 3.2 Core and 4.1 Core profile versions in
// cases where NSOpenGLContext creates a 4.1 context where a 3.2
// context was requested. Due to the semantics of QSurfaceFormat
@@ -167,312 +122,327 @@ QCocoaGLContext::QCocoaGLContext(const QSurfaceFormat &format, QPlatformOpenGLCo
GLint shareContextRequestedProfile;
[m_shareContext.pixelFormat getValues:&shareContextRequestedProfile
forAttribute:NSOpenGLPFAOpenGLProfile forVirtualScreen:0];
- auto shareContextActualProfile = share->format().version();
-
- if (shareContextRequestedProfile == NSOpenGLProfileVersion3_2Core &&
- shareContextActualProfile >= qMakePair(4, 1)) {
+ auto shareContextActualProfile = shareContext->format().version();
- // There is a mismatch, downgrade requested format to make the
- // NSOpenGLPFAOpenGLProfile attributes match. (NSOpenGLContext will
- // fail to create a new context if there is a mismatch).
+ if (shareContextRequestedProfile == NSOpenGLProfileVersion3_2Core
+ && shareContextActualProfile >= qMakePair(4, 1)) {
+ // There is a mismatch. Downgrade requested format to make the
+ // NSOpenGLPFAOpenGLProfile attributes match. (NSOpenGLContext
+ // will fail to create a new context if there is a mismatch).
if (m_format.version() >= qMakePair(4, 1))
m_format.setVersion(3, 2);
}
}
- // create native context for the requested pixel format and share
- NSOpenGLPixelFormat *pixelFormat = createNSOpenGLPixelFormat(m_format);
+ // ------------------------- Create NSOpenGLContext -------------------------
+
+ NSOpenGLPixelFormat *pixelFormat = [pixelFormatForSurfaceFormat(m_format) autorelease];
m_context = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:m_shareContext];
- // retry without sharing on context creation failure.
if (!m_context && m_shareContext) {
- m_shareContext = nil;
+ qCWarning(lcQpaOpenGLContext, "Could not create NSOpenGLContext with shared context, "
+ "falling back to unshared context.");
m_context = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:nil];
- if (m_context)
- qWarning("QCocoaGLContext: Falling back to unshared context.");
+ m_shareContext = nil;
}
- // give up if we still did not get a native context
- [pixelFormat release];
if (!m_context) {
- qWarning("QCocoaGLContext: Failed to create context.");
+ qCWarning(lcQpaOpenGLContext, "Failed to create NSOpenGLContext");
return;
}
- const GLint interval = format.swapInterval() >= 0 ? format.swapInterval() : 1;
+ // --------------------- Set NSOpenGLContext properties ---------------------
+
+ const GLint interval = m_format.swapInterval() >= 0 ? m_format.swapInterval() : 1;
[m_context setValues:&interval forParameter:NSOpenGLCPSwapInterval];
- if (format.alphaBufferSize() > 0) {
+ if (m_format.alphaBufferSize() > 0) {
int zeroOpacity = 0;
[m_context setValues:&zeroOpacity forParameter:NSOpenGLCPSurfaceOpacity];
}
-
- // OpenGL surfaces can be ordered either above(default) or below the NSWindow.
+ // 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];
updateSurfaceFormat();
}
-QCocoaGLContext::~QCocoaGLContext()
+NSOpenGLPixelFormat *QCocoaGLContext::pixelFormatForSurfaceFormat(const QSurfaceFormat &format)
{
- if (m_currentWindow && m_currentWindow.data()->handle())
- static_cast<QCocoaWindow *>(m_currentWindow.data()->handle())->setCurrentContext(0);
+ QVector<NSOpenGLPixelFormatAttribute> attrs;
- [m_context release];
-}
+ attrs << NSOpenGLPFAOpenGLProfile;
+ if (format.profile() == QSurfaceFormat::CoreProfile) {
+ if (format.version() >= qMakePair(4, 1))
+ attrs << NSOpenGLProfileVersion4_1Core;
+ else if (format.version() >= qMakePair(3, 2))
+ attrs << NSOpenGLProfileVersion3_2Core;
+ else
+ attrs << NSOpenGLProfileVersionLegacy;
+ } else {
+ attrs << NSOpenGLProfileVersionLegacy;
+ }
-QVariant QCocoaGLContext::nativeHandle() const
-{
- return QVariant::fromValue<QCocoaNativeContext>(QCocoaNativeContext(m_context));
-}
+ switch (format.swapBehavior()) {
+ case QSurfaceFormat::SingleBuffer:
+ break; // The NSOpenGLPixelFormat default, no attribute to set
+ case QSurfaceFormat::DefaultSwapBehavior:
+ // Technically this should be single-buffered, but we force double-buffered
+ // FIXME: Why do we force double-buffered?
+ Q_FALLTHROUGH();
+ case QSurfaceFormat::DoubleBuffer:
+ attrs.append(NSOpenGLPFADoubleBuffer);
+ break;
+ case QSurfaceFormat::TripleBuffer:
+ attrs.append(NSOpenGLPFATripleBuffer);
+ break;
+ }
-QSurfaceFormat QCocoaGLContext::format() const
-{
- return m_format;
-}
+ if (format.depthBufferSize() > 0)
+ attrs << NSOpenGLPFADepthSize << format.depthBufferSize();
+ if (format.stencilBufferSize() > 0)
+ attrs << NSOpenGLPFAStencilSize << format.stencilBufferSize();
+ if (format.alphaBufferSize() > 0)
+ attrs << NSOpenGLPFAAlphaSize << format.alphaBufferSize();
+ if (format.redBufferSize() > 0 && format.greenBufferSize() > 0 && format.blueBufferSize() > 0) {
+ const int colorSize = format.redBufferSize() + format.greenBufferSize() + format.blueBufferSize();
+ attrs << NSOpenGLPFAColorSize << colorSize << NSOpenGLPFAMinimumPolicy;
+ }
-void QCocoaGLContext::windowWasHidden()
-{
- // If the window is hidden, we need to unset the m_currentWindow
- // variable so that succeeding makeCurrent's will not abort prematurely
- // because of the optimization in setActiveWindow.
- // Doing a full doneCurrent here is not preferable, because the GL context
- // might be rendering in a different thread at this time.
- m_currentWindow.clear();
-}
+ if (format.samples() > 0) {
+ attrs << NSOpenGLPFAMultisample
+ << NSOpenGLPFASampleBuffers << NSOpenGLPixelFormatAttribute(1)
+ << NSOpenGLPFASamples << NSOpenGLPixelFormatAttribute(format.samples());
+ }
-void QCocoaGLContext::swapBuffers(QPlatformSurface *surface)
-{
- if (surface->surface()->surfaceClass() == QSurface::Offscreen)
- return; // Nothing to do
+ // Allow rendering on GPUs without a connected display
+ attrs << NSOpenGLPFAAllowOfflineRenderers;
- QWindow *window = static_cast<QCocoaWindow *>(surface)->window();
- setActiveWindow(window);
+ // FIXME: Pull this information out of the NSView
+ QByteArray useLayer = qgetenv("QT_MAC_WANTS_LAYER");
+ if (!useLayer.isEmpty() && useLayer.toInt() > 0) {
+ // Disable the software rendering fallback. This makes compositing
+ // OpenGL and raster NSViews using Core Animation layers possible.
+ attrs << NSOpenGLPFANoRecovery;
+ }
- QMutexLocker locker(&s_contextMutex);
- [m_context flushBuffer];
+ attrs << 0; // 0-terminate array
+ return [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs.constData()];
}
-bool QCocoaGLContext::makeCurrent(QPlatformSurface *surface)
+/*!
+ Updates the surface format of this context based on properties of
+ the native context and GL state, so that the result of creating
+ the context is reflected back in QOpenGLContext.
+*/
+void QCocoaGLContext::updateSurfaceFormat()
{
- Q_ASSERT(surface->surface()->supportsOpenGL());
-
- QMacAutoReleasePool pool;
+ NSOpenGLContext *oldContext = [NSOpenGLContext currentContext];
[m_context makeCurrentContext];
- if (surface->surface()->surfaceClass() == QSurface::Offscreen)
- return true;
+ // --------------------- Query GL state ---------------------
- QWindow *window = static_cast<QCocoaWindow *>(surface)->window();
- setActiveWindow(window);
-
- // Disable high-resolution surfaces when using the software renderer, which has the
- // problem that the system silently falls back to a to using a low-resolution buffer
- // when a high-resolution buffer is requested. This is not detectable using the NSWindow
- // convertSizeToBacking and backingScaleFactor APIs. A typical result of this is that Qt
- // will display a quarter of the window content when running in a virtual machine.
- if (!m_didCheckForSoftwareContext) {
- m_didCheckForSoftwareContext = true;
-
- const GLubyte* renderer = glGetString(GL_RENDERER);
- if (qstrcmp((const char *)renderer, "Apple Software Renderer") == 0) {
- NSView *view = static_cast<QCocoaWindow *>(surface)->m_view;
- [view setWantsBestResolutionOpenGLSurface:NO];
- }
+ int major = 0, minor = 0;
+ QByteArray versionString(getGlString(GL_VERSION));
+ if (QPlatformOpenGLContext::parseOpenGLVersion(versionString, major, minor)) {
+ m_format.setMajorVersion(major);
+ m_format.setMinorVersion(minor);
}
- update();
- return true;
-}
+ m_format.setProfile(QSurfaceFormat::NoProfile);
+ if (m_format.version() >= qMakePair(3, 2)) {
+ GLint value = 0;
+ glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &value);
+ if (value & GL_CONTEXT_CORE_PROFILE_BIT)
+ m_format.setProfile(QSurfaceFormat::CoreProfile);
+ else if (value & GL_CONTEXT_COMPATIBILITY_PROFILE_BIT)
+ m_format.setProfile(QSurfaceFormat::CompatibilityProfile);
+ }
-void QCocoaGLContext::setActiveWindow(QWindow *window)
-{
- if (window == m_currentWindow.data())
- return;
+ m_format.setOption(QSurfaceFormat::DeprecatedFunctions, [&]() {
+ if (m_format.version() < qMakePair(3, 0)) {
+ return true;
+ } else {
+ GLint value = 0;
+ glGetIntegerv(GL_CONTEXT_FLAGS, &value);
+ return !(value & GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT);
+ }
+ }());
- if (m_currentWindow && m_currentWindow.data()->handle())
- static_cast<QCocoaWindow *>(m_currentWindow.data()->handle())->setCurrentContext(0);
+ // Debug contexts not supported on macOS
+ m_format.setOption(QSurfaceFormat::DebugContext, false);
- Q_ASSERT(window->handle());
+ // Nor are stereo buffers (deprecated in macOS 10.12)
+ m_format.setOption(QSurfaceFormat::StereoBuffers, false);
- m_currentWindow = window;
+ // ------------------ Query the pixel format ------------------
- QCocoaWindow *cocoaWindow = static_cast<QCocoaWindow *>(window->handle());
- cocoaWindow->setCurrentContext(this);
+ NSOpenGLPixelFormat *pixelFormat = m_context.pixelFormat;
- Q_ASSERT(!cocoaWindow->isForeignWindow());
- [qnsview_cast(cocoaWindow->view()) setQCocoaGLContext:this];
-}
+ int colorSize = [pixelFormat qt_getAttribute:NSOpenGLPFAColorSize];
+ colorSize /= 4; // The attribute includes the alpha component
+ m_format.setRedBufferSize(colorSize);
+ m_format.setGreenBufferSize(colorSize);
+ m_format.setBlueBufferSize(colorSize);
-void QCocoaGLContext::updateSurfaceFormat()
-{
- // At present it is impossible to turn an option off on a QSurfaceFormat (see
- // https://codereview.qt-project.org/#change,70599). So we have to populate
- // the actual surface format from scratch
- QSurfaceFormat requestedFormat = m_format;
- m_format = QSurfaceFormat();
- m_format.setRenderableType(QSurfaceFormat::OpenGL);
-
- // CoreGL doesn't require a drawable to make the context current
- CGLContextObj oldContext = CGLGetCurrentContext();
- CGLContextObj ctx = static_cast<CGLContextObj>([m_context CGLContextObj]);
- CGLSetCurrentContext(ctx);
-
- // Get the data that OpenGL provides
- updateFormatFromContext(&m_format);
-
- // Get the data contained within the pixel format
- CGLPixelFormatObj cglPixelFormat = static_cast<CGLPixelFormatObj>(CGLGetPixelFormat(ctx));
- NSOpenGLPixelFormat *pixelFormat = [[NSOpenGLPixelFormat alloc] initWithCGLPixelFormatObj:cglPixelFormat];
-
- int colorSize = -1;
- [pixelFormat getValues:&colorSize forAttribute:NSOpenGLPFAColorSize forVirtualScreen:0];
- if (colorSize > 0) {
- // This seems to return the total color buffer depth, including alpha
- m_format.setRedBufferSize(colorSize / 4);
- m_format.setGreenBufferSize(colorSize / 4);
- m_format.setBlueBufferSize(colorSize / 4);
- }
+ // Surfaces on macOS always have an alpha channel, but unless the user requested
+ // one via setAlphaBufferSize(), which triggered setting NSOpenGLCPSurfaceOpacity
+ // to make the surface non-opaque, we don't want to report back the actual alpha
+ // size, as that will make the user believe the alpha channel can be used for
+ // something useful, when in reality it can't, due to the surface being opaque.
+ if (m_format.alphaBufferSize() > 0)
+ m_format.setAlphaBufferSize([pixelFormat qt_getAttribute:NSOpenGLPFAAlphaSize]);
- // The pixel format always seems to return 8 for alpha. However, the framebuffer only
- // seems to have alpha enabled if we requested it explicitly. I can't find any other
- // attribute to check explicitly for this so we use our best guess for alpha.
- int alphaSize = -1;
- [pixelFormat getValues:&alphaSize forAttribute:NSOpenGLPFAAlphaSize forVirtualScreen:0];
- if (alphaSize > 0 && requestedFormat.alphaBufferSize() > 0)
- m_format.setAlphaBufferSize(alphaSize);
-
- int depthSize = -1;
- [pixelFormat getValues:&depthSize forAttribute:NSOpenGLPFADepthSize forVirtualScreen:0];
- if (depthSize > 0)
- m_format.setDepthBufferSize(depthSize);
-
- int stencilSize = -1;
- [pixelFormat getValues:&stencilSize forAttribute:NSOpenGLPFAStencilSize forVirtualScreen:0];
- if (stencilSize > 0)
- m_format.setStencilBufferSize(stencilSize);
-
- int samples = -1;
- [pixelFormat getValues:&samples forAttribute:NSOpenGLPFASamples forVirtualScreen:0];
- if (samples > 0)
- m_format.setSamples(samples);
-
- int doubleBuffered = -1;
- int tripleBuffered = -1;
- [pixelFormat getValues:&doubleBuffered forAttribute:NSOpenGLPFADoubleBuffer forVirtualScreen:0];
- [pixelFormat getValues:&tripleBuffered forAttribute:NSOpenGLPFATripleBuffer forVirtualScreen:0];
-
- if (tripleBuffered == 1)
+ m_format.setDepthBufferSize([pixelFormat qt_getAttribute:NSOpenGLPFADepthSize]);
+ m_format.setStencilBufferSize([pixelFormat qt_getAttribute:NSOpenGLPFAStencilSize]);
+ m_format.setSamples([pixelFormat qt_getAttribute:NSOpenGLPFASamples]);
+
+ if ([pixelFormat qt_getAttribute:NSOpenGLPFATripleBuffer])
m_format.setSwapBehavior(QSurfaceFormat::TripleBuffer);
- else if (doubleBuffered == 1)
+ else if ([pixelFormat qt_getAttribute:NSOpenGLPFADoubleBuffer])
m_format.setSwapBehavior(QSurfaceFormat::DoubleBuffer);
else
m_format.setSwapBehavior(QSurfaceFormat::SingleBuffer);
- int steroBuffers = -1;
- [pixelFormat getValues:&steroBuffers forAttribute:NSOpenGLPFAStereo forVirtualScreen:0];
- if (steroBuffers == 1)
- m_format.setOption(QSurfaceFormat::StereoBuffers);
+ // ------------------- Query the context -------------------
- [pixelFormat release];
+ m_format.setSwapInterval([m_context qt_getParameter:NSOpenGLCPSwapInterval]);
- GLint swapInterval = -1;
- [m_context getValues:&swapInterval forParameter:NSOpenGLCPSwapInterval];
- if (swapInterval >= 0)
- m_format.setSwapInterval(swapInterval);
+ if (oldContext)
+ [oldContext makeCurrentContext];
+ else
+ [NSOpenGLContext clearCurrentContext];
+}
- // Restore the original context
- CGLSetCurrentContext(oldContext);
+QCocoaGLContext::~QCocoaGLContext()
+{
+ [m_context release];
}
-void QCocoaGLContext::doneCurrent()
+bool QCocoaGLContext::makeCurrent(QPlatformSurface *surface)
{
- if (m_currentWindow && m_currentWindow.data()->handle())
- static_cast<QCocoaWindow *>(m_currentWindow.data()->handle())->setCurrentContext(0);
+ qCDebug(lcQpaOpenGLContext) << "Making" << m_context << "current"
+ << "in" << QThread::currentThread() << "for" << surface;
+
+ Q_ASSERT(surface->surface()->supportsOpenGL());
- m_currentWindow.clear();
+ if (!setDrawable(surface))
+ return false;
- [NSOpenGLContext clearCurrentContext];
+ [m_context makeCurrentContext];
+
+ if (surface->surface()->surfaceClass() == QSurface::Window) {
+ // Disable high-resolution surfaces when using the software renderer, which has the
+ // problem that the system silently falls back to a to using a low-resolution buffer
+ // when a high-resolution buffer is requested. This is not detectable using the NSWindow
+ // convertSizeToBacking and backingScaleFactor APIs. A typical result of this is that Qt
+ // will display a quarter of the window content when running in a virtual machine.
+ if (!m_didCheckForSoftwareContext) {
+ // FIXME: This ensures we check only once per context,
+ // but the context may be used for multiple surfaces.
+ m_didCheckForSoftwareContext = true;
+
+ const GLubyte* renderer = glGetString(GL_RENDERER);
+ if (qstrcmp((const char *)renderer, "Apple Software Renderer") == 0) {
+ NSView *view = static_cast<QCocoaWindow *>(surface)->m_view;
+ [view setWantsBestResolutionOpenGLSurface:NO];
+ }
+ }
+
+ update();
+ }
+
+ return true;
}
-QFunctionPointer QCocoaGLContext::getProcAddress(const char *procName)
+/*!
+ Sets the drawable object of the NSOpenGLContext, which is the
+ frame buffer that is the target of OpenGL drawing operations.
+*/
+bool QCocoaGLContext::setDrawable(QPlatformSurface *surface)
{
- return (QFunctionPointer)dlsym(RTLD_DEFAULT, procName);
+ // Make sure any surfaces released during this process are deallocated
+ // straight away, otherwise we may run out of surfaces when spinning a
+ // render-loop that doesn't return to one of the outer pools.
+ QMacAutoReleasePool pool;
+
+ if (!surface || surface->surface()->surfaceClass() == QSurface::Offscreen) {
+ // 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;
+ [m_context clearDrawable];
+ return true;
+ }
+
+ Q_ASSERT(surface->surface()->surfaceClass() == QSurface::Window);
+ QNSView *view = qnsview_cast(static_cast<QCocoaWindow *>(surface)->view());
+
+ if (view == m_context.view)
+ return true;
+
+ if ((m_context.view = view) != view) {
+ qCInfo(lcQpaOpenGLContext) << "Failed to set" << view << "as drawable for" << m_context;
+ return false;
+ }
+
+ qCInfo(lcQpaOpenGLContext) << "Set drawable for" << m_context << "to" << m_context.view;
+
+ return true;
}
+// 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;
+
void QCocoaGLContext::update()
{
- QMutexLocker locker(&s_contextMutex);
+ // Make sure any surfaces released during this process are deallocated
+ // straight away, otherwise we may run out of surfaces when spinning a
+ // render-loop that doesn't return to one of the outer pools.
+ QMacAutoReleasePool pool;
+
+ QMutexLocker locker(&s_reentrancyMutex);
+ qCInfo(lcQpaOpenGLContext) << "Updating" << m_context << "for" << m_context.view;
[m_context update];
}
-NSOpenGLPixelFormat *QCocoaGLContext::createNSOpenGLPixelFormat(const QSurfaceFormat &format)
+void QCocoaGLContext::swapBuffers(QPlatformSurface *surface)
{
- QVector<NSOpenGLPixelFormatAttribute> attrs;
-
- if (format.swapBehavior() == QSurfaceFormat::DoubleBuffer
- || format.swapBehavior() == QSurfaceFormat::DefaultSwapBehavior)
- attrs.append(NSOpenGLPFADoubleBuffer);
- else if (format.swapBehavior() == QSurfaceFormat::TripleBuffer)
- attrs.append(NSOpenGLPFATripleBuffer);
-
-
- // Select OpenGL profile
- attrs << NSOpenGLPFAOpenGLProfile;
- if (format.profile() == QSurfaceFormat::CoreProfile) {
- if (format.version() >= qMakePair(4, 1))
- attrs << NSOpenGLProfileVersion4_1Core;
- else if (format.version() >= qMakePair(3, 2))
- attrs << NSOpenGLProfileVersion3_2Core;
- else
- attrs << NSOpenGLProfileVersionLegacy;
- } else {
- attrs << NSOpenGLProfileVersionLegacy;
- }
+ qCDebug(lcQpaOpenGLContext) << "Swapping" << m_context
+ << "in" << QThread::currentThread() << "to" << surface;
- if (format.depthBufferSize() > 0)
- attrs << NSOpenGLPFADepthSize << format.depthBufferSize();
- if (format.stencilBufferSize() > 0)
- attrs << NSOpenGLPFAStencilSize << format.stencilBufferSize();
- if (format.alphaBufferSize() > 0)
- attrs << NSOpenGLPFAAlphaSize << format.alphaBufferSize();
- if ((format.redBufferSize() > 0) &&
- (format.greenBufferSize() > 0) &&
- (format.blueBufferSize() > 0)) {
- const int colorSize = format.redBufferSize() +
- format.greenBufferSize() +
- format.blueBufferSize();
- attrs << NSOpenGLPFAColorSize << colorSize << NSOpenGLPFAMinimumPolicy;
- }
+ if (surface->surface()->surfaceClass() == QSurface::Offscreen)
+ return; // Nothing to do
- if (format.samples() > 0) {
- attrs << NSOpenGLPFAMultisample
- << NSOpenGLPFASampleBuffers << (NSOpenGLPixelFormatAttribute) 1
- << NSOpenGLPFASamples << (NSOpenGLPixelFormatAttribute) format.samples();
+ if (!setDrawable(surface)) {
+ qCWarning(lcQpaOpenGLContext) << "Can't flush" << m_context
+ << "without" << surface << "as drawable";
+ return;
}
- if (format.stereo())
- attrs << NSOpenGLPFAStereo;
-
- attrs << NSOpenGLPFAAllowOfflineRenderers;
+ QMutexLocker locker(&s_reentrancyMutex);
+ [m_context flushBuffer];
+}
- QByteArray useLayer = qgetenv("QT_MAC_WANTS_LAYER");
- if (!useLayer.isEmpty() && useLayer.toInt() > 0) {
- // Disable the software rendering fallback. This makes compositing
- // OpenGL and raster NSViews using Core Animation layers possible.
- attrs << NSOpenGLPFANoRecovery;
- }
+void QCocoaGLContext::doneCurrent()
+{
+ qCDebug(lcQpaOpenGLContext) << "Clearing current context"
+ << [NSOpenGLContext currentContext] << "in" << QThread::currentThread();
- attrs << 0;
+ // Note: We do not need to clear the current drawable here.
+ // As long as there is no current context, GL calls will
+ // do nothing.
- return [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs.constData()];
+ [NSOpenGLContext clearCurrentContext];
}
-NSOpenGLContext *QCocoaGLContext::nsOpenGLContext() const
+QSurfaceFormat QCocoaGLContext::format() const
{
- return m_context;
+ return m_format;
}
bool QCocoaGLContext::isValid() const
@@ -485,5 +455,14 @@ bool QCocoaGLContext::isSharing() const
return m_shareContext != nil;
}
-QT_END_NAMESPACE
+NSOpenGLContext *QCocoaGLContext::nativeContext() const
+{
+ return m_context;
+}
+QFunctionPointer QCocoaGLContext::getProcAddress(const char *procName)
+{
+ return (QFunctionPointer)dlsym(RTLD_DEFAULT, procName);
+}
+
+QT_END_NAMESPACE