summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJulian Thijssen <Nimthora@gmail.com>2016-07-27 15:45:31 +0200
committerLaszlo Agocs <laszlo.agocs@qt.io>2017-01-23 11:34:05 +0000
commit7dc88b68904c7f1b7e012bd65ccdcbf48cc6e2e0 (patch)
tree6de47b86b99200b999eddd8de0f07c2e86d8e027 /src
parentda4b6c4774c0adf8dee5ee4a9a8d9d24967ede3c (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')
-rw-r--r--src/gui/opengl/qopenglengineshadermanager.cpp102
-rw-r--r--src/gui/opengl/qopenglengineshadersource_p.h517
-rw-r--r--src/gui/opengl/qopenglpaintengine.cpp130
-rw-r--r--src/gui/opengl/qopenglpaintengine_p.h74
-rw-r--r--src/gui/opengl/qopengltextureglyphcache.cpp4
5 files changed, 726 insertions, 101 deletions
diff --git a/src/gui/opengl/qopenglengineshadermanager.cpp b/src/gui/opengl/qopenglengineshadermanager.cpp
index c7e457b364..dd9e8e9d1e 100644
--- a/src/gui/opengl/qopenglengineshadermanager.cpp
+++ b/src/gui/opengl/qopenglengineshadermanager.cpp
@@ -126,11 +126,65 @@ QOpenGLEngineSharedShaders::QOpenGLEngineSharedShaders(QOpenGLContext* context)
around without having to change the order of the glsl strings. It is hoped this will
make future hard-to-find runtime bugs more obvious and generally give more solid code.
*/
- static bool snippetsPopulated = false;
- if (!snippetsPopulated) {
- const char** code = qShaderSnippets; // shortcut
+ // Check if the user has requested an OpenGL 3.2 Core Profile or higher
+ // and if so use GLSL 1.50 core shaders instead of legacy ones.
+ const QSurfaceFormat &fmt = context->format();
+ const bool isCoreProfile = fmt.profile() == QSurfaceFormat::CoreProfile && fmt.version() >= qMakePair(3,2);
+
+ const char** code = qShaderSnippets; // shortcut
+
+ if (isCoreProfile) {
+ code[MainVertexShader] = qopenglslMainVertexShader_core;
+ code[MainWithTexCoordsVertexShader] = qopenglslMainWithTexCoordsVertexShader_core;
+ code[MainWithTexCoordsAndOpacityVertexShader] = qopenglslMainWithTexCoordsAndOpacityVertexShader_core;
+
+ code[UntransformedPositionVertexShader] = qopenglslUntransformedPositionVertexShader_core;
+ code[PositionOnlyVertexShader] = qopenglslPositionOnlyVertexShader_core;
+ code[ComplexGeometryPositionOnlyVertexShader] = qopenglslComplexGeometryPositionOnlyVertexShader_core;
+ code[PositionWithPatternBrushVertexShader] = qopenglslPositionWithPatternBrushVertexShader_core;
+ code[PositionWithLinearGradientBrushVertexShader] = qopenglslPositionWithLinearGradientBrushVertexShader_core;
+ code[PositionWithConicalGradientBrushVertexShader] = qopenglslPositionWithConicalGradientBrushVertexShader_core;
+ code[PositionWithRadialGradientBrushVertexShader] = qopenglslPositionWithRadialGradientBrushVertexShader_core;
+ code[PositionWithTextureBrushVertexShader] = qopenglslPositionWithTextureBrushVertexShader_core;
+ code[AffinePositionWithPatternBrushVertexShader] = qopenglslAffinePositionWithPatternBrushVertexShader_core;
+ code[AffinePositionWithLinearGradientBrushVertexShader] = qopenglslAffinePositionWithLinearGradientBrushVertexShader_core;
+ code[AffinePositionWithConicalGradientBrushVertexShader] = qopenglslAffinePositionWithConicalGradientBrushVertexShader_core;
+ code[AffinePositionWithRadialGradientBrushVertexShader] = qopenglslAffinePositionWithRadialGradientBrushVertexShader_core;
+ code[AffinePositionWithTextureBrushVertexShader] = qopenglslAffinePositionWithTextureBrushVertexShader_core;
+
+ code[MainFragmentShader_CMO] = qopenglslMainFragmentShader_CMO_core;
+ code[MainFragmentShader_CM] = qopenglslMainFragmentShader_CM_core;
+ code[MainFragmentShader_MO] = qopenglslMainFragmentShader_MO_core;
+ code[MainFragmentShader_M] = qopenglslMainFragmentShader_M_core;
+ code[MainFragmentShader_CO] = qopenglslMainFragmentShader_CO_core;
+ code[MainFragmentShader_C] = qopenglslMainFragmentShader_C_core;
+ code[MainFragmentShader_O] = qopenglslMainFragmentShader_O_core;
+ code[MainFragmentShader] = qopenglslMainFragmentShader_core;
+ code[MainFragmentShader_ImageArrays] = qopenglslMainFragmentShader_ImageArrays_core;
+
+ code[ImageSrcFragmentShader] = qopenglslImageSrcFragmentShader_core;
+ code[ImageSrcWithPatternFragmentShader] = qopenglslImageSrcWithPatternFragmentShader_core;
+ code[NonPremultipliedImageSrcFragmentShader] = qopenglslNonPremultipliedImageSrcFragmentShader_core;
+ code[GrayscaleImageSrcFragmentShader] = qopenglslGrayscaleImageSrcFragmentShader_core;
+ code[AlphaImageSrcFragmentShader] = qopenglslAlphaImageSrcFragmentShader_core;
+ code[CustomImageSrcFragmentShader] = qopenglslCustomSrcFragmentShader_core; // Calls "customShader", which must be appended
+ code[SolidBrushSrcFragmentShader] = qopenglslSolidBrushSrcFragmentShader_core;
+
+ code[TextureBrushSrcFragmentShader] = qopenglslTextureBrushSrcFragmentShader_desktop_core;
+ code[TextureBrushSrcWithPatternFragmentShader] = qopenglslTextureBrushSrcWithPatternFragmentShader_core;
+ code[PatternBrushSrcFragmentShader] = qopenglslPatternBrushSrcFragmentShader_core;
+ code[LinearGradientBrushSrcFragmentShader] = qopenglslLinearGradientBrushSrcFragmentShader_core;
+ code[RadialGradientBrushSrcFragmentShader] = qopenglslRadialGradientBrushSrcFragmentShader_core;
+ code[ConicalGradientBrushSrcFragmentShader] = qopenglslConicalGradientBrushSrcFragmentShader_core;
+ code[ShockingPinkSrcFragmentShader] = qopenglslShockingPinkSrcFragmentShader_core;
+ code[NoMaskFragmentShader] = "";
+ code[MaskFragmentShader] = qopenglslMaskFragmentShader_core;
+ code[RgbMaskFragmentShaderPass1] = qopenglslRgbMaskFragmentShaderPass1_core;
+ code[RgbMaskFragmentShaderPass2] = qopenglslRgbMaskFragmentShaderPass2_core;
+ code[RgbMaskWithGammaFragmentShader] = ""; //###
+ } else {
code[MainVertexShader] = qopenglslMainVertexShader;
code[MainWithTexCoordsVertexShader] = qopenglslMainWithTexCoordsVertexShader;
code[MainWithTexCoordsAndOpacityVertexShader] = qopenglslMainWithTexCoordsAndOpacityVertexShader;
@@ -182,31 +236,33 @@ QOpenGLEngineSharedShaders::QOpenGLEngineSharedShaders(QOpenGLContext* context)
code[RgbMaskFragmentShaderPass1] = qopenglslRgbMaskFragmentShaderPass1;
code[RgbMaskFragmentShaderPass2] = qopenglslRgbMaskFragmentShaderPass2;
code[RgbMaskWithGammaFragmentShader] = ""; //###
+ }
- code[NoCompositionModeFragmentShader] = "";
- code[MultiplyCompositionModeFragmentShader] = ""; //###
- code[ScreenCompositionModeFragmentShader] = ""; //###
- code[OverlayCompositionModeFragmentShader] = ""; //###
- code[DarkenCompositionModeFragmentShader] = ""; //###
- code[LightenCompositionModeFragmentShader] = ""; //###
- code[ColorDodgeCompositionModeFragmentShader] = ""; //###
- code[ColorBurnCompositionModeFragmentShader] = ""; //###
- code[HardLightCompositionModeFragmentShader] = ""; //###
- code[SoftLightCompositionModeFragmentShader] = ""; //###
- code[DifferenceCompositionModeFragmentShader] = ""; //###
- code[ExclusionCompositionModeFragmentShader] = ""; //###
+ // These shaders are not implemented yet and therefore are the same
+ // for all profiles. Implementations should make a version for both
+ // profiles and put the appropriate lines in the if-statement above.
+ code[NoCompositionModeFragmentShader] = "";
+ code[MultiplyCompositionModeFragmentShader] = ""; //###
+ code[ScreenCompositionModeFragmentShader] = ""; //###
+ code[OverlayCompositionModeFragmentShader] = ""; //###
+ code[DarkenCompositionModeFragmentShader] = ""; //###
+ code[LightenCompositionModeFragmentShader] = ""; //###
+ code[ColorDodgeCompositionModeFragmentShader] = ""; //###
+ code[ColorBurnCompositionModeFragmentShader] = ""; //###
+ code[HardLightCompositionModeFragmentShader] = ""; //###
+ code[SoftLightCompositionModeFragmentShader] = ""; //###
+ code[DifferenceCompositionModeFragmentShader] = ""; //###
+ code[ExclusionCompositionModeFragmentShader] = ""; //###
#if defined(QT_DEBUG)
- // Check that all the elements have been filled:
- for (int i = 0; i < TotalSnippetCount; ++i) {
- if (Q_UNLIKELY(!qShaderSnippets[i])) {
- qFatal("Shader snippet for %s (#%d) is missing!",
- snippetNameStr(SnippetName(i)).constData(), i);
- }
+ // Check that all the elements have been filled:
+ for (int i = 0; i < TotalSnippetCount; ++i) {
+ if (Q_UNLIKELY(!qShaderSnippets[i])) {
+ qFatal("Shader snippet for %s (#%d) is missing!",
+ snippetNameStr(SnippetName(i)).constData(), i);
}
-#endif
- snippetsPopulated = true;
}
+#endif
QByteArray vertexSource;
QByteArray fragSource;
diff --git a/src/gui/opengl/qopenglengineshadersource_p.h b/src/gui/opengl/qopenglengineshadersource_p.h
index 1e88ac63b5..a165643839 100644
--- a/src/gui/opengl/qopenglengineshadersource_p.h
+++ b/src/gui/opengl/qopenglengineshadersource_p.h
@@ -58,7 +58,6 @@
QT_BEGIN_NAMESPACE
-
static const char* const qopenglslMainVertexShader = "\n\
void setPosition(); \n\
void main(void) \n\
@@ -532,40 +531,498 @@ static const char* const qopenglslRgbMaskFragmentShaderPass2 = "\n\
ExclusionCompositionModeFragmentShader,
*/
-// OpenGL 3.2 core profile versions of shaders that are used by QOpenGLTextureGlyphCache
+/*
+ OpenGL 3.2+ Core Profile shaders
+ The following shader snippets are copies of the snippets above
+ but use the modern GLSL 1.5 keywords. New shaders should make
+ a snippet for both profiles and add them appropriately in the
+ shader manager.
+*/
+static const char* const qopenglslMainVertexShader_core =
+ "#version 150 core\n\
+ void setPosition(); \n\
+ void main(void) \n\
+ { \n\
+ setPosition(); \n\
+ }\n";
+
+static const char* const qopenglslMainWithTexCoordsVertexShader_core =
+ "#version 150 core\n\
+ in vec2 textureCoordArray; \n\
+ out vec2 textureCoords; \n\
+ void setPosition(); \n\
+ void main(void) \n\
+ { \n\
+ setPosition(); \n\
+ textureCoords = textureCoordArray; \n\
+ }\n";
+
+static const char* const qopenglslMainWithTexCoordsAndOpacityVertexShader_core =
+ "#version 150 core\n\
+ in vec2 textureCoordArray; \n\
+ in float opacityArray; \n\
+ out vec2 textureCoords; \n\
+ out float opacity; \n\
+ void setPosition(); \n\
+ void main(void) \n\
+ { \n\
+ setPosition(); \n\
+ textureCoords = textureCoordArray; \n\
+ opacity = opacityArray; \n\
+ }\n";
+
+// NOTE: We let GL do the perspective correction so texture lookups in the fragment
+// shader are also perspective corrected.
+static const char* const qopenglslPositionOnlyVertexShader_core = "\n\
+ in vec2 vertexCoordsArray; \n\
+ in vec3 pmvMatrix1; \n\
+ in vec3 pmvMatrix2; \n\
+ in vec3 pmvMatrix3; \n\
+ void setPosition(void) \n\
+ { \n\
+ mat3 pmvMatrix = mat3(pmvMatrix1, pmvMatrix2, pmvMatrix3); \n\
+ vec3 transformedPos = pmvMatrix * vec3(vertexCoordsArray.xy, 1.0); \n\
+ gl_Position = vec4(transformedPos.xy, 0.0, transformedPos.z); \n\
+ }\n";
-static const char* const qopenglslMainWithTexCoordsVertexShader_core = "#version 150 core \n\
- in vec2 textureCoordArray; \n\
- out vec2 textureCoords; \n\
- void setPosition(); \n\
- void main(void) \n\
- { \n\
- setPosition(); \n\
- textureCoords = textureCoordArray; \n\
- }\n";
+static const char* const qopenglslComplexGeometryPositionOnlyVertexShader_core = "\n\
+ in vec2 vertexCoordsArray; \n\
+ uniform mat3 matrix; \n\
+ void setPosition(void) \n\
+ { \n\
+ gl_Position = vec4(matrix * vec3(vertexCoordsArray, 1), 1);\n\
+ } \n";
static const char* const qopenglslUntransformedPositionVertexShader_core = "\n\
- in vec4 vertexCoordsArray; \n\
- void setPosition(void) \n\
- { \n\
- gl_Position = vertexCoordsArray; \n\
- }\n";
-
-static const char* const qopenglslMainFragmentShader_core = "#version 150 core \n\
- vec4 srcPixel(); \n\
- out vec4 fragColor; \n\
- void main() \n\
- { \n\
- fragColor = srcPixel(); \n\
- }\n";
+ in vec4 vertexCoordsArray; \n\
+ void setPosition(void) \n\
+ { \n\
+ gl_Position = vertexCoordsArray; \n\
+ }\n";
+
+// Pattern Brush - This assumes the texture size is 8x8 and thus, the inverted size is 0.125
+static const char* const qopenglslPositionWithPatternBrushVertexShader_core = "\n\
+ in vec2 vertexCoordsArray; \n\
+ in vec3 pmvMatrix1; \n\
+ in vec3 pmvMatrix2; \n\
+ in vec3 pmvMatrix3; \n\
+ out vec2 patternTexCoords; \n\
+ uniform vec2 halfViewportSize; \n\
+ uniform vec2 invertedTextureSize; \n\
+ uniform mat3 brushTransform; \n\
+ void setPosition(void) \n\
+ { \n\
+ mat3 pmvMatrix = mat3(pmvMatrix1, pmvMatrix2, pmvMatrix3); \n\
+ vec3 transformedPos = pmvMatrix * vec3(vertexCoordsArray.xy, 1.0); \n\
+ gl_Position.xy = transformedPos.xy / transformedPos.z; \n\
+ vec2 viewportCoords = (gl_Position.xy + 1.0) * halfViewportSize; \n\
+ vec3 hTexCoords = brushTransform * vec3(viewportCoords, 1.0); \n\
+ float invertedHTexCoordsZ = 1.0 / hTexCoords.z; \n\
+ gl_Position = vec4(gl_Position.xy * invertedHTexCoordsZ, 0.0, invertedHTexCoordsZ); \n\
+ patternTexCoords.xy = (hTexCoords.xy * 0.125) * invertedHTexCoordsZ; \n\
+ }\n";
+
+static const char* const qopenglslAffinePositionWithPatternBrushVertexShader_core
+ = qopenglslPositionWithPatternBrushVertexShader_core;
+
+static const char* const qopenglslPatternBrushSrcFragmentShader_core = "\n\
+ in vec2 patternTexCoords;\n\
+ uniform sampler2D brushTexture; \n\
+ uniform vec4 patternColor; \n\
+ vec4 srcPixel() \n\
+ { \n\
+ return patternColor * (1.0 - texture(brushTexture, patternTexCoords).r); \n\
+ }\n";
+
+
+// Linear Gradient Brush
+static const char* const qopenglslPositionWithLinearGradientBrushVertexShader_core = "\n\
+ in vec2 vertexCoordsArray; \n\
+ in vec3 pmvMatrix1; \n\
+ in vec3 pmvMatrix2; \n\
+ in vec3 pmvMatrix3; \n\
+ out float index; \n\
+ uniform vec2 halfViewportSize; \n\
+ uniform vec3 linearData; \n\
+ uniform mat3 brushTransform; \n\
+ void setPosition() \n\
+ { \n\
+ mat3 pmvMatrix = mat3(pmvMatrix1, pmvMatrix2, pmvMatrix3); \n\
+ vec3 transformedPos = pmvMatrix * vec3(vertexCoordsArray.xy, 1.0); \n\
+ gl_Position.xy = transformedPos.xy / transformedPos.z; \n\
+ vec2 viewportCoords = (gl_Position.xy + 1.0) * halfViewportSize; \n\
+ vec3 hTexCoords = brushTransform * vec3(viewportCoords, 1); \n\
+ float invertedHTexCoordsZ = 1.0 / hTexCoords.z; \n\
+ gl_Position = vec4(gl_Position.xy * invertedHTexCoordsZ, 0.0, invertedHTexCoordsZ); \n\
+ index = (dot(linearData.xy, hTexCoords.xy) * linearData.z) * invertedHTexCoordsZ; \n\
+ }\n";
+
+static const char* const qopenglslAffinePositionWithLinearGradientBrushVertexShader_core
+ = qopenglslPositionWithLinearGradientBrushVertexShader_core;
+
+static const char* const qopenglslLinearGradientBrushSrcFragmentShader_core = "\n\
+ uniform sampler2D brushTexture; \n\
+ in float index; \n\
+ vec4 srcPixel() \n\
+ { \n\
+ vec2 val = vec2(index, 0.5); \n\
+ return texture(brushTexture, val); \n\
+ }\n";
+
+
+// Conical Gradient Brush
+static const char* const qopenglslPositionWithConicalGradientBrushVertexShader_core = "\n\
+ in vec2 vertexCoordsArray; \n\
+ in vec3 pmvMatrix1; \n\
+ in vec3 pmvMatrix2; \n\
+ in vec3 pmvMatrix3; \n\
+ out vec2 A; \n\
+ uniform vec2 halfViewportSize; \n\
+ uniform mat3 brushTransform; \n\
+ void setPosition(void) \n\
+ { \n\
+ mat3 pmvMatrix = mat3(pmvMatrix1, pmvMatrix2, pmvMatrix3); \n\
+ vec3 transformedPos = pmvMatrix * vec3(vertexCoordsArray.xy, 1.0); \n\
+ gl_Position.xy = transformedPos.xy / transformedPos.z; \n\
+ vec2 viewportCoords = (gl_Position.xy + 1.0) * halfViewportSize; \n\
+ vec3 hTexCoords = brushTransform * vec3(viewportCoords, 1); \n\
+ float invertedHTexCoordsZ = 1.0 / hTexCoords.z; \n\
+ gl_Position = vec4(gl_Position.xy * invertedHTexCoordsZ, 0.0, invertedHTexCoordsZ); \n\
+ A = hTexCoords.xy * invertedHTexCoordsZ; \n\
+ }\n";
+
+static const char* const qopenglslAffinePositionWithConicalGradientBrushVertexShader_core
+ = qopenglslPositionWithConicalGradientBrushVertexShader_core;
+
+static const char* const qopenglslConicalGradientBrushSrcFragmentShader_core = "\n\
+ #define INVERSE_2PI 0.1591549430918953358 \n\
+ in vec2 A; \n\
+ uniform sampler2D brushTexture; \n\
+ uniform float angle; \n\
+ vec4 srcPixel() \n\
+ { \n\
+ float t; \n\
+ if (abs(A.y) == abs(A.x)) \n\
+ t = (atan(-A.y + 0.002, A.x) + angle) * INVERSE_2PI; \n\
+ else \n\
+ t = (atan(-A.y, A.x) + angle) * INVERSE_2PI; \n\
+ return texture(brushTexture, vec2(t - floor(t), 0.5)); \n\
+ }\n";
+
+
+// Radial Gradient Brush
+static const char* const qopenglslPositionWithRadialGradientBrushVertexShader_core = "\n\
+ in vec2 vertexCoordsArray;\n\
+ in vec3 pmvMatrix1; \n\
+ in vec3 pmvMatrix2; \n\
+ in vec3 pmvMatrix3; \n\
+ out float b; \n\
+ out vec2 A; \n\
+ uniform vec2 halfViewportSize; \n\
+ uniform mat3 brushTransform; \n\
+ uniform vec2 fmp; \n\
+ uniform vec3 bradius; \n\
+ void setPosition(void) \n\
+ {\n\
+ mat3 pmvMatrix = mat3(pmvMatrix1, pmvMatrix2, pmvMatrix3); \n\
+ vec3 transformedPos = pmvMatrix * vec3(vertexCoordsArray.xy, 1.0); \n\
+ gl_Position.xy = transformedPos.xy / transformedPos.z; \n\
+ vec2 viewportCoords = (gl_Position.xy + 1.0) * halfViewportSize; \n\
+ vec3 hTexCoords = brushTransform * vec3(viewportCoords, 1); \n\
+ float invertedHTexCoordsZ = 1.0 / hTexCoords.z; \n\
+ gl_Position = vec4(gl_Position.xy * invertedHTexCoordsZ, 0.0, invertedHTexCoordsZ); \n\
+ A = hTexCoords.xy * invertedHTexCoordsZ; \n\
+ b = bradius.x + 2.0 * dot(A, fmp); \n\
+ }\n";
+
+static const char* const qopenglslAffinePositionWithRadialGradientBrushVertexShader_core
+ = qopenglslPositionWithRadialGradientBrushVertexShader_core;
+
+static const char* const qopenglslRadialGradientBrushSrcFragmentShader_core = "\n\
+ in float b; \n\
+ in vec2 A; \n\
+ uniform sampler2D brushTexture; \n\
+ uniform float fmp2_m_radius2; \n\
+ uniform float inverse_2_fmp2_m_radius2; \n\
+ uniform float sqrfr; \n\
+ uniform vec3 bradius; \n\
+ \n\
+ vec4 srcPixel() \n\
+ { \n\
+ float c = sqrfr-dot(A, A); \n\
+ float det = b*b - 4.0*fmp2_m_radius2*c; \n\
+ vec4 result = vec4(0.0); \n\
+ if (det >= 0.0) { \n\
+ float detSqrt = sqrt(det); \n\
+ float w = max((-b - detSqrt) * inverse_2_fmp2_m_radius2, (-b + detSqrt) * inverse_2_fmp2_m_radius2); \n\
+ if (bradius.y + w * bradius.z >= 0.0) \n\
+ result = texture(brushTexture, vec2(w, 0.5)); \n\
+ } \n\
+ return result; \n\
+ }\n";
+
+
+// Texture Brush
+static const char* const qopenglslPositionWithTextureBrushVertexShader_core = "\n\
+ in vec2 vertexCoordsArray; \n\
+ in vec3 pmvMatrix1; \n\
+ in vec3 pmvMatrix2; \n\
+ in vec3 pmvMatrix3; \n\
+ out vec2 brushTextureCoords; \n\
+ uniform vec2 halfViewportSize; \n\
+ uniform vec2 invertedTextureSize; \n\
+ uniform mat3 brushTransform; \n\
+ \n\
+ void setPosition(void) \n\
+ { \n\
+ mat3 pmvMatrix = mat3(pmvMatrix1, pmvMatrix2, pmvMatrix3); \n\
+ vec3 transformedPos = pmvMatrix * vec3(vertexCoordsArray.xy, 1.0); \n\
+ gl_Position.xy = transformedPos.xy / transformedPos.z; \n\
+ vec2 viewportCoords = (gl_Position.xy + 1.0) * halfViewportSize; \n\
+ vec3 hTexCoords = brushTransform * vec3(viewportCoords, 1); \n\
+ float invertedHTexCoordsZ = 1.0 / hTexCoords.z; \n\
+ gl_Position = vec4(gl_Position.xy * invertedHTexCoordsZ, 0.0, invertedHTexCoordsZ); \n\
+ brushTextureCoords.xy = (hTexCoords.xy * invertedTextureSize) * gl_Position.w; \n\
+ }\n";
+
+static const char* const qopenglslAffinePositionWithTextureBrushVertexShader_core
+ = qopenglslPositionWithTextureBrushVertexShader_core;
+
+static const char* const qopenglslTextureBrushSrcFragmentShader_desktop_core = "\n\
+ in vec2 brushTextureCoords; \n\
+ uniform sampler2D brushTexture; \n\
+ vec4 srcPixel() \n\
+ { \n\
+ return texture(brushTexture, brushTextureCoords); \n\
+ }\n";
+
+static const char* const qopenglslTextureBrushSrcWithPatternFragmentShader_core = "\n\
+ in vec2 brushTextureCoords; \n\
+ uniform vec4 patternColor; \n\
+ uniform sampler2D brushTexture; \n\
+ vec4 srcPixel() \n\
+ { \n\
+ return patternColor * (1.0 - texture(brushTexture, brushTextureCoords).r); \n\
+ }\n";
+
+// Solid Fill Brush
+static const char* const qopenglslSolidBrushSrcFragmentShader_core = "\n\
+ uniform vec4 fragmentColor; \n\
+ vec4 srcPixel() \n\
+ { \n\
+ return fragmentColor; \n\
+ }\n";
static const char* const qopenglslImageSrcFragmentShader_core = "\n\
- in vec2 textureCoords; \n\
- uniform sampler2D imageTexture; \n\
- vec4 srcPixel() \n\
- { \n"
- "return texture(imageTexture, textureCoords); \n"
- "}\n";
+ in vec2 textureCoords; \n\
+ uniform sampler2D imageTexture; \n\
+ vec4 srcPixel() \n\
+ { \n\
+ return texture(imageTexture, textureCoords); \n\
+ }\n";
+
+static const char* const qopenglslCustomSrcFragmentShader_core = "\n\
+ in vec2 textureCoords; \n\
+ uniform sampler2D imageTexture; \n\
+ vec4 srcPixel() \n\
+ { \n\
+ return customShader(imageTexture, textureCoords); \n\
+ }\n";
+
+static const char* const qopenglslImageSrcWithPatternFragmentShader_core = "\n\
+ in vec2 textureCoords; \n\
+ uniform vec4 patternColor; \n\
+ uniform sampler2D imageTexture; \n\
+ vec4 srcPixel() \n\
+ { \n\
+ return patternColor * (1.0 - texture(imageTexture, textureCoords).r); \n\
+ }\n";
+
+static const char* const qopenglslNonPremultipliedImageSrcFragmentShader_core = "\n\
+ in vec2 textureCoords; \n\
+ uniform sampler2D imageTexture; \n\
+ vec4 srcPixel() \n\
+ { \n\
+ vec4 sample = texture(imageTexture, textureCoords); \n\
+ sample.rgb = sample.rgb * sample.a; \n\
+ return sample; \n\
+ }\n";
+
+static const char* const qopenglslGrayscaleImageSrcFragmentShader_core = "\n\
+ in vec2 textureCoords; \n\
+ uniform sampler2D imageTexture; \n\
+ vec4 srcPixel() \n\
+ { \n\
+ return texture(imageTexture, textureCoords).rrra; \n\
+ }\n";
+
+static const char* const qopenglslAlphaImageSrcFragmentShader_core = "\n\
+ in vec2 textureCoords; \n\
+ uniform sampler2D imageTexture; \n\
+ vec4 srcPixel() \n\
+ { \n\
+ return vec4(0, 0, 0, texture(imageTexture, textureCoords).r); \n\
+ }\n";
+
+static const char* const qopenglslShockingPinkSrcFragmentShader_core = "\n\
+ vec4 srcPixel() \n\
+ { \n\
+ return vec4(0.98, 0.06, 0.75, 1.0); \n\
+ }\n";
+
+static const char* const qopenglslMainFragmentShader_ImageArrays_core =
+ "#version 150 core\n\
+ in float opacity; \n\
+ out vec4 fragColor; \n\
+ vec4 srcPixel(); \n\
+ void main() \n\
+ { \n\
+ fragColor = srcPixel() * opacity; \n\
+ }\n";
+
+static const char* const qopenglslMainFragmentShader_CMO_core =
+ "#version 150 core\n\
+ out vec4 fragColor; \n\
+ uniform float globalOpacity; \n\
+ vec4 srcPixel(); \n\
+ vec4 applyMask(vec4); \n\
+ vec4 compose(vec4); \n\
+ void main() \n\
+ { \n\
+ fragColor = applyMask(compose(srcPixel()*globalOpacity))); \n\
+ }\n";
+
+static const char* const qopenglslMainFragmentShader_CM_core =
+ "#version 150 core\n\
+ out vec4 fragColor; \n\
+ vec4 srcPixel(); \n\
+ vec4 applyMask(vec4); \n\
+ vec4 compose(vec4); \n\
+ void main() \n\
+ { \n\
+ fragColor = applyMask(compose(srcPixel())); \n\
+ }\n";
+
+static const char* const qopenglslMainFragmentShader_MO_core =
+ "#version 150 core\n\
+ out vec4 fragColor; \n\
+ uniform float globalOpacity; \n\
+ vec4 srcPixel(); \n\
+ vec4 applyMask(vec4); \n\
+ void main() \n\
+ { \n\
+ fragColor = applyMask(srcPixel()*globalOpacity); \n\
+ }\n";
+
+static const char* const qopenglslMainFragmentShader_M_core =
+ "#version 150 core\n\
+ out vec4 fragColor; \n\
+ vec4 srcPixel(); \n\
+ vec4 applyMask(vec4); \n\
+ void main() \n\
+ { \n\
+ fragColor = applyMask(srcPixel()); \n\
+ }\n";
+
+static const char* const qopenglslMainFragmentShader_CO_core =
+ "#version 150 core\n\
+ out vec4 fragColor; \n\
+ uniform float globalOpacity; \n\
+ vec4 srcPixel(); \n\
+ vec4 compose(vec4); \n\
+ void main() \n\
+ { \n\
+ fragColor = compose(srcPixel()*globalOpacity); \n\
+ }\n";
+
+static const char* const qopenglslMainFragmentShader_C_core =
+ "#version 150 core\n\
+ out vec4 fragColor; \n\
+ vec4 srcPixel(); \n\
+ vec4 compose(vec4); \n\
+ void main() \n\
+ { \n\
+ fragColor = compose(srcPixel()); \n\
+ }\n";
+
+static const char* const qopenglslMainFragmentShader_O_core =
+ "#version 150 core\n\
+ out vec4 fragColor; \n\
+ uniform float globalOpacity; \n\
+ vec4 srcPixel(); \n\
+ void main() \n\
+ { \n\
+ fragColor = srcPixel()*globalOpacity; \n\
+ }\n";
+
+static const char* const qopenglslMainFragmentShader_core =
+ "#version 150 core\n\
+ out vec4 fragColor; \n\
+ vec4 srcPixel(); \n\
+ void main() \n\
+ { \n\
+ fragColor = srcPixel(); \n\
+ }\n";
+
+static const char* const qopenglslMaskFragmentShader_core = "\n\
+ in vec2 textureCoords;\n\
+ uniform sampler2D maskTexture;\n\
+ vec4 applyMask(vec4 src) \n\
+ {\n\
+ vec4 mask = texture(maskTexture, textureCoords); \n\
+ return src * mask.r; \n\
+ }\n";
+
+// For source over with subpixel antialiasing, the final color is calculated per component as follows
+// (.a is alpha component, .c is red, green or blue component):
+// alpha = src.a * mask.c * opacity
+// dest.c = dest.c * (1 - alpha) + src.c * alpha
+//
+// In the first pass, calculate: dest.c = dest.c * (1 - alpha) with blend funcs: zero, 1 - source color
+// In the second pass, calculate: dest.c = dest.c + src.c * alpha with blend funcs: one, one
+//
+// If source is a solid color (src is constant), only the first pass is needed, with blend funcs: constant, 1 - source color
+
+// For source composition with subpixel antialiasing, the final color is calculated per component as follows:
+// alpha = src.a * mask.c * opacity
+// dest.c = dest.c * (1 - mask.c) + src.c * alpha
+//
+
+static const char* const qopenglslRgbMaskFragmentShaderPass1_core = "\n\
+ in vec2 textureCoords;\n\
+ uniform sampler2D maskTexture;\n\
+ vec4 applyMask(vec4 src) \n\
+ { \n\
+ vec4 mask = texture(maskTexture, textureCoords); \n\
+ return src.a * mask; \n\
+ }\n";
+
+static const char* const qopenglslRgbMaskFragmentShaderPass2_core = "\n\
+ in vec2 textureCoords;\n\
+ uniform sampler2D maskTexture;\n\
+ vec4 applyMask(vec4 src) \n\
+ { \n\
+ vec4 mask = texture(maskTexture, textureCoords); \n\
+ return src * mask; \n\
+ }\n";
+
+/*
+ Left to implement:
+ RgbMaskFragmentShader_core,
+ RgbMaskWithGammaFragmentShader_core,
+
+ MultiplyCompositionModeFragmentShader_core,
+ ScreenCompositionModeFragmentShader_core,
+ OverlayCompositionModeFragmentShader_core,
+ DarkenCompositionModeFragmentShader_core,
+ LightenCompositionModeFragmentShader_core,
+ ColorDodgeCompositionModeFragmentShader_core,
+ ColorBurnCompositionModeFragmentShader_core,
+ HardLightCompositionModeFragmentShader_core,
+ SoftLightCompositionModeFragmentShader_core,
+ DifferenceCompositionModeFragmentShader_core,
+ ExclusionCompositionModeFragmentShader_core,
+*/
QT_END_NAMESPACE
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;
diff --git a/src/gui/opengl/qopenglpaintengine_p.h b/src/gui/opengl/qopenglpaintengine_p.h
index 807efb1ec2..679b3c0557 100644
--- a/src/gui/opengl/qopenglpaintengine_p.h
+++ b/src/gui/opengl/qopenglpaintengine_p.h
@@ -65,6 +65,9 @@
#include <private/qopenglextensions_p.h>
+#include <QOpenGLVertexArrayObject>
+#include <QOpenGLBuffer>
+
enum EngineMode {
ImageDrawingMode,
TextDrawingMode,
@@ -193,7 +196,11 @@ public:
snapToPixelGrid(false),
nativePaintingActive(false),
inverseScale(1),
- lastTextureUnitUsed(QT_UNKNOWN_TEXTURE_UNIT)
+ lastTextureUnitUsed(QT_UNKNOWN_TEXTURE_UNIT),
+ vertexBuffer(QOpenGLBuffer::VertexBuffer),
+ texCoordBuffer(QOpenGLBuffer::VertexBuffer),
+ opacityBuffer(QOpenGLBuffer::VertexBuffer),
+ indexBuffer(QOpenGLBuffer::IndexBuffer)
{ }
~QOpenGL2PaintEngineExPrivate();
@@ -222,7 +229,8 @@ public:
void drawCachedGlyphs(QFontEngine::GlyphFormat glyphFormat, QStaticTextItem *staticTextItem);
// Calls glVertexAttributePointer if the pointer has changed
- inline void setVertexAttributePointer(unsigned int arrayIndex, const GLfloat *pointer);
+ inline void uploadData(unsigned int arrayIndex, const GLfloat *data, GLuint count);
+ inline bool uploadIndexData(const void *data, GLenum indexValueType, GLuint count);
// draws whatever is in the vertex array:
void drawVertexArrays(const float *data, int *stops, int stopCount, GLenum primitive);
@@ -313,6 +321,12 @@ public:
GLenum lastTextureUnitUsed;
GLuint lastTextureUsed;
+ QOpenGLVertexArrayObject vao;
+ QOpenGLBuffer vertexBuffer;
+ QOpenGLBuffer texCoordBuffer;
+ QOpenGLBuffer opacityBuffer;
+ QOpenGLBuffer indexBuffer;
+
bool needsSync;
bool multisamplingAlwaysEnabled;
@@ -326,17 +340,55 @@ public:
};
-void QOpenGL2PaintEngineExPrivate::setVertexAttributePointer(unsigned int arrayIndex, const GLfloat *pointer)
+void QOpenGL2PaintEngineExPrivate::uploadData(unsigned int arrayIndex, const GLfloat *data, GLuint count)
{
Q_ASSERT(arrayIndex < 3);
- if (pointer == vertexAttribPointers[arrayIndex])
- return;
-
- vertexAttribPointers[arrayIndex] = pointer;
- if (arrayIndex == QT_OPACITY_ATTR)
- funcs.glVertexAttribPointer(arrayIndex, 1, GL_FLOAT, GL_FALSE, 0, pointer);
- else
- funcs.glVertexAttribPointer(arrayIndex, 2, GL_FLOAT, GL_FALSE, 0, pointer);
+
+ // If a vertex array object is created we have a profile that supports them
+ // and we will upload the data via a QOpenGLBuffer. Otherwise we will use
+ // the legacy way of uploading the data via glVertexAttribPointer.
+ if (vao.isCreated()) {
+ if (arrayIndex == QT_VERTEX_COORDS_ATTR) {
+ vertexBuffer.bind();
+ vertexBuffer.allocate(data, count * sizeof(float));
+ }
+ if (arrayIndex == QT_TEXTURE_COORDS_ATTR) {
+ texCoordBuffer.bind();
+ texCoordBuffer.allocate(data, count * sizeof(float));
+ }
+ if (arrayIndex == QT_OPACITY_ATTR) {
+ opacityBuffer.bind();
+ opacityBuffer.allocate(data, count * sizeof(float));
+ }
+ if (arrayIndex == QT_OPACITY_ATTR)
+ funcs.glVertexAttribPointer(arrayIndex, 1, GL_FLOAT, GL_FALSE, 0, 0);
+ else
+ funcs.glVertexAttribPointer(arrayIndex, 2, GL_FLOAT, GL_FALSE, 0, 0);
+ } else {
+ // If we already uploaded the data we don't have to do it again
+ if (data == vertexAttribPointers[arrayIndex])
+ return;
+
+ // Store the data in cache and upload it to the graphics card.
+ vertexAttribPointers[arrayIndex] = data;
+ if (arrayIndex == QT_OPACITY_ATTR)
+ funcs.glVertexAttribPointer(arrayIndex, 1, GL_FLOAT, GL_FALSE, 0, data);
+ else
+ funcs.glVertexAttribPointer(arrayIndex, 2, GL_FLOAT, GL_FALSE, 0, data);
+ }
+}
+
+bool QOpenGL2PaintEngineExPrivate::uploadIndexData(const void *data, GLenum indexValueType, GLuint count)
+{
+ // Follow the uploadData() logic: VBOs are used only when VAO support is available.
+ // Otherwise the legacy client-side pointer path is used.
+ if (vao.isCreated()) {
+ Q_ASSERT(indexValueType == GL_UNSIGNED_SHORT || indexValueType == GL_UNSIGNED_INT);
+ indexBuffer.bind();
+ indexBuffer.allocate(data, count * (indexValueType == GL_UNSIGNED_SHORT ? sizeof(quint16) : sizeof(quint32)));
+ return true;
+ }
+ return false;
}
QT_END_NAMESPACE
diff --git a/src/gui/opengl/qopengltextureglyphcache.cpp b/src/gui/opengl/qopengltextureglyphcache.cpp
index afd5004cec..62b069a1d0 100644
--- a/src/gui/opengl/qopengltextureglyphcache.cpp
+++ b/src/gui/opengl/qopengltextureglyphcache.cpp
@@ -372,8 +372,8 @@ void QOpenGLTextureGlyphCache::resizeTextureData(int width, int height)
blitProgram = m_blitProgram;
} else {
- pex->setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, m_vertexCoordinateArray);
- pex->setVertexAttributePointer(QT_TEXTURE_COORDS_ATTR, m_textureCoordinateArray);
+ pex->uploadData(QT_VERTEX_COORDS_ATTR, m_vertexCoordinateArray, 8);
+ pex->uploadData(QT_TEXTURE_COORDS_ATTR, m_textureCoordinateArray, 8);
pex->shaderManager->useBlitProgram();
blitProgram = pex->shaderManager->blitProgram();