14#include <QtGui/QGuiApplication>
15#include <QtGui/QOpenGLContext>
17#import <OpenGLES/EAGL.h>
18#import <OpenGLES/ES2/glext.h>
19#import <QuartzCore/CAEAGLLayer.h>
26 : QPlatformOpenGLContext()
27 , m_sharedContext(
static_cast<
QIOSContext *>(context->shareHandle()))
29 , m_format(context->format())
31 m_format.setRenderableType(QSurfaceFormat::OpenGLES);
33 EAGLSharegroup *shareGroup = m_sharedContext ? [m_sharedContext->m_eaglContext sharegroup] : nil;
34 const int preferredVersion = m_format.majorVersion() == 1 ? kEAGLRenderingAPIOpenGLES1 : kEAGLRenderingAPIOpenGLES3;
35 for (
int version = preferredVersion; !m_eaglContext && version >= m_format.majorVersion(); --version)
36 m_eaglContext = [[EAGLContext alloc] initWithAPI:EAGLRenderingAPI(version) sharegroup:shareGroup];
38 if (m_eaglContext != nil) {
39 EAGLContext *originalContext = [EAGLContext currentContext];
40 [EAGLContext setCurrentContext:m_eaglContext];
41 const GLubyte *s = glGetString(GL_VERSION);
43 QByteArray version = QByteArray(
reinterpret_cast<
const char *>(s));
45 if (QPlatformOpenGLContext::parseOpenGLVersion(version, major, minor)) {
46 m_format.setMajorVersion(major);
47 m_format.setMinorVersion(minor);
50 [EAGLContext setCurrentContext:originalContext];
57 m_format.setSwapBehavior(QSurfaceFormat::DoubleBuffer);
59 qCDebug(lcQpaGLContext) <<
"created context with format" << m_format <<
"shared with" << m_sharedContext;
64 [EAGLContext setCurrentContext:m_eaglContext];
66 foreach (
const FramebufferObject &framebufferObject, m_framebufferObjects)
67 deleteBuffers(framebufferObject);
69 [EAGLContext setCurrentContext:nil];
70 [m_eaglContext release];
73void QIOSContext::deleteBuffers(
const FramebufferObject &framebufferObject)
75 if (framebufferObject.handle)
76 glDeleteFramebuffers(1, &framebufferObject.handle);
77 if (framebufferObject.colorRenderbuffer)
78 glDeleteRenderbuffers(1, &framebufferObject.colorRenderbuffer);
79 if (framebufferObject.depthRenderbuffer)
80 glDeleteRenderbuffers(1, &framebufferObject.depthRenderbuffer);
88#define QT_IOS_GL_STATUS_CASE(val) case val: return QLatin1StringView(#val)
98 return QString(QStringLiteral(
"unknown status: %x")).arg(status);
102#define Q_ASSERT_IS_GL_SURFACE(surface)
103 Q_ASSERT(surface && (surface->surface()->surfaceType() == QSurface::OpenGLSurface))
109 if (!verifyGraphicsHardwareAvailability())
112 [EAGLContext setCurrentContext:m_eaglContext];
115 if (surface->surface()->surfaceClass() == QSurface::Offscreen)
118 Q_ASSERT(surface->surface()->surfaceClass() == QSurface::Window);
119 FramebufferObject &framebufferObject = backingFramebufferObjectFor(surface);
121 if (!framebufferObject.handle) {
123 glGenFramebuffers(1, &framebufferObject.handle);
124 glBindFramebuffer(GL_FRAMEBUFFER, framebufferObject.handle);
126 glGenRenderbuffers(1, &framebufferObject.colorRenderbuffer);
127 glBindRenderbuffer(GL_RENDERBUFFER, framebufferObject.colorRenderbuffer);
128 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
129 framebufferObject.colorRenderbuffer);
131 if (m_format.depthBufferSize() > 0 || m_format.stencilBufferSize() > 0) {
132 glGenRenderbuffers(1, &framebufferObject.depthRenderbuffer);
133 glBindRenderbuffer(GL_RENDERBUFFER, framebufferObject.depthRenderbuffer);
134 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER,
135 framebufferObject.depthRenderbuffer);
137 if (m_format.stencilBufferSize() > 0)
138 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
139 framebufferObject.depthRenderbuffer);
142 glBindFramebuffer(GL_FRAMEBUFFER, framebufferObject.handle);
145 if (needsRenderbufferResize(surface)) {
147 CAEAGLLayer *layer =
static_cast<QIOSWindow *>(surface)->eaglLayer();
148 qCDebug(lcQpaGLContext,
"Reallocating renderbuffer storage - current: %dx%d, layer: %gx%g",
149 framebufferObject.renderbufferWidth, framebufferObject.renderbufferHeight,
150 layer.frame.size.width * layer.contentsScale, layer.frame.size.height * layer.contentsScale);
152 glBindRenderbuffer(GL_RENDERBUFFER, framebufferObject.colorRenderbuffer);
153 [m_eaglContext renderbufferStorage:GL_RENDERBUFFER fromDrawable:layer];
155 glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &framebufferObject.renderbufferWidth);
156 glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &framebufferObject.renderbufferHeight);
158 if (framebufferObject.depthRenderbuffer) {
159 glBindRenderbuffer(GL_RENDERBUFFER, framebufferObject.depthRenderbuffer);
162 if (m_format.stencilBufferSize() > 0)
163 glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8_OES,
164 framebufferObject.renderbufferWidth, framebufferObject.renderbufferHeight);
166 glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16,
167 framebufferObject.renderbufferWidth, framebufferObject.renderbufferHeight);
170 framebufferObject.isComplete = glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE;
172 if (!framebufferObject.isComplete) {
173 qCWarning(lcQpaGLContext,
"QIOSContext failed to make complete framebuffer object (%s)",
174 qPrintable(fboStatusString(glCheckFramebufferStatus(GL_FRAMEBUFFER))));
178 return framebufferObject.isComplete;
183 [EAGLContext setCurrentContext:nil];
190 if (!verifyGraphicsHardwareAvailability())
193 if (surface->surface()->surfaceClass() == QSurface::Offscreen)
196 FramebufferObject &framebufferObject = backingFramebufferObjectFor(surface);
197 Q_ASSERT_X(framebufferObject.isComplete,
"QIOSContext",
"swapBuffers on incomplete FBO");
199 if (needsRenderbufferResize(surface)) {
200 qCWarning(lcQpaGLContext,
"CAEAGLLayer was resized between makeCurrent and swapBuffers, skipping flush");
204 [EAGLContext setCurrentContext:m_eaglContext];
205 glBindRenderbuffer(GL_RENDERBUFFER, framebufferObject.colorRenderbuffer);
206 [m_eaglContext presentRenderbuffer:GL_RENDERBUFFER];
216 return m_sharedContext->backingFramebufferObjectFor(surface);
218 if (!m_framebufferObjects.contains(surface)) {
220 connect(
static_cast<QIOSWindow *>(surface), SIGNAL(destroyed(QObject*)),
this, SLOT(windowDestroyed(QObject*)));
223 return m_framebufferObjects[surface];
228 if (surface->surface()->surfaceClass() == QSurface::Offscreen) {
235 FramebufferObject &framebufferObject = backingFramebufferObjectFor(surface);
236 Q_ASSERT_X(framebufferObject.handle,
"QIOSContext",
"can't resolve default FBO before makeCurrent");
238 return framebufferObject.handle;
241bool QIOSContext::needsRenderbufferResize(QPlatformSurface *surface)
const
243 Q_ASSERT(surface->surface()->surfaceClass() == QSurface::Window);
245 FramebufferObject &framebufferObject = backingFramebufferObjectFor(surface);
246 CAEAGLLayer *layer =
static_cast<QIOSWindow *>(surface)->eaglLayer();
248 if (framebufferObject.renderbufferWidth != (layer.frame.size.width * layer.contentsScale))
251 if (framebufferObject.renderbufferHeight != (layer.frame.size.height * layer.contentsScale))
257bool QIOSContext::verifyGraphicsHardwareAvailability()
266 static bool applicationBackgrounded = QGuiApplication::applicationState() == Qt::ApplicationSuspended;
268 static dispatch_once_t onceToken = 0;
269 dispatch_once(&onceToken, ^{
271 connect(applicationState, &QIOSApplicationState::applicationStateWillChange,
272 [](Qt::ApplicationState oldState, Qt::ApplicationState newState) {
274 if (applicationBackgrounded && newState != Qt::ApplicationSuspended) {
275 qCDebug(lcQpaGLContext) <<
"app no longer backgrounded, rendering enabled";
276 applicationBackgrounded =
false;
280 connect(applicationState, &QIOSApplicationState::applicationStateDidChange,
281 [](Qt::ApplicationState oldState, Qt::ApplicationState newState) {
283 if (newState != Qt::ApplicationSuspended)
286 qCDebug(lcQpaGLContext) <<
"app backgrounded, rendering disabled";
287 applicationBackgrounded =
true;
293 if (QOpenGLContext *currentContext = QOpenGLContext::currentContext()) {
294 qCWarning(lcQpaGLContext) <<
"explicitly glFinishing and deactivating" << currentContext;
296 currentContext->doneCurrent();
302 if (applicationBackgrounded)
303 qCWarning(lcQpaGLContext,
"OpenGL ES calls are not allowed while an application is backgrounded");
305 return !applicationBackgrounded;
310 QIOSWindow *window =
static_cast<QIOSWindow *>(object);
311 if (!m_framebufferObjects.contains(window))
314 qCDebug(lcQpaGLContext) << object <<
"destroyed, deleting corresponding FBO";
316 EAGLContext *originalContext = [EAGLContext currentContext];
317 [EAGLContext setCurrentContext:m_eaglContext];
318 deleteBuffers(m_framebufferObjects[window]);
319 m_framebufferObjects.remove(window);
320 [EAGLContext setCurrentContext:originalContext];
325 return QFunctionPointer(dlsym(RTLD_DEFAULT, functionName));
330 return m_eaglContext;
335 return m_sharedContext;
340#include "moc_qioscontext.cpp"
QSurfaceFormat format() const override
void doneCurrent() override
bool isSharing() const override
void swapBuffers(QPlatformSurface *surface) override
Reimplement in subclass to native swap buffers calls.
GLuint defaultFramebufferObject(QPlatformSurface *) const override
Reimplement in subclass if your platform uses framebuffer objects for surfaces.
bool isValid() const override
QFunctionPointer getProcAddress(const char *procName) override
Reimplement in subclass to allow dynamic querying of OpenGL symbols.
bool makeCurrent(QPlatformSurface *surface) override
static QIOSIntegration * instance()
Q_LOGGING_CATEGORY(lcEventDispatcher, "qt.eventdispatcher")
#define Q_ASSERT_IS_GL_SURFACE(surface)
#define QT_IOS_GL_STATUS_CASE(val)
static QString fboStatusString(GLenum status)