gl_engine: optimize tessellation performance

* restrict the scissor box of composite task
* do not tessellate stroke or fill geometry if there is no Fill or
  Stroke color
* use actually transformed curve to calculate polyline count when doing
  curve flatten
This commit is contained in:
RuiwenTang 2024-05-01 17:25:34 +08:00 committed by Sergii Liebodkin
parent b1912e2ef9
commit eb6067f87b
7 changed files with 187 additions and 27 deletions

View file

@ -93,5 +93,11 @@ struct GlRadialGradientBlock
alignas(16) float stopColors[4 * MAX_GRADIENT_STOPS] = {};
};
struct GlCompositor : public Compositor
{
RenderRegion bbox = {};
GlCompositor(const RenderRegion& box) : bbox(box) {}
};
#endif /* _TVG_GL_COMMON_H_ */

View file

@ -38,17 +38,21 @@ bool GlGeometry::tesselate(const RenderShape& rshape, RenderUpdateFlag flag)
BWTessellator bwTess{&fillVertex, &fillIndex};
bwTess.tessellate(&rshape);
bwTess.tessellate(&rshape, mMatrix);
mFillRule = rshape.rule;
mBounds = bwTess.bounds();
}
if (flag & (RenderUpdateFlag::Stroke | RenderUpdateFlag::Transform)) {
strokeVertex.clear();
strokeIndex.clear();
Stroker stroke{&strokeVertex, &strokeIndex};
Stroker stroke{&strokeVertex, &strokeIndex, mMatrix};
stroke.stroke(&rshape);
mBounds = stroke.bounds();
}
return true;
@ -90,6 +94,32 @@ bool GlGeometry::tesselate(const Surface* image, const RenderMesh* mesh, RenderU
index += 3;
}
float left = mesh->triangles[0].vertex[0].pt.x;
float top = mesh->triangles[0].vertex[0].pt.y;
float right = mesh->triangles[0].vertex[0].pt.x;
float bottom = mesh->triangles[0].vertex[0].pt.y;
for (uint32_t i = 0; i < mesh->triangleCnt; i++) {
left = min(left, mesh->triangles[i].vertex[0].pt.x);
left = min(left, mesh->triangles[i].vertex[1].pt.x);
left = min(left, mesh->triangles[i].vertex[2].pt.x);
top = min(top, mesh->triangles[i].vertex[0].pt.y);
top = min(top, mesh->triangles[i].vertex[1].pt.y);
top = min(top, mesh->triangles[i].vertex[2].pt.y);
right = max(right, mesh->triangles[i].vertex[0].pt.x);
right = max(right, mesh->triangles[i].vertex[1].pt.x);
right = max(right, mesh->triangles[i].vertex[2].pt.x);
bottom = max(bottom, mesh->triangles[i].vertex[0].pt.y);
bottom = max(bottom, mesh->triangles[i].vertex[1].pt.y);
bottom = max(bottom, mesh->triangles[i].vertex[2].pt.y);
}
mBounds.x = static_cast<int32_t>(left);
mBounds.y = static_cast<int32_t>(top);
mBounds.w = static_cast<int32_t>(right - left);
mBounds.h = static_cast<int32_t>(bottom - top);
} else {
fillVertex.reserve(5 * 4);
fillIndex.reserve(6);
@ -131,6 +161,11 @@ bool GlGeometry::tesselate(const Surface* image, const RenderMesh* mesh, RenderU
fillIndex.push(2);
fillIndex.push(1);
fillIndex.push(3);
mBounds.x = 0;
mBounds.y = 0;
mBounds.w = image->w;
mBounds.h = image->h;
}
}
@ -186,12 +221,15 @@ void GlGeometry::updateTransform(const RenderTransform* transform, float w, floa
float modelMatrix[16];
if (transform) {
GET_MATRIX44(transform->m, modelMatrix);
mMatrix = transform->m;
} else {
memset(modelMatrix, 0, 16 * sizeof(float));
modelMatrix[0] = 1.f;
modelMatrix[5] = 1.f;
modelMatrix[10] = 1.f;
modelMatrix[15] = 1.f;
mMatrix = Matrix{1, 0, 0, 0, 1, 0, 0, 0, 1};
}
MVP_MATRIX();
@ -219,3 +257,37 @@ GlStencilMode GlGeometry::getStencilMode(RenderUpdateFlag flag)
return GlStencilMode::None;
}
RenderRegion GlGeometry::getBounds() const
{
if (mathIdentity(&mMatrix)) {
return mBounds;
} else {
Point lt{static_cast<float>(mBounds.x), static_cast<float>(mBounds.y)};
Point lb{static_cast<float>(mBounds.x), static_cast<float>(mBounds.y + mBounds.h)};
Point rt{static_cast<float>(mBounds.x + mBounds.w), static_cast<float>(mBounds.y)};
Point rb{static_cast<float>(mBounds.x + mBounds.w), static_cast<float>(mBounds.y + mBounds.h)};
mathMultiply(&lt, &mMatrix);
mathMultiply(&lb, &mMatrix);
mathMultiply(&rt, &mMatrix);
mathMultiply(&rb, &mMatrix);
float left = min(min(lt.x, lb.x), min(rt.x, rb.x));
float top = min(min(lt.y, lb.y), min(rt.y, rb.y));
float right = max(max(lt.x, lb.x), max(rt.x, rb.x));
float bottom = max(max(lt.y, lb.y), max(rt.y, rb.y));
auto bounds = RenderRegion {
static_cast<int32_t>(left),
static_cast<int32_t>(top),
static_cast<int32_t>(right - left),
static_cast<int32_t>(bottom - top),
};
if (bounds.x < 0 || bounds.y < 0 || bounds.w < 0 || bounds.h < 0) {
return mBounds;
} else {
return bounds;
}
}
}

View file

@ -196,6 +196,7 @@ public:
void setViewport(const RenderRegion& viewport);
float* getTransforMatrix();
GlStencilMode getStencilMode(RenderUpdateFlag flag);
RenderRegion getBounds() const;
private:
RenderRegion viewport = {};
@ -204,8 +205,10 @@ private:
Array<uint32_t> fillIndex = {};
Array<uint32_t> strokeIndex = {};
float mTransform[16];
Matrix mMatrix = {};
FillRule mFillRule = FillRule::Winding;
RenderRegion mBounds = {};
};
#endif /* _TVG_GL_GEOMETRY_H_ */

View file

@ -133,9 +133,10 @@ bool GlRenderer::sync()
}
RenderRegion GlRenderer::region(TVG_UNUSED RenderData data)
RenderRegion GlRenderer::region(RenderData data)
{
return {0, 0, static_cast<int32_t>(surface.w), static_cast<int32_t>(surface.h)};
auto shape = reinterpret_cast<GlShape*>(data);
return shape->geometry->getBounds();
}
@ -158,9 +159,9 @@ bool GlRenderer::postRender()
}
Compositor* GlRenderer::target(TVG_UNUSED const RenderRegion& region, TVG_UNUSED ColorSpace cs)
Compositor* GlRenderer::target(const RenderRegion& region, TVG_UNUSED ColorSpace cs)
{
mComposeStack.emplace_back(make_unique<tvg::Compositor>());
mComposeStack.emplace_back(make_unique<GlCompositor>(region));
return mComposeStack.back().get();
}
@ -209,6 +210,9 @@ ColorSpace GlRenderer::colorSpace()
bool GlRenderer::blend(TVG_UNUSED BlendMethod method)
{
if (method != BlendMethod::Normal) {
return true;
}
//TODO:
return false;
}
@ -273,6 +277,8 @@ bool GlRenderer::renderShape(RenderData data)
auto sdata = static_cast<GlShape*>(data);
if (!sdata) return false;
if (sdata->updateFlag == RenderUpdateFlag::None) return false;
if (!sdata->clips.empty()) drawClip(sdata->clips);
uint8_t r = 0, g = 0, b = 0, a = 0;
@ -351,7 +357,7 @@ RenderData GlRenderer::prepare(Surface* image, const RenderMesh* mesh, RenderDat
sdata->viewWd = static_cast<float>(surface.w);
sdata->viewHt = static_cast<float>(surface.h);
sdata->updateFlag = flags;
sdata->updateFlag = RenderUpdateFlag::Image;
if (sdata->texId == 0) {
sdata->texId = _genTexture(image);
@ -398,9 +404,7 @@ RenderData GlRenderer::prepare(const RenderShape& rshape, RenderData data, const
sdata->viewWd = static_cast<float>(surface.w);
sdata->viewHt = static_cast<float>(surface.h);
sdata->updateFlag = flags;
if (sdata->updateFlag == RenderUpdateFlag::None) return sdata;
sdata->updateFlag = RenderUpdateFlag::None;
sdata->geometry = make_unique<GlGeometry>();
sdata->opacity = opacity;
@ -417,6 +421,17 @@ RenderData GlRenderer::prepare(const RenderShape& rshape, RenderData data, const
return sdata;
}
if (clipper) {
sdata->updateFlag = RenderUpdateFlag::Path;
} else {
if (alphaF) sdata->updateFlag = static_cast<RenderUpdateFlag>(RenderUpdateFlag::Color | sdata->updateFlag);
if (rshape.fill) sdata->updateFlag = static_cast<RenderUpdateFlag>(RenderUpdateFlag::Gradient | sdata->updateFlag);
if (alphaS) sdata->updateFlag = static_cast<RenderUpdateFlag>(RenderUpdateFlag::Stroke | sdata->updateFlag);
if (rshape.strokeFill()) sdata->updateFlag = static_cast<RenderUpdateFlag>(RenderUpdateFlag::GradientStroke | sdata->updateFlag);
}
if (sdata->updateFlag == RenderUpdateFlag::None) return sdata;
sdata->geometry->updateTransform(transform, sdata->viewWd, sdata->viewHt);
sdata->geometry->setViewport(RenderRegion{
mViewport.x,
@ -835,7 +850,7 @@ GlRenderPass* GlRenderer::currentPass()
void GlRenderer::prepareBlitTask(GlBlitTask* task)
{
prepareCmpTask(task);
prepareCmpTask(task, mViewport);
{
uint32_t loc = task->getProgram()->getUniformLocation("uSrcTexture");
@ -843,7 +858,7 @@ void GlRenderer::prepareBlitTask(GlBlitTask* task)
}
}
void GlRenderer::prepareCmpTask(GlRenderTask* task)
void GlRenderer::prepareCmpTask(GlRenderTask* task, const RenderRegion& vp)
{
// we use 1:1 blit mapping since compositor fbo is same size as root fbo
Array<float> vertices(4 * 4);
@ -891,15 +906,16 @@ void GlRenderer::prepareCmpTask(GlRenderTask* task)
task->setDrawRange(indexOffset, indices.count);
task->setViewport(RenderRegion{
mViewport.x,
static_cast<int32_t>((surface.h - mViewport.y - mViewport.h)),
mViewport.w,
mViewport.h,
vp.x,
static_cast<int32_t>((surface.h - vp.y - vp.h)),
vp.w,
vp.h,
});
}
void GlRenderer::endRenderPass(Compositor* cmp)
{
auto gl_cmp = static_cast<GlCompositor*>(cmp);
if (cmp->method != CompositeMethod::None) {
auto self_pass = std::move(mRenderPassStack.back());
mRenderPassStack.pop_back();
@ -949,7 +965,7 @@ void GlRenderer::endRenderPass(Compositor* cmp)
auto compose_task = self_pass.endRenderPass<GlDrawBlitTask>(program, currentPass()->getFboId());
prepareCmpTask(compose_task);
prepareCmpTask(compose_task, gl_cmp->bbox);
{
uint32_t loc = program->getUniformLocation("uSrcTexture");
@ -971,7 +987,7 @@ void GlRenderer::endRenderPass(Compositor* cmp)
auto task = renderPass.endRenderPass<GlDrawBlitTask>(
mPrograms[RT_Image].get(), currentPass()->getFboId());
prepareCmpTask(task);
prepareCmpTask(task, gl_cmp->bbox);
// matrix buffer
{

View file

@ -93,7 +93,7 @@ private:
GlRenderPass* currentPass();
void prepareBlitTask(GlBlitTask* task);
void prepareCmpTask(GlRenderTask* task);
void prepareCmpTask(GlRenderTask* task, const RenderRegion& vp);
void endRenderPass(Compositor* cmp);
GLint mTargetFboId = 0;
@ -105,7 +105,7 @@ private:
unique_ptr<GlRenderTarget> mRootTarget = {};
Array<GlRenderTarget*> mComposePool = {};
vector<GlRenderPass> mRenderPassStack = {};
vector<unique_ptr<Compositor>> mComposeStack = {};
vector<unique_ptr<GlCompositor>> mComposeStack = {};
bool mClearBuffer = true;
};

View file

@ -1638,7 +1638,7 @@ void Tessellator::emitTriangle(detail::Vertex *p1, detail::Vertex *p2, detail::V
}
Stroker::Stroker(Array<float> *points, Array<uint32_t> *indices) : mResGlPoints(points), mResIndices(indices)
Stroker::Stroker(Array<float> *points, Array<uint32_t> *indices, const Matrix& matrix) : mResGlPoints(points), mResIndices(indices), mMatrix(matrix)
{
}
@ -1665,6 +1665,16 @@ void Stroker::stroke(const RenderShape *rshape)
}
}
RenderRegion Stroker::bounds() const
{
return RenderRegion {
static_cast<int32_t>(mLeftTop.x),
static_cast<int32_t>(mLeftTop.y),
static_cast<int32_t>(mRightBottom.x - mLeftTop.x),
static_cast<int32_t>(mRightBottom.y - mLeftTop.y),
};
}
void Stroker::doStroke(const PathCommand *cmds, uint32_t cmd_count, const Point *pts, uint32_t pts_count)
{
mResGlPoints->reserve(pts_count * 4 + 16);
@ -1773,6 +1783,16 @@ void Stroker::strokeLineTo(const GlPoint &curr)
mStrokeState.prevPtDir = dir;
mStrokeState.prevPt = curr;
}
if (ia == 0) {
mRightBottom.x = mLeftTop.x = curr.x;
mRightBottom.y = mLeftTop.y = curr.y;
} else {
mLeftTop.x = min(mLeftTop.x, curr.x);
mLeftTop.y = min(mLeftTop.y, curr.y);
mRightBottom.x = max(mRightBottom.x, curr.x);
mRightBottom.y = max(mRightBottom.y , curr.y);
}
}
void Stroker::strokeCubicTo(const GlPoint &cnt1, const GlPoint &cnt2, const GlPoint &end)
@ -1783,7 +1803,13 @@ void Stroker::strokeCubicTo(const GlPoint &cnt1, const GlPoint &cnt2, const GlPo
curve.ctrl2 = Point{cnt2.x, cnt2.y};
curve.end = Point{end.x, end.y};
auto count = detail::_bezierCurveCount(curve);
Bezier relCurve {curve.start, curve.ctrl1, curve.ctrl2, curve.end};
mathMultiply(&relCurve.start, &mMatrix);
mathMultiply(&relCurve.ctrl1, &mMatrix);
mathMultiply(&relCurve.ctrl2, &mMatrix);
mathMultiply(&relCurve.end, &mMatrix);
auto count = detail::_bezierCurveCount(relCurve);
float step = 1.f / count;
@ -2110,7 +2136,7 @@ BWTessellator::BWTessellator(Array<float>* points, Array<uint32_t>* indices): mR
{
}
void BWTessellator::tessellate(const RenderShape *rshape)
void BWTessellator::tessellate(const RenderShape *rshape, const Matrix& matrix)
{
auto cmds = rshape->path.cmds.data;
auto cmdCnt = rshape->path.cmds.count;
@ -2148,7 +2174,13 @@ void BWTessellator::tessellate(const RenderShape *rshape)
case PathCommand::CubicTo: {
Bezier curve{pts[-1], pts[0], pts[1], pts[2]};
auto stepCount = detail::_bezierCurveCount(curve);
Bezier relCurve {pts[-1], pts[0], pts[1], pts[2]};
mathMultiply(&relCurve.start, &matrix);
mathMultiply(&relCurve.ctrl1, &matrix);
mathMultiply(&relCurve.ctrl2, &matrix);
mathMultiply(&relCurve.end, &matrix);
auto stepCount = detail::_bezierCurveCount(relCurve);
if (stepCount <= 1) stepCount = 2;
@ -2176,9 +2208,31 @@ void BWTessellator::tessellate(const RenderShape *rshape)
}
}
RenderRegion BWTessellator::bounds() const
{
return RenderRegion {
static_cast<int32_t>(mLeftTop.x),
static_cast<int32_t>(mLeftTop.y),
static_cast<int32_t>(mRightBottom.x - mLeftTop.x),
static_cast<int32_t>(mRightBottom.y - mLeftTop.y),
};
}
uint32_t BWTessellator::pushVertex(float x, float y)
{
return detail::_pushVertex(mResPoints, x, y);
auto index = detail::_pushVertex(mResPoints, x, y);
if (index == 0) {
mRightBottom.x = mLeftTop.x = x;
mRightBottom.y = mLeftTop.y = y;
} else {
mLeftTop.x = min(mLeftTop.x, x);
mLeftTop.y = min(mLeftTop.y, y);
mRightBottom.x = max(mRightBottom.x, x);
mRightBottom.y = max(mRightBottom.y , y);
}
return index;
}
void BWTessellator::pushTriangle(uint32_t a, uint32_t b, uint32_t c)

View file

@ -108,11 +108,13 @@ class Stroker final
bool hasMove = false;
};
public:
Stroker(Array<float>* points, Array<uint32_t>* indices);
Stroker(Array<float>* points, Array<uint32_t>* indices, const Matrix& matrix);
~Stroker() = default;
void stroke(const RenderShape *rshape);
RenderRegion bounds() const;
private:
void doStroke(const PathCommand* cmds, uint32_t cmd_count, const Point* pts, uint32_t pts_count);
void doDashStroke(const PathCommand* cmds, uint32_t cmd_count, const Point* pts, uint32_t pts_count,
@ -141,11 +143,14 @@ private:
private:
Array<float>* mResGlPoints;
Array<uint32_t>* mResIndices;
Matrix mMatrix;
float mStrokeWidth = 1.f;
float mMiterLimit = 4.f;
StrokeCap mStrokeCap = StrokeCap::Square;
StrokeJoin mStrokeJoin = StrokeJoin::Bevel;
State mStrokeState = {};
GlPoint mLeftTop = {};
GlPoint mRightBottom = {};
};
class DashStroke
@ -183,7 +188,9 @@ public:
BWTessellator(Array<float>* points, Array<uint32_t>* indices);
~BWTessellator() = default;
void tessellate(const RenderShape *rshape);
void tessellate(const RenderShape *rshape, const Matrix& matrix);
RenderRegion bounds() const;
private:
uint32_t pushVertex(float x, float y);
@ -192,6 +199,8 @@ private:
private:
Array<float>* mResPoints;
Array<uint32_t>* mResIndices;
GlPoint mLeftTop = {};
GlPoint mRightBottom = {};
};
} // namespace tvg