1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtGui module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40//
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 <QtGui/private/qtguiglobal_p.h>
224#include <QOpenGLShader>
225#include <QOpenGLShaderProgram>
226#include <QPainter>
227#include <private/qopenglcontext_p.h>
228#include <private/qopenglcustomshaderstage_p.h>
229
230QT_BEGIN_NAMESPACE
231
232
233
234/*
235struct QOpenGLEngineCachedShaderProg
236{
237 QOpenGLEngineCachedShaderProg(QOpenGLEngineShaderManager::ShaderName vertexMain,
238 QOpenGLEngineShaderManager::ShaderName vertexPosition,
239 QOpenGLEngineShaderManager::ShaderName fragMain,
240 QOpenGLEngineShaderManager::ShaderName pixelSrc,
241 QOpenGLEngineShaderManager::ShaderName mask,
242 QOpenGLEngineShaderManager::ShaderName composition);
243
244 int cacheKey;
245 QOpenGLShaderProgram* program;
246}
247*/
248
249static const GLuint QT_VERTEX_COORDS_ATTR = 0;
250static const GLuint QT_TEXTURE_COORDS_ATTR = 1;
251static const GLuint QT_OPACITY_ATTR = 2;
252static const GLuint QT_PMV_MATRIX_1_ATTR = 3;
253static const GLuint QT_PMV_MATRIX_2_ATTR = 4;
254static const GLuint QT_PMV_MATRIX_3_ATTR = 5;
255
256class QOpenGLEngineShaderProg;
257
258class Q_GUI_EXPORT QOpenGLEngineSharedShaders
259{
260 Q_GADGET
261public:
262
263 enum SnippetName {
264 MainVertexShader,
265 MainWithTexCoordsVertexShader,
266 MainWithTexCoordsAndOpacityVertexShader,
267
268 // UntransformedPositionVertexShader must be first in the list:
269 UntransformedPositionVertexShader,
270 PositionOnlyVertexShader,
271 ComplexGeometryPositionOnlyVertexShader,
272 PositionWithPatternBrushVertexShader,
273 PositionWithLinearGradientBrushVertexShader,
274 PositionWithConicalGradientBrushVertexShader,
275 PositionWithRadialGradientBrushVertexShader,
276 PositionWithTextureBrushVertexShader,
277 AffinePositionWithPatternBrushVertexShader,
278 AffinePositionWithLinearGradientBrushVertexShader,
279 AffinePositionWithConicalGradientBrushVertexShader,
280 AffinePositionWithRadialGradientBrushVertexShader,
281 AffinePositionWithTextureBrushVertexShader,
282
283 // MainFragmentShader_CMO must be first in the list:
284 MainFragmentShader_MO,
285 MainFragmentShader_M,
286 MainFragmentShader_O,
287 MainFragmentShader,
288 MainFragmentShader_ImageArrays,
289
290 // ImageSrcFragmentShader must be first in the list::
291 ImageSrcFragmentShader,
292 ImageSrcWithPatternFragmentShader,
293 NonPremultipliedImageSrcFragmentShader,
294 GrayscaleImageSrcFragmentShader,
295 AlphaImageSrcFragmentShader,
296 CustomImageSrcFragmentShader,
297 SolidBrushSrcFragmentShader,
298 TextureBrushSrcFragmentShader,
299 TextureBrushSrcWithPatternFragmentShader,
300 PatternBrushSrcFragmentShader,
301 LinearGradientBrushSrcFragmentShader,
302 RadialGradientBrushSrcFragmentShader,
303 ConicalGradientBrushSrcFragmentShader,
304 ShockingPinkSrcFragmentShader,
305
306 // NoMaskFragmentShader must be first in the list:
307 NoMaskFragmentShader,
308 MaskFragmentShader,
309 RgbMaskFragmentShaderPass1,
310 RgbMaskFragmentShaderPass2,
311 RgbMaskWithGammaFragmentShader,
312
313 // NoCompositionModeFragmentShader must be first in the list:
314 NoCompositionModeFragmentShader,
315 MultiplyCompositionModeFragmentShader,
316 ScreenCompositionModeFragmentShader,
317 OverlayCompositionModeFragmentShader,
318 DarkenCompositionModeFragmentShader,
319 LightenCompositionModeFragmentShader,
320 ColorDodgeCompositionModeFragmentShader,
321 ColorBurnCompositionModeFragmentShader,
322 HardLightCompositionModeFragmentShader,
323 SoftLightCompositionModeFragmentShader,
324 DifferenceCompositionModeFragmentShader,
325 ExclusionCompositionModeFragmentShader,
326
327 TotalSnippetCount, InvalidSnippetName
328 };
329#if defined (QT_DEBUG)
330 Q_ENUM(SnippetName)
331 static QByteArray snippetNameStr(SnippetName snippetName);
332#endif
333
334/*
335 // These allow the ShaderName enum to be used as a cache key
336 const int mainVertexOffset = 0;
337 const int positionVertexOffset = (1<<2) - PositionOnlyVertexShader;
338 const int mainFragOffset = (1<<6) - MainFragmentShader_CMO;
339 const int srcPixelOffset = (1<<10) - ImageSrcFragmentShader;
340 const int maskOffset = (1<<14) - NoMaskShader;
341 const int compositionOffset = (1 << 16) - MultiplyCompositionModeFragmentShader;
342*/
343
344 QOpenGLEngineSharedShaders(QOpenGLContext *context);
345 ~QOpenGLEngineSharedShaders();
346
347 QOpenGLShaderProgram *simpleProgram() { return simpleShaderProg; }
348 QOpenGLShaderProgram *blitProgram() { return blitShaderProg; }
349 // Compile the program if it's not already in the cache, return the item in the cache.
350 QOpenGLEngineShaderProg *findProgramInCache(const QOpenGLEngineShaderProg &prog);
351 // Compile the custom shader if it's not already in the cache, return the item in the cache.
352
353 static QOpenGLEngineSharedShaders *shadersForContext(QOpenGLContext *context);
354
355 // Ideally, this would be static and cleanup all programs in all contexts which
356 // contain the custom code. Currently it is just a hint and we rely on deleted
357 // custom shaders being cleaned up by being kicked out of the cache when it's
358 // full.
359 void cleanupCustomStage(QOpenGLCustomShaderStage* stage);
360
361private:
362 QOpenGLShaderProgram *blitShaderProg;
363 QOpenGLShaderProgram *simpleShaderProg;
364 QList<QOpenGLEngineShaderProg*> cachedPrograms;
365
366 static const char* qShaderSnippets[TotalSnippetCount];
367};
368
369
370class QOpenGLEngineShaderProg
371{
372public:
373 QOpenGLEngineShaderProg() : program(nullptr) {}
374
375 ~QOpenGLEngineShaderProg() {
376 if (program)
377 delete program;
378 }
379
380 QOpenGLEngineSharedShaders::SnippetName mainVertexShader;
381 QOpenGLEngineSharedShaders::SnippetName positionVertexShader;
382 QOpenGLEngineSharedShaders::SnippetName mainFragShader;
383 QOpenGLEngineSharedShaders::SnippetName srcPixelFragShader;
384 QOpenGLEngineSharedShaders::SnippetName maskFragShader;
385 QOpenGLEngineSharedShaders::SnippetName compositionFragShader;
386
387 QByteArray customStageSource; //TODO: Decent cache key for custom stages
388 QOpenGLShaderProgram* program;
389
390 QVector<uint> uniformLocations;
391
392 bool useTextureCoords;
393 bool useOpacityAttribute;
394 bool usePmvMatrixAttribute;
395
396 bool operator==(const QOpenGLEngineShaderProg& other) const {
397 // We don't care about the program
398 return ( mainVertexShader == other.mainVertexShader &&
399 positionVertexShader == other.positionVertexShader &&
400 mainFragShader == other.mainFragShader &&
401 srcPixelFragShader == other.srcPixelFragShader &&
402 maskFragShader == other.maskFragShader &&
403 compositionFragShader == other.compositionFragShader &&
404 customStageSource == other.customStageSource
405 );
406 }
407};
408
409class Q_GUI_EXPORT QOpenGLEngineShaderManager : public QObject
410{
411 Q_OBJECT
412public:
413 QOpenGLEngineShaderManager(QOpenGLContext* context);
414 ~QOpenGLEngineShaderManager();
415
416 enum MaskType {NoMask, PixelMask, SubPixelMaskPass1, SubPixelMaskPass2, SubPixelWithGammaMask};
417 enum PixelSrcType {
418 ImageSrc = Qt::TexturePattern+1,
419 NonPremultipliedImageSrc = Qt::TexturePattern+2,
420 PatternSrc = Qt::TexturePattern+3,
421 TextureSrcWithPattern = Qt::TexturePattern+4,
422 GrayscaleImageSrc = Qt::TexturePattern+5,
423 AlphaImageSrc = Qt::TexturePattern+6,
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 NumUniforms
446 };
447
448 enum OpacityMode {
449 NoOpacity,
450 UniformOpacity,
451 AttributeOpacity
452 };
453
454 // There are optimizations we can do, depending on the brush transform:
455 // 1) May not have to apply perspective-correction
456 // 2) Can use lower precision for matrix
457 void optimiseForBrushTransform(QTransform::TransformationType transformType);
458 void setSrcPixelType(Qt::BrushStyle);
459 void setSrcPixelType(PixelSrcType); // For non-brush sources, like pixmaps & images
460 void setOpacityMode(OpacityMode);
461 void setMaskType(MaskType);
462 void setCompositionMode(QPainter::CompositionMode);
463 void setCustomStage(QOpenGLCustomShaderStage* stage);
464 void removeCustomStage();
465
466 GLuint getUniformLocation(Uniform id);
467
468 void setDirty(); // someone has manually changed the current shader program
469 bool useCorrectShaderProg(); // returns true if the shader program needed to be changed
470
471 void useSimpleProgram();
472 void useBlitProgram();
473 void setHasComplexGeometry(bool hasComplexGeometry)
474 {
475 complexGeometry = hasComplexGeometry;
476 shaderProgNeedsChanging = true;
477 }
478 bool hasComplexGeometry() const
479 {
480 return complexGeometry;
481 }
482
483 QOpenGLShaderProgram* currentProgram(); // Returns pointer to the shader the manager has chosen
484 QOpenGLShaderProgram* simpleProgram(); // Used to draw into e.g. stencil buffers
485 QOpenGLShaderProgram* blitProgram(); // Used to blit a texture into the framebuffer
486
487 QOpenGLEngineSharedShaders* sharedShaders;
488
489private:
490 QOpenGLContext* ctx;
491 bool shaderProgNeedsChanging;
492 bool complexGeometry;
493
494 // Current state variables which influence the choice of shader:
495 QTransform brushTransform;
496 int srcPixelType;
497 OpacityMode opacityMode;
498 MaskType maskType;
499 QPainter::CompositionMode compositionMode;
500 QOpenGLCustomShaderStage* customSrcStage;
501
502 QOpenGLEngineShaderProg* currentShaderProg;
503};
504
505QT_END_NAMESPACE
506
507#endif //QOPENGLENGINE_SHADER_MANAGER_H
508