mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-14 12:04:29 +00:00
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:
parent
a9d91e3620
commit
2855b9b339
10 changed files with 242 additions and 5 deletions
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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_ */
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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,8 +538,12 @@ void GlRenderer::drawPrimitive(GlShape& sdata, uint8_t r, uint8_t g, uint8_t b,
|
|||
});
|
||||
}
|
||||
|
||||
if (stencilTask) {
|
||||
currentPass()->addRenderTask(new GlStencilCoverTask(stencilTask, task));
|
||||
} else {
|
||||
currentPass()->addRenderTask(task);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GlRenderer::drawPrimitive(GlShape& sdata, const Fill* fill, RenderUpdateFlag flag)
|
||||
|
@ -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,8 +670,12 @@ void GlRenderer::drawPrimitive(GlShape& sdata, const Fill* fill, RenderUpdateFla
|
|||
task->addBindResource(gradientBinding);
|
||||
}
|
||||
|
||||
if (stencilTask) {
|
||||
currentPass()->addRenderTask(new GlStencilCoverTask(stencilTask, task));
|
||||
} else {
|
||||
currentPass()->addRenderTask(task);
|
||||
}
|
||||
}
|
||||
|
||||
GlRenderPass* GlRenderer::currentPass()
|
||||
{
|
||||
|
|
|
@ -46,6 +46,7 @@ public:
|
|||
RT_MaskSub,
|
||||
RT_MaskIntersect,
|
||||
RT_MaskDifference,
|
||||
RT_Stencil,
|
||||
|
||||
RT_None,
|
||||
};
|
||||
|
|
|
@ -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
|
||||
);
|
||||
|
|
|
@ -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_ */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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_ */
|
||||
|
|
Loading…
Add table
Reference in a new issue