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
This commit is contained in:
RuiwenTang 2024-02-06 18:46:04 +08:00 committed by Hermet Park
parent a9d91e3620
commit 2855b9b339
10 changed files with 242 additions and 5 deletions

View file

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

View file

@ -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<uint32_t> fillIndex = {};
Array<uint32_t> strokeIndex = {};
float mTransform[16];
bool mStencilFill = false;
};
#endif /* _TVG_GL_GEOMETRY_H_ */

View file

@ -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<GlRenderTask*>&& tasks)
:GlRenderTask(program) ,mTargetFbo(target), mSelfFbo(selfFbo), mTasks()
{

View file

@ -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<GlBindingResource> 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:

View file

@ -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<GlProgram>(GlShader::gen(MASK_VERT_SHADER, MASK_SUB_FRAG_SHADER)));
mPrograms.push_back(make_unique<GlProgram>(GlShader::gen(MASK_VERT_SHADER, MASK_INTERSECT_FRAG_SHADER)));
mPrograms.push_back(make_unique<GlProgram>(GlShader::gen(MASK_VERT_SHADER, MASK_DIFF_FRAG_SHADER)));
// stencil Renderer
mPrograms.push_back(make_unique<GlProgram>(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<uint32_t>(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<uint32_t>(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()

View file

@ -46,6 +46,7 @@ public:
RT_MaskSub,
RT_MaskIntersect,
RT_MaskDifference,
RT_Stencil,
RT_None,
};

View file

@ -453,3 +453,19 @@ void main() { \n
} \n
} \n
);
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
);

View file

@ -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_ */

View file

@ -2107,4 +2107,87 @@ void DashStroke::cubicTo(const GlPoint &cnt1, const GlPoint &cnt2, const GlPoint
mCmds->push(PathCommand::CubicTo);
}
BWTessellator::BWTessellator(Array<float>* points, Array<uint32_t>* 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<uint32_t>(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

View file

@ -177,6 +177,23 @@ private:
GlPoint mPtCur;
};
class BWTessellator
{
public:
BWTessellator(Array<float>* points, Array<uint32_t>* 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<float>* mResPoints;
Array<uint32_t>* mResIndices;
};
} // namespace tvg
#endif /* _TVG_GL_TESSELLATOR_H_ */