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, QOpenGLShader & QOpenGLShaderProgram will make partial
57 shaders work by concatenating the source in each QOpenGLShader and compiling
58 it as a single shader. This is abstracted nicely by QOpenGLShaderProgram 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 qopenglslMainVertexShader
89 qopenglslMainWithTexCoordsVertexShader
90
91 And the following position vertex shaders:
92 qopenglslPositionOnlyVertexShader
93 qopenglslPositionWithTextureBrushVertexShader
94 qopenglslPositionWithPatternBrushVertexShader
95 qopenglslPositionWithLinearGradientBrushVertexShader
96 qopenglslPositionWithRadialGradientBrushVertexShader
97 qopenglslPositionWithConicalGradientBrushVertexShader
98 qopenglslAffinePositionWithTextureBrushVertexShader
99 qopenglslAffinePositionWithPatternBrushVertexShader
100 qopenglslAffinePositionWithLinearGradientBrushVertexShader
101 qopenglslAffinePositionWithRadialGradientBrushVertexShader
102 qopenglslAffinePositionWithConicalGradientBrushVertexShader
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 qopenglslImageSrcFragShader
130 qopenglslImageSrcWithPatternFragShader
131 qopenglslNonPremultipliedImageSrcFragShader
132 qopenglslSolidBrushSrcFragShader
133 qopenglslTextureBrushSrcFragShader
134 qopenglslTextureBrushWithPatternFragShader
135 qopenglslPatternBrushSrcFragShader
136 qopenglslLinearGradientBrushSrcFragShader
137 qopenglslRadialGradientBrushSrcFragShader
138 qopenglslConicalGradientBrushSrcFragShader
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 qopenglslMaskFragmentShader
143 qopenglslRgbMaskFragmentShaderPass1
144 qopenglslRgbMaskFragmentShaderPass2
145 qopenglslRgbMaskWithGammaFragmentShader
146
147 Composition modes are "qcolorp vec4 compose(qcolorp vec4 src)":
148 qopenglslColorBurnCompositionModeFragmentShader
149 qopenglslColorDodgeCompositionModeFragmentShader
150 qopenglslDarkenCompositionModeFragmentShader
151 qopenglslDifferenceCompositionModeFragmentShader
152 qopenglslExclusionCompositionModeFragmentShader
153 qopenglslHardLightCompositionModeFragmentShader
154 qopenglslLightenCompositionModeFragmentShader
155 qopenglslMultiplyCompositionModeFragmentShader
156 qopenglslOverlayCompositionModeFragmentShader
157 qopenglslScreenCompositionModeFragmentShader
158 qopenglslSoftLightCompositionModeFragmentShader
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 qopenglslMainFragmentShader_CMO
187 qopenglslMainFragmentShader_CM
188 qopenglslMainFragmentShader_MO
189 qopenglslMainFragmentShader_M
190 qopenglslMainFragmentShader_CO
191 qopenglslMainFragmentShader_C
192 qopenglslMainFragmentShader_O
193 qopenglslMainFragmentShader
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 (QOpenGLCustomShaderStage). 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 QOPENGLENGINE_SHADER_MANAGER_H
221#define QOPENGLENGINE_SHADER_MANAGER_H
222
223#include <QOpenGLShader>
224#include <QOpenGLShaderProgram>
225#include <QPainter>
226#include <private/qopenglcontext_p.h>
227#include <private/qopenglcustomshaderstage_p.h>
228
229QT_BEGIN_NAMESPACE
230
231
232
233/*
234struct QOpenGLEngineCachedShaderProg
235{
236 QOpenGLEngineCachedShaderProg(QOpenGLEngineShaderManager::ShaderName vertexMain,
237 QOpenGLEngineShaderManager::ShaderName vertexPosition,
238 QOpenGLEngineShaderManager::ShaderName fragMain,
239 QOpenGLEngineShaderManager::ShaderName pixelSrc,
240 QOpenGLEngineShaderManager::ShaderName mask,
241 QOpenGLEngineShaderManager::ShaderName composition);
242
243 int cacheKey;
244 QOpenGLShaderProgram* 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 QOpenGLEngineShaderProg;
256
257class Q_OPENGL_EXPORT QOpenGLEngineSharedShaders
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_MO,
284 MainFragmentShader_M,
285 MainFragmentShader_O,
286 MainFragmentShader,
287 MainFragmentShader_ImageArrays,
288
289 // ImageSrcFragmentShader must be first in the list::
290 ImageSrcFragmentShader,
291 ImageSrcWithPatternFragmentShader,
292 NonPremultipliedImageSrcFragmentShader,
293 GrayscaleImageSrcFragmentShader,
294 AlphaImageSrcFragmentShader,
295 CustomImageSrcFragmentShader,
296 SolidBrushSrcFragmentShader,
297 TextureBrushSrcFragmentShader,
298 TextureBrushSrcWithPatternFragmentShader,
299 PatternBrushSrcFragmentShader,
300 LinearGradientBrushSrcFragmentShader,
301 RadialGradientBrushSrcFragmentShader,
302 ConicalGradientBrushSrcFragmentShader,
303 ShockingPinkSrcFragmentShader,
304
305 // NoMaskFragmentShader must be first in the list:
306 NoMaskFragmentShader,
307 MaskFragmentShader,
308 RgbMaskFragmentShaderPass1,
309 RgbMaskFragmentShaderPass2,
310 RgbMaskWithGammaFragmentShader,
311
312 // NoCompositionModeFragmentShader must be first in the list:
313 NoCompositionModeFragmentShader,
314 MultiplyCompositionModeFragmentShader,
315 ScreenCompositionModeFragmentShader,
316 OverlayCompositionModeFragmentShader,
317 DarkenCompositionModeFragmentShader,
318 LightenCompositionModeFragmentShader,
319 ColorDodgeCompositionModeFragmentShader,
320 ColorBurnCompositionModeFragmentShader,
321 HardLightCompositionModeFragmentShader,
322 SoftLightCompositionModeFragmentShader,
323 DifferenceCompositionModeFragmentShader,
324 ExclusionCompositionModeFragmentShader,
325
326 TotalSnippetCount, InvalidSnippetName
327 };
328#if defined (QT_DEBUG)
329 Q_ENUM(SnippetName)
330 static QByteArray snippetNameStr(SnippetName snippetName);
331#endif
332
333/*
334 // These allow the ShaderName enum to be used as a cache key
335 const int mainVertexOffset = 0;
336 const int positionVertexOffset = (1<<2) - PositionOnlyVertexShader;
337 const int mainFragOffset = (1<<6) - MainFragmentShader_CMO;
338 const int srcPixelOffset = (1<<10) - ImageSrcFragmentShader;
339 const int maskOffset = (1<<14) - NoMaskShader;
340 const int compositionOffset = (1 << 16) - MultiplyCompositionModeFragmentShader;
341*/
342
343 QOpenGLEngineSharedShaders(QOpenGLContext *context);
344 ~QOpenGLEngineSharedShaders();
345
346 QOpenGLShaderProgram *simpleProgram() { return simpleShaderProg; }
347 QOpenGLShaderProgram *blitProgram() { return blitShaderProg; }
348 // Compile the program if it's not already in the cache, return the item in the cache.
349 QOpenGLEngineShaderProg *findProgramInCache(const QOpenGLEngineShaderProg &prog);
350 // Compile the custom shader if it's not already in the cache, return the item in the cache.
351
352 static QOpenGLEngineSharedShaders *shadersForContext(QOpenGLContext *context);
353
354 // Ideally, this would be static and cleanup all programs in all contexts which
355 // contain the custom code. Currently it is just a hint and we rely on deleted
356 // custom shaders being cleaned up by being kicked out of the cache when it's
357 // full.
358 void cleanupCustomStage(QOpenGLCustomShaderStage* stage);
359
360private:
361 QOpenGLShaderProgram *blitShaderProg;
362 QOpenGLShaderProgram *simpleShaderProg;
363 QList<QOpenGLEngineShaderProg*> cachedPrograms;
364
365 static const char* qShaderSnippets[TotalSnippetCount];
366};
367
368
369class QOpenGLEngineShaderProg
370{
371public:
372 QOpenGLEngineShaderProg() : program(nullptr) {}
373
374 ~QOpenGLEngineShaderProg() {
375 if (program)
376 delete program;
377 }
378
379 QOpenGLEngineSharedShaders::SnippetName mainVertexShader;
380 QOpenGLEngineSharedShaders::SnippetName positionVertexShader;
381 QOpenGLEngineSharedShaders::SnippetName mainFragShader;
382 QOpenGLEngineSharedShaders::SnippetName srcPixelFragShader;
383 QOpenGLEngineSharedShaders::SnippetName maskFragShader;
384 QOpenGLEngineSharedShaders::SnippetName compositionFragShader;
385
386 QByteArray customStageSource; //TODO: Decent cache key for custom stages
387 QOpenGLShaderProgram* program;
388
389 QList<uint> uniformLocations;
390
391 bool useTextureCoords;
392 bool useOpacityAttribute;
393 bool usePmvMatrixAttribute;
394
395 bool operator==(const QOpenGLEngineShaderProg& other) const {
396 // We don't care about the program
397 return ( mainVertexShader == other.mainVertexShader &&
398 positionVertexShader == other.positionVertexShader &&
399 mainFragShader == other.mainFragShader &&
400 srcPixelFragShader == other.srcPixelFragShader &&
401 maskFragShader == other.maskFragShader &&
402 compositionFragShader == other.compositionFragShader &&
403 customStageSource == other.customStageSource
404 );
405 }
406};
407
408class Q_OPENGL_EXPORT QOpenGLEngineShaderManager : public QObject
409{
410 Q_OBJECT
411public:
412 QOpenGLEngineShaderManager(QOpenGLContext* context);
413 ~QOpenGLEngineShaderManager();
414
415 enum MaskType {NoMask, PixelMask, SubPixelMaskPass1, SubPixelMaskPass2, SubPixelWithGammaMask};
416 enum PixelSrcType {
417 ImageSrc = Qt::TexturePattern+1,
418 NonPremultipliedImageSrc = Qt::TexturePattern+2,
419 PatternSrc = Qt::TexturePattern+3,
420 TextureSrcWithPattern = Qt::TexturePattern+4,
421 GrayscaleImageSrc = Qt::TexturePattern+5,
422 AlphaImageSrc = Qt::TexturePattern+6,
423 };
424
425 enum Uniform {
426 ImageTexture,
427 PatternColor,
428 GlobalOpacity,
429 Depth,
430 MaskTexture,
431 FragmentColor,
432 LinearData,
433 Angle,
434 HalfViewportSize,
435 Fmp,
436 Fmp2MRadius2,
437 Inverse2Fmp2MRadius2,
438 SqrFr,
439 BRadius,
440 InvertedTextureSize,
441 BrushTransform,
442 BrushTexture,
443 Matrix,
444 NumUniforms
445 };
446
447 enum OpacityMode {
448 NoOpacity,
449 UniformOpacity,
450 AttributeOpacity
451 };
452
453 // There are optimizations we can do, depending on the brush transform:
454 // 1) May not have to apply perspective-correction
455 // 2) Can use lower precision for matrix
456 void optimiseForBrushTransform(QTransform::TransformationType transformType);
457 void setSrcPixelType(Qt::BrushStyle);
458 void setSrcPixelType(PixelSrcType); // For non-brush sources, like pixmaps & images
459 void setOpacityMode(OpacityMode);
460 void setMaskType(MaskType);
461 void setCompositionMode(QPainter::CompositionMode);
462 void setCustomStage(QOpenGLCustomShaderStage* stage);
463 void removeCustomStage();
464
465 GLuint getUniformLocation(Uniform id);
466
467 void setDirty(); // someone has manually changed the current shader program
468 bool useCorrectShaderProg(); // returns true if the shader program needed to be changed
469
470 void useSimpleProgram();
471 void useBlitProgram();
472 void setHasComplexGeometry(bool hasComplexGeometry)
473 {
474 complexGeometry = hasComplexGeometry;
475 shaderProgNeedsChanging = true;
476 }
477 bool hasComplexGeometry() const
478 {
479 return complexGeometry;
480 }
481
482 QOpenGLShaderProgram* currentProgram(); // Returns pointer to the shader the manager has chosen
483 QOpenGLShaderProgram* simpleProgram(); // Used to draw into e.g. stencil buffers
484 QOpenGLShaderProgram* blitProgram(); // Used to blit a texture into the framebuffer
485
486 QOpenGLEngineSharedShaders* sharedShaders;
487
488private:
489 QOpenGLContext* ctx;
490 bool shaderProgNeedsChanging;
491 bool complexGeometry;
492
493 // Current state variables which influence the choice of shader:
494 QTransform brushTransform;
495 int srcPixelType;
496 OpacityMode opacityMode;
497 MaskType maskType;
498 QPainter::CompositionMode compositionMode;
499 QOpenGLCustomShaderStage* customSrcStage;
500
501 QOpenGLEngineShaderProg* currentShaderProg;
502};
503
504QT_END_NAMESPACE
505
506#endif //QOPENGLENGINE_SHADER_MANAGER_H
507