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
55QT_BEGIN_NAMESPACE
56
57class QGLEngineSharedShadersResource : public QOpenGLSharedResource
58{
59public:
60 QGLEngineSharedShadersResource(QOpenGLContext *ctx)
61 : QOpenGLSharedResource(ctx->shareGroup())
62 , m_shaders(new QGLEngineSharedShaders(QGLContext::fromOpenGLContext(ctx)))
63 {
64 }
65
66 ~QGLEngineSharedShadersResource()
67 {
68 delete m_shaders;
69 }
70
71 void invalidateResource() override
72 {
73 delete m_shaders;
74 m_shaders = 0;
75 }
76
77 void freeResource(QOpenGLContext *) override
78 {
79 }
80
81 QGLEngineSharedShaders *shaders() const { return m_shaders; }
82
83private:
84 QGLEngineSharedShaders *m_shaders;
85};
86
87class QGLShaderStorage
88{
89public:
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
99private:
100 QThreadStorage<QOpenGLMultiGroupSharedResource *> m_storage;
101};
102
103Q_GLOBAL_STATIC(QGLShaderStorage, qt_shader_storage);
104
105QGLEngineSharedShaders *QGLEngineSharedShaders::shadersForContext(const QGLContext *context)
106{
107 return qt_shader_storage()->shadersForThread(context);
108}
109
110const 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
117QGLEngineSharedShaders::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
301QGLEngineSharedShaders::~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)
324QByteArray 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.
333QGLEngineShaderProg *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
475void 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
490QGLEngineShaderManager::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
504QGLEngineShaderManager::~QGLEngineShaderManager()
505{
506 //###
507 removeCustomStage();
508}
509
510GLuint 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
548void QGLEngineShaderManager::optimiseForBrushTransform(QTransform::TransformationType transformType)
549{
550 Q_UNUSED(transformType); // Currently ignored
551}
552
553void QGLEngineShaderManager::setDirty()
554{
555 shaderProgNeedsChanging = true;
556}
557
558void 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
568void QGLEngineShaderManager::setSrcPixelType(PixelSrcType type)
569{
570 if (srcPixelType == type)
571 return;
572
573 srcPixelType = type;
574 shaderProgNeedsChanging = true; //###
575}
576
577void QGLEngineShaderManager::setOpacityMode(OpacityMode mode)
578{
579 if (opacityMode == mode)
580 return;
581
582 opacityMode = mode;
583 shaderProgNeedsChanging = true; //###
584}
585
586void QGLEngineShaderManager::setMaskType(MaskType type)
587{
588 if (maskType == type)
589 return;
590
591 maskType = type;
592 shaderProgNeedsChanging = true; //###
593}
594
595void QGLEngineShaderManager::setCompositionMode(QPainter::CompositionMode mode)
596{
597 if (compositionMode == mode)
598 return;
599
600 compositionMode = mode;
601 shaderProgNeedsChanging = true; //###
602}
603
604void QGLEngineShaderManager::setCustomStage(QGLCustomShaderStage* stage)
605{
606 if (customSrcStage)
607 removeCustomStage();
608 customSrcStage = stage;
609 shaderProgNeedsChanging = true;
610}
611
612void QGLEngineShaderManager::removeCustomStage()
613{
614 if (customSrcStage)
615 customSrcStage->setInactive();
616 customSrcStage = 0;
617 shaderProgNeedsChanging = true;
618}
619
620QGLShaderProgram* QGLEngineShaderManager::currentProgram()
621{
622 if (currentShaderProg)
623 return currentShaderProg->program;
624 else
625 return sharedShaders->simpleProgram();
626}
627
628void 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
638void 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
648QGLShaderProgram* QGLEngineShaderManager::simpleProgram()
649{
650 return sharedShaders->simpleProgram();
651}
652
653QGLShaderProgram* 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.
662bool 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
875QT_END_NAMESPACE
876