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 
230 QT_BEGIN_NAMESPACE
231 
232 
233 
234 /*
235 struct 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 
249 static const GLuint QT_VERTEX_COORDS_ATTR  = 0;
250 static const GLuint QT_TEXTURE_COORDS_ATTR = 1;
251 static const GLuint QT_OPACITY_ATTR = 2;
252 static const GLuint QT_PMV_MATRIX_1_ATTR = 3;
253 static const GLuint QT_PMV_MATRIX_2_ATTR = 4;
254 static const GLuint QT_PMV_MATRIX_3_ATTR = 5;
255 
256 class QOpenGLEngineShaderProg;
257 
258 class Q_GUI_EXPORT QOpenGLEngineSharedShaders
259 {
260     Q_GADGET
261 public:
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 
simpleProgram()347     QOpenGLShaderProgram *simpleProgram() { return simpleShaderProg; }
blitProgram()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 
361 private:
362     QOpenGLShaderProgram *blitShaderProg;
363     QOpenGLShaderProgram *simpleShaderProg;
364     QList<QOpenGLEngineShaderProg*> cachedPrograms;
365 
366     static const char* qShaderSnippets[TotalSnippetCount];
367 };
368 
369 
370 class QOpenGLEngineShaderProg
371 {
372 public:
QOpenGLEngineShaderProg()373     QOpenGLEngineShaderProg() : program(nullptr) {}
374 
~QOpenGLEngineShaderProg()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 
409 class Q_GUI_EXPORT QOpenGLEngineShaderManager : public QObject
410 {
411     Q_OBJECT
412 public:
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();
setHasComplexGeometry(bool hasComplexGeometry)473     void setHasComplexGeometry(bool hasComplexGeometry)
474     {
475         complexGeometry = hasComplexGeometry;
476         shaderProgNeedsChanging = true;
477     }
hasComplexGeometry()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 
489 private:
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 
505 QT_END_NAMESPACE
506 
507 #endif //QOPENGLENGINE_SHADER_MANAGER_H
508