5#include <AppKit/AppKit.h>
12#include <QtCore/private/qcore_mac_p.h>
19 if (
const GLubyte *s = glGetString(param))
20 return QByteArray(
reinterpret_cast<
const char*>(s));
24#if defined(Q_PROCESSOR_X86_64)
25QT_DECLARE_NAMESPACED_OBJC_INTERFACE(QNoopDisplayDelegate, NSObject<CALayerDelegate>)
41 m_context = [nativeContext retain];
50 if (QPlatformOpenGLContext *shareContext = context()->shareHandle())
51 m_shareContext =
static_cast<QCocoaGLContext *>(shareContext)->nativeContext();
53 updateSurfaceFormat();
60 if (m_format.renderableType() == QSurfaceFormat::DefaultRenderableType)
61 m_format.setRenderableType(QSurfaceFormat::OpenGL);
62 if (m_format.renderableType() != QSurfaceFormat::OpenGL)
65 if (QPlatformOpenGLContext *shareContext = context()->shareHandle()) {
66 m_shareContext =
static_cast<QCocoaGLContext *>(shareContext)->nativeContext();
73 GLint shareContextRequestedProfile;
74 [m_shareContext.pixelFormat getValues:&shareContextRequestedProfile
75 forAttribute:NSOpenGLPFAOpenGLProfile forVirtualScreen:0];
76 auto shareContextActualProfile = shareContext->format().version();
78 if (shareContextRequestedProfile == NSOpenGLProfileVersion3_2Core
79 && shareContextActualProfile >= qMakePair(4, 1)) {
83 if (m_format.version() >= qMakePair(4, 1))
84 m_format.setVersion(3, 2);
90 NSOpenGLPixelFormat *pixelFormat = [pixelFormatForSurfaceFormat(m_format) autorelease];
91 m_context = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:m_shareContext];
93 if (!m_context && m_shareContext) {
94 qCWarning(lcQpaOpenGLContext,
"Could not create NSOpenGLContext with shared context, "
95 "falling back to unshared context.");
96 m_context = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:nil];
101 qCWarning(lcQpaOpenGLContext,
"Failed to create NSOpenGLContext");
107 const GLint interval = m_format.swapInterval() >= 0 ? m_format.swapInterval() : 1;
108 [m_context setValues:&interval forParameter:NSOpenGLContextParameterSwapInterval];
110 if (m_format.alphaBufferSize() > 0) {
112 [m_context setValues:&zeroOpacity forParameter:NSOpenGLContextParameterSurfaceOpacity];
117 const GLint order = qt_mac_resolveOption(1,
"QT_MAC_OPENGL_SURFACE_ORDER");
118 [m_context setValues:&order forParameter:NSOpenGLContextParameterSurfaceOrder];
120 updateSurfaceFormat();
122 qCDebug(lcQpaOpenGLContext).verbosity(3) <<
"Created" <<
this <<
"based on requested" << context()->format();
125NSOpenGLPixelFormat *
QCocoaGLContext::pixelFormatForSurfaceFormat(
const QSurfaceFormat &format)
127 QVector<NSOpenGLPixelFormatAttribute> attrs;
129 attrs << NSOpenGLPFAOpenGLProfile;
130 if (format.profile() == QSurfaceFormat::CoreProfile) {
131 if (format.version() >= qMakePair(4, 1))
132 attrs << NSOpenGLProfileVersion4_1Core;
133 else if (format.version() >= qMakePair(3, 2))
134 attrs << NSOpenGLProfileVersion3_2Core;
136 attrs << NSOpenGLProfileVersionLegacy;
138 attrs << NSOpenGLProfileVersionLegacy;
141 switch (format.swapBehavior()) {
142 case QSurfaceFormat::SingleBuffer:
144 case QSurfaceFormat::DefaultSwapBehavior:
148 case QSurfaceFormat::DoubleBuffer:
149 attrs.append(NSOpenGLPFADoubleBuffer);
151 case QSurfaceFormat::TripleBuffer:
152 attrs.append(NSOpenGLPFATripleBuffer);
156 if (format.depthBufferSize() > 0)
157 attrs << NSOpenGLPFADepthSize << format.depthBufferSize();
158 if (format.stencilBufferSize() > 0)
159 attrs << NSOpenGLPFAStencilSize << format.stencilBufferSize();
160 if (format.alphaBufferSize() > 0)
161 attrs << NSOpenGLPFAAlphaSize << format.alphaBufferSize();
163 auto rbz = format.redBufferSize();
164 auto gbz = format.greenBufferSize();
165 auto bbz = format.blueBufferSize();
166 if (rbz > 0 || gbz > 0 || bbz > 0) {
167 auto fallbackSize = qMax(rbz, qMax(gbz, bbz));
168 auto colorSize = (rbz > 0 ? rbz : fallbackSize)
169 + (gbz > 0 ? gbz : fallbackSize)
170 + (bbz > 0 ? bbz : fallbackSize);
171 attrs << NSOpenGLPFAColorSize << colorSize << NSOpenGLPFAMinimumPolicy;
174 if (format.samples() > 0) {
175 attrs << NSOpenGLPFAMultisample
176 << NSOpenGLPFASampleBuffers << NSOpenGLPixelFormatAttribute(1)
177 << NSOpenGLPFASamples << NSOpenGLPixelFormatAttribute(format.samples());
182 static bool offlineRenderersAllowed = qEnvironmentVariableIsEmpty(
"QT_MAC_PRO_WEBENGINE_WORKAROUND");
183 if (offlineRenderersAllowed) {
185 attrs << NSOpenGLPFAAllowOfflineRenderers;
188 if (qGuiApp->testAttribute(Qt::AA_UseSoftwareOpenGL)) {
191 attrs << NSOpenGLPFARendererID << kCGLRendererGenericFloatID;
195 return [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs.constData()];
199
200
201
202
205 NSOpenGLContext *oldContext = [NSOpenGLContext currentContext];
206 [m_context makeCurrentContext];
210 int major = 0, minor = 0;
211 QByteArray versionString(getGlString(GL_VERSION));
212 if (QPlatformOpenGLContext::parseOpenGLVersion(versionString, major, minor)) {
213 m_format.setMajorVersion(major);
214 m_format.setMinorVersion(minor);
217 m_format.setProfile(QSurfaceFormat::NoProfile);
218 if (m_format.version() >= qMakePair(3, 2)) {
220 glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &value);
221 if (value & GL_CONTEXT_CORE_PROFILE_BIT)
222 m_format.setProfile(QSurfaceFormat::CoreProfile);
223 else if (value & GL_CONTEXT_COMPATIBILITY_PROFILE_BIT)
224 m_format.setProfile(QSurfaceFormat::CompatibilityProfile);
227 m_format.setOption(QSurfaceFormat::DeprecatedFunctions, [&]() {
228 if (m_format.version() < qMakePair(3, 0)) {
232 glGetIntegerv(GL_CONTEXT_FLAGS, &value);
233 return !(value & GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT);
238 m_format.setOption(QSurfaceFormat::DebugContext,
false);
241 m_format.setOption(QSurfaceFormat::StereoBuffers,
false);
245 NSOpenGLPixelFormat *pixelFormat = m_context.pixelFormat;
247 GLint virtualScreen = [&,
this]() {
248 auto *platformScreen =
static_cast<
QCocoaScreen*>(context()->screen()->handle());
249 auto displayId = platformScreen->nativeScreen().qt_displayId;
250 auto requestedDisplay = CGDisplayIDToOpenGLDisplayMask(displayId);
251 for (
int i = 0; i < pixelFormat.numberOfVirtualScreens; ++i) {
252 GLint supportedDisplays;
253 [pixelFormat getValues:&supportedDisplays forAttribute:NSOpenGLPFAScreenMask forVirtualScreen:i];
258 if (requestedDisplay & supportedDisplays)
261 qCWarning(lcQpaOpenGLContext) <<
"Could not find virtual screen for"
262 << platformScreen <<
"with displayId" << displayId;
266 auto pixelFormatAttribute = [&](NSOpenGLPixelFormatAttribute attribute) {
268 [pixelFormat getValues:&value forAttribute:attribute forVirtualScreen:virtualScreen];
274 GLint redBits, greenBits, blueBits;
275 glGetIntegerv(GL_RED_BITS, &redBits);
276 glGetIntegerv(GL_GREEN_BITS, &greenBits);
277 glGetIntegerv(GL_BLUE_BITS, &blueBits);
278 m_format.setRedBufferSize(redBits);
279 m_format.setGreenBufferSize(greenBits);
280 m_format.setBlueBufferSize(blueBits);
287 if (m_format.alphaBufferSize() > 0) {
289 glGetIntegerv(GL_ALPHA_BITS, &alphaBits);
290 m_format.setAlphaBufferSize(alphaBits);
293 m_format.setDepthBufferSize(pixelFormatAttribute(NSOpenGLPFADepthSize));
294 m_format.setStencilBufferSize(pixelFormatAttribute(NSOpenGLPFAStencilSize));
295 m_format.setSamples(pixelFormatAttribute(NSOpenGLPFASamples));
297 if (pixelFormatAttribute(NSOpenGLPFATripleBuffer))
298 m_format.setSwapBehavior(QSurfaceFormat::TripleBuffer);
299 else if (pixelFormatAttribute(NSOpenGLPFADoubleBuffer))
300 m_format.setSwapBehavior(QSurfaceFormat::DoubleBuffer);
302 m_format.setSwapBehavior(QSurfaceFormat::SingleBuffer);
304 m_isSoftwareContext = (pixelFormatAttribute(NSOpenGLPFARendererID)
305 & kCGLRendererIDMatchingMask) == kCGLRendererGenericFloatID;
309 auto glContextParameter = [&](NSOpenGLContextParameter parameter) {
311 [m_context getValues:&value forParameter:parameter];
315 m_format.setSwapInterval(glContextParameter(NSOpenGLContextParameterSwapInterval));
318 [oldContext makeCurrentContext];
320 [NSOpenGLContext clearCurrentContext];
330 QMacAutoReleasePool pool;
332 qCDebug(lcQpaOpenGLContext) <<
"Making" <<
this <<
"current"
333 <<
"in" << QThread::currentThread() <<
"for" << surface;
335 Q_ASSERT(surface->surface()->supportsOpenGL());
337 if (!setDrawable(surface))
340 [m_context makeCurrentContext];
342 if (surface->surface()->surfaceClass() == QSurface::Window) {
343 if (m_needsUpdate.fetchAndStoreRelaxed(
false))
352 QMacAutoReleasePool pool;
354 Q_ASSERT(context() && context()->surface());
355 auto *surface = context()->surface()->surfaceHandle();
358 qCDebug(lcQpaOpenGLContext) <<
"Beginning frame for" <<
this
359 <<
"in" << QThread::currentThread() <<
"for" << surface;
361 Q_ASSERT(surface->surface()->supportsOpenGL());
363 if (surface->surface()->surfaceClass() == QSurface::Window) {
364 if (m_needsUpdate.fetchAndStoreRelaxed(
false))
370
371
372
378 QMacAutoReleasePool pool;
380 if (!surface || surface->surface()->surfaceClass() == QSurface::Offscreen) {
384 qCDebug(lcQpaOpenGLContext) <<
"Clearing current drawable" << QT_IGNORE_DEPRECATIONS(m_context.view) <<
"for" << m_context;
385 [m_context clearDrawable];
389 Q_ASSERT(surface->surface()->surfaceClass() == QSurface::Window);
390 auto *cocoaWindow =
static_cast<
QCocoaWindow *>(surface);
391 QNSView *view = qnsview_cast(cocoaWindow->view());
393 if (view == QT_IGNORE_DEPRECATIONS(m_context.view))
400 QT_IGNORE_DEPRECATIONS(view.wantsBestResolutionOpenGLSurface) = qt_mac_resolveOption(YES,
401 cocoaWindow->window(),
"_q_mac_wantsBestResolutionOpenGLSurface",
402 "QT_MAC_WANTS_BEST_RESOLUTION_OPENGL_SURFACE");
410 auto updateCallback = [
this, view]() {
411 Q_ASSERT(QThread::currentThread() == qApp->thread());
412 if (QT_IGNORE_DEPRECATIONS(m_context.view) != view)
414 m_needsUpdate =
true;
417 m_updateObservers.clear();
419 m_updateObservers.append(QMacNotificationObserver(view, NSViewFrameDidChangeNotification, updateCallback));
420 m_updateObservers.append(QMacNotificationObserver(view.window, NSWindowDidChangeScreenNotification, updateCallback));
422 m_updateObservers.append(QMacNotificationObserver([NSApplication sharedApplication],
423 NSApplicationDidChangeScreenParametersNotification, updateCallback));
425 m_updateObservers.append(QMacNotificationObserver(view,
426 QCocoaWindowWillReleaseQNSViewNotification, [
this, view] {
427 if (QT_IGNORE_DEPRECATIONS(m_context.view) != view)
429 qCDebug(lcQpaOpenGLContext) << view <<
"about to be released."
430 <<
"Clearing current drawable for" << m_context;
431 [m_context clearDrawable];
440 QT_IGNORE_DEPRECATIONS(m_context.view) = view;
441 if (QT_IGNORE_DEPRECATIONS(m_context.view) != view) {
442 qCInfo(lcQpaOpenGLContext) <<
"Failed to set" << view <<
"as drawable for" << m_context;
443 m_updateObservers.clear();
447 qCInfo(lcQpaOpenGLContext) <<
"Set drawable for" << m_context <<
"to" << QT_IGNORE_DEPRECATIONS(m_context.view);
454Q_CONSTINIT
static QMutex s_reentrancyMutex;
461 QMacAutoReleasePool pool;
463 QMutexLocker locker(&s_reentrancyMutex);
464 qCInfo(lcQpaOpenGLContext) <<
"Updating" << m_context <<
"for" << QT_IGNORE_DEPRECATIONS(m_context.view);
469#if defined(Q_PROCESSOR_X86_64)
470 static bool tahoeOrAbove = QOperatingSystemVersion::current() >= QOperatingSystemVersion::MacOSTahoe;
471 auto *layer = QT_IGNORE_DEPRECATIONS(m_context.view.layer);
472 if (tahoeOrAbove && isSoftwareContext() && layer.needsDisplay) {
473 static QNoopDisplayDelegate *noopDisplayDelegate = [QNoopDisplayDelegate
new];
474 qCDebug(lcQpaOpenGLContext) <<
"Layer needs display. Installing noop display delegate" << noopDisplayDelegate;
475 auto *orignalDelegate = layer.delegate;
476 layer.delegate = noopDisplayDelegate;
480 qCDebug(lcQpaOpenGLContext) <<
"Restoring original layer delegate" << orignalDelegate;
481 layer.delegate = orignalDelegate;
482 [layer setNeedsDisplay];
493 QMacAutoReleasePool pool;
495 qCDebug(lcQpaOpenGLContext) <<
"Swapping" << m_context
496 <<
"in" << QThread::currentThread() <<
"to" << surface;
498 if (surface->surface()->surfaceClass() == QSurface::Offscreen)
501 if (!setDrawable(surface)) {
502 qCWarning(lcQpaOpenGLContext) <<
"Can't flush" << m_context
503 <<
"without" << surface <<
"as drawable";
513 auto *cocoaWindow =
static_cast<
QCocoaWindow *>(surface);
514 if (cocoaWindow->geometry().size() != cocoaWindow->m_exposedRect.size()) {
515 qCInfo(lcQpaOpenGLContext) <<
"Window exposed size does not match geometry (yet)."
516 <<
"Skipping flush to avoid visual artifacts.";
520 QMutexLocker locker(&s_reentrancyMutex);
521 [m_context flushBuffer];
526 QMacAutoReleasePool pool;
528 qCDebug(lcQpaOpenGLContext) <<
"Clearing current context"
529 << [NSOpenGLContext currentContext] <<
"in" << QThread::currentThread();
535 [NSOpenGLContext clearCurrentContext];
545 return m_context != nil;
550 return m_shareContext != nil;
555 return m_isSoftwareContext;
565 return (QFunctionPointer)dlsym(RTLD_NEXT, procName);
568#ifndef QT_NO_DEBUG_STREAM
571 QDebugStateSaver saver(debug);
573 debug <<
"QCocoaGLContext(" << (
const void *)context;
575 if (debug.verbosity() > QDebug::DefaultVerbosity)
576 debug <<
", " << context->format();
577 debug <<
", " << context->nativeContext();
586#if defined(Q_PROCESSOR_X86_64)
587@implementation QNoopDisplayDelegate
588- (
void)displayLayer:(CALayer *)layer
590 qCWarning(lcQpaOpenGLContext) <<
"Ignoring display of" << layer <<
"during [NSOpenGLContext update]";
NSOpenGLContext * nativeContext() const override
void swapBuffers(QPlatformSurface *surface) override
Reimplement in subclass to native swap buffers calls.
void doneCurrent() override
QSurfaceFormat format() const override
bool isSoftwareContext() const
bool isSharing() const override
void initialize() override
Called after a new instance is constructed.
void beginFrame() override
Called when the RHI begins rendering a new frame in the context.
QFunctionPointer getProcAddress(const char *procName) override
Reimplement in subclass to allow dynamic querying of OpenGL symbols.
QCocoaGLContext(QOpenGLContext *context)
bool makeCurrent(QPlatformSurface *surface) override
bool isValid() const override
static QByteArray getGlString(GLenum param)
Q_LOGGING_CATEGORY(lcEventDispatcher, "qt.eventdispatcher")