1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtGui module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qopenglengineshadermanager_p.h"
41#include "qopenglengineshadersource_p.h"
42#include "qopenglpaintengine_p.h"
43#include "qopenglshadercache_p.h"
44
45#include <QtGui/private/qopenglcontext_p.h>
46#include <QtCore/qthreadstorage.h>
47
48#include <algorithm>
49
50#if defined(QT_DEBUG)
51#include <QMetaEnum>
52#endif
53
54// #define QT_GL_SHARED_SHADER_DEBUG
55
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(x: 3,y: 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(msg: "Shader snippet for %s (#%d) is missing!",
250 snippetNameStr(snippetName: 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(s: qShaderSnippets[MainVertexShader]);
264 vertexSource.append(s: qShaderSnippets[PositionOnlyVertexShader]);
265#endif
266 fragSource.append(s: qShaderSnippets[MainFragmentShader]);
267 fragSource.append(s: 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(type: QOpenGLShader::Vertex, source: vertexSource))
277 qWarning(msg: "Vertex shader for simpleShaderProg (MainVertexShader & PositionOnlyVertexShader) failed to compile");
278 if (!simpleShaderProg->addCacheableShaderFromSourceCode(type: QOpenGLShader::Fragment, source: fragSource))
279 qWarning(msg: "Fragment shader for simpleShaderProg (MainFragmentShader & ShockingPinkSrcFragmentShader) failed to compile");
280
281 simpleShaderProg->bindAttributeLocation(name: "vertexCoordsArray", location: QT_VERTEX_COORDS_ATTR);
282 simpleShaderProg->bindAttributeLocation(name: "pmvMatrix1", location: QT_PMV_MATRIX_1_ATTR);
283 simpleShaderProg->bindAttributeLocation(name: "pmvMatrix2", location: QT_PMV_MATRIX_2_ATTR);
284 simpleShaderProg->bindAttributeLocation(name: "pmvMatrix3", location: QT_PMV_MATRIX_3_ATTR);
285 }
286
287 simpleShaderProg->link();
288
289 if (Q_UNLIKELY(!simpleShaderProg->isLinked())) {
290 qCritical(msg: "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(s: qShaderSnippets[MainWithTexCoordsVertexShader]);
299 vertexSource.append(s: qShaderSnippets[UntransformedPositionVertexShader]);
300
301 fragSource.clear();
302 fragSource.append(s: qShaderSnippets[MainFragmentShader]);
303 fragSource.append(s: 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(type: QOpenGLShader::Vertex, source: vertexSource))
313 qWarning(msg: "Vertex shader for blitShaderProg (MainWithTexCoordsVertexShader & UntransformedPositionVertexShader) failed to compile");
314 if (!blitShaderProg->addCacheableShaderFromSourceCode(type: QOpenGLShader::Fragment, source: fragSource))
315 qWarning(msg: "Fragment shader for blitShaderProg (MainFragmentShader & ImageSrcFragmentShader) failed to compile");
316
317 blitShaderProg->bindAttributeLocation(name: "textureCoordArray", location: QT_TEXTURE_COORDS_ATTR);
318 blitShaderProg->bindAttributeLocation(name: "vertexCoordsArray", location: QT_VERTEX_COORDS_ATTR);
319 }
320
321 blitShaderProg->link();
322 if (Q_UNLIKELY(!blitShaderProg->isLinked())) {
323 qCritical(msg: "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(c: 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(index: staticMetaObject.indexOfEnumerator(name: "SnippetName"));
357 return QByteArray(m.valueToKey(value: 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(from: i, to: 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(a: prog.customStageSource);
383 fragSource.append(s: qShaderSnippets[prog.mainFragShader]);
384 fragSource.append(s: qShaderSnippets[prog.srcPixelFragShader]);
385 if (prog.compositionFragShader)
386 fragSource.append(s: qShaderSnippets[prog.compositionFragShader]);
387 if (prog.maskFragShader)
388 fragSource.append(s: 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(s: qShaderSnippets[prog.mainVertexShader]);
396 vertexSource.append(s: 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(type: QOpenGLShader::Vertex, source: vertexSource)) {
405 QByteArray description;
406#if defined(QT_DEBUG)
407 description.append(s: "Vertex shader: main=");
408 description.append(a: snippetNameStr(name: prog.mainVertexShader));
409 description.append(s: ", position=");
410 description.append(a: snippetNameStr(name: prog.positionVertexShader));
411#endif
412 qWarning(msg: "Warning: \"%s\" failed to compile!", description.constData());
413 break;
414 }
415 if (!shaderProgram->addCacheableShaderFromSourceCode(type: QOpenGLShader::Fragment, source: fragSource)) {
416 QByteArray description;
417#if defined(QT_DEBUG)
418 description.append(s: "Fragment shader: main=");
419 description.append(a: snippetNameStr(name: prog.mainFragShader));
420 description.append(s: ", srcPixel=");
421 description.append(a: snippetNameStr(name: prog.srcPixelFragShader));
422 if (prog.compositionFragShader) {
423 description.append(s: ", composition=");
424 description.append(a: snippetNameStr(name: prog.compositionFragShader));
425 }
426 if (prog.maskFragShader) {
427 description.append(s: ", mask=");
428 description.append(a: snippetNameStr(name: prog.maskFragShader));
429 }
430#endif
431 qWarning(msg: "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(name: "vertexCoordsArray", location: QT_VERTEX_COORDS_ATTR);
437 if (prog.useTextureCoords)
438 shaderProgram->bindAttributeLocation(name: "textureCoordArray", location: QT_TEXTURE_COORDS_ATTR);
439 if (prog.useOpacityAttribute)
440 shaderProgram->bindAttributeLocation(name: "opacityArray", location: QT_OPACITY_ATTR);
441 if (prog.usePmvMatrixAttribute) {
442 shaderProgram->bindAttributeLocation(name: "pmvMatrix1", location: QT_PMV_MATRIX_1_ATTR);
443 shaderProgram->bindAttributeLocation(name: "pmvMatrix2", location: QT_PMV_MATRIX_2_ATTR);
444 shaderProgram->bindAttributeLocation(name: "pmvMatrix3", location: QT_PMV_MATRIX_3_ATTR);
445 }
446 }
447
448 newProg.reset(other: 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(name: "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(i: 0, t: 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.erase(afirst: std::remove_if(first: cachedPrograms.begin(), last: cachedPrograms.end(),
497 pred: hasStageAsCustomShaderSouce),
498 alast: cachedPrograms.end());
499}
500
501
502QOpenGLEngineShaderManager::QOpenGLEngineShaderManager(QOpenGLContext* context)
503 : ctx(context),
504 shaderProgNeedsChanging(true),
505 complexGeometry(false),
506 srcPixelType(Qt::NoBrush),
507 opacityMode(NoOpacity),
508 maskType(NoMask),
509 compositionMode(QPainter::CompositionMode_SourceOver),
510 customSrcStage(nullptr),
511 currentShaderProg(nullptr)
512{
513 sharedShaders = QOpenGLEngineSharedShaders::shadersForContext(context);
514}
515
516QOpenGLEngineShaderManager::~QOpenGLEngineShaderManager()
517{
518 //###
519 removeCustomStage();
520}
521
522GLuint QOpenGLEngineShaderManager::getUniformLocation(Uniform id)
523{
524 if (!currentShaderProg)
525 return 0;
526
527 QVector<uint> &uniformLocations = currentShaderProg->uniformLocations;
528 if (uniformLocations.isEmpty())
529 uniformLocations.fill(from: GLuint(-1), asize: NumUniforms);
530
531 const char uniformNames[][26] = {
532 "imageTexture",
533 "patternColor",
534 "globalOpacity",
535 "depth",
536 "maskTexture",
537 "fragmentColor",
538 "linearData",
539 "angle",
540 "halfViewportSize",
541 "fmp",
542 "fmp2_m_radius2",
543 "inverse_2_fmp2_m_radius2",
544 "sqrfr",
545 "bradius",
546 "invertedTextureSize",
547 "brushTransform",
548 "brushTexture",
549 "matrix"
550 };
551
552 if (uniformLocations.at(i: id) == GLuint(-1))
553 uniformLocations[id] = currentShaderProg->program->uniformLocation(name: uniformNames[id]);
554
555 return uniformLocations.at(i: id);
556}
557
558
559void QOpenGLEngineShaderManager::optimiseForBrushTransform(QTransform::TransformationType transformType)
560{
561 Q_UNUSED(transformType); // Currently ignored
562}
563
564void QOpenGLEngineShaderManager::setDirty()
565{
566 shaderProgNeedsChanging = true;
567}
568
569void QOpenGLEngineShaderManager::setSrcPixelType(Qt::BrushStyle style)
570{
571 Q_ASSERT(style != Qt::NoBrush);
572 if (srcPixelType == PixelSrcType(style))
573 return;
574
575 srcPixelType = style;
576 shaderProgNeedsChanging = true; //###
577}
578
579void QOpenGLEngineShaderManager::setSrcPixelType(PixelSrcType type)
580{
581 if (srcPixelType == type)
582 return;
583
584 srcPixelType = type;
585 shaderProgNeedsChanging = true; //###
586}
587
588void QOpenGLEngineShaderManager::setOpacityMode(OpacityMode mode)
589{
590 if (opacityMode == mode)
591 return;
592
593 opacityMode = mode;
594 shaderProgNeedsChanging = true; //###
595}
596
597void QOpenGLEngineShaderManager::setMaskType(MaskType type)
598{
599 if (maskType == type)
600 return;
601
602 maskType = type;
603 shaderProgNeedsChanging = true; //###
604}
605
606void QOpenGLEngineShaderManager::setCompositionMode(QPainter::CompositionMode mode)
607{
608 if (compositionMode == mode)
609 return;
610
611 bool wasAdvanced = compositionMode > QPainter::CompositionMode_Plus;
612 bool isAdvanced = mode > QPainter::CompositionMode_Plus;
613
614 compositionMode = mode;
615 shaderProgNeedsChanging = shaderProgNeedsChanging || wasAdvanced || isAdvanced;
616}
617
618void QOpenGLEngineShaderManager::setCustomStage(QOpenGLCustomShaderStage* stage)
619{
620 if (customSrcStage)
621 removeCustomStage();
622 customSrcStage = stage;
623 shaderProgNeedsChanging = true;
624}
625
626void QOpenGLEngineShaderManager::removeCustomStage()
627{
628 if (customSrcStage)
629 customSrcStage->setInactive();
630 customSrcStage = nullptr;
631 shaderProgNeedsChanging = true;
632}
633
634QOpenGLShaderProgram* QOpenGLEngineShaderManager::currentProgram()
635{
636 if (currentShaderProg)
637 return currentShaderProg->program;
638 else
639 return sharedShaders->simpleProgram();
640}
641
642void QOpenGLEngineShaderManager::useSimpleProgram()
643{
644 sharedShaders->simpleProgram()->bind();
645 QOpenGLContextPrivate* ctx_d = ctx->d_func();
646 Q_UNUSED(ctx_d);
647
648 QOpenGL2PaintEngineEx *active_engine = static_cast<QOpenGL2PaintEngineEx *>(ctx_d->active_engine);
649
650 active_engine->d_func()->setVertexAttribArrayEnabled(arrayIndex: QT_VERTEX_COORDS_ATTR, enabled: true);
651 active_engine->d_func()->setVertexAttribArrayEnabled(arrayIndex: QT_TEXTURE_COORDS_ATTR, enabled: false);
652 active_engine->d_func()->setVertexAttribArrayEnabled(arrayIndex: QT_OPACITY_ATTR, enabled: false);
653
654 shaderProgNeedsChanging = true;
655}
656
657void QOpenGLEngineShaderManager::useBlitProgram()
658{
659 sharedShaders->blitProgram()->bind();
660 QOpenGLContextPrivate* ctx_d = ctx->d_func();
661 QOpenGL2PaintEngineEx *active_engine = static_cast<QOpenGL2PaintEngineEx *>(ctx_d->active_engine);
662 active_engine->d_func()->setVertexAttribArrayEnabled(arrayIndex: QT_VERTEX_COORDS_ATTR, enabled: true);
663 active_engine->d_func()->setVertexAttribArrayEnabled(arrayIndex: QT_TEXTURE_COORDS_ATTR, enabled: true);
664 active_engine->d_func()->setVertexAttribArrayEnabled(arrayIndex: QT_OPACITY_ATTR, enabled: false);
665 shaderProgNeedsChanging = true;
666}
667
668QOpenGLShaderProgram* QOpenGLEngineShaderManager::simpleProgram()
669{
670 return sharedShaders->simpleProgram();
671}
672
673QOpenGLShaderProgram* QOpenGLEngineShaderManager::blitProgram()
674{
675 return sharedShaders->blitProgram();
676}
677
678
679
680// Select & use the correct shader program using the current state.
681// Returns \c true if program needed changing.
682bool QOpenGLEngineShaderManager::useCorrectShaderProg()
683{
684 if (!shaderProgNeedsChanging)
685 return false;
686
687 bool useCustomSrc = customSrcStage != nullptr;
688 if (useCustomSrc && srcPixelType != QOpenGLEngineShaderManager::ImageSrc && srcPixelType != Qt::TexturePattern) {
689 useCustomSrc = false;
690 qWarning(msg: "QOpenGLEngineShaderManager - Ignoring custom shader stage for non image src");
691 }
692
693 QOpenGLEngineShaderProg requiredProgram;
694
695 bool texCoords = false;
696
697 // Choose vertex shader shader position function (which typically also sets
698 // varyings) and the source pixel (srcPixel) fragment shader function:
699 requiredProgram.positionVertexShader = QOpenGLEngineSharedShaders::InvalidSnippetName;
700 requiredProgram.srcPixelFragShader = QOpenGLEngineSharedShaders::InvalidSnippetName;
701 bool isAffine = brushTransform.isAffine();
702 if ( (srcPixelType >= Qt::Dense1Pattern) && (srcPixelType <= Qt::DiagCrossPattern) ) {
703 if (isAffine)
704 requiredProgram.positionVertexShader = QOpenGLEngineSharedShaders::AffinePositionWithPatternBrushVertexShader;
705 else
706 requiredProgram.positionVertexShader = QOpenGLEngineSharedShaders::PositionWithPatternBrushVertexShader;
707
708 requiredProgram.srcPixelFragShader = QOpenGLEngineSharedShaders::PatternBrushSrcFragmentShader;
709 }
710 else switch (srcPixelType) {
711 default:
712 case Qt::NoBrush:
713 qFatal(msg: "QOpenGLEngineShaderManager::useCorrectShaderProg() - Qt::NoBrush style is set");
714 break;
715 case QOpenGLEngineShaderManager::ImageSrc:
716 requiredProgram.srcPixelFragShader = QOpenGLEngineSharedShaders::ImageSrcFragmentShader;
717 requiredProgram.positionVertexShader = QOpenGLEngineSharedShaders::PositionOnlyVertexShader;
718 texCoords = true;
719 break;
720 case QOpenGLEngineShaderManager::NonPremultipliedImageSrc:
721 requiredProgram.srcPixelFragShader = QOpenGLEngineSharedShaders::NonPremultipliedImageSrcFragmentShader;
722 requiredProgram.positionVertexShader = QOpenGLEngineSharedShaders::PositionOnlyVertexShader;
723 texCoords = true;
724 break;
725 case QOpenGLEngineShaderManager::GrayscaleImageSrc:
726 requiredProgram.srcPixelFragShader = QOpenGLEngineSharedShaders::GrayscaleImageSrcFragmentShader;
727 requiredProgram.positionVertexShader = QOpenGLEngineSharedShaders::PositionOnlyVertexShader;
728 texCoords = true;
729 break;
730 case QOpenGLEngineShaderManager::AlphaImageSrc:
731 requiredProgram.srcPixelFragShader = QOpenGLEngineSharedShaders::AlphaImageSrcFragmentShader;
732 requiredProgram.positionVertexShader = QOpenGLEngineSharedShaders::PositionOnlyVertexShader;
733 texCoords = true;
734 break;
735 case QOpenGLEngineShaderManager::PatternSrc:
736 requiredProgram.srcPixelFragShader = QOpenGLEngineSharedShaders::ImageSrcWithPatternFragmentShader;
737 requiredProgram.positionVertexShader = QOpenGLEngineSharedShaders::PositionOnlyVertexShader;
738 texCoords = true;
739 break;
740 case QOpenGLEngineShaderManager::TextureSrcWithPattern:
741 requiredProgram.srcPixelFragShader = QOpenGLEngineSharedShaders::TextureBrushSrcWithPatternFragmentShader;
742 requiredProgram.positionVertexShader = isAffine ? QOpenGLEngineSharedShaders::AffinePositionWithTextureBrushVertexShader
743 : QOpenGLEngineSharedShaders::PositionWithTextureBrushVertexShader;
744 break;
745 case Qt::SolidPattern:
746 requiredProgram.srcPixelFragShader = QOpenGLEngineSharedShaders::SolidBrushSrcFragmentShader;
747 requiredProgram.positionVertexShader = QOpenGLEngineSharedShaders::PositionOnlyVertexShader;
748 break;
749 case Qt::LinearGradientPattern:
750 requiredProgram.srcPixelFragShader = QOpenGLEngineSharedShaders::LinearGradientBrushSrcFragmentShader;
751 requiredProgram.positionVertexShader = isAffine ? QOpenGLEngineSharedShaders::AffinePositionWithLinearGradientBrushVertexShader
752 : QOpenGLEngineSharedShaders::PositionWithLinearGradientBrushVertexShader;
753 break;
754 case Qt::ConicalGradientPattern:
755 requiredProgram.srcPixelFragShader = QOpenGLEngineSharedShaders::ConicalGradientBrushSrcFragmentShader;
756 requiredProgram.positionVertexShader = isAffine ? QOpenGLEngineSharedShaders::AffinePositionWithConicalGradientBrushVertexShader
757 : QOpenGLEngineSharedShaders::PositionWithConicalGradientBrushVertexShader;
758 break;
759 case Qt::RadialGradientPattern:
760 requiredProgram.srcPixelFragShader = QOpenGLEngineSharedShaders::RadialGradientBrushSrcFragmentShader;
761 requiredProgram.positionVertexShader = isAffine ? QOpenGLEngineSharedShaders::AffinePositionWithRadialGradientBrushVertexShader
762 : QOpenGLEngineSharedShaders::PositionWithRadialGradientBrushVertexShader;
763 break;
764 case Qt::TexturePattern:
765 requiredProgram.srcPixelFragShader = QOpenGLEngineSharedShaders::TextureBrushSrcFragmentShader;
766 requiredProgram.positionVertexShader = isAffine ? QOpenGLEngineSharedShaders::AffinePositionWithTextureBrushVertexShader
767 : QOpenGLEngineSharedShaders::PositionWithTextureBrushVertexShader;
768 break;
769 };
770
771 if (useCustomSrc) {
772 requiredProgram.srcPixelFragShader = QOpenGLEngineSharedShaders::CustomImageSrcFragmentShader;
773 requiredProgram.customStageSource = customSrcStage->source();
774 }
775
776 const bool hasCompose = compositionMode > QPainter::CompositionMode_Plus;
777 const bool hasMask = maskType != QOpenGLEngineShaderManager::NoMask;
778
779 // Choose fragment shader main function:
780 if (opacityMode == AttributeOpacity) {
781 Q_ASSERT(!hasCompose && !hasMask);
782 requiredProgram.mainFragShader = QOpenGLEngineSharedShaders::MainFragmentShader_ImageArrays;
783 } else {
784 bool useGlobalOpacity = (opacityMode == UniformOpacity);
785 if (hasMask && useGlobalOpacity)
786 requiredProgram.mainFragShader = QOpenGLEngineSharedShaders::MainFragmentShader_MO;
787 if (hasMask && !useGlobalOpacity)
788 requiredProgram.mainFragShader = QOpenGLEngineSharedShaders::MainFragmentShader_M;
789 if (!hasMask && useGlobalOpacity)
790 requiredProgram.mainFragShader = QOpenGLEngineSharedShaders::MainFragmentShader_O;
791 if (!hasMask && !useGlobalOpacity)
792 requiredProgram.mainFragShader = QOpenGLEngineSharedShaders::MainFragmentShader;
793 }
794
795 if (hasMask) {
796 if (maskType == PixelMask) {
797 requiredProgram.maskFragShader = QOpenGLEngineSharedShaders::MaskFragmentShader;
798 texCoords = true;
799 } else if (maskType == SubPixelMaskPass1) {
800 requiredProgram.maskFragShader = QOpenGLEngineSharedShaders::RgbMaskFragmentShaderPass1;
801 texCoords = true;
802 } else if (maskType == SubPixelMaskPass2) {
803 requiredProgram.maskFragShader = QOpenGLEngineSharedShaders::RgbMaskFragmentShaderPass2;
804 texCoords = true;
805 } else if (maskType == SubPixelWithGammaMask) {
806 requiredProgram.maskFragShader = QOpenGLEngineSharedShaders::RgbMaskWithGammaFragmentShader;
807 texCoords = true;
808 } else {
809 qCritical(msg: "QOpenGLEngineShaderManager::useCorrectShaderProg() - Unknown mask type");
810 }
811 } else {
812 requiredProgram.maskFragShader = QOpenGLEngineSharedShaders::NoMaskFragmentShader;
813 }
814
815 if (hasCompose) {
816 switch (compositionMode) {
817 case QPainter::CompositionMode_Multiply:
818 requiredProgram.compositionFragShader = QOpenGLEngineSharedShaders::MultiplyCompositionModeFragmentShader;
819 break;
820 case QPainter::CompositionMode_Screen:
821 requiredProgram.compositionFragShader = QOpenGLEngineSharedShaders::ScreenCompositionModeFragmentShader;
822 break;
823 case QPainter::CompositionMode_Overlay:
824 requiredProgram.compositionFragShader = QOpenGLEngineSharedShaders::OverlayCompositionModeFragmentShader;
825 break;
826 case QPainter::CompositionMode_Darken:
827 requiredProgram.compositionFragShader = QOpenGLEngineSharedShaders::DarkenCompositionModeFragmentShader;
828 break;
829 case QPainter::CompositionMode_Lighten:
830 requiredProgram.compositionFragShader = QOpenGLEngineSharedShaders::LightenCompositionModeFragmentShader;
831 break;
832 case QPainter::CompositionMode_ColorDodge:
833 requiredProgram.compositionFragShader = QOpenGLEngineSharedShaders::ColorDodgeCompositionModeFragmentShader;
834 break;
835 case QPainter::CompositionMode_ColorBurn:
836 requiredProgram.compositionFragShader = QOpenGLEngineSharedShaders::ColorBurnCompositionModeFragmentShader;
837 break;
838 case QPainter::CompositionMode_HardLight:
839 requiredProgram.compositionFragShader = QOpenGLEngineSharedShaders::HardLightCompositionModeFragmentShader;
840 break;
841 case QPainter::CompositionMode_SoftLight:
842 requiredProgram.compositionFragShader = QOpenGLEngineSharedShaders::SoftLightCompositionModeFragmentShader;
843 break;
844 case QPainter::CompositionMode_Difference:
845 requiredProgram.compositionFragShader = QOpenGLEngineSharedShaders::DifferenceCompositionModeFragmentShader;
846 break;
847 case QPainter::CompositionMode_Exclusion:
848 requiredProgram.compositionFragShader = QOpenGLEngineSharedShaders::ExclusionCompositionModeFragmentShader;
849 break;
850 default:
851 qWarning(msg: "QOpenGLEngineShaderManager::useCorrectShaderProg() - Unsupported composition mode");
852 }
853 } else {
854 requiredProgram.compositionFragShader = QOpenGLEngineSharedShaders::NoCompositionModeFragmentShader;
855 }
856
857 // Choose vertex shader main function
858 if (opacityMode == AttributeOpacity) {
859 Q_ASSERT(texCoords);
860 requiredProgram.mainVertexShader = QOpenGLEngineSharedShaders::MainWithTexCoordsAndOpacityVertexShader;
861 } else if (texCoords) {
862 requiredProgram.mainVertexShader = QOpenGLEngineSharedShaders::MainWithTexCoordsVertexShader;
863 } else {
864 requiredProgram.mainVertexShader = QOpenGLEngineSharedShaders::MainVertexShader;
865 }
866 requiredProgram.useTextureCoords = texCoords;
867 requiredProgram.useOpacityAttribute = (opacityMode == AttributeOpacity);
868 if (complexGeometry && srcPixelType == Qt::SolidPattern) {
869 requiredProgram.positionVertexShader = QOpenGLEngineSharedShaders::ComplexGeometryPositionOnlyVertexShader;
870 requiredProgram.usePmvMatrixAttribute = false;
871 } else {
872 requiredProgram.usePmvMatrixAttribute = true;
873
874 // Force complexGeometry off, since we currently don't support that mode for
875 // non-solid brushes
876 complexGeometry = false;
877 }
878
879 // At this point, requiredProgram is fully populated so try to find the program in the cache
880 currentShaderProg = sharedShaders->findProgramInCache(prog: requiredProgram);
881
882 if (currentShaderProg && useCustomSrc) {
883 customSrcStage->setUniforms(currentShaderProg->program);
884 }
885
886 // Make sure all the vertex attribute arrays the program uses are enabled (and the ones it
887 // doesn't use are disabled)
888 QOpenGLContextPrivate* ctx_d = ctx->d_func();
889 QOpenGL2PaintEngineEx *active_engine = static_cast<QOpenGL2PaintEngineEx *>(ctx_d->active_engine);
890 active_engine->d_func()->setVertexAttribArrayEnabled(arrayIndex: QT_VERTEX_COORDS_ATTR, enabled: true);
891 active_engine->d_func()->setVertexAttribArrayEnabled(arrayIndex: QT_TEXTURE_COORDS_ATTR, enabled: currentShaderProg && currentShaderProg->useTextureCoords);
892 active_engine->d_func()->setVertexAttribArrayEnabled(arrayIndex: QT_OPACITY_ATTR, enabled: currentShaderProg && currentShaderProg->useOpacityAttribute);
893
894 shaderProgNeedsChanging = false;
895 return true;
896}
897
898QT_END_NAMESPACE
899

source code of qtbase/src/gui/opengl/qopenglengineshadermanager.cpp