From 59f00d062bf6527d74f67de729dc3cbeebe1c4fd Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Thu, 17 Jul 2025 16:43:33 +0900 Subject: [PATCH] gl_engine: revised the effect logic - consolidated the effect implementation from renderer - applied the deferred initialization --- src/renderer/gl_engine/meson.build | 2 + src/renderer/gl_engine/tvgGlCommon.h | 19 -- src/renderer/gl_engine/tvgGlEffect.cpp | 358 +++++++++++++++++++++++ src/renderer/gl_engine/tvgGlEffect.h | 64 ++++ src/renderer/gl_engine/tvgGlRenderer.cpp | 254 +--------------- src/renderer/gl_engine/tvgGlRenderer.h | 18 +- src/renderer/gl_engine/tvgGlShaderSrc.h | 1 + 7 files changed, 435 insertions(+), 281 deletions(-) create mode 100644 src/renderer/gl_engine/tvgGlEffect.cpp create mode 100644 src/renderer/gl_engine/tvgGlEffect.h diff --git a/src/renderer/gl_engine/meson.build b/src/renderer/gl_engine/meson.build index 766fa446..d9be7477 100644 --- a/src/renderer/gl_engine/meson.build +++ b/src/renderer/gl_engine/meson.build @@ -1,6 +1,7 @@ source_file = [ 'tvgGl.h', 'tvgGlCommon.h', + 'tvgGlEffect.h', 'tvgGlGpuBuffer.h', 'tvgGlProgram.h', 'tvgGlRenderer.h', @@ -10,6 +11,7 @@ source_file = [ 'tvgGlShader.h', 'tvgGlShaderSrc.h', 'tvgGl.cpp', + 'tvgGlEffect.cpp', 'tvgGlGeometry.cpp', 'tvgGlGpuBuffer.cpp', 'tvgGlProgram.cpp', diff --git a/src/renderer/gl_engine/tvgGlCommon.h b/src/renderer/gl_engine/tvgGlCommon.h index 44695a2e..054f630e 100644 --- a/src/renderer/gl_engine/tvgGlCommon.h +++ b/src/renderer/gl_engine/tvgGlCommon.h @@ -161,24 +161,5 @@ struct GlCompositor : RenderCompositor GlCompositor(const RenderRegion& box) : bbox(box) {} }; -#define GL_GAUSSIAN_MAX_LEVEL 3 -struct GlGaussianBlur { - int level{}; - float sigma{}; - float scale{}; - float extend{}; -}; - -struct GlDropShadow: GlGaussianBlur { - float color[4]; - float offset[2]; -}; - -struct GlEffectParams { - // fill: [0..3]: color - // tint: [0..2]: black, [4..6]: white, [8]: intensity - // tritone: [0..2]: shadow, [4..6]: midtone, [8..10]: highlight [11]: blender - float params[4+4+4]; -}; #endif /* _TVG_GL_COMMON_H_ */ \ No newline at end of file diff --git a/src/renderer/gl_engine/tvgGlEffect.cpp b/src/renderer/gl_engine/tvgGlEffect.cpp new file mode 100644 index 00000000..f3c24d5b --- /dev/null +++ b/src/renderer/gl_engine/tvgGlEffect.cpp @@ -0,0 +1,358 @@ +/* + * Copyright (c) 2025 the ThorVG project. All rights reserved. + + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "tvgMath.h" +#include "tvgGlRenderTask.h" +#include "tvgGlGpuBuffer.h" +#include "tvgGlRenderPass.h" +#include "tvgGlShaderSrc.h" +#include "tvgGlEffect.h" + + +/************************************************************************/ +/* Gaussian Blur */ +/************************************************************************/ + +#define GL_GAUSSIAN_MAX_LEVEL 3 + +struct GlGaussianBlur { + int level{}; + float sigma{}; + float scale{}; + float extend{}; +}; + + +bool GlEffect::region(RenderEffectGaussianBlur* effect) +{ + auto gaussianBlur = (GlGaussianBlur*)effect->rd; + if (effect->direction != 2) { + effect->extend.min.x = -gaussianBlur->extend; + effect->extend.max.x = +gaussianBlur->extend; + } + if (effect->direction != 1) { + effect->extend.min.y = -gaussianBlur->extend; + effect->extend.max.y = +gaussianBlur->extend; + } + return true; +}; + + +void GlEffect::update(RenderEffectGaussianBlur* effect, const Matrix& transform) +{ + GlGaussianBlur* blur = (GlGaussianBlur*)effect->rd; + if (!blur) blur = tvg::malloc(sizeof(GlGaussianBlur)); + blur->sigma = effect->sigma; + blur->scale = std::sqrt(transform.e11 * transform.e11 + transform.e12 * transform.e12); + blur->extend = 2 * blur->sigma * blur->scale; + blur->level = int(GL_GAUSSIAN_MAX_LEVEL * ((effect->quality - 1) * 0.01f)) + 1; + effect->rd = blur; + effect->valid = (blur->extend > 0); +} + + +GlRenderTask* GlEffect::render(RenderEffectGaussianBlur* effect, GlRenderTarget* dstFbo, Array& blendPool, const RenderRegion& vp, uint32_t voffset, uint32_t ioffset) +{ + if (!pBlurV) pBlurV = new GlProgram(EFFECT_VERTEX, GAUSSIAN_VERTICAL); + if (!pBlurH) pBlurH = new GlProgram(EFFECT_VERTEX, GAUSSIAN_HORIZONTAL); + + // get current and intermidiate framebuffers + auto dstCopyFbo0 = blendPool[0]->getRenderTarget(vp); + auto dstCopyFbo1 = blendPool[1]->getRenderTarget(vp); + + // add uniform data + auto blurOffset = gpuBuffer->push((GlGaussianBlur*)(effect->rd), sizeof(GlGaussianBlur), true); + + // create gaussian blur tasks + auto task = new GlGaussianBlurTask(dstFbo, dstCopyFbo0, dstCopyFbo1); + task->effect = effect; + task->setViewport({{0, 0}, {vp.sw(), vp.sh()}}); + // horizontal blur task and geometry + task->horzTask = new GlRenderTask(pBlurH); + task->horzTask->addBindResource(GlBindingResource{0, pBlurH->getUniformBlockIndex("Gaussian"), gpuBuffer->getBufferId(), blurOffset, sizeof(GlGaussianBlur)}); + task->horzTask->addVertexLayout(GlVertexLayout{0, 2, 2 * sizeof(float), voffset}); + task->horzTask->setDrawRange(ioffset, 6); + // vertical blur task and geometry + task->vertTask = new GlRenderTask(pBlurV); + task->vertTask->addBindResource(GlBindingResource{0, pBlurV->getUniformBlockIndex("Gaussian"), gpuBuffer->getBufferId(), blurOffset, sizeof(GlGaussianBlur)}); + task->vertTask->addVertexLayout(GlVertexLayout{0, 2, 2 * sizeof(float), voffset}); + task->vertTask->setDrawRange(ioffset, 6); + + return task; +} + + +/************************************************************************/ +/* DropShadow */ +/************************************************************************/ + +struct GlDropShadow: GlGaussianBlur { + float color[4]; + float offset[2]; +}; + + +bool GlEffect::region(RenderEffectDropShadow* effect) +{ + auto gaussianBlur = (GlDropShadow*)effect->rd; + effect->extend.min.x = -gaussianBlur->extend; + effect->extend.max.x = +gaussianBlur->extend; + effect->extend.min.y = -gaussianBlur->extend; + effect->extend.max.y = +gaussianBlur->extend; + return true; +}; + + +void GlEffect::update(RenderEffectDropShadow* effect, const Matrix& transform) +{ + GlDropShadow* dropShadow = (GlDropShadow*)effect->rd; + if (!dropShadow) dropShadow = tvg::malloc(sizeof(GlDropShadow)); + const float sigma = effect->sigma; + const float scale = std::sqrt(transform.e11 * transform.e11 + transform.e12 * transform.e12); + const float radian = tvg::deg2rad(90.0f - effect->angle); + const Point offset = { + -effect->distance * cosf(radian) * scale, + -effect->distance * sinf(radian) * scale + }; + dropShadow->sigma = sigma; + dropShadow->scale = scale; + dropShadow->level = int(GL_GAUSSIAN_MAX_LEVEL * ((effect->quality - 1) * 0.01f)) + 1; + dropShadow->color[3] = effect->color[3] / 255.0f; + //Drop shadow effect applies blending in the shader (GL_BLEND disabled), so the color should be premultiplied: + dropShadow->color[0] = effect->color[0] / 255.0f * dropShadow->color[3]; + dropShadow->color[1] = effect->color[1] / 255.0f * dropShadow->color[3]; + dropShadow->color[2] = effect->color[2] / 255.0f * dropShadow->color[3]; + dropShadow->offset[0] = offset.x; + dropShadow->offset[1] = offset.y; + dropShadow->extend = 2 * std::max(sigma * scale + std::abs(offset.x), sigma * scale + std::abs(offset.y)); + effect->rd = dropShadow; + effect->valid = (dropShadow->extend > 0); +} + + +GlRenderTask* GlEffect::render(RenderEffectDropShadow* effect, GlRenderTarget* dstFbo, Array& blendPool, const RenderRegion& vp, uint32_t voffset, uint32_t ioffset) +{ + if (!pBlurV) pBlurV = new GlProgram(EFFECT_VERTEX, GAUSSIAN_VERTICAL); + if (!pBlurH) pBlurH = new GlProgram(EFFECT_VERTEX, GAUSSIAN_HORIZONTAL); + if (!pDropShadow) pDropShadow = new GlProgram(EFFECT_VERTEX, EFFECT_DROPSHADOW); + + // get current and intermidiate framebuffers + auto dstCopyFbo0 = blendPool[0]->getRenderTarget(vp); + auto dstCopyFbo1 = blendPool[1]->getRenderTarget(vp); + + // add uniform data + GlDropShadow* params = (GlDropShadow*)(effect->rd); + auto paramsOffset = gpuBuffer->push(params, sizeof(GlDropShadow), true); + + // create gaussian blur tasks + auto task = new GlEffectDropShadowTask(pDropShadow, dstFbo, dstCopyFbo0, dstCopyFbo1); + task->effect = (RenderEffectDropShadow*)effect; + task->setViewport({{0, 0}, {vp.sw(), vp.sh()}}); + task->addBindResource(GlBindingResource{0, pDropShadow->getUniformBlockIndex("DropShadow"), gpuBuffer->getBufferId(), paramsOffset, sizeof(GlDropShadow)}); + task->addVertexLayout(GlVertexLayout{0, 2, 2 * sizeof(float), voffset}); + task->setDrawRange(ioffset, 6); + + // horizontal blur task and geometry + task->horzTask = new GlRenderTask(pBlurH); + task->horzTask->addBindResource(GlBindingResource{0, pBlurH->getUniformBlockIndex("Gaussian"), gpuBuffer->getBufferId(), paramsOffset, sizeof(GlGaussianBlur)}); + task->horzTask->addVertexLayout(GlVertexLayout{0, 2, 2 * sizeof(float), voffset}); + task->horzTask->setDrawRange(ioffset, 6); + + // vertical blur task and geometry + task->vertTask = new GlRenderTask(pBlurV); + task->vertTask->addBindResource(GlBindingResource{0, pBlurV->getUniformBlockIndex("Gaussian"), gpuBuffer->getBufferId(), paramsOffset, sizeof(GlGaussianBlur)}); + task->vertTask->addVertexLayout(GlVertexLayout{0, 2, 2 * sizeof(float), voffset}); + task->vertTask->setDrawRange(ioffset, 6); + + return task; +} + + +/************************************************************************/ +/* ColorReplacement */ +/************************************************************************/ + +struct GlEffectParams { + // fill: [0..3]: color + // tint: [0..2]: black, [4..6]: white, [8]: intensity + // tritone: [0..2]: shadow, [4..6]: midtone, [8..10]: highlight [11]: blender + float params[4+4+4]; +}; + + +void GlEffect::update(RenderEffectFill* effect, const Matrix& transform) +{ + auto params = (GlEffectParams*)effect->rd; + if (!params) params = tvg::malloc(sizeof(GlEffectParams)); + params->params[0] = effect->color[0] / 255.0f; + params->params[1] = effect->color[1] / 255.0f; + params->params[2] = effect->color[2] / 255.0f; + params->params[3] = effect->color[3] / 255.0f; + effect->rd = params; + effect->valid = true; +} + + +void GlEffect::update(RenderEffectTint* effect, const Matrix& transform) +{ + effect->valid = (effect->intensity > 0); + if (!effect->valid) return; + + auto params = (GlEffectParams*)effect->rd; + if (!params) params = tvg::malloc(sizeof(GlEffectParams)); + params->params[0] = effect->black[0] / 255.0f; + params->params[1] = effect->black[1] / 255.0f; + params->params[2] = effect->black[2] / 255.0f; + params->params[3] = 0.0f; + params->params[4] = effect->white[0] / 255.0f; + params->params[5] = effect->white[1] / 255.0f; + params->params[6] = effect->white[2] / 255.0f; + params->params[7] = 0.0f; + params->params[8] = effect->intensity / 255.0f; + effect->rd = params; +} + + +void GlEffect::update(RenderEffectTritone* effect, const Matrix& transform) +{ + effect->valid = (effect->blender < 255); + if (!effect->valid) return; + + auto params = (GlEffectParams*)effect->rd; + if (!params) params = tvg::malloc(sizeof(GlEffectParams)); + params->params[0] = effect->shadow[0] / 255.0f; + params->params[1] = effect->shadow[1] / 255.0f; + params->params[2] = effect->shadow[2] / 255.0f; + params->params[3] = 0.0f; + params->params[4] = effect->midtone[0] / 255.0f; + params->params[5] = effect->midtone[1] / 255.0f; + params->params[6] = effect->midtone[2] / 255.0f; + params->params[7] = 0.0f; + params->params[8] = effect->highlight[0] / 255.0f; + params->params[9] = effect->highlight[1] / 255.0f; + params->params[10] = effect->highlight[2] / 255.0f; + params->params[11] = effect->blender / 255.0f; + effect->rd = params; +} + + +GlRenderTask* GlEffect::render(RenderEffect* effect, GlRenderTarget* dstFbo, Array& blendPool, const RenderRegion& vp, uint32_t voffset, uint32_t ioffset) +{ + //common color replacement effects + GlProgram* program = nullptr; + if (effect->type == SceneEffect::Fill) { + if (!pFill) pFill = new GlProgram(EFFECT_VERTEX, EFFECT_FILL); + program = pFill; + } else if (effect->type == SceneEffect::Tint) { + if (!pTint) pTint = new GlProgram(EFFECT_VERTEX, EFFECT_TINT); + program = pTint; + } else if (effect->type == SceneEffect::Tritone) { + if (!pTritone) pTritone = new GlProgram(EFFECT_VERTEX, EFFECT_TRITONE); + program = pTritone; + } else return nullptr; + + // get current and intermidiate framebuffers + auto dstCopyFbo = blendPool[0]->getRenderTarget(vp); + + // add uniform data + auto params = (GlEffectParams*)(effect->rd); + auto paramsOffset = gpuBuffer->push(params, sizeof(GlEffectParams), true); + + // create and setup task + auto task = new GlEffectColorTransformTask(program, dstFbo, dstCopyFbo); + task->setViewport({{0, 0}, {vp.sw(), vp.sh()}}); + task->addBindResource(GlBindingResource{0, program->getUniformBlockIndex("Params"), gpuBuffer->getBufferId(), paramsOffset, sizeof(GlEffectParams)}); + task->addVertexLayout(GlVertexLayout{0, 2, 2 * sizeof(float), voffset}); + task->setDrawRange(ioffset, 6); + + return task; +} + + +/************************************************************************/ +/* External Class Implementation */ +/************************************************************************/ + +void GlEffect::update(RenderEffect* effect, const Matrix& transform) +{ + switch (effect->type) { + case SceneEffect::GaussianBlur: update(static_cast(effect), transform); break; + case SceneEffect::DropShadow : update(static_cast(effect), transform); break; + case SceneEffect::Fill: update(static_cast(effect), transform); break; + case SceneEffect::Tint: update(static_cast(effect), transform); break; + case SceneEffect::Tritone: update(static_cast(effect), transform); break; + default: break; + } +} + + +bool GlEffect::region(RenderEffect* effect) +{ + switch (effect->type) { + case SceneEffect::GaussianBlur: return region(static_cast(effect)); + case SceneEffect::DropShadow : return region(static_cast(effect)); + default: return false; + } +} + + +bool GlEffect::render(RenderEffect* effect, GlRenderPass* pass, Array& blendPool) +{ + if (pass->isEmpty()) return false; + auto vp = pass->getViewport(); + + // add render geometry + const float vdata[] = {-1.0f, +1.0f, +1.0f, +1.0f, +1.0f, -1.0f, -1.0f, -1.0f}; + const uint32_t idata[] = { 0, 1, 2, 0, 2, 3 }; + auto voffset = gpuBuffer->push((void*)vdata, sizeof(vdata)); + auto ioffset = gpuBuffer->pushIndex((void*)idata, sizeof(idata)); + GlRenderTask* output = nullptr; + + if (effect->type == SceneEffect::GaussianBlur) { + output = render(static_cast(effect), pass->getFbo(), blendPool, vp, voffset, ioffset); + } else if (effect->type == SceneEffect::DropShadow) { + output = render(static_cast(effect), pass->getFbo(), blendPool, vp, voffset, ioffset); + } else { + output = render(effect, pass->getFbo(), blendPool, vp, voffset, ioffset); + } + + if (!output) return false; + + pass->addRenderTask(output); + return true; +} + + +GlEffect::GlEffect(GlStageBuffer* buffer) : gpuBuffer(buffer) +{ +} + + +GlEffect::~GlEffect() +{ + delete(pBlurV); + delete(pBlurH); + delete(pDropShadow); + delete(pFill); + delete(pTint); + delete(pTritone); +} \ No newline at end of file diff --git a/src/renderer/gl_engine/tvgGlEffect.h b/src/renderer/gl_engine/tvgGlEffect.h new file mode 100644 index 00000000..d84f526c --- /dev/null +++ b/src/renderer/gl_engine/tvgGlEffect.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2025 the ThorVG project. All rights reserved. + + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _TVG_GL_EFFECT_H_ +#define _TVG_GL_EFFECT_H_ + +#include "tvgRender.h" +#include "tvgGlProgram.h" + + +class GlEffect +{ +private: + GlStageBuffer* gpuBuffer; //shared resource with GlRenderer + + GlProgram* pBlurV{}; + GlProgram* pBlurH{}; + GlProgram* pDropShadow{}; + GlProgram* pFill{}; + GlProgram* pTint{}; + GlProgram* pTritone{}; + + void update(RenderEffectGaussianBlur* effect, const Matrix& transform); + void update(RenderEffectDropShadow* effect, const Matrix& transform); + void update(RenderEffectFill* effect, const Matrix& transform); + void update(RenderEffectTint* effect, const Matrix& transform); + void update(RenderEffectTritone* effect, const Matrix& transform); + + bool region(RenderEffectGaussianBlur* effect); + bool region(RenderEffectDropShadow* effect); + + GlRenderTask* render(RenderEffectGaussianBlur* effect, GlRenderTarget* dstFbo, Array& blendPool, const RenderRegion& vp, uint32_t voffset, uint32_t ioffset); + GlRenderTask* render(RenderEffectDropShadow* effect, GlRenderTarget* dstFbo, Array& blendPool, const RenderRegion& vp, uint32_t voffset, uint32_t ioffset); + GlRenderTask* render(RenderEffect* effect, GlRenderTarget* dstFbo, Array& blendPool, const RenderRegion& vp, uint32_t voffset, uint32_t ioffset); + +public: + GlEffect(GlStageBuffer* buffer); + ~GlEffect(); + + void update(RenderEffect* effect, const Matrix& transform); + bool region(RenderEffect* effect); + bool render(RenderEffect* effect, GlRenderPass* pass, Array& blendPool); +}; + +#endif /* _TVG_GL_EFFECT_H_ */ diff --git a/src/renderer/gl_engine/tvgGlRenderer.cpp b/src/renderer/gl_engine/tvgGlRenderer.cpp index e6b5c83d..51b44c30 100644 --- a/src/renderer/gl_engine/tvgGlRenderer.cpp +++ b/src/renderer/gl_engine/tvgGlRenderer.cpp @@ -21,13 +21,14 @@ */ #include +#include "tvgFill.h" #include "tvgGlCommon.h" #include "tvgGlRenderer.h" #include "tvgGlGpuBuffer.h" #include "tvgGlRenderTask.h" #include "tvgGlProgram.h" #include "tvgGlShaderSrc.h" -#include "tvgFill.h" + /************************************************************************/ /* Internal Class Implementation */ @@ -79,7 +80,7 @@ void GlRenderer::currentContext() } -GlRenderer::GlRenderer() +GlRenderer::GlRenderer() : mEffect(GlEffect(&mGpuBuffer)) { ++rendererCnt; } @@ -92,8 +93,6 @@ GlRenderer::~GlRenderer() flush(); ARRAY_FOREACH(p, mPrograms) delete(*p); - - } @@ -159,14 +158,6 @@ 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)); - - // effects - mPrograms.push(new GlProgram(EFFECT_VERTEX, GAUSSIAN_VERTICAL)); - mPrograms.push(new GlProgram(EFFECT_VERTEX, GAUSSIAN_HORIZONTAL)); - mPrograms.push(new GlProgram(EFFECT_VERTEX, EFFECT_DROPSHADOW)); - mPrograms.push(new GlProgram(EFFECT_VERTEX, EFFECT_FILL)); - mPrograms.push(new GlProgram(EFFECT_VERTEX, EFFECT_TINT)); - mPrograms.push(new GlProgram(EFFECT_VERTEX, EFFECT_TRITONE)); } @@ -946,254 +937,25 @@ bool GlRenderer::endComposite(RenderCompositor* cmp) } -void GlRenderer::effectGaussianBlurUpdate(RenderEffectGaussianBlur* effect, const Matrix& transform) -{ - GlGaussianBlur* blur = (GlGaussianBlur*)effect->rd; - if (!blur) blur = tvg::malloc(sizeof(GlGaussianBlur)); - blur->sigma = effect->sigma; - blur->scale = std::sqrt(transform.e11 * transform.e11 + transform.e12 * transform.e12); - blur->extend = 2 * blur->sigma * blur->scale; - blur->level = int(GL_GAUSSIAN_MAX_LEVEL * ((effect->quality - 1) * 0.01f)) + 1; - effect->rd = blur; - effect->valid = (blur->extend > 0); -} - - -void GlRenderer::effectDropShadowUpdate(RenderEffectDropShadow* effect, const Matrix& transform) -{ - GlDropShadow* dropShadow = (GlDropShadow*)effect->rd; - if (!dropShadow) dropShadow = tvg::malloc(sizeof(GlDropShadow)); - const float sigma = effect->sigma; - const float scale = std::sqrt(transform.e11 * transform.e11 + transform.e12 * transform.e12); - const float radian = tvg::deg2rad(90.0f - effect->angle); - const Point offset = { - -effect->distance * cosf(radian) * scale, - -effect->distance * sinf(radian) * scale - }; - dropShadow->sigma = sigma; - dropShadow->scale = scale; - dropShadow->level = int(GL_GAUSSIAN_MAX_LEVEL * ((effect->quality - 1) * 0.01f)) + 1; - dropShadow->color[3] = effect->color[3] / 255.0f; - //Drop shadow effect applies blending in the shader (GL_BLEND disabled), so the color should be premultiplied: - dropShadow->color[0] = effect->color[0] / 255.0f * dropShadow->color[3]; - dropShadow->color[1] = effect->color[1] / 255.0f * dropShadow->color[3]; - dropShadow->color[2] = effect->color[2] / 255.0f * dropShadow->color[3]; - dropShadow->offset[0] = offset.x; - dropShadow->offset[1] = offset.y; - dropShadow->extend = 2 * std::max(sigma * scale + std::abs(offset.x), sigma * scale + std::abs(offset.y)); - effect->rd = dropShadow; - effect->valid = (dropShadow->extend > 0); -} - - -void GlRenderer::effectFillUpdate(RenderEffectFill* effect, const Matrix& transform) -{ - auto params = (GlEffectParams*)effect->rd; - if (!params) params = tvg::malloc(sizeof(GlEffectParams)); - params->params[0] = effect->color[0] / 255.0f; - params->params[1] = effect->color[1] / 255.0f; - params->params[2] = effect->color[2] / 255.0f; - params->params[3] = effect->color[3] / 255.0f; - effect->rd = params; - effect->valid = true; -} - - -void GlRenderer::effectTintUpdate(RenderEffectTint* effect, const Matrix& transform) -{ - effect->valid = (effect->intensity > 0); - if (!effect->valid) return; - - auto params = (GlEffectParams*)effect->rd; - if (!params) params = tvg::malloc(sizeof(GlEffectParams)); - params->params[0] = effect->black[0] / 255.0f; - params->params[1] = effect->black[1] / 255.0f; - params->params[2] = effect->black[2] / 255.0f; - params->params[3] = 0.0f; - params->params[4] = effect->white[0] / 255.0f; - params->params[5] = effect->white[1] / 255.0f; - params->params[6] = effect->white[2] / 255.0f; - params->params[7] = 0.0f; - params->params[8] = effect->intensity / 255.0f; - effect->rd = params; -} - - -void GlRenderer::effectTritoneUpdate(RenderEffectTritone* effect, const Matrix& transform) -{ - effect->valid = (effect->blender < 255); - if (!effect->valid) return; - - auto params = (GlEffectParams*)effect->rd; - if (!params) params = tvg::malloc(sizeof(GlEffectParams)); - params->params[0] = effect->shadow[0] / 255.0f; - params->params[1] = effect->shadow[1] / 255.0f; - params->params[2] = effect->shadow[2] / 255.0f; - params->params[3] = 0.0f; - params->params[4] = effect->midtone[0] / 255.0f; - params->params[5] = effect->midtone[1] / 255.0f; - params->params[6] = effect->midtone[2] / 255.0f; - params->params[7] = 0.0f; - params->params[8] = effect->highlight[0] / 255.0f; - params->params[9] = effect->highlight[1] / 255.0f; - params->params[10] = effect->highlight[2] / 255.0f; - params->params[11] = effect->blender / 255.0f; - effect->rd = params; -} - - -bool GlRenderer::effectGaussianBlurRegion(RenderEffectGaussianBlur* effect) -{ - auto gaussianBlur = (GlGaussianBlur*)effect->rd; - if (effect->direction != 2) { - effect->extend.min.x = -gaussianBlur->extend; - effect->extend.max.x = +gaussianBlur->extend; - } - if (effect->direction != 1) { - effect->extend.min.y = -gaussianBlur->extend; - effect->extend.max.y = +gaussianBlur->extend; - } - return true; -}; - - -bool GlRenderer::effectDropShadowRegion(RenderEffectDropShadow* effect) -{ - auto gaussianBlur = (GlDropShadow*)effect->rd; - effect->extend.min.x = -gaussianBlur->extend; - effect->extend.max.x = +gaussianBlur->extend; - effect->extend.min.y = -gaussianBlur->extend; - effect->extend.max.y = +gaussianBlur->extend; - return true; -}; - - void GlRenderer::prepare(RenderEffect* effect, const Matrix& transform) { // we must be sure, that we have intermidiate FBOs if (mBlendPool.count < 1) mBlendPool.push(new GlRenderTargetPool(surface.w, surface.h)); if (mBlendPool.count < 2) mBlendPool.push(new GlRenderTargetPool(surface.w, surface.h)); - - switch (effect->type) { - case SceneEffect::GaussianBlur: effectGaussianBlurUpdate(static_cast(effect), transform); break; - case SceneEffect::DropShadow : effectDropShadowUpdate(static_cast(effect), transform); break; - case SceneEffect::Fill: effectFillUpdate(static_cast(effect), transform); break; - case SceneEffect::Tint: effectTintUpdate(static_cast(effect), transform); break; - case SceneEffect::Tritone: effectTritoneUpdate(static_cast(effect), transform); break; - default: break; - } + + mEffect.update(effect, transform); } bool GlRenderer::region(RenderEffect* effect) { - switch (effect->type) { - case SceneEffect::GaussianBlur: return effectGaussianBlurRegion(static_cast(effect)); - case SceneEffect::DropShadow : return effectDropShadowRegion(static_cast(effect)); - case SceneEffect::Fill: - case SceneEffect::Tint: - case SceneEffect::Tritone: return true; - default: return false; - } - return false; + return mEffect.region(effect); } -bool GlRenderer::render(TVG_UNUSED RenderCompositor* cmp, const RenderEffect* effect, bool direct) +bool GlRenderer::render(TVG_UNUSED RenderCompositor* cmp, const RenderEffect* effect, TVG_UNUSED bool direct) { - // get current pass and properties - auto pass = currentPass(); - if (pass->isEmpty()) return false; - auto vp = pass->getViewport(); - - // add render geometry - const float vdata[] = {-1.0f, +1.0f, +1.0f, +1.0f, +1.0f, -1.0f, -1.0f, -1.0f}; - const uint32_t idata[] = { 0, 1, 2, 0, 2, 3 }; - auto voffset = mGpuBuffer.push((void*)vdata, sizeof(vdata)); - auto ioffset = mGpuBuffer.pushIndex((void*)idata, sizeof(idata)); - - if (effect->type == SceneEffect::GaussianBlur) { - // get gaussian programs - GlProgram* programHorz = mPrograms[RT_GaussianHorz]; - GlProgram* programVert = mPrograms[RT_GaussianVert]; - // get current and intermidiate framebuffers - auto dstFbo = pass->getFbo(); - auto dstCopyFbo0 = mBlendPool[0]->getRenderTarget(vp); - auto dstCopyFbo1 = mBlendPool[1]->getRenderTarget(vp); - // add uniform data - GlGaussianBlur* blur = (GlGaussianBlur*)(effect->rd); - auto blurOffset = mGpuBuffer.push(blur, sizeof(GlGaussianBlur), true); - - // create gaussian blur tasks - auto gaussianTask = new GlGaussianBlurTask(dstFbo, dstCopyFbo0, dstCopyFbo1); - gaussianTask->effect = (RenderEffectGaussianBlur*)effect; - gaussianTask->setViewport({{0, 0}, {vp.sw(), vp.sh()}}); - // horizontal blur task and geometry - gaussianTask->horzTask = new GlRenderTask(programHorz); - gaussianTask->horzTask->addBindResource(GlBindingResource{0, programHorz->getUniformBlockIndex("Gaussian"), mGpuBuffer.getBufferId(), blurOffset, sizeof(GlGaussianBlur)}); - gaussianTask->horzTask->addVertexLayout(GlVertexLayout{0, 2, 2 * sizeof(float), voffset}); - gaussianTask->horzTask->setDrawRange(ioffset, 6); - // vertical blur task and geometry - gaussianTask->vertTask = new GlRenderTask(programVert); - gaussianTask->vertTask->addBindResource(GlBindingResource{0, programVert->getUniformBlockIndex("Gaussian"), mGpuBuffer.getBufferId(), blurOffset, sizeof(GlGaussianBlur)}); - gaussianTask->vertTask->addVertexLayout(GlVertexLayout{0, 2, 2 * sizeof(float), voffset}); - gaussianTask->vertTask->setDrawRange(ioffset, 6); - // add task to render pipeline - pass->addRenderTask(gaussianTask); - } else if (effect->type == SceneEffect::DropShadow) { - // get programs - GlProgram* program = mPrograms[RT_DropShadow]; - GlProgram* programHorz = mPrograms[RT_GaussianHorz]; - GlProgram* programVert = mPrograms[RT_GaussianVert]; - // get current and intermidiate framebuffers - auto dstFbo = pass->getFbo(); - auto dstCopyFbo0 = mBlendPool[0]->getRenderTarget(vp); - auto dstCopyFbo1 = mBlendPool[1]->getRenderTarget(vp); - // add uniform data - GlDropShadow* params = (GlDropShadow*)(effect->rd); - auto paramsOffset = mGpuBuffer.push(params, sizeof(GlDropShadow), true); - - // create gaussian blur tasks - auto task = new GlEffectDropShadowTask(program, dstFbo, dstCopyFbo0, dstCopyFbo1); - task->effect = (RenderEffectDropShadow*)effect; - task->setViewport({{0, 0}, {vp.sw(), vp.sh()}}); - task->addBindResource(GlBindingResource{0, program->getUniformBlockIndex("DropShadow"), mGpuBuffer.getBufferId(), paramsOffset, sizeof(GlDropShadow)}); - task->addVertexLayout(GlVertexLayout{0, 2, 2 * sizeof(float), voffset}); - task->setDrawRange(ioffset, 6); - // horizontal blur task and geometry - task->horzTask = new GlRenderTask(programHorz); - task->horzTask->addBindResource(GlBindingResource{0, programHorz->getUniformBlockIndex("Gaussian"), mGpuBuffer.getBufferId(), paramsOffset, sizeof(GlGaussianBlur)}); - task->horzTask->addVertexLayout(GlVertexLayout{0, 2, 2 * sizeof(float), voffset}); - task->horzTask->setDrawRange(ioffset, 6); - // vertical blur task and geometry - task->vertTask = new GlRenderTask(programVert); - task->vertTask->addBindResource(GlBindingResource{0, programVert->getUniformBlockIndex("Gaussian"), mGpuBuffer.getBufferId(), paramsOffset, sizeof(GlGaussianBlur)}); - task->vertTask->addVertexLayout(GlVertexLayout{0, 2, 2 * sizeof(float), voffset}); - task->vertTask->setDrawRange(ioffset, 6); - // add task to render pipeline - pass->addRenderTask(task); - } else if ((effect->type == SceneEffect::Fill) || (effect->type == SceneEffect::Tint) || (effect->type == SceneEffect::Tritone)) { - GlProgram* program{}; - if (effect->type == SceneEffect::Fill) program = mPrograms[RT_EffectFill]; - else if (effect->type == SceneEffect::Tint) program = mPrograms[RT_EffectTint]; - else if (effect->type == SceneEffect::Tritone) program = mPrograms[RT_EffectTritone]; - else return false; - // get current and intermidiate framebuffers - auto dstFbo = pass->getFbo(); - auto dstCopyFbo = mBlendPool[0]->getRenderTarget(vp); - // add uniform data - auto params = (GlEffectParams*)(effect->rd); - auto paramsOffset = mGpuBuffer.push(params, sizeof(GlEffectParams), true); - // create and setup task - auto task = new GlEffectColorTransformTask(program, dstFbo, dstCopyFbo); - task->setViewport({{0, 0}, {vp.sw(), vp.sh()}}); - task->addBindResource(GlBindingResource{0, program->getUniformBlockIndex("Params"), mGpuBuffer.getBufferId(), paramsOffset, sizeof(GlEffectParams)}); - task->addVertexLayout(GlVertexLayout{0, 2, 2 * sizeof(float), voffset}); - task->setDrawRange(ioffset, 6); - // add task to render pipeline - pass->addRenderTask(task); - } - return false; + return mEffect.render(const_cast(effect), currentPass(), mBlendPool); } diff --git a/src/renderer/gl_engine/tvgGlRenderer.h b/src/renderer/gl_engine/tvgGlRenderer.h index 237fe0a8..49b13347 100644 --- a/src/renderer/gl_engine/tvgGlRenderer.h +++ b/src/renderer/gl_engine/tvgGlRenderer.h @@ -28,6 +28,7 @@ #include "tvgGlRenderTask.h" #include "tvgGlGpuBuffer.h" #include "tvgGlRenderPass.h" +#include "tvgGlEffect.h" class GlRenderer : public RenderMethod { @@ -60,13 +61,6 @@ public: RT_DifferenceBlend, RT_ExclusionBlend, - RT_GaussianVert, - RT_GaussianHorz, - RT_DropShadow, - RT_EffectFill, - RT_EffectTint, - RT_EffectTritone, - RT_None, }; @@ -125,15 +119,6 @@ private: void prepareCmpTask(GlRenderTask* task, const RenderRegion& vp, uint32_t cmpWidth, uint32_t cmpHeight); void endRenderPass(RenderCompositor* cmp); - void effectGaussianBlurUpdate(RenderEffectGaussianBlur* effect, const Matrix& transform); - void effectDropShadowUpdate(RenderEffectDropShadow* effect, const Matrix& transform); - void effectFillUpdate(RenderEffectFill* effect, const Matrix& transform); - void effectTintUpdate(RenderEffectTint* effect, const Matrix& transform); - void effectTritoneUpdate(RenderEffectTritone* effect, const Matrix& transform); - - bool effectGaussianBlurRegion(RenderEffectGaussianBlur* effect); - bool effectDropShadowRegion(RenderEffectDropShadow* effect); - void flush(); void clearDisposes(); void currentContext(); @@ -143,6 +128,7 @@ private: GLint mTargetFboId = 0; GlStageBuffer mGpuBuffer; GlRenderTarget mRootTarget; + GlEffect mEffect; Array mPrograms; Array mComposePool; Array mBlendPool; diff --git a/src/renderer/gl_engine/tvgGlShaderSrc.h b/src/renderer/gl_engine/tvgGlShaderSrc.h index bc793168..514729e0 100644 --- a/src/renderer/gl_engine/tvgGlShaderSrc.h +++ b/src/renderer/gl_engine/tvgGlShaderSrc.h @@ -58,6 +58,7 @@ 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* GAUSSIAN_VERTICAL; extern const char* GAUSSIAN_HORIZONTAL;