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