diff options
author | Julian Thijssen <Nimthora@gmail.com> | 2016-07-27 15:45:31 +0200 |
---|---|---|
committer | Laszlo Agocs <laszlo.agocs@qt.io> | 2017-01-23 11:34:05 +0000 |
commit | 7dc88b68904c7f1b7e012bd65ccdcbf48cc6e2e0 (patch) | |
tree | 6de47b86b99200b999eddd8de0f07c2e86d8e027 /src/gui/opengl/qopenglpaintengine.cpp | |
parent | da4b6c4774c0adf8dee5ee4a9a8d9d24967ede3c (diff) |
Add support for OpenGL 3.2+ core profile contexts in QPainter
This change allows painting via QPainter onto a QOpenGLWindow, QOpenGLWidget
or QOpenGLFramebufferObject when an core profile context is in use. This is
important on macOS in particular, where compatibility profiles are not
available, and so the only way to use modern OpenGL is via a core profile
context.
Added core profile compatible shaders with moder GLSL keywords.
The paint engine binds a VAO and two VBOs from now on, whenever VAOs are
supported. Note that this changes behavior also for OpenGL 2.x context that
have VAO support via extensions.
The Lancelot test suite gains support for core profile contexts. This can
be triggered via -coreglbuffer in place of -glbuffer when manually inspecting
via 'lance', while tst_lancelot will automatically run core context-based tests
whenever supported.
Task-number: QTBUG-33535
Change-Id: I6323a7ea2aaa9e111651ebbffd3e40259c8e7a9c
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'src/gui/opengl/qopenglpaintengine.cpp')
-rw-r--r-- | src/gui/opengl/qopenglpaintengine.cpp | 130 |
1 files changed, 95 insertions, 35 deletions
diff --git a/src/gui/opengl/qopenglpaintengine.cpp b/src/gui/opengl/qopenglpaintengine.cpp index 8db806f1d5..f6bae32d54 100644 --- a/src/gui/opengl/qopenglpaintengine.cpp +++ b/src/gui/opengl/qopenglpaintengine.cpp @@ -99,6 +99,12 @@ QOpenGL2PaintEngineExPrivate::~QOpenGL2PaintEngineExPrivate() { delete shaderManager; + vertexBuffer.destroy(); + texCoordBuffer.destroy(); + opacityBuffer.destroy(); + indexBuffer.destroy(); + vao.destroy(); + if (elementIndicesVBOId != 0) { funcs.glDeleteBuffers(1, &elementIndicesVBOId); elementIndicesVBOId = 0; @@ -578,6 +584,12 @@ void QOpenGL2PaintEngineExPrivate::drawTexture(const QOpenGLRect& dest, const QO setCoords(staticVertexCoordinateArray, dest); setCoords(staticTextureCoordinateArray, srcTextureRect); + setVertexAttribArrayEnabled(QT_VERTEX_COORDS_ATTR, true); + setVertexAttribArrayEnabled(QT_TEXTURE_COORDS_ATTR, true); + + uploadData(QT_VERTEX_COORDS_ATTR, staticVertexCoordinateArray, 8); + uploadData(QT_TEXTURE_COORDS_ATTR, staticTextureCoordinateArray, 8); + funcs.glDrawArrays(GL_TRIANGLE_FAN, 0, 4); } @@ -664,6 +676,11 @@ void QOpenGL2PaintEngineExPrivate::resetGLState() float color[] = { 1.0f, 1.0f, 1.0f, 1.0f }; funcs.glVertexAttrib4fv(3, color); } + if (vao.isCreated()) { + vao.release(); + funcs.glBindBuffer(GL_ARRAY_BUFFER, 0); + funcs.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + } } void QOpenGL2PaintEngineEx::endNativePainting() @@ -696,16 +713,16 @@ void QOpenGL2PaintEngineExPrivate::transferMode(EngineMode newMode) } if (newMode == ImageDrawingMode) { - setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, staticVertexCoordinateArray); - setVertexAttributePointer(QT_TEXTURE_COORDS_ATTR, staticTextureCoordinateArray); + uploadData(QT_VERTEX_COORDS_ATTR, staticVertexCoordinateArray, 8); + uploadData(QT_TEXTURE_COORDS_ATTR, staticTextureCoordinateArray, 8); } if (newMode == ImageArrayDrawingMode || newMode == ImageOpacityArrayDrawingMode) { - setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, (GLfloat*)vertexCoordinateArray.data()); - setVertexAttributePointer(QT_TEXTURE_COORDS_ATTR, (GLfloat*)textureCoordinateArray.data()); + uploadData(QT_VERTEX_COORDS_ATTR, (GLfloat*)vertexCoordinateArray.data(), vertexCoordinateArray.vertexCount() * 2); + uploadData(QT_TEXTURE_COORDS_ATTR, (GLfloat*)textureCoordinateArray.data(), textureCoordinateArray.vertexCount() * 2); if (newMode == ImageOpacityArrayDrawingMode) - setVertexAttributePointer(QT_OPACITY_ATTR, (GLfloat*)opacityArray.data()); + uploadData(QT_OPACITY_ATTR, (GLfloat*)opacityArray.data(), opacityArray.size()); } // This needs to change when we implement high-quality anti-aliasing... @@ -826,9 +843,10 @@ void QOpenGL2PaintEngineExPrivate::fill(const QVectorPath& path) prepareForDraw(currentBrush.isOpaque()); #ifdef QT_OPENGL_CACHE_AS_VBOS funcs.glBindBuffer(GL_ARRAY_BUFFER, cache->vbo); + uploadData(QT_VERTEX_COORD_ATTR, 0, cache->vertexCount); setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, 0); #else - setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, cache->vertices); + uploadData(QT_VERTEX_COORDS_ATTR, cache->vertices, cache->vertexCount * 2); #endif funcs.glDrawArrays(cache->primitiveType, 0, cache->vertexCount); @@ -922,6 +940,7 @@ void QOpenGL2PaintEngineExPrivate::fill(const QVectorPath& path) #ifdef QT_OPENGL_CACHE_AS_VBOS funcs.glBindBuffer(GL_ARRAY_BUFFER, cache->vbo); funcs.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cache->ibo); + uploadData(QT_VERTEX_COORDS_ATTR, 0, cache->vertexCount); setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, 0); if (cache->indexType == QVertexIndexVector::UnsignedInt) funcs.glDrawElements(cache->primitiveType, cache->indexCount, GL_UNSIGNED_INT, 0); @@ -930,11 +949,10 @@ void QOpenGL2PaintEngineExPrivate::fill(const QVectorPath& path) funcs.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); funcs.glBindBuffer(GL_ARRAY_BUFFER, 0); #else - setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, cache->vertices); - if (cache->indexType == QVertexIndexVector::UnsignedInt) - funcs.glDrawElements(cache->primitiveType, cache->indexCount, GL_UNSIGNED_INT, (qint32 *)cache->indices); - else - funcs.glDrawElements(cache->primitiveType, cache->indexCount, GL_UNSIGNED_SHORT, (qint16 *)cache->indices); + uploadData(QT_VERTEX_COORDS_ATTR, cache->vertices, cache->vertexCount * 2); + const GLenum indexValueType = cache->indexType == QVertexIndexVector::UnsignedInt ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT; + const bool useIndexVbo = uploadIndexData(cache->indices, indexValueType, cache->indexCount); + funcs.glDrawElements(cache->primitiveType, cache->indexCount, indexValueType, useIndexVbo ? nullptr : cache->indices); #endif } else { @@ -959,11 +977,10 @@ void QOpenGL2PaintEngineExPrivate::fill(const QVectorPath& path) vertices[i] = float(inverseScale * polys.vertices.at(i)); prepareForDraw(currentBrush.isOpaque()); - setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, vertices.constData()); - if (funcs.hasOpenGLExtension(QOpenGLExtensions::ElementIndexUint)) - funcs.glDrawElements(GL_TRIANGLES, polys.indices.size(), GL_UNSIGNED_INT, polys.indices.data()); - else - funcs.glDrawElements(GL_TRIANGLES, polys.indices.size(), GL_UNSIGNED_SHORT, polys.indices.data()); + uploadData(QT_VERTEX_COORDS_ATTR, vertices.constData(), vertices.size()); + const GLenum indexValueType = funcs.hasOpenGLExtension(QOpenGLExtensions::ElementIndexUint) ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT; + const bool useIndexVbo = uploadIndexData(polys.indices.data(), indexValueType, polys.indices.size()); + funcs.glDrawElements(GL_TRIANGLES, polys.indices.size(), indexValueType, useIndexVbo ? nullptr : polys.indices.data()); } else { // We can't handle big, concave painter paths with OpenGL without stencil buffer. qWarning("Painter path exceeds +/-32767 pixels."); @@ -1085,7 +1102,8 @@ void QOpenGL2PaintEngineExPrivate::fillStencilWithVertexArray(const float *data, } else { funcs.glStencilFunc(GL_ALWAYS, GL_STENCIL_HIGH_BIT, 0xff); } - setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, data); + + uploadData(QT_VERTEX_COORDS_ATTR, data, count * 2); funcs.glDrawArrays(GL_TRIANGLE_STRIP, 0, count); #endif } @@ -1215,7 +1233,8 @@ bool QOpenGL2PaintEngineExPrivate::prepareForDraw(bool srcPixelsAreOpaque) void QOpenGL2PaintEngineExPrivate::composite(const QOpenGLRect& boundingRect) { setCoords(staticVertexCoordinateArray, boundingRect); - setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, staticVertexCoordinateArray); + + uploadData(QT_VERTEX_COORDS_ATTR, staticVertexCoordinateArray, 8); funcs.glDrawArrays(GL_TRIANGLE_FAN, 0, 4); } @@ -1224,16 +1243,12 @@ void QOpenGL2PaintEngineExPrivate::drawVertexArrays(const float *data, int *stop GLenum primitive) { // Now setup the pointer to the vertex array: - setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, data); + uploadData(QT_VERTEX_COORDS_ATTR, data, stops[stopCount-1] * 2); int previousStop = 0; for (int i=0; i<stopCount; ++i) { int stop = stops[i]; -/* - qDebug("Drawing triangle fan for vertecies %d -> %d:", previousStop, stop-1); - for (int i=previousStop; i<stop; ++i) - qDebug(" %02d: [%.2f, %.2f]", i, vertexArray.data()[i].x, vertexArray.data()[i].y); -*/ + funcs.glDrawArrays(primitive, previousStop, stop - previousStop); previousStop = stop; } @@ -1325,14 +1340,9 @@ void QOpenGL2PaintEngineExPrivate::stroke(const QVectorPath &path, const QPen &p if (opaque) { prepareForDraw(opaque); - setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, stroker.vertices()); - funcs.glDrawArrays(GL_TRIANGLE_STRIP, 0, stroker.vertexCount() / 2); - -// QBrush b(Qt::green); -// d->setBrush(&b); -// d->prepareForDraw(true); -// glDrawArrays(GL_LINE_STRIP, 0, d->stroker.vertexCount() / 2); + uploadData(QT_VERTEX_COORDS_ATTR, stroker.vertices(), stroker.vertexCount()); + funcs.glDrawArrays(GL_TRIANGLE_STRIP, 0, stroker.vertexCount() / 2); } else { qreal width = qpen_widthf(pen) / 2; if (width == 0) @@ -1841,8 +1851,8 @@ void QOpenGL2PaintEngineExPrivate::drawCachedGlyphs(QFontEngine::GlyphFormat gly } if (glyphFormat != QFontEngine::Format_ARGB || recreateVertexArrays) { - setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, (GLfloat*)vertexCoordinates->data()); - setVertexAttributePointer(QT_TEXTURE_COORDS_ATTR, (GLfloat*)textureCoordinates->data()); + uploadData(QT_VERTEX_COORDS_ATTR, (GLfloat*)vertexCoordinates->data(), vertexCoordinates->vertexCount() * 2); + uploadData(QT_TEXTURE_COORDS_ATTR, (GLfloat*)textureCoordinates->data(), textureCoordinates->vertexCount() * 2); } if (!snapToPixelGrid) { @@ -1906,7 +1916,8 @@ void QOpenGL2PaintEngineExPrivate::drawCachedGlyphs(QFontEngine::GlyphFormat gly #if defined(QT_OPENGL_DRAWCACHEDGLYPHS_INDEX_ARRAY_VBO) funcs.glDrawElements(GL_TRIANGLE_STRIP, 6 * numGlyphs, GL_UNSIGNED_SHORT, 0); #else - funcs.glDrawElements(GL_TRIANGLE_STRIP, 6 * numGlyphs, GL_UNSIGNED_SHORT, elementIndices.data()); + const bool useIndexVbo = uploadIndexData(elementIndices.data(), GL_UNSIGNED_SHORT, 6 * numGlyphs); + funcs.glDrawElements(GL_TRIANGLE_STRIP, 6 * numGlyphs, GL_UNSIGNED_SHORT, useIndexVbo ? nullptr : elementIndices.data()); #endif shaderManager->setMaskType(QOpenGLEngineShaderManager::SubPixelMaskPass2); @@ -1957,7 +1968,8 @@ void QOpenGL2PaintEngineExPrivate::drawCachedGlyphs(QFontEngine::GlyphFormat gly funcs.glDrawElements(GL_TRIANGLE_STRIP, 6 * numGlyphs, GL_UNSIGNED_SHORT, 0); funcs.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); #else - funcs.glDrawElements(GL_TRIANGLE_STRIP, 6 * numGlyphs, GL_UNSIGNED_SHORT, elementIndices.data()); + const bool useIndexVbo = uploadIndexData(elementIndices.data(), GL_UNSIGNED_SHORT, 6 * numGlyphs); + funcs.glDrawElements(GL_TRIANGLE_STRIP, 6 * numGlyphs, GL_UNSIGNED_SHORT, useIndexVbo ? nullptr : elementIndices.data()); #endif } @@ -2076,6 +2088,15 @@ bool QOpenGL2PaintEngineEx::begin(QPaintDevice *pdev) return false; } + if (d->ctx != QOpenGLContext::currentContext() + || (d->ctx && QOpenGLContext::currentContext() && d->ctx->format() != QOpenGLContext::currentContext()->format())) { + d->vertexBuffer.destroy(); + d->texCoordBuffer.destroy(); + d->opacityBuffer.destroy(); + d->indexBuffer.destroy(); + d->vao.destroy(); + } + d->ctx = QOpenGLContext::currentContext(); d->ctx->d_func()->active_engine = this; @@ -2083,6 +2104,42 @@ bool QOpenGL2PaintEngineEx::begin(QPaintDevice *pdev) d->funcs.initializeOpenGLFunctions(); + // Generate a new Vertex Array Object if we don't have one already. We can + // only hit the VAO-based path when using a core profile context. This is + // because while non-core contexts can support VAOs via extensions, legacy + // components like the QtOpenGL module do not know about VAOs. There are + // still tests for QGL-QOpenGL paint engine interoperability, so keep the + // status quo for now, and avoid introducing a VAO in non-core contexts. + const bool needsVAO = d->ctx->format().profile() == QSurfaceFormat::CoreProfile + && d->ctx->format().version() >= qMakePair(3, 2); + if (needsVAO && !d->vao.isCreated()) { + bool created = d->vao.create(); + + // If we managed to create it then we have a profile that supports VAOs + if (created) { + d->vao.bind(); + + // Generate a new Vertex Buffer Object if we don't have one already + if (!d->vertexBuffer.isCreated()) { + d->vertexBuffer.create(); + // Set its usage to StreamDraw, we will use this buffer only a few times before refilling it + d->vertexBuffer.setUsagePattern(QOpenGLBuffer::StreamDraw); + } + if (!d->texCoordBuffer.isCreated()) { + d->texCoordBuffer.create(); + d->texCoordBuffer.setUsagePattern(QOpenGLBuffer::StreamDraw); + } + if (!d->opacityBuffer.isCreated()) { + d->opacityBuffer.create(); + d->opacityBuffer.setUsagePattern(QOpenGLBuffer::StreamDraw); + } + if (!d->indexBuffer.isCreated()) { + d->indexBuffer.create(); + d->indexBuffer.setUsagePattern(QOpenGLBuffer::StreamDraw); + } + } + } + for (int i = 0; i < QT_GL_VERTEX_ARRAY_TRACKED_COUNT; ++i) d->vertexAttributeArraysEnabledState[i] = false; @@ -2164,6 +2221,9 @@ void QOpenGL2PaintEngineEx::ensureActive() Q_D(QOpenGL2PaintEngineEx); QOpenGLContext *ctx = d->ctx; + if (d->vao.isCreated()) + d->vao.bind(); + if (isActive() && ctx->d_func()->active_engine != this) { ctx->d_func()->active_engine = this; d->needsSync = true; |