1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtOpenGL module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40//
41// W A R N I N G
42// -------------
43//
44// This file is not part of the Qt API. It exists purely as an
45// implementation detail. This header file may change from version to
46// version without notice, or even be removed.
47//
48// We mean it.
49//
50
51/*
52 VERTEX SHADERS
53 ==============
54
55 Vertex shaders are specified as multiple (partial) shaders. On desktop,
56 this works fine. On ES, QGLShader & QGLShaderProgram will make partial
57 shaders work by concatenating the source in each QGLShader and compiling
58 it as a single shader. This is abstracted nicely by QGLShaderProgram and
59 the GL2 engine doesn't need to worry about it.
60
61 Generally, there's two vertex shader objects. The position shaders are
62 the ones which set gl_Position. There's also two "main" vertex shaders,
63 one which just calls the position shader and another which also passes
64 through some texture coordinates from a vertex attribute array to a
65 varying. These texture coordinates are used for mask position in text
66 rendering and for the source coordinates in drawImage/drawPixmap. There's
67 also a "Simple" vertex shader for rendering a solid colour (used to render
68 into the stencil buffer where the actual colour value is discarded).
69
70 The position shaders for brushes look scary. This is because many of the
71 calculations which logically belong in the fragment shader have been moved
72 into the vertex shader to improve performance. This is why the position
73 calculation is in a separate shader. Not only does it calculate the
74 position, but it also calculates some data to be passed to the fragment
75 shader as a varying. It is optimal to move as much of the calculation as
76 possible into the vertex shader as this is executed less often.
77
78 The varyings passed to the fragment shaders are interpolated (which is
79 cheap). Unfortunately, GL will apply perspective correction to the
80 interpolation calusing errors. To get around this, the vertex shader must
81 apply perspective correction itself and set the w-value of gl_Position to
82 zero. That way, GL will be tricked into thinking it doesn't need to apply a
83 perspective correction and use linear interpolation instead (which is what
84 we want). Of course, if the brush transform is affeine, no perspective
85 correction is needed and a simpler vertex shader can be used instead.
86
87 So there are the following "main" vertex shaders:
88 qglslMainVertexShader
89 qglslMainWithTexCoordsVertexShader
90
91 And the following position vertex shaders:
92 qglslPositionOnlyVertexShader
93 qglslPositionWithTextureBrushVertexShader
94 qglslPositionWithPatternBrushVertexShader
95 qglslPositionWithLinearGradientBrushVertexShader
96 qglslPositionWithRadialGradientBrushVertexShader
97 qglslPositionWithConicalGradientBrushVertexShader
98 qglslAffinePositionWithTextureBrushVertexShader
99 qglslAffinePositionWithPatternBrushVertexShader
100 qglslAffinePositionWithLinearGradientBrushVertexShader
101 qglslAffinePositionWithRadialGradientBrushVertexShader
102 qglslAffinePositionWithConicalGradientBrushVertexShader
103
104 Leading to 23 possible vertex shaders
105
106
107 FRAGMENT SHADERS
108 ================
109
110 Fragment shaders are also specified as multiple (partial) shaders. The
111 different fragment shaders represent the different stages in Qt's fragment
112 pipeline. There are 1-3 stages in this pipeline: First stage is to get the
113 fragment's colour value. The next stage is to get the fragment's mask value
114 (coverage value for anti-aliasing) and the final stage is to blend the
115 incoming fragment with the background (for composition modes not supported
116 by GL).
117
118 Of these, the first stage will always be present. If Qt doesn't need to
119 apply anti-aliasing (because it's off or handled by multisampling) then
120 the coverage value doesn't need to be applied. (Note: There are two types
121 of mask, one for regular anti-aliasing and one for sub-pixel anti-
122 aliasing.) If the composition mode is one which GL supports natively then
123 the blending stage doesn't need to be applied.
124
125 As eash stage can have multiple implementations, they are abstracted as
126 GLSL function calls with the following signatures:
127
128 Brushes & image drawing are implementations of "qcolorp vec4 srcPixel()":
129 qglslImageSrcFragShader
130 qglslImageSrcWithPatternFragShader
131 qglslNonPremultipliedImageSrcFragShader
132 qglslSolidBrushSrcFragShader
133 qglslTextureBrushSrcFragShader
134 qglslTextureBrushWithPatternFragShader
135 qglslPatternBrushSrcFragShader
136 qglslLinearGradientBrushSrcFragShader
137 qglslRadialGradientBrushSrcFragShader
138 qglslConicalGradientBrushSrcFragShader
139 NOTE: It is assumed the colour returned by srcPixel() is pre-multiplied
140
141 Masks are implementations of "qcolorp vec4 applyMask(qcolorp vec4 src)":
142 qglslMaskFragmentShader
143 qglslRgbMaskFragmentShaderPass1
144 qglslRgbMaskFragmentShaderPass2
145 qglslRgbMaskWithGammaFragmentShader
146
147 Composition modes are "qcolorp vec4 compose(qcolorp vec4 src)":
148 qglslColorBurnCompositionModeFragmentShader
149 qglslColorDodgeCompositionModeFragmentShader
150 qglslDarkenCompositionModeFragmentShader
151 qglslDifferenceCompositionModeFragmentShader
152 qglslExclusionCompositionModeFragmentShader
153 qglslHardLightCompositionModeFragmentShader
154 qglslLightenCompositionModeFragmentShader
155 qglslMultiplyCompositionModeFragmentShader
156 qglslOverlayCompositionModeFragmentShader
157 qglslScreenCompositionModeFragmentShader
158 qglslSoftLightCompositionModeFragmentShader
159
160
161 Note: In the future, some GLSL compilers will support an extension allowing
162 a new 'color' precision specifier. To support this, qcolorp is used for
163 all color components so it can be defined to colorp or lowp depending upon
164 the implementation.
165
166 So there are differnt frament shader main functions, depending on the
167 number & type of pipelines the fragment needs to go through.
168
169 The choice of which main() fragment shader string to use depends on:
170 - Use of global opacity
171 - Brush style (some brushes apply opacity themselves)
172 - Use & type of mask (TODO: Need to support high quality anti-aliasing & text)
173 - Use of non-GL Composition mode
174
175 Leading to the following fragment shader main functions:
176 gl_FragColor = compose(applyMask(srcPixel()*globalOpacity));
177 gl_FragColor = compose(applyMask(srcPixel()));
178 gl_FragColor = applyMask(srcPixel()*globalOpacity);
179 gl_FragColor = applyMask(srcPixel());
180 gl_FragColor = compose(srcPixel()*globalOpacity);
181 gl_FragColor = compose(srcPixel());
182 gl_FragColor = srcPixel()*globalOpacity;
183 gl_FragColor = srcPixel();
184
185 Called:
186 qglslMainFragmentShader_CMO
187 qglslMainFragmentShader_CM
188 qglslMainFragmentShader_MO
189 qglslMainFragmentShader_M
190 qglslMainFragmentShader_CO
191 qglslMainFragmentShader_C
192 qglslMainFragmentShader_O
193 qglslMainFragmentShader
194
195 Where:
196 M = Mask
197 C = Composition
198 O = Global Opacity
199
200
201 CUSTOM SHADER CODE
202 ==================
203
204 The use of custom shader code is supported by the engine for drawImage and
205 drawPixmap calls. This is implemented via hooks in the fragment pipeline.
206
207 The custom shader is passed to the engine as a partial fragment shader
208 (QGLCustomShaderStage). The shader will implement a pre-defined method name
209 which Qt's fragment pipeline will call:
210
211 lowp vec4 customShader(lowp sampler2d imageTexture, highp vec2 textureCoords)
212
213 The provided src and srcCoords parameters can be used to sample from the
214 source image.
215
216 Transformations, clipping, opacity, and composition modes set using QPainter
217 will be respected when using the custom shader hook.
218*/
219
220#ifndef QGLENGINE_SHADER_MANAGER_H
221#define QGLENGINE_SHADER_MANAGER_H
222
223#include <QGLShader>
224#include <QGLShaderProgram>
225#include <QPainter>
226#include <private/qgl_p.h>
227#include <private/qglcustomshaderstage_p.h>
228
229QT_BEGIN_NAMESPACE
230
231
232
233/*
234struct QGLEngineCachedShaderProg
235{
236 QGLEngineCachedShaderProg(QGLEngineShaderManager::ShaderName vertexMain,
237 QGLEngineShaderManager::ShaderName vertexPosition,
238 QGLEngineShaderManager::ShaderName fragMain,
239 QGLEngineShaderManager::ShaderName pixelSrc,
240 QGLEngineShaderManager::ShaderName mask,
241 QGLEngineShaderManager::ShaderName composition);
242
243 int cacheKey;
244 QGLShaderProgram* program;
245}
246*/
247
248static const GLuint QT_VERTEX_COORDS_ATTR = 0;
249static const GLuint QT_TEXTURE_COORDS_ATTR = 1;
250static const GLuint QT_OPACITY_ATTR = 2;
251static const GLuint QT_PMV_MATRIX_1_ATTR = 3;
252static const GLuint QT_PMV_MATRIX_2_ATTR = 4;
253static const GLuint QT_PMV_MATRIX_3_ATTR = 5;
254
255class QGLEngineShaderProg;
256
257class Q_OPENGL_EXPORT QGLEngineSharedShaders
258{
259 Q_GADGET
260public:
261
262 enum SnippetName {
263 MainVertexShader,
264 MainWithTexCoordsVertexShader,
265 MainWithTexCoordsAndOpacityVertexShader,
266
267 // UntransformedPositionVertexShader must be first in the list:
268 UntransformedPositionVertexShader,
269 PositionOnlyVertexShader,
270 ComplexGeometryPositionOnlyVertexShader,
271 PositionWithPatternBrushVertexShader,
272 PositionWithLinearGradientBrushVertexShader,
273 PositionWithConicalGradientBrushVertexShader,
274 PositionWithRadialGradientBrushVertexShader,
275 PositionWithTextureBrushVertexShader,
276 AffinePositionWithPatternBrushVertexShader,
277 AffinePositionWithLinearGradientBrushVertexShader,
278 AffinePositionWithConicalGradientBrushVertexShader,
279 AffinePositionWithRadialGradientBrushVertexShader,
280 AffinePositionWithTextureBrushVertexShader,
281
282 // MainFragmentShader_CMO must be first in the list:
283 MainFragmentShader_CMO,
284 MainFragmentShader_CM,
285 MainFragmentShader_MO,
286 MainFragmentShader_M,
287 MainFragmentShader_CO,
288 MainFragmentShader_C,
289 MainFragmentShader_O,
290 MainFragmentShader,
291 MainFragmentShader_ImageArrays,
292
293 // ImageSrcFragmentShader must be first in the list::
294 ImageSrcFragmentShader,
295 ImageSrcWithPatternFragmentShader,
296 NonPremultipliedImageSrcFragmentShader,
297 CustomImageSrcFragmentShader,
298 SolidBrushSrcFragmentShader,
299 TextureBrushSrcFragmentShader,
300 TextureBrushSrcWithPatternFragmentShader,
301 PatternBrushSrcFragmentShader,
302 LinearGradientBrushSrcFragmentShader,
303 RadialGradientBrushSrcFragmentShader,
304 ConicalGradientBrushSrcFragmentShader,
305 ShockingPinkSrcFragmentShader,
306
307 // NoMaskFragmentShader must be first in the list:
308 NoMaskFragmentShader,
309 MaskFragmentShader,
310 RgbMaskFragmentShaderPass1,
311 RgbMaskFragmentShaderPass2,
312 RgbMaskWithGammaFragmentShader,
313
314 // NoCompositionModeFragmentShader must be first in the list:
315 NoCompositionModeFragmentShader,
316 MultiplyCompositionModeFragmentShader,
317 ScreenCompositionModeFragmentShader,
318 OverlayCompositionModeFragmentShader,
319 DarkenCompositionModeFragmentShader,
320 LightenCompositionModeFragmentShader,
321 ColorDodgeCompositionModeFragmentShader,
322 ColorBurnCompositionModeFragmentShader,
323 HardLightCompositionModeFragmentShader,
324 SoftLightCompositionModeFragmentShader,
325 DifferenceCompositionModeFragmentShader,
326 ExclusionCompositionModeFragmentShader,
327
328 TotalSnippetCount, InvalidSnippetName
329 };
330#if defined (QT_DEBUG)
331 Q_ENUMS(SnippetName)
332 static QByteArray snippetNameStr(SnippetName snippetName);
333#endif
334
335/*
336 // These allow the ShaderName enum to be used as a cache key
337 const int mainVertexOffset = 0;
338 const int positionVertexOffset = (1<<2) - PositionOnlyVertexShader;
339 const int mainFragOffset = (1<<6) - MainFragmentShader_CMO;
340 const int srcPixelOffset = (1<<10) - ImageSrcFragmentShader;
341 const int maskOffset = (1<<14) - NoMaskShader;
342 const int compositionOffset = (1 << 16) - MultiplyCompositionModeFragmentShader;
343*/
344
345 QGLEngineSharedShaders(const QGLContext *context);
346 ~QGLEngineSharedShaders();
347
348 QGLShaderProgram *simpleProgram() { return simpleShaderProg; }
349 QGLShaderProgram *blitProgram() { return blitShaderProg; }
350 // Compile the program if it's not already in the cache, return the item in the cache.
351 QGLEngineShaderProg *findProgramInCache(const QGLEngineShaderProg &prog);
352 // Compile the custom shader if it's not already in the cache, return the item in the cache.
353
354 static QGLEngineSharedShaders *shadersForContext(const QGLContext *context);
355
356 // Ideally, this would be static and cleanup all programs in all contexts which
357 // contain the custom code. Currently it is just a hint and we rely on deleted
358 // custom shaders being cleaned up by being kicked out of the cache when it's
359 // full.
360 void cleanupCustomStage(QGLCustomShaderStage* stage);
361
362private:
363 QGLShaderProgram *blitShaderProg;
364 QGLShaderProgram *simpleShaderProg;
365 QList<QGLEngineShaderProg*> cachedPrograms;
366 QList<QGLShader *> shaders;
367
368 static const char* qShaderSnippets[TotalSnippetCount];
369};
370
371
372class QGLEngineShaderProg
373{
374public:
375 QGLEngineShaderProg() : program(nullptr) {}
376
377 ~QGLEngineShaderProg() {
378 if (program)
379 delete program;
380 }
381
382 QGLEngineSharedShaders::SnippetName mainVertexShader;
383 QGLEngineSharedShaders::SnippetName positionVertexShader;
384 QGLEngineSharedShaders::SnippetName mainFragShader;
385 QGLEngineSharedShaders::SnippetName srcPixelFragShader;
386 QGLEngineSharedShaders::SnippetName maskFragShader;
387 QGLEngineSharedShaders::SnippetName compositionFragShader;
388
389 QByteArray customStageSource; //TODO: Decent cache key for custom stages
390 QGLShaderProgram* program;
391
392 QVector<uint> uniformLocations;
393
394 bool useTextureCoords;
395 bool useOpacityAttribute;
396 bool usePmvMatrixAttribute;
397
398 bool operator==(const QGLEngineShaderProg& other) const {
399 // We don't care about the program
400 return ( mainVertexShader == other.mainVertexShader &&
401 positionVertexShader == other.positionVertexShader &&
402 mainFragShader == other.mainFragShader &&
403 srcPixelFragShader == other.srcPixelFragShader &&
404 maskFragShader == other.maskFragShader &&
405 compositionFragShader == other.compositionFragShader &&
406 customStageSource == other.customStageSource
407 );
408 }
409};
410
411class Q_OPENGL_EXPORT QGLEngineShaderManager : public QObject
412{
413 Q_OBJECT
414public:
415 QGLEngineShaderManager(QGLContext* context);
416 ~QGLEngineShaderManager();
417
418 enum MaskType {NoMask, PixelMask, SubPixelMaskPass1, SubPixelMaskPass2, SubPixelWithGammaMask};
419 enum PixelSrcType {
420 ImageSrc = Qt::TexturePattern+1,
421 NonPremultipliedImageSrc = Qt::TexturePattern+2,
422 PatternSrc = Qt::TexturePattern+3,
423 TextureSrcWithPattern = Qt::TexturePattern+4
424 };
425
426 enum Uniform {
427 ImageTexture,
428 PatternColor,
429 GlobalOpacity,
430 Depth,
431 MaskTexture,
432 FragmentColor,
433 LinearData,
434 Angle,
435 HalfViewportSize,
436 Fmp,
437 Fmp2MRadius2,
438 Inverse2Fmp2MRadius2,
439 SqrFr,
440 BRadius,
441 InvertedTextureSize,
442 BrushTransform,
443 BrushTexture,
444 Matrix,
445 TranslateZ,
446 NumUniforms
447 };
448
449 enum OpacityMode {
450 NoOpacity,
451 UniformOpacity,
452 AttributeOpacity
453 };
454
455 // There are optimizations we can do, depending on the brush transform:
456 // 1) May not have to apply perspective-correction
457 // 2) Can use lower precision for matrix
458 void optimiseForBrushTransform(QTransform::TransformationType transformType);
459 void setSrcPixelType(Qt::BrushStyle);
460 void setSrcPixelType(PixelSrcType); // For non-brush sources, like pixmaps & images
461 void setOpacityMode(OpacityMode);
462 void setMaskType(MaskType);
463 void setCompositionMode(QPainter::CompositionMode);
464 void setCustomStage(QGLCustomShaderStage* stage);
465 void removeCustomStage();
466
467 GLuint getUniformLocation(Uniform id);
468
469 void setDirty(); // someone has manually changed the current shader program
470 bool useCorrectShaderProg(); // returns true if the shader program needed to be changed
471
472 void useSimpleProgram();
473 void useBlitProgram();
474 void setHasComplexGeometry(bool hasComplexGeometry)
475 {
476 complexGeometry = hasComplexGeometry;
477 shaderProgNeedsChanging = true;
478 }
479 bool hasComplexGeometry() const
480 {
481 return complexGeometry;
482 }
483
484 QGLShaderProgram* currentProgram(); // Returns pointer to the shader the manager has chosen
485 QGLShaderProgram* simpleProgram(); // Used to draw into e.g. stencil buffers
486 QGLShaderProgram* blitProgram(); // Used to blit a texture into the framebuffer
487
488 QGLEngineSharedShaders* sharedShaders;
489
490private:
491 QGLContext* ctx;
492 bool shaderProgNeedsChanging;
493 bool complexGeometry;
494
495 // Current state variables which influence the choice of shader:
496 QTransform brushTransform;
497 int srcPixelType;
498 OpacityMode opacityMode;
499 MaskType maskType;
500 QPainter::CompositionMode compositionMode;
501 QGLCustomShaderStage* customSrcStage;
502
503 QGLEngineShaderProg* currentShaderProg;
504};
505
506QT_END_NAMESPACE
507
508#endif //QGLENGINE_SHADER_MANAGER_H
509