From 96efeab991de419eee294249e990941f8bd0bd6b Mon Sep 17 00:00:00 2001 From: Matt Woodrow Date: Fri, 9 May 2014 22:06:18 +1200 Subject: [PATCH] Bug 950312 - Part 5: Implement SCREEN and MULTIPLY for CompositorOGL. r=mtseng --- gfx/layers/opengl/CompositorOGL.cpp | 75 ++++++++++++++++++++++---- gfx/layers/opengl/CompositorOGL.h | 18 ++++--- gfx/layers/opengl/OGLShaderProgram.cpp | 9 ++++ gfx/layers/opengl/OGLShaderProgram.h | 4 +- 4 files changed, 88 insertions(+), 18 deletions(-) diff --git a/gfx/layers/opengl/CompositorOGL.cpp b/gfx/layers/opengl/CompositorOGL.cpp index 47f96de7a720..79d0c348d2ff 100644 --- a/gfx/layers/opengl/CompositorOGL.cpp +++ b/gfx/layers/opengl/CompositorOGL.cpp @@ -803,7 +803,9 @@ CompositorOGL::CreateFBOWithTexture(const IntRect& aRect, bool aCopyFromSource, } ShaderConfigOGL -CompositorOGL::GetShaderConfigFor(Effect *aEffect, MaskType aMask) const +CompositorOGL::GetShaderConfigFor(Effect *aEffect, + MaskType aMask, + gfx::CompositionOp aOp) const { ShaderConfigOGL config; @@ -841,6 +843,12 @@ CompositorOGL::GetShaderConfigFor(Effect *aEffect, MaskType aMask) const source->GetFormat() == gfx::SurfaceFormat::R5G6B5); config = ShaderConfigFromTargetAndFormat(source->GetTextureTarget(), source->GetFormat()); + if (aOp == gfx::CompositionOp::OP_MULTIPLY && + !texturedEffect->mPremultiplied) { + // We can do these blend modes just using glBlendFunc but we need the data + // to be premultiplied first. + config.SetPremultiply(true); + } break; } } @@ -886,6 +894,40 @@ CompositorOGL::DrawLines(const std::vector& aLines, const gfx::Rect& } } +static bool SetBlendMode(GLContext* aGL, gfx::CompositionOp aBlendMode, bool aIsPremultiplied = true) +{ + if (aBlendMode == gfx::CompositionOp::OP_OVER && aIsPremultiplied) { + return false; + } + + GLenum srcBlend; + GLenum dstBlend; + + switch (aBlendMode) { + case gfx::CompositionOp::OP_OVER: + MOZ_ASSERT(!aIsPremultiplied); + srcBlend = LOCAL_GL_SRC_ALPHA; + dstBlend = LOCAL_GL_ONE_MINUS_SRC_ALPHA; + break; + case gfx::CompositionOp::OP_SCREEN: + srcBlend = aIsPremultiplied ? LOCAL_GL_ONE : LOCAL_GL_SRC_ALPHA; + dstBlend = LOCAL_GL_ONE_MINUS_SRC_COLOR; + break; + case gfx::CompositionOp::OP_MULTIPLY: + // If the source data was un-premultiplied we should have already + // asked the fragment shader to fix that. + srcBlend = LOCAL_GL_DST_COLOR; + dstBlend = LOCAL_GL_ONE_MINUS_SRC_ALPHA; + break; + default: + MOZ_ASSERT(0, "Unsupported blend mode!"); + } + + aGL->fBlendFuncSeparate(srcBlend, dstBlend, + LOCAL_GL_ONE, LOCAL_GL_ONE); + return true; +} + void CompositorOGL::DrawQuadInternal(const Rect& aRect, const Rect& aClipRect, @@ -963,7 +1005,14 @@ CompositorOGL::DrawQuadInternal(const Rect& aRect, aOpacity = 1.f; } - ShaderConfigOGL config = GetShaderConfigFor(aEffectChain.mPrimaryEffect, maskType); + gfx::CompositionOp blendMode = gfx::CompositionOp::OP_OVER; + if (aEffectChain.mSecondaryEffects[EffectTypes::BLEND_MODE]) { + EffectBlendMode *blendEffect = + static_cast(aEffectChain.mSecondaryEffects[EffectTypes::BLEND_MODE].get()); + blendMode = blendEffect->mBlendMode; + } + + ShaderConfigOGL config = GetShaderConfigFor(aEffectChain.mPrimaryEffect, maskType, blendMode); config.SetOpacity(aOpacity != 1.f); ShaderProgramOGL *program = GetShaderProgramFor(config); program->Activate(); @@ -982,6 +1031,8 @@ CompositorOGL::DrawQuadInternal(const Rect& aRect, program->SetTexCoordMultiplier(source->GetSize().width, source->GetSize().height); } + bool didSetBlendMode = false; + switch (aEffectChain.mPrimaryEffect->mType) { case EffectTypes::SOLID_COLOR: { program->SetRenderColor(color); @@ -990,6 +1041,8 @@ CompositorOGL::DrawQuadInternal(const Rect& aRect, BindMaskForProgram(program, sourceMask, LOCAL_GL_TEXTURE0, maskQuadTransform); } + didSetBlendMode = SetBlendMode(gl(), blendMode); + BindAndDrawQuad(program, aDrawMode); } break; @@ -999,10 +1052,7 @@ CompositorOGL::DrawQuadInternal(const Rect& aRect, static_cast(aEffectChain.mPrimaryEffect.get()); TextureSource *source = texturedEffect->mTexture; - if (!texturedEffect->mPremultiplied) { - mGLContext->fBlendFuncSeparate(LOCAL_GL_SRC_ALPHA, LOCAL_GL_ONE_MINUS_SRC_ALPHA, - LOCAL_GL_ONE, LOCAL_GL_ONE); - } + didSetBlendMode = SetBlendMode(gl(), blendMode, texturedEffect->mPremultiplied); gfx::Filter filter = texturedEffect->mFilter; gfx3DMatrix textureTransform; @@ -1030,11 +1080,6 @@ CompositorOGL::DrawQuadInternal(const Rect& aRect, BindAndDrawQuadWithTextureRect(program, textureTransform, texturedEffect->mTextureCoords, source); - - if (!texturedEffect->mPremultiplied) { - mGLContext->fBlendFuncSeparate(LOCAL_GL_ONE, LOCAL_GL_ONE_MINUS_SRC_ALPHA, - LOCAL_GL_ONE, LOCAL_GL_ONE); - } } break; case EffectTypes::YCBCR: { @@ -1060,6 +1105,7 @@ CompositorOGL::DrawQuadInternal(const Rect& aRect, if (maskType != MaskType::MaskNone) { BindMaskForProgram(program, sourceMask, LOCAL_GL_TEXTURE3, maskQuadTransform); } + didSetBlendMode = SetBlendMode(gl(), blendMode); BindAndDrawQuadWithTextureRect(program, gfx3DMatrix(), effectYCbCr->mTextureCoords, @@ -1096,11 +1142,13 @@ CompositorOGL::DrawQuadInternal(const Rect& aRect, // Drawing is always flipped, but when copying between surfaces we want to avoid // this. Pass true for the flip parameter to introduce a second flip // that cancels the other one out. + didSetBlendMode = SetBlendMode(gl(), blendMode); BindAndDrawQuad(program); } break; case EffectTypes::COMPONENT_ALPHA: { MOZ_ASSERT(gfxPrefs::ComponentAlphaEnabled()); + MOZ_ASSERT(blendMode == gfx::CompositionOp::OP_OVER, "Can't support blend modes with component alpha!"); EffectComponentAlpha* effectComponentAlpha = static_cast(aEffectChain.mPrimaryEffect.get()); TextureSourceOGL* sourceOnWhite = effectComponentAlpha->mOnWhite->AsSourceOGL(); @@ -1149,6 +1197,11 @@ CompositorOGL::DrawQuadInternal(const Rect& aRect, break; } + if (didSetBlendMode) { + gl()->fBlendFuncSeparate(LOCAL_GL_ONE, LOCAL_GL_ONE_MINUS_SRC_ALPHA, + LOCAL_GL_ONE, LOCAL_GL_ONE); + } + // in case rendering has used some other GL context MakeCurrent(); } diff --git a/gfx/layers/opengl/CompositorOGL.h b/gfx/layers/opengl/CompositorOGL.h index 421343028b97..d65313af0b3f 100644 --- a/gfx/layers/opengl/CompositorOGL.h +++ b/gfx/layers/opengl/CompositorOGL.h @@ -178,11 +178,15 @@ public: virtual TextureFactoryIdentifier GetTextureFactoryIdentifier() MOZ_OVERRIDE { - return TextureFactoryIdentifier(LayersBackend::LAYERS_OPENGL, - XRE_GetProcessType(), - GetMaxTextureSize(), - mFBOTextureTarget == LOCAL_GL_TEXTURE_2D, - SupportsPartialTextureUpdate()); + TextureFactoryIdentifier result = + TextureFactoryIdentifier(LayersBackend::LAYERS_OPENGL, + XRE_GetProcessType(), + GetMaxTextureSize(), + mFBOTextureTarget == LOCAL_GL_TEXTURE_2D, + SupportsPartialTextureUpdate()); + result.mSupportedBlendModes += gfx::CompositionOp::OP_SCREEN; + result.mSupportedBlendModes += gfx::CompositionOp::OP_MULTIPLY; + return result; } virtual TemporaryRef @@ -360,7 +364,9 @@ private: gfx::Rect *aClipRectOut = nullptr, gfx::Rect *aRenderBoundsOut = nullptr) MOZ_OVERRIDE; - ShaderConfigOGL GetShaderConfigFor(Effect *aEffect, MaskType aMask = MaskType::MaskNone) const; + ShaderConfigOGL GetShaderConfigFor(Effect *aEffect, + MaskType aMask = MaskType::MaskNone, + gfx::CompositionOp aOp = gfx::CompositionOp::OP_OVER) const; ShaderProgramOGL* GetShaderProgramFor(const ShaderConfigOGL &aConfig); /** diff --git a/gfx/layers/opengl/OGLShaderProgram.cpp b/gfx/layers/opengl/OGLShaderProgram.cpp index 6a44e80682c8..5ef580672c61 100644 --- a/gfx/layers/opengl/OGLShaderProgram.cpp +++ b/gfx/layers/opengl/OGLShaderProgram.cpp @@ -130,6 +130,12 @@ ShaderConfigOGL::SetMask3D(bool aEnabled) SetFeature(ENABLE_MASK_3D, aEnabled); } +void +ShaderConfigOGL::SetPremultiply(bool aEnabled) +{ + SetFeature(ENABLE_PREMULTIPLY, aEnabled); +} + /* static */ ProgramProfileOGL ProgramProfileOGL::GetProfileFor(ShaderConfigOGL aConfig) { @@ -323,6 +329,9 @@ ProgramProfileOGL::GetProfileFor(ShaderConfigOGL aConfig) if (aConfig.mFeatures & ENABLE_OPACITY) { fs << " color *= uLayerOpacity;" << endl; } + if (aConfig.mFeatures & ENABLE_PREMULTIPLY) { + fs << " color.rgb *= color.a;" << endl; + } } if (aConfig.mFeatures & ENABLE_MASK_3D) { fs << " vec2 maskCoords = vMaskCoord.xy / vMaskCoord.z;" << endl; diff --git a/gfx/layers/opengl/OGLShaderProgram.h b/gfx/layers/opengl/OGLShaderProgram.h index 1fca423045e8..0e3f1980bb4a 100644 --- a/gfx/layers/opengl/OGLShaderProgram.h +++ b/gfx/layers/opengl/OGLShaderProgram.h @@ -41,7 +41,8 @@ enum ShaderFeatures { ENABLE_BLUR=0x100, ENABLE_COLOR_MATRIX=0x200, ENABLE_MASK_2D=0x400, - ENABLE_MASK_3D=0x800 + ENABLE_MASK_3D=0x800, + ENABLE_PREMULTIPLY=0x1000 }; class KnownUniform { @@ -171,6 +172,7 @@ public: void SetBlur(bool aEnabled); void SetMask2D(bool aEnabled); void SetMask3D(bool aEnabled); + void SetPremultiply(bool aEnabled); bool operator< (const ShaderConfigOGL& other) const { return mFeatures < other.mFeatures;