diff --git a/src/renderer/gl_engine/tvgGlCommon.h b/src/renderer/gl_engine/tvgGlCommon.h index e9a92471..9ed9325d 100644 --- a/src/renderer/gl_engine/tvgGlCommon.h +++ b/src/renderer/gl_engine/tvgGlCommon.h @@ -70,6 +70,7 @@ struct GlShape ColorSpace texColorSpace = ColorSpace::ABGR8888; RenderUpdateFlag updateFlag = None; unique_ptr geometry; + Array clips; }; #define MAX_GRADIENT_STOPS 16 diff --git a/src/renderer/gl_engine/tvgGlGeometry.cpp b/src/renderer/gl_engine/tvgGlGeometry.cpp index 4d7986a1..d5168bb7 100644 --- a/src/renderer/gl_engine/tvgGlGeometry.cpp +++ b/src/renderer/gl_engine/tvgGlGeometry.cpp @@ -31,7 +31,7 @@ GlGeometry::~GlGeometry() bool GlGeometry::tesselate(const RenderShape& rshape, RenderUpdateFlag flag) { - if (flag & (RenderUpdateFlag::Color | RenderUpdateFlag::Gradient | RenderUpdateFlag::Transform)) { + if (flag & (RenderUpdateFlag::Color | RenderUpdateFlag::Gradient | RenderUpdateFlag::Transform | RenderUpdateFlag::Path)) { fillVertex.clear(); fillIndex.clear(); diff --git a/src/renderer/gl_engine/tvgGlRenderPass.cpp b/src/renderer/gl_engine/tvgGlRenderPass.cpp index 9c9c2d19..88c7b485 100644 --- a/src/renderer/gl_engine/tvgGlRenderPass.cpp +++ b/src/renderer/gl_engine/tvgGlRenderPass.cpp @@ -35,8 +35,8 @@ GlRenderTarget::~GlRenderTarget() if (mColorTex != 0) { GL_CHECK(glDeleteTextures(1, &mColorTex)); } - if (mStencilBuffer != 0) { - GL_CHECK(glDeleteRenderbuffers(1, &mStencilBuffer)); + if (mDepthStencilBuffer != 0) { + GL_CHECK(glDeleteRenderbuffers(1, &mDepthStencilBuffer)); } } @@ -52,16 +52,16 @@ void GlRenderTarget::init(GLint resolveId) GL_CHECK(glBindRenderbuffer(GL_RENDERBUFFER, mColorBuffer)); GL_CHECK(glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_RGBA8, mWidth, mHeight)); - GL_CHECK(glGenRenderbuffers(1, &mStencilBuffer)); + GL_CHECK(glGenRenderbuffers(1, &mDepthStencilBuffer)); - GL_CHECK(glBindRenderbuffer(GL_RENDERBUFFER, mStencilBuffer)); + GL_CHECK(glBindRenderbuffer(GL_RENDERBUFFER, mDepthStencilBuffer)); - GL_CHECK(glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_STENCIL_INDEX8, mWidth, mHeight)); + 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_STENCIL_ATTACHMENT, GL_RENDERBUFFER, mStencilBuffer)); + GL_CHECK(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, mDepthStencilBuffer)); // resolve target GL_CHECK(glGenTextures(1, &mColorTex)); diff --git a/src/renderer/gl_engine/tvgGlRenderPass.h b/src/renderer/gl_engine/tvgGlRenderPass.h index 241ad42c..004d5de4 100644 --- a/src/renderer/gl_engine/tvgGlRenderPass.h +++ b/src/renderer/gl_engine/tvgGlRenderPass.h @@ -51,7 +51,7 @@ private: uint32_t mHeight = 0; GLuint mFbo = 0; GLuint mColorBuffer = 0; - GLuint mStencilBuffer = 0; + GLuint mDepthStencilBuffer = 0; GLuint mResolveFbo = 0; GLuint mColorTex = 0; }; diff --git a/src/renderer/gl_engine/tvgGlRenderTask.cpp b/src/renderer/gl_engine/tvgGlRenderTask.cpp index 4233132b..836fcb8f 100644 --- a/src/renderer/gl_engine/tvgGlRenderTask.cpp +++ b/src/renderer/gl_engine/tvgGlRenderTask.cpp @@ -175,15 +175,21 @@ void GlComposeTask::run() // clear this fbo GL_CHECK(glClearColor(0, 0, 0, 0)); GL_CHECK(glClearStencil(0)); - GL_CHECK(glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)); + GL_CHECK(glClearDepthf(1.0)); + GL_CHECK(glDepthMask(1)); + + GL_CHECK(glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); + GL_CHECK(glDepthMask(0)); for(uint32_t i = 0; i < mTasks.count; i++) { mTasks[i]->run(); } - GLenum stencil_attachment = GL_STENCIL_ATTACHMENT; - GL_CHECK(glInvalidateFramebuffer(GL_FRAMEBUFFER, 1, &stencil_attachment)); + GLenum attachments[2] = {GL_STENCIL_ATTACHMENT, GL_DEPTH_ATTACHMENT }; + GL_CHECK(glInvalidateFramebuffer(GL_FRAMEBUFFER, 2, attachments)); + // reset scissor box + GL_CHECK(glScissor(0, 0, mFbo->getWidth(), mFbo->getHeight())); onResolve(); } @@ -212,6 +218,7 @@ void GlBlitTask::run() GL_CHECK(glClearColor(0, 0, 0, 0)); GL_CHECK(glClear(GL_COLOR_BUFFER_BIT)); + GL_CHECK(glDisable(GL_DEPTH_TEST)); // make sure the blending is correct GL_CHECK(glEnable(GL_BLEND)); GL_CHECK(glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA)); @@ -232,3 +239,43 @@ void GlDrawBlitTask::run() GlRenderTask::run(); } + +GlClipTask::GlClipTask(GlRenderTask* clip, GlRenderTask* mask) + :GlRenderTask(nullptr), mClipTask(clip), mMaskTask(mask) {} + +void GlClipTask::run() +{ + GL_CHECK(glEnable(GL_STENCIL_TEST)); + GL_CHECK(glDepthFunc(GL_ALWAYS)); + GL_CHECK(glColorMask(0, 0, 0, 0)); + // draw clip path as normal stencil mask + 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)); + + mClipTask->run(); + + + // draw clip mask + GL_CHECK(glDepthMask(1)); + GL_CHECK(glStencilFunc(GL_EQUAL, 0x0, 0xFF)); + GL_CHECK(glStencilOp(GL_REPLACE, GL_KEEP, GL_REPLACE)); + + mMaskTask->run(); + + GL_CHECK(glColorMask(1, 1, 1, 1)); + GL_CHECK(glDepthMask(0)); + GL_CHECK(glDepthFunc(GL_LESS)); + GL_CHECK(glDisable(GL_STENCIL_TEST)); +} + +void GlClipClearTask::run() +{ + GL_CHECK(glDisable(GL_SCISSOR_TEST)); + GL_CHECK(glDepthMask(1)); + GL_CHECK(glClear(GL_DEPTH_BUFFER_BIT)); + GL_CHECK(glDepthMask(0)); + GL_CHECK(glEnable(GL_SCISSOR_TEST)); +} diff --git a/src/renderer/gl_engine/tvgGlRenderTask.h b/src/renderer/gl_engine/tvgGlRenderTask.h index 8463dda6..2c7367c8 100644 --- a/src/renderer/gl_engine/tvgGlRenderTask.h +++ b/src/renderer/gl_engine/tvgGlRenderTask.h @@ -157,5 +157,27 @@ public: void run() override; }; +class GlClipTask : public GlRenderTask +{ +public: + GlClipTask(GlRenderTask* clip, GlRenderTask* mask); + ~GlClipTask() override = default; + + void run() override; + +private: + GlRenderTask* mClipTask; + GlRenderTask* mMaskTask; +}; + +class GlClipClearTask : public GlRenderTask +{ +public: + GlClipClearTask(): GlRenderTask(nullptr) {} + ~GlClipClearTask() override = default; + + void run() override; +}; + #endif /* _TVG_GL_RENDER_TASK_H_ */ diff --git a/src/renderer/gl_engine/tvgGlRenderer.cpp b/src/renderer/gl_engine/tvgGlRenderer.cpp index f3567e6b..853096eb 100644 --- a/src/renderer/gl_engine/tvgGlRenderer.cpp +++ b/src/renderer/gl_engine/tvgGlRenderer.cpp @@ -93,6 +93,8 @@ bool GlRenderer::sync() GL_CHECK(glEnable(GL_SCISSOR_TEST)); GL_CHECK(glCullFace(GL_FRONT_AND_BACK)); GL_CHECK(glFrontFace(GL_CCW)); + GL_CHECK(glEnable(GL_DEPTH_TEST)); + GL_CHECK(glDepthFunc(GL_LESS)); auto task = mRenderPassStack.front().endRenderPass(mPrograms[RT_Blit].get(), mTargetFboId); @@ -205,6 +207,8 @@ bool GlRenderer::renderImage(void* data) if ((sdata->updateFlag & RenderUpdateFlag::Image) == 0) return false; + if (!sdata->clips.empty()) drawClip(sdata->clips); + auto task = new GlRenderTask(mPrograms[RT_Image].get()); if (!sdata->geometry->draw(task, mGpuBuffer.get(), RenderUpdateFlag::Image)) return false; @@ -243,6 +247,8 @@ bool GlRenderer::renderImage(void* data) currentPass()->addRenderTask(task); + if (!sdata->clips.empty()) currentPass()->addRenderTask(new GlClipClearTask); + return true; } @@ -252,6 +258,8 @@ bool GlRenderer::renderShape(RenderData data) auto sdata = static_cast(data); if (!sdata) return false; + if (!sdata->clips.empty()) drawClip(sdata->clips); + uint8_t r = 0, g = 0, b = 0, a = 0; size_t flags = static_cast(sdata->updateFlag); @@ -276,14 +284,15 @@ bool GlRenderer::renderShape(RenderData data) if (gradient) { drawPrimitive(*sdata, gradient, RenderUpdateFlag::GradientStroke); } else { - sdata->rshape->strokeColor(&r, &g, &b, &a); - if (a > 0) + if (sdata->rshape->strokeColor(&r, &g, &b, &a) && a > 0) { drawPrimitive(*sdata, r, g, b, a, RenderUpdateFlag::Stroke); } } } + if (!sdata->clips.empty()) currentPass()->addRenderTask(new GlClipClearTask); + return true; } @@ -317,7 +326,7 @@ static GLuint _genTexture(Surface* image) return tex; } -RenderData GlRenderer::prepare(Surface* image, const RenderMesh* mesh, RenderData data, const RenderTransform* transform, TVG_UNUSED Array& clips, uint8_t opacity, RenderUpdateFlag flags) +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; @@ -347,6 +356,8 @@ RenderData GlRenderer::prepare(Surface* image, const RenderMesh* mesh, RenderDat sdata->geometry->tesselate(image, mesh, flags); + if (!clips.empty()) sdata->clips.push(clips); + return sdata; } @@ -358,8 +369,11 @@ RenderData GlRenderer::prepare(TVG_UNUSED const Array& scene, TVG_UN } -RenderData GlRenderer::prepare(const RenderShape& rshape, RenderData data, const RenderTransform* transform, Array& clips, uint8_t opacity, RenderUpdateFlag flags, TVG_UNUSED bool clipper) +RenderData GlRenderer::prepare(const RenderShape& rshape, RenderData data, const RenderTransform* transform, Array& clips, uint8_t opacity, RenderUpdateFlag flags, bool clipper) { + // If prepare for clip, only path is meaningful. + if (clipper) flags = RenderUpdateFlag::Path; + //prepare shape data GlShape* sdata = static_cast(data); if (!sdata) { @@ -396,10 +410,13 @@ RenderData GlRenderer::prepare(const RenderShape& rshape, RenderData data, const mViewport.h, }); - if (sdata->updateFlag & (RenderUpdateFlag::Color | RenderUpdateFlag::Stroke | RenderUpdateFlag::Gradient | RenderUpdateFlag::Transform) ) + if (sdata->updateFlag & (RenderUpdateFlag::Color | RenderUpdateFlag::Stroke | RenderUpdateFlag::Gradient | RenderUpdateFlag::Transform | RenderUpdateFlag::Path)) { if (!sdata->geometry->tesselate(rshape, sdata->updateFlag)) return sdata; } + + if (!clipper && !clips.empty()) sdata->clips.push(clips); + return sdata; } @@ -688,6 +705,80 @@ void GlRenderer::drawPrimitive(GlShape& sdata, const Fill* fill, RenderUpdateFla } } +void GlRenderer::drawClip(Array& clips) +{ + Array identityVertex(4 * 2); + float left = -1.f; + float top = 1.f; + float right = 1.f; + float bottom = -1.f; + + identityVertex.push(left); + identityVertex.push(top); + identityVertex.push(left); + identityVertex.push(bottom); + identityVertex.push(right); + identityVertex.push(top); + identityVertex.push(right); + identityVertex.push(bottom); + + Array indentityIndex(6); + indentityIndex.push(0); + indentityIndex.push(1); + indentityIndex.push(2); + indentityIndex.push(2); + indentityIndex.push(1); + indentityIndex.push(3); + + float mat4[16]; + memset(mat4, 0, sizeof(float) * 16); + mat4[0] = 1.f; + mat4[5] = 1.f; + mat4[10] = 1.f; + mat4[15] = 1.f; + + auto identityVertexOffset = mGpuBuffer->push(identityVertex.data, 8 * sizeof(float)); + auto identityIndexOffset = mGpuBuffer->push(indentityIndex.data, 6 * sizeof(uint32_t)); + auto mat4Offset = mGpuBuffer->push(mat4, 16 * sizeof(float), true); + + for (uint32_t i = 0; i < clips.count; ++i) { + auto sdata = static_cast(clips[i]); + + auto clipTask = new GlRenderTask(mPrograms[RT_Stencil].get()); + + sdata->geometry->draw(clipTask, mGpuBuffer.get(), RenderUpdateFlag::Path); + + auto matrix = sdata->geometry->getTransforMatrix(); + + uint32_t loc = clipTask->getProgram()->getUniformBlockIndex("Matrix"); + + uint32_t viewOffset = mGpuBuffer->push(matrix, 16 * sizeof(float), true); + + clipTask->addBindResource(GlBindingResource{ + 0, + loc, + mGpuBuffer->getBufferId(), + viewOffset, + 16 * sizeof(float), + }); + + auto maskTask = new GlRenderTask(mPrograms[RT_Stencil].get()); + + maskTask->addVertexLayout(GlVertexLayout{0, 2, 2 * sizeof(float), identityVertexOffset}); + maskTask->addBindResource(GlBindingResource{ + 0, + loc, + mGpuBuffer->getBufferId(), + mat4Offset, 16 * sizeof(float), + }); + + maskTask->setDrawRange(identityIndexOffset, 6); + maskTask->setViewport(RenderRegion{0, 0, static_cast(surface.w), static_cast(surface.h)}); + + currentPass()->addRenderTask(new GlClipTask(clipTask, maskTask)); + } +} + GlRenderPass* GlRenderer::currentPass() { if (mRenderPassStack.empty()) return nullptr; diff --git a/src/renderer/gl_engine/tvgGlRenderer.h b/src/renderer/gl_engine/tvgGlRenderer.h index 4c34ae04..853e082b 100644 --- a/src/renderer/gl_engine/tvgGlRenderer.h +++ b/src/renderer/gl_engine/tvgGlRenderer.h @@ -88,6 +88,7 @@ private: void initShaders(); void drawPrimitive(GlShape& sdata, uint8_t r, uint8_t g, uint8_t b, uint8_t a, RenderUpdateFlag flag); void drawPrimitive(GlShape& sdata, const Fill* fill, RenderUpdateFlag flag); + void drawClip(Array& clips); GlRenderPass* currentPass();