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)) { if (!tess.tessellate(&rshape, true)) {
fillVertex.clear(); fillVertex.clear();
fillIndex.clear(); fillIndex.clear();
BWTessellator bwTess{&fillVertex, &fillIndex};
bwTess.tessellate(&rshape);
mStencilFill = true;
} }
} }
@ -212,3 +218,12 @@ float* GlGeometry::getTransforMatrix()
{ {
return mTransform; 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 updateTransform(const RenderTransform* transform, float w, float h);
void setViewport(const RenderRegion& viewport); void setViewport(const RenderRegion& viewport);
float* getTransforMatrix(); float* getTransforMatrix();
bool needStencilCover(RenderUpdateFlag flag);
private: private:
RenderRegion viewport = {}; RenderRegion viewport = {};
@ -203,6 +204,8 @@ private:
Array<uint32_t> fillIndex = {}; Array<uint32_t> fillIndex = {};
Array<uint32_t> strokeIndex = {}; Array<uint32_t> strokeIndex = {};
float mTransform[16]; float mTransform[16];
bool mStencilFill = false;
}; };
#endif /* _TVG_GL_GEOMETRY_H_ */ #endif /* _TVG_GL_GEOMETRY_H_ */

View file

@ -27,6 +27,14 @@
/* External Class Implementation */ /* 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() void GlRenderTask::run()
{ {
// bind shader // bind shader
@ -90,6 +98,39 @@ void GlRenderTask::setViewport(const RenderRegion &viewport)
mViewport = 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) GlComposeTask::GlComposeTask(GlProgram* program, GLuint target, GLuint selfFbo, Array<GlRenderTask*>&& tasks)
:GlRenderTask(program) ,mTargetFbo(target), mSelfFbo(selfFbo), mTasks() :GlRenderTask(program) ,mTargetFbo(target), mSelfFbo(selfFbo), mTasks()
{ {

View file

@ -76,6 +76,8 @@ class GlRenderTask
{ {
public: public:
GlRenderTask(GlProgram* program): mProgram(program) {} GlRenderTask(GlProgram* program): mProgram(program) {}
GlRenderTask(GlProgram* program, GlRenderTask* other);
virtual ~GlRenderTask() = default; virtual ~GlRenderTask() = default;
virtual void run(); virtual void run();
@ -95,6 +97,19 @@ private:
Array<GlBindingResource> mBindingResources = {}; 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 class GlComposeTask : public GlRenderTask
{ {
public: 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(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_BLEND));
GL_CHECK(glEnable(GL_SCISSOR_TEST)); GL_CHECK(glEnable(GL_SCISSOR_TEST));
GL_CHECK(glCullFace(GL_FRONT_AND_BACK));
GL_CHECK(glFrontFace(GL_CCW));
mGpuBuffer->bind(); 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_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_INTERSECT_FRAG_SHADER)));
mPrograms.push_back(make_unique<GlProgram>(GlShader::gen(MASK_VERT_SHADER, MASK_DIFF_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; 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); a = MULTIPLY(a, sdata.opacity);
// matrix buffer // 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(); auto matrix = sdata.geometry->getTransforMatrix();
uint32_t loc = task->getProgram()->getUniformBlockIndex("Matrix"); uint32_t loc = task->getProgram()->getUniformBlockIndex("Matrix");
uint32_t viewOffset = mGpuBuffer->push(matrix, 16 * sizeof(float), true);
task->addBindResource(GlBindingResource{ task->addBindResource(GlBindingResource{
0, 0,
loc, loc,
mGpuBuffer->getBufferId(), mGpuBuffer->getBufferId(),
mGpuBuffer->push(matrix, 16 * sizeof(float), true), viewOffset,
16 * sizeof(float), 16 * sizeof(float),
}); });
if (stencilTask) {
stencilTask->addBindResource(GlBindingResource{
0,
static_cast<uint32_t>(stencilTask->getProgram()->getUniformBlockIndex("Matrix")),
mGpuBuffer->getBufferId(),
viewOffset,
16 * sizeof(float),
});
}
} }
// color // 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; 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 // matrix buffer
{ {
auto matrix = sdata.geometry->getTransforMatrix(); auto matrix = sdata.geometry->getTransforMatrix();
uint32_t loc = task->getProgram()->getUniformBlockIndex("Matrix"); uint32_t loc = task->getProgram()->getUniformBlockIndex("Matrix");
uint32_t viewOffset = mGpuBuffer->push(matrix, 16 * sizeof(float), true);
task->addBindResource(GlBindingResource{ task->addBindResource(GlBindingResource{
0, 0,
loc, loc,
mGpuBuffer->getBufferId(), mGpuBuffer->getBufferId(),
mGpuBuffer->push(matrix, 16 * sizeof(float), true), viewOffset,
16 * sizeof(float), 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 // gradient block
@ -630,7 +670,11 @@ void GlRenderer::drawPrimitive(GlShape& sdata, const Fill* fill, RenderUpdateFla
task->addBindResource(gradientBinding); task->addBindResource(gradientBinding);
} }
currentPass()->addRenderTask(task); if (stencilTask) {
currentPass()->addRenderTask(new GlStencilCoverTask(stencilTask, task));
} else {
currentPass()->addRenderTask(task);
}
} }
GlRenderPass* GlRenderer::currentPass() GlRenderPass* GlRenderer::currentPass()

View file

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

View file

@ -452,4 +452,20 @@ void main() { \n
FragColor = vec4(maskColor.rgb, maskColor.a * (-da) * vOpacity);\n FragColor = vec4(maskColor.rgb, maskColor.a * (-da) * vOpacity);\n
} \n } \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_SUB_FRAG_SHADER;
extern const char* MASK_INTERSECT_FRAG_SHADER; extern const char* MASK_INTERSECT_FRAG_SHADER;
extern const char* MASK_DIFF_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_ */ #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); 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 } // namespace tvg

View file

@ -177,6 +177,23 @@ private:
GlPoint mPtCur; 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 } // namespace tvg
#endif /* _TVG_GL_TESSELLATOR_H_ */ #endif /* _TVG_GL_TESSELLATOR_H_ */