Add support for advanced blend equations to our OpenGL QPainter

Mesa and NVidia have been supporting these extensions several years now.
It also means we can get rid of the dead unused code we had for advanced
compositions.

Change-Id: I6a2fcda13490abd977eb4cc3d8b34f186d05ca25
Reviewed-by: Laszlo Agocs <laszlo.agocs@qt.io>
bb10
Allan Sandfeld Jensen 2018-02-19 12:33:57 +01:00
parent 5ddaadaa7e
commit 73be2aab75
7 changed files with 175 additions and 150 deletions

View File

@ -153,12 +153,8 @@ QOpenGLEngineSharedShaders::QOpenGLEngineSharedShaders(QOpenGLContext* context)
code[AffinePositionWithRadialGradientBrushVertexShader] = qopenglslAffinePositionWithRadialGradientBrushVertexShader_core;
code[AffinePositionWithTextureBrushVertexShader] = qopenglslAffinePositionWithTextureBrushVertexShader_core;
code[MainFragmentShader_CMO] = qopenglslMainFragmentShader_CMO_core;
code[MainFragmentShader_CM] = qopenglslMainFragmentShader_CM_core;
code[MainFragmentShader_MO] = qopenglslMainFragmentShader_MO_core;
code[MainFragmentShader_M] = qopenglslMainFragmentShader_M_core;
code[MainFragmentShader_CO] = qopenglslMainFragmentShader_CO_core;
code[MainFragmentShader_C] = qopenglslMainFragmentShader_C_core;
code[MainFragmentShader_O] = qopenglslMainFragmentShader_O_core;
code[MainFragmentShader] = qopenglslMainFragmentShader_core;
code[MainFragmentShader_ImageArrays] = qopenglslMainFragmentShader_ImageArrays_core;
@ -203,12 +199,8 @@ QOpenGLEngineSharedShaders::QOpenGLEngineSharedShaders(QOpenGLContext* context)
code[AffinePositionWithRadialGradientBrushVertexShader] = qopenglslAffinePositionWithRadialGradientBrushVertexShader;
code[AffinePositionWithTextureBrushVertexShader] = qopenglslAffinePositionWithTextureBrushVertexShader;
code[MainFragmentShader_CMO] = qopenglslMainFragmentShader_CMO;
code[MainFragmentShader_CM] = qopenglslMainFragmentShader_CM;
code[MainFragmentShader_MO] = qopenglslMainFragmentShader_MO;
code[MainFragmentShader_M] = qopenglslMainFragmentShader_M;
code[MainFragmentShader_CO] = qopenglslMainFragmentShader_CO;
code[MainFragmentShader_C] = qopenglslMainFragmentShader_C;
code[MainFragmentShader_O] = qopenglslMainFragmentShader_O;
code[MainFragmentShader] = qopenglslMainFragmentShader;
code[MainFragmentShader_ImageArrays] = qopenglslMainFragmentShader_ImageArrays;
@ -238,21 +230,20 @@ QOpenGLEngineSharedShaders::QOpenGLEngineSharedShaders(QOpenGLContext* context)
code[RgbMaskWithGammaFragmentShader] = ""; //###
}
// These shaders are not implemented yet and therefore are the same
// for all profiles. Implementations should make a version for both
// profiles and put the appropriate lines in the if-statement above.
// The composition shaders are just layout qualifiers and the same
// for all profiles that support them.
code[NoCompositionModeFragmentShader] = "";
code[MultiplyCompositionModeFragmentShader] = ""; //###
code[ScreenCompositionModeFragmentShader] = ""; //###
code[OverlayCompositionModeFragmentShader] = ""; //###
code[DarkenCompositionModeFragmentShader] = ""; //###
code[LightenCompositionModeFragmentShader] = ""; //###
code[ColorDodgeCompositionModeFragmentShader] = ""; //###
code[ColorBurnCompositionModeFragmentShader] = ""; //###
code[HardLightCompositionModeFragmentShader] = ""; //###
code[SoftLightCompositionModeFragmentShader] = ""; //###
code[DifferenceCompositionModeFragmentShader] = ""; //###
code[ExclusionCompositionModeFragmentShader] = ""; //###
code[MultiplyCompositionModeFragmentShader] = qopenglslMultiplyCompositionModeFragmentShader;
code[ScreenCompositionModeFragmentShader] = qopenglslScreenCompositionModeFragmentShader;
code[OverlayCompositionModeFragmentShader] = qopenglslOverlayCompositionModeFragmentShader;
code[DarkenCompositionModeFragmentShader] = qopenglslDarkenCompositionModeFragmentShader;
code[LightenCompositionModeFragmentShader] = qopenglslLightenCompositionModeFragmentShader;
code[ColorDodgeCompositionModeFragmentShader] = qopenglslColorDodgeCompositionModeFragmentShader;
code[ColorBurnCompositionModeFragmentShader] = qopenglslColorBurnCompositionModeFragmentShader;
code[HardLightCompositionModeFragmentShader] = qopenglslHardLightCompositionModeFragmentShader;
code[SoftLightCompositionModeFragmentShader] = qopenglslSoftLightCompositionModeFragmentShader;
code[DifferenceCompositionModeFragmentShader] = qopenglslDifferenceCompositionModeFragmentShader;
code[ExclusionCompositionModeFragmentShader] = qopenglslExclusionCompositionModeFragmentShader;
#if defined(QT_DEBUG)
// Check that all the elements have been filled:
@ -612,8 +603,11 @@ void QOpenGLEngineShaderManager::setCompositionMode(QPainter::CompositionMode mo
if (compositionMode == mode)
return;
bool wasAdvanced = compositionMode > QPainter::CompositionMode_Plus;
bool isAdvanced = mode > QPainter::CompositionMode_Plus;
compositionMode = mode;
shaderProgNeedsChanging = true; //###
shaderProgNeedsChanging = shaderProgNeedsChanging || wasAdvanced || isAdvanced;
}
void QOpenGLEngineShaderManager::setCustomStage(QOpenGLCustomShaderStage* stage)
@ -783,21 +777,13 @@ bool QOpenGLEngineShaderManager::useCorrectShaderProg()
requiredProgram.mainFragShader = QOpenGLEngineSharedShaders::MainFragmentShader_ImageArrays;
} else {
bool useGlobalOpacity = (opacityMode == UniformOpacity);
if (hasCompose && hasMask && useGlobalOpacity)
requiredProgram.mainFragShader = QOpenGLEngineSharedShaders::MainFragmentShader_CMO;
if (hasCompose && hasMask && !useGlobalOpacity)
requiredProgram.mainFragShader = QOpenGLEngineSharedShaders::MainFragmentShader_CM;
if (!hasCompose && hasMask && useGlobalOpacity)
if (hasMask && useGlobalOpacity)
requiredProgram.mainFragShader = QOpenGLEngineSharedShaders::MainFragmentShader_MO;
if (!hasCompose && hasMask && !useGlobalOpacity)
if (hasMask && !useGlobalOpacity)
requiredProgram.mainFragShader = QOpenGLEngineSharedShaders::MainFragmentShader_M;
if (hasCompose && !hasMask && useGlobalOpacity)
requiredProgram.mainFragShader = QOpenGLEngineSharedShaders::MainFragmentShader_CO;
if (hasCompose && !hasMask && !useGlobalOpacity)
requiredProgram.mainFragShader = QOpenGLEngineSharedShaders::MainFragmentShader_C;
if (!hasCompose && !hasMask && useGlobalOpacity)
if (!hasMask && useGlobalOpacity)
requiredProgram.mainFragShader = QOpenGLEngineSharedShaders::MainFragmentShader_O;
if (!hasCompose && !hasMask && !useGlobalOpacity)
if (!hasMask && !useGlobalOpacity)
requiredProgram.mainFragShader = QOpenGLEngineSharedShaders::MainFragmentShader;
}

View File

@ -281,12 +281,8 @@ public:
AffinePositionWithTextureBrushVertexShader,
// MainFragmentShader_CMO must be first in the list:
MainFragmentShader_CMO,
MainFragmentShader_CM,
MainFragmentShader_MO,
MainFragmentShader_M,
MainFragmentShader_CO,
MainFragmentShader_C,
MainFragmentShader_O,
MainFragmentShader,
MainFragmentShader_ImageArrays,

View File

@ -403,25 +403,6 @@ static const char* const qopenglslMainFragmentShader_ImageArrays = "\n\
gl_FragColor = srcPixel() * opacity; \n\
}\n";
static const char* const qopenglslMainFragmentShader_CMO = "\n\
uniform lowp float globalOpacity; \n\
lowp vec4 srcPixel(); \n\
lowp vec4 applyMask(lowp vec4); \n\
lowp vec4 compose(lowp vec4); \n\
void main() \n\
{ \n\
gl_FragColor = applyMask(compose(srcPixel()*globalOpacity))); \n\
}\n";
static const char* const qopenglslMainFragmentShader_CM = "\n\
lowp vec4 srcPixel(); \n\
lowp vec4 applyMask(lowp vec4); \n\
lowp vec4 compose(lowp vec4); \n\
void main() \n\
{ \n\
gl_FragColor = applyMask(compose(srcPixel())); \n\
}\n";
static const char* const qopenglslMainFragmentShader_MO = "\n\
uniform lowp float globalOpacity; \n\
lowp vec4 srcPixel(); \n\
@ -439,23 +420,6 @@ static const char* const qopenglslMainFragmentShader_M = "\n\
gl_FragColor = applyMask(srcPixel()); \n\
}\n";
static const char* const qopenglslMainFragmentShader_CO = "\n\
uniform lowp float globalOpacity; \n\
lowp vec4 srcPixel(); \n\
lowp vec4 compose(lowp vec4); \n\
void main() \n\
{ \n\
gl_FragColor = compose(srcPixel()*globalOpacity); \n\
}\n";
static const char* const qopenglslMainFragmentShader_C = "\n\
lowp vec4 srcPixel(); \n\
lowp vec4 compose(lowp vec4); \n\
void main() \n\
{ \n\
gl_FragColor = compose(srcPixel()); \n\
}\n";
static const char* const qopenglslMainFragmentShader_O = "\n\
uniform lowp float globalOpacity; \n\
lowp vec4 srcPixel(); \n\
@ -513,22 +477,65 @@ static const char* const qopenglslRgbMaskFragmentShaderPass2 = "\n\
return src * mask; \n\
}\n";
static const char* const qopenglslMultiplyCompositionModeFragmentShader = "\n\
#ifdef GL_KHR_blend_equation_advanced\n\
layout(blend_support_multiply) out;\n\
#endif\n";
static const char* const qopenglslScreenCompositionModeFragmentShader = "\n\
#ifdef GL_KHR_blend_equation_advanced\n\
layout(blend_support_screen) out;\n\
#endif\n";
static const char* const qopenglslOverlayCompositionModeFragmentShader = "\n\
#ifdef GL_KHR_blend_equation_advanced\n\
layout(blend_support_overlay) out;\n\
#endif\n";
static const char* const qopenglslDarkenCompositionModeFragmentShader = "\n\
#ifdef GL_KHR_blend_equation_advanced\n\
layout(blend_support_darken) out;\n\
#endif\n";
static const char* const qopenglslLightenCompositionModeFragmentShader = "\n\
#ifdef GL_KHR_blend_equation_advanced\n\
layout(blend_support_lighten) out;\n\
#endif\n";
static const char* const qopenglslColorDodgeCompositionModeFragmentShader = "\n\
#ifdef GL_KHR_blend_equation_advanced\n\
layout(blend_support_colordodge) out;\n\
#endif\n";
static const char* const qopenglslColorBurnCompositionModeFragmentShader = "\n\
#ifdef GL_KHR_blend_equation_advanced\n\
layout(blend_support_colorburn) out;\n\
#endif\n";
static const char* const qopenglslHardLightCompositionModeFragmentShader = "\n\
#ifdef GL_KHR_blend_equation_advanced\n\
layout(blend_support_hardlight) out;\n\
#endif\n";
static const char* const qopenglslSoftLightCompositionModeFragmentShader = "\n\
#ifdef GL_KHR_blend_equation_advanced\n\
layout(blend_support_softlight) out;\n\
#endif\n";
static const char* const qopenglslDifferenceCompositionModeFragmentShader = "\n\
#ifdef GL_KHR_blend_equation_advanced\n\
layout(blend_support_difference) out;\n\
#endif\n";
static const char* const qopenglslExclusionCompositionModeFragmentShader = "\n\
#ifdef GL_KHR_blend_equation_advanced\n\
layout(blend_support_exclusion) out;\n\
#endif\n";
/*
Left to implement:
RgbMaskFragmentShader,
RgbMaskWithGammaFragmentShader,
MultiplyCompositionModeFragmentShader,
ScreenCompositionModeFragmentShader,
OverlayCompositionModeFragmentShader,
DarkenCompositionModeFragmentShader,
LightenCompositionModeFragmentShader,
ColorDodgeCompositionModeFragmentShader,
ColorBurnCompositionModeFragmentShader,
HardLightCompositionModeFragmentShader,
SoftLightCompositionModeFragmentShader,
DifferenceCompositionModeFragmentShader,
ExclusionCompositionModeFragmentShader,
*/
/*
@ -880,29 +887,6 @@ static const char* const qopenglslMainFragmentShader_ImageArrays_core =
fragColor = srcPixel() * opacity; \n\
}\n";
static const char* const qopenglslMainFragmentShader_CMO_core =
"#version 150 core\n\
out vec4 fragColor; \n\
uniform float globalOpacity; \n\
vec4 srcPixel(); \n\
vec4 applyMask(vec4); \n\
vec4 compose(vec4); \n\
void main() \n\
{ \n\
fragColor = applyMask(compose(srcPixel()*globalOpacity))); \n\
}\n";
static const char* const qopenglslMainFragmentShader_CM_core =
"#version 150 core\n\
out vec4 fragColor; \n\
vec4 srcPixel(); \n\
vec4 applyMask(vec4); \n\
vec4 compose(vec4); \n\
void main() \n\
{ \n\
fragColor = applyMask(compose(srcPixel())); \n\
}\n";
static const char* const qopenglslMainFragmentShader_MO_core =
"#version 150 core\n\
out vec4 fragColor; \n\
@ -924,27 +908,6 @@ static const char* const qopenglslMainFragmentShader_M_core =
fragColor = applyMask(srcPixel()); \n\
}\n";
static const char* const qopenglslMainFragmentShader_CO_core =
"#version 150 core\n\
out vec4 fragColor; \n\
uniform float globalOpacity; \n\
vec4 srcPixel(); \n\
vec4 compose(vec4); \n\
void main() \n\
{ \n\
fragColor = compose(srcPixel()*globalOpacity); \n\
}\n";
static const char* const qopenglslMainFragmentShader_C_core =
"#version 150 core\n\
out vec4 fragColor; \n\
vec4 srcPixel(); \n\
vec4 compose(vec4); \n\
void main() \n\
{ \n\
fragColor = compose(srcPixel()); \n\
}\n";
static const char* const qopenglslMainFragmentShader_O_core =
"#version 150 core\n\
out vec4 fragColor; \n\
@ -1010,18 +973,6 @@ static const char* const qopenglslRgbMaskFragmentShaderPass2_core = "\n\
Left to implement:
RgbMaskFragmentShader_core,
RgbMaskWithGammaFragmentShader_core,
MultiplyCompositionModeFragmentShader_core,
ScreenCompositionModeFragmentShader_core,
OverlayCompositionModeFragmentShader_core,
DarkenCompositionModeFragmentShader_core,
LightenCompositionModeFragmentShader_core,
ColorDodgeCompositionModeFragmentShader_core,
ColorBurnCompositionModeFragmentShader_core,
HardLightCompositionModeFragmentShader_core,
SoftLightCompositionModeFragmentShader_core,
DifferenceCompositionModeFragmentShader_core,
ExclusionCompositionModeFragmentShader_core,
*/
QT_END_NAMESPACE

View File

@ -294,9 +294,19 @@ QOpenGLExtensions::QOpenGLExtensions(QOpenGLContext *context)
static int qt_gl_resolve_features()
{
QOpenGLContext *ctx = QOpenGLContext::currentContext();
QOpenGLExtensionMatcher extensions;
int features = 0;
if ((extensions.match("GL_KHR_blend_equation_advanced")
|| extensions.match("GL_NV_blend_equation_advanced")) &&
(extensions.match("GL_KHR_blend_equation_advanced_coherent")
|| extensions.match("GL_NV_blend_equation_advanced_coherent"))) {
// We need both the advanced equations and the coherency for us
// to be able to easily use the new blend equations
features |= QOpenGLFunctions::BlendEquationAdvanced;
}
if (ctx->isOpenGLES()) {
// OpenGL ES
int features = QOpenGLFunctions::Multitexture |
features |= QOpenGLFunctions::Multitexture |
QOpenGLFunctions::Shaders |
QOpenGLFunctions::Buffers |
QOpenGLFunctions::Framebuffers |
@ -308,7 +318,6 @@ static int qt_gl_resolve_features()
QOpenGLFunctions::CompressedTextures |
QOpenGLFunctions::Multisample |
QOpenGLFunctions::StencilSeparate;
QOpenGLExtensionMatcher extensions;
if (extensions.match("GL_IMG_texture_npot"))
features |= QOpenGLFunctions::NPOTTextures;
if (extensions.match("GL_OES_texture_npot"))
@ -320,14 +329,18 @@ static int qt_gl_resolve_features()
if (!(renderer && strstr(renderer, "Mesa")))
features |= QOpenGLFunctions::TextureRGFormats;
}
if (ctx->format().majorVersion() >= 3)
if (ctx->format().majorVersion() >= 3) {
features |= QOpenGLFunctions::MultipleRenderTargets;
if (ctx->format().minorVersion() >= 2 && extensions.match("GL_KHR_blend_equation_advanced_coherent")) {
// GL_KHR_blend_equation_advanced is included in OpenGL ES/3.2
features |= QOpenGLFunctions::BlendEquationAdvanced;
}
}
return features;
} else {
// OpenGL
int features = QOpenGLFunctions::TextureRGFormats;
features |= QOpenGLFunctions::TextureRGFormats;
QSurfaceFormat format = QOpenGLContext::currentContext()->format();
QOpenGLExtensionMatcher extensions;
if (format.majorVersion() >= 3)
features |= QOpenGLFunctions::Framebuffers | QOpenGLFunctions::MultipleRenderTargets;

View File

@ -277,7 +277,8 @@ public:
NPOTTextureRepeat = 0x2000,
FixedFunctionPipeline = 0x4000,
TextureRGFormats = 0x8000,
MultipleRenderTargets = 0x10000
MultipleRenderTargets = 0x10000,
BlendEquationAdvanced = 0x20000,
};
Q_DECLARE_FLAGS(OpenGLFeatures, OpenGLFeature)

View File

@ -87,8 +87,27 @@
#include <QDebug>
QT_BEGIN_NAMESPACE
#ifndef GL_KHR_blend_equation_advanced
#define GL_KHR_blend_equation_advanced 1
#define GL_MULTIPLY_KHR 0x9294
#define GL_SCREEN_KHR 0x9295
#define GL_OVERLAY_KHR 0x9296
#define GL_DARKEN_KHR 0x9297
#define GL_LIGHTEN_KHR 0x9298
#define GL_COLORDODGE_KHR 0x9299
#define GL_COLORBURN_KHR 0x929A
#define GL_HARDLIGHT_KHR 0x929B
#define GL_SOFTLIGHT_KHR 0x929C
#define GL_DIFFERENCE_KHR 0x929E
#define GL_EXCLUSION_KHR 0x92A0
#endif /* GL_KHR_blend_equation_advanced */
#ifndef GL_KHR_blend_equation_advanced_coherent
#define GL_KHR_blend_equation_advanced_coherent 1
#define GL_BLEND_ADVANCED_COHERENT_KHR 0x9285
#endif /* GL_KHR_blend_equation_advanced_coherent */
QT_BEGIN_NAMESPACE
Q_GUI_EXPORT QImage qt_imageForBrush(int brushStyle, bool invert);
@ -498,6 +517,21 @@ void QOpenGL2PaintEngineExPrivate::updateCompositionMode()
// NOTE: The entire paint engine works on pre-multiplied data - which is why some of these
// composition modes look odd.
// qDebug() << "QOpenGL2PaintEngineExPrivate::updateCompositionMode() - Setting GL composition mode for " << q->state()->composition_mode;
if (ctx->functions()->hasOpenGLFeature(QOpenGLFunctions::BlendEquationAdvanced)) {
if (q->state()->composition_mode <= QPainter::CompositionMode_Plus) {
funcs.glDisable(GL_BLEND_ADVANCED_COHERENT_KHR);
funcs.glBlendEquation(GL_FUNC_ADD);
} else {
funcs.glEnable(GL_BLEND_ADVANCED_COHERENT_KHR);
}
shaderManager->setCompositionMode(q->state()->composition_mode);
} else {
if (q->state()->composition_mode > QPainter::CompositionMode_Plus) {
qWarning("Unsupported composition mode");
compositionModeDirty = false;
return;
}
}
switch(q->state()->composition_mode) {
case QPainter::CompositionMode_SourceOver:
funcs.glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
@ -538,6 +572,39 @@ void QOpenGL2PaintEngineExPrivate::updateCompositionMode()
case QPainter::CompositionMode_Plus:
funcs.glBlendFunc(GL_ONE, GL_ONE);
break;
case QPainter::CompositionMode_Multiply:
funcs.glBlendEquation(GL_MULTIPLY_KHR);
break;
case QPainter::CompositionMode_Screen:
funcs.glBlendEquation(GL_SCREEN_KHR);
break;
case QPainter::CompositionMode_Overlay:
funcs.glBlendEquation(GL_OVERLAY_KHR);
break;
case QPainter::CompositionMode_Darken:
funcs.glBlendEquation(GL_DARKEN_KHR);
break;
case QPainter::CompositionMode_Lighten:
funcs.glBlendEquation(GL_LIGHTEN_KHR);
break;
case QPainter::CompositionMode_ColorDodge:
funcs.glBlendEquation(GL_COLORDODGE_KHR);
break;
case QPainter::CompositionMode_ColorBurn:
funcs.glBlendEquation(GL_COLORBURN_KHR);
break;
case QPainter::CompositionMode_HardLight:
funcs.glBlendEquation(GL_HARDLIGHT_KHR);
break;
case QPainter::CompositionMode_SoftLight:
funcs.glBlendEquation(GL_SOFTLIGHT_KHR);
break;
case QPainter::CompositionMode_Difference:
funcs.glBlendEquation(GL_DIFFERENCE_KHR);
break;
case QPainter::CompositionMode_Exclusion:
funcs.glBlendEquation(GL_EXCLUSION_KHR);
break;
default:
qWarning("Unsupported composition mode");
break;

View File

@ -497,6 +497,13 @@ static const char redefineHighp[] =
"#endif\n";
#endif
// Boiler-plate header to have the layout attributes available we need later
static const char blendEquationAdvancedHeader[] =
"#ifdef GL_KHR_blend_equation_advanced\n"
"#extension GL_ARB_fragment_coord_conventions : enable\n"
"#extension GL_KHR_blend_equation_advanced : enable\n"
"#endif\n";
struct QVersionDirectivePosition
{
Q_DECL_CONSTEXPR QVersionDirectivePosition(int position = 0, int line = -1)
@ -637,6 +644,10 @@ bool QOpenGLShader::compileSourceCode(const char *source)
}
}
}
if (d->shaderType == Fragment) {
sourceChunks.append(blendEquationAdvancedHeader);
sourceChunkLengths.append(GLint(sizeof(blendEquationAdvancedHeader) - 1));
}
// The precision qualifiers are useful on OpenGL/ES systems,
// but usually not present on desktop systems.