Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
Classes | Namespaces | Macros | Functions
PatchWriter.h File Reference
#include "include/core/SkAlphaType.h"
#include "include/core/SkScalar.h"
#include "include/core/SkTypes.h"
#include "include/private/SkColorData.h"
#include "include/private/base/SkDebug.h"
#include "include/private/base/SkFloatingPoint.h"
#include "include/private/base/SkPoint_impl.h"
#include "include/private/base/SkTemplates.h"
#include "src/base/SkUtils.h"
#include "src/base/SkVx.h"
#include "src/gpu/BufferWriter.h"
#include "src/gpu/tessellate/LinearTolerances.h"
#include "src/gpu/tessellate/MiddleOutPolygonTriangulator.h"
#include "src/gpu/tessellate/Tessellation.h"
#include "src/gpu/tessellate/WangsFormula.h"
#include <algorithm>
#include <cstdint>
#include <cstring>
#include <math.h>
#include <tuple>
#include <type_traits>
#include <utility>
#include <variant>

Go to the source code of this file.

Classes

struct  skgpu::tess::Required< A >
 
struct  skgpu::tess::Optional< A >
 
struct  skgpu::tess::TrackJoinControlPoints
 
struct  skgpu::tess::AddTrianglesWhenChopping
 
struct  skgpu::tess::DiscardFlatCurves
 
struct  skgpu::tess::ReplicateLineEndPoints
 
struct  skgpu::tess::AttribValue< A, T, Required, Optional >
 
struct  skgpu::tess::PatchStorage< Stride >
 
struct  skgpu::tess::NullTriangulator
 
class  skgpu::tess::PatchWriter< PatchAllocator, Traits >
 

Namespaces

namespace  skgpu
 
namespace  skgpu::tess
 

Macros

#define AI   SK_ALWAYS_INLINE
 
#define ENABLE_IF(cond)   template <typename Void=void> std::enable_if_t<cond, Void>
 
#define DEF_ATTRIB_TYPE(name, A, T)
 

Functions

template<PatchAttribs A, typename T , bool Required, bool Optional>
VertexWriterskgpu::tess::operator<< (VertexWriter &w, const AttribValue< A, T, Required, Optional > &v)
 

Macro Definition Documentation

◆ AI

#define AI   SK_ALWAYS_INLINE

Definition at line 203 of file PatchWriter.h.

◆ DEF_ATTRIB_TYPE

#define DEF_ATTRIB_TYPE (   name,
  A,
  T 
)
Value:
static constexpr bool kRequire##name = req_attrib<A>::value; \
static constexpr bool kOptional##name = opt_attrib<A>::value; \
using name = attrib_t<A, T, kRequire##name, kOptional##name>
const char * name
Definition fuchsia.cc:50

Definition at line 226 of file PatchWriter.h.

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 // Types that vary depending on the activated features, but do not define the patch data.
257 using DeferredPatch = std::conditional_t<kTrackJoinControlPoints,
258 PatchStorage<kMaxStride>, std::monostate>;
259 using InnerTriangulator = std::conditional_t<kAddTrianglesWhenChopping,
260 MiddleOutPolygonTriangulator, NullTriangulator>;
261
262 using float2 = skvx::float2;
263 using float4 = skvx::float4;
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> // forwarded to PatchAllocator
269 PatchWriter(PatchAttribs attribs,
270 Args&&... allocArgs)
271 : fAttribs(attribs)
272 , fPatchAllocator(PatchStride(attribs), std::forward<Args>(allocArgs)...)
273 , fJoin(attribs)
274 , fFanPoint(attribs)
275 , fStrokeParams(attribs)
276 , fColor(attribs)
277 , fDepth(attribs)
278 , fSsboIndex(attribs) {
279 // Explicit curve types are provided on the writePatch signature, and not a field of
280 // PatchWriter, so initialize one in the ctor to validate the provided runtime attribs.
281 SkDEBUGCODE((void) CurveTypeAttrib(attribs);)
282 // Validate the kWideColorIfEnabled attribute variant flag as well
283 if constexpr (req_attrib<PatchAttribs::kWideColorIfEnabled>::value) {
284 SkASSERT(attribs & PatchAttribs::kWideColorIfEnabled); // required
285 } else if constexpr (!opt_attrib<PatchAttribs::kWideColorIfEnabled>::value) {
286 SkASSERT(!(attribs & PatchAttribs::kWideColorIfEnabled)); // disabled
287 }
288 }
289
290 ~PatchWriter() {
291 if constexpr (kTrackJoinControlPoints) {
292 // flush any pending patch
293 this->writeDeferredStrokePatch();
294 }
295 }
296
297 PatchAttribs attribs() const { return fAttribs; }
298
299 // The max scale factor should be derived from the same matrix that 'xform' was. It's only used
300 // in stroking calculations, so can be ignored for path filling.
301 void setShaderTransform(const wangs_formula::VectorXform& xform,
302 float maxScale = 1.f) {
303 fApproxTransform = xform;
304 fMaxScale = maxScale;
305 }
306
307 // Completes a closed contour of a stroke by rewriting a deferred patch with now-available
308 // join control point information. Automatically resets the join control point attribute.
309 ENABLE_IF(kTrackJoinControlPoints) writeDeferredStrokePatch() {
310 if (fDeferredPatch.hasPending()) {
311 SkASSERT(!fDeferredPatch.fMustDefer);
312 // Overwrite join control point with updated value, which is the first attribute
313 // after the 4 control points.
314 memcpy(SkTAddOffset<void>(fDeferredPatch.fData, 4 * sizeof(SkPoint)),
315 &fJoin, sizeof(SkPoint));
316 // Assuming that the stroke parameters aren't changing within a contour, we only have
317 // to set the parametric segments in order to recover the LinearTolerances state at the
318 // time the deferred patch was recorded.
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 // Updates the stroke's join control point that will be written out with each patch. This is
329 // automatically adjusted when appending various geometries (e.g. Conic/Cubic), but sometimes
330 // must be set explicitly.
331 ENABLE_IF(JoinAttrib::kEnabled) updateJoinControlPointAttrib(SkPoint lastControlPoint) {
332 SkASSERT(fAttribs & PatchAttribs::kJoinControlPoint); // must be runtime enabled as well
333 fJoin = lastControlPoint;
334 if constexpr (kTrackJoinControlPoints) {
335 fDeferredPatch.fMustDefer = false;
336 }
337 }
338
339 // Updates the fan point that will be written out with each patch (i.e., the point that wedges
340 // fan around).
341 ENABLE_IF(FanPointAttrib::kEnabled) updateFanPointAttrib(SkPoint fanPoint) {
342 SkASSERT(fAttribs & PatchAttribs::kFanPoint);
343 fFanPoint = fanPoint;
344 }
345
346 // Updates the stroke params that are written out with each patch.
347 ENABLE_IF(StrokeAttrib::kEnabled) updateStrokeParamsAttrib(StrokeParams strokeParams) {
348 SkASSERT(fAttribs & PatchAttribs::kStrokeParams);
349 fStrokeParams = strokeParams;
350 fTolerances.setStroke(strokeParams, fMaxScale);
351 }
352 // Updates tolerances to account for stroke params that are stored as uniforms instead of
353 // dynamic instance attributes.
354 ENABLE_IF(StrokeAttrib::kEnabled) updateUniformStrokeParams(StrokeParams strokeParams) {
355 SkASSERT(!(fAttribs & PatchAttribs::kStrokeParams));
356 fTolerances.setStroke(strokeParams, fMaxScale);
357 }
358
359 // Updates the color that will be written out with each patch.
360 ENABLE_IF(ColorAttrib::kEnabled) updateColorAttrib(const SkPMColor4f& color) {
361 SkASSERT(fAttribs & PatchAttribs::kColor);
362 // Converts SkPMColor4f to the selected 'Color' attrib type. The always-wide and never-wide
363 // branches match what VertexColor does based on the runtime check.
364 if constexpr (req_attrib<PatchAttribs::kWideColorIfEnabled>::value) {
365 fColor = color;
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 // Updates the paint depth written out with each patch.
374 ENABLE_IF(DepthAttrib::kEnabled) updatePaintDepthAttrib(float depth) {
375 SkASSERT(fAttribs & PatchAttribs::kPaintDepth);
376 fDepth = depth;
377 }
378
379 // Updates the storage buffer index used to access uniforms.
380 ENABLE_IF(SsboIndexAttrib::kEnabled)
381 updateSsboIndexAttrib(skvx::ushort2 ssboIndex) {
382 SkASSERT(fAttribs & PatchAttribs::kSsboIndex);
383 fSsboIndex = ssboIndex;
384 }
385
386 /**
387 * writeX functions for supported patch geometry types. Every geometric type is converted to an
388 * equivalent cubic or conic, so this will always write at minimum 8 floats for the four control
389 * points (cubic) or three control points and {w, inf} (conics). The PatchWriter additionally
390 * writes the current values of all attributes enabled in its PatchAttribs flags.
391 */
392
393 // Write a cubic curve with its four control points.
394 AI void writeCubic(float2 p0, float2 p1, float2 p2, float2 p3) {
395 float n4 = wangs_formula::cubic_p4(kPrecision, p0, p1, p2, p3, fApproxTransform);
396 if constexpr (kDiscardFlatCurves) {
397 if (n4 <= 1.f) {
398 // This cubic only needs one segment (e.g. a line) but we're not filling space with
399 // fans or stroking, so nothing actually needs to be drawn.
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]) {
410 float4 p0p1 = float4::Load(pts);
411 float4 p2p3 = float4::Load(pts + 2);
412 this->writeCubic(p0p1.lo, p0p1.hi, p2p3.lo, p2p3.hi);
413 }
414
415 // Write a conic curve with three control points and 'w', with the last coord of the last
416 // control point signaling a conic by being set to infinity.
417 AI void writeConic(float2 p0, float2 p1, float2 p2, float w) {
418 float n2 = wangs_formula::conic_p2(kPrecision, p0, p1, p2, w, fApproxTransform);
419 if constexpr (kDiscardFlatCurves) {
420 if (n2 <= 1.f) {
421 // This conic only needs one segment (e.g. a line) but we're not filling space with
422 // fans or stroking, so nothing actually needs to be drawn.
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]),
436 w);
437 }
438
439 // Write a quadratic curve that automatically converts its three control points into an
440 // equivalent cubic.
441 AI void writeQuadratic(float2 p0, float2 p1, float2 p2) {
442 float n4 = wangs_formula::quadratic_p4(kPrecision, p0, p1, p2, fApproxTransform);
443 if constexpr (kDiscardFlatCurves) {
444 if (n4 <= 1.f) {
445 // This quad only needs one segment (e.g. a line) but we're not filling space with
446 // fans or stroking, so nothing actually needs to be drawn.
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 // Write a line that is automatically converted into an equivalent cubic.
463 AI void writeLine(float4 p0p1) {
464 // No chopping needed, a line only ever requires one segment (the minimum required already).
465 fTolerances.setParametricSegments(1.f);
466 if constexpr (kReplicateLineEndPoints) {
467 // Visually this cubic is still a line, but 't' does not move linearly over the line,
468 // so Wang's formula is more pessimistic. Shaders should avoid evaluating Wang's
469 // formula when a patch has control points in this arrangement.
470 this->writeCubicPatch(p0p1.lo, p0p1.lo, p0p1.hi, p0p1.hi);
471 } else {
472 // In exact math, this cubic structure should have Wang's formula return 0. Due to
473 // floating point math, this isn't always the case, so shaders need some way to restrict
474 // the number of parametric segments if Wang's formula numerically blows up.
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}); }
479 AI void writeLine(SkPoint p0, SkPoint p1) {
480 this->writeLine(sk_bit_cast<float2>(p0), sk_bit_cast<float2>(p1));
481 }
482
483 // Write a triangle by setting it to a conic with w=Inf, and using a distinct
484 // explicit curve type for when inf isn't supported in shaders.
485 AI void writeTriangle(float2 p0, float2 p1, float2 p2) {
486 // No chopping needed, the max supported segment count should always support 2 lines
487 // (which form a triangle when implicitly closed).
488 static constexpr float kTriangleSegments_p4 = 2.f * 2.f * 2.f * 2.f;
489 fTolerances.setParametricSegments(kTriangleSegments_p4);
490 this->writePatch(p0, p1, p2, {SK_FloatInfinity, SK_FloatInfinity},
491 kTriangularConicCurveType);
492 }
493 AI void writeTriangle(SkPoint p0, SkPoint p1, SkPoint p2) {
494 this->writeTriangle(sk_bit_cast<float2>(p0),
495 sk_bit_cast<float2>(p1),
496 sk_bit_cast<float2>(p2));
497 }
498
499 // Writes a circle used for round caps and joins in stroking, encoded as a cubic with
500 // identical control points and an empty join.
501 AI void writeCircle(SkPoint p) {
502 // This does not use writePatch() because it uses its own location as the join attribute
503 // value instead of fJoin and never defers.
504 fTolerances.setParametricSegments(0.f);
505 if (VertexWriter vw = fPatchAllocator.append(fTolerances)) {
506 vw << VertexWriter::Repeat<4>(p); // p0,p1,p2,p3 = p -> 4 copies
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 // NOTE: operator<< overrides automatically handle optional and disabled attribs.
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());
524 SkASSERT(PatchStride(fAttribs) <= kMaxStride);
525 // Save the computed parametric segment tolerance value so that we can pass that to
526 // the PatchAllocator when flushing the deferred patch.
527 fDeferredPatch.fN_p4 = fTolerances.numParametricSegments_p4();
528 return {fDeferredPatch.fData, PatchStride(fAttribs)};
529 }
530 }
531 return fPatchAllocator.append(fTolerances);
532 }
533
534 AI void writePatch(float2 p0, float2 p1, float2 p2, float2 p3, float explicitCurveType) {
535 if (VertexWriter vw = this->appendPatch()) {
536 // NOTE: fJoin will be undefined if we're writing to a deferred patch. If that's the
537 // case, correct data will overwrite it when the contour is closed (this is fine since a
538 // deferred patch writes to CPU memory instead of directly to the GPU buffer).
539 vw << p0 << p1 << p2 << p3;
540 this->emitPatchAttribs(std::move(vw), fJoin, explicitCurveType);
541
542 // Automatically update join control point for next patch.
543 if constexpr (kTrackJoinControlPoints) {
544 if (explicitCurveType == kCubicCurveType && any(p3 != p2)) {
545 // p2 is control point defining the tangent vector into the next patch.
546 p2.store(&fJoin);
547 } else if (any(p2 != p1)) {
548 // p1 is the control point defining the tangent vector.
549 p1.store(&fJoin);
550 } else {
551 // p0 is the control point defining the tangent vector.
552 p0.store(&fJoin);
553 }
554 fDeferredPatch.fMustDefer = false;
555 }
556 }
557 }
558
559 // Helpers that normalize curves to a generic patch, but do no other work.
560 AI void writeCubicPatch(float2 p0, float2 p1, float2 p2, float2 p3) {
561 this->writePatch(p0, p1, p2, p3, kCubicCurveType);
562 }
563 AI void writeCubicPatch(float2 p0, float4 p1p2, float2 p3) {
564 this->writeCubicPatch(p0, p1p2.lo, p1p2.hi, p3);
565 }
566 AI void writeQuadPatch(float2 p0, float2 p1, float2 p2) {
567 this->writeCubicPatch(p0, mix(float4(p0, p2), p1.xyxy(), 2/3.f), p2);
568 }
569 AI void writeConicPatch(float2 p0, float2 p1, float2 p2, float w) {
570 this->writePatch(p0, p1, p2, {w, SK_FloatInfinity}, kConicCurveType);
571 }
572
573 int accountForCurve(float n4) {
574 if (n4 <= kMaxParametricSegments_p4) {
575 // Record n^4 and return 0 to signal no chopping
576 fTolerances.setParametricSegments(n4);
577 return 0;
578 } else {
579 // Clamp to max allowed segmentation for a patch and return required number of chops
580 // to achieve visual correctness.
581 fTolerances.setParametricSegments(kMaxParametricSegments_p4);
582 return SkScalarCeilToInt(wangs_formula::root4(std::min(n4, kMaxSegmentsPerCurve_p4) /
583 kMaxParametricSegments_p4));
584 }
585 }
586
587 // This does not return b when t==1, but it otherwise seems to get better precision than
588 // "a*(1 - t) + b*t" for things like chopping cubics on exact cusp points.
589 // The responsibility falls on the caller to check that t != 1 before calling.
590 static AI float4 mix(float4 a, float4 b, float4 T) {
591 SkASSERT(all((0 <= T) & (T < 1)));
592 return (b - a)*T + a;
593 }
594
595 // Helpers that chop the curve type into 'numPatches' parametrically uniform curves. It is
596 // assumed that 'numPatches' is calculated such that the resulting curves require the maximum
597 // number of segments to draw appropriately (since the original presumably needed even more).
598 void chopAndWriteQuads(float2 p0, float2 p1, float2 p2, int numPatches) {
599 InnerTriangulator triangulator(numPatches, sk_bit_cast<SkPoint>(p0));
600 for (; numPatches >= 3; numPatches -= 2) {
601 // Chop into 3 quads.
602 float4 T = float4(1,1,2,2) / numPatches;
603 float4 ab = mix(p0.xyxy(), p1.xyxy(), T);
604 float4 bc = mix(p1.xyxy(), p2.xyxy(), T);
605 float4 abc = mix(ab, bc, T);
606 // p1 & p2 of the cubic representation of the middle quad.
607 float4 middle = mix(ab, bc, mix(T, T.zwxy(), 2/3.f));
608
609 this->writeQuadPatch(p0, ab.lo, abc.lo); // Write the 1st quad.
610 if constexpr (kAddTrianglesWhenChopping) {
611 this->writeTriangle(p0, abc.lo, abc.hi);
612 }
613 this->writeCubicPatch(abc.lo, middle, abc.hi); // Write the 2nd quad (already a cubic)
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}; // Save the 3rd quad.
618 }
619 if (numPatches == 2) {
620 // Chop into 2 quads.
621 float2 ab = (p0 + p1) * .5f;
622 float2 bc = (p1 + p2) * .5f;
623 float2 abc = (ab + bc) * .5f;
624
625 this->writeQuadPatch(p0, ab, abc); // Write the 1st quad.
626 if constexpr (kAddTrianglesWhenChopping) {
627 this->writeTriangle(p0, abc, p2);
628 }
629 this->writeQuadPatch(abc, bc, p2); // Write the 2nd quad.
630 } else {
631 SkASSERT(numPatches == 1);
632 this->writeQuadPatch(p0, p1, p2); // Write the single remaining quad.
633 }
634 if constexpr (kAddTrianglesWhenChopping) {
635 this->writeTriangleStack(triangulator.pushVertex(sk_bit_cast<SkPoint>(p2)));
636 this->writeTriangleStack(triangulator.close());
637 }
638 }
639
640 void chopAndWriteConics(float2 p0, float2 p1, float2 p2, float w, int numPatches) {
641 InnerTriangulator triangulator(numPatches, sk_bit_cast<SkPoint>(p0));
642 // Load the conic in 3d homogeneous (unprojected) space.
643 float4 h0 = float4(p0,1,1);
644 float4 h1 = float4(p1,1,1) * w;
645 float4 h2 = float4(p2,1,1);
646 for (; numPatches >= 2; --numPatches) {
647 // Chop in homogeneous space.
648 float T = 1.f/numPatches;
649 float4 ab = mix(h0, h1, T);
650 float4 bc = mix(h1, h2, T);
651 float4 abc = mix(ab, bc, T);
652
653 // Project and write the 1st conic.
654 float2 midpoint = abc.xy() / abc.w();
655 this->writeConicPatch(h0.xy() / h0.w(),
656 ab.xy() / ab.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}; // Save the 2nd conic (in homogeneous space).
663 }
664 // Project and write the remaining conic.
665 SkASSERT(numPatches == 1);
666 this->writeConicPatch(h0.xy() / h0.w(),
667 h1.xy() / h1.w(),
668 h2.xy(), // h2.w == 1
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
676 void chopAndWriteCubics(float2 p0, float2 p1, float2 p2, float2 p3, int numPatches) {
677 InnerTriangulator triangulator(numPatches, sk_bit_cast<SkPoint>(p0));
678 for (; numPatches >= 3; numPatches -= 2) {
679 // Chop into 3 cubics.
680 float4 T = float4(1,1,2,2) / numPatches;
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);
684 float4 abc = mix(ab, bc, T);
685 float4 bcd = mix(bc, cd, T);
686 float4 abcd = mix(abc, bcd, T);
687 float4 middle = mix(abc, bcd, T.zwxy()); // p1 & p2 of the middle cubic.
688
689 this->writeCubicPatch(p0, ab.lo, abc.lo, abcd.lo); // Write the 1st cubic.
690 if constexpr (kAddTrianglesWhenChopping) {
691 this->writeTriangle(p0, abcd.lo, abcd.hi);
692 }
693 this->writeCubicPatch(abcd.lo, middle, abcd.hi); // Write the 2nd cubic.
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}; // Save the 3rd cubic.
698 }
699 if (numPatches == 2) {
700 // Chop into 2 cubics.
701 float2 ab = (p0 + p1) * .5f;
702 float2 bc = (p1 + p2) * .5f;
703 float2 cd = (p2 + p3) * .5f;
704 float2 abc = (ab + bc) * .5f;
705 float2 bcd = (bc + cd) * .5f;
706 float2 abcd = (abc + bcd) * .5f;
707
708 this->writeCubicPatch(p0, ab, abc, abcd); // Write the 1st cubic.
709 if constexpr (kAddTrianglesWhenChopping) {
710 this->writeTriangle(p0, abcd, p3);
711 }
712 this->writeCubicPatch(abcd, bcd, cd, p3); // Write the 2nd cubic.
713 } else {
714 SkASSERT(numPatches == 1);
715 this->writeCubicPatch(p0, p1, p2, p3); // Write the single remaining cubic.
716 }
717 if constexpr (kAddTrianglesWhenChopping) {
718 this->writeTriangleStack(triangulator.pushVertex(sk_bit_cast<SkPoint>(p3)));
719 this->writeTriangleStack(triangulator.close());
720 }
721 }
722
723 ENABLE_IF(kAddTrianglesWhenChopping)
724 writeTriangleStack(MiddleOutPolygonTriangulator::PoppedTriangleStack&& stack) {
725 for (auto [p0, p1, p2] : stack) {
726 this->writeTriangle(p0, p1, p2);
727 }
728 }
729
730 // Runtime configuration, will always contain required attribs but may not have all optional
731 // attribs enabled (e.g. depending on caps or batching).
732 const PatchAttribs fAttribs;
733
734 // The 2x2 approximation of the local-to-device transform that will affect subsequently
735 // recorded curves (when fully transformed in the vertex shader).
736 wangs_formula::VectorXform fApproxTransform = {};
737 // A maximum scale factor extracted from the current approximate transform.
738 float fMaxScale = 1.0f;
739 // Tracks the linear tolerances for the most recently written patches.
740 LinearTolerances fTolerances;
741
742 PatchAllocator fPatchAllocator;
743 DeferredPatch fDeferredPatch; // only usable if kTrackJoinControlPoints is true
744
745 // Instance attribute state written after the 4 control points of a patch
746 JoinAttrib fJoin;
747 FanPointAttrib fFanPoint;
748 StrokeAttrib fStrokeParams;
749 ColorAttrib fColor;
750 DepthAttrib fDepth;
751
752 // Index into a shared storage buffer containing this PatchWriter's patches' corresponding
753 // uniforms. Written out as an attribute with every patch, to read the appropriate uniform
754 // values from the storage buffer on draw.
755 SsboIndexAttrib fSsboIndex;
756};
757
758} // namespace skgpu::tess
759
760#undef ENABLE_IF
761#undef AI
762
763#endif // skgpu_tessellate_PatchWriter_DEFINED
SkPaint::Join fJoin
skvx::float4 float4
SkColor4f color
#define ENABLE_IF(cond)
#define AI
#define SkASSERT(cond)
Definition SkAssert.h:116
#define SkDEBUGCODE(...)
Definition SkDebug.h:23
constexpr float SK_FloatInfinity
#define SkScalarCeilToInt(x)
Definition SkScalar.h:36
static bool b
struct MyStruct a[10]
Definition ab.py:1
constexpr size_t PatchStride(PatchAttribs attribs)
Vec< 4, float > float4
Definition SkVx.h:1146
SINT Vec< 2 *N, T > join(const Vec< N, T > &lo, const Vec< N, T > &hi)
Definition SkVx.h:242
SIT bool all(const Vec< 1, T > &x)
Definition SkVx.h:582
Vec< 2, float > float2
Definition SkVx.h:1145
SIT bool any(const Vec< 1, T > &x)
Definition SkVx.h:530
Definition ref_ptr.h:256
SkScalar w
#define T
static SKVX_ALWAYS_INLINE Vec Load(const void *ptr)
Definition SkVx.h:109
SKVX_ALWAYS_INLINE void store(void *ptr) const
Definition SkVx.h:112
Vec< N/2, T > hi
Definition SkVx.h:117
Vec< N/2, T > lo
Definition SkVx.h:117

◆ ENABLE_IF

#define ENABLE_IF (   cond)    template <typename Void=void> std::enable_if_t<cond, Void>

Definition at line 204 of file PatchWriter.h.