1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtGui module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "qopenglengineshadermanager_p.h"
41 #include "qopenglengineshadersource_p.h"
42 #include "qopenglpaintengine_p.h"
43 #include "qopenglshadercache_p.h"
44 
45 #include <QtGui/private/qopenglcontext_p.h>
46 #include <QtCore/qthreadstorage.h>
47 
48 #include <algorithm>
49 
50 #if defined(QT_DEBUG)
51 #include <QMetaEnum>
52 #endif
53 
54 // #define QT_GL_SHARED_SHADER_DEBUG
55 
56 QT_BEGIN_NAMESPACE
57 
58 class QOpenGLEngineSharedShadersResource : public QOpenGLSharedResource
59 {
60 public:
QOpenGLEngineSharedShadersResource(QOpenGLContext * ctx)61     QOpenGLEngineSharedShadersResource(QOpenGLContext *ctx)
62         : QOpenGLSharedResource(ctx->shareGroup())
63         , m_shaders(new QOpenGLEngineSharedShaders(ctx))
64     {
65     }
66 
~QOpenGLEngineSharedShadersResource()67     ~QOpenGLEngineSharedShadersResource()
68     {
69         delete m_shaders;
70     }
71 
invalidateResource()72     void invalidateResource() override
73     {
74         delete m_shaders;
75         m_shaders = nullptr;
76     }
77 
freeResource(QOpenGLContext *)78     void freeResource(QOpenGLContext *) override
79     {
80     }
81 
shaders() const82     QOpenGLEngineSharedShaders *shaders() const { return m_shaders; }
83 
84 private:
85     QOpenGLEngineSharedShaders *m_shaders;
86 };
87 
88 class QOpenGLShaderStorage
89 {
90 public:
shadersForThread(QOpenGLContext * context)91     QOpenGLEngineSharedShaders *shadersForThread(QOpenGLContext *context) {
92         QOpenGLMultiGroupSharedResource *&shaders = m_storage.localData();
93         if (!shaders)
94             shaders = new QOpenGLMultiGroupSharedResource;
95         QOpenGLEngineSharedShadersResource *resource =
96             shaders->value<QOpenGLEngineSharedShadersResource>(context);
97         return resource ? resource->shaders() : nullptr;
98     }
99 
100 private:
101     QThreadStorage<QOpenGLMultiGroupSharedResource *> m_storage;
102 };
103 
104 Q_GLOBAL_STATIC(QOpenGLShaderStorage, qt_shader_storage);
105 
shadersForContext(QOpenGLContext * context)106 QOpenGLEngineSharedShaders *QOpenGLEngineSharedShaders::shadersForContext(QOpenGLContext *context)
107 {
108     return qt_shader_storage()->shadersForThread(context);
109 }
110 
111 const char* QOpenGLEngineSharedShaders::qShaderSnippets[] = {
112     0,0,0,0,0,0,0,0,0,0,
113     0,0,0,0,0,0,0,0,0,0,
114     0,0,0,0,0,0,0,0,0,0,
115     0,0,0,0,0
116 };
117 
QOpenGLEngineSharedShaders(QOpenGLContext * context)118 QOpenGLEngineSharedShaders::QOpenGLEngineSharedShaders(QOpenGLContext* context)
119     : blitShaderProg(nullptr)
120     , simpleShaderProg(nullptr)
121 {
122 
123 /*
124     Rather than having the shader source array statically initialised, it is initialised
125     here instead. This is to allow new shader names to be inserted or existing names moved
126     around without having to change the order of the glsl strings. It is hoped this will
127     make future hard-to-find runtime bugs more obvious and generally give more solid code.
128 */
129 
130     // Check if the user has requested an OpenGL 3.2 Core Profile or higher
131     // and if so use GLSL 1.50 core shaders instead of legacy ones.
132     const QSurfaceFormat &fmt = context->format();
133     const bool isCoreProfile = fmt.profile() == QSurfaceFormat::CoreProfile && fmt.version() >= qMakePair(3,2);
134 
135     const char** code = qShaderSnippets; // shortcut
136 
137     if (isCoreProfile) {
138         code[MainVertexShader] = qopenglslMainVertexShader_core;
139         code[MainWithTexCoordsVertexShader] = qopenglslMainWithTexCoordsVertexShader_core;
140         code[MainWithTexCoordsAndOpacityVertexShader] = qopenglslMainWithTexCoordsAndOpacityVertexShader_core;
141 
142         code[UntransformedPositionVertexShader] = qopenglslUntransformedPositionVertexShader_core;
143         code[PositionOnlyVertexShader] = qopenglslPositionOnlyVertexShader_core;
144         code[ComplexGeometryPositionOnlyVertexShader] = qopenglslComplexGeometryPositionOnlyVertexShader_core;
145         code[PositionWithPatternBrushVertexShader] = qopenglslPositionWithPatternBrushVertexShader_core;
146         code[PositionWithLinearGradientBrushVertexShader] = qopenglslPositionWithLinearGradientBrushVertexShader_core;
147         code[PositionWithConicalGradientBrushVertexShader] = qopenglslPositionWithConicalGradientBrushVertexShader_core;
148         code[PositionWithRadialGradientBrushVertexShader] = qopenglslPositionWithRadialGradientBrushVertexShader_core;
149         code[PositionWithTextureBrushVertexShader] = qopenglslPositionWithTextureBrushVertexShader_core;
150         code[AffinePositionWithPatternBrushVertexShader] = qopenglslAffinePositionWithPatternBrushVertexShader_core;
151         code[AffinePositionWithLinearGradientBrushVertexShader] = qopenglslAffinePositionWithLinearGradientBrushVertexShader_core;
152         code[AffinePositionWithConicalGradientBrushVertexShader] = qopenglslAffinePositionWithConicalGradientBrushVertexShader_core;
153         code[AffinePositionWithRadialGradientBrushVertexShader] = qopenglslAffinePositionWithRadialGradientBrushVertexShader_core;
154         code[AffinePositionWithTextureBrushVertexShader] = qopenglslAffinePositionWithTextureBrushVertexShader_core;
155 
156         code[MainFragmentShader_MO] = qopenglslMainFragmentShader_MO_core;
157         code[MainFragmentShader_M] = qopenglslMainFragmentShader_M_core;
158         code[MainFragmentShader_O] = qopenglslMainFragmentShader_O_core;
159         code[MainFragmentShader] = qopenglslMainFragmentShader_core;
160         code[MainFragmentShader_ImageArrays] = qopenglslMainFragmentShader_ImageArrays_core;
161 
162         code[ImageSrcFragmentShader] = qopenglslImageSrcFragmentShader_core;
163         code[ImageSrcWithPatternFragmentShader] = qopenglslImageSrcWithPatternFragmentShader_core;
164         code[NonPremultipliedImageSrcFragmentShader] = qopenglslNonPremultipliedImageSrcFragmentShader_core;
165         code[GrayscaleImageSrcFragmentShader] = qopenglslGrayscaleImageSrcFragmentShader_core;
166         code[AlphaImageSrcFragmentShader] = qopenglslAlphaImageSrcFragmentShader_core;
167         code[CustomImageSrcFragmentShader] = qopenglslCustomSrcFragmentShader_core; // Calls "customShader", which must be appended
168         code[SolidBrushSrcFragmentShader] = qopenglslSolidBrushSrcFragmentShader_core;
169 
170         code[TextureBrushSrcFragmentShader] = qopenglslTextureBrushSrcFragmentShader_core;
171         code[TextureBrushSrcWithPatternFragmentShader] = qopenglslTextureBrushSrcWithPatternFragmentShader_core;
172         code[PatternBrushSrcFragmentShader] = qopenglslPatternBrushSrcFragmentShader_core;
173         code[LinearGradientBrushSrcFragmentShader] = qopenglslLinearGradientBrushSrcFragmentShader_core;
174         code[RadialGradientBrushSrcFragmentShader] = qopenglslRadialGradientBrushSrcFragmentShader_core;
175         code[ConicalGradientBrushSrcFragmentShader] = qopenglslConicalGradientBrushSrcFragmentShader_core;
176         code[ShockingPinkSrcFragmentShader] = qopenglslShockingPinkSrcFragmentShader_core;
177 
178         code[NoMaskFragmentShader] = "";
179         code[MaskFragmentShader] = qopenglslMaskFragmentShader_core;
180         code[RgbMaskFragmentShaderPass1] = qopenglslRgbMaskFragmentShaderPass1_core;
181         code[RgbMaskFragmentShaderPass2] = qopenglslRgbMaskFragmentShaderPass2_core;
182         code[RgbMaskWithGammaFragmentShader] = ""; //###
183     } else {
184         code[MainVertexShader] = qopenglslMainVertexShader;
185         code[MainWithTexCoordsVertexShader] = qopenglslMainWithTexCoordsVertexShader;
186         code[MainWithTexCoordsAndOpacityVertexShader] = qopenglslMainWithTexCoordsAndOpacityVertexShader;
187 
188         code[UntransformedPositionVertexShader] = qopenglslUntransformedPositionVertexShader;
189         code[PositionOnlyVertexShader] = qopenglslPositionOnlyVertexShader;
190         code[ComplexGeometryPositionOnlyVertexShader] = qopenglslComplexGeometryPositionOnlyVertexShader;
191         code[PositionWithPatternBrushVertexShader] = qopenglslPositionWithPatternBrushVertexShader;
192         code[PositionWithLinearGradientBrushVertexShader] = qopenglslPositionWithLinearGradientBrushVertexShader;
193         code[PositionWithConicalGradientBrushVertexShader] = qopenglslPositionWithConicalGradientBrushVertexShader;
194         code[PositionWithRadialGradientBrushVertexShader] = qopenglslPositionWithRadialGradientBrushVertexShader;
195         code[PositionWithTextureBrushVertexShader] = qopenglslPositionWithTextureBrushVertexShader;
196         code[AffinePositionWithPatternBrushVertexShader] = qopenglslAffinePositionWithPatternBrushVertexShader;
197         code[AffinePositionWithLinearGradientBrushVertexShader] = qopenglslAffinePositionWithLinearGradientBrushVertexShader;
198         code[AffinePositionWithConicalGradientBrushVertexShader] = qopenglslAffinePositionWithConicalGradientBrushVertexShader;
199         code[AffinePositionWithRadialGradientBrushVertexShader] = qopenglslAffinePositionWithRadialGradientBrushVertexShader;
200         code[AffinePositionWithTextureBrushVertexShader] = qopenglslAffinePositionWithTextureBrushVertexShader;
201 
202         code[MainFragmentShader_MO] = qopenglslMainFragmentShader_MO;
203         code[MainFragmentShader_M] = qopenglslMainFragmentShader_M;
204         code[MainFragmentShader_O] = qopenglslMainFragmentShader_O;
205         code[MainFragmentShader] = qopenglslMainFragmentShader;
206         code[MainFragmentShader_ImageArrays] = qopenglslMainFragmentShader_ImageArrays;
207 
208         code[ImageSrcFragmentShader] = qopenglslImageSrcFragmentShader;
209         code[ImageSrcWithPatternFragmentShader] = qopenglslImageSrcWithPatternFragmentShader;
210         code[NonPremultipliedImageSrcFragmentShader] = qopenglslNonPremultipliedImageSrcFragmentShader;
211         code[GrayscaleImageSrcFragmentShader] = qopenglslGrayscaleImageSrcFragmentShader;
212         code[AlphaImageSrcFragmentShader] = qopenglslAlphaImageSrcFragmentShader;
213         code[CustomImageSrcFragmentShader] = qopenglslCustomSrcFragmentShader; // Calls "customShader", which must be appended
214         code[SolidBrushSrcFragmentShader] = qopenglslSolidBrushSrcFragmentShader;
215         code[TextureBrushSrcFragmentShader] = qopenglslTextureBrushSrcFragmentShader;
216         code[TextureBrushSrcWithPatternFragmentShader] = qopenglslTextureBrushSrcWithPatternFragmentShader;
217         code[PatternBrushSrcFragmentShader] = qopenglslPatternBrushSrcFragmentShader;
218         code[LinearGradientBrushSrcFragmentShader] = qopenglslLinearGradientBrushSrcFragmentShader;
219         code[RadialGradientBrushSrcFragmentShader] = qopenglslRadialGradientBrushSrcFragmentShader;
220         code[ConicalGradientBrushSrcFragmentShader] = qopenglslConicalGradientBrushSrcFragmentShader;
221         code[ShockingPinkSrcFragmentShader] = qopenglslShockingPinkSrcFragmentShader;
222 
223         code[NoMaskFragmentShader] = "";
224         code[MaskFragmentShader] = qopenglslMaskFragmentShader;
225         code[RgbMaskFragmentShaderPass1] = qopenglslRgbMaskFragmentShaderPass1;
226         code[RgbMaskFragmentShaderPass2] = qopenglslRgbMaskFragmentShaderPass2;
227         code[RgbMaskWithGammaFragmentShader] = ""; //###
228     }
229 
230     // The composition shaders are just layout qualifiers and the same
231     // for all profiles that support them.
232     code[NoCompositionModeFragmentShader] = "";
233     code[MultiplyCompositionModeFragmentShader] = qopenglslMultiplyCompositionModeFragmentShader;
234     code[ScreenCompositionModeFragmentShader] = qopenglslScreenCompositionModeFragmentShader;
235     code[OverlayCompositionModeFragmentShader] = qopenglslOverlayCompositionModeFragmentShader;
236     code[DarkenCompositionModeFragmentShader] = qopenglslDarkenCompositionModeFragmentShader;
237     code[LightenCompositionModeFragmentShader] = qopenglslLightenCompositionModeFragmentShader;
238     code[ColorDodgeCompositionModeFragmentShader] = qopenglslColorDodgeCompositionModeFragmentShader;
239     code[ColorBurnCompositionModeFragmentShader] = qopenglslColorBurnCompositionModeFragmentShader;
240     code[HardLightCompositionModeFragmentShader] = qopenglslHardLightCompositionModeFragmentShader;
241     code[SoftLightCompositionModeFragmentShader] = qopenglslSoftLightCompositionModeFragmentShader;
242     code[DifferenceCompositionModeFragmentShader] = qopenglslDifferenceCompositionModeFragmentShader;
243     code[ExclusionCompositionModeFragmentShader] = qopenglslExclusionCompositionModeFragmentShader;
244 
245 #if defined(QT_DEBUG)
246     // Check that all the elements have been filled:
247     for (int i = 0; i < TotalSnippetCount; ++i) {
248         if (Q_UNLIKELY(!qShaderSnippets[i])) {
249             qFatal("Shader snippet for %s (#%d) is missing!",
250                    snippetNameStr(SnippetName(i)).constData(), i);
251         }
252     }
253 #endif
254 
255     QByteArray vertexSource;
256     QByteArray fragSource;
257 
258     // Compile up the simple shader:
259 #ifdef Q_OS_WASM
260     vertexSource.append(qShaderSnippets[PositionOnlyVertexShader]);
261     vertexSource.append(qShaderSnippets[MainVertexShader]);
262 #else
263     vertexSource.append(qShaderSnippets[MainVertexShader]);
264     vertexSource.append(qShaderSnippets[PositionOnlyVertexShader]);
265 #endif
266     fragSource.append(qShaderSnippets[MainFragmentShader]);
267     fragSource.append(qShaderSnippets[ShockingPinkSrcFragmentShader]);
268 
269     simpleShaderProg = new QOpenGLShaderProgram;
270 
271     CachedShader simpleShaderCache(fragSource, vertexSource);
272 
273     bool inCache = simpleShaderCache.load(simpleShaderProg, context);
274 
275     if (!inCache) {
276         if (!simpleShaderProg->addCacheableShaderFromSourceCode(QOpenGLShader::Vertex, vertexSource))
277             qWarning("Vertex shader for simpleShaderProg (MainVertexShader & PositionOnlyVertexShader) failed to compile");
278         if (!simpleShaderProg->addCacheableShaderFromSourceCode(QOpenGLShader::Fragment, fragSource))
279             qWarning("Fragment shader for simpleShaderProg (MainFragmentShader & ShockingPinkSrcFragmentShader) failed to compile");
280 
281         simpleShaderProg->bindAttributeLocation("vertexCoordsArray", QT_VERTEX_COORDS_ATTR);
282         simpleShaderProg->bindAttributeLocation("pmvMatrix1", QT_PMV_MATRIX_1_ATTR);
283         simpleShaderProg->bindAttributeLocation("pmvMatrix2", QT_PMV_MATRIX_2_ATTR);
284         simpleShaderProg->bindAttributeLocation("pmvMatrix3", QT_PMV_MATRIX_3_ATTR);
285     }
286 
287     simpleShaderProg->link();
288 
289     if (Q_UNLIKELY(!simpleShaderProg->isLinked())) {
290         qCritical("Errors linking simple shader: %s", qPrintable(simpleShaderProg->log()));
291     } else {
292         if (!inCache)
293             simpleShaderCache.store(simpleShaderProg, context);
294     }
295 
296     // Compile the blit shader:
297     vertexSource.clear();
298     vertexSource.append(qShaderSnippets[MainWithTexCoordsVertexShader]);
299     vertexSource.append(qShaderSnippets[UntransformedPositionVertexShader]);
300 
301     fragSource.clear();
302     fragSource.append(qShaderSnippets[MainFragmentShader]);
303     fragSource.append(qShaderSnippets[ImageSrcFragmentShader]);
304 
305     blitShaderProg = new QOpenGLShaderProgram;
306 
307     CachedShader blitShaderCache(fragSource, vertexSource);
308 
309     inCache = blitShaderCache.load(blitShaderProg, context);
310 
311     if (!inCache) {
312         if (!blitShaderProg->addCacheableShaderFromSourceCode(QOpenGLShader::Vertex, vertexSource))
313             qWarning("Vertex shader for blitShaderProg (MainWithTexCoordsVertexShader & UntransformedPositionVertexShader) failed to compile");
314         if (!blitShaderProg->addCacheableShaderFromSourceCode(QOpenGLShader::Fragment, fragSource))
315             qWarning("Fragment shader for blitShaderProg (MainFragmentShader & ImageSrcFragmentShader) failed to compile");
316 
317         blitShaderProg->bindAttributeLocation("textureCoordArray", QT_TEXTURE_COORDS_ATTR);
318         blitShaderProg->bindAttributeLocation("vertexCoordsArray", QT_VERTEX_COORDS_ATTR);
319     }
320 
321     blitShaderProg->link();
322     if (Q_UNLIKELY(!blitShaderProg->isLinked())) {
323         qCritical("Errors linking blit shader: %s", qPrintable(blitShaderProg->log()));
324     } else {
325         if (!inCache)
326             blitShaderCache.store(blitShaderProg, context);
327     }
328 
329 #ifdef QT_GL_SHARED_SHADER_DEBUG
330     qDebug(" -> QOpenGLEngineSharedShaders() %p for thread %p.", this, QThread::currentThread());
331 #endif
332 }
333 
~QOpenGLEngineSharedShaders()334 QOpenGLEngineSharedShaders::~QOpenGLEngineSharedShaders()
335 {
336 #ifdef QT_GL_SHARED_SHADER_DEBUG
337     qDebug(" -> ~QOpenGLEngineSharedShaders() %p for thread %p.", this, QThread::currentThread());
338 #endif
339     qDeleteAll(cachedPrograms);
340     cachedPrograms.clear();
341 
342     if (blitShaderProg) {
343         delete blitShaderProg;
344         blitShaderProg = nullptr;
345     }
346 
347     if (simpleShaderProg) {
348         delete simpleShaderProg;
349         simpleShaderProg = nullptr;
350     }
351 }
352 
353 #if defined (QT_DEBUG)
snippetNameStr(SnippetName name)354 QByteArray QOpenGLEngineSharedShaders::snippetNameStr(SnippetName name)
355 {
356     QMetaEnum m = staticMetaObject.enumerator(staticMetaObject.indexOfEnumerator("SnippetName"));
357     return QByteArray(m.valueToKey(name));
358 }
359 #endif
360 
361 // The address returned here will only be valid until next time this function is called.
362 // The program is return bound.
findProgramInCache(const QOpenGLEngineShaderProg & prog)363 QOpenGLEngineShaderProg *QOpenGLEngineSharedShaders::findProgramInCache(const QOpenGLEngineShaderProg &prog)
364 {
365     for (int i = 0; i < cachedPrograms.size(); ++i) {
366         QOpenGLEngineShaderProg *cachedProg = cachedPrograms[i];
367         if (*cachedProg == prog) {
368             // Move the program to the top of the list as a poor-man's cache algo
369             cachedPrograms.move(i, 0);
370             cachedProg->program->bind();
371             return cachedProg;
372         }
373     }
374 
375     QScopedPointer<QOpenGLEngineShaderProg> newProg;
376 
377     do {
378         QByteArray fragSource;
379         // Insert the custom stage before the srcPixel shader to work around an ATI driver bug
380         // where you cannot forward declare a function that takes a sampler as argument.
381         if (prog.srcPixelFragShader == CustomImageSrcFragmentShader)
382             fragSource.append(prog.customStageSource);
383         fragSource.append(qShaderSnippets[prog.mainFragShader]);
384         fragSource.append(qShaderSnippets[prog.srcPixelFragShader]);
385         if (prog.compositionFragShader)
386             fragSource.append(qShaderSnippets[prog.compositionFragShader]);
387         if (prog.maskFragShader)
388             fragSource.append(qShaderSnippets[prog.maskFragShader]);
389 
390         QByteArray vertexSource;
391 #ifdef Q_OS_WASM
392         vertexSource.append(qShaderSnippets[prog.positionVertexShader]);
393         vertexSource.append(qShaderSnippets[prog.mainVertexShader]);
394 #else
395         vertexSource.append(qShaderSnippets[prog.mainVertexShader]);
396         vertexSource.append(qShaderSnippets[prog.positionVertexShader]);
397 #endif
398         QScopedPointer<QOpenGLShaderProgram> shaderProgram(new QOpenGLShaderProgram);
399 
400         CachedShader shaderCache(fragSource, vertexSource);
401         bool inCache = shaderCache.load(shaderProgram.data(), QOpenGLContext::currentContext());
402 
403         if (!inCache) {
404             if (!shaderProgram->addCacheableShaderFromSourceCode(QOpenGLShader::Vertex, vertexSource)) {
405                 QByteArray description;
406 #if defined(QT_DEBUG)
407                 description.append("Vertex shader: main=");
408                 description.append(snippetNameStr(prog.mainVertexShader));
409                 description.append(", position=");
410                 description.append(snippetNameStr(prog.positionVertexShader));
411 #endif
412                 qWarning("Warning: \"%s\" failed to compile!", description.constData());
413                 break;
414             }
415             if (!shaderProgram->addCacheableShaderFromSourceCode(QOpenGLShader::Fragment, fragSource)) {
416                 QByteArray description;
417 #if defined(QT_DEBUG)
418                 description.append("Fragment shader: main=");
419                 description.append(snippetNameStr(prog.mainFragShader));
420                 description.append(", srcPixel=");
421                 description.append(snippetNameStr(prog.srcPixelFragShader));
422                 if (prog.compositionFragShader) {
423                     description.append(", composition=");
424                     description.append(snippetNameStr(prog.compositionFragShader));
425                 }
426                 if (prog.maskFragShader) {
427                     description.append(", mask=");
428                     description.append(snippetNameStr(prog.maskFragShader));
429                 }
430 #endif
431                 qWarning("Warning: \"%s\" failed to compile!", description.constData());
432                 break;
433             }
434 
435             // We have to bind the vertex attribute names before the program is linked:
436             shaderProgram->bindAttributeLocation("vertexCoordsArray", QT_VERTEX_COORDS_ATTR);
437             if (prog.useTextureCoords)
438                 shaderProgram->bindAttributeLocation("textureCoordArray", QT_TEXTURE_COORDS_ATTR);
439             if (prog.useOpacityAttribute)
440                 shaderProgram->bindAttributeLocation("opacityArray", QT_OPACITY_ATTR);
441             if (prog.usePmvMatrixAttribute) {
442                 shaderProgram->bindAttributeLocation("pmvMatrix1", QT_PMV_MATRIX_1_ATTR);
443                 shaderProgram->bindAttributeLocation("pmvMatrix2", QT_PMV_MATRIX_2_ATTR);
444                 shaderProgram->bindAttributeLocation("pmvMatrix3", QT_PMV_MATRIX_3_ATTR);
445             }
446         }
447 
448         newProg.reset(new QOpenGLEngineShaderProg(prog));
449         newProg->program = shaderProgram.take();
450 
451         newProg->program->link();
452         if (newProg->program->isLinked()) {
453             if (!inCache)
454                 shaderCache.store(newProg->program, QOpenGLContext::currentContext());
455         } else {
456             QString error;
457             error = QLatin1String("Shader program failed to link")
458                     + QLatin1String("  Error Log:\n")
459                     + QLatin1String("    ") + newProg->program->log();
460             qWarning() << error;
461             break;
462         }
463 
464         newProg->program->bind();
465 
466         if (newProg->maskFragShader != QOpenGLEngineSharedShaders::NoMaskFragmentShader) {
467             GLuint location = newProg->program->uniformLocation("maskTexture");
468             newProg->program->setUniformValue(location, QT_MASK_TEXTURE_UNIT);
469         }
470 
471         if (cachedPrograms.count() > 30) {
472             // The cache is full, so delete the last 5 programs in the list.
473             // These programs will be least used, as a program us bumped to
474             // the top of the list when it's used.
475             for (int i = 0; i < 5; ++i) {
476                 delete cachedPrograms.last();
477                 cachedPrograms.removeLast();
478             }
479         }
480 
481         cachedPrograms.insert(0, newProg.data());
482     } while (false);
483 
484     return newProg.take();
485 }
486 
cleanupCustomStage(QOpenGLCustomShaderStage * stage)487 void QOpenGLEngineSharedShaders::cleanupCustomStage(QOpenGLCustomShaderStage* stage)
488 {
489     auto hasStageAsCustomShaderSouce = [stage](QOpenGLEngineShaderProg *cachedProg) -> bool {
490         if (cachedProg->customStageSource == stage->source()) {
491             delete cachedProg;
492             return true;
493         }
494         return false;
495     };
496     cachedPrograms.erase(std::remove_if(cachedPrograms.begin(), cachedPrograms.end(),
497                                         hasStageAsCustomShaderSouce),
498                          cachedPrograms.end());
499 }
500 
501 
QOpenGLEngineShaderManager(QOpenGLContext * context)502 QOpenGLEngineShaderManager::QOpenGLEngineShaderManager(QOpenGLContext* context)
503     : ctx(context),
504       shaderProgNeedsChanging(true),
505       complexGeometry(false),
506       srcPixelType(Qt::NoBrush),
507       opacityMode(NoOpacity),
508       maskType(NoMask),
509       compositionMode(QPainter::CompositionMode_SourceOver),
510       customSrcStage(nullptr),
511       currentShaderProg(nullptr)
512 {
513     sharedShaders = QOpenGLEngineSharedShaders::shadersForContext(context);
514 }
515 
~QOpenGLEngineShaderManager()516 QOpenGLEngineShaderManager::~QOpenGLEngineShaderManager()
517 {
518     //###
519     removeCustomStage();
520 }
521 
getUniformLocation(Uniform id)522 GLuint QOpenGLEngineShaderManager::getUniformLocation(Uniform id)
523 {
524     if (!currentShaderProg)
525         return 0;
526 
527     QVector<uint> &uniformLocations = currentShaderProg->uniformLocations;
528     if (uniformLocations.isEmpty())
529         uniformLocations.fill(GLuint(-1), NumUniforms);
530 
531     const char uniformNames[][26] = {
532         "imageTexture",
533         "patternColor",
534         "globalOpacity",
535         "depth",
536         "maskTexture",
537         "fragmentColor",
538         "linearData",
539         "angle",
540         "halfViewportSize",
541         "fmp",
542         "fmp2_m_radius2",
543         "inverse_2_fmp2_m_radius2",
544         "sqrfr",
545         "bradius",
546         "invertedTextureSize",
547         "brushTransform",
548         "brushTexture",
549         "matrix"
550     };
551 
552     if (uniformLocations.at(id) == GLuint(-1))
553         uniformLocations[id] = currentShaderProg->program->uniformLocation(uniformNames[id]);
554 
555     return uniformLocations.at(id);
556 }
557 
558 
optimiseForBrushTransform(QTransform::TransformationType transformType)559 void QOpenGLEngineShaderManager::optimiseForBrushTransform(QTransform::TransformationType transformType)
560 {
561     Q_UNUSED(transformType); // Currently ignored
562 }
563 
setDirty()564 void QOpenGLEngineShaderManager::setDirty()
565 {
566     shaderProgNeedsChanging = true;
567 }
568 
setSrcPixelType(Qt::BrushStyle style)569 void QOpenGLEngineShaderManager::setSrcPixelType(Qt::BrushStyle style)
570 {
571     Q_ASSERT(style != Qt::NoBrush);
572     if (srcPixelType == PixelSrcType(style))
573         return;
574 
575     srcPixelType = style;
576     shaderProgNeedsChanging = true; //###
577 }
578 
setSrcPixelType(PixelSrcType type)579 void QOpenGLEngineShaderManager::setSrcPixelType(PixelSrcType type)
580 {
581     if (srcPixelType == type)
582         return;
583 
584     srcPixelType = type;
585     shaderProgNeedsChanging = true; //###
586 }
587 
setOpacityMode(OpacityMode mode)588 void QOpenGLEngineShaderManager::setOpacityMode(OpacityMode mode)
589 {
590     if (opacityMode == mode)
591         return;
592 
593     opacityMode = mode;
594     shaderProgNeedsChanging = true; //###
595 }
596 
setMaskType(MaskType type)597 void QOpenGLEngineShaderManager::setMaskType(MaskType type)
598 {
599     if (maskType == type)
600         return;
601 
602     maskType = type;
603     shaderProgNeedsChanging = true; //###
604 }
605 
setCompositionMode(QPainter::CompositionMode mode)606 void QOpenGLEngineShaderManager::setCompositionMode(QPainter::CompositionMode mode)
607 {
608     if (compositionMode == mode)
609         return;
610 
611     bool wasAdvanced = compositionMode > QPainter::CompositionMode_Plus;
612     bool isAdvanced = mode > QPainter::CompositionMode_Plus;
613 
614     compositionMode = mode;
615     shaderProgNeedsChanging = shaderProgNeedsChanging || wasAdvanced || isAdvanced;
616 }
617 
setCustomStage(QOpenGLCustomShaderStage * stage)618 void QOpenGLEngineShaderManager::setCustomStage(QOpenGLCustomShaderStage* stage)
619 {
620     if (customSrcStage)
621         removeCustomStage();
622     customSrcStage = stage;
623     shaderProgNeedsChanging = true;
624 }
625 
removeCustomStage()626 void QOpenGLEngineShaderManager::removeCustomStage()
627 {
628     if (customSrcStage)
629         customSrcStage->setInactive();
630     customSrcStage = nullptr;
631     shaderProgNeedsChanging = true;
632 }
633 
currentProgram()634 QOpenGLShaderProgram* QOpenGLEngineShaderManager::currentProgram()
635 {
636     if (currentShaderProg)
637         return currentShaderProg->program;
638     else
639         return sharedShaders->simpleProgram();
640 }
641 
useSimpleProgram()642 void QOpenGLEngineShaderManager::useSimpleProgram()
643 {
644     sharedShaders->simpleProgram()->bind();
645     QOpenGLContextPrivate* ctx_d = ctx->d_func();
646     Q_UNUSED(ctx_d);
647 
648     QOpenGL2PaintEngineEx *active_engine = static_cast<QOpenGL2PaintEngineEx *>(ctx_d->active_engine);
649 
650     active_engine->d_func()->setVertexAttribArrayEnabled(QT_VERTEX_COORDS_ATTR, true);
651     active_engine->d_func()->setVertexAttribArrayEnabled(QT_TEXTURE_COORDS_ATTR, false);
652     active_engine->d_func()->setVertexAttribArrayEnabled(QT_OPACITY_ATTR, false);
653 
654     shaderProgNeedsChanging = true;
655 }
656 
useBlitProgram()657 void QOpenGLEngineShaderManager::useBlitProgram()
658 {
659     sharedShaders->blitProgram()->bind();
660     QOpenGLContextPrivate* ctx_d = ctx->d_func();
661     QOpenGL2PaintEngineEx *active_engine = static_cast<QOpenGL2PaintEngineEx *>(ctx_d->active_engine);
662     active_engine->d_func()->setVertexAttribArrayEnabled(QT_VERTEX_COORDS_ATTR, true);
663     active_engine->d_func()->setVertexAttribArrayEnabled(QT_TEXTURE_COORDS_ATTR, true);
664     active_engine->d_func()->setVertexAttribArrayEnabled(QT_OPACITY_ATTR, false);
665     shaderProgNeedsChanging = true;
666 }
667 
simpleProgram()668 QOpenGLShaderProgram* QOpenGLEngineShaderManager::simpleProgram()
669 {
670     return sharedShaders->simpleProgram();
671 }
672 
blitProgram()673 QOpenGLShaderProgram* QOpenGLEngineShaderManager::blitProgram()
674 {
675     return sharedShaders->blitProgram();
676 }
677 
678 
679 
680 // Select & use the correct shader program using the current state.
681 // Returns \c true if program needed changing.
useCorrectShaderProg()682 bool QOpenGLEngineShaderManager::useCorrectShaderProg()
683 {
684     if (!shaderProgNeedsChanging)
685         return false;
686 
687     bool useCustomSrc = customSrcStage != nullptr;
688     if (useCustomSrc && srcPixelType != QOpenGLEngineShaderManager::ImageSrc && srcPixelType != Qt::TexturePattern) {
689         useCustomSrc = false;
690         qWarning("QOpenGLEngineShaderManager - Ignoring custom shader stage for non image src");
691     }
692 
693     QOpenGLEngineShaderProg requiredProgram;
694 
695     bool texCoords = false;
696 
697     // Choose vertex shader shader position function (which typically also sets
698     // varyings) and the source pixel (srcPixel) fragment shader function:
699     requiredProgram.positionVertexShader = QOpenGLEngineSharedShaders::InvalidSnippetName;
700     requiredProgram.srcPixelFragShader = QOpenGLEngineSharedShaders::InvalidSnippetName;
701     bool isAffine = brushTransform.isAffine();
702     if ( (srcPixelType >= Qt::Dense1Pattern) && (srcPixelType <= Qt::DiagCrossPattern) ) {
703         if (isAffine)
704             requiredProgram.positionVertexShader = QOpenGLEngineSharedShaders::AffinePositionWithPatternBrushVertexShader;
705         else
706             requiredProgram.positionVertexShader = QOpenGLEngineSharedShaders::PositionWithPatternBrushVertexShader;
707 
708         requiredProgram.srcPixelFragShader = QOpenGLEngineSharedShaders::PatternBrushSrcFragmentShader;
709     }
710     else switch (srcPixelType) {
711         default:
712         case Qt::NoBrush:
713             qFatal("QOpenGLEngineShaderManager::useCorrectShaderProg() - Qt::NoBrush style is set");
714             break;
715         case QOpenGLEngineShaderManager::ImageSrc:
716             requiredProgram.srcPixelFragShader = QOpenGLEngineSharedShaders::ImageSrcFragmentShader;
717             requiredProgram.positionVertexShader = QOpenGLEngineSharedShaders::PositionOnlyVertexShader;
718             texCoords = true;
719             break;
720         case QOpenGLEngineShaderManager::NonPremultipliedImageSrc:
721             requiredProgram.srcPixelFragShader = QOpenGLEngineSharedShaders::NonPremultipliedImageSrcFragmentShader;
722             requiredProgram.positionVertexShader = QOpenGLEngineSharedShaders::PositionOnlyVertexShader;
723             texCoords = true;
724             break;
725         case QOpenGLEngineShaderManager::GrayscaleImageSrc:
726             requiredProgram.srcPixelFragShader = QOpenGLEngineSharedShaders::GrayscaleImageSrcFragmentShader;
727             requiredProgram.positionVertexShader = QOpenGLEngineSharedShaders::PositionOnlyVertexShader;
728             texCoords = true;
729             break;
730         case QOpenGLEngineShaderManager::AlphaImageSrc:
731             requiredProgram.srcPixelFragShader = QOpenGLEngineSharedShaders::AlphaImageSrcFragmentShader;
732             requiredProgram.positionVertexShader = QOpenGLEngineSharedShaders::PositionOnlyVertexShader;
733             texCoords = true;
734             break;
735         case QOpenGLEngineShaderManager::PatternSrc:
736             requiredProgram.srcPixelFragShader = QOpenGLEngineSharedShaders::ImageSrcWithPatternFragmentShader;
737             requiredProgram.positionVertexShader = QOpenGLEngineSharedShaders::PositionOnlyVertexShader;
738             texCoords = true;
739             break;
740         case QOpenGLEngineShaderManager::TextureSrcWithPattern:
741             requiredProgram.srcPixelFragShader = QOpenGLEngineSharedShaders::TextureBrushSrcWithPatternFragmentShader;
742             requiredProgram.positionVertexShader = isAffine ? QOpenGLEngineSharedShaders::AffinePositionWithTextureBrushVertexShader
743                                                 : QOpenGLEngineSharedShaders::PositionWithTextureBrushVertexShader;
744             break;
745         case Qt::SolidPattern:
746             requiredProgram.srcPixelFragShader = QOpenGLEngineSharedShaders::SolidBrushSrcFragmentShader;
747             requiredProgram.positionVertexShader = QOpenGLEngineSharedShaders::PositionOnlyVertexShader;
748             break;
749         case Qt::LinearGradientPattern:
750             requiredProgram.srcPixelFragShader = QOpenGLEngineSharedShaders::LinearGradientBrushSrcFragmentShader;
751             requiredProgram.positionVertexShader = isAffine ? QOpenGLEngineSharedShaders::AffinePositionWithLinearGradientBrushVertexShader
752                                                 : QOpenGLEngineSharedShaders::PositionWithLinearGradientBrushVertexShader;
753             break;
754         case Qt::ConicalGradientPattern:
755             requiredProgram.srcPixelFragShader = QOpenGLEngineSharedShaders::ConicalGradientBrushSrcFragmentShader;
756             requiredProgram.positionVertexShader = isAffine ? QOpenGLEngineSharedShaders::AffinePositionWithConicalGradientBrushVertexShader
757                                                 : QOpenGLEngineSharedShaders::PositionWithConicalGradientBrushVertexShader;
758             break;
759         case Qt::RadialGradientPattern:
760             requiredProgram.srcPixelFragShader = QOpenGLEngineSharedShaders::RadialGradientBrushSrcFragmentShader;
761             requiredProgram.positionVertexShader = isAffine ? QOpenGLEngineSharedShaders::AffinePositionWithRadialGradientBrushVertexShader
762                                                 : QOpenGLEngineSharedShaders::PositionWithRadialGradientBrushVertexShader;
763             break;
764         case Qt::TexturePattern:
765             requiredProgram.srcPixelFragShader = QOpenGLEngineSharedShaders::TextureBrushSrcFragmentShader;
766             requiredProgram.positionVertexShader = isAffine ? QOpenGLEngineSharedShaders::AffinePositionWithTextureBrushVertexShader
767                                                 : QOpenGLEngineSharedShaders::PositionWithTextureBrushVertexShader;
768             break;
769     };
770 
771     if (useCustomSrc) {
772         requiredProgram.srcPixelFragShader = QOpenGLEngineSharedShaders::CustomImageSrcFragmentShader;
773         requiredProgram.customStageSource = customSrcStage->source();
774     }
775 
776     const bool hasCompose = compositionMode > QPainter::CompositionMode_Plus;
777     const bool hasMask = maskType != QOpenGLEngineShaderManager::NoMask;
778 
779     // Choose fragment shader main function:
780     if (opacityMode == AttributeOpacity) {
781         Q_ASSERT(!hasCompose && !hasMask);
782         requiredProgram.mainFragShader = QOpenGLEngineSharedShaders::MainFragmentShader_ImageArrays;
783     } else {
784         bool useGlobalOpacity = (opacityMode == UniformOpacity);
785         if (hasMask && useGlobalOpacity)
786             requiredProgram.mainFragShader = QOpenGLEngineSharedShaders::MainFragmentShader_MO;
787         if (hasMask && !useGlobalOpacity)
788             requiredProgram.mainFragShader = QOpenGLEngineSharedShaders::MainFragmentShader_M;
789         if (!hasMask && useGlobalOpacity)
790             requiredProgram.mainFragShader = QOpenGLEngineSharedShaders::MainFragmentShader_O;
791         if (!hasMask && !useGlobalOpacity)
792             requiredProgram.mainFragShader = QOpenGLEngineSharedShaders::MainFragmentShader;
793     }
794 
795     if (hasMask) {
796         if (maskType == PixelMask) {
797             requiredProgram.maskFragShader = QOpenGLEngineSharedShaders::MaskFragmentShader;
798             texCoords = true;
799         } else if (maskType == SubPixelMaskPass1) {
800             requiredProgram.maskFragShader = QOpenGLEngineSharedShaders::RgbMaskFragmentShaderPass1;
801             texCoords = true;
802         } else if (maskType == SubPixelMaskPass2) {
803             requiredProgram.maskFragShader = QOpenGLEngineSharedShaders::RgbMaskFragmentShaderPass2;
804             texCoords = true;
805         } else if (maskType == SubPixelWithGammaMask) {
806             requiredProgram.maskFragShader = QOpenGLEngineSharedShaders::RgbMaskWithGammaFragmentShader;
807             texCoords = true;
808         } else {
809             qCritical("QOpenGLEngineShaderManager::useCorrectShaderProg() - Unknown mask type");
810         }
811     } else {
812         requiredProgram.maskFragShader = QOpenGLEngineSharedShaders::NoMaskFragmentShader;
813     }
814 
815     if (hasCompose) {
816         switch (compositionMode) {
817             case QPainter::CompositionMode_Multiply:
818                 requiredProgram.compositionFragShader = QOpenGLEngineSharedShaders::MultiplyCompositionModeFragmentShader;
819                 break;
820             case QPainter::CompositionMode_Screen:
821                 requiredProgram.compositionFragShader = QOpenGLEngineSharedShaders::ScreenCompositionModeFragmentShader;
822                 break;
823             case QPainter::CompositionMode_Overlay:
824                 requiredProgram.compositionFragShader = QOpenGLEngineSharedShaders::OverlayCompositionModeFragmentShader;
825                 break;
826             case QPainter::CompositionMode_Darken:
827                 requiredProgram.compositionFragShader = QOpenGLEngineSharedShaders::DarkenCompositionModeFragmentShader;
828                 break;
829             case QPainter::CompositionMode_Lighten:
830                 requiredProgram.compositionFragShader = QOpenGLEngineSharedShaders::LightenCompositionModeFragmentShader;
831                 break;
832             case QPainter::CompositionMode_ColorDodge:
833                 requiredProgram.compositionFragShader = QOpenGLEngineSharedShaders::ColorDodgeCompositionModeFragmentShader;
834                 break;
835             case QPainter::CompositionMode_ColorBurn:
836                 requiredProgram.compositionFragShader = QOpenGLEngineSharedShaders::ColorBurnCompositionModeFragmentShader;
837                 break;
838             case QPainter::CompositionMode_HardLight:
839                 requiredProgram.compositionFragShader = QOpenGLEngineSharedShaders::HardLightCompositionModeFragmentShader;
840                 break;
841             case QPainter::CompositionMode_SoftLight:
842                 requiredProgram.compositionFragShader = QOpenGLEngineSharedShaders::SoftLightCompositionModeFragmentShader;
843                 break;
844             case QPainter::CompositionMode_Difference:
845                 requiredProgram.compositionFragShader = QOpenGLEngineSharedShaders::DifferenceCompositionModeFragmentShader;
846                 break;
847             case QPainter::CompositionMode_Exclusion:
848                 requiredProgram.compositionFragShader = QOpenGLEngineSharedShaders::ExclusionCompositionModeFragmentShader;
849                 break;
850             default:
851                 qWarning("QOpenGLEngineShaderManager::useCorrectShaderProg() - Unsupported composition mode");
852         }
853     } else {
854         requiredProgram.compositionFragShader = QOpenGLEngineSharedShaders::NoCompositionModeFragmentShader;
855     }
856 
857     // Choose vertex shader main function
858     if (opacityMode == AttributeOpacity) {
859         Q_ASSERT(texCoords);
860         requiredProgram.mainVertexShader = QOpenGLEngineSharedShaders::MainWithTexCoordsAndOpacityVertexShader;
861     } else if (texCoords) {
862         requiredProgram.mainVertexShader = QOpenGLEngineSharedShaders::MainWithTexCoordsVertexShader;
863     } else {
864         requiredProgram.mainVertexShader = QOpenGLEngineSharedShaders::MainVertexShader;
865     }
866     requiredProgram.useTextureCoords = texCoords;
867     requiredProgram.useOpacityAttribute = (opacityMode == AttributeOpacity);
868     if (complexGeometry && srcPixelType == Qt::SolidPattern) {
869         requiredProgram.positionVertexShader = QOpenGLEngineSharedShaders::ComplexGeometryPositionOnlyVertexShader;
870         requiredProgram.usePmvMatrixAttribute = false;
871     } else {
872         requiredProgram.usePmvMatrixAttribute = true;
873 
874         // Force complexGeometry off, since we currently don't support that mode for
875         // non-solid brushes
876         complexGeometry = false;
877     }
878 
879     // At this point, requiredProgram is fully populated so try to find the program in the cache
880     currentShaderProg = sharedShaders->findProgramInCache(requiredProgram);
881 
882     if (currentShaderProg && useCustomSrc) {
883         customSrcStage->setUniforms(currentShaderProg->program);
884     }
885 
886     // Make sure all the vertex attribute arrays the program uses are enabled (and the ones it
887     // doesn't use are disabled)
888     QOpenGLContextPrivate* ctx_d = ctx->d_func();
889     QOpenGL2PaintEngineEx *active_engine = static_cast<QOpenGL2PaintEngineEx *>(ctx_d->active_engine);
890     active_engine->d_func()->setVertexAttribArrayEnabled(QT_VERTEX_COORDS_ATTR, true);
891     active_engine->d_func()->setVertexAttribArrayEnabled(QT_TEXTURE_COORDS_ATTR, currentShaderProg && currentShaderProg->useTextureCoords);
892     active_engine->d_func()->setVertexAttribArrayEnabled(QT_OPACITY_ATTR, currentShaderProg && currentShaderProg->useOpacityAttribute);
893 
894     shaderProgNeedsChanging = false;
895     return true;
896 }
897 
898 QT_END_NAMESPACE
899