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));
24QT_DECLARE_NAMESPACED_OBJC_INTERFACE(QNoopDisplayDelegate, NSObject<CALayerDelegate>)
28Q_LOGGING_CATEGORY(lcQpaOpenGLContext,
"qt.qpa.openglcontext", QtWarningMsg);
39 m_context = [nativeContext retain];
48 if (QPlatformOpenGLContext *shareContext = context()->shareHandle())
49 m_shareContext =
static_cast<QCocoaGLContext *>(shareContext)->nativeContext();
51 updateSurfaceFormat();
58 if (m_format.renderableType() == QSurfaceFormat::DefaultRenderableType)
59 m_format.setRenderableType(QSurfaceFormat::OpenGL);
60 if (m_format.renderableType() != QSurfaceFormat::OpenGL)
63 if (QPlatformOpenGLContext *shareContext = context()->shareHandle()) {
64 m_shareContext =
static_cast<QCocoaGLContext *>(shareContext)->nativeContext();
71 GLint shareContextRequestedProfile;
72 [m_shareContext.pixelFormat getValues:&shareContextRequestedProfile
73 forAttribute:NSOpenGLPFAOpenGLProfile forVirtualScreen:0];
74 auto shareContextActualProfile = shareContext->format().version();
76 if (shareContextRequestedProfile == NSOpenGLProfileVersion3_2Core
77 && shareContextActualProfile >= qMakePair(4, 1)) {
81 if (m_format.version() >= qMakePair(4, 1))
82 m_format.setVersion(3, 2);
88 NSOpenGLPixelFormat *pixelFormat = [pixelFormatForSurfaceFormat(m_format) autorelease];
89 m_context = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:m_shareContext];
91 if (!m_context && m_shareContext) {
92 qCWarning(lcQpaOpenGLContext,
"Could not create NSOpenGLContext with shared context, "
93 "falling back to unshared context.");
94 m_context = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:nil];
99 qCWarning(lcQpaOpenGLContext,
"Failed to create NSOpenGLContext");
105 const GLint interval = m_format.swapInterval() >= 0 ? m_format.swapInterval() : 1;
106 [m_context setValues:&interval forParameter:NSOpenGLContextParameterSwapInterval];
108 if (m_format.alphaBufferSize() > 0) {
110 [m_context setValues:&zeroOpacity forParameter:NSOpenGLContextParameterSurfaceOpacity];
115 const GLint order = qt_mac_resolveOption(1,
"QT_MAC_OPENGL_SURFACE_ORDER");
116 [m_context setValues:&order forParameter:NSOpenGLContextParameterSurfaceOrder];
118 updateSurfaceFormat();
120 qCDebug(lcQpaOpenGLContext).verbosity(3) <<
"Created" <<
this <<
"based on requested" << context()->format();
123NSOpenGLPixelFormat *
QCocoaGLContext::pixelFormatForSurfaceFormat(
const QSurfaceFormat &format)
125 QVector<NSOpenGLPixelFormatAttribute> attrs;
127 attrs << NSOpenGLPFAOpenGLProfile;
128 if (format.profile() == QSurfaceFormat::CoreProfile) {
129 if (format.version() >= qMakePair(4, 1))
130 attrs << NSOpenGLProfileVersion4_1Core;
131 else if (format.version() >= qMakePair(3, 2))
132 attrs << NSOpenGLProfileVersion3_2Core;
134 attrs << NSOpenGLProfileVersionLegacy;
136 attrs << NSOpenGLProfileVersionLegacy;
139 switch (format.swapBehavior()) {
140 case QSurfaceFormat::SingleBuffer:
142 case QSurfaceFormat::DefaultSwapBehavior:
146 case QSurfaceFormat::DoubleBuffer:
147 attrs.append(NSOpenGLPFADoubleBuffer);
149 case QSurfaceFormat::TripleBuffer:
150 attrs.append(NSOpenGLPFATripleBuffer);
154 if (format.depthBufferSize() > 0)
155 attrs << NSOpenGLPFADepthSize << format.depthBufferSize();
156 if (format.stencilBufferSize() > 0)
157 attrs << NSOpenGLPFAStencilSize << format.stencilBufferSize();
158 if (format.alphaBufferSize() > 0)
159 attrs << NSOpenGLPFAAlphaSize << format.alphaBufferSize();
161 auto rbz = format.redBufferSize();
162 auto gbz = format.greenBufferSize();
163 auto bbz = format.blueBufferSize();
164 if (rbz > 0 || gbz > 0 || bbz > 0) {
165 auto fallbackSize = qMax(rbz, qMax(gbz, bbz));
166 auto colorSize = (rbz > 0 ? rbz : fallbackSize)
167 + (gbz > 0 ? gbz : fallbackSize)
168 + (bbz > 0 ? bbz : fallbackSize);
169 attrs << NSOpenGLPFAColorSize << colorSize << NSOpenGLPFAMinimumPolicy;
172 if (format.samples() > 0) {
173 attrs << NSOpenGLPFAMultisample
174 << NSOpenGLPFASampleBuffers << NSOpenGLPixelFormatAttribute(1)
175 << NSOpenGLPFASamples << NSOpenGLPixelFormatAttribute(format.samples());
180 static bool offlineRenderersAllowed = qEnvironmentVariableIsEmpty(
"QT_MAC_PRO_WEBENGINE_WORKAROUND");
181 if (offlineRenderersAllowed) {
183 attrs << NSOpenGLPFAAllowOfflineRenderers;
186 if (qGuiApp->testAttribute(Qt::AA_UseSoftwareOpenGL)) {
189 attrs << NSOpenGLPFARendererID << kCGLRendererGenericFloatID;
193 return [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs.constData()];
197
198
199
200
203 NSOpenGLContext *oldContext = [NSOpenGLContext currentContext];
204 [m_context makeCurrentContext];
208 int major = 0, minor = 0;
209 QByteArray versionString(getGlString(GL_VERSION));
210 if (QPlatformOpenGLContext::parseOpenGLVersion(versionString, major, minor)) {
211 m_format.setMajorVersion(major);
212 m_format.setMinorVersion(minor);
215 m_format.setProfile(QSurfaceFormat::NoProfile);
216 if (m_format.version() >= qMakePair(3, 2)) {
218 glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &value);
219 if (value & GL_CONTEXT_CORE_PROFILE_BIT)
220 m_format.setProfile(QSurfaceFormat::CoreProfile);
221 else if (value & GL_CONTEXT_COMPATIBILITY_PROFILE_BIT)
222 m_format.setProfile(QSurfaceFormat::CompatibilityProfile);
225 m_format.setOption(QSurfaceFormat::DeprecatedFunctions, [&]() {
226 if (m_format.version() < qMakePair(3, 0)) {
230 glGetIntegerv(GL_CONTEXT_FLAGS, &value);
231 return !(value & GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT);
236 m_format.setOption(QSurfaceFormat::DebugContext,
false);
239 m_format.setOption(QSurfaceFormat::StereoBuffers,
false);
243 NSOpenGLPixelFormat *pixelFormat = m_context.pixelFormat;
245 GLint virtualScreen = [&,
this]() {
246 auto *platformScreen =
static_cast<
QCocoaScreen*>(context()->screen()->handle());
247 auto displayId = platformScreen->nativeScreen().qt_displayId;
248 auto requestedDisplay = CGDisplayIDToOpenGLDisplayMask(displayId);
249 for (
int i = 0; i < pixelFormat.numberOfVirtualScreens; ++i) {
250 GLint supportedDisplays;
251 [pixelFormat getValues:&supportedDisplays forAttribute:NSOpenGLPFAScreenMask forVirtualScreen:i];
256 if (requestedDisplay & supportedDisplays)
259 qCWarning(lcQpaOpenGLContext) <<
"Could not find virtual screen for"
260 << platformScreen <<
"with displayId" << displayId;
264 auto pixelFormatAttribute = [&](NSOpenGLPixelFormatAttribute attribute) {
266 [pixelFormat getValues:&value forAttribute:attribute forVirtualScreen:virtualScreen];
270 const auto pixelFormatAlphaSize = pixelFormatAttribute(NSOpenGLPFAAlphaSize);
274 if (pixelFormatAttribute(NSOpenGLPFAOpenGLProfile) == NSOpenGLProfileVersionLegacy) {
275 GLint redBits, greenBits, blueBits;
276 glGetIntegerv(GL_RED_BITS, &redBits);
277 glGetIntegerv(GL_GREEN_BITS, &greenBits);
278 glGetIntegerv(GL_BLUE_BITS, &blueBits);
279 m_format.setRedBufferSize(redBits);
280 m_format.setGreenBufferSize(greenBits);
281 m_format.setBlueBufferSize(blueBits);
283 int colorSize = pixelFormatAttribute(NSOpenGLPFAColorSize);
284 colorSize -= pixelFormatAlphaSize;
286 m_format.setRedBufferSize(colorSize);
287 m_format.setGreenBufferSize(colorSize);
288 m_format.setBlueBufferSize(colorSize);
296 if (m_format.alphaBufferSize() > 0)
297 m_format.setAlphaBufferSize(pixelFormatAlphaSize);
299 m_format.setDepthBufferSize(pixelFormatAttribute(NSOpenGLPFADepthSize));
300 m_format.setStencilBufferSize(pixelFormatAttribute(NSOpenGLPFAStencilSize));
301 m_format.setSamples(pixelFormatAttribute(NSOpenGLPFASamples));
303 if (pixelFormatAttribute(NSOpenGLPFATripleBuffer))
304 m_format.setSwapBehavior(QSurfaceFormat::TripleBuffer);
305 else if (pixelFormatAttribute(NSOpenGLPFADoubleBuffer))
306 m_format.setSwapBehavior(QSurfaceFormat::DoubleBuffer);
308 m_format.setSwapBehavior(QSurfaceFormat::SingleBuffer);
310 m_isSoftwareContext = (pixelFormatAttribute(NSOpenGLPFARendererID)
311 & kCGLRendererIDMatchingMask) == kCGLRendererGenericFloatID;
315 auto glContextParameter = [&](NSOpenGLContextParameter parameter) {
317 [m_context getValues:&value forParameter:parameter];
321 m_format.setSwapInterval(glContextParameter(NSOpenGLContextParameterSwapInterval));
324 [oldContext makeCurrentContext];
326 [NSOpenGLContext clearCurrentContext];
336 QMacAutoReleasePool pool;
338 qCDebug(lcQpaOpenGLContext) <<
"Making" <<
this <<
"current"
339 <<
"in" << QThread::currentThread() <<
"for" << surface;
341 Q_ASSERT(surface->surface()->supportsOpenGL());
343 if (!setDrawable(surface))
346 [m_context makeCurrentContext];
348 if (surface->surface()->surfaceClass() == QSurface::Window) {
349 if (m_needsUpdate.fetchAndStoreRelaxed(
false))
358 QMacAutoReleasePool pool;
360 Q_ASSERT(context() && context()->surface());
361 auto *surface = context()->surface()->surfaceHandle();
364 qCDebug(lcQpaOpenGLContext) <<
"Beginning frame for" <<
this
365 <<
"in" << QThread::currentThread() <<
"for" << surface;
367 Q_ASSERT(surface->surface()->supportsOpenGL());
369 if (surface->surface()->surfaceClass() == QSurface::Window) {
370 if (m_needsUpdate.fetchAndStoreRelaxed(
false))
376
377
378
384 QMacAutoReleasePool pool;
386 if (!surface || surface->surface()->surfaceClass() == QSurface::Offscreen) {
390 qCDebug(lcQpaOpenGLContext) <<
"Clearing current drawable" << QT_IGNORE_DEPRECATIONS(m_context.view) <<
"for" << m_context;
391 [m_context clearDrawable];
395 Q_ASSERT(surface->surface()->surfaceClass() == QSurface::Window);
396 auto *cocoaWindow =
static_cast<
QCocoaWindow *>(surface);
397 QNSView *view = qnsview_cast(cocoaWindow->view());
399 if (view == QT_IGNORE_DEPRECATIONS(m_context.view))
406 QT_IGNORE_DEPRECATIONS(view.wantsBestResolutionOpenGLSurface) = qt_mac_resolveOption(YES,
407 cocoaWindow->window(),
"_q_mac_wantsBestResolutionOpenGLSurface",
408 "QT_MAC_WANTS_BEST_RESOLUTION_OPENGL_SURFACE");
416 auto updateCallback = [
this, view]() {
417 Q_ASSERT(QThread::currentThread() == qApp->thread());
418 if (QT_IGNORE_DEPRECATIONS(m_context.view) != view)
420 m_needsUpdate =
true;
423 m_updateObservers.clear();
425 m_updateObservers.append(QMacNotificationObserver(view, NSViewFrameDidChangeNotification, updateCallback));
426 m_updateObservers.append(QMacNotificationObserver(view.window, NSWindowDidChangeScreenNotification, updateCallback));
428 m_updateObservers.append(QMacNotificationObserver([NSApplication sharedApplication],
429 NSApplicationDidChangeScreenParametersNotification, updateCallback));
431 m_updateObservers.append(QMacNotificationObserver(view,
432 QCocoaWindowWillReleaseQNSViewNotification, [
this, view] {
433 if (QT_IGNORE_DEPRECATIONS(m_context.view) != view)
435 qCDebug(lcQpaOpenGLContext) << view <<
"about to be released."
436 <<
"Clearing current drawable for" << m_context;
437 [m_context clearDrawable];
446 QT_IGNORE_DEPRECATIONS(m_context.view) = view;
447 if (QT_IGNORE_DEPRECATIONS(m_context.view) != view) {
448 qCInfo(lcQpaOpenGLContext) <<
"Failed to set" << view <<
"as drawable for" << m_context;
449 m_updateObservers.clear();
453 qCInfo(lcQpaOpenGLContext) <<
"Set drawable for" << m_context <<
"to" << QT_IGNORE_DEPRECATIONS(m_context.view);
460Q_CONSTINIT
static QMutex s_reentrancyMutex;
467 QMacAutoReleasePool pool;
469 QMutexLocker locker(&s_reentrancyMutex);
470 qCInfo(lcQpaOpenGLContext) <<
"Updating" << m_context <<
"for" << QT_IGNORE_DEPRECATIONS(m_context.view);
476 using OSV = QOperatingSystemVersion;
477 static const bool affectedTahoeVersion =
478 OSV::current() >= OSV(OSV::MacOS, 26, 0) &&
479 OSV::current() < OSV(OSV::MacOS, 26, 5);
480 auto *layer = QT_IGNORE_DEPRECATIONS(m_context.view.layer);
482 static QNoopDisplayDelegate *noopDisplayDelegate = [QNoopDisplayDelegate
new];
483 qCDebug(lcQpaOpenGLContext) <<
"Layer needs display. Installing noop display delegate" << noopDisplayDelegate;
484 auto *orignalDelegate = layer.delegate;
485 layer.delegate = noopDisplayDelegate;
489 qCDebug(lcQpaOpenGLContext) <<
"Restoring original layer delegate" << orignalDelegate;
490 layer.delegate = orignalDelegate;
491 [layer setNeedsDisplay];
497 glGetIntegerv(GL_FRAMEBUFFER_BINDING, &previousFBO);
498 glBindFramebuffer(GL_FRAMEBUFFER, 0);
500 glBindFramebuffer(GL_FRAMEBUFFER, previousFBO);
505 QMacAutoReleasePool pool;
507 qCDebug(lcQpaOpenGLContext) <<
"Swapping" << m_context
508 <<
"in" << QThread::currentThread() <<
"to" << surface;
510 if (surface->surface()->surfaceClass() == QSurface::Offscreen)
513 if (!setDrawable(surface)) {
514 qCWarning(lcQpaOpenGLContext) <<
"Can't flush" << m_context
515 <<
"without" << surface <<
"as drawable";
525 auto *cocoaWindow =
static_cast<
QCocoaWindow *>(surface);
526 if (cocoaWindow->geometry().size() != cocoaWindow->m_exposedRect.size()) {
527 qCInfo(lcQpaOpenGLContext) <<
"Window exposed size does not match geometry (yet)."
528 <<
"Skipping flush to avoid visual artifacts.";
532 QMutexLocker locker(&s_reentrancyMutex);
533 [m_context flushBuffer];
538 QMacAutoReleasePool pool;
540 qCDebug(lcQpaOpenGLContext) <<
"Clearing current context"
541 << [NSOpenGLContext currentContext] <<
"in" << QThread::currentThread();
547 [NSOpenGLContext clearCurrentContext];
557 return m_context != nil;
562 return m_shareContext != nil;
567 return m_isSoftwareContext;
577 return (QFunctionPointer)dlsym(RTLD_NEXT, procName);
580#ifndef QT_NO_DEBUG_STREAM
583 QDebugStateSaver saver(debug);
585 debug <<
"QCocoaGLContext(" << (
const void *)context;
587 if (debug.verbosity() > QDebug::DefaultVerbosity)
588 debug <<
", " << context->format();
589 debug <<
", " << context->nativeContext();
598@implementation QNoopDisplayDelegate
599- (
void)displayLayer:(CALayer *)layer
601 qCInfo(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)