From c778f451b12cc0502e3ce5cbea7eb16a5c4a2faf Mon Sep 17 00:00:00 2001 From: Sergii Liebodkin Date: Tue, 16 Apr 2024 11:31:57 +0300 Subject: [PATCH] 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 --- src/renderer/wg_engine/tvgWgGeometry.cpp | 707 ++++++------------- src/renderer/wg_engine/tvgWgGeometry.h | 44 +- src/renderer/wg_engine/tvgWgRenderData.cpp | 172 +++-- src/renderer/wg_engine/tvgWgRenderData.h | 22 +- src/renderer/wg_engine/tvgWgRenderTarget.cpp | 35 +- src/renderer/wg_engine/tvgWgRenderTarget.h | 8 +- src/renderer/wg_engine/tvgWgRenderer.cpp | 14 +- 7 files changed, 383 insertions(+), 619 deletions(-) diff --git a/src/renderer/wg_engine/tvgWgGeometry.cpp b/src/renderer/wg_engine/tvgWgGeometry.cpp index 77a2a2ad..ea459b92 100644 --- a/src/renderer/wg_engine/tvgWgGeometry.cpp +++ b/src/renderer/wg_engine/tvgWgGeometry.cpp @@ -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); + } } } } diff --git a/src/renderer/wg_engine/tvgWgGeometry.h b/src/renderer/wg_engine/tvgWgGeometry.h index f7abe644..46149b25 100644 --- a/src/renderer/wg_engine/tvgWgGeometry.h +++ b/src/renderer/wg_engine/tvgWgGeometry.h @@ -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 sinus; @@ -102,14 +101,12 @@ struct WgGeometryData { static WgMath* gMath; - Array positions{}; + WgPolyline positions{}; Array texCoords{}; Array 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 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_ \ No newline at end of file diff --git a/src/renderer/wg_engine/tvgWgRenderData.cpp b/src/renderer/wg_engine/tvgWgRenderData.cpp index 02cb07ea..f9586cc8 100644 --- a/src/renderer/wg_engine/tvgWgRenderData.cpp +++ b/src/renderer/wg_engine/tvgWgRenderData.cpp @@ -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); } diff --git a/src/renderer/wg_engine/tvgWgRenderData.h b/src/renderer/wg_engine/tvgWgRenderData.h index ac3c9372..361be074 100644 --- a/src/renderer/wg_engine/tvgWgRenderData.h +++ b/src/renderer/wg_engine/tvgWgRenderData.h @@ -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 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; }; diff --git a/src/renderer/wg_engine/tvgWgRenderTarget.cpp b/src/renderer/wg_engine/tvgWgRenderTarget.cpp index c9b6c59c..9ac5e88c 100644 --- a/src/renderer/wg_engine/tvgWgRenderTarget.cpp +++ b/src/renderer/wg_engine/tvgWgRenderTarget.cpp @@ -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); } } diff --git a/src/renderer/wg_engine/tvgWgRenderTarget.h b/src/renderer/wg_engine/tvgWgRenderTarget.h index 80a4bdaf..219a5dbe 100644 --- a/src/renderer/wg_engine/tvgWgRenderTarget.h +++ b/src/renderer/wg_engine/tvgWgRenderTarget.h @@ -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); diff --git a/src/renderer/wg_engine/tvgWgRenderer.cpp b/src/renderer/wg_engine/tvgWgRenderer.cpp index b960056a..79680a56 100644 --- a/src/renderer/wg_engine/tvgWgRenderer.cpp +++ b/src/renderer/wg_engine/tvgWgRenderer.cpp @@ -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);