From 3fbb55440add28d0c52af499efc1f9c591c1ecb8 Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Mon, 12 May 2025 11:50:52 +0900 Subject: [PATCH] renderer: ++engines safety Added drawing exceptions when target is not properly ready. Now, Canvas::update() Canvas::draw() will return InsufficientCondition if the target has not been set beforehand. --- src/renderer/gl_engine/tvgGlRenderTarget.cpp | 3 ++- src/renderer/gl_engine/tvgGlRenderTarget.h | 3 ++- src/renderer/gl_engine/tvgGlRenderer.cpp | 6 ++++++ src/renderer/sw_engine/tvgSwRenderer.cpp | 4 ++-- src/renderer/wg_engine/tvgWgCommon.h | 5 +++++ src/renderer/wg_engine/tvgWgRenderer.cpp | 6 ++++++ test/testPaint.cpp | 3 +++ test/testScene.cpp | 3 ++- test/testText.cpp | 4 ++++ 9 files changed, 32 insertions(+), 5 deletions(-) diff --git a/src/renderer/gl_engine/tvgGlRenderTarget.cpp b/src/renderer/gl_engine/tvgGlRenderTarget.cpp index b0157ff3..47c2f831 100644 --- a/src/renderer/gl_engine/tvgGlRenderTarget.cpp +++ b/src/renderer/gl_engine/tvgGlRenderTarget.cpp @@ -40,8 +40,9 @@ GlRenderTarget::~GlRenderTarget() void GlRenderTarget::init(GLint resolveId) { - if (mFbo != 0 || mWidth == 0 || mHeight == 0) return; + if (mFbo != GL_INVALID_VALUE || mWidth == 0 || mHeight == 0) return; + //TODO: fbo is used. maybe we can consider the direct rendering with resolveId as well. GL_CHECK(glGenFramebuffers(1, &mFbo)); GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, mFbo)); diff --git a/src/renderer/gl_engine/tvgGlRenderTarget.h b/src/renderer/gl_engine/tvgGlRenderTarget.h index c1ff6d50..2cc9733b 100644 --- a/src/renderer/gl_engine/tvgGlRenderTarget.h +++ b/src/renderer/gl_engine/tvgGlRenderTarget.h @@ -44,12 +44,13 @@ public: void setViewport(const RenderRegion& vp) { mViewport = vp; } const RenderRegion& getViewport() const { return mViewport; } + bool invalid() const { return mFbo == GL_INVALID_VALUE; } private: uint32_t mWidth = 0; uint32_t mHeight = 0; RenderRegion mViewport{}; - GLuint mFbo = 0; + GLuint mFbo = GL_INVALID_VALUE; GLuint mColorBuffer = 0; GLuint mDepthStencilBuffer = 0; GLuint mResolveFbo = 0; diff --git a/src/renderer/gl_engine/tvgGlRenderer.cpp b/src/renderer/gl_engine/tvgGlRenderer.cpp index 685bfc15..e177854e 100644 --- a/src/renderer/gl_engine/tvgGlRenderer.cpp +++ b/src/renderer/gl_engine/tvgGlRenderer.cpp @@ -809,6 +809,8 @@ void GlRenderer::endRenderPass(RenderCompositor* cmp) bool GlRenderer::clear() { + if (mRootTarget.invalid()) return false; + mClearBuffer = true; return true; } @@ -896,6 +898,8 @@ RenderRegion GlRenderer::region(RenderData data) bool GlRenderer::preRender() { + if (mRootTarget.invalid()) return false; + currentContext(); if (mPrograms.empty()) initShaders(); mRenderPassStack.push(new GlRenderPass(&mRootTarget)); @@ -1525,6 +1529,8 @@ bool GlRenderer::viewport(const RenderRegion& vp) bool GlRenderer::preUpdate() { + if (mRootTarget.invalid()) return false; + currentContext(); return true; } diff --git a/src/renderer/sw_engine/tvgSwRenderer.cpp b/src/renderer/sw_engine/tvgSwRenderer.cpp index c93e6ff4..36c9aca2 100644 --- a/src/renderer/sw_engine/tvgSwRenderer.cpp +++ b/src/renderer/sw_engine/tvgSwRenderer.cpp @@ -359,7 +359,7 @@ bool SwRenderer::target(pixel_t* data, uint32_t stride, uint32_t w, uint32_t h, bool SwRenderer::preUpdate() { - return true; + return surface != nullptr; } @@ -371,7 +371,7 @@ bool SwRenderer::postUpdate() bool SwRenderer::preRender() { - return true; + return surface != nullptr; } diff --git a/src/renderer/wg_engine/tvgWgCommon.h b/src/renderer/wg_engine/tvgWgCommon.h index 847f50b3..45774b9e 100644 --- a/src/renderer/wg_engine/tvgWgCommon.h +++ b/src/renderer/wg_engine/tvgWgCommon.h @@ -70,6 +70,11 @@ struct WgContext { // release buffer objects void releaseBuffer(WGPUBuffer& buffer); + + bool invalid() + { + return !instance || !device; + } }; #endif // _TVG_WG_COMMON_H_ diff --git a/src/renderer/wg_engine/tvgWgRenderer.cpp b/src/renderer/wg_engine/tvgWgRenderer.cpp index 0c7e2950..1257738c 100644 --- a/src/renderer/wg_engine/tvgWgRenderer.cpp +++ b/src/renderer/wg_engine/tvgWgRenderer.cpp @@ -195,6 +195,8 @@ RenderData WgRenderer::prepare(RenderSurface* surface, RenderData data, const Ma bool WgRenderer::preRender() { + if (mContext.invalid()) return false; + // push rot render storage to the render tree stack assert(mRenderStorageStack.count == 0); mRenderStorageStack.push(&mRenderStorageRoot); @@ -301,6 +303,8 @@ const RenderSurface* WgRenderer::mainSurface() bool WgRenderer::clear() { + if (mContext.invalid()) return false; + //TODO: clear the current target buffer only if clear() is called return true; } @@ -632,6 +636,8 @@ void WgRenderer::dispose(RenderEffect* effect) bool WgRenderer::preUpdate() { + if (mContext.invalid()) return false; + return true; } diff --git a/test/testPaint.cpp b/test/testPaint.cpp index 8659769b..45fd97d3 100644 --- a/test/testPaint.cpp +++ b/test/testPaint.cpp @@ -121,6 +121,9 @@ TEST_CASE("Bounding Box", "[tvgPaint]") Initializer::init(0); auto canvas = unique_ptr(SwCanvas::gen()); + uint32_t buffer[100*100]; + canvas->target(buffer, 100, 100, 100, ColorSpace::ABGR8888); + auto shape = Shape::gen(); canvas->push(shape); canvas->sync(); diff --git a/test/testScene.cpp b/test/testScene.cpp index 77f127bf..7cc173b3 100644 --- a/test/testScene.cpp +++ b/test/testScene.cpp @@ -80,7 +80,8 @@ TEST_CASE("Scene Clear And Reuse Shape", "[tvgScene]") REQUIRE(Initializer::init(0) == Result::Success); { auto canvas = unique_ptr(SwCanvas::gen()); - REQUIRE(canvas); + uint32_t buffer[100*100]; + canvas->target(buffer, 100, 100, 100, ColorSpace::ABGR8888); auto scene = Scene::gen(); REQUIRE(scene); diff --git a/test/testText.cpp b/test/testText.cpp index 927b5040..91769482 100644 --- a/test/testText.cpp +++ b/test/testText.cpp @@ -120,6 +120,8 @@ TEST_CASE("Text Basic", "[tvgText]") Initializer::init(0); auto canvas = unique_ptr(SwCanvas::gen()); + uint32_t buffer[100*100]; + canvas->target(buffer, 100, 100, 100, ColorSpace::ABGR8888); auto text = Text::gen(); REQUIRE(text); @@ -144,6 +146,8 @@ TEST_CASE("Text with composite glyphs", "[tvgText]") Initializer::init(0); auto canvas = unique_ptr(SwCanvas::gen()); + uint32_t buffer[100*100]; + canvas->target(buffer, 100, 100, 100, ColorSpace::ABGR8888); auto text = Text::gen(); REQUIRE(text);