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)) {
|
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;
|
||||||
|
}
|
||||||
|
|
|
@ -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_ */
|
||||||
|
|
|
@ -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()
|
||||||
{
|
{
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -46,6 +46,7 @@ public:
|
||||||
RT_MaskSub,
|
RT_MaskSub,
|
||||||
RT_MaskIntersect,
|
RT_MaskIntersect,
|
||||||
RT_MaskDifference,
|
RT_MaskDifference,
|
||||||
|
RT_Stencil,
|
||||||
|
|
||||||
RT_None,
|
RT_None,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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
|
||||||
|
);
|
||||||
|
|
|
@ -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_ */
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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_ */
|
||||||
|
|
Loading…
Add table
Reference in a new issue