248 : 0) +
249 (FanPointAttrib::kEnabled ?
sizeof(
SkPoint) : 0) +
250 (StrokeAttrib::kEnabled ? sizeof(StrokeParams) : 0) +
251 (ColorAttrib::kEnabled ? std::min(
sizeof(Color),
sizeof(
SkPMColor4f)) : 0) +
252 (DepthAttrib::kEnabled ? sizeof(float) : 0) +
253 (CurveTypeAttrib::kEnabled ? sizeof(float) : 0) +
254 (SsboIndexAttrib::kEnabled ? sizeof(int) : 0);
255
256
257 using DeferredPatch = std::conditional_t<kTrackJoinControlPoints,
258 PatchStorage<kMaxStride>, std::monostate>;
259 using InnerTriangulator = std::conditional_t<kAddTrianglesWhenChopping,
260 MiddleOutPolygonTriangulator, NullTriangulator>;
261
264
265 static_assert(!kTrackJoinControlPoints || req_attrib<PatchAttribs::kJoinControlPoint>::value,
266 "Deferred patches and auto-updating joins requires kJoinControlPoint attrib");
267public:
268 template <typename... Args>
269 PatchWriter(PatchAttribs attribs,
270 Args&&... allocArgs)
271 : fAttribs(attribs)
272 , fPatchAllocator(
PatchStride(attribs),
std::forward<Args>(allocArgs)...)
274 , fFanPoint(attribs)
275 , fStrokeParams(attribs)
276 , fColor(attribs)
278 , fSsboIndex(attribs) {
279
280
282
283 if constexpr (req_attrib<PatchAttribs::kWideColorIfEnabled>::value) {
284 SkASSERT(attribs & PatchAttribs::kWideColorIfEnabled);
285 } else if constexpr (!opt_attrib<PatchAttribs::kWideColorIfEnabled>::value) {
286 SkASSERT(!(attribs & PatchAttribs::kWideColorIfEnabled));
287 }
288 }
289
290 ~PatchWriter() {
291 if constexpr (kTrackJoinControlPoints) {
292
293 this->writeDeferredStrokePatch();
294 }
295 }
296
298
299
300
301 void setShaderTransform(const wangs_formula::VectorXform& xform,
302 float maxScale = 1.f) {
303 fApproxTransform = xform;
304 fMaxScale = maxScale;
305 }
306
307
308
309 ENABLE_IF(kTrackJoinControlPoints) writeDeferredStrokePatch() {
310 if (fDeferredPatch.hasPending()) {
311 SkASSERT(!fDeferredPatch.fMustDefer);
312
313
314 memcpy(SkTAddOffset<void>(fDeferredPatch.fData, 4 *
sizeof(
SkPoint)),
316
317
318
319 fTolerances.setParametricSegments(fDeferredPatch.fN_p4);
320 if (VertexWriter vw = fPatchAllocator.append(fTolerances)) {
321 vw << VertexWriter::Array<char>(fDeferredPatch.fData,
PatchStride(fAttribs));
322 }
323 }
324
325 fDeferredPatch.reset();
326 }
327
328
329
330
331 ENABLE_IF(JoinAttrib::kEnabled) updateJoinControlPointAttrib(
SkPoint lastControlPoint) {
332 SkASSERT(fAttribs & PatchAttribs::kJoinControlPoint);
333 fJoin = lastControlPoint;
334 if constexpr (kTrackJoinControlPoints) {
335 fDeferredPatch.fMustDefer = false;
336 }
337 }
338
339
340
341 ENABLE_IF(FanPointAttrib::kEnabled) updateFanPointAttrib(
SkPoint fanPoint) {
342 SkASSERT(fAttribs & PatchAttribs::kFanPoint);
343 fFanPoint = fanPoint;
344 }
345
346
347 ENABLE_IF(StrokeAttrib::kEnabled) updateStrokeParamsAttrib(StrokeParams strokeParams) {
348 SkASSERT(fAttribs & PatchAttribs::kStrokeParams);
349 fStrokeParams = strokeParams;
350 fTolerances.setStroke(strokeParams, fMaxScale);
351 }
352
353
354 ENABLE_IF(StrokeAttrib::kEnabled) updateUniformStrokeParams(StrokeParams strokeParams) {
355 SkASSERT(!(fAttribs & PatchAttribs::kStrokeParams));
356 fTolerances.setStroke(strokeParams, fMaxScale);
357 }
358
359
361 SkASSERT(fAttribs & PatchAttribs::kColor);
362
363
364 if constexpr (req_attrib<PatchAttribs::kWideColorIfEnabled>::value) {
366 } else if constexpr (opt_attrib<PatchAttribs::kWideColorIfEnabled>::value) {
367 fColor = VertexColor(
color, fAttribs & PatchAttribs::kWideColorIfEnabled);
368 } else {
369 fColor =
color.toBytes_RGBA();
370 }
371 }
372
373
374 ENABLE_IF(DepthAttrib::kEnabled) updatePaintDepthAttrib(
float depth) {
375 SkASSERT(fAttribs & PatchAttribs::kPaintDepth);
377 }
378
379
382 SkASSERT(fAttribs & PatchAttribs::kSsboIndex);
383 fSsboIndex = ssboIndex;
384 }
385
386
387
388
389
390
391
392
393
395 float n4 = wangs_formula::cubic_p4(kPrecision, p0, p1, p2, p3, fApproxTransform);
396 if constexpr (kDiscardFlatCurves) {
397 if (n4 <= 1.f) {
398
399
400 return;
401 }
402 }
403 if (int numPatches = this->accountForCurve(n4)) {
404 this->chopAndWriteCubics(p0, p1, p2, p3, numPatches);
405 } else {
406 this->writeCubicPatch(p0, p1, p2, p3);
407 }
408 }
409 AI void writeCubic(
const SkPoint pts[4]) {
412 this->writeCubic(p0p1.
lo, p0p1.
hi, p2p3.
lo, p2p3.
hi);
413 }
414
415
416
418 float n2 = wangs_formula::conic_p2(kPrecision, p0, p1, p2,
w, fApproxTransform);
419 if constexpr (kDiscardFlatCurves) {
420 if (n2 <= 1.f) {
421
422
423 return;
424 }
425 }
426 if (int numPatches = this->accountForCurve(n2 * n2)) {
427 this->chopAndWriteConics(p0, p1, p2,
w, numPatches);
428 } else {
429 this->writeConicPatch(p0, p1, p2,
w);
430 }
431 }
432 AI void writeConic(
const SkPoint pts[3],
float w) {
433 this->writeConic(sk_bit_cast<float2>(pts[0]),
434 sk_bit_cast<float2>(pts[1]),
435 sk_bit_cast<float2>(pts[2]),
437 }
438
439
440
442 float n4 = wangs_formula::quadratic_p4(kPrecision, p0, p1, p2, fApproxTransform);
443 if constexpr (kDiscardFlatCurves) {
444 if (n4 <= 1.f) {
445
446
447 return;
448 }
449 }
450 if (int numPatches = this->accountForCurve(n4)) {
451 this->chopAndWriteQuads(p0, p1, p2, numPatches);
452 } else {
453 this->writeQuadPatch(p0, p1, p2);
454 }
455 }
456 AI void writeQuadratic(
const SkPoint pts[3]) {
457 this->writeQuadratic(sk_bit_cast<float2>(pts[0]),
458 sk_bit_cast<float2>(pts[1]),
459 sk_bit_cast<float2>(pts[2]));
460 }
461
462
464
465 fTolerances.setParametricSegments(1.f);
466 if constexpr (kReplicateLineEndPoints) {
467
468
469
470 this->writeCubicPatch(p0p1.
lo, p0p1.
lo, p0p1.
hi, p0p1.
hi);
471 } else {
472
473
474
475 this->writeCubicPatch(p0p1.
lo, (p0p1.zwxy() - p0p1) * (1/3.f) + p0p1, p0p1.
hi);
476 }
477 }
478 AI void writeLine(
float2 p0,
float2 p1) { this->writeLine({p0, p1}); }
480 this->writeLine(sk_bit_cast<float2>(p0), sk_bit_cast<float2>(p1));
481 }
482
483
484
486
487
488 static constexpr float kTriangleSegments_p4 = 2.f * 2.f * 2.f * 2.f;
489 fTolerances.setParametricSegments(kTriangleSegments_p4);
491 kTriangularConicCurveType);
492 }
494 this->writeTriangle(sk_bit_cast<float2>(p0),
495 sk_bit_cast<float2>(p1),
496 sk_bit_cast<float2>(p2));
497 }
498
499
500
502
503
504 fTolerances.setParametricSegments(0.f);
505 if (VertexWriter vw = fPatchAllocator.append(fTolerances)) {
506 vw << VertexWriter::Repeat<4>(p);
507 this->emitPatchAttribs(std::move(vw), {fAttribs,
p}, kCubicCurveType);
508 }
509 }
510
511private:
512 AI void emitPatchAttribs(VertexWriter vertexWriter,
513 const JoinAttrib& join,
514 float explicitCurveType) {
515
516 vertexWriter <<
join << fFanPoint << fStrokeParams << fColor <<
fDepth
517 << CurveTypeAttrib{fAttribs, explicitCurveType} << fSsboIndex;
518 }
519
520 AI VertexWriter appendPatch() {
521 if constexpr (kTrackJoinControlPoints) {
522 if (fDeferredPatch.fMustDefer) {
523 SkASSERT(!fDeferredPatch.hasPending());
525
526
527 fDeferredPatch.fN_p4 = fTolerances.numParametricSegments_p4();
528 return {fDeferredPatch.fData,
PatchStride(fAttribs)};
529 }
530 }
531 return fPatchAllocator.append(fTolerances);
532 }
533
535 if (VertexWriter vw = this->appendPatch()) {
536
537
538
539 vw << p0 << p1 << p2 << p3;
540 this->emitPatchAttribs(std::move(vw),
fJoin, explicitCurveType);
541
542
543 if constexpr (kTrackJoinControlPoints) {
544 if (explicitCurveType == kCubicCurveType &&
any(p3 != p2)) {
545
547 }
else if (
any(p2 != p1)) {
548
550 } else {
551
553 }
554 fDeferredPatch.fMustDefer = false;
555 }
556 }
557 }
558
559
561 this->writePatch(p0, p1, p2, p3, kCubicCurveType);
562 }
564 this->writeCubicPatch(p0, p1p2.
lo, p1p2.
hi, p3);
565 }
567 this->writeCubicPatch(p0, mix(
float4(p0, p2), p1.xyxy(), 2/3.f), p2);
568 }
571 }
572
573 int accountForCurve(float n4) {
574 if (n4 <= kMaxParametricSegments_p4) {
575
576 fTolerances.setParametricSegments(n4);
577 return 0;
578 } else {
579
580
581 fTolerances.setParametricSegments(kMaxParametricSegments_p4);
582 return SkScalarCeilToInt(wangs_formula::root4(std::min(n4, kMaxSegmentsPerCurve_p4) /
583 kMaxParametricSegments_p4));
584 }
585 }
586
587
588
589
592 return (
b -
a)*
T +
a;
593 }
594
595
596
597
599 InnerTriangulator triangulator(numPatches, sk_bit_cast<SkPoint>(p0));
600 for (; numPatches >= 3; numPatches -= 2) {
601
603 float4 ab = mix(p0.xyxy(), p1.xyxy(),
T);
604 float4 bc = mix(p1.xyxy(), p2.xyxy(),
T);
606
607 float4 middle = mix(
ab, bc, mix(
T,
T.zwxy(), 2/3.f));
608
609 this->writeQuadPatch(p0,
ab.lo, abc.
lo);
610 if constexpr (kAddTrianglesWhenChopping) {
611 this->writeTriangle(p0, abc.
lo, abc.
hi);
612 }
613 this->writeCubicPatch(abc.
lo, middle, abc.
hi);
614 if constexpr (kAddTrianglesWhenChopping) {
615 this->writeTriangleStack(triangulator.pushVertex(sk_bit_cast<SkPoint>(abc.
hi)));
616 }
617 std::tie(p0, p1) = {abc.
hi, bc.
hi};
618 }
619 if (numPatches == 2) {
620
622 float2 bc = (p1 + p2) * .5f;
624
625 this->writeQuadPatch(p0,
ab, abc);
626 if constexpr (kAddTrianglesWhenChopping) {
627 this->writeTriangle(p0, abc, p2);
628 }
629 this->writeQuadPatch(abc, bc, p2);
630 } else {
632 this->writeQuadPatch(p0, p1, p2);
633 }
634 if constexpr (kAddTrianglesWhenChopping) {
635 this->writeTriangleStack(triangulator.pushVertex(sk_bit_cast<SkPoint>(p2)));
636 this->writeTriangleStack(triangulator.close());
637 }
638 }
639
641 InnerTriangulator triangulator(numPatches, sk_bit_cast<SkPoint>(p0));
642
646 for (; numPatches >= 2; --numPatches) {
647
648 float T = 1.f/numPatches;
652
653
654 float2 midpoint = abc.xy() / abc.w();
655 this->writeConicPatch(h0.xy() / h0.w(),
657 midpoint,
658 ab.w() / sqrtf(h0.w() * abc.w()));
659 if constexpr (kAddTrianglesWhenChopping) {
660 this->writeTriangleStack(triangulator.pushVertex(sk_bit_cast<SkPoint>(midpoint)));
661 }
662 std::tie(h0, h1) = {abc, bc};
663 }
664
666 this->writeConicPatch(h0.xy() / h0.w(),
667 h1.xy() / h1.w(),
668 h2.xy(),
669 h1.w() / sqrtf(h0.w()));
670 if constexpr (kAddTrianglesWhenChopping) {
671 this->writeTriangleStack(triangulator.pushVertex(sk_bit_cast<SkPoint>(h2.xy())));
672 this->writeTriangleStack(triangulator.close());
673 }
674 }
675
677 InnerTriangulator triangulator(numPatches, sk_bit_cast<SkPoint>(p0));
678 for (; numPatches >= 3; numPatches -= 2) {
679
681 float4 ab = mix(p0.xyxy(), p1.xyxy(),
T);
682 float4 bc = mix(p1.xyxy(), p2.xyxy(),
T);
683 float4 cd = mix(p2.xyxy(), p3.xyxy(),
T);
686 float4 abcd = mix(abc, bcd,
T);
687 float4 middle = mix(abc, bcd,
T.zwxy());
688
689 this->writeCubicPatch(p0,
ab.lo, abc.
lo, abcd.
lo);
690 if constexpr (kAddTrianglesWhenChopping) {
691 this->writeTriangle(p0, abcd.
lo, abcd.
hi);
692 }
693 this->writeCubicPatch(abcd.
lo, middle, abcd.
hi);
694 if constexpr (kAddTrianglesWhenChopping) {
695 this->writeTriangleStack(triangulator.pushVertex(sk_bit_cast<SkPoint>(abcd.
hi)));
696 }
697 std::tie(p0, p1, p2) = {abcd.
hi, bcd.
hi, cd.
hi};
698 }
699 if (numPatches == 2) {
700
702 float2 bc = (p1 + p2) * .5f;
703 float2 cd = (p2 + p3) * .5f;
705 float2 bcd = (bc + cd) * .5f;
706 float2 abcd = (abc + bcd) * .5f;
707
708 this->writeCubicPatch(p0,
ab, abc, abcd);
709 if constexpr (kAddTrianglesWhenChopping) {
710 this->writeTriangle(p0, abcd, p3);
711 }
712 this->writeCubicPatch(abcd, bcd, cd, p3);
713 } else {
715 this->writeCubicPatch(p0, p1, p2, p3);
716 }
717 if constexpr (kAddTrianglesWhenChopping) {
718 this->writeTriangleStack(triangulator.pushVertex(sk_bit_cast<SkPoint>(p3)));
719 this->writeTriangleStack(triangulator.close());
720 }
721 }
722
724 writeTriangleStack(MiddleOutPolygonTriangulator::PoppedTriangleStack&& stack) {
725 for (auto [p0, p1, p2] : stack) {
726 this->writeTriangle(p0, p1, p2);
727 }
728 }
729
730
731
733
734
735
736 wangs_formula::VectorXform fApproxTransform = {};
737
738 float fMaxScale = 1.0f;
739
740 LinearTolerances fTolerances;
741
742 PatchAllocator fPatchAllocator;
743 DeferredPatch fDeferredPatch;
744
745
747 FanPointAttrib fFanPoint;
748 StrokeAttrib fStrokeParams;
749 ColorAttrib fColor;
751
752
753
754
755 SsboIndexAttrib fSsboIndex;
756};
757
758}
759
760#undef ENABLE_IF
761#undef AI
762
763#endif
constexpr float SK_FloatInfinity
#define SkScalarCeilToInt(x)
constexpr size_t PatchStride(PatchAttribs attribs)
SINT Vec< 2 *N, T > join(const Vec< N, T > &lo, const Vec< N, T > &hi)
SIT bool all(const Vec< 1, T > &x)
SIT bool any(const Vec< 1, T > &x)
static SKVX_ALWAYS_INLINE Vec Load(const void *ptr)
SKVX_ALWAYS_INLINE void store(void *ptr) const