From 76f98008e81e34b87eaf1396591f1b51d0413358 Mon Sep 17 00:00:00 2001 From: RuiwenTang Date: Tue, 25 Jun 2024 22:15:30 +0800 Subject: [PATCH] gl_engine: optimize off-screen rendering Performing a full-screen RenderPass resolve is too expensive. Because most composite cases only require a small area to be rendered off-screen. To improve performance, use the bounds of the Geometry for off-screen rendering whenever possible --- src/renderer/gl_engine/meson.build | 2 + src/renderer/gl_engine/tvgGlGeometry.cpp | 23 +- src/renderer/gl_engine/tvgGlGeometry.h | 8 +- src/renderer/gl_engine/tvgGlRenderPass.cpp | 78 ++---- src/renderer/gl_engine/tvgGlRenderPass.h | 44 ++-- src/renderer/gl_engine/tvgGlRenderTarget.cpp | 132 ++++++++++ src/renderer/gl_engine/tvgGlRenderTarget.h | 69 ++++++ src/renderer/gl_engine/tvgGlRenderTask.cpp | 30 ++- src/renderer/gl_engine/tvgGlRenderTask.h | 15 +- src/renderer/gl_engine/tvgGlRenderer.cpp | 246 ++++++++++++++----- src/renderer/gl_engine/tvgGlRenderer.h | 5 +- 11 files changed, 479 insertions(+), 173 deletions(-) create mode 100644 src/renderer/gl_engine/tvgGlRenderTarget.cpp create mode 100644 src/renderer/gl_engine/tvgGlRenderTarget.h diff --git a/src/renderer/gl_engine/meson.build b/src/renderer/gl_engine/meson.build index 7341f82b..4f1dea66 100644 --- a/src/renderer/gl_engine/meson.build +++ b/src/renderer/gl_engine/meson.build @@ -6,6 +6,7 @@ source_file = [ 'tvgGlProgram.h', 'tvgGlRenderer.h', 'tvgGlRenderPass.h', + 'tvgGlRenderTarget.h', 'tvgGlRenderTask.h', 'tvgGlShader.h', 'tvgGlShaderSrc.h', @@ -14,6 +15,7 @@ source_file = [ 'tvgGlProgram.cpp', 'tvgGlRenderer.cpp', 'tvgGlRenderPass.cpp', + 'tvgGlRenderTarget.cpp', 'tvgGlRenderTask.cpp', 'tvgGlShader.cpp', 'tvgGlShaderSrc.cpp', diff --git a/src/renderer/gl_engine/tvgGlGeometry.cpp b/src/renderer/gl_engine/tvgGlGeometry.cpp index f9896dcb..fc6d1a09 100644 --- a/src/renderer/gl_engine/tvgGlGeometry.cpp +++ b/src/renderer/gl_engine/tvgGlGeometry.cpp @@ -211,29 +211,17 @@ bool GlGeometry::draw(GlRenderTask* task, GlStageBuffer* gpuBuffer, RenderUpdate task->addVertexLayout(GlVertexLayout{0, 2, 2 * sizeof(float), vertexOffset}); } task->setDrawRange(indexOffset, indexBuffer->count); - task->setViewport(viewport); return true; } -void GlGeometry::updateTransform(const RenderTransform* transform, float w, float h) +void GlGeometry::updateTransform(const RenderTransform* transform) { - float modelMatrix[16]; if (transform) { - GET_MATRIX44(transform->m, modelMatrix); mMatrix = transform->m; } else { - memset(modelMatrix, 0, 16 * sizeof(float)); - modelMatrix[0] = 1.f; - modelMatrix[5] = 1.f; - modelMatrix[10] = 1.f; - modelMatrix[15] = 1.f; - mMatrix = Matrix{1, 0, 0, 0, 1, 0, 0, 0, 1}; } - - MVP_MATRIX(); - MULTIPLY_MATRIX(mvp, modelMatrix, mTransform) } void GlGeometry::setViewport(const RenderRegion& viewport) @@ -241,9 +229,14 @@ void GlGeometry::setViewport(const RenderRegion& viewport) this->viewport = viewport; } -float* GlGeometry::getTransformMatrix() +const RenderRegion& GlGeometry::getViewport() { - return mTransform; + return viewport; +} + +const Matrix& GlGeometry::getTransformMatrix() +{ + return mMatrix; } GlStencilMode GlGeometry::getStencilMode(RenderUpdateFlag flag) diff --git a/src/renderer/gl_engine/tvgGlGeometry.h b/src/renderer/gl_engine/tvgGlGeometry.h index e7edcdde..9f4678a9 100644 --- a/src/renderer/gl_engine/tvgGlGeometry.h +++ b/src/renderer/gl_engine/tvgGlGeometry.h @@ -28,7 +28,7 @@ #include "tvgMath.h" -#define MVP_MATRIX() \ +#define MVP_MATRIX(w, h) \ float mvp[4*4] = { \ 2.f / w, 0.0, 0.0f, 0.0f, \ 0.0, -2.f / h, 0.0f, 0.0f, \ @@ -192,9 +192,10 @@ public: bool tesselate(const Surface* image, const RenderMesh* mesh, RenderUpdateFlag flag); void disableVertex(uint32_t location); bool draw(GlRenderTask* task, GlStageBuffer* gpuBuffer, RenderUpdateFlag flag); - void updateTransform(const RenderTransform* transform, float w, float h); + void updateTransform(const RenderTransform* transform); void setViewport(const RenderRegion& viewport); - float* getTransformMatrix(); + const RenderRegion& getViewport(); + const Matrix& getTransformMatrix(); GlStencilMode getStencilMode(RenderUpdateFlag flag); RenderRegion getBounds() const; @@ -204,7 +205,6 @@ private: Array strokeVertex = {}; Array fillIndex = {}; Array strokeIndex = {}; - float mTransform[16]; Matrix mMatrix = {}; FillRule mFillRule = FillRule::Winding; diff --git a/src/renderer/gl_engine/tvgGlRenderPass.cpp b/src/renderer/gl_engine/tvgGlRenderPass.cpp index fbeaeeca..934cbffb 100644 --- a/src/renderer/gl_engine/tvgGlRenderPass.cpp +++ b/src/renderer/gl_engine/tvgGlRenderPass.cpp @@ -21,67 +21,10 @@ * SOFTWARE. */ +#include "tvgMath.h" #include "tvgGlRenderPass.h" #include "tvgGlRenderTask.h" - -GlRenderTarget::GlRenderTarget(uint32_t width, uint32_t height): mWidth(width), mHeight(height) {} - -GlRenderTarget::~GlRenderTarget() -{ - if (mFbo == 0) return; - GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, 0)); - GL_CHECK(glDeleteFramebuffers(1, &mFbo)); - - if (mColorTex != 0) { - GL_CHECK(glDeleteTextures(1, &mColorTex)); - } - if (mDepthStencilBuffer != 0) { - GL_CHECK(glDeleteRenderbuffers(1, &mDepthStencilBuffer)); - } -} - -void GlRenderTarget::init(GLint resolveId) -{ - if (mFbo != 0 || mWidth == 0 || mHeight == 0) return; - - GL_CHECK(glGenFramebuffers(1, &mFbo)); - - GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, mFbo)); - - GL_CHECK(glGenRenderbuffers(1, &mColorBuffer)); - GL_CHECK(glBindRenderbuffer(GL_RENDERBUFFER, mColorBuffer)); - GL_CHECK(glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_RGBA8, mWidth, mHeight)); - - GL_CHECK(glGenRenderbuffers(1, &mDepthStencilBuffer)); - - GL_CHECK(glBindRenderbuffer(GL_RENDERBUFFER, mDepthStencilBuffer)); - - GL_CHECK(glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_DEPTH24_STENCIL8, mWidth, mHeight)); - - GL_CHECK(glBindRenderbuffer(GL_RENDERBUFFER, 0)); - - GL_CHECK(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, mColorBuffer)); - GL_CHECK(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, mDepthStencilBuffer)); - - // resolve target - GL_CHECK(glGenTextures(1, &mColorTex)); - - GL_CHECK(glBindTexture(GL_TEXTURE_2D, mColorTex)); - GL_CHECK(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, mWidth, mHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr)); - - GL_CHECK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); - GL_CHECK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); - GL_CHECK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); - GL_CHECK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); - - GL_CHECK(glBindTexture(GL_TEXTURE_2D, 0)); - - GL_CHECK(glGenFramebuffers(1, &mResolveFbo)); - GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, mResolveFbo)); - GL_CHECK(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mColorTex, 0)); - - GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, resolveId)); -} +#include "tvgGlGeometry.h" GlRenderPass::GlRenderPass(GlRenderTarget* fbo): mFbo(fbo), mTasks(), mDrawDepth(0) {} @@ -109,3 +52,20 @@ void GlRenderPass::addRenderTask(GlRenderTask* task) { mTasks.push(task); } + +void GlRenderPass::getMatrix(float *dst, const Matrix &matrix) const +{ + const auto& vp = getViewport(); + + Matrix postMatrix{}; + mathIdentity(&postMatrix); + mathTranslate(&postMatrix, -vp.x, -vp.y); + + auto m = postMatrix * matrix; + + float modelMatrix[16]; + GET_MATRIX44(m, modelMatrix); + MVP_MATRIX(vp.w, vp.h); + + MULTIPLY_MATRIX(mvp, modelMatrix, dst); +} diff --git a/src/renderer/gl_engine/tvgGlRenderPass.h b/src/renderer/gl_engine/tvgGlRenderPass.h index b2bc1d15..a0fefc17 100644 --- a/src/renderer/gl_engine/tvgGlRenderPass.h +++ b/src/renderer/gl_engine/tvgGlRenderPass.h @@ -28,34 +28,10 @@ #include "tvgGlCommon.h" #include "tvgGlRenderTask.h" +#include "tvgGlRenderTarget.h" class GlProgram; -class GlRenderTarget -{ -public: - GlRenderTarget(uint32_t width, uint32_t height); - ~GlRenderTarget(); - - void init(GLint resolveId); - - GLuint getFboId() { return mFbo; } - GLuint getResolveFboId() { return mResolveFbo; } - GLuint getColorTexture() { return mColorTex; } - - uint32_t getWidth() const { return mWidth; } - uint32_t getHeight() const { return mHeight; } - -private: - uint32_t mWidth = 0; - uint32_t mHeight = 0; - GLuint mFbo = 0; - GLuint mColorBuffer = 0; - GLuint mDepthStencilBuffer = 0; - GLuint mResolveFbo = 0; - GLuint mColorTex = 0; -}; - class GlRenderPass { public: @@ -64,12 +40,22 @@ public: ~GlRenderPass(); + bool isEmpty() const { return mFbo == nullptr; } + void addRenderTask(GlRenderTask* task); GLuint getFboId() { return mFbo->getFboId(); } GLuint getTextureId() { return mFbo->getColorTexture(); } + const RenderRegion& getViewport() const { return mFbo->getViewport(); } + + uint32_t getFboWidth() const { return mFbo->getWidth(); } + + uint32_t getFboHeight() const { return mFbo->getHeight(); } + + void getMatrix(float dst[16], const Matrix& matrix) const; + template T* endRenderPass(GlProgram* program, GLuint targetFbo) { int32_t maxDepth = mDrawDepth + 1; @@ -78,7 +64,13 @@ public: mTasks[i]->normalizeDrawDepth(maxDepth); } - return new T(program, targetFbo, mFbo, std::move(mTasks)); + auto task = new T(program, targetFbo, mFbo, std::move(mTasks)); + + const auto& vp = mFbo->getViewport(); + + task->setRenderSize(static_cast(vp.w), static_cast(vp.h)); + + return task; } int nextDrawDepth() { return ++mDrawDepth; } diff --git a/src/renderer/gl_engine/tvgGlRenderTarget.cpp b/src/renderer/gl_engine/tvgGlRenderTarget.cpp new file mode 100644 index 00000000..dfb0a6da --- /dev/null +++ b/src/renderer/gl_engine/tvgGlRenderTarget.cpp @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2023 - 2024 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 "tvgGlRenderTarget.h" + +GlRenderTarget::GlRenderTarget(uint32_t width, uint32_t height): mWidth(width), mHeight(height) {} + +GlRenderTarget::~GlRenderTarget() +{ + if (mFbo == 0) return; + GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, 0)); + GL_CHECK(glDeleteFramebuffers(1, &mFbo)); + + if (mColorTex != 0) { + GL_CHECK(glDeleteTextures(1, &mColorTex)); + } + if (mDepthStencilBuffer != 0) { + GL_CHECK(glDeleteRenderbuffers(1, &mDepthStencilBuffer)); + } +} + +void GlRenderTarget::init(GLint resolveId) +{ + if (mFbo != 0 || mWidth == 0 || mHeight == 0) return; + + GL_CHECK(glGenFramebuffers(1, &mFbo)); + + GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, mFbo)); + + GL_CHECK(glGenRenderbuffers(1, &mColorBuffer)); + GL_CHECK(glBindRenderbuffer(GL_RENDERBUFFER, mColorBuffer)); + GL_CHECK(glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_RGBA8, mWidth, mHeight)); + + GL_CHECK(glGenRenderbuffers(1, &mDepthStencilBuffer)); + + GL_CHECK(glBindRenderbuffer(GL_RENDERBUFFER, mDepthStencilBuffer)); + + GL_CHECK(glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_DEPTH24_STENCIL8, mWidth, mHeight)); + + GL_CHECK(glBindRenderbuffer(GL_RENDERBUFFER, 0)); + + GL_CHECK(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, mColorBuffer)); + GL_CHECK(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, mDepthStencilBuffer)); + + // resolve target + GL_CHECK(glGenTextures(1, &mColorTex)); + + GL_CHECK(glBindTexture(GL_TEXTURE_2D, mColorTex)); + GL_CHECK(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, mWidth, mHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr)); + + GL_CHECK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); + GL_CHECK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); + GL_CHECK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); + GL_CHECK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); + + GL_CHECK(glBindTexture(GL_TEXTURE_2D, 0)); + + GL_CHECK(glGenFramebuffers(1, &mResolveFbo)); + GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, mResolveFbo)); + GL_CHECK(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mColorTex, 0)); + + GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, resolveId)); +} + +GlRenderTargetPool::GlRenderTargetPool(uint32_t maxWidth, uint32_t maxHeight): mMaxWidth(maxWidth), mMaxHeight(maxHeight), mPool() {} + +GlRenderTargetPool::~GlRenderTargetPool() +{ + for (uint32_t i = 0; i < mPool.count; i++) { + delete mPool[i]; + } +} + +uint32_t alignPow2(uint32_t value) +{ + uint32_t ret = 1; + while (ret < value) { + ret <<= 1; + } + return ret; +} + +GlRenderTarget* GlRenderTargetPool::getRenderTarget(const RenderRegion& vp, GLuint resolveId) +{ + uint32_t width = static_cast(vp.w); + uint32_t height = static_cast(vp.h); + + // pow2 align width and height + if (width >= mMaxWidth) width = mMaxWidth; + else width = alignPow2(width); + + if (width >= mMaxWidth) width = mMaxWidth; + + if (height >= mMaxHeight) height = mMaxHeight; + else height = alignPow2(height); + + if (height >= mMaxHeight) height = mMaxHeight; + + for (uint32_t i = 0; i < mPool.count; i++) { + auto rt = mPool[i]; + + if (rt->getWidth() == width && rt->getHeight() == height) { + rt->setViewport(vp); + return rt; + } + } + + auto rt = new GlRenderTarget(width, height); + rt->init(resolveId); + rt->setViewport(vp); + mPool.push(rt); + return rt; +} diff --git a/src/renderer/gl_engine/tvgGlRenderTarget.h b/src/renderer/gl_engine/tvgGlRenderTarget.h new file mode 100644 index 00000000..994104b0 --- /dev/null +++ b/src/renderer/gl_engine/tvgGlRenderTarget.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2023 - 2024 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_RENDER_RENDER_TARGET_H_ +#define _TVG_GL_RENDER_RENDER_TARGET_H_ + +#include "tvgGlCommon.h" + +class GlRenderTarget +{ +public: + GlRenderTarget(uint32_t width, uint32_t height); + ~GlRenderTarget(); + + void init(GLint resolveId); + + GLuint getFboId() { return mFbo; } + GLuint getResolveFboId() { return mResolveFbo; } + GLuint getColorTexture() { return mColorTex; } + + uint32_t getWidth() const { return mWidth; } + uint32_t getHeight() const { return mHeight; } + + void setViewport(const RenderRegion& vp) { mViewport = vp; } + const RenderRegion& getViewport() const { return mViewport; } + +private: + uint32_t mWidth = 0; + uint32_t mHeight = 0; + RenderRegion mViewport{}; + GLuint mFbo = 0; + GLuint mColorBuffer = 0; + GLuint mDepthStencilBuffer = 0; + GLuint mResolveFbo = 0; + GLuint mColorTex = 0; +}; + +class GlRenderTargetPool { +public: + GlRenderTargetPool(uint32_t maxWidth, uint32_t maxHeight); + ~GlRenderTargetPool(); + + GlRenderTarget* getRenderTarget(const RenderRegion& vp, GLuint resolveId = 0); +private: + uint32_t mMaxWidth = 0; + uint32_t mMaxHeight = 0; + Array mPool; +}; + +#endif //_TVG_GL_RENDER_RENDER_TARGET_H_ \ No newline at end of file diff --git a/src/renderer/gl_engine/tvgGlRenderTask.cpp b/src/renderer/gl_engine/tvgGlRenderTask.cpp index 793046f2..816ae840 100644 --- a/src/renderer/gl_engine/tvgGlRenderTask.cpp +++ b/src/renderer/gl_engine/tvgGlRenderTask.cpp @@ -103,6 +103,13 @@ void GlRenderTask::setDrawRange(uint32_t offset, uint32_t count) void GlRenderTask::setViewport(const RenderRegion &viewport) { mViewport = viewport; + if (mViewport.w < 0) { + mViewport.w = 0; + } + + if (mViewport.h < 0) { + mViewport.h = 0; + } } GlStencilCoverTask::GlStencilCoverTask(GlRenderTask* stencil, GlRenderTask* cover, GlStencilMode mode) @@ -172,11 +179,9 @@ GlComposeTask::~GlComposeTask() void GlComposeTask::run() { GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, getSelfFbo())); - GL_CHECK(glViewport(0, 0, mFbo->getWidth(), mFbo->getHeight())); + GL_CHECK(glViewport(0, 0, mRenderWidth, mRenderHeight)); - const auto& vp = getViewport(); - - GL_CHECK(glScissor(vp.x, vp.y, vp.w, vp.h)); + GL_CHECK(glScissor(0, 0, mRenderWidth, mRenderHeight)); // clear this fbo if (mClearBuffer) { @@ -211,14 +216,8 @@ void GlComposeTask::onResolve() { GL_CHECK(glBindFramebuffer(GL_READ_FRAMEBUFFER, getSelfFbo())); GL_CHECK(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, getResolveFboId())); - const auto& vp = getViewport(); - auto x1 = vp.x; - auto y1 = vp.y; - auto x2 = x1 + vp.w; - auto y2 = y1 + vp.h; - - GL_CHECK(glBlitFramebuffer(x1, y1, x2, y2, x1, y1, x2, y2, GL_COLOR_BUFFER_BIT, GL_NEAREST)); + GL_CHECK(glBlitFramebuffer(0, 0, mRenderWidth, mRenderHeight, 0, 0, mRenderWidth, mRenderHeight, GL_COLOR_BUFFER_BIT, GL_NEAREST)); } GlBlitTask::GlBlitTask(GlProgram* program, GLuint target, GlRenderTarget* fbo, Array&& tasks) @@ -251,12 +250,21 @@ GlDrawBlitTask::GlDrawBlitTask(GlProgram* program, GLuint target, GlRenderTarget { } +GlDrawBlitTask::~GlDrawBlitTask() +{ + if (mPrevTask) delete mPrevTask; +} + void GlDrawBlitTask::run() { + if (mPrevTask) mPrevTask->run(); + GlComposeTask::run(); GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, getTargetFbo())); + GL_CHECK(glViewport(0, 0, mParentWidth, mParentHeight)); + GL_CHECK(glScissor(0, 0, mParentWidth, mParentWidth)); GlRenderTask::run(); } diff --git a/src/renderer/gl_engine/tvgGlRenderTask.h b/src/renderer/gl_engine/tvgGlRenderTask.h index 0c7c87be..46b87560 100644 --- a/src/renderer/gl_engine/tvgGlRenderTask.h +++ b/src/renderer/gl_engine/tvgGlRenderTask.h @@ -126,6 +126,8 @@ public: void run() override; + void setRenderSize(uint32_t width, uint32_t height) { mRenderWidth = width; mRenderHeight = height; } + bool mClearBuffer = true; protected: @@ -140,6 +142,8 @@ private: GLuint mTargetFbo; GlRenderTarget* mFbo; Array mTasks; + uint32_t mRenderWidth = 0; + uint32_t mRenderHeight = 0; }; class GlBlitTask : public GlComposeTask @@ -162,9 +166,18 @@ class GlDrawBlitTask : public GlComposeTask { public: GlDrawBlitTask(GlProgram*, GLuint target, GlRenderTarget* fbo, Array&& tasks); - ~GlDrawBlitTask() override = default; + ~GlDrawBlitTask() override; + + void setPrevTask(GlRenderTask* task) { mPrevTask = task; } + + void setParentSize(uint32_t width, uint32_t height) { mParentWidth = width; mParentHeight = height; } void run() override; + +private: + GlRenderTask* mPrevTask = nullptr; + uint32_t mParentWidth = 0; + uint32_t mParentHeight = 0; }; class GlClipTask : public GlRenderTask diff --git a/src/renderer/gl_engine/tvgGlRenderer.cpp b/src/renderer/gl_engine/tvgGlRenderer.cpp index 5485174f..a40c390b 100644 --- a/src/renderer/gl_engine/tvgGlRenderer.cpp +++ b/src/renderer/gl_engine/tvgGlRenderer.cpp @@ -66,6 +66,7 @@ bool GlRenderer::target(int32_t id, uint32_t w, uint32_t h) mTargetFboId = static_cast(id); mRootTarget = make_unique(surface.w, surface.h); + mRootTarget->setViewport(RenderRegion{0, 0, static_cast(surface.w), static_cast(surface.h)}); mRootTarget->init(mTargetFboId); mRenderPassStack.clear(); @@ -119,8 +120,15 @@ bool GlRenderer::sync() RenderRegion GlRenderer::region(RenderData data) { + if (currentPass()->isEmpty()) return RenderRegion{0, 0, 0, 0}; + auto shape = reinterpret_cast(data); - return shape->geometry->getBounds(); + auto bounds = shape->geometry->getBounds(); + + auto const& vp = currentPass()->getViewport(); + bounds.intersect(vp); + + return bounds; } @@ -145,7 +153,12 @@ bool GlRenderer::postRender() Compositor* GlRenderer::target(const RenderRegion& region, TVG_UNUSED ColorSpace cs) { - mComposeStack.emplace_back(make_unique(region)); + auto vp = region; + if (currentPass()->isEmpty()) return nullptr; + + vp.intersect(currentPass()->getViewport()); + + mComposeStack.emplace_back(make_unique(vp)); return mComposeStack.back().get(); } @@ -160,10 +173,18 @@ bool GlRenderer::beginComposite(Compositor* cmp, CompositeMethod method, uint8_t uint32_t index = mRenderPassStack.size() - 1; if (index >= mComposePool.count) { - mComposePool.push( new GlRenderTarget(surface.w, surface.h)); - mComposePool[index]->init(mTargetFboId); + mComposePool.push( new GlRenderTargetPool(surface.w, surface.h)); + } + + auto glCmp = static_cast(cmp); + + if (glCmp->bbox.w > 0 && glCmp->bbox.h > 0) { + auto renderTarget = mComposePool[index]->getRenderTarget(glCmp->bbox); + mRenderPassStack.emplace_back(GlRenderPass(renderTarget)); + } else { + // empty render pass + mRenderPassStack.emplace_back(GlRenderPass(nullptr)); } - mRenderPassStack.emplace_back(GlRenderPass(mComposePool[index])); return true; } @@ -214,7 +235,20 @@ bool GlRenderer::renderImage(void* data) if (!sdata) return false; + if (currentPass()->isEmpty()) return true; + if ((sdata->updateFlag & RenderUpdateFlag::Image) == 0) return true; + const auto& vp = currentPass()->getViewport(); + + auto bbox = sdata->geometry->getViewport(); + + bbox.intersect(vp); + + if (bbox.w <= 0 || bbox.h <= 0) return true; + + auto x = bbox.x - vp.x; + auto y = bbox.y - vp.y; + int32_t drawDepth = currentPass()->nextDrawDepth(); @@ -227,14 +261,19 @@ bool GlRenderer::renderImage(void* data) // matrix buffer { - auto matrix = sdata->geometry->getTransformMatrix(); + const auto& matrix = sdata->geometry->getTransformMatrix(); + + float matrix44[16]; + + currentPass()->getMatrix(matrix44, matrix); + uint32_t loc = task->getProgram()->getUniformBlockIndex("Matrix"); task->addBindResource(GlBindingResource{ 0, loc, mGpuBuffer->getBufferId(), - mGpuBuffer->push(matrix, 16 * sizeof(float), true), + mGpuBuffer->push(matrix44, 16 * sizeof(float), true), 16 * sizeof(float), }); } @@ -257,6 +296,13 @@ bool GlRenderer::renderImage(void* data) task->addBindResource(GlBindingResource{0, sdata->texId, loc}); } + task->setViewport(RenderRegion{ + x, + vp.h - y - bbox.h, + bbox.w, + bbox.h + }); + currentPass()->addRenderTask(task); return true; @@ -268,8 +314,17 @@ bool GlRenderer::renderShape(RenderData data) auto sdata = static_cast(data); if (!sdata) return false; + if (currentPass()->isEmpty()) return true; + if (sdata->updateFlag == RenderUpdateFlag::None) return true; + const auto& vp = currentPass()->getViewport(); + + auto bbox = sdata->geometry->getViewport(); + bbox.intersect(vp); + + if (bbox.w <= 0 || bbox.h <= 0) return true; + uint8_t r = 0, g = 0, b = 0, a = 0; int32_t drawDepth1 = 0, drawDepth2 = 0, drawDepth3 = 0; @@ -355,7 +410,7 @@ static GLuint _genTexture(Surface* image) RenderData GlRenderer::prepare(Surface* image, const RenderMesh* mesh, RenderData data, const RenderTransform* transform, Array& clips, uint8_t opacity, RenderUpdateFlag flags) { - if (flags == RenderUpdateFlag::None) return nullptr; + if (flags == RenderUpdateFlag::None) return data; auto sdata = static_cast(data); @@ -373,13 +428,8 @@ RenderData GlRenderer::prepare(Surface* image, const RenderMesh* mesh, RenderDat sdata->geometry = make_unique(); } - sdata->geometry->updateTransform(transform, sdata->viewWd, sdata->viewHt); - sdata->geometry->setViewport(RenderRegion{ - mViewport.x, - static_cast((surface.h - mViewport.y - mViewport.h)), - mViewport.w, - mViewport.h, - }); + sdata->geometry->updateTransform(transform); + sdata->geometry->setViewport(mViewport); sdata->geometry->tesselate(image, mesh, flags); @@ -438,13 +488,8 @@ RenderData GlRenderer::prepare(const RenderShape& rshape, RenderData data, const if (sdata->updateFlag == RenderUpdateFlag::None) return sdata; - sdata->geometry->updateTransform(transform, sdata->viewWd, sdata->viewHt); - sdata->geometry->setViewport(RenderRegion{ - mViewport.x, - static_cast(surface.h - mViewport.y - mViewport.h), - mViewport.w, - mViewport.h, - }); + sdata->geometry->updateTransform(transform); + sdata->geometry->setViewport(mViewport); if (sdata->updateFlag & (RenderUpdateFlag::Color | RenderUpdateFlag::Stroke | RenderUpdateFlag::Gradient | RenderUpdateFlag::GradientStroke | RenderUpdateFlag::Transform | RenderUpdateFlag::Path)) { @@ -563,6 +608,16 @@ void GlRenderer::initShaders() void GlRenderer::drawPrimitive(GlShape& sdata, uint8_t r, uint8_t g, uint8_t b, uint8_t a, RenderUpdateFlag flag, int32_t depth) { + const auto& vp = currentPass()->getViewport(); + auto bbox = sdata.geometry->getViewport(); + + bbox.intersect(vp); + + auto x = bbox.x - vp.x; + auto y = bbox.y - vp.y; + auto w = bbox.w; + auto h = bbox.h; + auto task = new GlRenderTask(mPrograms[RT_Color].get()); task->setDrawDepth(depth); @@ -571,6 +626,13 @@ void GlRenderer::drawPrimitive(GlShape& sdata, uint8_t r, uint8_t g, uint8_t b, return; } + task->setViewport(RenderRegion { + x, + vp.h - y - h, + w, + h + }); + GlRenderTask* stencilTask = nullptr; GlStencilMode stencilMode = sdata.geometry->getStencilMode(flag); @@ -583,10 +645,15 @@ void GlRenderer::drawPrimitive(GlShape& sdata, uint8_t r, uint8_t g, uint8_t b, // matrix buffer { - auto matrix = sdata.geometry->getTransformMatrix(); + const auto& matrix = sdata.geometry->getTransformMatrix(); + + float matrix44[16]; + + currentPass()->getMatrix(matrix44, matrix); + uint32_t loc = task->getProgram()->getUniformBlockIndex("Matrix"); - uint32_t viewOffset = mGpuBuffer->push(matrix, 16 * sizeof(float), true); + uint32_t viewOffset = mGpuBuffer->push(matrix44, 16 * sizeof(float), true); task->addBindResource(GlBindingResource{ 0, @@ -631,6 +698,12 @@ void GlRenderer::drawPrimitive(GlShape& sdata, uint8_t r, uint8_t g, uint8_t b, void GlRenderer::drawPrimitive(GlShape& sdata, const Fill* fill, RenderUpdateFlag flag, int32_t depth) { + + const auto& vp = currentPass()->getViewport(); + auto bbox = sdata.geometry->getViewport(); + + bbox.intersect(vp); + const Fill::ColorStop* stops = nullptr; auto stopCnt = min(fill->colorStops(&stops), static_cast(MAX_GRADIENT_STOPS)); @@ -648,7 +721,20 @@ void GlRenderer::drawPrimitive(GlShape& sdata, const Fill* fill, RenderUpdateFla task->setDrawDepth(depth); - if (!sdata.geometry->draw(task, mGpuBuffer.get(), flag)) return; + if (!sdata.geometry->draw(task, mGpuBuffer.get(), flag)) { + delete task; + return; + } + + auto x = bbox.x - vp.x; + auto y = bbox.y - vp.y; + + task->setViewport(RenderRegion { + x, + vp.h - y - bbox.h, + bbox.w, + bbox.h + }); GlRenderTask* stencilTask = nullptr; GlStencilMode stencilMode = sdata.geometry->getStencilMode(flag); @@ -659,7 +745,7 @@ void GlRenderer::drawPrimitive(GlShape& sdata, const Fill* fill, RenderUpdateFla // matrix buffer { - auto matrix = sdata.geometry->getTransformMatrix(); + const auto& matrix = sdata.geometry->getTransformMatrix(); auto gradientTransform = fill->transform(); float invMat4[16]; @@ -676,9 +762,13 @@ void GlRenderer::drawPrimitive(GlShape& sdata, const Fill* fill, RenderUpdateFla invMat4[15] = 1.f; } + float matrix44[16]; + + currentPass()->getMatrix(matrix44, matrix); + uint32_t loc = task->getProgram()->getUniformBlockIndex("Matrix"); - uint32_t viewOffset = mGpuBuffer->push(matrix, 16 * sizeof(float), true); + uint32_t viewOffset = mGpuBuffer->push(matrix44, 16 * sizeof(float), true); task->addBindResource(GlBindingResource{ 0, @@ -840,6 +930,8 @@ void GlRenderer::drawClip(Array& clips) clipDepths[i] = currentPass()->nextDrawDepth(); } + const auto& vp = currentPass()->getViewport(); + for (uint32_t i = 0; i < clips.count; ++i) { auto sdata = static_cast(clips[i]); @@ -849,11 +941,29 @@ void GlRenderer::drawClip(Array& clips) sdata->geometry->draw(clipTask, mGpuBuffer.get(), RenderUpdateFlag::Path); - auto matrix = sdata->geometry->getTransformMatrix(); + auto bbox = sdata->geometry->getViewport(); + + bbox.intersect(vp); + + auto x = bbox.x - vp.x; + auto y = bbox.y - vp.y; + + clipTask->setViewport(RenderRegion { + x, + vp.h - y - bbox.h, + bbox.w, + bbox.h, + }); + + const auto& matrix = sdata->geometry->getTransformMatrix(); + + float matrix44[16]; + + currentPass()->getMatrix(matrix44, matrix); uint32_t loc = clipTask->getProgram()->getUniformBlockIndex("Matrix"); - uint32_t viewOffset = mGpuBuffer->push(matrix, 16 * sizeof(float), true); + uint32_t viewOffset = mGpuBuffer->push(matrix44, 16 * sizeof(float), true); clipTask->addBindResource(GlBindingResource{ 0, @@ -876,7 +986,7 @@ void GlRenderer::drawClip(Array& clips) }); maskTask->setDrawRange(identityIndexOffset, 6); - maskTask->setViewport(RenderRegion{0, 0, static_cast(surface.w), static_cast(surface.h)}); + maskTask->setViewport(RenderRegion{0, 0, static_cast(vp.w), static_cast(vp.h)}); currentPass()->addRenderTask(new GlClipTask(clipTask, maskTask)); } @@ -891,7 +1001,8 @@ GlRenderPass* GlRenderer::currentPass() void GlRenderer::prepareBlitTask(GlBlitTask* task) { - prepareCmpTask(task, mViewport); + RenderRegion region{0, 0, static_cast(surface.w), static_cast(surface.h)}; + prepareCmpTask(task, region, surface.w, surface.h); { uint32_t loc = task->getProgram()->getUniformLocation("uSrcTexture"); @@ -899,21 +1010,43 @@ void GlRenderer::prepareBlitTask(GlBlitTask* task) } } -void GlRenderer::prepareCmpTask(GlRenderTask* task, const RenderRegion& vp) +void GlRenderer::prepareCmpTask(GlRenderTask* task, const RenderRegion& vp, uint32_t cmpWidth, uint32_t cmpHeight) { // we use 1:1 blit mapping since compositor fbo is same size as root fbo Array vertices(4 * 4); - float left = -1.f; - float top = 1.f; - float right = 1.f; - float bottom = -1.f; + const auto& passVp = currentPass()->getViewport(); + + auto taskVp = vp; + taskVp.intersect(passVp); + + auto x = taskVp.x - passVp.x; + auto y = taskVp.y - passVp.y; + auto w = taskVp.w; + auto h = taskVp.h; + + float rw = static_cast(passVp.w); + float rh = static_cast(passVp.h); + + float l = static_cast(x); + float t = static_cast(rh - y); + float r = static_cast(x + w); + float b = static_cast(rh - y - h); + + // map vp ltrp to -1:1 + float left = (l / rw) * 2.f - 1.f; + float top = (t / rh) * 2.f - 1.f; + float right = (r / rw) * 2.f - 1.f; + float bottom = (b / rh) * 2.f - 1.f; + + float uw = static_cast(w) / static_cast(cmpWidth); + float uh = static_cast(h) / static_cast(cmpHeight); // left top point vertices.push(left); vertices.push(top); vertices.push(0.f); - vertices.push(1.f); + vertices.push(uh); // left bottom point vertices.push(left); vertices.push(bottom); @@ -922,12 +1055,12 @@ void GlRenderer::prepareCmpTask(GlRenderTask* task, const RenderRegion& vp) // right top point vertices.push(right); vertices.push(top); - vertices.push(1.f); - vertices.push(1.f); + vertices.push(uw); + vertices.push(uh); // right bottom point vertices.push(right); vertices.push(bottom); - vertices.push(1.f); + vertices.push(uw); vertices.push(0.f); Array indices(6); @@ -946,11 +1079,12 @@ void GlRenderer::prepareCmpTask(GlRenderTask* task, const RenderRegion& vp) task->addVertexLayout(GlVertexLayout{1, 2, 4 * sizeof(float), vertexOffset + 2 * sizeof(float)}); task->setDrawRange(indexOffset, indices.count); + task->setViewport(RenderRegion{ - vp.x, - static_cast((surface.h - vp.y - vp.h)), - vp.w, - vp.h, + x, + static_cast((passVp.h - y - h)), + w, + h, }); } @@ -965,6 +1099,8 @@ void GlRenderer::endRenderPass(Compositor* cmp) auto mask_pass = std::move(mRenderPassStack.back()); mRenderPassStack.pop_back(); + if (self_pass.isEmpty() || mask_pass.isEmpty()) return; + GlProgram* program = nullptr; switch(cmp->method) { case CompositeMethod::ClipPath: @@ -1002,17 +1138,14 @@ void GlRenderer::endRenderPass(Compositor* cmp) auto prev_task = mask_pass.endRenderPass(nullptr, currentPass()->getFboId()); prev_task->setDrawDepth(currentPass()->nextDrawDepth()); - prev_task->setViewport(RenderRegion{ - gl_cmp->bbox.x, - static_cast((surface.h - gl_cmp->bbox.y - gl_cmp->bbox.h)), - gl_cmp->bbox.w, - gl_cmp->bbox.h, - }); - currentPass()->addRenderTask(prev_task); + prev_task->setRenderSize(static_cast(gl_cmp->bbox.w), static_cast(gl_cmp->bbox.h)); + prev_task->setViewport(gl_cmp->bbox); auto compose_task = self_pass.endRenderPass(program, currentPass()->getFboId()); + compose_task->setRenderSize(static_cast(gl_cmp->bbox.w), static_cast(gl_cmp->bbox.h)); + compose_task->setPrevTask(prev_task); - prepareCmpTask(compose_task, gl_cmp->bbox); + prepareCmpTask(compose_task, gl_cmp->bbox, self_pass.getFboWidth(), self_pass.getFboHeight()); { uint32_t loc = program->getUniformLocation("uSrcTexture"); @@ -1025,16 +1158,19 @@ void GlRenderer::endRenderPass(Compositor* cmp) } compose_task->setDrawDepth(currentPass()->nextDrawDepth()); + compose_task->setParentSize(static_cast(currentPass()->getViewport().w), static_cast(currentPass()->getViewport().h)); currentPass()->addRenderTask(compose_task); } else { auto renderPass = std::move(mRenderPassStack.back()); mRenderPassStack.pop_back(); + if (renderPass.isEmpty()) return; + auto task = renderPass.endRenderPass( mPrograms[RT_Image].get(), currentPass()->getFboId()); - - prepareCmpTask(task, gl_cmp->bbox); + task->setRenderSize(static_cast(gl_cmp->bbox.w), static_cast(gl_cmp->bbox.h)); + prepareCmpTask(task, gl_cmp->bbox, renderPass.getFboWidth(), renderPass.getFboHeight()); task->setDrawDepth(currentPass()->nextDrawDepth()); // matrix buffer @@ -1073,7 +1209,7 @@ void GlRenderer::endRenderPass(Compositor* cmp) uint32_t loc = task->getProgram()->getUniformLocation("uTexture"); task->addBindResource(GlBindingResource{0, renderPass.getTextureId(), loc}); } - + task->setParentSize(static_cast(currentPass()->getViewport().w), static_cast(currentPass()->getViewport().h)); currentPass()->addRenderTask(std::move(task)); } } diff --git a/src/renderer/gl_engine/tvgGlRenderer.h b/src/renderer/gl_engine/tvgGlRenderer.h index cc024d39..f3a683ad 100644 --- a/src/renderer/gl_engine/tvgGlRenderer.h +++ b/src/renderer/gl_engine/tvgGlRenderer.h @@ -25,6 +25,7 @@ #include +#include "tvgGlRenderTarget.h" #include "tvgGlRenderTask.h" #include "tvgGlGpuBuffer.h" #include "tvgGlRenderPass.h" @@ -92,7 +93,7 @@ private: GlRenderPass* currentPass(); void prepareBlitTask(GlBlitTask* task); - void prepareCmpTask(GlRenderTask* task, const RenderRegion& vp); + void prepareCmpTask(GlRenderTask* task, const RenderRegion& vp, uint32_t cmpWidth, uint32_t cmpHeight); void endRenderPass(Compositor* cmp); Surface surface; @@ -102,7 +103,7 @@ private: unique_ptr mGpuBuffer; vector> mPrograms; unique_ptr mRootTarget = {}; - Array mComposePool = {}; + Array mComposePool = {}; vector mRenderPassStack = {}; vector> mComposeStack = {};