wg_engine: update mesh generations and render mechanics

[issues 1479: lottie](#1479)

- optimaze stroking algorithm by prevent vector normalizations
- using render meshes heaps to reduce webgpu instances re-creations
- using single instance of pilylines for standard operations such as trim and split
- "on-the-fly" strokes generations with dash pattern
- "on-the-fly" mesh objects generation in a process of path decoding
- merge strokes into single mesh by each shape
This commit is contained in:
Sergii Liebodkin 2024-04-16 11:31:57 +03:00 committed by Hermet Park
parent 398d181e3f
commit c778f451b1
7 changed files with 383 additions and 619 deletions

View file

@ -32,11 +32,11 @@ void WgMath::initialize()
{
if (initialized) return;
initialized = true;
const uint32_t nPoints = 360;
sinus.reserve(360);
cosin.reserve(360);
constexpr uint32_t nPoints = 360;
sinus.reserve(nPoints);
cosin.reserve(nPoints);
for (uint32_t i = 0; i < nPoints; i++) {
float angle = i * (2 * M_PI) / nPoints;
float angle = i * (2 * MATH_PI) / nPoints;
sinus.push(sin(angle));
cosin.push(cos(angle));
}
@ -54,8 +54,9 @@ void WgMath::release() {
WgPolyline::WgPolyline()
{
pts.reserve(1024);
dist.reserve(1014);
constexpr uint32_t nPoints = 360;
pts.reserve(nPoints);
dist.reserve(nPoints);
}
@ -179,123 +180,78 @@ void WgPolyline::getBBox(WgPoint& pmin, WgPoint& pmax) const
// WgGeometryData
//***********************************************************************
void WgGeometryData::computeTriFansIndexes()
{
if (positions.count <= 2) return;
indexes.reserve((positions.count - 2) * 3);
for (size_t i = 0; i < positions.count - 2; i++) {
indexes.push(0);
indexes.push(i + 1);
indexes.push(i + 2);
}
};
void WgGeometryData::computeContour(WgGeometryData* data)
{
assert(data);
assert(data->positions.count > 1);
clear();
uint32_t istart = data->getIndexMinX(); // index start
uint32_t icnt = data->positions.count; // index count
uint32_t iprev = istart == 0 ? icnt - 1 : istart - 1;
uint32_t inext = (istart + 1) % icnt; // index current
WgPoint p0 = data->positions[istart]; // current segment start
bool isIntersected = false;
bool isClockWise = !isCW(data->positions[iprev], data->positions[istart], data->positions[inext]);
uint32_t ii{}; // intersected segment index
WgPoint pi{}; // intersection point
positions.push(p0);
while(!((inext == istart) && (!isIntersected))) {
if (data->getClosestIntersection(p0, data->positions[inext], pi, ii)) {
isIntersected = true;
p0 = pi;
// operate intersection point
if (isClockWise) // clock wise behavior
inext = isCW(p0, pi, data->positions[ii]) ? ii : ((ii + 1) % icnt);
else // contr-clock wise behavior
inext = isCW(p0, pi, data->positions[ii]) ? ((ii + 1) % icnt) : ii;
} else { // simple next point
isIntersected = false;
p0 = data->positions[inext];
inext = (inext + 1) % icnt;
}
positions.push(p0);
}
WgGeometryData::WgGeometryData() {
constexpr uint32_t nPoints = 10240;
positions.pts.reserve(nPoints);
texCoords.reserve(nPoints);
indexes.reserve(nPoints);
}
void WgGeometryData::appendCubic(WgPoint p1, WgPoint p2, WgPoint p3)
void WgGeometryData::clear()
{
WgPoint p0 = positions.count > 0 ? positions.last() : WgPoint(0.0f, 0.0f);
const size_t segs = 16;
for (size_t i = 1; i <= segs; i++) {
float t = i / (float)segs;
// get cubic spline interpolation coefficients
float t0 = 1 * (1.0f - t) * (1.0f - t) * (1.0f - t);
float t1 = 3 * (1.0f - t) * (1.0f - t) * t;
float t2 = 3 * (1.0f - t) * t * t;
float t3 = 1 * t * t * t;
positions.push(p0 * t0 + p1 * t1 + p2 * t2 + p3 * t3);
}
};
indexes.clear();
positions.clear();
texCoords.clear();
}
void WgGeometryData::appendBox(WgPoint pmin, WgPoint pmax)
{
appendRect(
{ pmin.x, pmin.y},
{ pmax.x, pmin.y},
{ pmin.x, pmax.y},
{ pmax.x, pmax.y});
};
{ pmin.x, pmin.y },
{ pmax.x, pmin.y },
{ pmin.x, pmax.y },
{ pmax.x, pmax.y });
}
void WgGeometryData::appendRect(WgPoint p0, WgPoint p1, WgPoint p2, WgPoint p3)
{
uint32_t index = positions.count;
positions.push(p0); // +0
positions.push(p1); // +1
positions.push(p2); // +2
positions.push(p3); // +3
uint32_t index = positions.pts.count;
positions.appendPoint(p0); // +0
positions.appendPoint(p1); // +1
positions.appendPoint(p2); // +2
positions.appendPoint(p3); // +3
indexes.push(index + 0);
indexes.push(index + 1);
indexes.push(index + 2);
indexes.push(index + 1);
indexes.push(index + 3);
indexes.push(index + 2);
};
}
// TODO: optimize vertex and index count
void WgGeometryData::appendCircle(WgPoint center, float radius)
{
uint32_t index = positions.count;
uint32_t nSegments = std::trunc(radius);
for (uint32_t i = 0; i < nSegments; i++) {
float angle0 = (float)(i + 0) / nSegments * MATH_PI * 2.0f;
float angle1 = (float)(i + 1) / nSegments * MATH_PI * 2.0f;
WgPoint p0 = center + WgPoint(sin(angle0) * radius, cos(angle0) * radius);
WgPoint p1 = center + WgPoint(sin(angle1) * radius, cos(angle1) * radius);
positions.push(center); // +0
positions.push(p0); // +1
positions.push(p1); // +2
indexes.push(index + 0);
indexes.push(index + 1);
indexes.push(index + 2);
index += 3;
uint32_t indexCenter = positions.pts.count;
positions.appendPoint(center);
uint32_t index = positions.pts.count;
positions.appendPoint({ center.x + gMath->sinus[0] * radius, center.y + gMath->cosin[0] * radius });
uint32_t nPoints = (uint32_t)(radius * 2.0f);
nPoints = nPoints < 8 ? 8 : nPoints;
const uint32_t step = gMath->sinus.count / nPoints;
for (uint32_t i = step; i < gMath->sinus.count; i += step) {
positions.appendPoint({ center.x + gMath->sinus[i] * radius, center.y + gMath->cosin[i] * radius });
indexes.push(index); // prev point
indexes.push(indexCenter); // center point
indexes.push(index + 1); // curr point
index++;
}
};
positions.appendPoint({ center.x + gMath->sinus[0] * radius, center.y + gMath->cosin[0] * radius });
indexes.push(index); // prev point
indexes.push(indexCenter); // center point
indexes.push(index + 1); // curr point
positions.appendPoint(center);
}
void WgGeometryData::appendImageBox(float w, float h)
{
positions.push({ 0.0f, 0.0f });
positions.push({ w , 0.0f });
positions.push({ w , h });
positions.push({ 0.0f, h });
positions.appendPoint({ 0.0f, 0.0f });
positions.appendPoint({ w , 0.0f });
positions.appendPoint({ w , h });
positions.appendPoint({ 0.0f, h });
texCoords.push({ 0.0f, 0.0f });
texCoords.push({ 1.0f, 0.0f });
texCoords.push({ 1.0f, 1.0f });
@ -306,15 +262,15 @@ void WgGeometryData::appendImageBox(float w, float h)
indexes.push(0);
indexes.push(2);
indexes.push(3);
};
}
void WgGeometryData::appendBlitBox()
{
positions.push({ -1.0f, +1.0f });
positions.push({ +1.0f, +1.0f });
positions.push({ +1.0f, -1.0f });
positions.push({ -1.0f, -1.0f });
positions.appendPoint({ -1.0f, +1.0f });
positions.appendPoint({ +1.0f, +1.0f });
positions.appendPoint({ +1.0f, -1.0f });
positions.appendPoint({ -1.0f, -1.0f });
texCoords.push({ 0.0f, 0.0f });
texCoords.push({ 1.0f, 0.0f });
texCoords.push({ 1.0f, 1.0f });
@ -331,13 +287,13 @@ void WgGeometryData::appendBlitBox()
void WgGeometryData::appendMesh(const RenderMesh* rmesh)
{
assert(rmesh);
positions.reserve(rmesh->triangleCnt * 3);
positions.pts.reserve(rmesh->triangleCnt * 3);
texCoords.reserve(rmesh->triangleCnt * 3);
indexes.reserve(rmesh->triangleCnt * 3);
for (uint32_t i = 0; i < rmesh->triangleCnt; i++) {
positions.push(rmesh->triangles[i].vertex[0].pt);
positions.push(rmesh->triangles[i].vertex[1].pt);
positions.push(rmesh->triangles[i].vertex[2].pt);
positions.appendPoint(rmesh->triangles[i].vertex[0].pt);
positions.appendPoint(rmesh->triangles[i].vertex[1].pt);
positions.appendPoint(rmesh->triangles[i].vertex[2].pt);
texCoords.push(rmesh->triangles[i].vertex[0].uv);
texCoords.push(rmesh->triangles[i].vertex[1].uv);
texCoords.push(rmesh->triangles[i].vertex[2].uv);
@ -345,440 +301,185 @@ void WgGeometryData::appendMesh(const RenderMesh* rmesh)
indexes.push(i*3 + 1);
indexes.push(i*3 + 2);
}
};
WgPoint WgGeometryData::interpolate(float t, uint32_t& index)
{
assert(t >= 0.0f);
assert(t <= 1.0f);
// get total length
float totalLength = 0.0f;
for (uint32_t i = 0; i < positions.count - 1; i++)
totalLength += positions[i].dist(positions[i+1]);
float targetLength = totalLength * t;
// find current segment index and interpolation factor
float currentFactor = 0.0f;
float currentLength = 0.0f;
for (index = 0; index < positions.count - 1; index++) {
float segmentLength = positions[index].dist(positions[index+1]);
currentLength += segmentLength;
if(currentLength >= targetLength) {
currentFactor = 1.0f -(currentLength - targetLength) / segmentLength;
break;
}
}
// get interpolated position and segment index
return positions[index] * (1.0f - currentFactor) + positions[index + 1] * currentFactor;
}
float WgGeometryData::getLength()
{
float length = 0.0f;
for (uint32_t i = 0; i < positions.count - 1; i++)
length += positions[i].dist(positions[i+1]);
return length;
}
bool WgGeometryData::getClosestIntersection(WgPoint p1, WgPoint p2, WgPoint& pi, uint32_t& index)
{
bool finded = false;
pi = p2;
float dist = p1.dist(p2);
for (uint32_t i = 0; i < positions.count - 1; i++) {
WgPoint p3 = positions[i+0];
WgPoint p4 = positions[i+1];
float bot = (p1.x - p2.x)*(p3.y - p4.y) - (p1.y - p2.y)*(p3.x - p4.x);
if (bot != 0) {
float top0 = (p1.x - p3.x)*(p3.y - p4.y) - (p1.y - p3.y)*(p3.x - p4.x);
float top1 = (p1.x - p2.x)*(p1.y - p3.y) - (p1.y - p2.y)*(p1.x - p3.x);
float t0 = +top0 / bot;
float t1 = -top1 / bot;
if ((t0 > 0.0f) && (t0 < 1.0f) && (t1 > 0.0f) && (t1 < 1.0f)) {
WgPoint p = { p1.x + (p2.x - p1.x) * top0 / bot, p1.y + (p2.y - p1.y) * top0 / bot };
float d = p.dist(p1);
if (d < dist) {
pi = p;
index = i;
dist = d;
finded = true;
}
}
}
}
return finded;
}
bool WgGeometryData::isCW(WgPoint p1, WgPoint p2, WgPoint p3)
{
WgPoint v1 = p2 - p1;
WgPoint v2 = p3 - p1;
return (v1.x*v2.y - v1.y*v2.x) < 0.0;
}
uint32_t WgGeometryData::getIndexMinX()
{
assert(positions.count > 0);
uint32_t index = 0;
for (uint32_t i = 1; i < positions.count; i++)
if (positions[index].x > positions[i].x) index = i;
return index;
}
uint32_t WgGeometryData::getIndexMaxX()
{
assert(positions.count > 0);
uint32_t index = 0;
for (uint32_t i = 1; i < positions.count; i++)
if (positions[index].x < positions[i].x) index = i;
return index;
}
uint32_t WgGeometryData::getIndexMinY()
{
assert(positions.count > 0);
uint32_t index = 0;
for (uint32_t i = 1; i < positions.count; i++)
if (positions[index].y > positions[i].y) index = i;
return index;
}
uint32_t WgGeometryData::getIndexMaxY()
{
assert(positions.count > 0);
uint32_t index = 0;
for (uint32_t i = 1; i < positions.count; i++)
if (positions[index].y < positions[i].y) index = i;
return index;
}
void WgGeometryData::close()
{
if (positions.count > 1) {
positions.push(positions[0]);
}
};
void WgGeometryData::clear()
{
indexes.clear();
positions.clear();
texCoords.clear();
}
//***********************************************************************
// WgGeometryDataGroup
//***********************************************************************
void WgGeometryDataGroup::getBBox(WgPoint& pmin, WgPoint& pmax) {
assert(geometries.count > 0);
assert(geometries[0]->positions.count > 0);
pmin = geometries[0]->positions[0];
pmax = geometries[0]->positions[0];
for (uint32_t i = 0; i < geometries.count; i++) {
for (uint32_t j = 0; j < geometries[i]->positions.count; j++) {
pmin.x = std::min(pmin.x, geometries[i]->positions[j].x);
pmin.y = std::min(pmin.y, geometries[i]->positions[j].y);
pmax.x = std::max(pmax.x, geometries[i]->positions[j].x);
pmax.y = std::max(pmax.y, geometries[i]->positions[j].y);
}
}
}
void WgGeometryDataGroup::tesselate(const RenderShape& rshape)
{
// windiwg fill rule
if (rshape.rule == FillRule::Winding) {
WgGeometryDataGroup polylines{};
decodePath(rshape, &polylines);
contourPolyline(&polylines, this);
} else // even-odd fill rule
if (rshape.rule == FillRule::EvenOdd) {
decodePath(rshape, this);
}
// update triangle fans indexes
for (uint32_t i = 0; i < geometries.count; i++)
geometries[i]->computeTriFansIndexes();
}
void WgGeometryDataGroup::stroke(const RenderShape& rshape)
{
assert(rshape.stroke);
auto strokeData = new WgGeometryData();
// decode
WgGeometryDataGroup polylines{};
decodePath(rshape, &polylines);
// trim -> split -> stroke
if ((rshape.stroke->dashPattern) && ((rshape.stroke->trim.begin != 0.0f) || (rshape.stroke->trim.end != 1.0f))) {
WgGeometryDataGroup trimmed{};
WgGeometryDataGroup splitted{};
trimPolyline(&polylines, &trimmed, rshape.stroke);
splitPolyline(&trimmed, &splitted, rshape.stroke);
strokePolyline(&splitted, strokeData, rshape.stroke);
} else // trim -> stroke
if ((rshape.stroke->trim.begin != 0.0f) || (rshape.stroke->trim.end != 1.0f)) {
WgGeometryDataGroup trimmed{};
trimPolyline(&polylines, &trimmed, rshape.stroke);
strokePolyline(&trimmed, strokeData, rshape.stroke);
} else // split -> stroke
if (rshape.stroke->dashPattern) {
WgGeometryDataGroup splitted{};
splitPolyline(&polylines, &splitted, rshape.stroke);
strokePolyline(&splitted, strokeData, rshape.stroke);
} else { // stroke
strokePolyline(&polylines, strokeData, rshape.stroke);
}
// append stroke geometry
geometries.push(strokeData);
}
void WgGeometryDataGroup::release()
{
for (uint32_t i = 0; i < geometries.count; i++)
delete geometries[i];
geometries.clear();
}
void WgGeometryDataGroup::decodePath(const RenderShape& rshape, WgGeometryDataGroup* polyline)
{
size_t pntIndex = 0;
for (uint32_t cmdIndex = 0; cmdIndex < rshape.path.cmds.count; cmdIndex++) {
PathCommand cmd = rshape.path.cmds[cmdIndex];
if (cmd == PathCommand::MoveTo) {
polyline->geometries.push(new WgGeometryData);
auto outline = polyline->geometries.last();
outline->positions.push(rshape.path.pts[pntIndex]);
pntIndex++;
} else if (cmd == PathCommand::LineTo) {
auto outline = polyline->geometries.last();
if (outline)
outline->positions.push(rshape.path.pts[pntIndex]);
pntIndex++;
} else if (cmd == PathCommand::Close) {
auto outline = polyline->geometries.last();
if ((outline) && (outline->positions.count > 0))
outline->positions.push(outline->positions[0]);
} else if (cmd == PathCommand::CubicTo) {
auto outline = polyline->geometries.last();
if ((outline) && (outline->positions.count > 0))
outline->appendCubic(
rshape.path.pts[pntIndex + 0],
rshape.path.pts[pntIndex + 1],
rshape.path.pts[pntIndex + 2]
);
pntIndex += 3;
}
}
}
void WgGeometryDataGroup::contourPolyline(WgGeometryDataGroup* polyline, WgGeometryDataGroup* contours)
void WgGeometryData::appendStrokeDashed(const WgPolyline* polyline, const RenderStroke *stroke)
{
assert(stroke);
assert(polyline);
assert(contours);
for (uint32_t i = 0 ; i < polyline->geometries.count; i++) {
WgGeometryData* geometry = new WgGeometryData();
geometry->computeContour(polyline->geometries[i]);
contours->geometries.push(geometry);
}
}
void WgGeometryDataGroup::trimPolyline(WgGeometryDataGroup* polyline, WgGeometryDataGroup* trimmed, RenderStroke *stroke)
{
assert(stroke);
for (uint32_t i = 0; i < polyline->geometries.count; i++) {
auto segment = new WgGeometryData;
uint32_t is = 0;
uint32_t ie = 0;
WgPoint vs = polyline->geometries[i]->interpolate(stroke->trim.begin, is);
WgPoint ve = polyline->geometries[i]->interpolate(stroke->trim.end, ie);
segment->positions.push(vs);
for (uint32_t j = is+1; j <= ie; j++)
segment->positions.push(polyline->geometries[i]->positions[j]);
segment->positions.push(ve);
trimmed->geometries.push(segment);
}
}
void WgGeometryDataGroup::splitPolyline(WgGeometryDataGroup* polyline, WgGeometryDataGroup* splitted, RenderStroke *stroke)
{
assert(stroke);
for (uint32_t i = 0; i < polyline->geometries.count; i++) {
auto& vlist = polyline->geometries[i]->positions;
// append single point segment
if (vlist.count == 1) {
auto segment = new WgGeometryData;
segment->positions.push(vlist.last());
splitted->geometries.push(segment);
}
if (vlist.count >= 2) {
uint32_t icurr = 1;
uint32_t ipatt = 0;
WgPoint vcurr = vlist[0];
while (icurr < vlist.count) {
if (ipatt % 2 == 0) {
splitted->geometries.push(new WgGeometryData);
splitted->geometries.last()->positions.push(vcurr);
static WgPolyline dashed;
dashed.clear();
// append single point polyline
if (polyline->pts.count == 1)
appendStroke(polyline, stroke);
// append multy points dashed polyline
else if (polyline->pts.count >= 2) {
auto& pts = polyline->pts;
auto& dist = polyline->dist;
// starting state
uint32_t dashIndex = 0;
float currentLength = stroke->dashPattern[dashIndex];
// iterate by polyline points
for (uint32_t i = 0; i < pts.count - 1; i++) {
// append current polyline point
if (dashIndex % 2 == 0)
dashed.appendPoint(pts[i]);
// move inside polyline sergemnt
while(currentLength < dist[i+1]) {
// get current point
float t = currentLength / dist[i+1];
dashed.appendPoint(pts[i] + (pts[i+1] - pts[i]) * t);
// update current state
dashIndex = (dashIndex + 1) % stroke->dashCnt;
currentLength += stroke->dashPattern[dashIndex];
// append stroke if dash
if (dashIndex % 2 != 0) {
appendStroke(&dashed, stroke);
dashed.clear();
}
float lcurr = stroke->dashPattern[ipatt];
while ((icurr < vlist.count) && (vlist[icurr].dist(vcurr) < lcurr)) {
lcurr -= vlist[icurr].dist(vcurr);
vcurr = vlist[icurr];
icurr++;
if (ipatt % 2 == 0) splitted->geometries.last()->positions.push(vcurr);
}
if (icurr < vlist.count) {
vcurr = vcurr + (vlist[icurr] - vlist[icurr-1]).normal() * lcurr;
if (ipatt % 2 == 0) splitted->geometries.last()->positions.push(vcurr);
}
ipatt = (ipatt + 1) % stroke->dashCnt;
}
// update current subline length
currentLength -= dist[i+1];
}
// draw last subline
if (dashIndex % 2 == 0) {
dashed.appendPoint(pts.last());
appendStroke(&dashed, stroke);
dashed.clear();
}
}
}
void WgGeometryDataGroup::strokePolyline(WgGeometryDataGroup* polyline, WgGeometryData* strokes, RenderStroke *stroke)
void WgGeometryData::appendStroke(const WgPolyline* polyline, const RenderStroke *stroke)
{
assert(stroke);
assert(polyline);
float wdt = stroke->width / 2;
for (uint32_t i = 0; i < polyline->geometries.count; i++) {
auto outline = polyline->geometries[i];
// single point sub-path
if (outline->positions.count == 1) {
if (stroke->cap == StrokeCap::Round) {
strokes->appendCircle(outline->positions[0], wdt);
} else if (stroke->cap == StrokeCap::Butt) {
// for zero length sub-paths no stroke is rendered
} else if (stroke->cap == StrokeCap::Square) {
strokes->appendRect(
outline->positions[0] + WgPoint(+wdt, +wdt),
outline->positions[0] + WgPoint(+wdt, -wdt),
outline->positions[0] + WgPoint(-wdt, +wdt),
outline->positions[0] + WgPoint(-wdt, -wdt)
);
}
// single point sub-path
if (polyline->pts.count == 1) {
if (stroke->cap == StrokeCap::Round) {
appendCircle(polyline->pts[0], wdt);
} else if (stroke->cap == StrokeCap::Butt) {
// for zero length sub-paths no stroke is rendered
} else if (stroke->cap == StrokeCap::Square) {
appendRect(
polyline->pts[0] + WgPoint(+wdt, +wdt),
polyline->pts[0] + WgPoint(+wdt, -wdt),
polyline->pts[0] + WgPoint(-wdt, +wdt),
polyline->pts[0] + WgPoint(-wdt, -wdt)
);
}
// single line sub-path
if (outline->positions.count == 2) {
WgPoint v0 = outline->positions[0];
WgPoint v1 = outline->positions[1];
WgPoint dir0 = (v1 - v0).normal();
WgPoint nrm0 = WgPoint{ -dir0.y, +dir0.x };
if (stroke->cap == StrokeCap::Round) {
strokes->appendRect(
v0 - nrm0 * wdt, v0 + nrm0 * wdt,
v1 - nrm0 * wdt, v1 + nrm0 * wdt);
strokes->appendCircle(outline->positions[0], wdt);
strokes->appendCircle(outline->positions[1], wdt);
} else if (stroke->cap == StrokeCap::Butt) {
strokes->appendRect(
v0 - nrm0 * wdt, v0 + nrm0 * wdt,
v1 - nrm0 * wdt, v1 + nrm0 * wdt
);
} else if (stroke->cap == StrokeCap::Square) {
strokes->appendRect(
v0 - nrm0 * wdt - dir0 * wdt, v0 + nrm0 * wdt - dir0 * wdt,
v1 - nrm0 * wdt + dir0 * wdt, v1 + nrm0 * wdt + dir0 * wdt
);
}
} else
// single line sub-path
if (polyline->pts.count == 2) {
WgPoint v0 = polyline->pts[0];
WgPoint v1 = polyline->pts[1];
WgPoint dir0 = (v1 - v0) / polyline->dist[1];
WgPoint nrm0 = WgPoint{ -dir0.y, +dir0.x };
if (stroke->cap == StrokeCap::Round) {
appendRect(v0 - nrm0 * wdt, v0 + nrm0 * wdt, v1 - nrm0 * wdt, v1 + nrm0 * wdt);
appendCircle(polyline->pts[0], wdt);
appendCircle(polyline->pts[1], wdt);
} else if (stroke->cap == StrokeCap::Butt) {
appendRect(v0 - nrm0 * wdt, v0 + nrm0 * wdt, v1 - nrm0 * wdt, v1 + nrm0 * wdt);
} else if (stroke->cap == StrokeCap::Square) {
appendRect(
v0 - nrm0 * wdt - dir0 * wdt, v0 + nrm0 * wdt - dir0 * wdt,
v1 - nrm0 * wdt + dir0 * wdt, v1 + nrm0 * wdt + dir0 * wdt
);
}
} else
// multi-lined sub-path
if (outline->positions.count > 2) {
// multi-lined sub-path
if (polyline->pts.count > 2) {
if (polyline->isClosed()) {
if (stroke->join == StrokeJoin::Round) {
appendCircle(polyline->pts[0], wdt);
} else {
float dist0 = polyline->dist.last();
float dist1 = polyline->dist[1];
WgPoint v0 = polyline->pts[polyline->pts.count - 2];
WgPoint v1 = polyline->pts[0];
WgPoint v2 = polyline->pts[1];
WgPoint dir0 = (v1 - v0) / dist0;
WgPoint dir1 = (v2 - v1) / dist1;
WgPoint nrm0 { +dir0.y, -dir0.x };
WgPoint nrm1 { +dir1.y, -dir1.x };
WgPoint offset0 = nrm0 * wdt;
WgPoint offset1 = nrm1 * wdt;
if (stroke->join == StrokeJoin::Bevel) {
appendRect(v1 - offset0, v1 + offset0, v1 - offset1, v1 + offset1);
} else if (stroke->join == StrokeJoin::Miter) {
WgPoint nrm = (nrm0 + nrm1).normal();
float cosine = nrm.dot(nrm0);
if ((cosine != 0.0f) && (abs(cosine) != 1.0f) && (abs(wdt / cosine) <= stroke->miterlimit * 2)) {
appendRect(v1 + nrm * (wdt / cosine), v1 + offset0, v1 + offset1, v1 - nrm * (wdt / cosine));
} else {
appendRect(v1 - offset0, v1 + offset0, v1 - offset1, v1 + offset1);
}
}
}
} else {
// append first cap
WgPoint v0 = outline->positions[0];
WgPoint v1 = outline->positions[1];
WgPoint dir0 = (v1 - v0).normal();
WgPoint v0 = polyline->pts[0];
WgPoint v1 = polyline->pts[1];
WgPoint dir0 = (v1 - v0) / polyline->dist[1];
WgPoint nrm0 = WgPoint{ -dir0.y, +dir0.x };
if (stroke->cap == StrokeCap::Round) {
strokes->appendCircle(v0, wdt);
appendCircle(v0, wdt);
} else if (stroke->cap == StrokeCap::Butt) {
// no cap needed
} else if (stroke->cap == StrokeCap::Square) {
strokes->appendRect(
v0 - nrm0 * wdt - dir0 * wdt,
v0 + nrm0 * wdt - dir0 * wdt,
v0 - nrm0 * wdt,
v0 + nrm0 * wdt
);
appendRect(v0 - nrm0 * wdt - dir0 * wdt, v0 + nrm0 * wdt - dir0 * wdt, v0 - nrm0 * wdt, v0 + nrm0 * wdt);
}
// append last cap
v0 = outline->positions[outline->positions.count - 2];
v1 = outline->positions[outline->positions.count - 1];
dir0 = (v1 - v0).normal();
v0 = polyline->pts[polyline->pts.count - 2];
v1 = polyline->pts[polyline->pts.count - 1];
dir0 = (v1 - v0) / polyline->dist[polyline->pts.count - 1];
nrm0 = WgPoint{ -dir0.y, +dir0.x };
if (stroke->cap == StrokeCap::Round) {
strokes->appendCircle(v1, wdt);
appendCircle(v1, wdt);
} else if (stroke->cap == StrokeCap::Butt) {
// no cap needed
} else if (stroke->cap == StrokeCap::Square) {
strokes->appendRect(
v1 - nrm0 * wdt,
v1 + nrm0 * wdt,
v1 - nrm0 * wdt + dir0 * wdt,
v1 + nrm0 * wdt + dir0 * wdt
);
appendRect(v1 - nrm0 * wdt, v1 + nrm0 * wdt, v1 - nrm0 * wdt + dir0 * wdt, v1 + nrm0 * wdt + dir0 * wdt);
}
}
// append sub-lines
for (uint32_t j = 0; j < outline->positions.count - 1; j++) {
WgPoint v0 = outline->positions[j + 0];
WgPoint v1 = outline->positions[j + 1];
WgPoint dir = (v1 - v0).normal();
WgPoint nrm { -dir.y, +dir.x };
strokes->appendRect(
v0 - nrm * wdt,
v0 + nrm * wdt,
v1 - nrm * wdt,
v1 + nrm * wdt
);
}
// append sub-lines
for (uint32_t i = 0; i < polyline->pts.count - 1; i++) {
float dist1 = polyline->dist[i + 1];
WgPoint v1 = polyline->pts[i + 0];
WgPoint v2 = polyline->pts[i + 1];
WgPoint dir1 = (v2 - v1) / dist1;
WgPoint nrm1 { +dir1.y, -dir1.x };
WgPoint offset1 = nrm1 * wdt;
appendRect(v1 - offset1, v1 + offset1, v2 - offset1, v2 + offset1);
// append joints (TODO: separate by joint types)
for (uint32_t j = 1; j < outline->positions.count - 1; j++) {
WgPoint v0 = outline->positions[j - 1];
WgPoint v1 = outline->positions[j + 0];
WgPoint v2 = outline->positions[j + 1];
WgPoint dir0 = (v1 - v0).normal();
WgPoint dir1 = (v1 - v2).normal();
WgPoint nrm0 { -dir0.y, +dir0.x };
WgPoint nrm1 { +dir1.y, -dir1.x };
if (i > 0) {
if (stroke->join == StrokeJoin::Round) {
strokes->appendCircle(v1, wdt);
} else if (stroke->join == StrokeJoin::Bevel) {
strokes->appendRect(
v1 - nrm0 * wdt, v1 - nrm1 * wdt,
v1 + nrm1 * wdt, v1 + nrm0 * wdt
);
} else if (stroke->join == StrokeJoin::Miter) {
WgPoint nrm = (dir0 + dir1).normal();
float cosine = nrm.dot(nrm0);
if ((cosine != 0.0f) && (abs(wdt / cosine) <= stroke->miterlimit * 2)) {
strokes->appendRect(v1 + nrm * (wdt / cosine), v1 + nrm0 * wdt, v1 + nrm1 * wdt, v1);
strokes->appendRect(v1 - nrm * (wdt / cosine), v1 - nrm0 * wdt, v1 - nrm1 * wdt, v1);
} else {
strokes->appendRect(
v1 - nrm0 * wdt, v1 - nrm1 * wdt,
v1 + nrm1 * wdt, v1 + nrm0 * wdt);
appendCircle(v1, wdt);
} else {
float dist0 = polyline->dist[i + 0];
WgPoint v0 = polyline->pts[i - 1];
WgPoint dir0 = (v1 - v0) / dist0;
WgPoint nrm0 { +dir0.y, -dir0.x };
WgPoint offset0 = nrm0 * wdt;
if (stroke->join == StrokeJoin::Bevel) {
appendRect(v1 - offset0, v1 + offset0, v1 - offset1, v1 + offset1);
} else if (stroke->join == StrokeJoin::Miter) {
WgPoint nrm = (nrm0 + nrm1).normal();
float cosine = nrm.dot(nrm0);
if ((cosine != 0.0f) && (abs(cosine) != 1.0f) && (abs(wdt / cosine) <= stroke->miterlimit * 2)) {
appendRect(v1 + nrm * (wdt / cosine), v1 + offset0, v1 + offset1, v1);
appendRect(v1 - nrm * (wdt / cosine), v1 - offset0, v1 - offset1, v1);
} else {
appendRect(v1 - offset0, v1 + offset0, v1 - offset1, v1 + offset1);
}
}
}
}

View file

@ -59,7 +59,6 @@ public:
inline WgPoint lerp(const WgPoint& p, float t) const { return { x + (p.x - x) * t, y + (p.y - y) * t }; };
};
struct WgMath
{
Array<float> sinus;
@ -102,14 +101,12 @@ struct WgGeometryData
{
static WgMath* gMath;
Array<WgPoint> positions{};
WgPolyline positions{};
Array<WgPoint> texCoords{};
Array<uint32_t> indexes{};
// webgpu did not support triangle fans primitives type
// so we can emulate triangle fans using indexing
void computeTriFansIndexes();
void computeContour(WgGeometryData* data);
WgGeometryData();
void clear();
void appendCubic(WgPoint p1, WgPoint p2, WgPoint p3);
void appendBox(WgPoint pmin, WgPoint pmax);
@ -118,37 +115,8 @@ struct WgGeometryData
void appendImageBox(float w, float h);
void appendBlitBox();
void appendMesh(const RenderMesh* rmesh);
WgPoint interpolate(float t, uint32_t& ind); // t = [0;1]
float getLength();
bool getClosestIntersection(WgPoint p1, WgPoint p2, WgPoint& pi, uint32_t& index);
bool isCW(WgPoint p1, WgPoint p2, WgPoint p3);
uint32_t getIndexMinX();
uint32_t getIndexMaxX();
uint32_t getIndexMinY();
uint32_t getIndexMaxY();
void close();
void clear();
void appendStrokeDashed(const WgPolyline* polyline, const RenderStroke *stroke);
void appendStroke(const WgPolyline* polyline, const RenderStroke *stroke);
};
struct WgGeometryDataGroup
{
Array<WgGeometryData*> geometries{};
virtual ~WgGeometryDataGroup() { release(); }
void getBBox(WgPoint& pmin, WgPoint& pmax);
void tesselate(const RenderShape& rshape);
void stroke(const RenderShape& rshape);
void release();
private:
static void decodePath(const RenderShape& rshape, WgGeometryDataGroup* polyline);
static void contourPolyline(WgGeometryDataGroup* polyline, WgGeometryDataGroup* contours);
static void trimPolyline(WgGeometryDataGroup* polyline, WgGeometryDataGroup* trimmed, RenderStroke *stroke);
static void splitPolyline(WgGeometryDataGroup* polyline, WgGeometryDataGroup* splitted, RenderStroke *stroke);
static void strokePolyline(WgGeometryDataGroup* polyline, WgGeometryData* strokes, RenderStroke *stroke);
};
#endif // _TVG_WG_GEOMETRY_H_
#endif // _TVG_WG_GEOMETRY_H_

View file

@ -30,7 +30,7 @@
// WgMeshData
//***********************************************************************
void WgMeshData::draw(WGPURenderPassEncoder renderPassEncoder)
void WgMeshData::draw(WgContext& context, WGPURenderPassEncoder renderPassEncoder)
{
wgpuRenderPassEncoderSetVertexBuffer(renderPassEncoder, 0, bufferPosition, 0, vertexCount * sizeof(float) * 2);
wgpuRenderPassEncoderSetIndexBuffer(renderPassEncoder, bufferIndex, WGPUIndexFormat_Uint32, 0, indexCount * sizeof(uint32_t));
@ -38,7 +38,15 @@ void WgMeshData::draw(WGPURenderPassEncoder renderPassEncoder)
}
void WgMeshData::drawImage(WGPURenderPassEncoder renderPassEncoder)
void WgMeshData::drawFan(WgContext& context, WGPURenderPassEncoder renderPassEncoder)
{
wgpuRenderPassEncoderSetVertexBuffer(renderPassEncoder, 0, bufferPosition, 0, vertexCount * sizeof(float) * 2);
wgpuRenderPassEncoderSetIndexBuffer(renderPassEncoder, context.indexBufferFan, WGPUIndexFormat_Uint32, 0, indexCount * sizeof(uint32_t));
wgpuRenderPassEncoderDrawIndexed(renderPassEncoder, indexCount, 1, 0, 0, 0);
}
void WgMeshData::drawImage(WgContext& context, WGPURenderPassEncoder renderPassEncoder)
{
wgpuRenderPassEncoderSetVertexBuffer(renderPassEncoder, 0, bufferPosition, 0, vertexCount * sizeof(float) * 2);
wgpuRenderPassEncoderSetVertexBuffer(renderPassEncoder, 1, bufferTexCoord, 0, vertexCount * sizeof(float) * 2);
@ -47,13 +55,25 @@ void WgMeshData::drawImage(WGPURenderPassEncoder renderPassEncoder)
};
void WgMeshData::update(WgContext& context, WgGeometryData* geometryData){
void WgMeshData::update(WgContext& context, const WgPolyline* polyline)
{
assert(polyline);
assert(polyline->pts.count > 2);
vertexCount = polyline->pts.count;
indexCount = (polyline->pts.count - 2) * 3;
context.allocateVertexBuffer(bufferPosition, &polyline->pts[0], vertexCount * sizeof(float) * 2);
context.allocateIndexBufferFan(vertexCount);
}
void WgMeshData::update(WgContext& context, const WgGeometryData* geometryData)
{
assert(geometryData);
vertexCount = geometryData->positions.count;
vertexCount = geometryData->positions.pts.count;
indexCount = geometryData->indexes.count;
// buffer position data create and write
if (geometryData->positions.count > 0)
context.allocateVertexBuffer(bufferPosition, &geometryData->positions[0], vertexCount * sizeof(float) * 2);
if (geometryData->positions.pts.count > 0)
context.allocateVertexBuffer(bufferPosition, &geometryData->positions.pts[0], vertexCount * sizeof(float) * 2);
// buffer tex coords data create and write
if (geometryData->texCoords.count > 0)
context.allocateVertexBuffer(bufferTexCoord, &geometryData->texCoords[0], vertexCount * sizeof(float) * 2);
@ -63,6 +83,19 @@ void WgMeshData::update(WgContext& context, WgGeometryData* geometryData){
};
void WgMeshData::update(WgContext& context, const WgPoint pmin, const WgPoint pmax)
{
vertexCount = 4;
indexCount = 6;
const float data[] = {
pmin.x, pmin.y, pmax.x, pmin.y,
pmax.x, pmax.y, pmin.x, pmax.y
};
context.allocateVertexBuffer(bufferPosition, data, sizeof(data));
context.allocateIndexBufferFan(vertexCount);
}
void WgMeshData::release(WgContext& context)
{
context.releaseBuffer(bufferIndex);
@ -105,29 +138,41 @@ void WgMeshDataPool::release(WgContext& context)
mList.clear();
}
WgMeshDataPool* WgMeshDataGroup::MeshDataPool = nullptr;
WgMeshDataPool* WgMeshDataGroup::gMeshDataPool = nullptr;
//***********************************************************************
// WgMeshDataGroup
//***********************************************************************
void WgMeshDataGroup::update(WgContext& context, WgGeometryDataGroup* geometryDataGroup)
void WgMeshDataGroup::append(WgContext& context, const WgPolyline* polyline)
{
release(context);
assert(geometryDataGroup);
for (uint32_t i = 0; i < geometryDataGroup->geometries.count; i++) {
if (geometryDataGroup->geometries[i]->positions.count > 2) {
meshes.push(MeshDataPool->allocate(context));
meshes.last()->update(context, geometryDataGroup->geometries[i]);
}
}
};
assert(polyline);
assert(polyline->pts.count >= 3);
meshes.push(gMeshDataPool->allocate(context));
meshes.last()->update(context, polyline);
}
void WgMeshDataGroup::append(WgContext& context, const WgGeometryData* geometryData)
{
assert(geometryData);
assert(geometryData->positions.pts.count >= 3);
meshes.push(gMeshDataPool->allocate(context));
meshes.last()->update(context, geometryData);
}
void WgMeshDataGroup::append(WgContext& context, const WgPoint pmin, const WgPoint pmax)
{
meshes.push(gMeshDataPool->allocate(context));
meshes.last()->update(context, pmin, pmax);
}
void WgMeshDataGroup::release(WgContext& context)
{
for (uint32_t i = 0; i < meshes.count; i++)
MeshDataPool->free(context, meshes[i]);
gMeshDataPool->free(context, meshes[i]);
meshes.clear();
};
@ -223,44 +268,85 @@ void WgRenderDataShape::updateMeshes(WgContext &context, const RenderShape &rsha
{
releaseMeshes(context);
strokeFirst = false;
// update shapes geometry
WgGeometryDataGroup shapes;
shapes.tesselate(rshape);
meshGroupShapes.update(context, &shapes);
// update shapes bbox
WgPoint pmin{}, pmax{};
shapes.getBBox(pmin, pmax);
WgGeometryData box;
box.appendBox(pmin, pmax);
meshBBoxShapes.update(context, &box);
// update strokes geometry
if (rshape.stroke) {
strokeFirst = rshape.stroke->strokeFirst;
WgGeometryDataGroup strokes;
strokes.stroke(rshape);
strokes.getBBox(pmin, pmax);
meshGroupStrokes.update(context, &strokes);
// update strokes bbox
static WgPolyline polyline;
polyline.clear();
// decode path
size_t pntIndex = 0;
for (uint32_t cmdIndex = 0; cmdIndex < rshape.path.cmds.count; cmdIndex++) {
PathCommand cmd = rshape.path.cmds[cmdIndex];
if (cmd == PathCommand::MoveTo) {
// proceed current polyline
updateMeshes(context, &polyline, rshape.stroke);
polyline.clear();
polyline.appendPoint(rshape.path.pts[pntIndex]);
pntIndex++;
} else if (cmd == PathCommand::LineTo) {
polyline.appendPoint(rshape.path.pts[pntIndex]);
pntIndex++;
} else if (cmd == PathCommand::Close) {
polyline.close();
} else if (cmd == PathCommand::CubicTo) {
polyline.appendCubic(
rshape.path.pts[pntIndex + 0],
rshape.path.pts[pntIndex + 1],
rshape.path.pts[pntIndex + 2]);
pntIndex += 3;
}
}
// proceed last polyline
updateMeshes(context, &polyline, rshape.stroke);
}
void WgRenderDataShape::updateMeshes(WgContext& context, const WgPolyline* polyline, const RenderStroke* rstroke)
{
assert(polyline);
// generate fill geometry
if (polyline->pts.count >= 3) {
WgPoint pmin{}, pmax{};
strokes.getBBox(pmin, pmax);
WgGeometryData box;
box.appendBox(pmin, pmax);
meshBBoxStrokes.update(context, &box);
polyline->getBBox(pmin, pmax);
meshGroupShapes.append(context, polyline);
meshGroupShapesBBox.append(context, pmin, pmax);
}
// generate strokes geometry
if ((polyline->pts.count >= 1) && rstroke) {
static WgGeometryData geometryData; geometryData.clear();
static WgPolyline trimmed;
// trim -> split -> stroke
if ((rstroke->dashPattern) && ((rstroke->trim.begin != 0.0f) || (rstroke->trim.end != 1.0f))) {
polyline->trim(&trimmed, rstroke->trim.begin, rstroke->trim.end);
geometryData.appendStrokeDashed(&trimmed, rstroke);
} else // trim -> stroke
if ((rstroke->trim.begin != 0.0f) || (rstroke->trim.end != 1.0f)) {
polyline->trim(&trimmed, rstroke->trim.begin, rstroke->trim.end);
geometryData.appendStroke(&trimmed, rstroke);
} else // split -> stroke
if (rstroke->dashPattern) {
geometryData.appendStrokeDashed(polyline, rstroke);
} else { // stroke
geometryData.appendStroke(polyline, rstroke);
}
// append render meshes and bboxes
WgPoint pmin{}, pmax{};
geometryData.positions.getBBox(pmin, pmax);
meshGroupStrokes.append(context, &geometryData);
meshGroupStrokesBBox.append(context, pmin, pmax);
}
}
void WgRenderDataShape::releaseMeshes(WgContext &context)
{
meshGroupStrokesBBox.release(context);
meshGroupStrokes.release(context);
meshGroupShapesBBox.release(context);
meshGroupShapes.release(context);
}
void WgRenderDataShape::release(WgContext& context)
{
meshBBoxStrokes.release(context);
meshBBoxShapes.release(context);
releaseMeshes(context);
renderSettingsStroke.release(context);
renderSettingsShape.release(context);
@ -287,6 +373,8 @@ WgRenderDataShape* WgRenderDataShapePool::allocate(WgContext& context)
void WgRenderDataShapePool::free(WgContext& context, WgRenderDataShape* dataShape)
{
dataShape->meshGroupShapes.release(context);
dataShape->meshGroupStrokes.release(context);
mPool.push(dataShape);
}

View file

@ -30,10 +30,13 @@ struct WgMeshData {
size_t vertexCount{};
size_t indexCount{};
void draw(WGPURenderPassEncoder renderPassEncoder);
void drawImage(WGPURenderPassEncoder renderPassEncoder);
void draw(WgContext& context, WGPURenderPassEncoder renderPassEncoder);
void drawFan(WgContext& context, WGPURenderPassEncoder renderPassEncoder);
void drawImage(WgContext& context, WGPURenderPassEncoder renderPassEncoder);
void update(WgContext& context, WgGeometryData* geometryData);
void update(WgContext& context, const WgPolyline* polyline);
void update(WgContext& context, const WgGeometryData* geometryData);
void update(WgContext& context, const WgPoint pmin, const WgPoint pmax);
void release(WgContext& context);
};
@ -48,11 +51,13 @@ public:
};
struct WgMeshDataGroup {
static WgMeshDataPool* MeshDataPool;
static WgMeshDataPool* gMeshDataPool;
Array<WgMeshData*> meshes{};
void update(WgContext& context, WgGeometryDataGroup* geometryDataGroup);
void append(WgContext& context, const WgPolyline* polyline);
void append(WgContext& context, const WgGeometryData* geometryData);
void append(WgContext& context, const WgPoint pmin, const WgPoint pmax);
void release(WgContext& context);
};
@ -91,12 +96,13 @@ struct WgRenderDataShape: public WgRenderDataPaint
WgRenderSettings renderSettingsShape{};
WgRenderSettings renderSettingsStroke{};
WgMeshDataGroup meshGroupShapes{};
WgMeshDataGroup meshGroupShapesBBox{};
WgMeshDataGroup meshGroupStrokes{};
WgMeshData meshBBoxShapes{};
WgMeshData meshBBoxStrokes{};
WgMeshDataGroup meshGroupStrokesBBox{};
bool strokeFirst{};
void updateMeshes(WgContext& context, const RenderShape& rshape);
void updateMeshes(WgContext& context, const WgPolyline* polyline, const RenderStroke* rstroke);
void releaseMeshes(WgContext& context);
void release(WgContext& context) override;
uint32_t identifier() override { return TVG_CLASS_ID_SHAPE; };

View file

@ -78,40 +78,41 @@ void WgRenderStorage::release(WgContext& context)
}
void WgRenderStorage::renderShape(WgRenderDataShape* renderData, WgPipelineBlendType blendType)
void WgRenderStorage::renderShape(WgContext& context, WgRenderDataShape* renderData, WgPipelineBlendType blendType)
{
assert(renderData);
assert(mRenderPassEncoder);
if (renderData->strokeFirst)
drawStroke(renderData, blendType);
drawShape(renderData, blendType);
drawStroke(context, renderData, blendType);
drawShape(context, renderData, blendType);
if (!renderData->strokeFirst)
drawStroke(renderData, blendType);
drawStroke(context, renderData, blendType);
}
void WgRenderStorage::renderPicture(WgRenderDataPicture* renderData, WgPipelineBlendType blendType)
void WgRenderStorage::renderPicture(WgContext& context, WgRenderDataPicture* renderData, WgPipelineBlendType blendType)
{
assert(renderData);
assert(mRenderPassEncoder);
uint8_t blend = (uint8_t)blendType;
wgpuRenderPassEncoderSetStencilReference(mRenderPassEncoder, 0);
mPipelines->image[blend].use(mRenderPassEncoder, mBindGroupCanvas, renderData->bindGroupPaint, renderData->bindGroupPicture);
renderData->meshData.drawImage(mRenderPassEncoder);
renderData->meshData.drawImage(context, mRenderPassEncoder);
}
void WgRenderStorage::drawShape(WgRenderDataShape* renderData, WgPipelineBlendType blendType)
void WgRenderStorage::drawShape(WgContext& context, WgRenderDataShape* renderData, WgPipelineBlendType blendType)
{
assert(renderData);
assert(mRenderPassEncoder);
assert(renderData->meshGroupShapes.meshes.count == renderData->meshGroupShapesBBox.meshes.count);
// draw shape geometry
uint8_t blend = (uint8_t)blendType;
wgpuRenderPassEncoderSetStencilReference(mRenderPassEncoder, 0);
for (uint32_t i = 0; i < renderData->meshGroupShapes.meshes.count; i++) {
// draw to stencil (first pass)
mPipelines->fillShape.use(mRenderPassEncoder, mBindGroupCanvas, renderData->bindGroupPaint);
renderData->meshGroupShapes.meshes[i]->draw(mRenderPassEncoder);
renderData->meshGroupShapes.meshes[i]->drawFan(context, mRenderPassEncoder);
// fill shape (second pass)
WgRenderSettings& settings = renderData->renderSettingsShape;
if (settings.fillType == WgRenderSettingsType::Solid)
@ -120,24 +121,24 @@ void WgRenderStorage::drawShape(WgRenderDataShape* renderData, WgPipelineBlendTy
mPipelines->linear[blend].use(mRenderPassEncoder, mBindGroupCanvas, renderData->bindGroupPaint, settings.bindGroupLinear);
else if (settings.fillType == WgRenderSettingsType::Radial)
mPipelines->radial[blend].use(mRenderPassEncoder, mBindGroupCanvas, renderData->bindGroupPaint, settings.bindGroupRadial);
renderData->meshBBoxShapes.draw(mRenderPassEncoder);
renderData->meshGroupShapesBBox.meshes[i]->drawFan(context, mRenderPassEncoder);
}
}
void WgRenderStorage::drawStroke(WgRenderDataShape* renderData, WgPipelineBlendType blendType)
void WgRenderStorage::drawStroke(WgContext& context, WgRenderDataShape* renderData, WgPipelineBlendType blendType)
{
assert(renderData);
assert(mRenderPassEncoder);
assert(renderData->meshGroupStrokes.meshes.count == renderData->meshGroupStrokesBBox.meshes.count);
// draw stroke geometry
uint8_t blend = (uint8_t)blendType;
if (renderData->meshGroupStrokes.meshes.count > 0) {
// draw strokes to stencil (first pass)
// draw strokes to stencil (first pass)
for (uint32_t i = 0; i < renderData->meshGroupStrokes.meshes.count; i++) {
// draw to stencil (first pass)
wgpuRenderPassEncoderSetStencilReference(mRenderPassEncoder, 255);
for (uint32_t i = 0; i < renderData->meshGroupStrokes.meshes.count; i++) {
mPipelines->fillStroke.use(mRenderPassEncoder, mBindGroupCanvas, renderData->bindGroupPaint);
renderData->meshGroupStrokes.meshes[i]->draw(mRenderPassEncoder);
}
mPipelines->fillStroke.use(mRenderPassEncoder, mBindGroupCanvas, renderData->bindGroupPaint);
renderData->meshGroupStrokes.meshes[i]->draw(context, mRenderPassEncoder);
// fill shape (second pass)
wgpuRenderPassEncoderSetStencilReference(mRenderPassEncoder, 0);
WgRenderSettings& settings = renderData->renderSettingsStroke;
@ -147,7 +148,7 @@ void WgRenderStorage::drawStroke(WgRenderDataShape* renderData, WgPipelineBlendT
mPipelines->linear[blend].use(mRenderPassEncoder, mBindGroupCanvas, renderData->bindGroupPaint, settings.bindGroupLinear);
else if (settings.fillType == WgRenderSettingsType::Radial)
mPipelines->radial[blend].use(mRenderPassEncoder, mBindGroupCanvas, renderData->bindGroupPaint, settings.bindGroupRadial);
renderData->meshBBoxStrokes.draw(mRenderPassEncoder);
renderData->meshGroupStrokesBBox.meshes[i]->drawFan(context, mRenderPassEncoder);
}
}

View file

@ -48,16 +48,16 @@ public:
void beginRenderPass(WGPUCommandEncoder commandEncoder, bool clear);
void endRenderPass();
void renderShape(WgRenderDataShape* renderData, WgPipelineBlendType blendType);
void renderPicture(WgRenderDataPicture* renderData, WgPipelineBlendType blendType);
void renderShape(WgContext& context, WgRenderDataShape* renderData, WgPipelineBlendType blendType);
void renderPicture(WgContext& context, WgRenderDataPicture* renderData, WgPipelineBlendType blendType);
void clear(WGPUCommandEncoder commandEncoder);
void blend(WGPUCommandEncoder commandEncoder, WgRenderStorage* targetSrc, WgBindGroupBlendMethod* blendMethod);
void compose(WGPUCommandEncoder commandEncoder, WgRenderStorage* targetMsk, WgBindGroupCompositeMethod* composeMethod, WgBindGroupOpacity* opacity);
void antialias(WGPUCommandEncoder commandEncoder, WgRenderStorage* targetSrc);
private:
void drawShape(WgRenderDataShape* renderData, WgPipelineBlendType blendType);
void drawStroke(WgRenderDataShape* renderData, WgPipelineBlendType blendType);
void drawShape(WgContext& context, WgRenderDataShape* renderData, WgPipelineBlendType blendType);
void drawStroke(WgContext& context, WgRenderDataShape* renderData, WgPipelineBlendType blendType);
void dispatchWorkgroups(WGPUComputePassEncoder computePassEncoder);

View file

@ -48,7 +48,7 @@ void WgRenderer::initialize()
mOpacityPool.initialize(mContext);
mBlendMethodPool.initialize(mContext);
mCompositeMethodPool.initialize(mContext);
WgMeshDataGroup::MeshDataPool = new WgMeshDataPool();
WgMeshDataGroup::gMeshDataPool = new WgMeshDataPool();
WgGeometryData::gMath = new WgMath();
WgGeometryData::gMath->initialize();
}
@ -59,8 +59,8 @@ void WgRenderer::release()
WgGeometryData::gMath->release();
delete WgGeometryData::gMath;
mRenderDataShapePool.release(mContext);
WgMeshDataGroup::MeshDataPool->release(mContext);
delete WgMeshDataGroup::MeshDataPool;
WgMeshDataGroup::gMeshDataPool->release(mContext);
delete WgMeshDataGroup::gMeshDataPool;
mCompositorStack.clear();
mRenderStorageStack.clear();
mRenderStoragePool.release(mContext);
@ -162,13 +162,13 @@ bool WgRenderer::renderShape(RenderData data)
assert(renderStorage);
// use hardware blend
if (WgPipelines::isBlendMethodSupportsHW(mBlendMethod))
renderStorage->renderShape((WgRenderDataShape *)data, blendType);
renderStorage->renderShape(mContext, (WgRenderDataShape *)data, blendType);
else { // use custom blend
// terminate current render pass
renderStorage->endRenderPass();
// render image to render target
mRenderTarget.beginRenderPass(mCommandEncoder, true);
mRenderTarget.renderShape((WgRenderDataShape *)data, blendType);
mRenderTarget.renderShape(mContext, (WgRenderDataShape *)data, blendType);
mRenderTarget.endRenderPass();
// blend shape with current render storage
WgBindGroupBlendMethod* blendMethod = mBlendMethodPool.allocate(mContext, mBlendMethod);
@ -188,13 +188,13 @@ bool WgRenderer::renderImage(RenderData data)
assert(renderStorage);
// use hardware blend
if (WgPipelines::isBlendMethodSupportsHW(mBlendMethod))
renderStorage->renderPicture((WgRenderDataPicture *)data, blendType);
renderStorage->renderPicture(mContext, (WgRenderDataPicture *)data, blendType);
else { // use custom blend
// terminate current render pass
renderStorage->endRenderPass();
// render image to render target
mRenderTarget.beginRenderPass(mCommandEncoder, true);
mRenderTarget.renderPicture((WgRenderDataPicture *)data, blendType);
mRenderTarget.renderPicture(mContext, (WgRenderDataPicture *)data, blendType);
mRenderTarget.endRenderPass();
// blend shape with current render storage
WgBindGroupBlendMethod* blendMethod = mBlendMethodPool.allocate(mContext, mBlendMethod);