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 #include "src/gpu/ganesh/ops/AALinearizingConvexPathRenderer.h"
8
9 #include "include/core/SkMatrix.h"
10 #include "include/core/SkPaint.h"
11 #include "include/core/SkPath.h"
12 #include "include/core/SkPoint.h"
13 #include "include/core/SkRect.h"
14 #include "include/core/SkRefCnt.h"
15 #include "include/core/SkScalar.h"
16 #include "include/core/SkString.h"
17 #include "include/core/SkStrokeRec.h"
18 #include "include/gpu/ganesh/GrRecordingContext.h"
19 #include "include/private/SkColorData.h"
20 #include "include/private/base/SkAssert.h"
21 #include "include/private/base/SkDebug.h"
22 #include "include/private/base/SkMalloc.h"
23 #include "include/private/base/SkMath.h"
24 #include "include/private/base/SkTArray.h"
25 #include "include/private/base/SkTDArray.h"
26 #include "include/private/gpu/ganesh/GrTypesPriv.h"
27 #include "src/gpu/BufferWriter.h"
28 #include "src/gpu/ganesh/GrAppliedClip.h"
29 #include "src/gpu/ganesh/GrAuditTrail.h"
30 #include "src/gpu/ganesh/GrBuffer.h"
31 #include "src/gpu/ganesh/GrDefaultGeoProcFactory.h"
32 #include "src/gpu/ganesh/GrDrawOpTest.h"
33 #include "src/gpu/ganesh/GrGeometryProcessor.h"
34 #include "src/gpu/ganesh/GrMeshDrawTarget.h"
35 #include "src/gpu/ganesh/GrOpFlushState.h"
36 #include "src/gpu/ganesh/GrPaint.h"
37 #include "src/gpu/ganesh/GrProcessorAnalysis.h"
38 #include "src/gpu/ganesh/GrProcessorSet.h"
39 #include "src/gpu/ganesh/GrProgramInfo.h"
40 #include "src/gpu/ganesh/GrRecordingContextPriv.h"
41 #include "src/gpu/ganesh/GrSimpleMesh.h"
42 #include "src/gpu/ganesh/GrStyle.h"
43 #include "src/gpu/ganesh/SurfaceDrawContext.h"
44 #include "src/gpu/ganesh/geometry/GrAAConvexTessellator.h"
45 #include "src/gpu/ganesh/geometry/GrStyledShape.h"
46 #include "src/gpu/ganesh/ops/GrMeshDrawOp.h"
47 #include "src/gpu/ganesh/ops/GrOp.h"
48 #include "src/gpu/ganesh/ops/GrSimpleMeshDrawOpHelperWithStencil.h"
49
50 #if defined(GPU_TEST_UTILS)
51 #include "src/base/SkRandom.h"
52 #include "src/gpu/ganesh/GrTestUtils.h"
53 #endif
54
55 #include <algorithm>
56 #include <cstdint>
57 #include <cstring>
58 #include <utility>
59
60 class GrCaps;
61 class GrDstProxyView;
62 class GrSurfaceProxyView;
63 class SkArenaAlloc;
64 enum class GrXferBarrierFlags;
65 struct GrUserStencilSettings;
66
67 using namespace skia_private;
68
69 ///////////////////////////////////////////////////////////////////////////////
70 namespace skgpu::ganesh {
71
72 namespace {
73
74 static const int DEFAULT_BUFFER_SIZE = 100;
75
76 // The thicker the stroke, the harder it is to produce high-quality results using tessellation. For
77 // the time being, we simply drop back to software rendering above this stroke width.
78 static const SkScalar kMaxStrokeWidth = 20.0;
79
80 // extract the result vertices and indices from the GrAAConvexTessellator
extract_verts(const GrAAConvexTessellator & tess,const SkMatrix * localCoordsMatrix,void * vertData,const VertexColor & color,uint16_t firstIndex,uint16_t * idxs)81 void extract_verts(const GrAAConvexTessellator& tess,
82 const SkMatrix* localCoordsMatrix,
83 void* vertData,
84 const VertexColor& color,
85 uint16_t firstIndex,
86 uint16_t* idxs) {
87 VertexWriter verts{vertData};
88 for (int i = 0; i < tess.numPts(); ++i) {
89 SkPoint lc;
90 if (localCoordsMatrix) {
91 localCoordsMatrix->mapPoints(&lc, &tess.point(i), 1);
92 }
93 verts << tess.point(i) << color << VertexWriter::If(localCoordsMatrix, lc)
94 << tess.coverage(i);
95 }
96
97 for (int i = 0; i < tess.numIndices(); ++i) {
98 idxs[i] = tess.index(i) + firstIndex;
99 }
100 }
101
create_lines_only_gp(SkArenaAlloc * arena,bool tweakAlphaForCoverage,bool usesLocalCoords,bool wideColor)102 GrGeometryProcessor* create_lines_only_gp(SkArenaAlloc* arena,
103 bool tweakAlphaForCoverage,
104 bool usesLocalCoords,
105 bool wideColor) {
106 using namespace GrDefaultGeoProcFactory;
107
108 Coverage::Type coverageType =
109 tweakAlphaForCoverage ? Coverage::kAttributeTweakAlpha_Type : Coverage::kAttribute_Type;
110 LocalCoords::Type localCoordsType =
111 usesLocalCoords ? LocalCoords::kHasExplicit_Type : LocalCoords::kUnused_Type;
112 Color::Type colorType =
113 wideColor ? Color::kPremulWideColorAttribute_Type : Color::kPremulGrColorAttribute_Type;
114
115 return Make(arena, colorType, coverageType, localCoordsType, SkMatrix::I());
116 }
117
118 class AAFlatteningConvexPathOp final : public GrMeshDrawOp {
119 private:
120 using Helper = GrSimpleMeshDrawOpHelperWithStencil;
121
122 public:
123 DEFINE_OP_CLASS_ID
124
Make(GrRecordingContext * context,GrPaint && paint,const SkMatrix & viewMatrix,const SkPath & path,SkScalar strokeWidth,SkStrokeRec::Style style,SkPaint::Join join,SkScalar miterLimit,const GrUserStencilSettings * stencilSettings)125 static GrOp::Owner Make(GrRecordingContext* context,
126 GrPaint&& paint,
127 const SkMatrix& viewMatrix,
128 const SkPath& path,
129 SkScalar strokeWidth,
130 SkStrokeRec::Style style,
131 SkPaint::Join join,
132 SkScalar miterLimit,
133 const GrUserStencilSettings* stencilSettings) {
134 return Helper::FactoryHelper<AAFlatteningConvexPathOp>(context, std::move(paint),
135 viewMatrix, path,
136 strokeWidth, style, join, miterLimit,
137 stencilSettings);
138 }
139
AAFlatteningConvexPathOp(GrProcessorSet * processorSet,const SkPMColor4f & color,const SkMatrix & viewMatrix,const SkPath & path,SkScalar strokeWidth,SkStrokeRec::Style style,SkPaint::Join join,SkScalar miterLimit,const GrUserStencilSettings * stencilSettings)140 AAFlatteningConvexPathOp(GrProcessorSet* processorSet,
141 const SkPMColor4f& color,
142 const SkMatrix& viewMatrix,
143 const SkPath& path,
144 SkScalar strokeWidth,
145 SkStrokeRec::Style style,
146 SkPaint::Join join,
147 SkScalar miterLimit,
148 const GrUserStencilSettings* stencilSettings)
149 : INHERITED(ClassID()), fHelper(processorSet, GrAAType::kCoverage, stencilSettings) {
150 fPaths.emplace_back(
151 PathData{viewMatrix, path, color, strokeWidth, miterLimit, style, join});
152
153 // compute bounds
154 SkRect bounds = path.getBounds();
155 SkScalar w = strokeWidth;
156 if (w > 0) {
157 w /= 2;
158 SkScalar maxScale = viewMatrix.getMaxScale();
159 // We should not have a perspective matrix, thus we should have a valid scale.
160 SkASSERT(maxScale != -1);
161 if (SkPaint::kMiter_Join == join && w * maxScale > 1.f) {
162 w *= miterLimit;
163 }
164 bounds.outset(w, w);
165 }
166 this->setTransformedBounds(bounds, viewMatrix, HasAABloat::kYes, IsHairline::kNo);
167 }
168
name() const169 const char* name() const override { return "AAFlatteningConvexPathOp"; }
170
visitProxies(const GrVisitProxyFunc & func) const171 void visitProxies(const GrVisitProxyFunc& func) const override {
172 if (fProgramInfo) {
173 fProgramInfo->visitFPProxies(func);
174 } else {
175 fHelper.visitProxies(func);
176 }
177 }
178
fixedFunctionFlags() const179 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
180
finalize(const GrCaps & caps,const GrAppliedClip * clip,GrClampType clampType)181 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
182 GrClampType clampType) override {
183 return fHelper.finalizeProcessors(caps, clip, clampType,
184 GrProcessorAnalysisCoverage::kSingleChannel,
185 &fPaths.back().fColor, &fWideColor);
186 }
187
188 private:
programInfo()189 GrProgramInfo* programInfo() override { return fProgramInfo; }
190
onCreateProgramInfo(const GrCaps * caps,SkArenaAlloc * arena,const GrSurfaceProxyView & writeView,bool usesMSAASurface,GrAppliedClip && appliedClip,const GrDstProxyView & dstProxyView,GrXferBarrierFlags renderPassXferBarriers,GrLoadOp colorLoadOp)191 void onCreateProgramInfo(const GrCaps* caps,
192 SkArenaAlloc* arena,
193 const GrSurfaceProxyView& writeView,
194 bool usesMSAASurface,
195 GrAppliedClip&& appliedClip,
196 const GrDstProxyView& dstProxyView,
197 GrXferBarrierFlags renderPassXferBarriers,
198 GrLoadOp colorLoadOp) override {
199 GrGeometryProcessor* gp = create_lines_only_gp(arena,
200 fHelper.compatibleWithCoverageAsAlpha(),
201 fHelper.usesLocalCoords(),
202 fWideColor);
203 if (!gp) {
204 SkDebugf("Couldn't create a GrGeometryProcessor\n");
205 return;
206 }
207
208 fProgramInfo = fHelper.createProgramInfoWithStencil(caps, arena, writeView, usesMSAASurface,
209 std::move(appliedClip), dstProxyView,
210 gp, GrPrimitiveType::kTriangles,
211 renderPassXferBarriers, colorLoadOp);
212 }
213
recordDraw(GrMeshDrawTarget * target,int vertexCount,size_t vertexStride,void * vertices,int indexCount,uint16_t * indices)214 void recordDraw(GrMeshDrawTarget* target,
215 int vertexCount, size_t vertexStride, void* vertices,
216 int indexCount, uint16_t* indices) {
217 if (vertexCount == 0 || indexCount == 0) {
218 return;
219 }
220 sk_sp<const GrBuffer> vertexBuffer;
221 int firstVertex;
222 void* verts = target->makeVertexSpace(vertexStride, vertexCount, &vertexBuffer,
223 &firstVertex);
224 if (!verts) {
225 SkDebugf("Could not allocate vertices\n");
226 return;
227 }
228 memcpy(verts, vertices, vertexCount * vertexStride);
229
230 sk_sp<const GrBuffer> indexBuffer;
231 int firstIndex;
232 uint16_t* idxs = target->makeIndexSpace(indexCount, &indexBuffer, &firstIndex);
233 if (!idxs) {
234 SkDebugf("Could not allocate indices\n");
235 return;
236 }
237 memcpy(idxs, indices, indexCount * sizeof(uint16_t));
238 GrSimpleMesh* mesh = target->allocMesh();
239 mesh->setIndexed(std::move(indexBuffer), indexCount, firstIndex, 0, vertexCount - 1,
240 GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
241 fMeshes.push_back(mesh);
242 }
243
onPrepareDraws(GrMeshDrawTarget * target)244 void onPrepareDraws(GrMeshDrawTarget* target) override {
245 if (!fProgramInfo) {
246 this->createProgramInfo(target);
247 if (!fProgramInfo) {
248 return;
249 }
250 }
251
252 size_t vertexStride = fProgramInfo->geomProc().vertexStride();
253 int instanceCount = fPaths.size();
254
255 int64_t vertexCount = 0;
256 int64_t indexCount = 0;
257 int64_t maxVertices = DEFAULT_BUFFER_SIZE;
258 int64_t maxIndices = DEFAULT_BUFFER_SIZE;
259 uint8_t* vertices = (uint8_t*) sk_malloc_throw(maxVertices * vertexStride);
260 uint16_t* indices = (uint16_t*) sk_malloc_throw(maxIndices * sizeof(uint16_t));
261 for (int i = 0; i < instanceCount; i++) {
262 const PathData& args = fPaths[i];
263 GrAAConvexTessellator tess(args.fStyle, args.fStrokeWidth,
264 args.fJoin, args.fMiterLimit);
265
266 if (!tess.tessellate(args.fViewMatrix, args.fPath)) {
267 continue;
268 }
269
270 int currentVertices = tess.numPts();
271 if (vertexCount + currentVertices > static_cast<int>(UINT16_MAX)) {
272 // if we added the current instance, we would overflow the indices we can store in a
273 // uint16_t. Draw what we've got so far and reset.
274 this->recordDraw(target, vertexCount, vertexStride, vertices, indexCount, indices);
275 vertexCount = 0;
276 indexCount = 0;
277 }
278 if (vertexCount + currentVertices > maxVertices) {
279 maxVertices = std::max(vertexCount + currentVertices, maxVertices * 2);
280 if (maxVertices * vertexStride > SK_MaxS32) {
281 sk_free(vertices);
282 sk_free(indices);
283 return;
284 }
285 vertices = (uint8_t*) sk_realloc_throw(vertices, maxVertices * vertexStride);
286 }
287 int currentIndices = tess.numIndices();
288 if (indexCount + currentIndices > maxIndices) {
289 maxIndices = std::max(indexCount + currentIndices, maxIndices * 2);
290 if (maxIndices * sizeof(uint16_t) > SK_MaxS32) {
291 sk_free(vertices);
292 sk_free(indices);
293 return;
294 }
295 indices = (uint16_t*) sk_realloc_throw(indices, maxIndices * sizeof(uint16_t));
296 }
297
298 const SkMatrix* localCoordsMatrix = nullptr;
299 SkMatrix ivm;
300 if (fHelper.usesLocalCoords()) {
301 if (!args.fViewMatrix.invert(&ivm)) {
302 ivm = SkMatrix::I();
303 }
304 localCoordsMatrix = &ivm;
305 }
306
307 extract_verts(tess, localCoordsMatrix, vertices + vertexStride * vertexCount,
308 VertexColor(args.fColor, fWideColor), vertexCount, indices + indexCount);
309 vertexCount += currentVertices;
310 indexCount += currentIndices;
311 }
312 if (vertexCount <= SK_MaxS32 && indexCount <= SK_MaxS32) {
313 this->recordDraw(target, vertexCount, vertexStride, vertices, indexCount, indices);
314 }
315 sk_free(vertices);
316 sk_free(indices);
317 }
318
onExecute(GrOpFlushState * flushState,const SkRect & chainBounds)319 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
320 if (!fProgramInfo || fMeshes.empty()) {
321 return;
322 }
323
324 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
325 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
326 for (int i = 0; i < fMeshes.size(); ++i) {
327 flushState->drawMesh(*fMeshes[i]);
328 }
329 }
330
onCombineIfPossible(GrOp * t,SkArenaAlloc *,const GrCaps & caps)331 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
332 AAFlatteningConvexPathOp* that = t->cast<AAFlatteningConvexPathOp>();
333 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
334 return CombineResult::kCannotCombine;
335 }
336
337 fPaths.push_back_n(that->fPaths.size(), that->fPaths.begin());
338 fWideColor |= that->fWideColor;
339 return CombineResult::kMerged;
340 }
341
342 #if defined(GPU_TEST_UTILS)
onDumpInfo() const343 SkString onDumpInfo() const override {
344 SkString string;
345 for (const auto& path : fPaths) {
346 string.appendf(
347 "Color: 0x%08x, StrokeWidth: %.2f, Style: %d, Join: %d, "
348 "MiterLimit: %.2f\n",
349 path.fColor.toBytes_RGBA(), path.fStrokeWidth, path.fStyle, path.fJoin,
350 path.fMiterLimit);
351 }
352 string += fHelper.dumpInfo();
353 return string;
354 }
355 #endif
356
357 struct PathData {
358 SkMatrix fViewMatrix;
359 SkPath fPath;
360 SkPMColor4f fColor;
361 SkScalar fStrokeWidth;
362 SkScalar fMiterLimit;
363 SkStrokeRec::Style fStyle;
364 SkPaint::Join fJoin;
365 };
366
367 STArray<1, PathData, true> fPaths;
368 Helper fHelper;
369 bool fWideColor;
370
371 SkTDArray<GrSimpleMesh*> fMeshes;
372 GrProgramInfo* fProgramInfo = nullptr;
373
374 using INHERITED = GrMeshDrawOp;
375 };
376
377 } // anonymous namespace
378
379 ///////////////////////////////////////////////////////////////////////////////////////////////////
380
381 PathRenderer::CanDrawPath
onCanDrawPath(const CanDrawPathArgs & args) const382 AALinearizingConvexPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
383 if (GrAAType::kCoverage != args.fAAType) {
384 return CanDrawPath::kNo;
385 }
386 if (!args.fShape->knownToBeConvex()) {
387 return CanDrawPath::kNo;
388 }
389 if (args.fShape->style().pathEffect()) {
390 return CanDrawPath::kNo;
391 }
392 if (args.fShape->inverseFilled()) {
393 return CanDrawPath::kNo;
394 }
395 if (args.fShape->bounds().width() <= 0 && args.fShape->bounds().height() <= 0) {
396 // Stroked zero length lines should draw, but this PR doesn't handle that case
397 return CanDrawPath::kNo;
398 }
399 const SkStrokeRec& stroke = args.fShape->style().strokeRec();
400
401 if (stroke.getStyle() == SkStrokeRec::kStroke_Style ||
402 stroke.getStyle() == SkStrokeRec::kStrokeAndFill_Style) {
403 if (!args.fViewMatrix->isSimilarity()) {
404 return CanDrawPath::kNo;
405 }
406 SkScalar strokeWidth = args.fViewMatrix->getMaxScale() * stroke.getWidth();
407 if (strokeWidth < 1.0f && stroke.getStyle() == SkStrokeRec::kStroke_Style) {
408 return CanDrawPath::kNo;
409 }
410 if ((strokeWidth > kMaxStrokeWidth && !args.fShape->isRect()) ||
411 !args.fShape->knownToBeClosed() ||
412 stroke.getJoin() == SkPaint::Join::kRound_Join) {
413 return CanDrawPath::kNo;
414 }
415 return CanDrawPath::kYes;
416 }
417 if (stroke.getStyle() != SkStrokeRec::kFill_Style) {
418 return CanDrawPath::kNo;
419 }
420 // This can almost handle perspective. It would need to use 3 component explicit local coords
421 // when there are FPs that require them. This is difficult to test because AAConvexPathRenderer
422 // takes almost all filled paths that could get here. So just avoid perspective fills.
423 if (args.fViewMatrix->hasPerspective()) {
424 return CanDrawPath::kNo;
425 }
426 return CanDrawPath::kYes;
427 }
428
onDrawPath(const DrawPathArgs & args)429 bool AALinearizingConvexPathRenderer::onDrawPath(const DrawPathArgs& args) {
430 GR_AUDIT_TRAIL_AUTO_FRAME(args.fContext->priv().auditTrail(),
431 "AALinearizingConvexPathRenderer::onDrawPath");
432 SkASSERT(args.fSurfaceDrawContext->numSamples() <= 1);
433 SkASSERT(!args.fShape->isEmpty());
434 SkASSERT(!args.fShape->style().pathEffect());
435
436 SkPath path;
437 args.fShape->asPath(&path);
438 bool fill = args.fShape->style().isSimpleFill();
439 const SkStrokeRec& stroke = args.fShape->style().strokeRec();
440 SkScalar strokeWidth = fill ? -1.0f : stroke.getWidth();
441 SkPaint::Join join = fill ? SkPaint::Join::kMiter_Join : stroke.getJoin();
442 SkScalar miterLimit = stroke.getMiter();
443
444 GrOp::Owner op = AAFlatteningConvexPathOp::Make(
445 args.fContext, std::move(args.fPaint), *args.fViewMatrix, path, strokeWidth,
446 stroke.getStyle(), join, miterLimit, args.fUserStencilSettings);
447 args.fSurfaceDrawContext->addDrawOp(args.fClip, std::move(op));
448 return true;
449 }
450
451 } // namespace skgpu::ganesh
452
453 #if defined(GPU_TEST_UTILS)
454
GR_DRAW_OP_TEST_DEFINE(AAFlatteningConvexPathOp)455 GR_DRAW_OP_TEST_DEFINE(AAFlatteningConvexPathOp) {
456 SkMatrix viewMatrix = GrTest::TestMatrixPreservesRightAngles(random);
457 const SkPath& path = GrTest::TestPathConvex(random);
458
459 SkStrokeRec::Style styles[3] = { SkStrokeRec::kFill_Style,
460 SkStrokeRec::kStroke_Style,
461 SkStrokeRec::kStrokeAndFill_Style };
462
463 SkStrokeRec::Style style = styles[random->nextU() % 3];
464
465 SkScalar strokeWidth = -1.f;
466 SkPaint::Join join = SkPaint::kMiter_Join;
467 SkScalar miterLimit = 0.5f;
468
469 if (SkStrokeRec::kFill_Style != style) {
470 strokeWidth = random->nextRangeF(1.0f, 10.0f);
471 if (random->nextBool()) {
472 join = SkPaint::kMiter_Join;
473 } else {
474 join = SkPaint::kBevel_Join;
475 }
476 miterLimit = random->nextRangeF(0.5f, 2.0f);
477 }
478 const GrUserStencilSettings* stencilSettings = GrGetRandomStencil(random, context);
479 return skgpu::ganesh::AAFlatteningConvexPathOp::Make(context,
480 std::move(paint),
481 viewMatrix,
482 path,
483 strokeWidth,
484 style,
485 join,
486 miterLimit,
487 stencilSettings);
488 }
489
490 #endif
491