Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
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,
95 SkPaint::Join join,
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,
110 SkPaint::Join join,
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 {
325 SkScalar fStrokeWidth;
326 SkScalar fMiterLimit;
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
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() ||
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
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
408 GrOp::Owner op = AAFlatteningConvexPathOp::Make(
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);
443 return skgpu::ganesh::AAFlatteningConvexPathOp::Make(context,
444 std::move(paint),
445 viewMatrix,
446 path,
448 style,
449 join,
450 miterLimit,
451 stencilSettings);
452}
453
454#endif
SkPath fPath
SkMatrix fViewMatrix
GrSimpleMesh * fMeshes
SkPaint::Join fJoin
SkStrokeRec::Style fStyle
static const int strokeWidth
Definition BlurTest.cpp:60
#define GR_AUDIT_TRAIL_AUTO_FRAME(audit_trail, framename)
#define DEFINE_OP_CLASS_ID
Definition GrOp.h:64
GrClampType
std::function< void(GrSurfaceProxy *, skgpu::Mipmapped)> GrVisitProxyFunc
GrAAType
GrLoadOp
GrXferBarrierFlags
SkColor4f color
#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)
static std::unique_ptr< SkEncoder > Make(SkWStream *dst, const SkPixmap *src, const SkYUVAPixmaps *srcYUVA, const SkColorSpace *srcYUVAColorSpace, const SkJpegEncoder::Options &options)
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:3824
#define INHERITED(method,...)
bool tessellate(const SkMatrix &m, const SkPath &path)
SkScalar coverage(int index) const
const SkPoint & point(int index) const
int index(int index) 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
const GrGeometryProcessor & geomProc() const
void visitFPProxies(const GrVisitProxyFunc &func) const
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()
SkScalar getMaxScale() const
@ kRound_Join
adds circle
Definition SkPaint.h:360
@ kMiter_Join
extends to miter limit
Definition SkPaint.h:359
@ kBevel_Join
connects outside edges
Definition SkPaint.h:361
void void void appendf(const char format[],...) SK_PRINTF_LIKE(2
Definition SkString.cpp:550
Style getStyle() const
@ kStrokeAndFill_Style
Definition SkStrokeRec.h:36
SkScalar getWidth() const
Definition SkStrokeRec.h:42
SkPaint::Join getJoin() const
Definition SkStrokeRec.h:45
SkScalar getMiter() const
Definition SkStrokeRec.h:43
CanDrawPath onCanDrawPath(const CanDrawPathArgs &) const override
const Paint & paint
float SkScalar
Definition extension.cpp:12
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
uint32_t * target
const char * name
Definition fuchsia.cc:50
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
SINT Vec< 2 *N, T > join(const Vec< N, T > &lo, const Vec< N, T > &hi)
Definition SkVx.h:242
SkScalar w
static Conditional< T > If(bool condition, const T &value)