From 2855b9b339171e969fbb339dbc57195c7e68b1cd Mon Sep 17 00:00:00 2001 From: RuiwenTang Date: Tue, 6 Feb 2024 18:46:04 +0800 Subject: [PATCH] gl_engine: add stencil and cover render task * add new render task to do stencil and cover rendering which is a fallback rendering method to handle cases that trianglation tessellation failed * add a new tessellator to generate stencil and cover vertex mesh --- src/renderer/gl_engine/tvgGlGeometry.cpp | 15 ++++ src/renderer/gl_engine/tvgGlGeometry.h | 3 + src/renderer/gl_engine/tvgGlRenderTask.cpp | 41 ++++++++++ src/renderer/gl_engine/tvgGlRenderTask.h | 15 ++++ src/renderer/gl_engine/tvgGlRenderer.cpp | 52 ++++++++++++- src/renderer/gl_engine/tvgGlRenderer.h | 1 + src/renderer/gl_engine/tvgGlShaderSrc.cpp | 18 ++++- src/renderer/gl_engine/tvgGlShaderSrc.h | 2 + src/renderer/gl_engine/tvgGlTessellator.cpp | 83 +++++++++++++++++++++ src/renderer/gl_engine/tvgGlTessellator.h | 17 +++++ 10 files changed, 242 insertions(+), 5 deletions(-) diff --git a/src/renderer/gl_engine/tvgGlGeometry.cpp b/src/renderer/gl_engine/tvgGlGeometry.cpp index 3338d1b5..fe80c62d 100644 --- a/src/renderer/gl_engine/tvgGlGeometry.cpp +++ b/src/renderer/gl_engine/tvgGlGeometry.cpp @@ -45,6 +45,12 @@ bool GlGeometry::tesselate(const RenderShape& rshape, RenderUpdateFlag flag) if (!tess.tessellate(&rshape, true)) { fillVertex.clear(); fillIndex.clear(); + + BWTessellator bwTess{&fillVertex, &fillIndex}; + + bwTess.tessellate(&rshape); + + mStencilFill = true; } } @@ -212,3 +218,12 @@ float* GlGeometry::getTransforMatrix() { return mTransform; } + +bool GlGeometry::needStencilCover(RenderUpdateFlag flag) +{ + if (flag & RenderUpdateFlag::Stroke) return false; + if (flag & RenderUpdateFlag::GradientStroke) return false; + if (flag & RenderUpdateFlag::Image) return false; + + return mStencilFill; +} diff --git a/src/renderer/gl_engine/tvgGlGeometry.h b/src/renderer/gl_engine/tvgGlGeometry.h index d3ca98ee..d60b56a6 100644 --- a/src/renderer/gl_engine/tvgGlGeometry.h +++ b/src/renderer/gl_engine/tvgGlGeometry.h @@ -195,6 +195,7 @@ public: void updateTransform(const RenderTransform* transform, float w, float h); void setViewport(const RenderRegion& viewport); float* getTransforMatrix(); + bool needStencilCover(RenderUpdateFlag flag); private: RenderRegion viewport = {}; @@ -203,6 +204,8 @@ private: Array fillIndex = {}; Array strokeIndex = {}; float mTransform[16]; + + bool mStencilFill = false; }; #endif /* _TVG_GL_GEOMETRY_H_ */ diff --git a/src/renderer/gl_engine/tvgGlRenderTask.cpp b/src/renderer/gl_engine/tvgGlRenderTask.cpp index d28dfc2c..ab4000e1 100644 --- a/src/renderer/gl_engine/tvgGlRenderTask.cpp +++ b/src/renderer/gl_engine/tvgGlRenderTask.cpp @@ -27,6 +27,14 @@ /* External Class Implementation */ /************************************************************************/ +GlRenderTask::GlRenderTask(GlProgram* program, GlRenderTask* other): mProgram(program) +{ + mVertexLayout.push(other->mVertexLayout); + mViewport = other->mViewport; + mIndexOffset = other->mIndexOffset; + mIndexCount = other->mIndexCount; +} + void GlRenderTask::run() { // bind shader @@ -90,6 +98,39 @@ void GlRenderTask::setViewport(const RenderRegion &viewport) mViewport = viewport; } +GlStencilCoverTask::GlStencilCoverTask(GlRenderTask* stencil, GlRenderTask* cover) + :GlRenderTask(nullptr), mStencilTask(stencil), mCoverTask(cover) {} + +GlStencilCoverTask::~GlStencilCoverTask() +{ + delete mStencilTask; + delete mCoverTask; +} + +void GlStencilCoverTask::run() +{ + GL_CHECK(glEnable(GL_STENCIL_TEST)); + + GL_CHECK(glStencilFuncSeparate(GL_FRONT, GL_ALWAYS, 0x1, 0xFF)); + GL_CHECK(glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_KEEP, GL_INCR_WRAP)); + + GL_CHECK(glStencilFuncSeparate(GL_BACK, GL_ALWAYS, 0x1, 0xFF)); + GL_CHECK(glStencilOpSeparate(GL_BACK, GL_KEEP, GL_KEEP, GL_DECR_WRAP)); + + GL_CHECK(glColorMask(0, 0, 0, 0)); + + mStencilTask->run(); + + GL_CHECK(glStencilFunc(GL_NOTEQUAL, 0x0, 0xFF)); + GL_CHECK(glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE)); + + GL_CHECK(glColorMask(1, 1, 1, 1)); + + mCoverTask->run(); + + GL_CHECK(glDisable(GL_STENCIL_TEST)); +} + GlComposeTask::GlComposeTask(GlProgram* program, GLuint target, GLuint selfFbo, Array&& tasks) :GlRenderTask(program) ,mTargetFbo(target), mSelfFbo(selfFbo), mTasks() { diff --git a/src/renderer/gl_engine/tvgGlRenderTask.h b/src/renderer/gl_engine/tvgGlRenderTask.h index a9404551..424012e6 100644 --- a/src/renderer/gl_engine/tvgGlRenderTask.h +++ b/src/renderer/gl_engine/tvgGlRenderTask.h @@ -76,6 +76,8 @@ class GlRenderTask { public: GlRenderTask(GlProgram* program): mProgram(program) {} + GlRenderTask(GlProgram* program, GlRenderTask* other); + virtual ~GlRenderTask() = default; virtual void run(); @@ -95,6 +97,19 @@ private: Array mBindingResources = {}; }; +class GlStencilCoverTask : public GlRenderTask +{ +public: + GlStencilCoverTask(GlRenderTask* stencil, GlRenderTask* cover); + ~GlStencilCoverTask() override; + + void run() override; + +private: + GlRenderTask* mStencilTask; + GlRenderTask* mCoverTask; +}; + class GlComposeTask : public GlRenderTask { public: diff --git a/src/renderer/gl_engine/tvgGlRenderer.cpp b/src/renderer/gl_engine/tvgGlRenderer.cpp index 7d5eaa59..be9f3db9 100644 --- a/src/renderer/gl_engine/tvgGlRenderer.cpp +++ b/src/renderer/gl_engine/tvgGlRenderer.cpp @@ -91,6 +91,8 @@ bool GlRenderer::sync() GL_CHECK(glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA)); GL_CHECK(glEnable(GL_BLEND)); GL_CHECK(glEnable(GL_SCISSOR_TEST)); + GL_CHECK(glCullFace(GL_FRONT_AND_BACK)); + GL_CHECK(glFrontFace(GL_CCW)); mGpuBuffer->bind(); @@ -479,6 +481,8 @@ void GlRenderer::initShaders() mPrograms.push_back(make_unique(GlShader::gen(MASK_VERT_SHADER, MASK_SUB_FRAG_SHADER))); mPrograms.push_back(make_unique(GlShader::gen(MASK_VERT_SHADER, MASK_INTERSECT_FRAG_SHADER))); mPrograms.push_back(make_unique(GlShader::gen(MASK_VERT_SHADER, MASK_DIFF_FRAG_SHADER))); + // stencil Renderer + mPrograms.push_back(make_unique(GlShader::gen(STENCIL_VERT_SHADER, STENCIL_FRAG_SHADER))); } @@ -488,6 +492,10 @@ void GlRenderer::drawPrimitive(GlShape& sdata, uint8_t r, uint8_t g, uint8_t b, if (!sdata.geometry->draw(task, mGpuBuffer.get(), flag)) return; + GlRenderTask* stencilTask = nullptr; + + if (sdata.geometry->needStencilCover(flag)) stencilTask = new GlRenderTask(mPrograms[RT_Stencil].get(), task); + a = MULTIPLY(a, sdata.opacity); // matrix buffer @@ -495,13 +503,25 @@ void GlRenderer::drawPrimitive(GlShape& sdata, uint8_t r, uint8_t g, uint8_t b, auto matrix = sdata.geometry->getTransforMatrix(); uint32_t loc = task->getProgram()->getUniformBlockIndex("Matrix"); + uint32_t viewOffset = mGpuBuffer->push(matrix, 16 * sizeof(float), true); + task->addBindResource(GlBindingResource{ 0, loc, mGpuBuffer->getBufferId(), - mGpuBuffer->push(matrix, 16 * sizeof(float), true), + viewOffset, 16 * sizeof(float), }); + + if (stencilTask) { + stencilTask->addBindResource(GlBindingResource{ + 0, + static_cast(stencilTask->getProgram()->getUniformBlockIndex("Matrix")), + mGpuBuffer->getBufferId(), + viewOffset, + 16 * sizeof(float), + }); + } } // color { @@ -518,7 +538,11 @@ void GlRenderer::drawPrimitive(GlShape& sdata, uint8_t r, uint8_t g, uint8_t b, }); } - currentPass()->addRenderTask(task); + if (stencilTask) { + currentPass()->addRenderTask(new GlStencilCoverTask(stencilTask, task)); + } else { + currentPass()->addRenderTask(task); + } } @@ -541,18 +565,34 @@ void GlRenderer::drawPrimitive(GlShape& sdata, const Fill* fill, RenderUpdateFla if (!sdata.geometry->draw(task, mGpuBuffer.get(), flag)) return; + GlRenderTask* stencilTask = nullptr; + + if (sdata.geometry->needStencilCover(flag)) stencilTask = new GlRenderTask(mPrograms[RT_Stencil].get(), task); + // matrix buffer { auto matrix = sdata.geometry->getTransforMatrix(); uint32_t loc = task->getProgram()->getUniformBlockIndex("Matrix"); + uint32_t viewOffset = mGpuBuffer->push(matrix, 16 * sizeof(float), true); + task->addBindResource(GlBindingResource{ 0, loc, mGpuBuffer->getBufferId(), - mGpuBuffer->push(matrix, 16 * sizeof(float), true), + viewOffset, 16 * sizeof(float), }); + + if (stencilTask) { + stencilTask->addBindResource(GlBindingResource{ + 0, + static_cast(stencilTask->getProgram()->getUniformBlockIndex("Matrix")), + mGpuBuffer->getBufferId(), + viewOffset, + 16 * sizeof(float), + }); + } } // gradient block @@ -630,7 +670,11 @@ void GlRenderer::drawPrimitive(GlShape& sdata, const Fill* fill, RenderUpdateFla task->addBindResource(gradientBinding); } - currentPass()->addRenderTask(task); + if (stencilTask) { + currentPass()->addRenderTask(new GlStencilCoverTask(stencilTask, task)); + } else { + currentPass()->addRenderTask(task); + } } GlRenderPass* GlRenderer::currentPass() diff --git a/src/renderer/gl_engine/tvgGlRenderer.h b/src/renderer/gl_engine/tvgGlRenderer.h index f8a58531..818d3559 100644 --- a/src/renderer/gl_engine/tvgGlRenderer.h +++ b/src/renderer/gl_engine/tvgGlRenderer.h @@ -46,6 +46,7 @@ public: RT_MaskSub, RT_MaskIntersect, RT_MaskDifference, + RT_Stencil, RT_None, }; diff --git a/src/renderer/gl_engine/tvgGlShaderSrc.cpp b/src/renderer/gl_engine/tvgGlShaderSrc.cpp index bcec1fe5..74ebc62d 100644 --- a/src/renderer/gl_engine/tvgGlShaderSrc.cpp +++ b/src/renderer/gl_engine/tvgGlShaderSrc.cpp @@ -452,4 +452,20 @@ void main() { \n FragColor = vec4(maskColor.rgb, maskColor.a * (-da) * vOpacity);\n } \n } \n -); \ No newline at end of file +); + +const char* STENCIL_VERT_SHADER = TVG_COMPOSE_SHADER( + layout(location = 0) in vec3 aLocation; \n + layout(std140) uniform Matrix { \n + mat4 transform; \n + } uMatrix; \n + void main() \n + { \n + gl_Position = \n + uMatrix.transform * vec4(aLocation.xy, 0.0, 1.0); \n + }); + +const char* STENCIL_FRAG_SHADER = TVG_COMPOSE_SHADER( + out vec4 FragColor; \n + void main() { FragColor = vec4(0.0); } \n +); diff --git a/src/renderer/gl_engine/tvgGlShaderSrc.h b/src/renderer/gl_engine/tvgGlShaderSrc.h index c768210d..680e0ab6 100644 --- a/src/renderer/gl_engine/tvgGlShaderSrc.h +++ b/src/renderer/gl_engine/tvgGlShaderSrc.h @@ -39,5 +39,7 @@ extern const char* MASK_ADD_FRAG_SHADER; extern const char* MASK_SUB_FRAG_SHADER; extern const char* MASK_INTERSECT_FRAG_SHADER; extern const char* MASK_DIFF_FRAG_SHADER; +extern const char* STENCIL_VERT_SHADER; +extern const char* STENCIL_FRAG_SHADER; #endif /* _TVG_GL_SHADERSRC_H_ */ diff --git a/src/renderer/gl_engine/tvgGlTessellator.cpp b/src/renderer/gl_engine/tvgGlTessellator.cpp index 4831f3bf..8ecac8b1 100644 --- a/src/renderer/gl_engine/tvgGlTessellator.cpp +++ b/src/renderer/gl_engine/tvgGlTessellator.cpp @@ -2107,4 +2107,87 @@ void DashStroke::cubicTo(const GlPoint &cnt1, const GlPoint &cnt2, const GlPoint mCmds->push(PathCommand::CubicTo); } + +BWTessellator::BWTessellator(Array* points, Array* indices): mResPoints(points), mResIndices(indices) +{ +} + +void BWTessellator::tessellate(const RenderShape *rshape) +{ + auto cmds = rshape->path.cmds.data; + auto cmdCnt = rshape->path.cmds.count; + auto pts = rshape->path.pts.data; + auto ptsCnt = rshape->path.pts.count; + + if (ptsCnt <= 2) return; + + uint32_t firstIndex = 0; + uint32_t prevIndex = 0; + + mResPoints->reserve(ptsCnt * 2); + mResIndices->reserve((ptsCnt - 2) * 3); + + for (uint32_t i = 0; i < cmdCnt; i++) { + switch(cmds[i]) { + case PathCommand::MoveTo: { + firstIndex = pushVertex(pts->x, pts->y); + prevIndex = 0; + pts++; + } break; + case PathCommand::LineTo: { + if (prevIndex == 0) { + prevIndex = pushVertex(pts->x, pts->y); + pts++; + } else { + auto currIndex = pushVertex(pts->x, pts->y); + + pushTriangle(firstIndex, prevIndex, currIndex); + + prevIndex = currIndex; + pts++; + } + } break; + case PathCommand::CubicTo: { + Bezier curve{pts[-1], pts[0], pts[1], pts[2]}; + + auto stepCount = detail::_bezierCurveCount(curve); + + if (stepCount <= 1) stepCount = 2; + + float step = 1.f / stepCount; + + for (uint32_t s = 1; s < static_cast(stepCount); s++) { + auto pt = bezPointAt(curve, step * s); + auto currIndex = pushVertex(pt.x, pt.y); + + if (prevIndex == 0) { + prevIndex = currIndex; + continue; + } + + pushTriangle(firstIndex, prevIndex, currIndex); + prevIndex = currIndex; + } + + pts += 3; + } break; + case PathCommand::Close: + default: + break; + } + } +} + +uint32_t BWTessellator::pushVertex(float x, float y) +{ + return detail::_pushVertex(mResPoints, x, y, 1.f); +} + +void BWTessellator::pushTriangle(uint32_t a, uint32_t b, uint32_t c) +{ + mResIndices->push(a); + mResIndices->push(b); + mResIndices->push(c); +} + } // namespace tvg diff --git a/src/renderer/gl_engine/tvgGlTessellator.h b/src/renderer/gl_engine/tvgGlTessellator.h index 622a2c4d..a4dbf075 100644 --- a/src/renderer/gl_engine/tvgGlTessellator.h +++ b/src/renderer/gl_engine/tvgGlTessellator.h @@ -177,6 +177,23 @@ private: GlPoint mPtCur; }; +class BWTessellator +{ +public: + BWTessellator(Array* points, Array* indices); + ~BWTessellator() = default; + + void tessellate(const RenderShape *rshape); + +private: + uint32_t pushVertex(float x, float y); + void pushTriangle(uint32_t a, uint32_t b, uint32_t c); + +private: + Array* mResPoints; + Array* mResIndices; +}; + } // namespace tvg #endif /* _TVG_GL_TESSELLATOR_H_ */