From 3c884232fd9af84df7e061897af4aab6727bef21 Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Mon, 12 May 2025 13:27:14 +0900 Subject: [PATCH] gl_engine: rewrite the blending shader Completely rewrite the GL backend shader to support advance blending and correct the blending behavior. Now the GL backend has the same blending result as SKIA and CanvasRenderingContext2D in browers did. The formula is referenced from: https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators issue: https://github.com/thorvg/thorvg/issues/2799 --- src/renderer/gl_engine/tvgGlCommon.h | 2 +- src/renderer/gl_engine/tvgGlProgram.cpp | 4 + src/renderer/gl_engine/tvgGlProgram.h | 5 + src/renderer/gl_engine/tvgGlRenderPass.cpp | 4 +- src/renderer/gl_engine/tvgGlRenderPass.h | 7 +- src/renderer/gl_engine/tvgGlRenderTarget.cpp | 10 +- src/renderer/gl_engine/tvgGlRenderTarget.h | 7 +- src/renderer/gl_engine/tvgGlRenderTask.cpp | 25 +-- src/renderer/gl_engine/tvgGlRenderTask.h | 8 +- src/renderer/gl_engine/tvgGlRenderer.cpp | 146 +++++++++------- src/renderer/gl_engine/tvgGlRenderer.h | 9 +- src/renderer/gl_engine/tvgGlShaderSrc.cpp | 174 +++++++++++++------ src/renderer/gl_engine/tvgGlShaderSrc.h | 5 +- 13 files changed, 256 insertions(+), 150 deletions(-) diff --git a/src/renderer/gl_engine/tvgGlCommon.h b/src/renderer/gl_engine/tvgGlCommon.h index 1dc2492d..d95ebf67 100644 --- a/src/renderer/gl_engine/tvgGlCommon.h +++ b/src/renderer/gl_engine/tvgGlCommon.h @@ -165,7 +165,7 @@ struct GlRadialGradientBlock struct GlCompositor : RenderCompositor { RenderRegion bbox = {}; - + BlendMethod blend = BlendMethod::Normal; GlCompositor(const RenderRegion& box) : bbox(box) {} }; diff --git a/src/renderer/gl_engine/tvgGlProgram.cpp b/src/renderer/gl_engine/tvgGlProgram.cpp index e3ac313d..a16ce38b 100644 --- a/src/renderer/gl_engine/tvgGlProgram.cpp +++ b/src/renderer/gl_engine/tvgGlProgram.cpp @@ -101,13 +101,17 @@ int32_t GlProgram::getAttributeLocation(const char* name) int32_t GlProgram::getUniformLocation(const char* name) { + if (mUniformLocation.count(name)) return mUniformLocation[name]; GL_CHECK(int32_t location = glGetUniformLocation(mProgramObj, name)); + mUniformLocation[name] = location; return location; } int32_t GlProgram::getUniformBlockIndex(const char* name) { + if (mUniformBlock.count(name)) return mUniformBlock[name]; GL_CHECK(int32_t index = glGetUniformBlockIndex(mProgramObj, name)); + mUniformBlock[name] = index; return index; } diff --git a/src/renderer/gl_engine/tvgGlProgram.h b/src/renderer/gl_engine/tvgGlProgram.h index d6d65e29..fabf08b0 100644 --- a/src/renderer/gl_engine/tvgGlProgram.h +++ b/src/renderer/gl_engine/tvgGlProgram.h @@ -25,6 +25,9 @@ #include "tvgGlShader.h" +#include +#include + class GlProgram { public: @@ -49,6 +52,8 @@ public: private: uint32_t mProgramObj; + std::unordered_map mUniformLocation; + std::unordered_map mUniformBlock; static uint32_t mCurrentProgram; }; diff --git a/src/renderer/gl_engine/tvgGlRenderPass.cpp b/src/renderer/gl_engine/tvgGlRenderPass.cpp index 743b5abd..74764674 100644 --- a/src/renderer/gl_engine/tvgGlRenderPass.cpp +++ b/src/renderer/gl_engine/tvgGlRenderPass.cpp @@ -25,9 +25,9 @@ #include "tvgGlRenderPass.h" #include "tvgGlRenderTask.h" -GlRenderPass::GlRenderPass(GlRenderTarget* fbo): mFbo(fbo), mTasks(), mDrawDepth(0) {} +GlRenderPass::GlRenderPass(GlRenderTarget* fbo, RenderRegion viewport): mFbo(fbo), mViewport(viewport), mTasks(), mDrawDepth(0) {} -GlRenderPass::GlRenderPass(GlRenderPass&& other): mFbo(other.mFbo), mTasks(), mDrawDepth(0) +GlRenderPass::GlRenderPass(GlRenderPass&& other): mFbo(other.mFbo), mViewport(other.mViewport), mTasks(), mDrawDepth(0) { mTasks.push(other.mTasks); diff --git a/src/renderer/gl_engine/tvgGlRenderPass.h b/src/renderer/gl_engine/tvgGlRenderPass.h index 96890f06..da254bd7 100644 --- a/src/renderer/gl_engine/tvgGlRenderPass.h +++ b/src/renderer/gl_engine/tvgGlRenderPass.h @@ -32,7 +32,7 @@ class GlProgram; class GlRenderPass { public: - GlRenderPass(GlRenderTarget* fbo); + GlRenderPass(GlRenderTarget* fbo, RenderRegion viewport); GlRenderPass(GlRenderPass&& other); ~GlRenderPass(); @@ -45,7 +45,7 @@ public: GLuint getTextureId() { return mFbo->getColorTexture(); } - const RenderRegion& getViewport() const { return mFbo->getViewport(); } + const RenderRegion& getViewport() const { return mViewport; } uint32_t getFboWidth() const { return mFbo->getWidth(); } @@ -63,7 +63,7 @@ public: auto task = new T(program, targetFbo, mFbo, std::move(mTasks)); - const auto& vp = mFbo->getViewport(); + const auto& vp = mViewport; task->setRenderSize(static_cast(vp.w), static_cast(vp.h)); @@ -77,6 +77,7 @@ public: GlRenderTarget* getFbo() { return mFbo; } private: GlRenderTarget* mFbo; + RenderRegion mViewport; Array mTasks = {}; int32_t mDrawDepth = 0; }; diff --git a/src/renderer/gl_engine/tvgGlRenderTarget.cpp b/src/renderer/gl_engine/tvgGlRenderTarget.cpp index 47c2f831..4837d41d 100644 --- a/src/renderer/gl_engine/tvgGlRenderTarget.cpp +++ b/src/renderer/gl_engine/tvgGlRenderTarget.cpp @@ -29,6 +29,7 @@ GlRenderTarget::~GlRenderTarget() if (mFbo == 0) return; GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, 0)); GL_CHECK(glDeleteFramebuffers(1, &mFbo)); + GL_CHECK(glDeleteFramebuffers(1, &mResolveFbo)); if (mColorTex != 0) { GL_CHECK(glDeleteTextures(1, &mColorTex)); @@ -36,6 +37,9 @@ GlRenderTarget::~GlRenderTarget() if (mDepthStencilBuffer != 0) { GL_CHECK(glDeleteRenderbuffers(1, &mDepthStencilBuffer)); } + if (mColorBuffer!= 0) { + GL_CHECK(glDeleteRenderbuffers(1, &mColorBuffer)); + } } void GlRenderTarget::init(GLint resolveId) @@ -119,15 +123,15 @@ GlRenderTarget* GlRenderTargetPool::getRenderTarget(const RenderRegion& vp, GLui for (uint32_t i = 0; i < mPool.count; i++) { auto rt = mPool[i]; - if (rt->getWidth() == width && rt->getHeight() == height) { - rt->setViewport(vp); + if (rt->getWidth() == width && rt->getHeight() == height && !rt->isInUse()) { + rt->setInUse(true); return rt; } } auto rt = new GlRenderTarget(width, height); rt->init(resolveId); - rt->setViewport(vp); + rt->setInUse(true); mPool.push(rt); return rt; } diff --git a/src/renderer/gl_engine/tvgGlRenderTarget.h b/src/renderer/gl_engine/tvgGlRenderTarget.h index 2cc9733b..f85f9e4e 100644 --- a/src/renderer/gl_engine/tvgGlRenderTarget.h +++ b/src/renderer/gl_engine/tvgGlRenderTarget.h @@ -45,16 +45,19 @@ public: const RenderRegion& getViewport() const { return mViewport; } bool invalid() const { return mFbo == GL_INVALID_VALUE; } + bool isInUse() const { return mInUse; } + void setInUse(bool inUse) { mInUse = inUse; } private: - uint32_t mWidth = 0; - uint32_t mHeight = 0; + uint32_t mWidth; + uint32_t mHeight; RenderRegion mViewport{}; GLuint mFbo = GL_INVALID_VALUE; GLuint mColorBuffer = 0; GLuint mDepthStencilBuffer = 0; GLuint mResolveFbo = 0; GLuint mColorTex = 0; + bool mInUse = false; }; class GlRenderTargetPool { diff --git a/src/renderer/gl_engine/tvgGlRenderTask.cpp b/src/renderer/gl_engine/tvgGlRenderTask.cpp index 0464b35f..f354e59e 100644 --- a/src/renderer/gl_engine/tvgGlRenderTask.cpp +++ b/src/renderer/gl_engine/tvgGlRenderTask.cpp @@ -332,12 +332,11 @@ void GlSimpleBlendTask::run() glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); } -GlComplexBlendTask::GlComplexBlendTask(GlProgram* program, GlRenderTarget* dstFbo, GlRenderTarget* dstCopyFbo, GlRenderTask* stencilTask, GlComposeTask* composeTask) - : GlRenderTask(program), mDstFbo(dstFbo), mDstCopyFbo(dstCopyFbo), mStencilTask(stencilTask), mComposeTask(composeTask) {} +GlComplexBlendTask::GlComplexBlendTask(GlProgram* program, GlRenderTarget* dstFbo, GlRenderTarget* dstCopyFbo, GlComposeTask* composeTask) + : GlRenderTask(program), mDstFbo(dstFbo), mDstCopyFbo(dstCopyFbo), mComposeTask(composeTask) {} GlComplexBlendTask::~GlComplexBlendTask() { - delete mStencilTask; delete mComposeTask; } @@ -349,8 +348,8 @@ void GlComplexBlendTask::run() GL_CHECK(glBindFramebuffer(GL_READ_FRAMEBUFFER, mDstFbo->getFboId())); GL_CHECK(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mDstCopyFbo->getResolveFboId())); - GL_CHECK(glViewport(0, 0, mDstFbo->getViewport().w, mDstFbo->getViewport().h)); - GL_CHECK(glScissor(0, 0, mDstFbo->getViewport().w, mDstFbo->getViewport().h)); + GL_CHECK(glViewport(0, 0, mViewport.w, mViewport.h)); + GL_CHECK(glScissor(0, 0, mViewport.w, mViewport.h)); const auto& vp = getViewport(); @@ -358,21 +357,6 @@ void GlComplexBlendTask::run() GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, mDstFbo->getFboId())); - GL_CHECK(glEnable(GL_STENCIL_TEST)); - GL_CHECK(glColorMask(0, 0, 0, 0)); - GL_CHECK(glStencilFuncSeparate(GL_FRONT, GL_ALWAYS, 0x0, 0xFF)); - GL_CHECK(glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_KEEP, GL_INCR_WRAP)); - - GL_CHECK(glStencilFuncSeparate(GL_BACK, GL_ALWAYS, 0x0, 0xFF)); - GL_CHECK(glStencilOpSeparate(GL_BACK, GL_KEEP, GL_KEEP, GL_DECR_WRAP)); - - - mStencilTask->run(); - - GL_CHECK(glColorMask(1, 1, 1, 1)); - GL_CHECK(glStencilFunc(GL_NOTEQUAL, 0x0, 0xFF)); - GL_CHECK(glStencilOp(GL_REPLACE, GL_KEEP, GL_REPLACE)); - GL_CHECK(glBlendFunc(GL_ONE, GL_ZERO)); GlRenderTask::run(); @@ -383,7 +367,6 @@ void GlComplexBlendTask::run() void GlComplexBlendTask::normalizeDrawDepth(int32_t maxDepth) { - mStencilTask->normalizeDrawDepth(maxDepth); GlRenderTask::normalizeDrawDepth(maxDepth); } diff --git a/src/renderer/gl_engine/tvgGlRenderTask.h b/src/renderer/gl_engine/tvgGlRenderTask.h index d6fa93f2..4622e795 100644 --- a/src/renderer/gl_engine/tvgGlRenderTask.h +++ b/src/renderer/gl_engine/tvgGlRenderTask.h @@ -206,17 +206,21 @@ private: class GlComplexBlendTask: public GlRenderTask { public: - GlComplexBlendTask(GlProgram* program, GlRenderTarget* dstFbo, GlRenderTarget* dstCopyFbo, GlRenderTask* stencilTask, GlComposeTask* composeTask); + GlComplexBlendTask(GlProgram* program, GlRenderTarget* dstFbo, GlRenderTarget* dstCopyFbo, GlComposeTask* composeTask); ~GlComplexBlendTask() override; void run() override; void normalizeDrawDepth(int32_t maxDepth) override; + + void setViewport(const RenderRegion& viewport) { mViewport = viewport; } + void setCopyViewport(const RenderRegion& viewport) { mCopyViewport = viewport; } private: GlRenderTarget* mDstFbo; GlRenderTarget* mDstCopyFbo; - GlRenderTask* mStencilTask; GlComposeTask* mComposeTask; + RenderRegion mViewport = {}; + RenderRegion mCopyViewport = {}; }; class GlGaussianBlurTask: public GlRenderTask diff --git a/src/renderer/gl_engine/tvgGlRenderer.cpp b/src/renderer/gl_engine/tvgGlRenderer.cpp index e177854e..afab0e3e 100644 --- a/src/renderer/gl_engine/tvgGlRenderer.cpp +++ b/src/renderer/gl_engine/tvgGlRenderer.cpp @@ -156,6 +156,8 @@ void GlRenderer::initShaders() mPrograms.push(new GlProgram(MASK_VERT_SHADER, SOFT_LIGHT_BLEND_FRAG)); mPrograms.push(new GlProgram(MASK_VERT_SHADER, DIFFERENCE_BLEND_FRAG)); mPrograms.push(new GlProgram(MASK_VERT_SHADER, EXCLUSION_BLEND_FRAG)); + mPrograms.push(new GlProgram(MASK_VERT_SHADER, LIGHTEN_BLEND_FRAG)); + mPrograms.push(new GlProgram(MASK_VERT_SHADER, DARKEN_BLEND_FRAG)); // effects mPrograms.push(new GlProgram(EFFECT_VERTEX, GAUSSIAN_VERTICAL)); @@ -174,7 +176,7 @@ void GlRenderer::drawPrimitive(GlShape& sdata, const RenderColor& c, RenderUpdat bbox.intersect(vp); - auto complexBlend = beginComplexBlending(bbox, sdata.geometry.getBounds()); + auto complexBlend = beginComplexBlending(mBlendMethod, bbox, sdata.geometry.getBounds()); if (complexBlend) { vp = currentPass()->getViewport(); @@ -254,11 +256,7 @@ void GlRenderer::drawPrimitive(GlShape& sdata, const RenderColor& c, RenderUpdat if (stencilTask) currentPass()->addRenderTask(new GlStencilCoverTask(stencilTask, task, stencilMode)); else currentPass()->addRenderTask(task); - if (complexBlend) { - auto task = new GlRenderTask(mPrograms[RT_Stencil]); - sdata.geometry.draw(task, &mGpuBuffer, flag); - endBlendingCompose(task, sdata.geometry.matrix); - } + if (complexBlend) endBlendingCompose(mBlendMethod); } @@ -290,7 +288,7 @@ void GlRenderer::drawPrimitive(GlShape& sdata, const Fill* fill, RenderUpdateFla return; } - auto complexBlend = beginComplexBlending(bbox, sdata.geometry.getBounds()); + auto complexBlend = beginComplexBlending(mBlendMethod, bbox, sdata.geometry.getBounds()); if (complexBlend) vp = currentPass()->getViewport(); @@ -441,11 +439,7 @@ void GlRenderer::drawPrimitive(GlShape& sdata, const Fill* fill, RenderUpdateFla currentPass()->addRenderTask(task); } - if (complexBlend) { - auto task = new GlRenderTask(mPrograms[RT_Stencil]); - sdata.geometry.draw(task, &mGpuBuffer, flag); - endBlendingCompose(task, sdata.geometry.matrix); - } + if (complexBlend) endBlendingCompose(mBlendMethod); } @@ -539,7 +533,7 @@ GlRenderPass* GlRenderer::currentPass() return mRenderPassStack.last(); } -bool GlRenderer::beginComplexBlending(const RenderRegion& vp, RenderRegion bounds) +bool GlRenderer::beginComplexBlending(BlendMethod blend, const RenderRegion& vp, RenderRegion bounds) { if (vp.w == 0 || vp.h == 0) return false; @@ -547,18 +541,18 @@ bool GlRenderer::beginComplexBlending(const RenderRegion& vp, RenderRegion bound if (bounds.w == 0 || bounds.h == 0) return false; - if (mBlendMethod == BlendMethod::Normal || mBlendMethod == BlendMethod::Add || mBlendMethod == BlendMethod::Darken || mBlendMethod == BlendMethod::Lighten) return false; + if (blend == BlendMethod::Normal || blend == BlendMethod::Add) return false; if (mBlendPool.empty()) mBlendPool.push(new GlRenderTargetPool(surface.w, surface.h)); auto blendFbo = mBlendPool[0]->getRenderTarget(bounds); - mRenderPassStack.push(new GlRenderPass(blendFbo)); + mRenderPassStack.push(new GlRenderPass(blendFbo, bounds)); return true; } -void GlRenderer::endBlendingCompose(GlRenderTask* stencilTask, const Matrix& matrix) +void GlRenderer::endBlendingCompose(BlendMethod blend) { auto blendPass = mRenderPassStack.last(); mRenderPassStack.pop(); @@ -571,34 +565,10 @@ void GlRenderer::endBlendingCompose(GlRenderTask* stencilTask, const Matrix& mat if (mBlendPool.count < 2) mBlendPool.push(new GlRenderTargetPool(surface.w, surface.h)); auto dstCopyFbo = mBlendPool[1]->getRenderTarget(vp); - { - const auto& passVp = currentPass()->getViewport(); + auto task = new GlComplexBlendTask(getBlendProgram(blend), currentPass()->getFbo(), dstCopyFbo, composeTask); - auto x = vp.x; - auto y = vp.y; - auto w = vp.w; - auto h = vp.h; - - stencilTask->setViewport({x, passVp.h - y - h, w, h}); - } - - stencilTask->setDrawDepth(currentPass()->nextDrawDepth()); - - { - // set view matrix - float matrix44[16]; - currentPass()->getMatrix(matrix44, matrix); - uint32_t viewOffset = mGpuBuffer.push(matrix44, 16 * sizeof(float), true); - stencilTask->addBindResource(GlBindingResource{ - 0, - stencilTask->getProgram()->getUniformBlockIndex("Matrix"), - mGpuBuffer.getBufferId(), - viewOffset, - 16 * sizeof(float), - }); - } - - auto task = new GlComplexBlendTask(getBlendProgram(), currentPass()->getFbo(), dstCopyFbo, stencilTask, composeTask); + task->setViewport(currentPass()->getViewport()); + task->setCopyViewport(blendPass->getViewport()); prepareCmpTask(task, vp, blendPass->getFboWidth(), blendPass->getFboHeight()); @@ -613,9 +583,9 @@ void GlRenderer::endBlendingCompose(GlRenderTask* stencilTask, const Matrix& mat delete(blendPass); } -GlProgram* GlRenderer::getBlendProgram() +GlProgram* GlRenderer::getBlendProgram(BlendMethod blend) { - switch (mBlendMethod) { + switch (blend) { case BlendMethod::Multiply: return mPrograms[RT_MultiplyBlend]; case BlendMethod::Screen: return mPrograms[RT_ScreenBlend]; case BlendMethod::Overlay: return mPrograms[RT_OverlayBlend]; @@ -625,6 +595,8 @@ GlProgram* GlRenderer::getBlendProgram() case BlendMethod::SoftLight: return mPrograms[RT_SoftLightBlend]; case BlendMethod::Difference: return mPrograms[RT_DifferenceBlend]; case BlendMethod::Exclusion: return mPrograms[RT_ExclusionBlend]; + case BlendMethod::Lighten: return mPrograms[RT_LightenBlend]; + case BlendMethod::Darken: return mPrograms[RT_DarkenBlend]; default: return nullptr; } } @@ -711,6 +683,41 @@ void GlRenderer::prepareCmpTask(GlRenderTask* task, const RenderRegion& vp, uint task->setViewport({x, static_cast((passVp.h - y - h)), w, h}); } +void GlRenderer::prepareCmpStencilTask(GlRenderTask* task, RenderRegion& vp) +{ + auto x = 0; + auto y = 0; + auto w = vp.w; + auto h = vp.h; + + Array vertices(4 * 2); + // left top + vertices.push(x); + vertices.push(y); + // left bottom + vertices.push(x); + vertices.push(y + h); + // right top + vertices.push(x + w); + vertices.push(y); + // right bottom + vertices.push(x + w); + vertices.push(y + h); + + Array indices(6); + indices.push(0); + indices.push(1); + indices.push(2); + indices.push(2); + indices.push(1); + indices.push(3); + + uint32_t vertexOffset = mGpuBuffer.push(vertices.data, vertices.count * sizeof(float)); + uint32_t indexOffset = mGpuBuffer.pushIndex(indices.data, indices.count * sizeof(uint32_t)); + task->addVertexLayout(GlVertexLayout{0, 2, 2 * sizeof(float), vertexOffset}); + task->setDrawRange(indexOffset, indices.count); +} + void GlRenderer::endRenderPass(RenderCompositor* cmp) { @@ -723,6 +730,8 @@ void GlRenderer::endRenderPass(RenderCompositor* cmp) auto maskPass = mRenderPassStack.last(); mRenderPassStack.pop(); + bool complexBlend = beginComplexBlending(glCmp->blend, glCmp->bbox, glCmp->bbox); + GlProgram* program = nullptr; switch(cmp->method) { case MaskMethod::Alpha: program = mPrograms[RT_MaskAlpha]; break; @@ -758,6 +767,11 @@ void GlRenderer::endRenderPass(RenderCompositor* cmp) currentPass()->addRenderTask(compose_task); } + if (complexBlend) endBlendingCompose(glCmp->blend); + + maskPass->getFbo()->setInUse(false); + selfPass->getFbo()->setInUse(false); + delete(selfPass); delete(maskPass); } else { @@ -766,6 +780,8 @@ void GlRenderer::endRenderPass(RenderCompositor* cmp) mRenderPassStack.pop(); if (!renderPass->isEmpty()) { + bool complexBlend = beginComplexBlending(glCmp->blend, glCmp->bbox, glCmp->bbox); + auto task = renderPass->endRenderPass(mPrograms[RT_Image], currentPass()->getFboId()); task->setRenderSize(static_cast(glCmp->bbox.w), static_cast(glCmp->bbox.h)); prepareCmpTask(task, glCmp->bbox, renderPass->getFboWidth(), renderPass->getFboHeight()); @@ -793,10 +809,22 @@ void GlRenderer::endRenderPass(RenderCompositor* cmp) 4 * sizeof(uint32_t), }); + task->addBindResource(GlBindingResource{ + 1, + task->getProgram()->getUniformBlockIndex("ColorInfo"), + mGpuBuffer.getBufferId(), + mGpuBuffer.push(info, 4 * sizeof(uint32_t), true), + 4 * sizeof(uint32_t), + }); + // texture id task->addBindResource(GlBindingResource{0, renderPass->getTextureId(), task->getProgram()->getUniformLocation("uTexture")}); task->setParentSize(static_cast(currentPass()->getViewport().w), static_cast(currentPass()->getViewport().h)); currentPass()->addRenderTask(std::move(task)); + + if (complexBlend) endBlendingCompose(glCmp->blend); + renderPass->getFbo()->setInUse(false); + } delete(renderPass); } @@ -902,7 +930,7 @@ bool GlRenderer::preRender() currentContext(); if (mPrograms.empty()) initShaders(); - mRenderPassStack.push(new GlRenderPass(&mRootTarget)); + mRenderPassStack.push(new GlRenderPass(&mRootTarget, RenderRegion{0, 0, static_cast(surface.w), static_cast(surface.h)})); return true; } @@ -940,13 +968,14 @@ bool GlRenderer::beginComposite(RenderCompositor* cmp, MaskMethod method, uint8_ } auto glCmp = static_cast(cmp); + glCmp->blend = mBlendMethod; if (glCmp->bbox.w > 0 && glCmp->bbox.h > 0) { auto renderTarget = mComposePool[index]->getRenderTarget(glCmp->bbox); - mRenderPassStack.push(new GlRenderPass(renderTarget)); + mRenderPassStack.push(new GlRenderPass(renderTarget, glCmp->bbox)); } else { // empty render pass - mRenderPassStack.push(new GlRenderPass(nullptr)); + mRenderPassStack.push(new GlRenderPass(nullptr, glCmp->bbox)); } return true; @@ -1265,6 +1294,10 @@ bool GlRenderer::renderImage(void* data) auto bbox = sdata->geometry.viewport; + bool complexBlend = beginComplexBlending(mBlendMethod, bbox, sdata->geometry.getBounds()); + + if (complexBlend) vp = currentPass()->getViewport(); + bbox.intersect(vp); if (bbox.w <= 0 || bbox.h <= 0) return true; @@ -1277,7 +1310,10 @@ bool GlRenderer::renderImage(void* data) if (!sdata->clips.empty()) drawClip(sdata->clips); - auto task = new GlRenderTask(mPrograms[RT_Image]); + GlRenderTask* task = nullptr; + if (mBlendMethod != BlendMethod::Normal && !complexBlend) task = new GlSimpleBlendTask(mBlendMethod, mPrograms[RT_Image]); + else task = new GlRenderTask(mPrograms[RT_Image]); + task->setDrawDepth(drawDepth); if (!sdata->geometry.draw(task, &mGpuBuffer, RenderUpdateFlag::Image)) { @@ -1285,10 +1321,6 @@ bool GlRenderer::renderImage(void* data) return true; } - bool complexBlend = beginComplexBlending(bbox, sdata->geometry.getBounds()); - - if (complexBlend) vp = currentPass()->getViewport(); - // matrix buffer float matrix44[16]; currentPass()->getMatrix(matrix44, sdata->geometry.matrix); @@ -1319,11 +1351,7 @@ bool GlRenderer::renderImage(void* data) currentPass()->addRenderTask(task); - if (complexBlend) { - auto task = new GlRenderTask(mPrograms[RT_Stencil]); - sdata->geometry.draw(task, &mGpuBuffer, RenderUpdateFlag::Image); - endBlendingCompose(task, sdata->geometry.matrix); - } + if (complexBlend) endBlendingCompose(mBlendMethod); return true; } @@ -1566,4 +1594,4 @@ GlRenderer* GlRenderer::gen(TVG_UNUSED uint32_t threads) } return new GlRenderer; -} \ No newline at end of file +} diff --git a/src/renderer/gl_engine/tvgGlRenderer.h b/src/renderer/gl_engine/tvgGlRenderer.h index 7ab1e901..fb48d1b0 100644 --- a/src/renderer/gl_engine/tvgGlRenderer.h +++ b/src/renderer/gl_engine/tvgGlRenderer.h @@ -59,6 +59,8 @@ public: RT_SoftLightBlend, RT_DifferenceBlend, RT_ExclusionBlend, + RT_LightenBlend, + RT_DarkenBlend, RT_GaussianVert, RT_GaussianHorz, @@ -113,12 +115,13 @@ private: GlRenderPass* currentPass(); - bool beginComplexBlending(const RenderRegion& vp, RenderRegion bounds); - void endBlendingCompose(GlRenderTask* stencilTask, const Matrix& matrix); - GlProgram* getBlendProgram(); + bool beginComplexBlending(BlendMethod blend, const RenderRegion& vp, RenderRegion bounds); + void endBlendingCompose(BlendMethod blend); + GlProgram* getBlendProgram(BlendMethod blend); void prepareBlitTask(GlBlitTask* task); void prepareCmpTask(GlRenderTask* task, const RenderRegion& vp, uint32_t cmpWidth, uint32_t cmpHeight); + void prepareCmpStencilTask(GlRenderTask* task, RenderRegion& vp); void endRenderPass(RenderCompositor* cmp); void effectGaussianBlurUpdate(RenderEffectGaussianBlur* effect, const Matrix& transform); diff --git a/src/renderer/gl_engine/tvgGlShaderSrc.cpp b/src/renderer/gl_engine/tvgGlShaderSrc.cpp index 73d5e8ae..43b401ae 100644 --- a/src/renderer/gl_engine/tvgGlShaderSrc.cpp +++ b/src/renderer/gl_engine/tvgGlShaderSrc.cpp @@ -598,131 +598,199 @@ const char* BLIT_FRAG_SHADER = TVG_COMPOSE_SHADER( \ in vec2 vUV; \ out vec4 FragColor; \ + + // 1/1024. + const float kEhCloseEnoughHalf = 0.0009765625; )" const char* MULTIPLY_BLEND_FRAG = COMPLEX_BLEND_HEADER R"( void main() { - vec4 srcColor = texture(uSrcTexture, vUV); - vec4 dstColor = texture(uDstTexture, vUV); - FragColor = srcColor * dstColor; + vec4 src = texture(uSrcTexture, vUV); + vec4 dst = texture(uDstTexture, vUV); + + FragColor = vec4((1.0 - src.a)*dst.rgb + (1.0 - dst.a)*src.rgb + src.rgb*dst.rgb, src.a + (1.0 - src.a)*dst.a); } )"; -#define SCREEN_BLEND_FUNC R"( \ - vec4 screenBlend(vec4 srcColor, vec4 dstColor) \ - { \ - return dstColor + srcColor - (dstColor * srcColor); \ - } \ -)" - -#define HARD_LIGHT_BLEND_FUNC R"( \ - vec4 hardLightBlend(vec4 srcColor, vec4 dstColor) \ - { \ - return vec4(srcColor.r < 0.5 ? 2.0 * srcColor.r * dstColor.r : 1.0 - 2.0 * (1.0 - srcColor.r) * (1.0 - dstColor.r), \ - srcColor.g < 0.5 ? 2.0 * srcColor.g * dstColor.g : 1.0 - 2.0 * (1.0 - srcColor.g) * (1.0 - dstColor.g), \ - srcColor.b < 0.5 ? 2.0 * srcColor.b * dstColor.b : 1.0 - 2.0 * (1.0 - srcColor.b) * (1.0 - dstColor.b), \ - 1.0); \ - } \ -)" - -#define SOFT_LIGHT_BLEND_FUNC R"( \ - float softLightD(float v) \ - { \ - if (v <= 0.25) return ((16.0 * v - 12.0) * v + 4.0) * v; \ - else return sqrt(v); \ - } \ -)" - -const char* SCREEN_BLEND_FRAG = COMPLEX_BLEND_HEADER SCREEN_BLEND_FUNC R"( +const char* SCREEN_BLEND_FRAG = COMPLEX_BLEND_HEADER R"( void main() { vec4 srcColor = texture(uSrcTexture, vUV); vec4 dstColor = texture(uDstTexture, vUV); - FragColor = screenBlend(srcColor, dstColor); + FragColor = srcColor + (1.0 - srcColor) * dstColor; } )"; -const char* OVERLAY_BLEND_FRAG = COMPLEX_BLEND_HEADER HARD_LIGHT_BLEND_FUNC R"( +#define BLEND_OVERLAY_FUNC R"( + float blend_overlay_component(vec2 s, vec2 d) + { + return (2.0 * d.x <= d.y) ? 2.0 * s.x * d.x + : s.y * d.y - 2.0 * (d.y - d.x) * (s.y - s.x); + } + + vec4 blend_overlay(vec4 src, vec4 dst) + { + vec4 result = vec4( + blend_overlay_component(src.ra, dst.ra), + blend_overlay_component(src.ga, dst.ga), + blend_overlay_component(src.ba, dst.ba), + src.a + (1.0 - src.a) * dst.a + ); + + result.rgb += dst.rgb*(1 - src.a) + src.rgb*(1 - dst.a); + + return result; + } +)" + +const char* OVERLAY_BLEND_FRAG = COMPLEX_BLEND_HEADER BLEND_OVERLAY_FUNC R"( void main() { vec4 srcColor = texture(uSrcTexture, vUV); vec4 dstColor = texture(uDstTexture, vUV); - FragColor = hardLightBlend(dstColor, srcColor); + + FragColor = blend_overlay(srcColor, dstColor); } )"; const char* COLOR_DODGE_BLEND_FRAG = COMPLEX_BLEND_HEADER R"( + float color_dodge_component(vec2 s, vec2 d) + { + float dxScale = d.x == 0.0 ? 0.0 : 1.0; + float delta = dxScale * min(d.y, abs(s.y - s.x) >= kEhCloseEnoughHalf ? (d.x * s.y / (s.y - s.x)) : d.y); + + return delta * s.y + s.x * (1.0 - d.y) + d.x * (1.0 - s.y); + } + void main() { vec4 srcColor = texture(uSrcTexture, vUV); vec4 dstColor = texture(uDstTexture, vUV); FragColor = vec4( - srcColor.r < 1.0 ? dstColor.r / (1.0 - srcColor.r) : (dstColor.r > 0.0 ? 1.0 : 0.0), - srcColor.g < 1.0 ? dstColor.g / (1.0 - srcColor.g) : (dstColor.g > 0.0 ? 1.0 : 0.0), - srcColor.b < 1.0 ? dstColor.b / (1.0 - srcColor.b) : (dstColor.b > 0.0 ? 1.0 : 0.0), - 1.0 + color_dodge_component(srcColor.ra, dstColor.ra), + color_dodge_component(srcColor.ga, dstColor.ga), + color_dodge_component(srcColor.ba, dstColor.ba), + srcColor.a + (1.0 - srcColor.a) * dstColor.a ); } )"; const char* COLOR_BURN_BLEND_FRAG = COMPLEX_BLEND_HEADER R"( + float color_burn_component(vec2 s, vec2 d) + { + float dyTerm = d.y == d.x ? d.y : 0.0; + float delta = abs(s.x) >= kEhCloseEnoughHalf ? d.y - min(d.y, ((d.y - d.x) * s.y / s.x)) : dyTerm; + + return delta * s.y + s.x * (1.0 - d.y) + d.x * (1.0 - s.y); + } + void main() { vec4 srcColor = texture(uSrcTexture, vUV); vec4 dstColor = texture(uDstTexture, vUV); FragColor = vec4( - srcColor.r > 0.0 ? (1.0 - (1.0 - dstColor.r) / srcColor.r) : (dstColor.r < 1.0 ? 0.0 : 1.0), - srcColor.g > 0.0 ? (1.0 - (1.0 - dstColor.g) / srcColor.g) : (dstColor.g < 1.0 ? 0.0 : 1.0), - srcColor.b > 0.0 ? (1.0 - (1.0 - dstColor.b) / srcColor.b) : (dstColor.b < 1.0 ? 0.0 : 1.0), - 1.0 + color_burn_component(srcColor.ra, dstColor.ra), + color_burn_component(srcColor.ga, dstColor.ga), + color_burn_component(srcColor.ba, dstColor.ba), + srcColor.a + (1.0 - srcColor.a) * dstColor.a ); } )"; -const char* HARD_LIGHT_BLEND_FRAG = COMPLEX_BLEND_HEADER HARD_LIGHT_BLEND_FUNC R"( +const char* HARD_LIGHT_BLEND_FRAG = COMPLEX_BLEND_HEADER BLEND_OVERLAY_FUNC R"( void main() { vec4 srcColor = texture(uSrcTexture, vUV); vec4 dstColor = texture(uDstTexture, vUV); - FragColor = hardLightBlend(srcColor, dstColor); + + FragColor = blend_overlay(dstColor, srcColor); } )"; -const char* SOFT_LIGHT_BLEND_FRAG = COMPLEX_BLEND_HEADER SOFT_LIGHT_BLEND_FUNC R"( +const char* SOFT_LIGHT_BLEND_FRAG = COMPLEX_BLEND_HEADER R"( + float soft_light_component(vec2 s, vec2 d) + { + if (2.0 * s.x <= s.y) { + return (d.x * d.x * (s.y - 2.0 * s.x) / d.y) + (1.0 - d.y) * s.x + d.x * (-s.y + 2.0 * s.x + 1.0); + } else if(4.0 * d.x <= d.y) { + float DSqd = d.x * d.x; + float DCub = DSqd * d.x; + float DaSqd = d.y * d.y; + float DaCub = DaSqd * d.y; + + return (DaSqd * (s.x - d.x * (3.0 * s.y - 6.0 * s.x - 1.0)) + 12.0 * d.y * DSqd * (s.y - 2.0 * s.x) - 16.0 * DCub * (s.y - 2.0 * s.x) - DaCub * s.x) / DaSqd; + } else { + return d.x * (s.y - 2.0 * s.x + 1.0) + s.x - sqrt(d.y * d.x) * (s.y - 2.0 * s.x) - d.y * s.x; + } + } + void main() { vec4 srcColor = texture(uSrcTexture, vUV); vec4 dstColor = texture(uDstTexture, vUV); - FragColor = vec4( - srcColor.r <= 0.5 ? dstColor.r - (1.0 - 2.0 * srcColor.r) * dstColor.r * (1.0 - dstColor.r) : dstColor.r + (2.0 * srcColor.r - 1.0) * (softLightD(dstColor.r) - dstColor.r), - srcColor.g <= 0.5 ? dstColor.g - (1.0 - 2.0 * srcColor.g) * dstColor.g * (1.0 - dstColor.g) : dstColor.g + (2.0 * srcColor.g - 1.0) * (softLightD(dstColor.g) - dstColor.g), - srcColor.b <= 0.5 ? dstColor.b - (1.0 - 2.0 * srcColor.b) * dstColor.b * (1.0 - dstColor.b) : dstColor.b + (2.0 * srcColor.b - 1.0) * (softLightD(dstColor.b) - dstColor.b), - 1.0 - ); + FragColor = dstColor.a == 0.0 ? srcColor : vec4( + soft_light_component(srcColor.ra, dstColor.ra), + soft_light_component(srcColor.ga, dstColor.ga), + soft_light_component(srcColor.ba, dstColor.ba), + srcColor.a + (1.0 - srcColor.a) * dstColor.a + ); } )"; const char* DIFFERENCE_BLEND_FRAG = COMPLEX_BLEND_HEADER R"( void main() { - vec4 srcColor = texture(uSrcTexture, vUV); - vec4 dstColor = texture(uDstTexture, vUV); + vec4 src = texture(uSrcTexture, vUV); + vec4 dst = texture(uDstTexture, vUV); - FragColor = abs(dstColor - srcColor); + FragColor = vec4(src.rgb + dst.rgb - 2.0 * min(src.rgb * dst.a, dst.rgb * src.a), src.a + (1.0 - src.a) * dst.a); } )"; const char* EXCLUSION_BLEND_FRAG = COMPLEX_BLEND_HEADER R"( + void main() + { + vec4 src = texture(uSrcTexture, vUV); + vec4 dst = texture(uDstTexture, vUV); + + FragColor = vec4(dst.rgb + src.rgb - 2.0 * dst.rgb * src.rgb, src.a + (1.0 - src.a) * dst.a); + } +)"; + +#define LD_BLEND_FUNC R"( + vec4 blendSrcOver(vec4 src, vec4 dst) { return src + (1 - src.a)*dst; } + + vec4 blendLightDarken(float mode, vec4 src, vec4 dst) + { + vec4 a = blendSrcOver(src, dst); + vec3 b = (1.0 - dst.a) * src.rgb + dst.rgb; + a.rgb = mode * min(a.rgb * mode, b.rgb * mode); + + return a; + } +)" + +const char* LIGHTEN_BLEND_FRAG = COMPLEX_BLEND_HEADER LD_BLEND_FUNC R"( void main() { vec4 srcColor = texture(uSrcTexture, vUV); vec4 dstColor = texture(uDstTexture, vUV); - FragColor = dstColor + srcColor - (2.0 * dstColor * srcColor); + FragColor = blendLightDarken(-1.0, srcColor, dstColor); + } +)"; + +const char* DARKEN_BLEND_FRAG = COMPLEX_BLEND_HEADER LD_BLEND_FUNC R"( + void main() + { + vec4 srcColor = texture(uSrcTexture, vUV); + vec4 dstColor = texture(uDstTexture, vUV); + + FragColor = blendLightDarken(1.0, srcColor, dstColor); } )"; diff --git a/src/renderer/gl_engine/tvgGlShaderSrc.h b/src/renderer/gl_engine/tvgGlShaderSrc.h index bc793168..e75062ea 100644 --- a/src/renderer/gl_engine/tvgGlShaderSrc.h +++ b/src/renderer/gl_engine/tvgGlShaderSrc.h @@ -58,12 +58,15 @@ extern const char* HARD_LIGHT_BLEND_FRAG; extern const char* SOFT_LIGHT_BLEND_FRAG; extern const char* DIFFERENCE_BLEND_FRAG; extern const char* EXCLUSION_BLEND_FRAG; -extern const char* EFFECT_VERTEX; +extern const char* LIGHTEN_BLEND_FRAG; +extern const char* DARKEN_BLEND_FRAG; extern const char* GAUSSIAN_VERTICAL; extern const char* GAUSSIAN_HORIZONTAL; +extern const char* EFFECT_VERTEX; extern const char* EFFECT_DROPSHADOW; extern const char* EFFECT_FILL; extern const char* EFFECT_TINT; extern const char* EFFECT_TRITONE; + #endif /* _TVG_GL_SHADERSRC_H_ */