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
8 #include "include/core/SkString.h"
9 #include "src/core/SkGeometry.h"
10 #include "src/core/SkPathPriv.h"
11 #include "src/core/SkTraceEvent.h"
12 #include "src/gpu/GrAuditTrail.h"
13 #include "src/gpu/GrCaps.h"
14 #include "src/gpu/GrDefaultGeoProcFactory.h"
15 #include "src/gpu/GrDrawOpTest.h"
16 #include "src/gpu/GrGeometryProcessor.h"
17 #include "src/gpu/GrOpFlushState.h"
18 #include "src/gpu/GrProcessor.h"
19 #include "src/gpu/GrRenderTargetContext.h"
20 #include "src/gpu/GrStyle.h"
21 #include "src/gpu/GrVertexWriter.h"
22 #include "src/gpu/geometry/GrPathUtils.h"
23 #include "src/gpu/geometry/GrShape.h"
24 #include "src/gpu/glsl/GrGLSLGeometryProcessor.h"
25 #include "src/gpu/ops/GrAAConvexTessellator.h"
26 #include "src/gpu/ops/GrAALinearizingConvexPathRenderer.h"
27 #include "src/gpu/ops/GrMeshDrawOp.h"
28 #include "src/gpu/ops/GrSimpleMeshDrawOpHelper.h"
29
30 static const int DEFAULT_BUFFER_SIZE = 100;
31
32 // The thicker the stroke, the harder it is to produce high-quality results using tessellation. For
33 // the time being, we simply drop back to software rendering above this stroke width.
34 static const SkScalar kMaxStrokeWidth = 20.0;
35
GrAALinearizingConvexPathRenderer()36 GrAALinearizingConvexPathRenderer::GrAALinearizingConvexPathRenderer() {
37 }
38
39 ///////////////////////////////////////////////////////////////////////////////
40
41 GrPathRenderer::CanDrawPath
onCanDrawPath(const CanDrawPathArgs & args) const42 GrAALinearizingConvexPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
43 if (GrAAType::kCoverage != args.fAAType) {
44 return CanDrawPath::kNo;
45 }
46 if (!args.fShape->knownToBeConvex()) {
47 return CanDrawPath::kNo;
48 }
49 if (args.fShape->style().pathEffect()) {
50 return CanDrawPath::kNo;
51 }
52 if (args.fShape->inverseFilled()) {
53 return CanDrawPath::kNo;
54 }
55 if (args.fShape->bounds().width() <= 0 && args.fShape->bounds().height() <= 0) {
56 // Stroked zero length lines should draw, but this PR doesn't handle that case
57 return CanDrawPath::kNo;
58 }
59 const SkStrokeRec& stroke = args.fShape->style().strokeRec();
60
61 if (stroke.getStyle() == SkStrokeRec::kStroke_Style ||
62 stroke.getStyle() == SkStrokeRec::kStrokeAndFill_Style) {
63 if (!args.fViewMatrix->isSimilarity()) {
64 return CanDrawPath::kNo;
65 }
66 SkScalar strokeWidth = args.fViewMatrix->getMaxScale() * stroke.getWidth();
67 if (strokeWidth < 1.0f && stroke.getStyle() == SkStrokeRec::kStroke_Style) {
68 return CanDrawPath::kNo;
69 }
70 if (strokeWidth > kMaxStrokeWidth ||
71 !args.fShape->knownToBeClosed() ||
72 stroke.getJoin() == SkPaint::Join::kRound_Join) {
73 return CanDrawPath::kNo;
74 }
75 return CanDrawPath::kYes;
76 }
77 if (stroke.getStyle() != SkStrokeRec::kFill_Style) {
78 return CanDrawPath::kNo;
79 }
80 return CanDrawPath::kYes;
81 }
82
83 // extract the result vertices and indices from the GrAAConvexTessellator
extract_verts(const GrAAConvexTessellator & tess,void * vertData,const GrVertexColor & color,uint16_t firstIndex,uint16_t * idxs)84 static void extract_verts(const GrAAConvexTessellator& tess,
85 void* vertData,
86 const GrVertexColor& color,
87 uint16_t firstIndex,
88 uint16_t* idxs) {
89 GrVertexWriter verts{vertData};
90 for (int i = 0; i < tess.numPts(); ++i) {
91 verts.write(tess.point(i), color, tess.coverage(i));
92 }
93
94 for (int i = 0; i < tess.numIndices(); ++i) {
95 idxs[i] = tess.index(i) + firstIndex;
96 }
97 }
98
create_lines_only_gp(const GrShaderCaps * shaderCaps,bool tweakAlphaForCoverage,const SkMatrix & viewMatrix,bool usesLocalCoords,bool wideColor)99 static sk_sp<GrGeometryProcessor> create_lines_only_gp(const GrShaderCaps* shaderCaps,
100 bool tweakAlphaForCoverage,
101 const SkMatrix& viewMatrix,
102 bool usesLocalCoords,
103 bool wideColor) {
104 using namespace GrDefaultGeoProcFactory;
105
106 Coverage::Type coverageType =
107 tweakAlphaForCoverage ? Coverage::kAttributeTweakAlpha_Type : Coverage::kAttribute_Type;
108 LocalCoords::Type localCoordsType =
109 usesLocalCoords ? LocalCoords::kUsePosition_Type : LocalCoords::kUnused_Type;
110 Color::Type colorType =
111 wideColor ? Color::kPremulWideColorAttribute_Type : Color::kPremulGrColorAttribute_Type;
112
113 return MakeForDeviceSpace(shaderCaps, colorType, coverageType, localCoordsType, viewMatrix);
114 }
115
116 namespace {
117
118 class AAFlatteningConvexPathOp final : public GrMeshDrawOp {
119 private:
120 using Helper = GrSimpleMeshDrawOpHelperWithStencil;
121
122 public:
123 DEFINE_OP_CLASS_ID
Make(GrRecordingContext * context,GrPaint && paint,const SkMatrix & viewMatrix,const SkPath & path,SkScalar strokeWidth,SkStrokeRec::Style style,SkPaint::Join join,SkScalar miterLimit,const GrUserStencilSettings * stencilSettings)124 static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
125 GrPaint&& paint,
126 const SkMatrix& viewMatrix,
127 const SkPath& path,
128 SkScalar strokeWidth,
129 SkStrokeRec::Style style,
130 SkPaint::Join join,
131 SkScalar miterLimit,
132 const GrUserStencilSettings* stencilSettings) {
133 return Helper::FactoryHelper<AAFlatteningConvexPathOp>(context, std::move(paint),
134 viewMatrix, path,
135 strokeWidth, style, join, miterLimit,
136 stencilSettings);
137 }
138
AAFlatteningConvexPathOp(const Helper::MakeArgs & helperArgs,const SkPMColor4f & color,const SkMatrix & viewMatrix,const SkPath & path,SkScalar strokeWidth,SkStrokeRec::Style style,SkPaint::Join join,SkScalar miterLimit,const GrUserStencilSettings * stencilSettings)139 AAFlatteningConvexPathOp(const Helper::MakeArgs& helperArgs,
140 const SkPMColor4f& color,
141 const SkMatrix& viewMatrix,
142 const SkPath& path,
143 SkScalar strokeWidth,
144 SkStrokeRec::Style style,
145 SkPaint::Join join,
146 SkScalar miterLimit,
147 const GrUserStencilSettings* stencilSettings)
148 : INHERITED(ClassID()), fHelper(helperArgs, GrAAType::kCoverage, stencilSettings) {
149 fPaths.emplace_back(
150 PathData{color, viewMatrix, path, strokeWidth, style, join, miterLimit});
151
152 // compute bounds
153 SkRect bounds = path.getBounds();
154 SkScalar w = strokeWidth;
155 if (w > 0) {
156 w /= 2;
157 // If the half stroke width is < 1 then we effectively fallback to bevel joins.
158 if (SkPaint::kMiter_Join == join && w > 1.f) {
159 w *= miterLimit;
160 }
161 bounds.outset(w, w);
162 }
163 this->setTransformedBounds(bounds, viewMatrix, HasAABloat::kYes, IsZeroArea::kNo);
164 }
165
name() const166 const char* name() const override { return "AAFlatteningConvexPathOp"; }
167
visitProxies(const VisitProxyFunc & func) const168 void visitProxies(const VisitProxyFunc& func) const override {
169 fHelper.visitProxies(func);
170 }
171
172 #ifdef SK_DEBUG
dumpInfo() const173 SkString dumpInfo() const override {
174 SkString string;
175 for (const auto& path : fPaths) {
176 string.appendf(
177 "Color: 0x%08x, StrokeWidth: %.2f, Style: %d, Join: %d, "
178 "MiterLimit: %.2f\n",
179 path.fColor.toBytes_RGBA(), path.fStrokeWidth, path.fStyle, path.fJoin,
180 path.fMiterLimit);
181 }
182 string += fHelper.dumpInfo();
183 string += INHERITED::dumpInfo();
184 return string;
185 }
186 #endif
187
fixedFunctionFlags() const188 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
189
finalize(const GrCaps & caps,const GrAppliedClip * clip,bool hasMixedSampledCoverage,GrClampType clampType)190 GrProcessorSet::Analysis finalize(
191 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
192 GrClampType clampType) override {
193 return fHelper.finalizeProcessors(
194 caps, clip, hasMixedSampledCoverage, clampType,
195 GrProcessorAnalysisCoverage::kSingleChannel, &fPaths.back().fColor, &fWideColor);
196 }
197
198 private:
recordDraw(Target * target,sk_sp<const GrGeometryProcessor> gp,int vertexCount,size_t vertexStride,void * vertices,int indexCount,uint16_t * indices) const199 void recordDraw(Target* target, sk_sp<const GrGeometryProcessor> gp, int vertexCount,
200 size_t vertexStride, void* vertices, int indexCount, uint16_t* indices) const {
201 if (vertexCount == 0 || indexCount == 0) {
202 return;
203 }
204 sk_sp<const GrBuffer> vertexBuffer;
205 int firstVertex;
206 void* verts = target->makeVertexSpace(vertexStride, vertexCount, &vertexBuffer,
207 &firstVertex);
208 if (!verts) {
209 SkDebugf("Could not allocate vertices\n");
210 return;
211 }
212 memcpy(verts, vertices, vertexCount * vertexStride);
213
214 sk_sp<const GrBuffer> indexBuffer;
215 int firstIndex;
216 uint16_t* idxs = target->makeIndexSpace(indexCount, &indexBuffer, &firstIndex);
217 if (!idxs) {
218 SkDebugf("Could not allocate indices\n");
219 return;
220 }
221 memcpy(idxs, indices, indexCount * sizeof(uint16_t));
222 GrMesh* mesh = target->allocMesh(GrPrimitiveType::kTriangles);
223 mesh->setIndexed(std::move(indexBuffer), indexCount, firstIndex, 0, vertexCount - 1,
224 GrPrimitiveRestart::kNo);
225 mesh->setVertexData(std::move(vertexBuffer), firstVertex);
226 target->recordDraw(std::move(gp), mesh);
227 }
228
onPrepareDraws(Target * target)229 void onPrepareDraws(Target* target) override {
230 // Setup GrGeometryProcessor
231 sk_sp<GrGeometryProcessor> gp(create_lines_only_gp(target->caps().shaderCaps(),
232 fHelper.compatibleWithCoverageAsAlpha(),
233 this->viewMatrix(),
234 fHelper.usesLocalCoords(),
235 fWideColor));
236 if (!gp) {
237 SkDebugf("Couldn't create a GrGeometryProcessor\n");
238 return;
239 }
240
241 size_t vertexStride = gp->vertexStride();
242 int instanceCount = fPaths.count();
243
244 int64_t vertexCount = 0;
245 int64_t indexCount = 0;
246 int64_t maxVertices = DEFAULT_BUFFER_SIZE;
247 int64_t maxIndices = DEFAULT_BUFFER_SIZE;
248 uint8_t* vertices = (uint8_t*) sk_malloc_throw(maxVertices * vertexStride);
249 uint16_t* indices = (uint16_t*) sk_malloc_throw(maxIndices * sizeof(uint16_t));
250 for (int i = 0; i < instanceCount; i++) {
251 const PathData& args = fPaths[i];
252 GrAAConvexTessellator tess(args.fStyle, args.fStrokeWidth,
253 args.fJoin, args.fMiterLimit);
254
255 if (!tess.tessellate(args.fViewMatrix, args.fPath)) {
256 continue;
257 }
258
259 int currentVertices = tess.numPts();
260 if (vertexCount + currentVertices > static_cast<int>(UINT16_MAX)) {
261 // if we added the current instance, we would overflow the indices we can store in a
262 // uint16_t. Draw what we've got so far and reset.
263 this->recordDraw(
264 target, gp, vertexCount, vertexStride, vertices, indexCount, indices);
265 vertexCount = 0;
266 indexCount = 0;
267 }
268 if (vertexCount + currentVertices > maxVertices) {
269 maxVertices = SkTMax(vertexCount + currentVertices, maxVertices * 2);
270 if (maxVertices * vertexStride > SK_MaxS32) {
271 sk_free(vertices);
272 sk_free(indices);
273 return;
274 }
275 vertices = (uint8_t*) sk_realloc_throw(vertices, maxVertices * vertexStride);
276 }
277 int currentIndices = tess.numIndices();
278 if (indexCount + currentIndices > maxIndices) {
279 maxIndices = SkTMax(indexCount + currentIndices, maxIndices * 2);
280 if (maxIndices * sizeof(uint16_t) > SK_MaxS32) {
281 sk_free(vertices);
282 sk_free(indices);
283 return;
284 }
285 indices = (uint16_t*) sk_realloc_throw(indices, maxIndices * sizeof(uint16_t));
286 }
287
288 extract_verts(tess, vertices + vertexStride * vertexCount,
289 GrVertexColor(args.fColor, fWideColor), vertexCount,
290 indices + indexCount);
291 vertexCount += currentVertices;
292 indexCount += currentIndices;
293 }
294 if (vertexCount <= SK_MaxS32 && indexCount <= SK_MaxS32) {
295 this->recordDraw(target, std::move(gp), vertexCount, vertexStride, vertices, indexCount,
296 indices);
297 }
298 sk_free(vertices);
299 sk_free(indices);
300 }
301
onExecute(GrOpFlushState * flushState,const SkRect & chainBounds)302 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
303 fHelper.executeDrawsAndUploads(this, flushState, chainBounds);
304 }
305
onCombineIfPossible(GrOp * t,const GrCaps & caps)306 CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
307 AAFlatteningConvexPathOp* that = t->cast<AAFlatteningConvexPathOp>();
308 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
309 return CombineResult::kCannotCombine;
310 }
311
312 fPaths.push_back_n(that->fPaths.count(), that->fPaths.begin());
313 fWideColor |= that->fWideColor;
314 return CombineResult::kMerged;
315 }
316
viewMatrix() const317 const SkMatrix& viewMatrix() const { return fPaths[0].fViewMatrix; }
318
319 struct PathData {
320 SkPMColor4f fColor;
321 SkMatrix fViewMatrix;
322 SkPath fPath;
323 SkScalar fStrokeWidth;
324 SkStrokeRec::Style fStyle;
325 SkPaint::Join fJoin;
326 SkScalar fMiterLimit;
327 };
328
329 SkSTArray<1, PathData, true> fPaths;
330 Helper fHelper;
331 bool fWideColor;
332
333 typedef GrMeshDrawOp INHERITED;
334 };
335
336 } // anonymous namespace
337
onDrawPath(const DrawPathArgs & args)338 bool GrAALinearizingConvexPathRenderer::onDrawPath(const DrawPathArgs& args) {
339 GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(),
340 "GrAALinearizingConvexPathRenderer::onDrawPath");
341 SkASSERT(args.fRenderTargetContext->numSamples() <= 1);
342 SkASSERT(!args.fShape->isEmpty());
343 SkASSERT(!args.fShape->style().pathEffect());
344
345 SkPath path;
346 args.fShape->asPath(&path);
347 bool fill = args.fShape->style().isSimpleFill();
348 const SkStrokeRec& stroke = args.fShape->style().strokeRec();
349 SkScalar strokeWidth = fill ? -1.0f : stroke.getWidth();
350 SkPaint::Join join = fill ? SkPaint::Join::kMiter_Join : stroke.getJoin();
351 SkScalar miterLimit = stroke.getMiter();
352
353 std::unique_ptr<GrDrawOp> op = AAFlatteningConvexPathOp::Make(
354 args.fContext, std::move(args.fPaint), *args.fViewMatrix, path, strokeWidth,
355 stroke.getStyle(), join, miterLimit, args.fUserStencilSettings);
356 args.fRenderTargetContext->addDrawOp(*args.fClip, std::move(op));
357 return true;
358 }
359
360 ///////////////////////////////////////////////////////////////////////////////////////////////////
361
362 #if GR_TEST_UTILS
363
GR_DRAW_OP_TEST_DEFINE(AAFlatteningConvexPathOp)364 GR_DRAW_OP_TEST_DEFINE(AAFlatteningConvexPathOp) {
365 SkMatrix viewMatrix = GrTest::TestMatrixPreservesRightAngles(random);
366 SkPath path = GrTest::TestPathConvex(random);
367
368 SkStrokeRec::Style styles[3] = { SkStrokeRec::kFill_Style,
369 SkStrokeRec::kStroke_Style,
370 SkStrokeRec::kStrokeAndFill_Style };
371
372 SkStrokeRec::Style style = styles[random->nextU() % 3];
373
374 SkScalar strokeWidth = -1.f;
375 SkPaint::Join join = SkPaint::kMiter_Join;
376 SkScalar miterLimit = 0.5f;
377
378 if (SkStrokeRec::kFill_Style != style) {
379 strokeWidth = random->nextRangeF(1.0f, 10.0f);
380 if (random->nextBool()) {
381 join = SkPaint::kMiter_Join;
382 } else {
383 join = SkPaint::kBevel_Join;
384 }
385 miterLimit = random->nextRangeF(0.5f, 2.0f);
386 }
387 const GrUserStencilSettings* stencilSettings = GrGetRandomStencil(random, context);
388 return AAFlatteningConvexPathOp::Make(context, std::move(paint), viewMatrix, path, strokeWidth,
389 style, join, miterLimit, stencilSettings);
390 }
391
392 #endif
393