mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-10 14:41:50 +00:00
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:
parent
0268e9e3d1
commit
da45fa6608
8 changed files with 177 additions and 16 deletions
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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_ */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue