Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
Public Member Functions | List of all members
skgpu::graphite::AnalyticRRectRenderStep Class Referencefinal

#include <AnalyticRRectRenderStep.h>

Inheritance diagram for skgpu::graphite::AnalyticRRectRenderStep:
skgpu::graphite::RenderStep

Public Member Functions

 AnalyticRRectRenderStep (StaticBufferManager *bufferManager)
 
 ~AnalyticRRectRenderStep () override
 
std::string vertexSkSL () const override
 
const char * fragmentCoverageSkSL () const override
 
void writeVertices (DrawWriter *, const DrawParams &, skvx::ushort2 ssboIndices) const override
 
void writeUniformsAndTextures (const DrawParams &, PipelineDataGatherer *) const override
 
- Public Member Functions inherited from skgpu::graphite::RenderStep
virtual ~RenderStep ()=default
 
virtual std::string texturesAndSamplersSkSL (const ResourceBindingRequirements &, int *nextBindingIndex) const
 
virtual const char * fragmentColorSkSL () const
 
uint32_t uniqueID () const
 
const char * name () const
 
bool requiresMSAA () const
 
bool performsShading () const
 
bool hasTextures () const
 
bool emitsPrimitiveColor () const
 
bool outsetBoundsForAA () const
 
Coverage coverage () const
 
PrimitiveType primitiveType () const
 
size_t vertexStride () const
 
size_t instanceStride () const
 
size_t numUniforms () const
 
size_t numVertexAttributes () const
 
size_t numInstanceAttributes () const
 
SkSpan< const Uniformuniforms () const
 
SkSpan< const AttributevertexAttributes () const
 
SkSpan< const AttributeinstanceAttributes () const
 
SkSpan< const Varyingvaryings () const
 
const DepthStencilSettingsdepthStencilSettings () const
 
SkEnumBitMask< DepthStencilFlagsdepthStencilFlags () const
 

Additional Inherited Members

- Static Public Member Functions inherited from skgpu::graphite::RenderStep
static const char * ssboIndicesAttribute ()
 
static const char * ssboIndicesVarying ()
 
- Protected Types inherited from skgpu::graphite::RenderStep
enum class  Flags : unsigned {
  kNone = 0b0000000 , kRequiresMSAA = 0b0000001 , kPerformsShading = 0b0000010 , kHasTextures = 0b0000100 ,
  kEmitsCoverage = 0b0001000 , kLCDCoverage = 0b0010000 , kEmitsPrimitiveColor = 0b0100000 , kOutsetBoundsForAA = 0b1000000
}
 
- Protected Member Functions inherited from skgpu::graphite::RenderStep
 RenderStep (std::string_view className, std::string_view variantName, SkEnumBitMask< Flags > flags, std::initializer_list< Uniform > uniforms, PrimitiveType primitiveType, DepthStencilSettings depthStencilSettings, SkSpan< const Attribute > vertexAttrs, SkSpan< const Attribute > instanceAttrs, SkSpan< const Varying > varyings={})
 

Detailed Description

Definition at line 17 of file AnalyticRRectRenderStep.h.

Constructor & Destructor Documentation

◆ AnalyticRRectRenderStep()

skgpu::graphite::AnalyticRRectRenderStep::AnalyticRRectRenderStep ( StaticBufferManager bufferManager)

Definition at line 340 of file AnalyticRRectRenderStep.cpp.

341 : RenderStep("AnalyticRRectRenderStep",
342 "",
344 /*uniforms=*/{},
347 /*vertexAttrs=*/{
350 // TODO: These values are all +1/0/-1, or +1/0, so could be packed
351 // much more densely than as three floats.
354 },
355 /*instanceAttrs=*/
356 {{"xRadiiOrFlags", VertexAttribType::kFloat4, SkSLType::kFloat4},
359 // XY stores center of rrect in local coords. Z and W store values to
360 // control interior fill behavior. Z can be -1, 0, or 1:
361 // -1: A stroked interior where AA insets overlap, but isn't solid.
362 // 0: A stroked interior with no complications.
363 // 1: A solid interior (fill or sufficiently large stroke width).
364 // W specifies the size of the AA inset if it's >= 0, or signals that
365 // the inner curves intersect in a complex manner (rare).
367
368 // TODO: pack depth and ssbo index into one 32-bit attribute, if we can
369 // go without needing both render step and paint ssbo index attributes.
372
376 /*varyings=*/{
377 // TODO: If the inverse transform is part of the draw's SSBO, we can
378 // reconstruct the Jacobian in the fragment shader using the existing
379 // local coordinates varying
380 {"jacobian", SkSLType::kFloat4}, // float2x2
381 // Distance to LTRB edges of unstroked shape. Depending on
382 // 'perPixelControl' these will either be local or device-space values.
383 {"edgeDistances", SkSLType::kFloat4}, // distance to LTRB edges
384 // TODO: These are constant for all fragments for a given instance,
385 // could we store them in the draw's SSBO?
386 {"xRadii", SkSLType::kFloat4},
387 {"yRadii", SkSLType::kFloat4},
388 // Matches the StrokeStyle struct (X is radius, Y < 0 is round join,
389 // Y = 0 is bevel, Y > 0 is miter join).
390 // TODO: These could easily be considered part of the draw's uniforms.
391 {"strokeParams", SkSLType::kFloat2},
392 // 'perPixelControl' is a tightly packed description of how to
393 // evaluate the possible edges that influence coverage in a pixel.
394 // The decision points and encoded values are spread across X and Y
395 // so that they are consistent regardless of whether or not MSAA is
396 // used and does not require centroid sampling.
397 //
398 // The signs of values are used to determine the type of coverage to
399 // calculate in the fragment shader and depending on the state, extra
400 // varying state is encoded in the fields:
401 // - A positive X value overrides all per-pixel coverage calculations
402 // and sets the pixel to full coverage. Y is ignored in this case.
403 // - A zero X value represents a solid interior shape.
404 // - X much less than 0 represents bidirectional coverage for a
405 // stroke, using a sufficiently negative value to avoid
406 // extrapolation from fill triangles. For actual shapes with
407 // bidirectional coverage, the fill triangles are zero area.
408 //
409 // - Y much greater than 0 takes precedence over the latter two X
410 // rules and signals that 'edgeDistances' holds device-space values
411 // and does not require additional per-pixel calculations. The
412 // coverage scale is encoded as (1+scale*w) and the bias is
413 // reconstructed from that. X is always 0 for non-fill triangles
414 // since device-space edge distance is only used for solid interiors
415 // - Otherwise, any negative Y value represents an additional
416 // reduction in coverage due to a device-space outset. It is clamped
417 // below 0 to avoid adding coverage from extrapolation.
418 {"perPixelControl", SkSLType::kFloat2},
419 }) {
420 // Initialize the static buffers we'll use when recording draw calls.
421 // NOTE: Each instance of this RenderStep gets its own copy of the data. Since there should only
422 // ever be one AnalyticRRectRenderStep at a time, this shouldn't be an issue.
423 write_vertex_buffer(bufferManager->getVertexWriter(sizeof(Vertex) * kVertexCount,
424 &fVertexBuffer));
425 write_index_buffer(bufferManager->getIndexWriter(sizeof(uint16_t) * kIndexCount,
426 &fIndexBuffer));
427}
RenderStep(std::string_view className, std::string_view variantName, SkEnumBitMask< Flags > flags, std::initializer_list< Uniform > uniforms, PrimitiveType primitiveType, DepthStencilSettings depthStencilSettings, SkSpan< const Attribute > vertexAttrs, SkSpan< const Attribute > instanceAttrs, SkSpan< const Varying > varyings={})
Definition Renderer.cpp:19
static void write_index_buffer(VertexWriter writer)
static constexpr DepthStencilSettings kDirectDepthGreaterPass
static constexpr int kIndexCount
static constexpr int kVertexCount
static void write_vertex_buffer(VertexWriter writer)

◆ ~AnalyticRRectRenderStep()

skgpu::graphite::AnalyticRRectRenderStep::~AnalyticRRectRenderStep ( )
override

Definition at line 429 of file AnalyticRRectRenderStep.cpp.

429{}

Member Function Documentation

◆ fragmentCoverageSkSL()

const char * skgpu::graphite::AnalyticRRectRenderStep::fragmentCoverageSkSL ( ) const
overridevirtual

Reimplemented from skgpu::graphite::RenderStep.

Definition at line 446 of file AnalyticRRectRenderStep.cpp.

446 {
447 // The returned SkSL must write its coverage into a 'half4 outputCoverage' variable (defined in
448 // the calling code) with the actual coverage splatted out into all four channels.
449 return "outputCoverage = analytic_rrect_coverage_fn(sk_FragCoord, "
450 "jacobian, "
451 "edgeDistances, "
452 "xRadii, "
453 "yRadii, "
454 "strokeParams, "
455 "perPixelControl);";
456}

◆ vertexSkSL()

std::string skgpu::graphite::AnalyticRRectRenderStep::vertexSkSL ( ) const
overridevirtual

Implements skgpu::graphite::RenderStep.

Definition at line 431 of file AnalyticRRectRenderStep.cpp.

431 {
432 // Returns the body of a vertex function, which must define a float4 devPosition variable and
433 // must write to an already-defined float2 stepLocalCoords variable.
434 return "float4 devPosition = analytic_rrect_vertex_fn("
435 // Vertex Attributes
436 "position, normal, normalScale, centerWeight, "
437 // Instance Attributes
438 "xRadiiOrFlags, radiiOrQuadXs, ltrbOrQuadYs, center, depth, "
439 "float3x3(mat0, mat1, mat2), "
440 // Varyings
441 "jacobian, edgeDistances, xRadii, yRadii, strokeParams, perPixelControl, "
442 // Render Step
443 "stepLocalCoords);\n";
444}

◆ writeUniformsAndTextures()

void skgpu::graphite::AnalyticRRectRenderStep::writeUniformsAndTextures ( const DrawParams ,
PipelineDataGatherer  
) const
overridevirtual

Implements skgpu::graphite::RenderStep.

Definition at line 613 of file AnalyticRRectRenderStep.cpp.

614 {
615 // All data is uploaded as instance attributes, so no uniforms are needed.
616}

◆ writeVertices()

void skgpu::graphite::AnalyticRRectRenderStep::writeVertices ( DrawWriter writer,
const DrawParams params,
skvx::ushort2  ssboIndices 
) const
overridevirtual

Implements skgpu::graphite::RenderStep.

Definition at line 458 of file AnalyticRRectRenderStep.cpp.

460 {
461 SkASSERT(params.geometry().isShape() || params.geometry().isEdgeAAQuad());
462
463 DrawWriter::Instances instance{*writer, fVertexBuffer, fIndexBuffer, kIndexCount};
464 auto vw = instance.append(1);
465
466 // The bounds of a rect is the rect, and the bounds of a rrect is tight (== SkRRect::getRect()).
467 Rect bounds = params.geometry().bounds();
468
469 // aaRadius will be set to a negative value to signal a complex self-intersection that has to
470 // be calculated in the vertex shader.
471 float aaRadius = params.transform().localAARadius(bounds);
472 float strokeInset = 0.f;
473 float centerWeight = kSolidInterior;
474
475 if (params.isStroke()) {
476 // EdgeAAQuads are not stroked so we know it's a Shape, but we support rects, rrects, and
477 // lines that all need to be converted to the same form.
478 const Shape& shape = params.geometry().shape();
479
480 SkASSERT(params.strokeStyle().halfWidth() >= 0.f);
481 SkASSERT(shape.isRect() || shape.isLine() || params.strokeStyle().halfWidth() == 0.f ||
482 (shape.isRRect() && SkRRectPriv::AllCornersCircular(shape.rrect())));
483
484 float strokeRadius = params.strokeStyle().halfWidth();
485
486 skvx::float2 size = shape.isLine() ? skvx::float2(length(shape.p1() - shape.p0()), 0.f)
487 : bounds.size(); // rect or [r]rect
488
489 skvx::float2 innerGap = size - 2.f * params.strokeStyle().halfWidth();
490 if (any(innerGap <= 0.f) && strokeRadius > 0.f) {
491 // AA inset intersections are measured from the *outset* and remain marked as "solid"
492 strokeInset = -strokeRadius;
493 } else {
494 // This will be upgraded to kFilledStrokeInterior if insets intersect
495 centerWeight = kStrokeInterior;
496 strokeInset = strokeRadius;
497 }
498
499 skvx::float4 xRadii = shape.isRRect() ? load_x_radii(shape.rrect()) : skvx::float4(0.f);
500 if (strokeRadius > 0.f || shape.isLine()) {
501 // Regular strokes only need to upload 4 corner radii; hairline lines can be uploaded in
502 // the same manner since it has no real corner radii.
503 float joinStyle = params.strokeStyle().joinLimit();
504 float lineFlag = shape.isLine() ? 1.f : 0.f;
505 auto empty = size == 0.f;
506
507 // Points and lines produce caps instead of joins. However, the capped geometry is
508 // visually equivalent to a joined, stroked [r]rect of the paired join style.
509 if (shape.isLine() || all(empty)) {
510 // However, butt-cap points are defined not to produce any geometry, so that combo
511 // should have been rejected earlier.
512 SkASSERT(shape.isLine() || params.strokeStyle().cap() != SkPaint::kButt_Cap);
513 switch(params.strokeStyle().cap()) {
514 case SkPaint::kRound_Cap: joinStyle = -1.f; break; // round cap == round join
515 case SkPaint::kButt_Cap: joinStyle = 0.f; break; // butt cap == bevel join
516 case SkPaint::kSquare_Cap: joinStyle = 1.f; break; // square cap == miter join
517 }
518 } else if (params.strokeStyle().isMiterJoin()) {
519 // Normal corners are 90-degrees so become beveled if the miter limit is < sqrt(2).
520 // If the [r]rect has a width or height of 0, the corners are actually 180-degrees,
521 // so the must always be beveled (or, equivalently, butt-capped).
522 if (params.strokeStyle().miterLimit() < SK_ScalarSqrt2 || any(empty)) {
523 joinStyle = 0.f; // == bevel (or butt if width or height are zero)
524 } else {
525 // Discard actual miter limit because a 90-degree corner never exceeds it.
526 joinStyle = 1.f;
527 }
528 } // else no join style correction needed for non-empty geometry or round joins
529
530 // Write a negative value outside [-1, 0] to signal a stroked shape, the line flag, then
531 // the style params, followed by corner radii and coords.
532 vw << -2.f << lineFlag << strokeRadius << joinStyle << xRadii
533 << (shape.isLine() ? shape.line() : bounds.ltrb());
534 } else {
535 // Write -2 - cornerRadii to encode the X radii in such a way to trigger stroking but
536 // guarantee the 2nd field is non-zero to signal hairline. Then we upload Y radii as
537 // well to allow for elliptical hairlines.
538 skvx::float4 yRadii = shape.isRRect() ? load_y_radii(shape.rrect()) : skvx::float4(0.f);
539 vw << (-2.f - xRadii) << yRadii << bounds.ltrb();
540 }
541 } else {
542 // Empty fills should not have been recorded at all.
543 SkASSERT(!bounds.isEmptyNegativeOrNaN());
544
545 if (params.geometry().isEdgeAAQuad()) {
546 // NOTE: If quad.isRect() && quad.edgeFlags() == kAll, the written data is identical to
547 // Shape.isRect() case below.
548 const EdgeAAQuad& quad = params.geometry().edgeAAQuad();
549
550 // If all edges are non-AA, set localAARadius to 0 so that the fill triangles cover the
551 // entire shape. Otherwise leave it as-is for the full AA rect case; in the event it's
552 // mixed-AA or a quad, it'll be converted to complex insets down below.
553 if (quad.edgeFlags() == EdgeAAQuad::Flags::kNone) {
554 aaRadius = 0.f;
555 }
556
557 // -1 for AA on, 0 for AA off
558 auto edgeSigns = skvx::float4{quad.edgeFlags() & AAFlags::kLeft ? -1.f : 0.f,
559 quad.edgeFlags() & AAFlags::kTop ? -1.f : 0.f,
560 quad.edgeFlags() & AAFlags::kRight ? -1.f : 0.f,
561 quad.edgeFlags() & AAFlags::kBottom ? -1.f : 0.f};
562
563 // The vertex shader expects points to be in clockwise order. EdgeAAQuad is the only
564 // shape that *might* have counter-clockwise input.
565 if (is_clockwise(quad)) {
566 vw << edgeSigns << quad.xs() << quad.ys();
567 } else {
568 vw << skvx::shuffle<2,1,0,3>(edgeSigns) // swap left and right AA bits
569 << skvx::shuffle<1,0,3,2>(quad.xs()) // swap TL with TR, and BL with BR
570 << skvx::shuffle<1,0,3,2>(quad.ys()); // ""
571 }
572 } else {
573 const Shape& shape = params.geometry().shape();
574 // Filled lines are empty by definition, so they shouldn't have been recorded
575 SkASSERT(!shape.isLine());
576
577 if (shape.isRect() || (shape.isRRect() && shape.rrect().isRect())) {
578 // Rectangles (or rectangles embedded in an SkRRect) are converted to the
579 // quadrilateral case, but with all edges anti-aliased (== -1).
580 skvx::float4 ltrb = bounds.ltrb();
581 vw << /*edge flags*/ skvx::float4(-1.f)
582 << /*xs*/ skvx::shuffle<0,2,2,0>(ltrb)
583 << /*ys*/ skvx::shuffle<1,1,3,3>(ltrb);
584 } else {
585 // A filled rounded rectangle, so make sure at least one corner radii > 0 or the
586 // shader won't detect it as a rounded rect.
587 SkASSERT(any(load_x_radii(shape.rrect()) > 0.f));
588
589 vw << load_x_radii(shape.rrect()) << load_y_radii(shape.rrect()) << bounds.ltrb();
590 }
591 }
592 }
593
594 if (opposite_insets_intersect(params.geometry(), strokeInset, aaRadius)) {
595 aaRadius = kComplexAAInsets;
596 if (centerWeight == kStrokeInterior) {
597 centerWeight = kFilledStrokeInterior;
598 }
599 }
600
601 // All instance types share the remaining instance attribute definitions
602 const SkM44& m = params.transform().matrix();
603 auto center = params.geometry().isEdgeAAQuad() ? quad_center(params.geometry().edgeAAQuad())
604 : bounds.center();
605 vw << center << centerWeight << aaRadius
606 << params.order().depthAsFloat()
607 << ssboIndices
608 << m.rc(0,0) << m.rc(1,0) << m.rc(3,0) // mat0
609 << m.rc(0,1) << m.rc(1,1) << m.rc(3,1) // mat1
610 << m.rc(0,3) << m.rc(1,3) << m.rc(3,3); // mat2
611}
#define SkASSERT(cond)
Definition SkAssert.h:116
#define SK_ScalarSqrt2
Definition SkScalar.h:20
static SkScalar center(float pos0, float pos1)
Shape
Definition SkM44.h:150
@ kRound_Cap
adds circle
Definition SkPaint.h:335
@ kButt_Cap
no stroke extension
Definition SkPaint.h:334
@ kSquare_Cap
adds square
Definition SkPaint.h:336
static bool AllCornersCircular(const SkRRect &rr, SkScalar tolerance=SK_ScalarNearlyZero)
Definition SkRRect.cpp:353
const EmbeddedViewParams * params
VkInstance instance
Definition main.cc:48
EMSCRIPTEN_KEEPALIVE void empty()
size_t length
Optional< SkRect > bounds
Definition SkRecords.h:189
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition switches.h:259
TRect< Scalar > Rect
Definition rect.h:746
static bool opposite_insets_intersect(const SkRRect &rrect, float strokeRadius, float aaRadius)
static constexpr float kComplexAAInsets
static constexpr float kFilledStrokeInterior
static bool is_clockwise(const EdgeAAQuad &quad)
static skvx::float4 load_x_radii(const SkRRect &rrect)
static constexpr float kSolidInterior
static constexpr float kStrokeInterior
static skvx::float4 load_y_radii(const SkRRect &rrect)
static skvx::float2 quad_center(const EdgeAAQuad &quad)
Definition SkVx.h:73
Vec< 4, float > float4
Definition SkVx.h:1146
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

The documentation for this class was generated from the following files: