Flutter Engine
The Flutter Engine
Public Member Functions | List of all members
skgpu::graphite::CoverageMaskRenderStep Class Referencefinal

#include <CoverageMaskRenderStep.h>

Inheritance diagram for skgpu::graphite::CoverageMaskRenderStep:
skgpu::graphite::RenderStep

Public Member Functions

 CoverageMaskRenderStep ()
 
 ~CoverageMaskRenderStep () override=default
 
std::string vertexSkSL () const override
 
std::string texturesAndSamplersSkSL (const ResourceBindingRequirements &, int *nextBindingIndex) 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 void writeVertices (DrawWriter *, const DrawParams &, skvx::ushort2 ssboIndices) const =0
 
virtual void writeUniformsAndTextures (const DrawParams &, PipelineDataGatherer *) const =0
 
virtual std::string vertexSkSL () const =0
 
virtual std::string texturesAndSamplersSkSL (const ResourceBindingRequirements &, int *nextBindingIndex) const
 
virtual const char * fragmentCoverageSkSL () 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 15 of file CoverageMaskRenderStep.h.

Constructor & Destructor Documentation

◆ CoverageMaskRenderStep()

skgpu::graphite::CoverageMaskRenderStep::CoverageMaskRenderStep ( )

Definition at line 38 of file CoverageMaskRenderStep.cpp.

39 : RenderStep("CoverageMaskRenderStep",
40 "",
41 // The mask will have AA outsets baked in, but the original bounds for clipping
42 // still require the outset for analytic coverage.
45 /*uniforms=*/{{"maskToDeviceRemainder", SkSLType::kFloat3x3}},
48 /*vertexAttrs=*/{},
49 /*instanceAttrs=*/
50 // Draw bounds and mask bounds are in normalized relative to the mask texture,
51 // but 'drawBounds' is stored in float since the coords may map outside of
52 // [0,1] for inverse-filled masks. 'drawBounds' is relative to the logical mask
53 // entry's origin, while 'maskBoundsIn' is atlas-relative. Inverse fills swap
54 // the order in 'maskBoundsIn' to be RBLT.
55 {{"drawBounds", VertexAttribType::kFloat4 , SkSLType::kFloat4}, // ltrb
57 // Remaining translation extracted from actual 'maskToDevice' transform.
61 // deviceToLocal matrix for producing local coords for shader evaluation
65 /*varyings=*/
66 {// `maskBounds` are the atlas-relative, sorted bounds of the coverage mask.
67 // `textureCoords` are the atlas-relative UV coordinates of the draw, which
68 // can spill beyond `maskBounds` for inverse fills.
69 // TODO: maskBounds is constant for all fragments for a given instance,
70 // could we store them in the draw's SSBO?
71 {"maskBounds" , SkSLType::kFloat4},
72 {"textureCoords", SkSLType::kFloat2},
73 // 'invert' is set to 0 use unmodified coverage, and set to 1 for "1-c".
74 {"invert", SkSLType::kHalf}}) {}
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 constexpr DepthStencilSettings kDirectDepthGreaterPass

◆ ~CoverageMaskRenderStep()

skgpu::graphite::CoverageMaskRenderStep::~CoverageMaskRenderStep ( )
overridedefault

Member Function Documentation

◆ fragmentCoverageSkSL()

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

Reimplemented from skgpu::graphite::RenderStep.

Definition at line 91 of file CoverageMaskRenderStep.cpp.

91 {
92 return R"(
93 half c = sample(pathAtlas, clamp(textureCoords, maskBounds.LT, maskBounds.RB)).r;
94 outputCoverage = half4(mix(c, 1 - c, invert));
95 )";
96}

◆ texturesAndSamplersSkSL()

std::string skgpu::graphite::CoverageMaskRenderStep::texturesAndSamplersSkSL ( const ResourceBindingRequirements bindingReqs,
int nextBindingIndex 
) const
overridevirtual

Reimplemented from skgpu::graphite::RenderStep.

Definition at line 86 of file CoverageMaskRenderStep.cpp.

87 {
88 return EmitSamplerLayout(bindingReqs, nextBindingIndex) + " sampler2D pathAtlas;";
89}
std::string EmitSamplerLayout(const ResourceBindingRequirements &bindingReqs, int *binding)

◆ vertexSkSL()

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

Implements skgpu::graphite::RenderStep.

Definition at line 76 of file CoverageMaskRenderStep.cpp.

76 {
77 // Returns the body of a vertex function, which must define a float4 devPosition variable and
78 // must write to an already-defined float2 stepLocalCoords variable.
79 return "float4 devPosition = coverage_mask_vertex_fn("
80 "float2(sk_VertexID >> 1, sk_VertexID & 1), "
81 "maskToDeviceRemainder, drawBounds, maskBoundsIn, deviceOrigin, "
82 "depth, float3x3(mat0, mat1, mat2), "
83 "maskBounds, textureCoords, invert, stepLocalCoords);\n";
84}

◆ writeUniformsAndTextures()

void skgpu::graphite::CoverageMaskRenderStep::writeUniformsAndTextures ( const DrawParams params,
PipelineDataGatherer gatherer 
) const
overridevirtual

Implements skgpu::graphite::RenderStep.

Definition at line 186 of file CoverageMaskRenderStep.cpp.

187 {
188 SkDEBUGCODE(UniformExpectationsValidator uev(gatherer, this->uniforms());)
189
190 const CoverageMaskShape& coverageMask = params.geometry().coverageMaskShape();
191 const TextureProxy* proxy = coverageMask.textureProxy();
192 SkASSERT(proxy);
193
194 // Most coverage masks are aligned with the device pixels, so the params' transform is an
195 // integer translation matrix. This translation is extracted as an instance attribute so that
196 // the remaining transform has a much lower frequency of changing (only complex-transformed
197 // mask filters).
198 skvx::float2 deviceOrigin = get_device_translation(params.transform().matrix());
199 SkMatrix maskToDevice = params.transform().matrix().asM33();
200 maskToDevice.preTranslate(-deviceOrigin.x(), -deviceOrigin.y());
201
202 // The mask coordinates in the vertex shader will be normalized, so scale by the proxy size
203 // to get back to Skia's texel-based coords.
204 maskToDevice.preScale(proxy->dimensions().width(), proxy->dimensions().height());
205
206 // Write uniforms:
207 gatherer->write(maskToDevice);
208
209 // Write textures and samplers:
210 const bool pixelAligned =
211 params.transform().type() <= Transform::Type::kSimpleRectStaysRect &&
212 params.transform().maxScaleFactor() == 1.f &&
213 all(deviceOrigin == floor(deviceOrigin + SK_ScalarNearlyZero));
214 constexpr SkTileMode kTileModes[2] = {SkTileMode::kClamp, SkTileMode::kClamp};
215 gatherer->add(sk_ref_sp(proxy),
216 {pixelAligned ? SkFilterMode::kNearest : SkFilterMode::kLinear, kTileModes});
217}
#define SkASSERT(cond)
Definition: SkAssert.h:116
sk_sp< T > sk_ref_sp(T *obj)
Definition: SkRefCnt.h:381
#define SK_ScalarNearlyZero
Definition: SkScalar.h:99
SkDEBUGCODE(SK_SPI) SkThreadID SkGetThreadID()
SkTileMode
Definition: SkTileMode.h:13
SkMatrix & preTranslate(SkScalar dx, SkScalar dy)
Definition: SkMatrix.cpp:263
SkMatrix & preScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py)
Definition: SkMatrix.cpp:315
SkSpan< const Uniform > uniforms() const
Definition: Renderer.h:143
const EmbeddedViewParams * params
static skvx::float2 get_device_translation(const SkM44 &localToDevice)
SIT bool all(const Vec< 1, T > &x)
Definition: SkVx.h:582
SIN Vec< N, float > floor(const Vec< N, float > &x)
Definition: SkVx.h:703
Definition: SkVx.h:83

◆ writeVertices()

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

Implements skgpu::graphite::RenderStep.

Definition at line 98 of file CoverageMaskRenderStep.cpp.

100 {
101 const CoverageMaskShape& coverageMask = params.geometry().coverageMaskShape();
102 const TextureProxy* proxy = coverageMask.textureProxy();
103 SkASSERT(proxy);
104
105 // A quad is a 4-vertex instance. The coordinates are derived from the vertex IDs.
106 DrawWriter::Instances instances(*dw, {}, {}, 4);
107
108 // The device origin is the translation extracted from the mask-to-device matrix so
109 // that the remaining matrix uniform has less variance between draws.
110 const auto& maskToDevice = params.transform().matrix();
111 skvx::float2 deviceOrigin = get_device_translation(maskToDevice);
112
113 // Relative to mask space (device origin and mask-to-device remainder must be applied in shader)
114 skvx::float4 maskBounds = coverageMask.bounds().ltrb();
115 skvx::float4 drawBounds;
116
117 if (coverageMask.inverted()) {
118 // Only mask filters trigger complex transforms, and they are never inverse filled. Since
119 // we know this is an inverted mask, then we can exactly map the draw's clip bounds to mask
120 // space so that the clip is still fully covered without branching in the vertex shader.
121 SkASSERT(maskToDevice == SkM44::Translate(deviceOrigin.x(), deviceOrigin.y()));
122 drawBounds = params.clip().drawBounds().makeOffset(-deviceOrigin).ltrb();
123
124 // If the mask is fully clipped out, then the shape's mask info should be (0,0,0,0).
125 // If it's not fully clipped out, then the mask info should be non-empty.
126 SkASSERT(!params.clip().transformedShapeBounds().isEmptyNegativeOrNaN() ^
127 all(maskBounds == 0.f));
128
129 if (params.clip().transformedShapeBounds().isEmptyNegativeOrNaN()) {
130 // The inversion check is strict inequality, so (0,0,0,0) would not be detected. Adjust
131 // to (0,0,1/2,1/2) to restrict sampling to the top-left quarter of the top-left pixel,
132 // which should have a value of 0 regardless of filtering mode.
133 maskBounds = skvx::float4{0.f, 0.f, 0.5f, 0.5f};
134 } else {
135 // Add 1/2px outset to the mask bounds so that clamped coordinates sample the texel
136 // center of the padding around the atlas entry.
137 maskBounds += skvx::float4{-0.5f, -0.5f, 0.5f, 0.5f};
138 }
139
140 // and store RBLT so that the 'maskBoundsIn' attribute has xy > zw to detect inverse fill.
141 maskBounds = skvx::shuffle<2,3,0,1>(maskBounds);
142 } else {
143 // If we aren't inverted, then the originally assigned values don't need to be adjusted, but
144 // also ensure the mask isn't empty (otherwise the draw should have been skipped earlier).
145 SkASSERT(!coverageMask.bounds().isEmptyNegativeOrNaN());
146 SkASSERT(all(maskBounds.xy() < maskBounds.zw()));
147
148 // Since the mask bounds and draw bounds are 1-to-1 with each other, the clamping of texture
149 // coords is mostly a formality. We inset the mask bounds by 1/2px so that we clamp to the
150 // texel center of the outer row/column of the mask. This should be a no-op for nearest
151 // sampling but prevents any linear sampling from incorporating adjacent data; for atlases
152 // this would just be 0 but for non-atlas coverage masks that might not have padding this
153 // avoids filtering unknown values in an approx-fit texture.
154 drawBounds = maskBounds;
155 maskBounds -= skvx::float4{-0.5f, -0.5f, 0.5f, 0.5f};
156 }
157
158 // Move 'drawBounds' and 'maskBounds' into the atlas coordinate space, then adjust the
159 // device translation to undo the atlas origin automatically in the vertex shader.
160 skvx::float2 textureOrigin = skvx::cast<float>(coverageMask.textureOrigin());
161 maskBounds += textureOrigin.xyxy();
162 drawBounds += textureOrigin.xyxy();
163 deviceOrigin -= textureOrigin;
164
165 // Normalize drawBounds and maskBounds after possibly correcting drawBounds for inverse fills.
166 // The maskToDevice matrix uniform will handle de-normalizing drawBounds for vertex positions.
167 auto atlasSizeInv = skvx::float2{1.f / proxy->dimensions().width(),
168 1.f / proxy->dimensions().height()};
169 drawBounds *= atlasSizeInv.xyxy();
170 maskBounds *= atlasSizeInv.xyxy();
171 deviceOrigin *= atlasSizeInv;
172
173 // Since the mask bounds define normalized texels of the texture, we can encode them as
174 // ushort_norm without losing precision to save space.
175 SkASSERT(all((maskBounds >= 0.f) & (maskBounds <= 1.f)));
176 maskBounds = 65535.f * maskBounds + 0.5f;
177
178 const SkM44& m = coverageMask.deviceToLocal();
179 instances.append(1) << drawBounds << skvx::cast<uint16_t>(maskBounds) << deviceOrigin
180 << params.order().depthAsFloat() << ssboIndices
181 << m.rc(0,0) << m.rc(1,0) << m.rc(3,0) // mat0
182 << m.rc(0,1) << m.rc(1,1) << m.rc(3,1) // mat1
183 << m.rc(0,3) << m.rc(1,3) << m.rc(3,3); // mat2
184}
Definition: SkM44.h:150
static SkM44 Translate(SkScalar x, SkScalar y, SkScalar z=0)
Definition: SkM44.h:225

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