src/opengl/qgl_egl.cpp
changeset 30 5dc02b23752f
parent 18 2f34d5167611
child 33 3e2da88830cd
--- a/src/opengl/qgl_egl.cpp	Wed Jun 23 19:07:03 2010 +0300
+++ b/src/opengl/qgl_egl.cpp	Tue Jul 06 15:10:48 2010 +0300
@@ -40,93 +40,114 @@
 ****************************************************************************/
 
 #include <QtOpenGL/qgl.h>
+#include <QtOpenGL/qglpixelbuffer.h>
 #include "qgl_p.h"
 #include "qgl_egl_p.h"
+#include "qglpixelbuffer_p.h"
+
+#ifdef Q_WS_X11
+#include <QtGui/private/qpixmap_x11_p.h>
+#endif
 
 QT_BEGIN_NAMESPACE
 
-// Set device configuration attributes from a QGLFormat instance.
-void qt_egl_set_format(QEglProperties& props, int deviceType, const QGLFormat& f)
+void qt_eglproperties_set_glformat(QEglProperties& eglProperties, const QGLFormat& glFormat)
 {
-    if (deviceType == QInternal::Pixmap || deviceType == QInternal::Image)
-        props.setValue(EGL_SURFACE_TYPE, EGL_PIXMAP_BIT);
-    else if (deviceType == QInternal::Pbuffer)
-        props.setValue(EGL_SURFACE_TYPE, EGL_PBUFFER_BIT);
-    else
-        props.setValue(EGL_SURFACE_TYPE, EGL_WINDOW_BIT);
+    int redSize     = glFormat.redBufferSize();
+    int greenSize   = glFormat.greenBufferSize();
+    int blueSize    = glFormat.blueBufferSize();
+    int alphaSize   = glFormat.alphaBufferSize();
+    int depthSize   = glFormat.depthBufferSize();
+    int stencilSize = glFormat.stencilBufferSize();
+    int sampleCount = glFormat.samples();
+
+    // QGLFormat uses a magic value of -1 to indicate "don't care", even when a buffer of that
+    // type has been requested. So we must check QGLFormat's booleans too if size is -1:
+    if (glFormat.alpha() && alphaSize <= 0)
+        alphaSize = 1;
+    if (glFormat.depth() && depthSize <= 0)
+        depthSize = 1;
+    if (glFormat.stencil() && stencilSize <= 0)
+        stencilSize = 1;
+    if (glFormat.sampleBuffers() && sampleCount <= 0)
+        sampleCount = 1;
 
-    // Set the pixel format to that contained in the QGLFormat
-    // if the system hasn't already chosen a fixed format to
-    // match the pixmap, widget, etc.
-    if (props.value(EGL_RED_SIZE) == 0 || f.redBufferSize() != -1)
-        props.setValue(EGL_RED_SIZE, f.redBufferSize() == -1 ? 1 : f.redBufferSize());
-    if (props.value(EGL_GREEN_SIZE) == 0 || f.greenBufferSize() != -1)
-        props.setValue(EGL_GREEN_SIZE, f.greenBufferSize() == -1 ? 1 : f.greenBufferSize());
-    if (props.value(EGL_BLUE_SIZE) == 0 || f.blueBufferSize() != -1)
-        props.setValue(EGL_BLUE_SIZE, f.blueBufferSize() == -1 ? 1 : f.blueBufferSize());
-    if (f.alpha()) {
-        if (props.value(EGL_ALPHA_SIZE) == 0 || f.alphaBufferSize() != -1)
-            props.setValue(EGL_ALPHA_SIZE, f.alphaBufferSize() == -1 ? 1 : f.alphaBufferSize());
-    }
+    // We want to make sure 16-bit configs are chosen over 32-bit configs as they will provide
+    // the best performance. The EGL config selection algorithm is a bit stange in this regard:
+    // The selection criteria for EGL_BUFFER_SIZE is "AtLeast", so we can't use it to discard
+    // 32-bit configs completely from the selection. So it then comes to the sorting algorithm.
+    // The red/green/blue sizes have a sort priority of 3, so they are sorted by first. The sort
+    // order is special and described as "by larger _total_ number of color bits.". So EGL will
+    // put 32-bit configs in the list before the 16-bit configs. However, the spec also goes on
+    // to say "If the requested number of bits in attrib_list for a particular component is 0,
+    // then the number of bits for that component is not considered". This part of the spec also
+    // seems to imply that setting the red/green/blue bits to zero means none of the components
+    // are considered and EGL disregards the entire sorting rule. It then looks to the next
+    // highest priority rule, which is EGL_BUFFER_SIZE. Despite the selection criteria being
+    // "AtLeast" for EGL_BUFFER_SIZE, it's sort order is "smaller" meaning 16-bit configs are
+    // put in the list before 32-bit configs. So, to make sure 16-bit is preffered over 32-bit,
+    // we must set the red/green/blue sizes to zero. This has an unfortunate consequence that
+    // if the application sets the red/green/blue size to 5/6/5 on the QGLFormat, they will
+    // probably get a 32-bit config, even when there's an RGB565 config avaliable. Oh well.
 
-    if (f.depth())
-        props.setValue(EGL_DEPTH_SIZE, f.depthBufferSize() == -1 ? 1 : f.depthBufferSize());
-    if (f.stencil())
-        props.setValue(EGL_STENCIL_SIZE, f.stencilBufferSize() == -1 ? 1 : f.stencilBufferSize());
-    if (f.sampleBuffers()) {
-        props.setValue(EGL_SAMPLE_BUFFERS, 1);
-        props.setValue(EGL_SAMPLES, f.samples() == -1 ? 1 : f.samples());
-    } else {
-        props.setValue(EGL_SAMPLE_BUFFERS, 0);
-    }
-    if (deviceType == QInternal::Widget)
-        props.setValue(EGL_LEVEL, f.plane());
+    // Now normalize the values so -1 becomes 0
+    redSize   = redSize   > 0 ? redSize   : 0;
+    greenSize = greenSize > 0 ? greenSize : 0;
+    blueSize  = blueSize  > 0 ? blueSize  : 0;
+    alphaSize = alphaSize > 0 ? alphaSize : 0;
+    depthSize = depthSize > 0 ? depthSize : 0;
+    stencilSize = stencilSize > 0 ? stencilSize : 0;
+    sampleCount = sampleCount > 0 ? sampleCount : 0;
+
+    eglProperties.setValue(EGL_RED_SIZE,   redSize);
+    eglProperties.setValue(EGL_GREEN_SIZE, greenSize);
+    eglProperties.setValue(EGL_BLUE_SIZE,  blueSize);
+    eglProperties.setValue(EGL_ALPHA_SIZE, alphaSize);
+    eglProperties.setValue(EGL_DEPTH_SIZE, depthSize);
+    eglProperties.setValue(EGL_STENCIL_SIZE, stencilSize);
+    eglProperties.setValue(EGL_SAMPLES, sampleCount);
+    eglProperties.setValue(EGL_SAMPLE_BUFFERS, sampleCount ? 1 : 0);
 }
 
 // Updates "format" with the parameters of the selected configuration.
-void qt_egl_update_format(const QEglContext& context, QGLFormat& format)
+void qt_glformat_from_eglconfig(QGLFormat& format, const EGLConfig config)
 {
-    EGLint value = 0;
-
-    if (context.configAttrib(EGL_RED_SIZE, &value))
-        format.setRedBufferSize(value);
-    if (context.configAttrib(EGL_GREEN_SIZE, &value))
-        format.setGreenBufferSize(value);
-    if (context.configAttrib(EGL_BLUE_SIZE, &value))
-        format.setBlueBufferSize(value);
-    if (context.configAttrib(EGL_ALPHA_SIZE, &value)) {
-        format.setAlpha(value != 0);
-        if (format.alpha())
-            format.setAlphaBufferSize(value);
-    }
+    EGLint redSize     = 0;
+    EGLint greenSize   = 0;
+    EGLint blueSize    = 0;
+    EGLint alphaSize   = 0;
+    EGLint depthSize   = 0;
+    EGLint stencilSize = 0;
+    EGLint sampleCount = 0;
+    EGLint level       = 0;
 
-    if (context.configAttrib(EGL_DEPTH_SIZE, &value)) {
-        format.setDepth(value != 0);
-        if (format.depth())
-            format.setDepthBufferSize(value);
-    }
-
-    if (context.configAttrib(EGL_LEVEL, &value))
-        format.setPlane(value);
+    EGLDisplay display = QEgl::display();
+    eglGetConfigAttrib(display, config, EGL_RED_SIZE,     &redSize);
+    eglGetConfigAttrib(display, config, EGL_GREEN_SIZE,   &greenSize);
+    eglGetConfigAttrib(display, config, EGL_BLUE_SIZE,    &blueSize);
+    eglGetConfigAttrib(display, config, EGL_ALPHA_SIZE,   &alphaSize);
+    eglGetConfigAttrib(display, config, EGL_DEPTH_SIZE,   &depthSize);
+    eglGetConfigAttrib(display, config, EGL_STENCIL_SIZE, &stencilSize);
+    eglGetConfigAttrib(display, config, EGL_SAMPLES,      &sampleCount);
+    eglGetConfigAttrib(display, config, EGL_LEVEL,        &level);
 
-    if (context.configAttrib(EGL_SAMPLE_BUFFERS, &value)) {
-        format.setSampleBuffers(value != 0);
-        if (format.sampleBuffers()) {
-            context.configAttrib(EGL_SAMPLES, &value);
-            format.setSamples(value);
-        }
-    }
-
-    if (context.configAttrib(EGL_STENCIL_SIZE, &value)) {
-        format.setStencil(value != 0);
-        if (format.stencil())
-            format.setStencilBufferSize(value);
-    }
+    format.setRedBufferSize(redSize);
+    format.setGreenBufferSize(greenSize);
+    format.setBlueBufferSize(blueSize);
+    format.setAlphaBufferSize(alphaSize);
+    format.setDepthBufferSize(depthSize);
+    format.setStencilBufferSize(stencilSize);
+    format.setSamples(sampleCount);
+    format.setPlane(level + 1);      // EGL calls level 0 "normal" whereas Qt calls 1 "normal"
+    format.setDirectRendering(true); // All EGL contexts are direct-rendered
+    format.setRgba(true);            // EGL doesn't support colour index rendering
+    format.setStereo(false);         // EGL doesn't support stereo buffers
+    format.setAccumBufferSize(0);    // EGL doesn't support accululation buffers
 
     // Clear the EGL error state because some of the above may
     // have errored out because the attribute is not applicable
     // to the surface type.  Such errors don't matter.
-    context.clearError();
+    eglGetError();
 }
 
 bool QGLFormat::hasOpenGL()
@@ -141,10 +162,11 @@
         return;
     d->cleanup();
     doneCurrent();
-    if (d->eglContext) {
+    if (d->eglContext && d->ownsEglContext) {
         d->destroyEglSurfaceForDevice();
         delete d->eglContext;
     }
+    d->ownsEglContext = false;
     d->eglContext = 0;
     d->eglSurface = EGL_NO_SURFACE;
     d->crWin = false;
@@ -158,13 +180,31 @@
 void QGLContext::makeCurrent()
 {
     Q_D(QGLContext);
-    if (!d->valid || !d->eglContext || d->eglSurface == EGL_NO_SURFACE) {
+    if (!d->valid || !d->eglContext || d->eglSurfaceForDevice() == EGL_NO_SURFACE) {
         qWarning("QGLContext::makeCurrent(): Cannot make invalid context current");
         return;
     }
 
-    if (d->eglContext->makeCurrent(d->eglSurface))
+    if (d->eglContext->makeCurrent(d->eglSurfaceForDevice())) {
         QGLContextPrivate::setCurrentContext(this);
+        if (!d->workaroundsCached) {
+            d->workaroundsCached = true;
+            const char *renderer = reinterpret_cast<const char *>(glGetString(GL_RENDERER));
+            if (strstr(renderer, "SGX") || strstr(renderer, "MBX")) {
+                // PowerVR MBX/SGX chips needs to clear all buffers when starting to render
+                // a new frame, otherwise there will be a performance penalty to pay for
+                // each frame.
+                d->workaround_needsFullClearOnEveryFrame = true;
+
+                // Older PowerVR SGX drivers (like the one in the N900) have a
+                // bug which prevents glCopyTexSubImage2D() to work with a POT
+                // or GL_ALPHA texture bound to an FBO. The only way to
+                // identify that driver is to check the EGL version number for it.
+                if (strstr(eglQueryString(d->eglContext->display(), EGL_VERSION), "1.3"))
+                    d->workaround_brokenFBOReadBack = true;
+            }
+        }
+    }
 }
 
 void QGLContext::doneCurrent()
@@ -183,7 +223,7 @@
     if (!d->valid || !d->eglContext)
         return;
 
-    d->eglContext->swapBuffers(d->eglSurface);
+    d->eglContext->swapBuffers(d->eglSurfaceForDevice());
 }
 
 void QGLContextPrivate::destroyEglSurfaceForDevice()
@@ -192,7 +232,7 @@
 #ifdef Q_WS_X11
         // Make sure we don't call eglDestroySurface on a surface which
         // was created for a different winId:
-        if (paintDevice->devType() == QInternal::Widget) {
+        if (paintDevice && paintDevice->devType() == QInternal::Widget) {
             QGLWidget* w = static_cast<QGLWidget*>(paintDevice);
 
             if (w->d_func()->eglSurfaceWindowId == w->winId())
@@ -206,6 +246,30 @@
     }
 }
 
+EGLSurface QGLContextPrivate::eglSurfaceForDevice() const
+{
+    // If a QPixmapData had to create the QGLContext, we don't have a paintDevice
+    if (!paintDevice)
+        return eglSurface;
+
+#ifdef Q_WS_X11
+    if (paintDevice->devType() == QInternal::Pixmap) {
+        QPixmapData *pmd = static_cast<QPixmap*>(paintDevice)->data_ptr().data();
+        if (pmd->classId() == QPixmapData::X11Class) {
+            QX11PixmapData* x11PixmapData = static_cast<QX11PixmapData*>(pmd);
+            return (EGLSurface)x11PixmapData->gl_surface;
+        }
+    }
+#endif
+
+    if (paintDevice->devType() == QInternal::Pbuffer) {
+        QGLPixelBuffer* pbuf = static_cast<QGLPixelBuffer*>(paintDevice);
+        return pbuf->d_func()->pbuf;
+    }
+
+    return eglSurface;
+}
+
 void QGLWidget::setMouseTracking(bool enable)
 {
     QWidget::setMouseTracking(enable);