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 0268e9e3d1
commit da45fa6608
8 changed files with 177 additions and 16 deletions

View file

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

View file

@ -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();

View file

@ -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));

View file

@ -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;
};

View file

@ -176,17 +176,22 @@ void GlComposeTask::run()
if (mClearBuffer) {
GL_CHECK(glClearColor(0, 0, 0, 0));
GL_CHECK(glClearStencil(0));
GL_CHECK(glClearDepthf(1.0));
GL_CHECK(glDepthMask(1));
GL_CHECK(glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT));
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();
}
@ -217,6 +222,7 @@ void GlBlitTask::run()
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));
@ -237,3 +243,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));
}

View file

@ -159,5 +159,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_ */

View file

@ -92,6 +92,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<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->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<GlShape*>(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<size_t>(sdata->updateFlag);
@ -276,14 +284,15 @@ bool GlRenderer::renderShape(RenderData data)
if (gradient) {
drawPrimitive(*sdata, gradient, RenderUpdateFlag::GradientStroke);
} else {
sdata->rshape->strokeFill(&r, &g, &b, &a);
if (a > 0)
if (sdata->rshape->strokeFill(&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<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;
@ -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<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
GlShape* sdata = static_cast<GlShape*>(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<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()
{
if (mRenderPassStack.empty()) return nullptr;

View file

@ -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<RenderData>& clips);
GlRenderPass* currentPass();