iOS: Move handling of FBOs to QIOSContext instead of QIOSWindow

The lifetime of an FBO is tied to its context, so letting each window
manage its own FBO failed when the window tried to delete the FBO
at destruction time without the proper context being current, or
even available anymore.

We solve this by moving all handling of FBOs to the context itself,
which is fine as we're exposing the necessary bits from the window
to allocate storage based on its layer.

Change-Id: I8c7c96cf63d6b667527c816f10ac2f4ff6a05e0c
Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@digia.com>
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@digia.com>
This commit is contained in:
Tor Arne Vestbø 2012-12-25 00:21:25 +01:00
parent 157d690b8c
commit 847ac6008c
4 changed files with 117 additions and 88 deletions

View File

@ -48,8 +48,10 @@
QT_BEGIN_NAMESPACE
class QIOSContext : public QPlatformOpenGLContext
class QIOSContext : public QObject, public QPlatformOpenGLContext
{
Q_OBJECT
public:
QIOSContext(QOpenGLContext *context);
~QIOSContext();
@ -62,15 +64,26 @@ public:
void doneCurrent();
GLuint defaultFramebufferObject(QPlatformSurface *) const;
GLuint defaultColorRenderbuffer(QPlatformSurface *) const;
QFunctionPointer getProcAddress(const QByteArray &procName);
EAGLContext *nativeContext() const;
private Q_SLOTS:
void windowDestroyed(QObject *object);
private:
EAGLContext *m_eaglContext;
QSurfaceFormat m_format;
struct FramebufferObject {
GLuint handle;
GLuint colorRenderbuffer;
GLuint depthRenderbuffer;
GLint renderbufferWidth;
GLint renderbufferHeight;
};
static void deleteBuffers(const FramebufferObject &framebufferObject);
mutable QHash<QWindow *, FramebufferObject> m_framebufferObjects;
};
QT_END_NAMESPACE

View File

@ -70,12 +70,25 @@ QIOSContext::QIOSContext(QOpenGLContext *context)
QIOSContext::~QIOSContext()
{
if ([EAGLContext currentContext] == m_eaglContext)
doneCurrent();
[EAGLContext setCurrentContext:m_eaglContext];
foreach (const FramebufferObject &framebufferObject, m_framebufferObjects)
deleteBuffers(framebufferObject);
[EAGLContext setCurrentContext:nil];
[m_eaglContext release];
}
void QIOSContext::deleteBuffers(const FramebufferObject &framebufferObject)
{
if (framebufferObject.handle)
glDeleteFramebuffers(1, &framebufferObject.handle);
if (framebufferObject.colorRenderbuffer)
glDeleteRenderbuffers(1, &framebufferObject.colorRenderbuffer);
if (framebufferObject.depthRenderbuffer)
glDeleteRenderbuffers(1, &framebufferObject.depthRenderbuffer);
}
QSurfaceFormat QIOSContext::format() const
{
return m_format;
@ -88,8 +101,7 @@ bool QIOSContext::makeCurrent(QPlatformSurface *surface)
[EAGLContext setCurrentContext:m_eaglContext];
glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebufferObject(surface));
// Ensures render buffers are set up and match the size of the window
return defaultColorRenderbuffer(surface) != 0;
return true;
}
void QIOSContext::doneCurrent()
@ -100,20 +112,86 @@ void QIOSContext::doneCurrent()
void QIOSContext::swapBuffers(QPlatformSurface *surface)
{
Q_ASSERT(surface && surface->surface()->surfaceType() == QSurface::OpenGLSurface);
Q_ASSERT(surface->surface()->surfaceClass() == QSurface::Window);
QWindow *window = static_cast<QWindow *>(surface->surface());
Q_ASSERT(m_framebufferObjects.contains(window));
[EAGLContext setCurrentContext:m_eaglContext];
glBindRenderbuffer(GL_RENDERBUFFER, defaultColorRenderbuffer(surface));
glBindRenderbuffer(GL_RENDERBUFFER, m_framebufferObjects[window].colorRenderbuffer);
[m_eaglContext presentRenderbuffer:GL_RENDERBUFFER];
}
GLuint QIOSContext::defaultFramebufferObject(QPlatformSurface *surface) const
{
return static_cast<QIOSWindow *>(surface)->framebufferObject(*this);
Q_ASSERT(surface && surface->surface()->surfaceClass() == QSurface::Window);
QWindow *window = static_cast<QWindow *>(surface->surface());
FramebufferObject &framebufferObject = m_framebufferObjects[window];
// Set up an FBO for the window if it hasn't been created yet
if (!framebufferObject.handle) {
[EAGLContext setCurrentContext:m_eaglContext];
glGenFramebuffers(1, &framebufferObject.handle);
glBindFramebuffer(GL_FRAMEBUFFER, framebufferObject.handle);
glGenRenderbuffers(1, &framebufferObject.colorRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, framebufferObject.colorRenderbuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
framebufferObject.colorRenderbuffer);
if (m_format.depthBufferSize() > 0 || m_format.stencilBufferSize() > 0) {
glGenRenderbuffers(1, &framebufferObject.depthRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, framebufferObject.depthRenderbuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER,
framebufferObject.depthRenderbuffer);
if (m_format.stencilBufferSize() > 0)
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
framebufferObject.depthRenderbuffer);
}
connect(window, SIGNAL(destroyed(QObject*)), this, SLOT(windowDestroyed(QObject*)));
}
// Ensure that the FBO's buffers match the size of the window
QIOSWindow *platformWindow = static_cast<QIOSWindow *>(surface);
if (framebufferObject.renderbufferWidth != platformWindow->effectiveWidth() ||
framebufferObject.renderbufferHeight != platformWindow->effectiveHeight()) {
glBindRenderbuffer(GL_RENDERBUFFER, framebufferObject.colorRenderbuffer);
CAEAGLLayer *layer = static_cast<CAEAGLLayer *>(platformWindow->nativeView().layer);
[m_eaglContext renderbufferStorage:GL_RENDERBUFFER fromDrawable:layer];
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &framebufferObject.renderbufferWidth);
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &framebufferObject.renderbufferHeight);
if (framebufferObject.depthRenderbuffer) {
glBindRenderbuffer(GL_RENDERBUFFER, framebufferObject.depthRenderbuffer);
// FIXME: Support more fine grained control over depth/stencil buffer sizes
if (m_format.stencilBufferSize() > 0)
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8_OES,
framebufferObject.renderbufferWidth, framebufferObject.renderbufferHeight);
else
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16,
framebufferObject.renderbufferWidth, framebufferObject.renderbufferHeight);
}
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
NSLog(@"Failed to make complete framebuffer object %x", glCheckFramebufferStatus(GL_FRAMEBUFFER));
}
return framebufferObject.handle;
}
GLuint QIOSContext::defaultColorRenderbuffer(QPlatformSurface *surface) const
void QIOSContext::windowDestroyed(QObject *object)
{
return static_cast<QIOSWindow *>(surface)->colorRenderbuffer(*this);
QWindow *window = static_cast<QWindow *>(object);
if (m_framebufferObjects.contains(window)) {
deleteBuffers(m_framebufferObjects[window]);
m_framebufferObjects.remove(window);
}
}
QFunctionPointer QIOSContext::getProcAddress(const QByteArray& functionName)
@ -121,7 +199,5 @@ QFunctionPointer QIOSContext::getProcAddress(const QByteArray& functionName)
return reinterpret_cast<QFunctionPointer>(dlsym(RTLD_NEXT, functionName.constData()));
}
EAGLContext *QIOSContext::nativeContext() const
{
return m_eaglContext;
}
#include "moc_qioscontext.cpp"

View File

@ -67,9 +67,9 @@ public:
void handleContentOrientationChange(Qt::ScreenOrientation orientation);
void setVisible(bool visible);
GLuint framebufferObject(const QIOSContext &context) const;
GLuint colorRenderbuffer(const QIOSContext &context) const;
qreal devicePixelRatio() const;
int effectiveWidth() const;
int effectiveHeight() const;
UIView *nativeView() const { return m_view; }
@ -77,13 +77,6 @@ private:
UIView *m_view;
QRect m_requestedGeometry;
mutable struct GLData {
GLuint framebufferObject;
GLuint colorRenderbuffer;
GLuint depthRenderbuffer;
GLint renderbufferWidth;
GLint renderbufferHeight;
} m_glData;
qreal m_devicePixelRatio;
};

View File

@ -229,7 +229,6 @@ QIOSWindow::QIOSWindow(QWindow *window)
: QPlatformWindow(window)
, m_view([[EAGLView alloc] initWithQIOSWindow:this])
, m_requestedGeometry(QPlatformWindow::geometry())
, m_glData()
, m_devicePixelRatio(1.0)
{
if (isQtApplication())
@ -249,13 +248,6 @@ QIOSWindow::QIOSWindow(QWindow *window)
QIOSWindow::~QIOSWindow()
{
if (m_glData.framebufferObject)
glDeleteFramebuffers(1, &m_glData.framebufferObject);
if (m_glData.colorRenderbuffer)
glDeleteRenderbuffers(1, &m_glData.colorRenderbuffer);
if (m_glData.depthRenderbuffer)
glDeleteRenderbuffers(1, &m_glData.depthRenderbuffer);
[m_view removeFromSuperview];
[m_view release];
}
@ -325,64 +317,19 @@ void QIOSWindow::handleContentOrientationChange(Qt::ScreenOrientation orientatio
[[UIApplication sharedApplication] setStatusBarOrientation:uiOrientation animated:NO];
}
GLuint QIOSWindow::framebufferObject(const QIOSContext &context) const
{
if (!m_glData.framebufferObject) {
[EAGLContext setCurrentContext:context.nativeContext()];
glGenFramebuffers(1, &m_glData.framebufferObject);
glBindFramebuffer(GL_FRAMEBUFFER, m_glData.framebufferObject);
glGenRenderbuffers(1, &m_glData.colorRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, m_glData.colorRenderbuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_glData.colorRenderbuffer);
QSurfaceFormat requestedFormat = context.format();
if (requestedFormat.depthBufferSize() > 0 || requestedFormat.stencilBufferSize() > 0) {
glGenRenderbuffers(1, &m_glData.depthRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, m_glData.depthRenderbuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_glData.depthRenderbuffer);
if (requestedFormat.stencilBufferSize() > 0)
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, m_glData.depthRenderbuffer);
}
}
return m_glData.framebufferObject;
}
GLuint QIOSWindow::colorRenderbuffer(const QIOSContext &context) const
{
if (!m_glData.colorRenderbuffer ||
m_glData.renderbufferWidth != geometry().width() * m_devicePixelRatio ||
m_glData.renderbufferHeight != geometry().height() * m_devicePixelRatio) {
glBindRenderbuffer(GL_RENDERBUFFER, m_glData.colorRenderbuffer);
[context.nativeContext() renderbufferStorage:GL_RENDERBUFFER fromDrawable:static_cast<CAEAGLLayer *>(m_view.layer)];
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &m_glData.renderbufferWidth);
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &m_glData.renderbufferHeight);
if (m_glData.depthRenderbuffer) {
glBindRenderbuffer(GL_RENDERBUFFER, m_glData.depthRenderbuffer);
// FIXME: Support more fine grained control over depth/stencil buffer sizes
if (context.format().stencilBufferSize() > 0)
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8_OES, m_glData.renderbufferWidth, m_glData.renderbufferHeight);
else
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, m_glData.renderbufferWidth, m_glData.renderbufferHeight);
}
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
NSLog(@"Failed to make complete framebuffer object %x", glCheckFramebufferStatus(GL_FRAMEBUFFER));
}
return m_glData.colorRenderbuffer;
}
qreal QIOSWindow::devicePixelRatio() const
{
return m_devicePixelRatio;
}
int QIOSWindow::effectiveWidth() const
{
return geometry().width() * m_devicePixelRatio;
}
int QIOSWindow::effectiveHeight() const
{
return geometry().height() * m_devicePixelRatio;
}
QT_END_NAMESPACE