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

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