From f6dee67827e72a0660f37998dafb18c6ccd9834e Mon Sep 17 00:00:00 2001 From: Rhys Weatherley Date: Fri, 25 Sep 2009 07:44:10 +1000 Subject: Push the "lazy doneCurrent" logic down from QtOpenVG into QEgl QtOpenVG was doing a lot of housekeeping to avoid having to switch EGL contexts if the same surface was used over and over. This housekeeping really belongs in the QEgl layer so that QtOpenGL could potentially use it as well. This change also adds some overrides for makeCurrent(), swapBuffers(), and destroySurface() that take an EGLSurface directly. This is the first step in separating EGL surface management from context management. Reviewed-by: Sarah Smith --- src/gui/egl/qegl.cpp | 83 +++++++++++++++++++++++- src/gui/egl/qegl_p.h | 9 ++- src/openvg/qpixmapdata_vg.cpp | 5 +- src/openvg/qvg_p.h | 9 --- src/openvg/qwindowsurface_vgegl.cpp | 123 ++++++++---------------------------- 5 files changed, 118 insertions(+), 111 deletions(-) diff --git a/src/gui/egl/qegl.cpp b/src/gui/egl/qegl.cpp index 222524e..a405bbb 100644 --- a/src/gui/egl/qegl.cpp +++ b/src/gui/egl/qegl.cpp @@ -47,21 +47,33 @@ QT_BEGIN_NAMESPACE +// Current GL and VG contexts. These are used to determine if +// we can avoid an eglMakeCurrent() after a call to lazyDoneCurrent(). +// If a background thread modifies the value, the worst that will +// happen is a redundant eglMakeCurrent() in the foreground thread. +static QEglContext * volatile currentGLContext = 0; +static QEglContext * volatile currentVGContext = 0; + QEglContext::QEglContext() : apiType(QEgl::OpenGL) , dpy(EGL_NO_DISPLAY) , ctx(EGL_NO_CONTEXT) , surf(EGL_NO_SURFACE) , cfg(0) + , currentSurface(EGL_NO_SURFACE) , share(false) , current(false) - , reserved(0) { } QEglContext::~QEglContext() { destroy(); + + if (currentGLContext == this) + currentGLContext = 0; + if (currentVGContext == this) + currentVGContext = 0; } bool QEglContext::isValid() const @@ -210,6 +222,19 @@ void QEglContext::destroySurface() } } +// Destroy an EGL surface object. If it was current on this context +// then call doneCurrent() for it first. +void QEglContext::destroySurface(EGLSurface surface) +{ + if (surface != EGL_NO_SURFACE) { + if (surface == currentSurface) + doneCurrent(); + eglDestroySurface(dpy, surface); + if (surf == surface) + surf = EGL_NO_SURFACE; + } +} + // Destroy the context. Note: this does not destroy the surface. void QEglContext::destroy() { @@ -224,14 +249,28 @@ void QEglContext::destroy() bool QEglContext::makeCurrent() { + return makeCurrent(surf); +} + +bool QEglContext::makeCurrent(EGLSurface surface) +{ if (ctx == EGL_NO_CONTEXT) { qWarning() << "QEglContext::makeCurrent(): Cannot make invalid context current"; return false; } + // If lazyDoneCurrent() was called on the surface, then we may be able + // to assume that it is still current within the thread. + if (surface == currentSurface && currentContext(apiType) == this) { + current = true; + return true; + } + current = true; + currentSurface = surface; + setCurrentContext(apiType, this); - bool ok = eglMakeCurrent(dpy, surf, surf, ctx); + bool ok = eglMakeCurrent(dpy, surface, surface, ctx); if (!ok) qWarning() << "QEglContext::makeCurrent():" << errorString(eglGetError()); return ok; @@ -245,6 +284,8 @@ bool QEglContext::doneCurrent() return false; current = false; + currentSurface = EGL_NO_SURFACE; + setCurrentContext(apiType, 0); // We need to select the correct API before calling eglMakeCurrent() // with EGL_NO_CONTEXT because threads can have both OpenGL and OpenVG @@ -264,6 +305,17 @@ bool QEglContext::doneCurrent() return ok; } +// Act as though doneCurrent() was called, but keep the context +// and the surface active for the moment. This allows makeCurrent() +// to skip a call to eglMakeCurrent() if we are using the same +// surface as the last set of painting operations. We leave the +// currentContext() pointer as-is for now. +bool QEglContext::lazyDoneCurrent() +{ + current = false; + return true; +} + bool QEglContext::swapBuffers() { if(ctx == EGL_NO_CONTEXT) @@ -275,6 +327,17 @@ bool QEglContext::swapBuffers() return ok; } +bool QEglContext::swapBuffers(EGLSurface surface) +{ + if(ctx == EGL_NO_CONTEXT) + return false; + + bool ok = eglSwapBuffers(dpy, surface); + if (!ok) + qWarning() << "QEglContext::swapBuffers():" << errorString(eglGetError()); + return ok; +} + // Wait for native rendering operations to complete before starting // to use OpenGL/OpenVG operations. void QEglContext::waitNative() @@ -411,4 +474,20 @@ bool QEglContext::hasExtension(const char* extensionName) return extensions().contains(QLatin1String(extensionName)); } +QEglContext *QEglContext::currentContext(QEgl::API api) +{ + if (api == QEgl::OpenGL) + return currentGLContext; + else + return currentVGContext; +} + +void QEglContext::setCurrentContext(QEgl::API api, QEglContext *context) +{ + if (api == QEgl::OpenGL) + currentGLContext = context; + else + currentVGContext = context; +} + QT_END_NAMESPACE diff --git a/src/gui/egl/qegl_p.h b/src/gui/egl/qegl_p.h index 366bd9e..3903cd0 100644 --- a/src/gui/egl/qegl_p.h +++ b/src/gui/egl/qegl_p.h @@ -92,12 +92,16 @@ public: bool createSurface(QPaintDevice *device, const QEglProperties *properties = 0); bool recreateSurface(QPaintDevice *device); void destroySurface(); + void destroySurface(EGLSurface surface); void destroy(); bool makeCurrent(); + bool makeCurrent(EGLSurface surface); bool doneCurrent(); + bool lazyDoneCurrent(); bool swapBuffers(); + bool swapBuffers(EGLSurface surface); void waitNative(); void waitClient(); @@ -131,11 +135,14 @@ private: EGLContext ctx; EGLSurface surf; EGLConfig cfg; + EGLSurface currentSurface; bool share; bool current; - void *reserved; // For extension data in future versions. static EGLDisplay getDisplay(QPaintDevice *device); + + static QEglContext *currentContext(QEgl::API api); + static void setCurrentContext(QEgl::API api, QEglContext *context); }; QT_END_NAMESPACE diff --git a/src/openvg/qpixmapdata_vg.cpp b/src/openvg/qpixmapdata_vg.cpp index 38a89e6..2003f3b 100644 --- a/src/openvg/qpixmapdata_vg.cpp +++ b/src/openvg/qpixmapdata_vg.cpp @@ -82,12 +82,11 @@ QVGPixmapData::~QVGPixmapData() // We don't currently have a widget surface active, but we // need a surface to make the context current. So use the // shared pbuffer surface instead. - qt_vg_make_current(context, qt_vg_shared_surface()); + context->makeCurrent(qt_vg_shared_surface()); vgDestroyImage(vgImage); if (vgImageOpacity != VG_INVALID_HANDLE) vgDestroyImage(vgImageOpacity); - qt_vg_done_current(context); - context->setSurface(EGL_NO_SURFACE); + context->lazyDoneCurrent(); } #else vgDestroyImage(vgImage); diff --git a/src/openvg/qvg_p.h b/src/openvg/qvg_p.h index 764e98f..04e2bab 100644 --- a/src/openvg/qvg_p.h +++ b/src/openvg/qvg_p.h @@ -81,15 +81,6 @@ Q_OPENVG_EXPORT void qt_vg_destroy_context(QEglContext *context); // destroy VGImage objects when there is no other surface available. Q_OPENVG_EXPORT EGLSurface qt_vg_shared_surface(void); -// Make a context current with a specific surface. -Q_OPENVG_EXPORT void qt_vg_make_current(QEglContext *context, EGLSurface surface); - -// Make a context uncurrent. -Q_OPENVG_EXPORT void qt_vg_done_current(QEglContext *context, bool force = false); - -// Destroy a surface that was previously associated with a context. -Q_OPENVG_EXPORT void qt_vg_destroy_surface(QEglContext *context, EGLSurface surface); - // Convert the configuration format in a context to a VG or QImage format. Q_OPENVG_EXPORT VGImageFormat qt_vg_config_to_vg_format(QEglContext *context); Q_OPENVG_EXPORT QImage::Format qt_vg_config_to_image_format(QEglContext *context); diff --git a/src/openvg/qwindowsurface_vgegl.cpp b/src/openvg/qwindowsurface_vgegl.cpp index 06759d4..cb3f7fc 100644 --- a/src/openvg/qwindowsurface_vgegl.cpp +++ b/src/openvg/qwindowsurface_vgegl.cpp @@ -125,7 +125,6 @@ public: int refCount; QVGPaintEngine *engine; EGLSurface surface; - EGLSurface lastSurface; }; QVGSharedContext::QVGSharedContext() @@ -133,7 +132,6 @@ QVGSharedContext::QVGSharedContext() , refCount(0) , engine(0) , surface(EGL_NO_SURFACE) - , lastSurface(EGL_NO_SURFACE) { } @@ -144,12 +142,12 @@ QVGSharedContext::~QVGSharedContext() ++refCount; if (context) - qt_vg_make_current(context, qt_vg_shared_surface()); + context->makeCurrent(qt_vg_shared_surface()); delete engine; if (context) - qt_vg_done_current(context, true); - if (surface != EGL_NO_SURFACE) - qt_vg_destroy_surface(context, surface); + context->doneCurrent(); + if (context && surface != EGL_NO_SURFACE) + context->destroySurface(surface); delete context; } @@ -265,12 +263,12 @@ void qt_vg_destroy_context(QEglContext *context) // This is not the shared context. Shouldn't happen! delete context; } else if (--(shared->refCount) <= 0) { - qt_vg_make_current(shared->context, qt_vg_shared_surface()); + shared->context->makeCurrent(qt_vg_shared_surface()); delete shared->engine; shared->engine = 0; - qt_vg_done_current(shared->context, true); + shared->context->doneCurrent(); if (shared->surface != EGL_NO_SURFACE) { - qt_vg_destroy_surface(shared->context, shared->surface); + eglDestroySurface(shared->context->display(), shared->surface); shared->surface = EGL_NO_SURFACE; } delete shared->context; @@ -303,50 +301,6 @@ EGLSurface qt_vg_shared_surface(void) return shared->surface; } -void qt_vg_make_current(QEglContext *context, EGLSurface surface) -{ - // Bail out if the context and surface are already current. - if (context->isCurrent() && context->surface() == surface) - return; - - // Are we setting the surface to the same as the last elided doneCurrent()? - QVGSharedContext *shared = sharedContext(); - if (context->isCurrent() && shared->lastSurface == surface) { - shared->lastSurface = EGL_NO_SURFACE; - context->setSurface(surface); - return; - } - - // Switch to the new context and surface. - shared->lastSurface = EGL_NO_SURFACE; - context->setSurface(surface); - context->makeCurrent(); -} - -void qt_vg_done_current(QEglContext *context, bool force) -{ - QVGSharedContext *shared = sharedContext(); - if (force) { - context->doneCurrent(); - shared->lastSurface = EGL_NO_SURFACE; - } else { - // Keep the context current for now just in case we immediately - // reuse the same surface for the next frame. - shared->lastSurface = context->surface(); - } -} - -void qt_vg_destroy_surface(QEglContext *context, EGLSurface surface) -{ - QVGSharedContext *shared = sharedContext(); - if (shared->lastSurface == surface) { - shared->lastSurface = EGL_NO_SURFACE; - context->doneCurrent(); - } - context->setSurface(surface); - context->destroySurface(); -} - #else QEglContext *qt_vg_create_context(QPaintDevice *device) @@ -364,24 +318,6 @@ EGLSurface qt_vg_shared_surface(void) return EGL_NO_SURFACE; } -void qt_vg_make_current(QEglContext *context, EGLSurface surface) -{ - context->setSurface(surface); - context->makeCurrent(); -} - -void qt_vg_done_current(QEglContext *context, bool force) -{ - Q_UNUSED(force); - context->doneCurrent(); -} - -void qt_vg_destroy_surface(QEglContext *context, EGLSurface surface) -{ - context->setSurface(surface); - context->destroySurface(); -} - #endif QVGEGLWindowSurfacePrivate::QVGEGLWindowSurfacePrivate(QWindowSurface *win) @@ -470,13 +406,13 @@ QVGEGLWindowSurfaceVGImage::~QVGEGLWindowSurfaceVGImage() // We need a current context to be able to destroy the image. // We use the shared surface because the native window handle // associated with "windowSurface" may have been destroyed already. - qt_vg_make_current(context, qt_vg_shared_surface()); - qt_vg_destroy_surface(context, backBufferSurface); + context->makeCurrent(qt_vg_shared_surface()); + context->destroySurface(backBufferSurface); vgDestroyImage(backBuffer); - qt_vg_done_current(context, true); + context->doneCurrent(); } if (windowSurface != EGL_NO_SURFACE) - qt_vg_destroy_surface(context, windowSurface); + context->destroySurface(windowSurface); qt_vg_destroy_context(context); } } @@ -489,7 +425,7 @@ QEglContext *QVGEGLWindowSurfaceVGImage::ensureContext(QWidget *widget) // the back buffer. Keep the same context and paint engine. size = newSize; if (isPaintingActive) - qt_vg_done_current(context, true); + context->doneCurrent(); isPaintingActive = false; recreateBackBuffer = true; } @@ -512,7 +448,7 @@ void QVGEGLWindowSurfaceVGImage::beginPaint(QWidget *widget) // Create a VGImage object to act as the back buffer // for this window. We have to create the VGImage with a // current context, so activate the main surface for the window. - qt_vg_make_current(context, mainSurface()); + context->makeCurrent(mainSurface()); recreateBackBuffer = false; if (backBufferSurface != EGL_NO_SURFACE) { eglDestroySurface(context->display(), backBufferSurface); @@ -538,9 +474,9 @@ void QVGEGLWindowSurfaceVGImage::beginPaint(QWidget *widget) } } if (backBufferSurface != EGL_NO_SURFACE) - qt_vg_make_current(context, backBufferSurface); + context->makeCurrent(backBufferSurface); else - qt_vg_make_current(context, mainSurface()); + context->makeCurrent(mainSurface()); isPaintingActive = true; } } @@ -555,9 +491,8 @@ void QVGEGLWindowSurfaceVGImage::endPaint if (backBufferSurface != EGL_NO_SURFACE) { if (isPaintingActive) vgFlush(); - qt_vg_done_current(context); + context->lazyDoneCurrent(); } - context->setSurface(EGL_NO_SURFACE); isPaintingActive = false; } } @@ -592,7 +527,7 @@ void QVGEGLWindowSurfaceQImage::endPaint if (backBufferSurface != EGL_NO_SURFACE) { if (isPaintingActive) vgFlush(); - qt_vg_make_current(context, mainSurface()); + context->makeCurrent(mainSurface()); QRegion rgn = region.intersected (QRect(0, 0, image->width(), image->height())); if (rgn.numRects() == 1) { @@ -602,9 +537,8 @@ void QVGEGLWindowSurfaceQImage::endPaint for (int index = 0; index < rects.size(); ++index) copySubImage(image, backBuffer, rects[index]); } - qt_vg_done_current(context); + context->lazyDoneCurrent(); } - context->setSurface(EGL_NO_SURFACE); isPaintingActive = false; } } @@ -625,7 +559,7 @@ QVGEGLWindowSurfaceDirect::~QVGEGLWindowSurfaceDirect() destroyPaintEngine(); if (context) { if (windowSurface != EGL_NO_SURFACE) - qt_vg_destroy_surface(context, windowSurface); + context->destroySurface(windowSurface); qt_vg_destroy_context(context); } } @@ -642,9 +576,8 @@ QEglContext *QVGEGLWindowSurfaceDirect::ensureContext(QWidget *widget) // We can keep the same context and paint engine. size = newSize; if (isPaintingActive) - qt_vg_done_current(context, true); - context->setSurface(windowSurface); - context->destroySurface(); + context->doneCurrent(); + context->destroySurface(windowSurface); #if defined(EGL_VG_ALPHA_FORMAT_PRE_BIT) if (isPremultipliedContext(context)) { surfaceProps.setValue @@ -667,8 +600,7 @@ QEglContext *QVGEGLWindowSurfaceDirect::ensureContext(QWidget *widget) // in the new context. qt_vg_destroy_paint_engine(engine); engine = 0; - context->setSurface(windowSurface); - context->destroySurface(); + context->destroySurface(windowSurface); qt_vg_destroy_context(context); context = 0; windowSurface = EGL_NO_SURFACE; @@ -730,7 +662,7 @@ void QVGEGLWindowSurfaceDirect::beginPaint(QWidget *widget) { QEglContext *context = ensureContext(widget); if (context) { - qt_vg_make_current(context, windowSurface); + context->makeCurrent(windowSurface); isPaintingActive = true; } } @@ -744,14 +676,13 @@ void QVGEGLWindowSurfaceDirect::endPaint if (context) { if (needToSwap) { if (!isPaintingActive) - qt_vg_make_current(context, windowSurface); - context->swapBuffers(); - qt_vg_done_current(context); + context->makeCurrent(windowSurface); + context->swapBuffers(windowSurface); + context->lazyDoneCurrent(); } else if (isPaintingActive) { vgFlush(); - qt_vg_done_current(context); + context->lazyDoneCurrent(); } - context->setSurface(EGL_NO_SURFACE); isPaintingActive = false; } } -- cgit v0.12