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 QtOpenGL 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 "qglengineshadermanager_p.h"
41 #include "qglengineshadersource_p.h"
42 #include "qpaintengineex_opengl2_p.h"
43 #include "qglshadercache_p.h"
44 
45 #include <QtGui/private/qopenglcontext_p.h>
46 
47 #if defined(QT_DEBUG)
48 #include <QMetaEnum>
49 #endif
50 
51 #include <algorithm>
52 
53 // #define QT_GL_SHARED_SHADER_DEBUG
54 
55 QT_BEGIN_NAMESPACE
56 
57 class QGLEngineSharedShadersResource : public QOpenGLSharedResource
58 {
59 public:
QGLEngineSharedShadersResource(QOpenGLContext * ctx)60     QGLEngineSharedShadersResource(QOpenGLContext *ctx)
61         : QOpenGLSharedResource(ctx->shareGroup())
62         , m_shaders(new QGLEngineSharedShaders(QGLContext::fromOpenGLContext(ctx)))
63     {
64     }
65 
~QGLEngineSharedShadersResource()66     ~QGLEngineSharedShadersResource()
67     {
68         delete m_shaders;
69     }
70 
invalidateResource()71     void invalidateResource() override
72     {
73         delete m_shaders;
74         m_shaders = 0;
75     }
76 
freeResource(QOpenGLContext *)77     void freeResource(QOpenGLContext *) override
78     {
79     }
80 
shaders() const81     QGLEngineSharedShaders *shaders() const { return m_shaders; }
82 
83 private:
84     QGLEngineSharedShaders *m_shaders;
85 };
86 
87 class QGLShaderStorage
88 {
89 public:
shadersForThread(const QGLContext * context)90     QGLEngineSharedShaders *shadersForThread(const QGLContext *context) {
91         QOpenGLMultiGroupSharedResource *&shaders = m_storage.localData();
92         if (!shaders)
93             shaders = new QOpenGLMultiGroupSharedResource;
94         QGLEngineSharedShadersResource *resource =
95             shaders->value<QGLEngineSharedShadersResource>(context->contextHandle());
96         return resource ? resource->shaders() : 0;
97     }
98 
99 private:
100     QThreadStorage<QOpenGLMultiGroupSharedResource *> m_storage;
101 };
102 
103 Q_GLOBAL_STATIC(QGLShaderStorage, qt_shader_storage);
104 
shadersForContext(const QGLContext * context)105 QGLEngineSharedShaders *QGLEngineSharedShaders::shadersForContext(const QGLContext *context)
106 {
107     return qt_shader_storage()->shadersForThread(context);
108 }
109 
110 const char* QGLEngineSharedShaders::qShaderSnippets[] = {
111     0,0,0,0,0,0,0,0,0,0,
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
115 };
116 
QGLEngineSharedShaders(const QGLContext * context)117 QGLEngineSharedShaders::QGLEngineSharedShaders(const QGLContext* context)
118     : blitShaderProg(0)
119     , simpleShaderProg(0)
120 {
121 
122 /*
123     Rather than having the shader source array statically initialised, it is initialised
124     here instead. This is to allow new shader names to be inserted or existing names moved
125     around without having to change the order of the glsl strings. It is hoped this will
126     make future hard-to-find runtime bugs more obvious and generally give more solid code.
127 */
128     static bool snippetsPopulated = false;
129     if (!snippetsPopulated) {
130 
131         const char** code = qShaderSnippets; // shortcut
132 
133         code[MainVertexShader] = qglslMainVertexShader;
134         code[MainWithTexCoordsVertexShader] = qglslMainWithTexCoordsVertexShader;
135         code[MainWithTexCoordsAndOpacityVertexShader] = qglslMainWithTexCoordsAndOpacityVertexShader;
136 
137         code[UntransformedPositionVertexShader] = qglslUntransformedPositionVertexShader;
138         code[PositionOnlyVertexShader] = qglslPositionOnlyVertexShader;
139         code[ComplexGeometryPositionOnlyVertexShader] = qglslComplexGeometryPositionOnlyVertexShader;
140         code[PositionWithPatternBrushVertexShader] = qglslPositionWithPatternBrushVertexShader;
141         code[PositionWithLinearGradientBrushVertexShader] = qglslPositionWithLinearGradientBrushVertexShader;
142         code[PositionWithConicalGradientBrushVertexShader] = qglslPositionWithConicalGradientBrushVertexShader;
143         code[PositionWithRadialGradientBrushVertexShader] = qglslPositionWithRadialGradientBrushVertexShader;
144         code[PositionWithTextureBrushVertexShader] = qglslPositionWithTextureBrushVertexShader;
145         code[AffinePositionWithPatternBrushVertexShader] = qglslAffinePositionWithPatternBrushVertexShader;
146         code[AffinePositionWithLinearGradientBrushVertexShader] = qglslAffinePositionWithLinearGradientBrushVertexShader;
147         code[AffinePositionWithConicalGradientBrushVertexShader] = qglslAffinePositionWithConicalGradientBrushVertexShader;
148         code[AffinePositionWithRadialGradientBrushVertexShader] = qglslAffinePositionWithRadialGradientBrushVertexShader;
149         code[AffinePositionWithTextureBrushVertexShader] = qglslAffinePositionWithTextureBrushVertexShader;
150 
151         code[MainFragmentShader_CMO] = qglslMainFragmentShader_CMO;
152         code[MainFragmentShader_CM] = qglslMainFragmentShader_CM;
153         code[MainFragmentShader_MO] = qglslMainFragmentShader_MO;
154         code[MainFragmentShader_M] = qglslMainFragmentShader_M;
155         code[MainFragmentShader_CO] = qglslMainFragmentShader_CO;
156         code[MainFragmentShader_C] = qglslMainFragmentShader_C;
157         code[MainFragmentShader_O] = qglslMainFragmentShader_O;
158         code[MainFragmentShader] = qglslMainFragmentShader;
159         code[MainFragmentShader_ImageArrays] = qglslMainFragmentShader_ImageArrays;
160 
161         code[ImageSrcFragmentShader] = qglslImageSrcFragmentShader;
162         code[ImageSrcWithPatternFragmentShader] = qglslImageSrcWithPatternFragmentShader;
163         code[NonPremultipliedImageSrcFragmentShader] = qglslNonPremultipliedImageSrcFragmentShader;
164         code[CustomImageSrcFragmentShader] = qglslCustomSrcFragmentShader; // Calls "customShader", which must be appended
165         code[SolidBrushSrcFragmentShader] = qglslSolidBrushSrcFragmentShader;
166         if (!context->contextHandle()->isOpenGLES())
167             code[TextureBrushSrcFragmentShader] = qglslTextureBrushSrcFragmentShader_desktop;
168         else
169             code[TextureBrushSrcFragmentShader] = qglslTextureBrushSrcFragmentShader_ES;
170         code[TextureBrushSrcWithPatternFragmentShader] = qglslTextureBrushSrcWithPatternFragmentShader;
171         code[PatternBrushSrcFragmentShader] = qglslPatternBrushSrcFragmentShader;
172         code[LinearGradientBrushSrcFragmentShader] = qglslLinearGradientBrushSrcFragmentShader;
173         code[RadialGradientBrushSrcFragmentShader] = qglslRadialGradientBrushSrcFragmentShader;
174         code[ConicalGradientBrushSrcFragmentShader] = qglslConicalGradientBrushSrcFragmentShader;
175         code[ShockingPinkSrcFragmentShader] = qglslShockingPinkSrcFragmentShader;
176 
177         code[NoMaskFragmentShader] = "";
178         code[MaskFragmentShader] = qglslMaskFragmentShader;
179         code[RgbMaskFragmentShaderPass1] = qglslRgbMaskFragmentShaderPass1;
180         code[RgbMaskFragmentShaderPass2] = qglslRgbMaskFragmentShaderPass2;
181         code[RgbMaskWithGammaFragmentShader] = ""; //###
182 
183         code[NoCompositionModeFragmentShader] = "";
184         code[MultiplyCompositionModeFragmentShader] = ""; //###
185         code[ScreenCompositionModeFragmentShader] = ""; //###
186         code[OverlayCompositionModeFragmentShader] = ""; //###
187         code[DarkenCompositionModeFragmentShader] = ""; //###
188         code[LightenCompositionModeFragmentShader] = ""; //###
189         code[ColorDodgeCompositionModeFragmentShader] = ""; //###
190         code[ColorBurnCompositionModeFragmentShader] = ""; //###
191         code[HardLightCompositionModeFragmentShader] = ""; //###
192         code[SoftLightCompositionModeFragmentShader] = ""; //###
193         code[DifferenceCompositionModeFragmentShader] = ""; //###
194         code[ExclusionCompositionModeFragmentShader] = ""; //###
195 
196 #if defined(QT_DEBUG)
197         // Check that all the elements have been filled:
198         for (int i = 0; i < TotalSnippetCount; ++i) {
199             if (Q_UNLIKELY(!qShaderSnippets[i])) {
200                 qFatal("Shader snippet for %s (#%d) is missing!",
201                        snippetNameStr(SnippetName(i)).constData(), i);
202             }
203         }
204 #endif
205         snippetsPopulated = true;
206     }
207 
208     QGLShader* fragShader;
209     QGLShader* vertexShader;
210     QByteArray vertexSource;
211     QByteArray fragSource;
212 
213     // Compile up the simple shader:
214     vertexSource.append(qShaderSnippets[MainVertexShader]);
215     vertexSource.append(qShaderSnippets[PositionOnlyVertexShader]);
216 
217     fragSource.append(qShaderSnippets[MainFragmentShader]);
218     fragSource.append(qShaderSnippets[ShockingPinkSrcFragmentShader]);
219 
220     simpleShaderProg = new QGLShaderProgram(context, 0);
221 
222     CachedShader simpleShaderCache(fragSource, vertexSource);
223 
224     bool inCache = simpleShaderCache.load(simpleShaderProg, context);
225 
226     if (!inCache) {
227         vertexShader = new QGLShader(QGLShader::Vertex, context, 0);
228         shaders.append(vertexShader);
229         if (!vertexShader->compileSourceCode(vertexSource))
230             qWarning("Vertex shader for simpleShaderProg (MainVertexShader & PositionOnlyVertexShader) failed to compile");
231 
232         fragShader = new QGLShader(QGLShader::Fragment, context, 0);
233         shaders.append(fragShader);
234         if (!fragShader->compileSourceCode(fragSource))
235             qWarning("Fragment shader for simpleShaderProg (MainFragmentShader & ShockingPinkSrcFragmentShader) failed to compile");
236 
237         simpleShaderProg->addShader(vertexShader);
238         simpleShaderProg->addShader(fragShader);
239 
240         simpleShaderProg->bindAttributeLocation("vertexCoordsArray", QT_VERTEX_COORDS_ATTR);
241         simpleShaderProg->bindAttributeLocation("pmvMatrix1", QT_PMV_MATRIX_1_ATTR);
242         simpleShaderProg->bindAttributeLocation("pmvMatrix2", QT_PMV_MATRIX_2_ATTR);
243         simpleShaderProg->bindAttributeLocation("pmvMatrix3", QT_PMV_MATRIX_3_ATTR);
244     }
245 
246     simpleShaderProg->link();
247 
248     if (Q_UNLIKELY(!simpleShaderProg->isLinked())) {
249         qCritical("Errors linking simple shader: %s", qPrintable(simpleShaderProg->log()));
250     } else {
251         if (!inCache)
252             simpleShaderCache.store(simpleShaderProg, context);
253     }
254 
255     // Compile the blit shader:
256     vertexSource.clear();
257     vertexSource.append(qShaderSnippets[MainWithTexCoordsVertexShader]);
258     vertexSource.append(qShaderSnippets[UntransformedPositionVertexShader]);
259 
260     fragSource.clear();
261     fragSource.append(qShaderSnippets[MainFragmentShader]);
262     fragSource.append(qShaderSnippets[ImageSrcFragmentShader]);
263 
264     blitShaderProg = new QGLShaderProgram(context, 0);
265 
266     CachedShader blitShaderCache(fragSource, vertexSource);
267 
268     inCache = blitShaderCache.load(blitShaderProg, context);
269 
270     if (!inCache) {
271         vertexShader = new QGLShader(QGLShader::Vertex, context, 0);
272         shaders.append(vertexShader);
273         if (!vertexShader->compileSourceCode(vertexSource))
274             qWarning("Vertex shader for blitShaderProg (MainWithTexCoordsVertexShader & UntransformedPositionVertexShader) failed to compile");
275 
276         fragShader = new QGLShader(QGLShader::Fragment, context, 0);
277         shaders.append(fragShader);
278         if (!fragShader->compileSourceCode(fragSource))
279             qWarning("Fragment shader for blitShaderProg (MainFragmentShader & ImageSrcFragmentShader) failed to compile");
280 
281         blitShaderProg->addShader(vertexShader);
282         blitShaderProg->addShader(fragShader);
283 
284         blitShaderProg->bindAttributeLocation("textureCoordArray", QT_TEXTURE_COORDS_ATTR);
285         blitShaderProg->bindAttributeLocation("vertexCoordsArray", QT_VERTEX_COORDS_ATTR);
286     }
287 
288     blitShaderProg->link();
289     if (Q_UNLIKELY(!blitShaderProg->isLinked())) {
290         qCritical("Errors linking blit shader: %s", qPrintable(blitShaderProg->log()));
291     } else {
292         if (!inCache)
293             blitShaderCache.store(blitShaderProg, context);
294     }
295 
296 #ifdef QT_GL_SHARED_SHADER_DEBUG
297     qDebug(" -> QGLEngineSharedShaders() %p for thread %p.", this, QThread::currentThread());
298 #endif
299 }
300 
~QGLEngineSharedShaders()301 QGLEngineSharedShaders::~QGLEngineSharedShaders()
302 {
303 #ifdef QT_GL_SHARED_SHADER_DEBUG
304     qDebug(" -> ~QGLEngineSharedShaders() %p for thread %p.", this, QThread::currentThread());
305 #endif
306     qDeleteAll(shaders);
307     shaders.clear();
308 
309     qDeleteAll(cachedPrograms);
310     cachedPrograms.clear();
311 
312     if (blitShaderProg) {
313         delete blitShaderProg;
314         blitShaderProg = 0;
315     }
316 
317     if (simpleShaderProg) {
318         delete simpleShaderProg;
319         simpleShaderProg = 0;
320     }
321 }
322 
323 #if defined (QT_DEBUG)
snippetNameStr(SnippetName name)324 QByteArray QGLEngineSharedShaders::snippetNameStr(SnippetName name)
325 {
326     QMetaEnum m = staticMetaObject.enumerator(staticMetaObject.indexOfEnumerator("SnippetName"));
327     return QByteArray(m.valueToKey(name));
328 }
329 #endif
330 
331 // The address returned here will only be valid until next time this function is called.
332 // The program is return bound.
findProgramInCache(const QGLEngineShaderProg & prog)333 QGLEngineShaderProg *QGLEngineSharedShaders::findProgramInCache(const QGLEngineShaderProg &prog)
334 {
335     for (int i = 0; i < cachedPrograms.size(); ++i) {
336         QGLEngineShaderProg *cachedProg = cachedPrograms.at(i);
337         if (*cachedProg == prog) {
338             // Move the program to the top of the list as a poor-man's cache algo
339             cachedPrograms.move(i, 0);
340             cachedProg->program->bind();
341             return cachedProg;
342         }
343     }
344 
345     QScopedPointer<QGLEngineShaderProg> newProg;
346 
347     do {
348         QByteArray fragSource;
349         // Insert the custom stage before the srcPixel shader to work around an ATI driver bug
350         // where you cannot forward declare a function that takes a sampler as argument.
351         if (prog.srcPixelFragShader == CustomImageSrcFragmentShader)
352             fragSource.append(prog.customStageSource);
353         fragSource.append(qShaderSnippets[prog.mainFragShader]);
354         fragSource.append(qShaderSnippets[prog.srcPixelFragShader]);
355         if (prog.compositionFragShader)
356             fragSource.append(qShaderSnippets[prog.compositionFragShader]);
357         if (prog.maskFragShader)
358             fragSource.append(qShaderSnippets[prog.maskFragShader]);
359 
360         QByteArray vertexSource;
361         vertexSource.append(qShaderSnippets[prog.mainVertexShader]);
362         vertexSource.append(qShaderSnippets[prog.positionVertexShader]);
363 
364         QScopedPointer<QGLShaderProgram> shaderProgram(new QGLShaderProgram);
365 
366         CachedShader shaderCache(fragSource, vertexSource);
367         bool inCache = shaderCache.load(shaderProgram.data(), QGLContext::currentContext());
368 
369         if (!inCache) {
370 
371             QScopedPointer<QGLShader> fragShader(new QGLShader(QGLShader::Fragment));
372             QByteArray description;
373 #if defined(QT_DEBUG)
374             // Name the shader for easier debugging
375             description.append("Fragment shader: main=");
376             description.append(snippetNameStr(prog.mainFragShader));
377             description.append(", srcPixel=");
378             description.append(snippetNameStr(prog.srcPixelFragShader));
379             if (prog.compositionFragShader) {
380                 description.append(", composition=");
381                 description.append(snippetNameStr(prog.compositionFragShader));
382             }
383             if (prog.maskFragShader) {
384                 description.append(", mask=");
385                 description.append(snippetNameStr(prog.maskFragShader));
386             }
387             fragShader->setObjectName(QString::fromLatin1(description));
388 #endif
389             if (!fragShader->compileSourceCode(fragSource)) {
390                 qWarning() << "Warning:" << description << "failed to compile!";
391                 break;
392             }
393 
394             QScopedPointer<QGLShader> vertexShader(new QGLShader(QGLShader::Vertex));
395 #if defined(QT_DEBUG)
396             // Name the shader for easier debugging
397             description.clear();
398             description.append("Vertex shader: main=");
399             description.append(snippetNameStr(prog.mainVertexShader));
400             description.append(", position=");
401             description.append(snippetNameStr(prog.positionVertexShader));
402             vertexShader->setObjectName(QString::fromLatin1(description));
403 #endif
404             if (!vertexShader->compileSourceCode(vertexSource)) {
405                 qWarning() << "Warning:" << description << "failed to compile!";
406                 break;
407             }
408 
409             shaders.append(vertexShader.data());
410             shaders.append(fragShader.data());
411             shaderProgram->addShader(vertexShader.take());
412             shaderProgram->addShader(fragShader.take());
413 
414             // We have to bind the vertex attribute names before the program is linked:
415             shaderProgram->bindAttributeLocation("vertexCoordsArray", QT_VERTEX_COORDS_ATTR);
416             if (prog.useTextureCoords)
417                 shaderProgram->bindAttributeLocation("textureCoordArray", QT_TEXTURE_COORDS_ATTR);
418             if (prog.useOpacityAttribute)
419                 shaderProgram->bindAttributeLocation("opacityArray", QT_OPACITY_ATTR);
420             if (prog.usePmvMatrixAttribute) {
421                 shaderProgram->bindAttributeLocation("pmvMatrix1", QT_PMV_MATRIX_1_ATTR);
422                 shaderProgram->bindAttributeLocation("pmvMatrix2", QT_PMV_MATRIX_2_ATTR);
423                 shaderProgram->bindAttributeLocation("pmvMatrix3", QT_PMV_MATRIX_3_ATTR);
424             }
425         }
426 
427         newProg.reset(new QGLEngineShaderProg(prog));
428         newProg->program = shaderProgram.take();
429 
430         newProg->program->link();
431         if (newProg->program->isLinked()) {
432             if (!inCache)
433                 shaderCache.store(newProg->program, QGLContext::currentContext());
434         } else {
435             QString error;
436             error = QLatin1String("Shader program failed to link,");
437 #if defined(QT_DEBUG)
438             QLatin1String br("\n");
439             error += QLatin1String("\n  Shaders Used:\n");
440             for (int i = 0; i < newProg->program->shaders().count(); ++i) {
441                 QGLShader *shader = newProg->program->shaders().at(i);
442                 error += QLatin1String("    ") + shader->objectName() + QLatin1String(": \n")
443                          + QLatin1String(shader->sourceCode()) + br;
444             }
445 #endif
446             error += QLatin1String("  Error Log:\n")
447                      + QLatin1String("    ") + newProg->program->log();
448             qWarning() << error;
449             break;
450         }
451 
452         newProg->program->bind();
453 
454         if (newProg->maskFragShader != QGLEngineSharedShaders::NoMaskFragmentShader) {
455             GLuint location = newProg->program->uniformLocation("maskTexture");
456             newProg->program->setUniformValue(location, QT_MASK_TEXTURE_UNIT);
457         }
458 
459         if (cachedPrograms.count() > 30) {
460             // The cache is full, so delete the last 5 programs in the list.
461             // These programs will be least used, as a program us bumped to
462             // the top of the list when it's used.
463             for (int i = 0; i < 5; ++i) {
464                 delete cachedPrograms.last();
465                 cachedPrograms.removeLast();
466             }
467         }
468 
469         cachedPrograms.insert(0, newProg.data());
470     } while (false);
471 
472     return newProg.take();
473 }
474 
cleanupCustomStage(QGLCustomShaderStage * stage)475 void QGLEngineSharedShaders::cleanupCustomStage(QGLCustomShaderStage* stage)
476 {
477     auto hasStageAsCustomShaderSouce = [stage](QGLEngineShaderProg *cachedProg) -> bool {
478         if (cachedProg->customStageSource == stage->source()) {
479             delete cachedProg;
480             return true;
481         }
482         return false;
483     };
484     cachedPrograms.erase(std::remove_if(cachedPrograms.begin(), cachedPrograms.end(),
485                                         hasStageAsCustomShaderSouce),
486                          cachedPrograms.end());
487 }
488 
489 
QGLEngineShaderManager(QGLContext * context)490 QGLEngineShaderManager::QGLEngineShaderManager(QGLContext* context)
491     : ctx(context),
492       shaderProgNeedsChanging(true),
493       complexGeometry(false),
494       srcPixelType(Qt::NoBrush),
495       opacityMode(NoOpacity),
496       maskType(NoMask),
497       compositionMode(QPainter::CompositionMode_SourceOver),
498       customSrcStage(0),
499       currentShaderProg(0)
500 {
501     sharedShaders = QGLEngineSharedShaders::shadersForContext(context);
502 }
503 
~QGLEngineShaderManager()504 QGLEngineShaderManager::~QGLEngineShaderManager()
505 {
506     //###
507     removeCustomStage();
508 }
509 
getUniformLocation(Uniform id)510 GLuint QGLEngineShaderManager::getUniformLocation(Uniform id)
511 {
512     if (!currentShaderProg)
513         return 0;
514 
515     QVector<uint> &uniformLocations = currentShaderProg->uniformLocations;
516     if (uniformLocations.isEmpty())
517         uniformLocations.fill(GLuint(-1), NumUniforms);
518 
519     static const char *const uniformNames[] = {
520         "imageTexture",
521         "patternColor",
522         "globalOpacity",
523         "depth",
524         "maskTexture",
525         "fragmentColor",
526         "linearData",
527         "angle",
528         "halfViewportSize",
529         "fmp",
530         "fmp2_m_radius2",
531         "inverse_2_fmp2_m_radius2",
532         "sqrfr",
533         "bradius",
534         "invertedTextureSize",
535         "brushTransform",
536         "brushTexture",
537         "matrix",
538         "translateZ"
539     };
540 
541     if (uniformLocations.at(id) == GLuint(-1))
542         uniformLocations[id] = currentShaderProg->program->uniformLocation(uniformNames[id]);
543 
544     return uniformLocations.at(id);
545 }
546 
547 
optimiseForBrushTransform(QTransform::TransformationType transformType)548 void QGLEngineShaderManager::optimiseForBrushTransform(QTransform::TransformationType transformType)
549 {
550     Q_UNUSED(transformType); // Currently ignored
551 }
552 
setDirty()553 void QGLEngineShaderManager::setDirty()
554 {
555     shaderProgNeedsChanging = true;
556 }
557 
setSrcPixelType(Qt::BrushStyle style)558 void QGLEngineShaderManager::setSrcPixelType(Qt::BrushStyle style)
559 {
560     Q_ASSERT(style != Qt::NoBrush);
561     if (srcPixelType == PixelSrcType(style))
562         return;
563 
564     srcPixelType = style;
565     shaderProgNeedsChanging = true; //###
566 }
567 
setSrcPixelType(PixelSrcType type)568 void QGLEngineShaderManager::setSrcPixelType(PixelSrcType type)
569 {
570     if (srcPixelType == type)
571         return;
572 
573     srcPixelType = type;
574     shaderProgNeedsChanging = true; //###
575 }
576 
setOpacityMode(OpacityMode mode)577 void QGLEngineShaderManager::setOpacityMode(OpacityMode mode)
578 {
579     if (opacityMode == mode)
580         return;
581 
582     opacityMode = mode;
583     shaderProgNeedsChanging = true; //###
584 }
585 
setMaskType(MaskType type)586 void QGLEngineShaderManager::setMaskType(MaskType type)
587 {
588     if (maskType == type)
589         return;
590 
591     maskType = type;
592     shaderProgNeedsChanging = true; //###
593 }
594 
setCompositionMode(QPainter::CompositionMode mode)595 void QGLEngineShaderManager::setCompositionMode(QPainter::CompositionMode mode)
596 {
597     if (compositionMode == mode)
598         return;
599 
600     compositionMode = mode;
601     shaderProgNeedsChanging = true; //###
602 }
603 
setCustomStage(QGLCustomShaderStage * stage)604 void QGLEngineShaderManager::setCustomStage(QGLCustomShaderStage* stage)
605 {
606     if (customSrcStage)
607         removeCustomStage();
608     customSrcStage = stage;
609     shaderProgNeedsChanging = true;
610 }
611 
removeCustomStage()612 void QGLEngineShaderManager::removeCustomStage()
613 {
614     if (customSrcStage)
615         customSrcStage->setInactive();
616     customSrcStage = 0;
617     shaderProgNeedsChanging = true;
618 }
619 
currentProgram()620 QGLShaderProgram* QGLEngineShaderManager::currentProgram()
621 {
622     if (currentShaderProg)
623         return currentShaderProg->program;
624     else
625         return sharedShaders->simpleProgram();
626 }
627 
useSimpleProgram()628 void QGLEngineShaderManager::useSimpleProgram()
629 {
630     sharedShaders->simpleProgram()->bind();
631     QGLContextPrivate* ctx_d = ctx->d_func();
632     ctx_d->setVertexAttribArrayEnabled(QT_VERTEX_COORDS_ATTR, true);
633     ctx_d->setVertexAttribArrayEnabled(QT_TEXTURE_COORDS_ATTR, false);
634     ctx_d->setVertexAttribArrayEnabled(QT_OPACITY_ATTR, false);
635     shaderProgNeedsChanging = true;
636 }
637 
useBlitProgram()638 void QGLEngineShaderManager::useBlitProgram()
639 {
640     sharedShaders->blitProgram()->bind();
641     QGLContextPrivate* ctx_d = ctx->d_func();
642     ctx_d->setVertexAttribArrayEnabled(QT_VERTEX_COORDS_ATTR, true);
643     ctx_d->setVertexAttribArrayEnabled(QT_TEXTURE_COORDS_ATTR, true);
644     ctx_d->setVertexAttribArrayEnabled(QT_OPACITY_ATTR, false);
645     shaderProgNeedsChanging = true;
646 }
647 
simpleProgram()648 QGLShaderProgram* QGLEngineShaderManager::simpleProgram()
649 {
650     return sharedShaders->simpleProgram();
651 }
652 
blitProgram()653 QGLShaderProgram* QGLEngineShaderManager::blitProgram()
654 {
655     return sharedShaders->blitProgram();
656 }
657 
658 
659 
660 // Select & use the correct shader program using the current state.
661 // Returns \c true if program needed changing.
useCorrectShaderProg()662 bool QGLEngineShaderManager::useCorrectShaderProg()
663 {
664     if (!shaderProgNeedsChanging)
665         return false;
666 
667     bool useCustomSrc = customSrcStage != 0;
668     if (useCustomSrc && srcPixelType != QGLEngineShaderManager::ImageSrc && srcPixelType != Qt::TexturePattern) {
669         useCustomSrc = false;
670         qWarning("QGLEngineShaderManager - Ignoring custom shader stage for non image src");
671     }
672 
673     QGLEngineShaderProg requiredProgram;
674 
675     bool texCoords = false;
676 
677     // Choose vertex shader shader position function (which typically also sets
678     // varyings) and the source pixel (srcPixel) fragment shader function:
679     requiredProgram.positionVertexShader = QGLEngineSharedShaders::InvalidSnippetName;
680     requiredProgram.srcPixelFragShader = QGLEngineSharedShaders::InvalidSnippetName;
681     bool isAffine = brushTransform.isAffine();
682     if ( (srcPixelType >= Qt::Dense1Pattern) && (srcPixelType <= Qt::DiagCrossPattern) ) {
683         if (isAffine)
684             requiredProgram.positionVertexShader = QGLEngineSharedShaders::AffinePositionWithPatternBrushVertexShader;
685         else
686             requiredProgram.positionVertexShader = QGLEngineSharedShaders::PositionWithPatternBrushVertexShader;
687 
688         requiredProgram.srcPixelFragShader = QGLEngineSharedShaders::PatternBrushSrcFragmentShader;
689     }
690     else switch (srcPixelType) {
691         default:
692         case Qt::NoBrush:
693             qFatal("QGLEngineShaderManager::useCorrectShaderProg() - Qt::NoBrush style is set");
694             break;
695         case QGLEngineShaderManager::ImageSrc:
696             requiredProgram.srcPixelFragShader = QGLEngineSharedShaders::ImageSrcFragmentShader;
697             requiredProgram.positionVertexShader = QGLEngineSharedShaders::PositionOnlyVertexShader;
698             texCoords = true;
699             break;
700         case QGLEngineShaderManager::NonPremultipliedImageSrc:
701             requiredProgram.srcPixelFragShader = QGLEngineSharedShaders::NonPremultipliedImageSrcFragmentShader;
702             requiredProgram.positionVertexShader = QGLEngineSharedShaders::PositionOnlyVertexShader;
703             texCoords = true;
704             break;
705         case QGLEngineShaderManager::PatternSrc:
706             requiredProgram.srcPixelFragShader = QGLEngineSharedShaders::ImageSrcWithPatternFragmentShader;
707             requiredProgram.positionVertexShader = QGLEngineSharedShaders::PositionOnlyVertexShader;
708             texCoords = true;
709             break;
710         case QGLEngineShaderManager::TextureSrcWithPattern:
711             requiredProgram.srcPixelFragShader = QGLEngineSharedShaders::TextureBrushSrcWithPatternFragmentShader;
712             requiredProgram.positionVertexShader = isAffine ? QGLEngineSharedShaders::AffinePositionWithTextureBrushVertexShader
713                                                 : QGLEngineSharedShaders::PositionWithTextureBrushVertexShader;
714             break;
715         case Qt::SolidPattern:
716             requiredProgram.srcPixelFragShader = QGLEngineSharedShaders::SolidBrushSrcFragmentShader;
717             requiredProgram.positionVertexShader = QGLEngineSharedShaders::PositionOnlyVertexShader;
718             break;
719         case Qt::LinearGradientPattern:
720             requiredProgram.srcPixelFragShader = QGLEngineSharedShaders::LinearGradientBrushSrcFragmentShader;
721             requiredProgram.positionVertexShader = isAffine ? QGLEngineSharedShaders::AffinePositionWithLinearGradientBrushVertexShader
722                                                 : QGLEngineSharedShaders::PositionWithLinearGradientBrushVertexShader;
723             break;
724         case Qt::ConicalGradientPattern:
725             requiredProgram.srcPixelFragShader = QGLEngineSharedShaders::ConicalGradientBrushSrcFragmentShader;
726             requiredProgram.positionVertexShader = isAffine ? QGLEngineSharedShaders::AffinePositionWithConicalGradientBrushVertexShader
727                                                 : QGLEngineSharedShaders::PositionWithConicalGradientBrushVertexShader;
728             break;
729         case Qt::RadialGradientPattern:
730             requiredProgram.srcPixelFragShader = QGLEngineSharedShaders::RadialGradientBrushSrcFragmentShader;
731             requiredProgram.positionVertexShader = isAffine ? QGLEngineSharedShaders::AffinePositionWithRadialGradientBrushVertexShader
732                                                 : QGLEngineSharedShaders::PositionWithRadialGradientBrushVertexShader;
733             break;
734         case Qt::TexturePattern:
735             requiredProgram.srcPixelFragShader = QGLEngineSharedShaders::TextureBrushSrcFragmentShader;
736             requiredProgram.positionVertexShader = isAffine ? QGLEngineSharedShaders::AffinePositionWithTextureBrushVertexShader
737                                                 : QGLEngineSharedShaders::PositionWithTextureBrushVertexShader;
738             break;
739     };
740 
741     if (useCustomSrc) {
742         requiredProgram.srcPixelFragShader = QGLEngineSharedShaders::CustomImageSrcFragmentShader;
743         requiredProgram.customStageSource = customSrcStage->source();
744     }
745 
746     const bool hasCompose = compositionMode > QPainter::CompositionMode_Plus;
747     const bool hasMask = maskType != QGLEngineShaderManager::NoMask;
748 
749     // Choose fragment shader main function:
750     if (opacityMode == AttributeOpacity) {
751         Q_ASSERT(!hasCompose && !hasMask);
752         requiredProgram.mainFragShader = QGLEngineSharedShaders::MainFragmentShader_ImageArrays;
753     } else {
754         bool useGlobalOpacity = (opacityMode == UniformOpacity);
755         if (hasCompose && hasMask && useGlobalOpacity)
756             requiredProgram.mainFragShader = QGLEngineSharedShaders::MainFragmentShader_CMO;
757         if (hasCompose && hasMask && !useGlobalOpacity)
758             requiredProgram.mainFragShader = QGLEngineSharedShaders::MainFragmentShader_CM;
759         if (!hasCompose && hasMask && useGlobalOpacity)
760             requiredProgram.mainFragShader = QGLEngineSharedShaders::MainFragmentShader_MO;
761         if (!hasCompose && hasMask && !useGlobalOpacity)
762             requiredProgram.mainFragShader = QGLEngineSharedShaders::MainFragmentShader_M;
763         if (hasCompose && !hasMask && useGlobalOpacity)
764             requiredProgram.mainFragShader = QGLEngineSharedShaders::MainFragmentShader_CO;
765         if (hasCompose && !hasMask && !useGlobalOpacity)
766             requiredProgram.mainFragShader = QGLEngineSharedShaders::MainFragmentShader_C;
767         if (!hasCompose && !hasMask && useGlobalOpacity)
768             requiredProgram.mainFragShader = QGLEngineSharedShaders::MainFragmentShader_O;
769         if (!hasCompose && !hasMask && !useGlobalOpacity)
770             requiredProgram.mainFragShader = QGLEngineSharedShaders::MainFragmentShader;
771     }
772 
773     if (hasMask) {
774         if (maskType == PixelMask) {
775             requiredProgram.maskFragShader = QGLEngineSharedShaders::MaskFragmentShader;
776             texCoords = true;
777         } else if (maskType == SubPixelMaskPass1) {
778             requiredProgram.maskFragShader = QGLEngineSharedShaders::RgbMaskFragmentShaderPass1;
779             texCoords = true;
780         } else if (maskType == SubPixelMaskPass2) {
781             requiredProgram.maskFragShader = QGLEngineSharedShaders::RgbMaskFragmentShaderPass2;
782             texCoords = true;
783         } else if (maskType == SubPixelWithGammaMask) {
784             requiredProgram.maskFragShader = QGLEngineSharedShaders::RgbMaskWithGammaFragmentShader;
785             texCoords = true;
786         } else {
787             qCritical("QGLEngineShaderManager::useCorrectShaderProg() - Unknown mask type");
788         }
789     } else {
790         requiredProgram.maskFragShader = QGLEngineSharedShaders::NoMaskFragmentShader;
791     }
792 
793     if (hasCompose) {
794         switch (compositionMode) {
795             case QPainter::CompositionMode_Multiply:
796                 requiredProgram.compositionFragShader = QGLEngineSharedShaders::MultiplyCompositionModeFragmentShader;
797                 break;
798             case QPainter::CompositionMode_Screen:
799                 requiredProgram.compositionFragShader = QGLEngineSharedShaders::ScreenCompositionModeFragmentShader;
800                 break;
801             case QPainter::CompositionMode_Overlay:
802                 requiredProgram.compositionFragShader = QGLEngineSharedShaders::OverlayCompositionModeFragmentShader;
803                 break;
804             case QPainter::CompositionMode_Darken:
805                 requiredProgram.compositionFragShader = QGLEngineSharedShaders::DarkenCompositionModeFragmentShader;
806                 break;
807             case QPainter::CompositionMode_Lighten:
808                 requiredProgram.compositionFragShader = QGLEngineSharedShaders::LightenCompositionModeFragmentShader;
809                 break;
810             case QPainter::CompositionMode_ColorDodge:
811                 requiredProgram.compositionFragShader = QGLEngineSharedShaders::ColorDodgeCompositionModeFragmentShader;
812                 break;
813             case QPainter::CompositionMode_ColorBurn:
814                 requiredProgram.compositionFragShader = QGLEngineSharedShaders::ColorBurnCompositionModeFragmentShader;
815                 break;
816             case QPainter::CompositionMode_HardLight:
817                 requiredProgram.compositionFragShader = QGLEngineSharedShaders::HardLightCompositionModeFragmentShader;
818                 break;
819             case QPainter::CompositionMode_SoftLight:
820                 requiredProgram.compositionFragShader = QGLEngineSharedShaders::SoftLightCompositionModeFragmentShader;
821                 break;
822             case QPainter::CompositionMode_Difference:
823                 requiredProgram.compositionFragShader = QGLEngineSharedShaders::DifferenceCompositionModeFragmentShader;
824                 break;
825             case QPainter::CompositionMode_Exclusion:
826                 requiredProgram.compositionFragShader = QGLEngineSharedShaders::ExclusionCompositionModeFragmentShader;
827                 break;
828             default:
829                 qWarning("QGLEngineShaderManager::useCorrectShaderProg() - Unsupported composition mode");
830         }
831     } else {
832         requiredProgram.compositionFragShader = QGLEngineSharedShaders::NoCompositionModeFragmentShader;
833     }
834 
835     // Choose vertex shader main function
836     if (opacityMode == AttributeOpacity) {
837         Q_ASSERT(texCoords);
838         requiredProgram.mainVertexShader = QGLEngineSharedShaders::MainWithTexCoordsAndOpacityVertexShader;
839     } else if (texCoords) {
840         requiredProgram.mainVertexShader = QGLEngineSharedShaders::MainWithTexCoordsVertexShader;
841     } else {
842         requiredProgram.mainVertexShader = QGLEngineSharedShaders::MainVertexShader;
843     }
844     requiredProgram.useTextureCoords = texCoords;
845     requiredProgram.useOpacityAttribute = (opacityMode == AttributeOpacity);
846     if (complexGeometry && srcPixelType == Qt::SolidPattern) {
847         requiredProgram.positionVertexShader = QGLEngineSharedShaders::ComplexGeometryPositionOnlyVertexShader;
848         requiredProgram.usePmvMatrixAttribute = false;
849     } else {
850         requiredProgram.usePmvMatrixAttribute = true;
851 
852         // Force complexGeometry off, since we currently don't support that mode for
853         // non-solid brushes
854         complexGeometry = false;
855     }
856 
857     // At this point, requiredProgram is fully populated so try to find the program in the cache
858     currentShaderProg = sharedShaders->findProgramInCache(requiredProgram);
859 
860     if (currentShaderProg && useCustomSrc) {
861         customSrcStage->setUniforms(currentShaderProg->program);
862     }
863 
864     // Make sure all the vertex attribute arrays the program uses are enabled (and the ones it
865     // doesn't use are disabled)
866     QGLContextPrivate* ctx_d = ctx->d_func();
867     ctx_d->setVertexAttribArrayEnabled(QT_VERTEX_COORDS_ATTR, true);
868     ctx_d->setVertexAttribArrayEnabled(QT_TEXTURE_COORDS_ATTR, currentShaderProg && currentShaderProg->useTextureCoords);
869     ctx_d->setVertexAttribArrayEnabled(QT_OPACITY_ATTR, currentShaderProg && currentShaderProg->useOpacityAttribute);
870 
871     shaderProgNeedsChanging = false;
872     return true;
873 }
874 
875 QT_END_NAMESPACE
876