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 "qopenglengineshadermanager_p.h"
41#include "qopenglengineshadersource_p.h"
42#include "qopenglpaintengine_p.h"
43#include <private/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
56QT_BEGIN_NAMESPACE
57
58class QOpenGLEngineSharedShadersResource : public QOpenGLSharedResource
59{
60public:
61 QOpenGLEngineSharedShadersResource(QOpenGLContext *ctx)
62 : QOpenGLSharedResource(ctx->shareGroup())
63 , m_shaders(new QOpenGLEngineSharedShaders(ctx))
64 {
65 }
66
67 ~QOpenGLEngineSharedShadersResource()
68 {
69 delete m_shaders;
70 }
71
72 void invalidateResource() override
73 {
74 delete m_shaders;
75 m_shaders = nullptr;
76 }
77
78 void freeResource(QOpenGLContext *) override
79 {
80 }
81
82 QOpenGLEngineSharedShaders *shaders() const { return m_shaders; }
83
84private:
85 QOpenGLEngineSharedShaders *m_shaders;
86};
87
88class QOpenGLShaderStorage
89{
90public:
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
100private:
101 QThreadStorage<QOpenGLMultiGroupSharedResource *> m_storage;
102};
103
104Q_GLOBAL_STATIC(QOpenGLShaderStorage, qt_shader_storage);
105
106QOpenGLEngineSharedShaders *QOpenGLEngineSharedShaders::shadersForContext(QOpenGLContext *context)
107{
108 return qt_shader_storage()->shadersForThread(context);
109}
110
111const 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
118QOpenGLEngineSharedShaders::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
334QOpenGLEngineSharedShaders::~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)
354QByteArray 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.
363QOpenGLEngineShaderProg *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
487void 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.removeIf(hasStageAsCustomShaderSouce);
497}
498
499
500QOpenGLEngineShaderManager::QOpenGLEngineShaderManager(QOpenGLContext* context)
501 : ctx(context),
502 shaderProgNeedsChanging(true),
503 complexGeometry(false),
504 srcPixelType(Qt::NoBrush),
505 opacityMode(NoOpacity),
506 maskType(NoMask),
507 compositionMode(QPainter::CompositionMode_SourceOver),
508 customSrcStage(nullptr),
509 currentShaderProg(nullptr)
510{
511 sharedShaders = QOpenGLEngineSharedShaders::shadersForContext(context);
512}
513
514QOpenGLEngineShaderManager::~QOpenGLEngineShaderManager()
515{
516 //###
517 removeCustomStage();
518}
519
520GLuint QOpenGLEngineShaderManager::getUniformLocation(Uniform id)
521{
522 if (!currentShaderProg)
523 return 0;
524
525 QList<uint> &uniformLocations = currentShaderProg->uniformLocations;
526 if (uniformLocations.isEmpty())
527 uniformLocations.fill(GLuint(-1), NumUniforms);
528
529 const char uniformNames[][26] = {
530 "imageTexture",
531 "patternColor",
532 "globalOpacity",
533 "depth",
534 "maskTexture",
535 "fragmentColor",
536 "linearData",
537 "angle",
538 "halfViewportSize",
539 "fmp",
540 "fmp2_m_radius2",
541 "inverse_2_fmp2_m_radius2",
542 "sqrfr",
543 "bradius",
544 "invertedTextureSize",
545 "brushTransform",
546 "brushTexture",
547 "matrix"
548 };
549
550 if (uniformLocations.at(id) == GLuint(-1))
551 uniformLocations[id] = currentShaderProg->program->uniformLocation(uniformNames[id]);
552
553 return uniformLocations.at(id);
554}
555
556
557void QOpenGLEngineShaderManager::optimiseForBrushTransform(QTransform::TransformationType transformType)
558{
559 Q_UNUSED(transformType); // Currently ignored
560}
561
562void QOpenGLEngineShaderManager::setDirty()
563{
564 shaderProgNeedsChanging = true;
565}
566
567void QOpenGLEngineShaderManager::setSrcPixelType(Qt::BrushStyle style)
568{
569 Q_ASSERT(style != Qt::NoBrush);
570 if (srcPixelType == PixelSrcType(style))
571 return;
572
573 srcPixelType = style;
574 shaderProgNeedsChanging = true; //###
575}
576
577void QOpenGLEngineShaderManager::setSrcPixelType(PixelSrcType type)
578{
579 if (srcPixelType == type)
580 return;
581
582 srcPixelType = type;
583 shaderProgNeedsChanging = true; //###
584}
585
586void QOpenGLEngineShaderManager::setOpacityMode(OpacityMode mode)
587{
588 if (opacityMode == mode)
589 return;
590
591 opacityMode = mode;
592 shaderProgNeedsChanging = true; //###
593}
594
595void QOpenGLEngineShaderManager::setMaskType(MaskType type)
596{
597 if (maskType == type)
598 return;
599
600 maskType = type;
601 shaderProgNeedsChanging = true; //###
602}
603
604void QOpenGLEngineShaderManager::setCompositionMode(QPainter::CompositionMode mode)
605{
606 if (compositionMode == mode)
607 return;
608
609 bool wasAdvanced = compositionMode > QPainter::CompositionMode_Plus;
610 bool isAdvanced = mode > QPainter::CompositionMode_Plus;
611
612 compositionMode = mode;
613 shaderProgNeedsChanging = shaderProgNeedsChanging || wasAdvanced || isAdvanced;
614}
615
616void QOpenGLEngineShaderManager::setCustomStage(QOpenGLCustomShaderStage* stage)
617{
618 if (customSrcStage)
619 removeCustomStage();
620 customSrcStage = stage;
621 shaderProgNeedsChanging = true;
622}
623
624void QOpenGLEngineShaderManager::removeCustomStage()
625{
626 if (customSrcStage)
627 customSrcStage->setInactive();
628 customSrcStage = nullptr;
629 shaderProgNeedsChanging = true;
630}
631
632QOpenGLShaderProgram* QOpenGLEngineShaderManager::currentProgram()
633{
634 if (currentShaderProg)
635 return currentShaderProg->program;
636 else
637 return sharedShaders->simpleProgram();
638}
639
640void QOpenGLEngineShaderManager::useSimpleProgram()
641{
642 sharedShaders->simpleProgram()->bind();
643 QOpenGLContextPrivate* ctx_d = ctx->d_func();
644 Q_UNUSED(ctx_d);
645
646 QOpenGL2PaintEngineEx *active_engine = static_cast<QOpenGL2PaintEngineEx *>(ctx_d->active_engine);
647
648 active_engine->d_func()->setVertexAttribArrayEnabled(QT_VERTEX_COORDS_ATTR, true);
649 active_engine->d_func()->setVertexAttribArrayEnabled(QT_TEXTURE_COORDS_ATTR, false);
650 active_engine->d_func()->setVertexAttribArrayEnabled(QT_OPACITY_ATTR, false);
651
652 shaderProgNeedsChanging = true;
653}
654
655void QOpenGLEngineShaderManager::useBlitProgram()
656{
657 sharedShaders->blitProgram()->bind();
658 QOpenGLContextPrivate* ctx_d = ctx->d_func();
659 QOpenGL2PaintEngineEx *active_engine = static_cast<QOpenGL2PaintEngineEx *>(ctx_d->active_engine);
660 active_engine->d_func()->setVertexAttribArrayEnabled(QT_VERTEX_COORDS_ATTR, true);
661 active_engine->d_func()->setVertexAttribArrayEnabled(QT_TEXTURE_COORDS_ATTR, true);
662 active_engine->d_func()->setVertexAttribArrayEnabled(QT_OPACITY_ATTR, false);
663 shaderProgNeedsChanging = true;
664}
665
666QOpenGLShaderProgram* QOpenGLEngineShaderManager::simpleProgram()
667{
668 return sharedShaders->simpleProgram();
669}
670
671QOpenGLShaderProgram* QOpenGLEngineShaderManager::blitProgram()
672{
673 return sharedShaders->blitProgram();
674}
675
676
677
678// Select & use the correct shader program using the current state.
679// Returns \c true if program needed changing.
680bool QOpenGLEngineShaderManager::useCorrectShaderProg()
681{
682 if (!shaderProgNeedsChanging)
683 return false;
684
685 bool useCustomSrc = customSrcStage != nullptr;
686 if (useCustomSrc && srcPixelType != QOpenGLEngineShaderManager::ImageSrc && srcPixelType != Qt::TexturePattern) {
687 useCustomSrc = false;
688 qWarning("QOpenGLEngineShaderManager - Ignoring custom shader stage for non image src");
689 }
690
691 QOpenGLEngineShaderProg requiredProgram;
692
693 bool texCoords = false;
694
695 // Choose vertex shader shader position function (which typically also sets
696 // varyings) and the source pixel (srcPixel) fragment shader function:
697 requiredProgram.positionVertexShader = QOpenGLEngineSharedShaders::InvalidSnippetName;
698 requiredProgram.srcPixelFragShader = QOpenGLEngineSharedShaders::InvalidSnippetName;
699 bool isAffine = brushTransform.isAffine();
700 if ( (srcPixelType >= Qt::Dense1Pattern) && (srcPixelType <= Qt::DiagCrossPattern) ) {
701 if (isAffine)
702 requiredProgram.positionVertexShader = QOpenGLEngineSharedShaders::AffinePositionWithPatternBrushVertexShader;
703 else
704 requiredProgram.positionVertexShader = QOpenGLEngineSharedShaders::PositionWithPatternBrushVertexShader;
705
706 requiredProgram.srcPixelFragShader = QOpenGLEngineSharedShaders::PatternBrushSrcFragmentShader;
707 }
708 else switch (srcPixelType) {
709 default:
710 case Qt::NoBrush:
711 qFatal("QOpenGLEngineShaderManager::useCorrectShaderProg() - Qt::NoBrush style is set");
712 break;
713 case QOpenGLEngineShaderManager::ImageSrc:
714 requiredProgram.srcPixelFragShader = QOpenGLEngineSharedShaders::ImageSrcFragmentShader;
715 requiredProgram.positionVertexShader = QOpenGLEngineSharedShaders::PositionOnlyVertexShader;
716 texCoords = true;
717 break;
718 case QOpenGLEngineShaderManager::NonPremultipliedImageSrc:
719 requiredProgram.srcPixelFragShader = QOpenGLEngineSharedShaders::NonPremultipliedImageSrcFragmentShader;
720 requiredProgram.positionVertexShader = QOpenGLEngineSharedShaders::PositionOnlyVertexShader;
721 texCoords = true;
722 break;
723 case QOpenGLEngineShaderManager::GrayscaleImageSrc:
724 requiredProgram.srcPixelFragShader = QOpenGLEngineSharedShaders::GrayscaleImageSrcFragmentShader;
725 requiredProgram.positionVertexShader = QOpenGLEngineSharedShaders::PositionOnlyVertexShader;
726 texCoords = true;
727 break;
728 case QOpenGLEngineShaderManager::AlphaImageSrc:
729 requiredProgram.srcPixelFragShader = QOpenGLEngineSharedShaders::AlphaImageSrcFragmentShader;
730 requiredProgram.positionVertexShader = QOpenGLEngineSharedShaders::PositionOnlyVertexShader;
731 texCoords = true;
732 break;
733 case QOpenGLEngineShaderManager::PatternSrc:
734 requiredProgram.srcPixelFragShader = QOpenGLEngineSharedShaders::ImageSrcWithPatternFragmentShader;
735 requiredProgram.positionVertexShader = QOpenGLEngineSharedShaders::PositionOnlyVertexShader;
736 texCoords = true;
737 break;
738 case QOpenGLEngineShaderManager::TextureSrcWithPattern:
739 requiredProgram.srcPixelFragShader = QOpenGLEngineSharedShaders::TextureBrushSrcWithPatternFragmentShader;
740 requiredProgram.positionVertexShader = isAffine ? QOpenGLEngineSharedShaders::AffinePositionWithTextureBrushVertexShader
741 : QOpenGLEngineSharedShaders::PositionWithTextureBrushVertexShader;
742 break;
743 case Qt::SolidPattern:
744 requiredProgram.srcPixelFragShader = QOpenGLEngineSharedShaders::SolidBrushSrcFragmentShader;
745 requiredProgram.positionVertexShader = QOpenGLEngineSharedShaders::PositionOnlyVertexShader;
746 break;
747 case Qt::LinearGradientPattern:
748 requiredProgram.srcPixelFragShader = QOpenGLEngineSharedShaders::LinearGradientBrushSrcFragmentShader;
749 requiredProgram.positionVertexShader = isAffine ? QOpenGLEngineSharedShaders::AffinePositionWithLinearGradientBrushVertexShader
750 : QOpenGLEngineSharedShaders::PositionWithLinearGradientBrushVertexShader;
751 break;
752 case Qt::ConicalGradientPattern:
753 requiredProgram.srcPixelFragShader = QOpenGLEngineSharedShaders::ConicalGradientBrushSrcFragmentShader;
754 requiredProgram.positionVertexShader = isAffine ? QOpenGLEngineSharedShaders::AffinePositionWithConicalGradientBrushVertexShader
755 : QOpenGLEngineSharedShaders::PositionWithConicalGradientBrushVertexShader;
756 break;
757 case Qt::RadialGradientPattern:
758 requiredProgram.srcPixelFragShader = QOpenGLEngineSharedShaders::RadialGradientBrushSrcFragmentShader;
759 requiredProgram.positionVertexShader = isAffine ? QOpenGLEngineSharedShaders::AffinePositionWithRadialGradientBrushVertexShader
760 : QOpenGLEngineSharedShaders::PositionWithRadialGradientBrushVertexShader;
761 break;
762 case Qt::TexturePattern:
763 requiredProgram.srcPixelFragShader = QOpenGLEngineSharedShaders::TextureBrushSrcFragmentShader;
764 requiredProgram.positionVertexShader = isAffine ? QOpenGLEngineSharedShaders::AffinePositionWithTextureBrushVertexShader
765 : QOpenGLEngineSharedShaders::PositionWithTextureBrushVertexShader;
766 break;
767 };
768
769 if (useCustomSrc) {
770 requiredProgram.srcPixelFragShader = QOpenGLEngineSharedShaders::CustomImageSrcFragmentShader;
771 requiredProgram.customStageSource = customSrcStage->source();
772 }
773
774 const bool hasCompose = compositionMode > QPainter::CompositionMode_Plus;
775 const bool hasMask = maskType != QOpenGLEngineShaderManager::NoMask;
776
777 // Choose fragment shader main function:
778 if (opacityMode == AttributeOpacity) {
779 Q_ASSERT(!hasCompose && !hasMask);
780 requiredProgram.mainFragShader = QOpenGLEngineSharedShaders::MainFragmentShader_ImageArrays;
781 } else {
782 bool useGlobalOpacity = (opacityMode == UniformOpacity);
783 if (hasMask && useGlobalOpacity)
784 requiredProgram.mainFragShader = QOpenGLEngineSharedShaders::MainFragmentShader_MO;
785 if (hasMask && !useGlobalOpacity)
786 requiredProgram.mainFragShader = QOpenGLEngineSharedShaders::MainFragmentShader_M;
787 if (!hasMask && useGlobalOpacity)
788 requiredProgram.mainFragShader = QOpenGLEngineSharedShaders::MainFragmentShader_O;
789 if (!hasMask && !useGlobalOpacity)
790 requiredProgram.mainFragShader = QOpenGLEngineSharedShaders::MainFragmentShader;
791 }
792
793 if (hasMask) {
794 if (maskType == PixelMask) {
795 requiredProgram.maskFragShader = QOpenGLEngineSharedShaders::MaskFragmentShader;
796 texCoords = true;
797 } else if (maskType == SubPixelMaskPass1) {
798 requiredProgram.maskFragShader = QOpenGLEngineSharedShaders::RgbMaskFragmentShaderPass1;
799 texCoords = true;
800 } else if (maskType == SubPixelMaskPass2) {
801 requiredProgram.maskFragShader = QOpenGLEngineSharedShaders::RgbMaskFragmentShaderPass2;
802 texCoords = true;
803 } else if (maskType == SubPixelWithGammaMask) {
804 requiredProgram.maskFragShader = QOpenGLEngineSharedShaders::RgbMaskWithGammaFragmentShader;
805 texCoords = true;
806 } else {
807 qCritical("QOpenGLEngineShaderManager::useCorrectShaderProg() - Unknown mask type");
808 }
809 } else {
810 requiredProgram.maskFragShader = QOpenGLEngineSharedShaders::NoMaskFragmentShader;
811 }
812
813 if (hasCompose) {
814 switch (compositionMode) {
815 case QPainter::CompositionMode_Multiply:
816 requiredProgram.compositionFragShader = QOpenGLEngineSharedShaders::MultiplyCompositionModeFragmentShader;
817 break;
818 case QPainter::CompositionMode_Screen:
819 requiredProgram.compositionFragShader = QOpenGLEngineSharedShaders::ScreenCompositionModeFragmentShader;
820 break;
821 case QPainter::CompositionMode_Overlay:
822 requiredProgram.compositionFragShader = QOpenGLEngineSharedShaders::OverlayCompositionModeFragmentShader;
823 break;
824 case QPainter::CompositionMode_Darken:
825 requiredProgram.compositionFragShader = QOpenGLEngineSharedShaders::DarkenCompositionModeFragmentShader;
826 break;
827 case QPainter::CompositionMode_Lighten:
828 requiredProgram.compositionFragShader = QOpenGLEngineSharedShaders::LightenCompositionModeFragmentShader;
829 break;
830 case QPainter::CompositionMode_ColorDodge:
831 requiredProgram.compositionFragShader = QOpenGLEngineSharedShaders::ColorDodgeCompositionModeFragmentShader;
832 break;
833 case QPainter::CompositionMode_ColorBurn:
834 requiredProgram.compositionFragShader = QOpenGLEngineSharedShaders::ColorBurnCompositionModeFragmentShader;
835 break;
836 case QPainter::CompositionMode_HardLight:
837 requiredProgram.compositionFragShader = QOpenGLEngineSharedShaders::HardLightCompositionModeFragmentShader;
838 break;
839 case QPainter::CompositionMode_SoftLight:
840 requiredProgram.compositionFragShader = QOpenGLEngineSharedShaders::SoftLightCompositionModeFragmentShader;
841 break;
842 case QPainter::CompositionMode_Difference:
843 requiredProgram.compositionFragShader = QOpenGLEngineSharedShaders::DifferenceCompositionModeFragmentShader;
844 break;
845 case QPainter::CompositionMode_Exclusion:
846 requiredProgram.compositionFragShader = QOpenGLEngineSharedShaders::ExclusionCompositionModeFragmentShader;
847 break;
848 default:
849 qWarning("QOpenGLEngineShaderManager::useCorrectShaderProg() - Unsupported composition mode");
850 }
851 } else {
852 requiredProgram.compositionFragShader = QOpenGLEngineSharedShaders::NoCompositionModeFragmentShader;
853 }
854
855 // Choose vertex shader main function
856 if (opacityMode == AttributeOpacity) {
857 Q_ASSERT(texCoords);
858 requiredProgram.mainVertexShader = QOpenGLEngineSharedShaders::MainWithTexCoordsAndOpacityVertexShader;
859 } else if (texCoords) {
860 requiredProgram.mainVertexShader = QOpenGLEngineSharedShaders::MainWithTexCoordsVertexShader;
861 } else {
862 requiredProgram.mainVertexShader = QOpenGLEngineSharedShaders::MainVertexShader;
863 }
864 requiredProgram.useTextureCoords = texCoords;
865 requiredProgram.useOpacityAttribute = (opacityMode == AttributeOpacity);
866 if (complexGeometry && srcPixelType == Qt::SolidPattern) {
867 requiredProgram.positionVertexShader = QOpenGLEngineSharedShaders::ComplexGeometryPositionOnlyVertexShader;
868 requiredProgram.usePmvMatrixAttribute = false;
869 } else {
870 requiredProgram.usePmvMatrixAttribute = true;
871
872 // Force complexGeometry off, since we currently don't support that mode for
873 // non-solid brushes
874 complexGeometry = false;
875 }
876
877 // At this point, requiredProgram is fully populated so try to find the program in the cache
878 currentShaderProg = sharedShaders->findProgramInCache(requiredProgram);
879
880 if (currentShaderProg && useCustomSrc) {
881 customSrcStage->setUniforms(currentShaderProg->program);
882 }
883
884 // Make sure all the vertex attribute arrays the program uses are enabled (and the ones it
885 // doesn't use are disabled)
886 QOpenGLContextPrivate* ctx_d = ctx->d_func();
887 QOpenGL2PaintEngineEx *active_engine = static_cast<QOpenGL2PaintEngineEx *>(ctx_d->active_engine);
888 active_engine->d_func()->setVertexAttribArrayEnabled(QT_VERTEX_COORDS_ATTR, true);
889 active_engine->d_func()->setVertexAttribArrayEnabled(QT_TEXTURE_COORDS_ATTR, currentShaderProg && currentShaderProg->useTextureCoords);
890 active_engine->d_func()->setVertexAttribArrayEnabled(QT_OPACITY_ATTR, currentShaderProg && currentShaderProg->useOpacityAttribute);
891
892 shaderProgNeedsChanging = false;
893 return true;
894}
895
896QT_END_NAMESPACE
897