gl_engine: using depth buffer to support path clip

* Append depth buffer attachment in tvgGlRenderPass
* using depth test if Shape has path clip
This commit is contained in:
RuiwenTang 2024-04-11 17:50:43 +08:00 committed by Hermet Park
parent 0c12e662e4
commit 216d958e97
8 changed files with 178 additions and 16 deletions

View file

@ -70,6 +70,7 @@ struct GlShape
ColorSpace texColorSpace = ColorSpace::ABGR8888; ColorSpace texColorSpace = ColorSpace::ABGR8888;
RenderUpdateFlag updateFlag = None; RenderUpdateFlag updateFlag = None;
unique_ptr<GlGeometry> geometry; unique_ptr<GlGeometry> geometry;
Array<RenderData> clips;
}; };
#define MAX_GRADIENT_STOPS 16 #define MAX_GRADIENT_STOPS 16

View file

@ -31,7 +31,7 @@ GlGeometry::~GlGeometry()
bool GlGeometry::tesselate(const RenderShape& rshape, RenderUpdateFlag flag) 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(); fillVertex.clear();
fillIndex.clear(); fillIndex.clear();

View file

@ -35,8 +35,8 @@ GlRenderTarget::~GlRenderTarget()
if (mColorTex != 0) { if (mColorTex != 0) {
GL_CHECK(glDeleteTextures(1, &mColorTex)); GL_CHECK(glDeleteTextures(1, &mColorTex));
} }
if (mStencilBuffer != 0) { if (mDepthStencilBuffer != 0) {
GL_CHECK(glDeleteRenderbuffers(1, &mStencilBuffer)); GL_CHECK(glDeleteRenderbuffers(1, &mDepthStencilBuffer));
} }
} }
@ -52,16 +52,16 @@ void GlRenderTarget::init(GLint resolveId)
GL_CHECK(glBindRenderbuffer(GL_RENDERBUFFER, mColorBuffer)); GL_CHECK(glBindRenderbuffer(GL_RENDERBUFFER, mColorBuffer));
GL_CHECK(glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_RGBA8, mWidth, mHeight)); 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(glBindRenderbuffer(GL_RENDERBUFFER, 0));
GL_CHECK(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, mColorBuffer)); 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 // resolve target
GL_CHECK(glGenTextures(1, &mColorTex)); GL_CHECK(glGenTextures(1, &mColorTex));

View file

@ -51,7 +51,7 @@ private:
uint32_t mHeight = 0; uint32_t mHeight = 0;
GLuint mFbo = 0; GLuint mFbo = 0;
GLuint mColorBuffer = 0; GLuint mColorBuffer = 0;
GLuint mStencilBuffer = 0; GLuint mDepthStencilBuffer = 0;
GLuint mResolveFbo = 0; GLuint mResolveFbo = 0;
GLuint mColorTex = 0; GLuint mColorTex = 0;
}; };

View file

@ -175,15 +175,21 @@ void GlComposeTask::run()
// clear this fbo // clear this fbo
GL_CHECK(glClearColor(0, 0, 0, 0)); GL_CHECK(glClearColor(0, 0, 0, 0));
GL_CHECK(glClearStencil(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++) { for(uint32_t i = 0; i < mTasks.count; i++) {
mTasks[i]->run(); mTasks[i]->run();
} }
GLenum stencil_attachment = GL_STENCIL_ATTACHMENT; GLenum attachments[2] = {GL_STENCIL_ATTACHMENT, GL_DEPTH_ATTACHMENT };
GL_CHECK(glInvalidateFramebuffer(GL_FRAMEBUFFER, 1, &stencil_attachment)); GL_CHECK(glInvalidateFramebuffer(GL_FRAMEBUFFER, 2, attachments));
// reset scissor box
GL_CHECK(glScissor(0, 0, mFbo->getWidth(), mFbo->getHeight()));
onResolve(); onResolve();
} }
@ -212,6 +218,7 @@ void GlBlitTask::run()
GL_CHECK(glClearColor(0, 0, 0, 0)); GL_CHECK(glClearColor(0, 0, 0, 0));
GL_CHECK(glClear(GL_COLOR_BUFFER_BIT)); GL_CHECK(glClear(GL_COLOR_BUFFER_BIT));
GL_CHECK(glDisable(GL_DEPTH_TEST));
// make sure the blending is correct // make sure the blending is correct
GL_CHECK(glEnable(GL_BLEND)); GL_CHECK(glEnable(GL_BLEND));
GL_CHECK(glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA)); GL_CHECK(glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA));
@ -232,3 +239,43 @@ void GlDrawBlitTask::run()
GlRenderTask::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));
}

View file

@ -157,5 +157,27 @@ public:
void run() override; 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_ */ #endif /* _TVG_GL_RENDER_TASK_H_ */

View file

@ -93,6 +93,8 @@ bool GlRenderer::sync()
GL_CHECK(glEnable(GL_SCISSOR_TEST)); GL_CHECK(glEnable(GL_SCISSOR_TEST));
GL_CHECK(glCullFace(GL_FRONT_AND_BACK)); GL_CHECK(glCullFace(GL_FRONT_AND_BACK));
GL_CHECK(glFrontFace(GL_CCW)); GL_CHECK(glFrontFace(GL_CCW));
GL_CHECK(glEnable(GL_DEPTH_TEST));
GL_CHECK(glDepthFunc(GL_LESS));
auto task = mRenderPassStack.front().endRenderPass<GlBlitTask>(mPrograms[RT_Blit].get(), mTargetFboId); auto task = mRenderPassStack.front().endRenderPass<GlBlitTask>(mPrograms[RT_Blit].get(), mTargetFboId);
@ -205,6 +207,8 @@ bool GlRenderer::renderImage(void* data)
if ((sdata->updateFlag & RenderUpdateFlag::Image) == 0) return false; if ((sdata->updateFlag & RenderUpdateFlag::Image) == 0) return false;
if (!sdata->clips.empty()) drawClip(sdata->clips);
auto task = new GlRenderTask(mPrograms[RT_Image].get()); auto task = new GlRenderTask(mPrograms[RT_Image].get());
if (!sdata->geometry->draw(task, mGpuBuffer.get(), RenderUpdateFlag::Image)) return false; if (!sdata->geometry->draw(task, mGpuBuffer.get(), RenderUpdateFlag::Image)) return false;
@ -243,6 +247,8 @@ bool GlRenderer::renderImage(void* data)
currentPass()->addRenderTask(task); currentPass()->addRenderTask(task);
if (!sdata->clips.empty()) currentPass()->addRenderTask(new GlClipClearTask);
return true; return true;
} }
@ -252,6 +258,8 @@ bool GlRenderer::renderShape(RenderData data)
auto sdata = static_cast<GlShape*>(data); auto sdata = static_cast<GlShape*>(data);
if (!sdata) return false; if (!sdata) return false;
if (!sdata->clips.empty()) drawClip(sdata->clips);
uint8_t r = 0, g = 0, b = 0, a = 0; uint8_t r = 0, g = 0, b = 0, a = 0;
size_t flags = static_cast<size_t>(sdata->updateFlag); size_t flags = static_cast<size_t>(sdata->updateFlag);
@ -276,14 +284,15 @@ bool GlRenderer::renderShape(RenderData data)
if (gradient) { if (gradient) {
drawPrimitive(*sdata, gradient, RenderUpdateFlag::GradientStroke); drawPrimitive(*sdata, gradient, RenderUpdateFlag::GradientStroke);
} else { } else {
sdata->rshape->strokeColor(&r, &g, &b, &a); if (sdata->rshape->strokeColor(&r, &g, &b, &a) && a > 0)
if (a > 0)
{ {
drawPrimitive(*sdata, r, g, b, a, RenderUpdateFlag::Stroke); drawPrimitive(*sdata, r, g, b, a, RenderUpdateFlag::Stroke);
} }
} }
} }
if (!sdata->clips.empty()) currentPass()->addRenderTask(new GlClipClearTask);
return true; return true;
} }
@ -317,7 +326,7 @@ static GLuint _genTexture(Surface* image)
return tex; return tex;
} }
RenderData GlRenderer::prepare(Surface* image, const RenderMesh* mesh, RenderData data, const RenderTransform* transform, TVG_UNUSED Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags) RenderData GlRenderer::prepare(Surface* image, const RenderMesh* mesh, RenderData data, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags)
{ {
if (flags == RenderUpdateFlag::None) return nullptr; 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); sdata->geometry->tesselate(image, mesh, flags);
if (!clips.empty()) sdata->clips.push(clips);
return sdata; return sdata;
} }
@ -358,8 +369,11 @@ RenderData GlRenderer::prepare(TVG_UNUSED const Array<RenderData>& scene, TVG_UN
} }
RenderData GlRenderer::prepare(const RenderShape& rshape, RenderData data, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags, TVG_UNUSED bool clipper) RenderData GlRenderer::prepare(const RenderShape& rshape, RenderData data, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags, bool clipper)
{ {
// If prepare for clip, only path is meaningful.
if (clipper) flags = RenderUpdateFlag::Path;
//prepare shape data //prepare shape data
GlShape* sdata = static_cast<GlShape*>(data); GlShape* sdata = static_cast<GlShape*>(data);
if (!sdata) { if (!sdata) {
@ -396,10 +410,13 @@ RenderData GlRenderer::prepare(const RenderShape& rshape, RenderData data, const
mViewport.h, 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 (!sdata->geometry->tesselate(rshape, sdata->updateFlag)) return sdata;
} }
if (!clipper && !clips.empty()) sdata->clips.push(clips);
return sdata; return sdata;
} }
@ -688,6 +705,80 @@ void GlRenderer::drawPrimitive(GlShape& sdata, const Fill* fill, RenderUpdateFla
} }
} }
void GlRenderer::drawClip(Array<RenderData>& clips)
{
Array<float> 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<uint32_t> 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<GlShape*>(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<int32_t>(surface.w), static_cast<int32_t>(surface.h)});
currentPass()->addRenderTask(new GlClipTask(clipTask, maskTask));
}
}
GlRenderPass* GlRenderer::currentPass() GlRenderPass* GlRenderer::currentPass()
{ {
if (mRenderPassStack.empty()) return nullptr; if (mRenderPassStack.empty()) return nullptr;

View file

@ -88,6 +88,7 @@ private:
void initShaders(); void initShaders();
void drawPrimitive(GlShape& sdata, uint8_t r, uint8_t g, uint8_t b, uint8_t a, RenderUpdateFlag flag); 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 drawPrimitive(GlShape& sdata, const Fill* fill, RenderUpdateFlag flag);
void drawClip(Array<RenderData>& clips);
GlRenderPass* currentPass(); GlRenderPass* currentPass();