Flutter Engine
The Flutter Engine
AALinearizingConvexPathRenderer.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2015 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
9
11#include "src/core/SkGeometry.h"
12#include "src/core/SkPathPriv.h"
30
31using namespace skia_private;
32
33///////////////////////////////////////////////////////////////////////////////
34namespace skgpu::ganesh {
35
36namespace {
37
38static const int DEFAULT_BUFFER_SIZE = 100;
39
40// The thicker the stroke, the harder it is to produce high-quality results using tessellation. For
41// the time being, we simply drop back to software rendering above this stroke width.
42static const SkScalar kMaxStrokeWidth = 20.0;
43
44// extract the result vertices and indices from the GrAAConvexTessellator
45void extract_verts(const GrAAConvexTessellator& tess,
46 const SkMatrix* localCoordsMatrix,
47 void* vertData,
48 const VertexColor& color,
49 uint16_t firstIndex,
50 uint16_t* idxs) {
51 VertexWriter verts{vertData};
52 for (int i = 0; i < tess.numPts(); ++i) {
53 SkPoint lc;
54 if (localCoordsMatrix) {
55 localCoordsMatrix->mapPoints(&lc, &tess.point(i), 1);
56 }
57 verts << tess.point(i) << color << VertexWriter::If(localCoordsMatrix, lc)
58 << tess.coverage(i);
59 }
60
61 for (int i = 0; i < tess.numIndices(); ++i) {
62 idxs[i] = tess.index(i) + firstIndex;
63 }
64}
65
66GrGeometryProcessor* create_lines_only_gp(SkArenaAlloc* arena,
67 bool tweakAlphaForCoverage,
68 bool usesLocalCoords,
69 bool wideColor) {
70 using namespace GrDefaultGeoProcFactory;
71
72 Coverage::Type coverageType =
73 tweakAlphaForCoverage ? Coverage::kAttributeTweakAlpha_Type : Coverage::kAttribute_Type;
74 LocalCoords::Type localCoordsType =
75 usesLocalCoords ? LocalCoords::kHasExplicit_Type : LocalCoords::kUnused_Type;
77 wideColor ? Color::kPremulWideColorAttribute_Type : Color::kPremulGrColorAttribute_Type;
78
79 return Make(arena, colorType, coverageType, localCoordsType, SkMatrix::I());
80}
81
82class AAFlatteningConvexPathOp final : public GrMeshDrawOp {
83private:
85
86public:
88
89 static GrOp::Owner Make(GrRecordingContext* context,
90 GrPaint&& paint,
91 const SkMatrix& viewMatrix,
92 const SkPath& path,
96 SkScalar miterLimit,
97 const GrUserStencilSettings* stencilSettings) {
98 return Helper::FactoryHelper<AAFlatteningConvexPathOp>(context, std::move(paint),
99 viewMatrix, path,
100 strokeWidth, style, join, miterLimit,
101 stencilSettings);
102 }
103
104 AAFlatteningConvexPathOp(GrProcessorSet* processorSet,
105 const SkPMColor4f& color,
106 const SkMatrix& viewMatrix,
107 const SkPath& path,
109 SkStrokeRec::Style style,
111 SkScalar miterLimit,
112 const GrUserStencilSettings* stencilSettings)
113 : INHERITED(ClassID()), fHelper(processorSet, GrAAType::kCoverage, stencilSettings) {
114 fPaths.emplace_back(
115 PathData{viewMatrix, path, color, strokeWidth, miterLimit, style, join});
116
117 // compute bounds
118 SkRect bounds = path.getBounds();
120 if (w > 0) {
121 w /= 2;
122 SkScalar maxScale = viewMatrix.getMaxScale();
123 // We should not have a perspective matrix, thus we should have a valid scale.
124 SkASSERT(maxScale != -1);
125 if (SkPaint::kMiter_Join == join && w * maxScale > 1.f) {
126 w *= miterLimit;
127 }
128 bounds.outset(w, w);
129 }
130 this->setTransformedBounds(bounds, viewMatrix, HasAABloat::kYes, IsHairline::kNo);
131 }
132
133 const char* name() const override { return "AAFlatteningConvexPathOp"; }
134
135 void visitProxies(const GrVisitProxyFunc& func) const override {
136 if (fProgramInfo) {
137 fProgramInfo->visitFPProxies(func);
138 } else {
139 fHelper.visitProxies(func);
140 }
141 }
142
143 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
144
145 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
146 GrClampType clampType) override {
147 return fHelper.finalizeProcessors(caps, clip, clampType,
149 &fPaths.back().fColor, &fWideColor);
150 }
151
152private:
153 GrProgramInfo* programInfo() override { return fProgramInfo; }
154
155 void onCreateProgramInfo(const GrCaps* caps,
156 SkArenaAlloc* arena,
157 const GrSurfaceProxyView& writeView,
158 bool usesMSAASurface,
159 GrAppliedClip&& appliedClip,
160 const GrDstProxyView& dstProxyView,
161 GrXferBarrierFlags renderPassXferBarriers,
162 GrLoadOp colorLoadOp) override {
163 GrGeometryProcessor* gp = create_lines_only_gp(arena,
165 fHelper.usesLocalCoords(),
166 fWideColor);
167 if (!gp) {
168 SkDebugf("Couldn't create a GrGeometryProcessor\n");
169 return;
170 }
171
172 fProgramInfo = fHelper.createProgramInfoWithStencil(caps, arena, writeView, usesMSAASurface,
173 std::move(appliedClip), dstProxyView,
175 renderPassXferBarriers, colorLoadOp);
176 }
177
178 void recordDraw(GrMeshDrawTarget* target,
179 int vertexCount, size_t vertexStride, void* vertices,
180 int indexCount, uint16_t* indices) {
181 if (vertexCount == 0 || indexCount == 0) {
182 return;
183 }
184 sk_sp<const GrBuffer> vertexBuffer;
185 int firstVertex;
186 void* verts = target->makeVertexSpace(vertexStride, vertexCount, &vertexBuffer,
187 &firstVertex);
188 if (!verts) {
189 SkDebugf("Could not allocate vertices\n");
190 return;
191 }
192 memcpy(verts, vertices, vertexCount * vertexStride);
193
194 sk_sp<const GrBuffer> indexBuffer;
195 int firstIndex;
196 uint16_t* idxs = target->makeIndexSpace(indexCount, &indexBuffer, &firstIndex);
197 if (!idxs) {
198 SkDebugf("Could not allocate indices\n");
199 return;
200 }
201 memcpy(idxs, indices, indexCount * sizeof(uint16_t));
202 GrSimpleMesh* mesh = target->allocMesh();
203 mesh->setIndexed(std::move(indexBuffer), indexCount, firstIndex, 0, vertexCount - 1,
204 GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
205 fMeshes.push_back(mesh);
206 }
207
208 void onPrepareDraws(GrMeshDrawTarget* target) override {
209 if (!fProgramInfo) {
210 this->createProgramInfo(target);
211 if (!fProgramInfo) {
212 return;
213 }
214 }
215
216 size_t vertexStride = fProgramInfo->geomProc().vertexStride();
217 int instanceCount = fPaths.size();
218
219 int64_t vertexCount = 0;
220 int64_t indexCount = 0;
221 int64_t maxVertices = DEFAULT_BUFFER_SIZE;
222 int64_t maxIndices = DEFAULT_BUFFER_SIZE;
223 uint8_t* vertices = (uint8_t*) sk_malloc_throw(maxVertices * vertexStride);
224 uint16_t* indices = (uint16_t*) sk_malloc_throw(maxIndices * sizeof(uint16_t));
225 for (int i = 0; i < instanceCount; i++) {
226 const PathData& args = fPaths[i];
227 GrAAConvexTessellator tess(args.fStyle, args.fStrokeWidth,
228 args.fJoin, args.fMiterLimit);
229
230 if (!tess.tessellate(args.fViewMatrix, args.fPath)) {
231 continue;
232 }
233
234 int currentVertices = tess.numPts();
235 if (vertexCount + currentVertices > static_cast<int>(UINT16_MAX)) {
236 // if we added the current instance, we would overflow the indices we can store in a
237 // uint16_t. Draw what we've got so far and reset.
238 this->recordDraw(target, vertexCount, vertexStride, vertices, indexCount, indices);
239 vertexCount = 0;
240 indexCount = 0;
241 }
242 if (vertexCount + currentVertices > maxVertices) {
243 maxVertices = std::max(vertexCount + currentVertices, maxVertices * 2);
244 if (maxVertices * vertexStride > SK_MaxS32) {
245 sk_free(vertices);
246 sk_free(indices);
247 return;
248 }
249 vertices = (uint8_t*) sk_realloc_throw(vertices, maxVertices * vertexStride);
250 }
251 int currentIndices = tess.numIndices();
252 if (indexCount + currentIndices > maxIndices) {
253 maxIndices = std::max(indexCount + currentIndices, maxIndices * 2);
254 if (maxIndices * sizeof(uint16_t) > SK_MaxS32) {
255 sk_free(vertices);
256 sk_free(indices);
257 return;
258 }
259 indices = (uint16_t*) sk_realloc_throw(indices, maxIndices * sizeof(uint16_t));
260 }
261
262 const SkMatrix* localCoordsMatrix = nullptr;
263 SkMatrix ivm;
264 if (fHelper.usesLocalCoords()) {
265 if (!args.fViewMatrix.invert(&ivm)) {
266 ivm = SkMatrix::I();
267 }
268 localCoordsMatrix = &ivm;
269 }
270
271 extract_verts(tess, localCoordsMatrix, vertices + vertexStride * vertexCount,
272 VertexColor(args.fColor, fWideColor), vertexCount, indices + indexCount);
273 vertexCount += currentVertices;
274 indexCount += currentIndices;
275 }
276 if (vertexCount <= SK_MaxS32 && indexCount <= SK_MaxS32) {
277 this->recordDraw(target, vertexCount, vertexStride, vertices, indexCount, indices);
278 }
279 sk_free(vertices);
280 sk_free(indices);
281 }
282
283 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
284 if (!fProgramInfo || fMeshes.empty()) {
285 return;
286 }
287
288 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
289 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
290 for (int i = 0; i < fMeshes.size(); ++i) {
291 flushState->drawMesh(*fMeshes[i]);
292 }
293 }
294
295 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
296 AAFlatteningConvexPathOp* that = t->cast<AAFlatteningConvexPathOp>();
297 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
298 return CombineResult::kCannotCombine;
299 }
300
301 fPaths.push_back_n(that->fPaths.size(), that->fPaths.begin());
302 fWideColor |= that->fWideColor;
303 return CombineResult::kMerged;
304 }
305
306#if defined(GR_TEST_UTILS)
307 SkString onDumpInfo() const override {
308 SkString string;
309 for (const auto& path : fPaths) {
310 string.appendf(
311 "Color: 0x%08x, StrokeWidth: %.2f, Style: %d, Join: %d, "
312 "MiterLimit: %.2f\n",
313 path.fColor.toBytes_RGBA(), path.fStrokeWidth, path.fStyle, path.fJoin,
314 path.fMiterLimit);
315 }
316 string += fHelper.dumpInfo();
317 return string;
318 }
319#endif
320
321 struct PathData {
329 };
330
332 Helper fHelper;
333 bool fWideColor;
334
336 GrProgramInfo* fProgramInfo = nullptr;
337
338 using INHERITED = GrMeshDrawOp;
339};
340
341} // anonymous namespace
342
343///////////////////////////////////////////////////////////////////////////////////////////////////
344
346AALinearizingConvexPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
347 if (GrAAType::kCoverage != args.fAAType) {
348 return CanDrawPath::kNo;
349 }
350 if (!args.fShape->knownToBeConvex()) {
351 return CanDrawPath::kNo;
352 }
353 if (args.fShape->style().pathEffect()) {
354 return CanDrawPath::kNo;
355 }
356 if (args.fShape->inverseFilled()) {
357 return CanDrawPath::kNo;
358 }
359 if (args.fShape->bounds().width() <= 0 && args.fShape->bounds().height() <= 0) {
360 // Stroked zero length lines should draw, but this PR doesn't handle that case
361 return CanDrawPath::kNo;
362 }
363 const SkStrokeRec& stroke = args.fShape->style().strokeRec();
364
365 if (stroke.getStyle() == SkStrokeRec::kStroke_Style ||
367 if (!args.fViewMatrix->isSimilarity()) {
368 return CanDrawPath::kNo;
369 }
370 SkScalar strokeWidth = args.fViewMatrix->getMaxScale() * stroke.getWidth();
371 if (strokeWidth < 1.0f && stroke.getStyle() == SkStrokeRec::kStroke_Style) {
372 return CanDrawPath::kNo;
373 }
374 if ((strokeWidth > kMaxStrokeWidth && !args.fShape->isRect()) ||
375 !args.fShape->knownToBeClosed() ||
376 stroke.getJoin() == SkPaint::Join::kRound_Join) {
377 return CanDrawPath::kNo;
378 }
379 return CanDrawPath::kYes;
380 }
381 if (stroke.getStyle() != SkStrokeRec::kFill_Style) {
382 return CanDrawPath::kNo;
383 }
384 // This can almost handle perspective. It would need to use 3 component explicit local coords
385 // when there are FPs that require them. This is difficult to test because AAConvexPathRenderer
386 // takes almost all filled paths that could get here. So just avoid perspective fills.
387 if (args.fViewMatrix->hasPerspective()) {
388 return CanDrawPath::kNo;
389 }
390 return CanDrawPath::kYes;
391}
392
393bool AALinearizingConvexPathRenderer::onDrawPath(const DrawPathArgs& args) {
394 GR_AUDIT_TRAIL_AUTO_FRAME(args.fContext->priv().auditTrail(),
395 "AALinearizingConvexPathRenderer::onDrawPath");
396 SkASSERT(args.fSurfaceDrawContext->numSamples() <= 1);
397 SkASSERT(!args.fShape->isEmpty());
398 SkASSERT(!args.fShape->style().pathEffect());
399
400 SkPath path;
401 args.fShape->asPath(&path);
402 bool fill = args.fShape->style().isSimpleFill();
403 const SkStrokeRec& stroke = args.fShape->style().strokeRec();
404 SkScalar strokeWidth = fill ? -1.0f : stroke.getWidth();
405 SkPaint::Join join = fill ? SkPaint::Join::kMiter_Join : stroke.getJoin();
406 SkScalar miterLimit = stroke.getMiter();
407
409 args.fContext, std::move(args.fPaint), *args.fViewMatrix, path, strokeWidth,
410 stroke.getStyle(), join, miterLimit, args.fUserStencilSettings);
411 args.fSurfaceDrawContext->addDrawOp(args.fClip, std::move(op));
412 return true;
413}
414
415} // namespace skgpu::ganesh
416
417#if defined(GR_TEST_UTILS)
418
419GR_DRAW_OP_TEST_DEFINE(AAFlatteningConvexPathOp) {
420 SkMatrix viewMatrix = GrTest::TestMatrixPreservesRightAngles(random);
421 const SkPath& path = GrTest::TestPathConvex(random);
422
426
427 SkStrokeRec::Style style = styles[random->nextU() % 3];
428
429 SkScalar strokeWidth = -1.f;
431 SkScalar miterLimit = 0.5f;
432
433 if (SkStrokeRec::kFill_Style != style) {
434 strokeWidth = random->nextRangeF(1.0f, 10.0f);
435 if (random->nextBool()) {
437 } else {
439 }
440 miterLimit = random->nextRangeF(0.5f, 2.0f);
441 }
442 const GrUserStencilSettings* stencilSettings = GrGetRandomStencil(random, context);
444 std::move(paint),
445 viewMatrix,
446 path,
448 style,
449 join,
450 miterLimit,
451 stencilSettings);
452}
453
454#endif
GrSimpleMesh * fMeshes
SkPaint::Join fJoin
SkStrokeRec::Style fStyle
SkPMColor4f fColor
static const int strokeWidth
Definition: BlurTest.cpp:60
#define GR_AUDIT_TRAIL_AUTO_FRAME(audit_trail, framename)
Definition: GrAuditTrail.h:167
#define DEFINE_OP_CLASS_ID
Definition: GrOp.h:64
GrClampType
Definition: GrTypesPriv.h:228
std::function< void(GrSurfaceProxy *, skgpu::Mipmapped)> GrVisitProxyFunc
Definition: GrTypesPriv.h:943
GrAAType
Definition: GrTypesPriv.h:200
GrLoadOp
Definition: GrTypesPriv.h:155
GrXferBarrierFlags
#define SkASSERT(cond)
Definition: SkAssert.h:116
void SK_SPI SkDebugf(const char format[],...) SK_PRINTF_LIKE(1
static SkColorType colorType(AImageDecoder *decoder, const AImageDecoderHeaderInfo *headerInfo)
SK_API void sk_free(void *)
static void * sk_malloc_throw(size_t size)
Definition: SkMalloc.h:67
SK_API void * sk_realloc_throw(void *buffer, size_t size)
static constexpr int32_t SK_MaxS32
Definition: SkMath.h:21
static SkPath clip(const SkPath &path, const SkHalfPlane &plane)
Definition: SkPath.cpp:3892
#define INHERITED(method,...)
Definition: SkRecorder.cpp:128
Definition: GrCaps.h:57
size_t vertexStride() const
void drawMesh(const GrSimpleMesh &mesh)
void bindPipelineAndScissorClip(const GrProgramInfo &programInfo, const SkRect &drawBounds)
void bindTextures(const GrGeometryProcessor &geomProc, const GrSurfaceProxy &singleGeomProcTexture, const GrPipeline &pipeline)
Definition: GrOp.h:70
std::unique_ptr< GrOp > Owner
Definition: GrOp.h:72
const T & cast() const
Definition: GrOp.h:148
const GrPipeline & pipeline() const
Definition: GrProgramInfo.h:39
const GrGeometryProcessor & geomProc() const
Definition: GrProgramInfo.h:40
void visitFPProxies(const GrVisitProxyFunc &func) const
Definition: GrProgramInfo.h:64
GrProcessorSet::Analysis finalizeProcessors(const GrCaps &caps, const GrAppliedClip *clip, GrClampType clampType, GrProcessorAnalysisCoverage geometryCoverage, GrProcessorAnalysisColor *geometryColor)
void visitProxies(const GrVisitProxyFunc &func) const
GrDrawOp::FixedFunctionFlags fixedFunctionFlags() const
bool isCompatible(const GrSimpleMeshDrawOpHelperWithStencil &that, const GrCaps &, const SkRect &thisBounds, const SkRect &thatBounds, bool ignoreAAType=false) const
GrProgramInfo * createProgramInfoWithStencil(const GrCaps *, SkArenaAlloc *, const GrSurfaceProxyView &writeView, bool usesMSAASurface, GrAppliedClip &&, const GrDstProxyView &, GrGeometryProcessor *, GrPrimitiveType, GrXferBarrierFlags renderPassXferBarriers, GrLoadOp colorLoadOp)
void mapPoints(SkPoint dst[], const SkPoint src[], int count) const
Definition: SkMatrix.cpp:770
static const SkMatrix & I()
Definition: SkMatrix.cpp:1544
SkScalar getMaxScale() const
Definition: SkMatrix.cpp:1531
@ kMiter_Join
extends to miter limit
Definition: SkPaint.h:359
@ kBevel_Join
connects outside edges
Definition: SkPaint.h:361
Definition: SkPath.h:59
void void void appendf(const char format[],...) SK_PRINTF_LIKE(2
Definition: SkString.cpp:550
@ kStrokeAndFill_Style
Definition: SkStrokeRec.h:36
const Paint & paint
Definition: color_source.cc:38
DlColor color
float SkScalar
Definition: extension.cpp:12
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
uint32_t * target
static float max(float r, float g, float b)
Definition: hsl.cpp:49
SK_API sk_sp< SkDocument > Make(SkWStream *dst, const SkSerialProcs *=nullptr, std::function< void(const SkPicture *)> onEndPage=nullptr)
Optional< SkRect > bounds
Definition: SkRecords.h:189
SkMesh mesh
Definition: SkRecords.h:345
void Helper(uword arg)
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition: switches.h:57
DEF_SWITCHES_START aot vmservice shared library name
Definition: switches.h:32
static TessellatorLibtess tess
SkScalar w
static SkString join(const CommandLineFlags::StringArray &)
Definition: skpbench.cpp:741
static Conditional< T > If(bool condition, const T &value)
Definition: BufferWriter.h:153