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 229 QT_BEGIN_NAMESPACE 230 231 232 233 /* 234 struct 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 248 static const GLuint QT_VERTEX_COORDS_ATTR = 0; 249 static const GLuint QT_TEXTURE_COORDS_ATTR = 1; 250 static const GLuint QT_OPACITY_ATTR = 2; 251 static const GLuint QT_PMV_MATRIX_1_ATTR = 3; 252 static const GLuint QT_PMV_MATRIX_2_ATTR = 4; 253 static const GLuint QT_PMV_MATRIX_3_ATTR = 5; 254 255 class QGLEngineShaderProg; 256 257 class Q_OPENGL_EXPORT QGLEngineSharedShaders 258 { 259 Q_GADGET 260 public: 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 simpleProgram()348 QGLShaderProgram *simpleProgram() { return simpleShaderProg; } blitProgram()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 362 private: 363 QGLShaderProgram *blitShaderProg; 364 QGLShaderProgram *simpleShaderProg; 365 QList<QGLEngineShaderProg*> cachedPrograms; 366 QList<QGLShader *> shaders; 367 368 static const char* qShaderSnippets[TotalSnippetCount]; 369 }; 370 371 372 class QGLEngineShaderProg 373 { 374 public: QGLEngineShaderProg()375 QGLEngineShaderProg() : program(nullptr) {} 376 ~QGLEngineShaderProg()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 411 class Q_OPENGL_EXPORT QGLEngineShaderManager : public QObject 412 { 413 Q_OBJECT 414 public: 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(); setHasComplexGeometry(bool hasComplexGeometry)474 void setHasComplexGeometry(bool hasComplexGeometry) 475 { 476 complexGeometry = hasComplexGeometry; 477 shaderProgNeedsChanging = true; 478 } hasComplexGeometry()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 490 private: 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 506 QT_END_NAMESPACE 507 508 #endif //QGLENGINE_SHADER_MANAGER_H 509