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