blob: b17bf1a4d5936f644b9cada12d71e0dc30f100ad [file] [log] [blame]
senorblancod6ed19c2015-02-26 06:58:17 -08001/*
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 "GrTessellatingPathRenderer.h"
9
robertphillips976f5f02016-06-03 10:59:20 -070010#include "GrAuditTrail.h"
bsalomon75398562015-08-17 12:55:38 -070011#include "GrBatchFlushState.h"
joshualitt2fbd4062015-05-07 13:06:41 -070012#include "GrBatchTest.h"
robertphillips976f5f02016-06-03 10:59:20 -070013#include "GrClip.h"
senorblancod6ed19c2015-02-26 06:58:17 -080014#include "GrDefaultGeoProcFactory.h"
egdaniel0e1853c2016-03-17 11:35:45 -070015#include "GrMesh.h"
senorblancod6ed19c2015-02-26 06:58:17 -080016#include "GrPathUtils.h"
senorblanco84cd6212015-08-04 10:01:58 -070017#include "GrResourceCache.h"
18#include "GrResourceProvider.h"
ethannicholase9709e82016-01-07 13:34:16 -080019#include "GrTessellator.h"
senorblancod6ed19c2015-02-26 06:58:17 -080020#include "SkGeometry.h"
21
bsalomon16b99132015-08-13 14:55:50 -070022#include "batches/GrVertexBatch.h"
joshualitt74417822015-08-07 11:42:16 -070023
senorblancod6ed19c2015-02-26 06:58:17 -080024#include <stdio.h>
25
26/*
ethannicholase9709e82016-01-07 13:34:16 -080027 * This path renderer tessellates the path into triangles using GrTessellator, uploads the triangles
halcanary9d524f22016-03-29 09:03:52 -070028 * to a vertex buffer, and renders them with a single draw call. It does not currently do
senorblancod6ed19c2015-02-26 06:58:17 -080029 * antialiasing, so it must be used in conjunction with multisampling.
senorblancod6ed19c2015-02-26 06:58:17 -080030 */
senorblancod6ed19c2015-02-26 06:58:17 -080031namespace {
32
senorblanco84cd6212015-08-04 10:01:58 -070033struct TessInfo {
34 SkScalar fTolerance;
senorblanco06f989a2015-09-02 09:05:17 -070035 int fCount;
senorblanco84cd6212015-08-04 10:01:58 -070036};
37
ethannicholase9709e82016-01-07 13:34:16 -080038// When the SkPathRef genID changes, invalidate a corresponding GrResource described by key.
39class PathInvalidator : public SkPathRef::GenIDChangeListener {
40public:
41 explicit PathInvalidator(const GrUniqueKey& key) : fMsg(key) {}
42private:
43 GrUniqueKeyInvalidatedMessage fMsg;
44
45 void onChange() override {
46 SkMessageBus<GrUniqueKeyInvalidatedMessage>::Post(fMsg);
47 }
48};
49
cdalton397536c2016-03-25 12:15:03 -070050bool cache_match(GrBuffer* vertexBuffer, SkScalar tol, int* actualCount) {
senorblanco84cd6212015-08-04 10:01:58 -070051 if (!vertexBuffer) {
52 return false;
53 }
54 const SkData* data = vertexBuffer->getUniqueKey().getCustomData();
55 SkASSERT(data);
56 const TessInfo* info = static_cast<const TessInfo*>(data->data());
57 if (info->fTolerance == 0 || info->fTolerance < 3.0f * tol) {
senorblanco06f989a2015-09-02 09:05:17 -070058 *actualCount = info->fCount;
senorblanco84cd6212015-08-04 10:01:58 -070059 return true;
60 }
61 return false;
62}
63
senorblanco6599eff2016-03-10 08:38:45 -080064class StaticVertexAllocator : public GrTessellator::VertexAllocator {
65public:
senorblancoaf1e21e2016-03-16 10:25:58 -070066 StaticVertexAllocator(GrResourceProvider* resourceProvider, bool canMapVB)
67 : fResourceProvider(resourceProvider)
senorblanco6599eff2016-03-10 08:38:45 -080068 , fCanMapVB(canMapVB)
69 , fVertices(nullptr) {
70 }
71 SkPoint* lock(int vertexCount) override {
72 size_t size = vertexCount * sizeof(SkPoint);
cdalton397536c2016-03-25 12:15:03 -070073 fVertexBuffer.reset(fResourceProvider->createBuffer(
cdaltone2e71c22016-04-07 18:13:29 -070074 size, kVertex_GrBufferType, kStatic_GrAccessPattern, 0));
senorblanco6599eff2016-03-10 08:38:45 -080075 if (!fVertexBuffer.get()) {
76 return nullptr;
77 }
78 if (fCanMapVB) {
79 fVertices = static_cast<SkPoint*>(fVertexBuffer->map());
80 } else {
81 fVertices = new SkPoint[vertexCount];
82 }
83 return fVertices;
84 }
85 void unlock(int actualCount) override {
86 if (fCanMapVB) {
87 fVertexBuffer->unmap();
88 } else {
89 fVertexBuffer->updateData(fVertices, actualCount * sizeof(SkPoint));
90 delete[] fVertices;
91 }
92 fVertices = nullptr;
93 }
cdalton397536c2016-03-25 12:15:03 -070094 GrBuffer* vertexBuffer() { return fVertexBuffer.get(); }
senorblanco6599eff2016-03-10 08:38:45 -080095private:
cdalton397536c2016-03-25 12:15:03 -070096 SkAutoTUnref<GrBuffer> fVertexBuffer;
senorblanco6599eff2016-03-10 08:38:45 -080097 GrResourceProvider* fResourceProvider;
98 bool fCanMapVB;
99 SkPoint* fVertices;
100};
101
ethannicholase9709e82016-01-07 13:34:16 -0800102} // namespace
senorblancod6ed19c2015-02-26 06:58:17 -0800103
104GrTessellatingPathRenderer::GrTessellatingPathRenderer() {
105}
106
bsalomon0aff2fa2015-07-31 06:48:27 -0700107bool GrTessellatingPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
bsalomonee432412016-06-27 07:18:18 -0700108 // This path renderer can draw fill styles but does not do antialiasing. It can do convex and
109 // concave paths, but we'll leave the convex ones to simpler algorithms. We pass on paths that
110 // have styles, though they may come back around after applying the styling information to the
111 // geometry to create a filled path. We also skip paths that don't have a key since the real
112 // advantage of this path renderer comes from caching the tessellated geometry.
113 return !args.fShape->style().applies() && args.fShape->style().isSimpleFill() &&
114 !args.fAntiAlias && args.fShape->hasUnstyledKey() && !args.fShape->knownToBeConvex();
senorblancod6ed19c2015-02-26 06:58:17 -0800115}
116
bsalomonabd30f52015-08-13 13:34:48 -0700117class TessellatingPathBatch : public GrVertexBatch {
senorblanco9ba39722015-03-05 07:13:42 -0800118public:
reed1b55a962015-09-17 20:16:13 -0700119 DEFINE_BATCH_CLASS_ID
senorblanco9ba39722015-03-05 07:13:42 -0800120
bsalomonabd30f52015-08-13 13:34:48 -0700121 static GrDrawBatch* Create(const GrColor& color,
bsalomonee432412016-06-27 07:18:18 -0700122 const GrShape& shape,
bsalomonabd30f52015-08-13 13:34:48 -0700123 const SkMatrix& viewMatrix,
124 SkRect clipBounds) {
bsalomonee432412016-06-27 07:18:18 -0700125 return new TessellatingPathBatch(color, shape, viewMatrix, clipBounds);
senorblanco9ba39722015-03-05 07:13:42 -0800126 }
127
mtklein36352bf2015-03-25 18:17:31 -0700128 const char* name() const override { return "TessellatingPathBatch"; }
senorblanco9ba39722015-03-05 07:13:42 -0800129
halcanary9d524f22016-03-29 09:03:52 -0700130 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -0800131 GrInitInvariantOutput* coverage,
132 GrBatchToXPOverrides* overrides) const override {
133 color->setKnownFourComponents(fColor);
134 coverage->setUnknownSingleComponent();
senorblanco9ba39722015-03-05 07:13:42 -0800135 }
136
bsalomone46f9fe2015-08-18 06:05:14 -0700137private:
ethannicholasff210322015-11-24 12:10:10 -0800138 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
senorblanco9ba39722015-03-05 07:13:42 -0800139 // Handle any color overrides
ethannicholasff210322015-11-24 12:10:10 -0800140 if (!overrides.readsColor()) {
senorblanco9ba39722015-03-05 07:13:42 -0800141 fColor = GrColor_ILLEGAL;
senorblanco9ba39722015-03-05 07:13:42 -0800142 }
ethannicholasff210322015-11-24 12:10:10 -0800143 overrides.getOverrideColorIfSet(&fColor);
144 fPipelineInfo = overrides;
senorblanco9ba39722015-03-05 07:13:42 -0800145 }
146
senorblanco6599eff2016-03-10 08:38:45 -0800147 void draw(Target* target, const GrGeometryProcessor* gp) const {
bsalomon6663acf2016-05-10 09:14:17 -0700148 GrResourceProvider* rp = target->resourceProvider();
149 SkScalar screenSpaceTol = GrPathUtils::kDefaultTolerance;
150 SkScalar tol = GrPathUtils::scaleToleranceToSrc(screenSpaceTol, fViewMatrix,
bsalomonee432412016-06-27 07:18:18 -0700151 fShape.bounds());
bsalomon6663acf2016-05-10 09:14:17 -0700152
bsalomonee432412016-06-27 07:18:18 -0700153 SkPath path;
154 fShape.asPath(&path);
155 bool inverseFill = path.isInverseFillType();
senorblanco84cd6212015-08-04 10:01:58 -0700156 // construct a cache key from the path's genID and the view matrix
157 static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
158 GrUniqueKey key;
bsalomonee432412016-06-27 07:18:18 -0700159 static constexpr int kClipBoundsCnt = sizeof(fClipBounds) / sizeof(uint32_t);
160 int shapeKeyDataCnt = fShape.unstyledKeySize();
161 SkASSERT(shapeKeyDataCnt >= 0);
162 GrUniqueKey::Builder builder(&key, kDomain, shapeKeyDataCnt + kClipBoundsCnt);
163 fShape.writeUnstyledKey(&builder[0]);
164 // For inverse fills, the tessellation is dependent on clip bounds.
165 if (inverseFill) {
166 memcpy(&builder[shapeKeyDataCnt], &fClipBounds, sizeof(fClipBounds));
167 } else {
168 memset(&builder[shapeKeyDataCnt], 0, sizeof(fClipBounds));
169 }
170 builder.finish();
171 SkAutoTUnref<GrBuffer> cachedVertexBuffer(rp->findAndRefTByUniqueKey<GrBuffer>(key));
172 int actualCount;
173 if (cache_match(cachedVertexBuffer.get(), tol, &actualCount)) {
174 this->drawVertices(target, gp, cachedVertexBuffer.get(), 0, actualCount);
175 return;
senorblanco84cd6212015-08-04 10:01:58 -0700176 }
177
senorblanco6599eff2016-03-10 08:38:45 -0800178 bool isLinear;
179 bool canMapVB = GrCaps::kNone_MapFlags != target->caps().mapBufferFlags();
senorblancoaf1e21e2016-03-16 10:25:58 -0700180 StaticVertexAllocator allocator(rp, canMapVB);
senorblanco6599eff2016-03-10 08:38:45 -0800181 int count = GrTessellator::PathToTriangles(path, tol, fClipBounds, &allocator, &isLinear);
182 if (count == 0) {
183 return;
184 }
senorblancoaf1e21e2016-03-16 10:25:58 -0700185 this->drawVertices(target, gp, allocator.vertexBuffer(), 0, count);
bsalomonee432412016-06-27 07:18:18 -0700186 TessInfo info;
187 info.fTolerance = isLinear ? 0 : tol;
188 info.fCount = count;
189 SkAutoTUnref<SkData> data(SkData::NewWithCopy(&info, sizeof(info)));
190 key.setCustomData(data.get());
191 rp->assignUniqueKeyToResource(key, allocator.vertexBuffer());
senorblanco6599eff2016-03-10 08:38:45 -0800192 }
193
194 void onPrepareDraws(Target* target) const override {
bungeman06ca8ec2016-06-09 08:01:03 -0700195 sk_sp<GrGeometryProcessor> gp;
joshualittdf0c5572015-08-03 11:35:28 -0700196 {
197 using namespace GrDefaultGeoProcFactory;
198
199 Color color(fColor);
200 LocalCoords localCoords(fPipelineInfo.readsLocalCoords() ?
201 LocalCoords::kUsePosition_Type :
202 LocalCoords::kUnused_Type);
203 Coverage::Type coverageType;
204 if (fPipelineInfo.readsCoverage()) {
205 coverageType = Coverage::kSolid_Type;
206 } else {
207 coverageType = Coverage::kNone_Type;
208 }
209 Coverage coverage(coverageType);
bungeman06ca8ec2016-06-09 08:01:03 -0700210 gp = GrDefaultGeoProcFactory::Make(color, coverage, localCoords, fViewMatrix);
joshualittdf0c5572015-08-03 11:35:28 -0700211 }
senorblanco6599eff2016-03-10 08:38:45 -0800212 this->draw(target, gp.get());
213 }
senorblanco84cd6212015-08-04 10:01:58 -0700214
cdalton397536c2016-03-25 12:15:03 -0700215 void drawVertices(Target* target, const GrGeometryProcessor* gp, const GrBuffer* vb,
senorblanco6599eff2016-03-10 08:38:45 -0800216 int firstVertex, int count) const {
senorblanco84cd6212015-08-04 10:01:58 -0700217 SkASSERT(gp->getVertexStride() == sizeof(SkPoint));
senorblanco9ba39722015-03-05 07:13:42 -0800218
ethannicholase9709e82016-01-07 13:34:16 -0800219 GrPrimitiveType primitiveType = TESSELLATOR_WIREFRAME ? kLines_GrPrimitiveType
220 : kTriangles_GrPrimitiveType;
egdaniel0e1853c2016-03-17 11:35:45 -0700221 GrMesh mesh;
222 mesh.init(primitiveType, vb, firstVertex, count);
bsalomon342bfc22016-04-01 06:06:20 -0700223 target->draw(gp, mesh);
senorblanco9ba39722015-03-05 07:13:42 -0800224 }
225
bsalomoncb02b382015-08-12 11:14:50 -0700226 bool onCombineIfPossible(GrBatch*, const GrCaps&) override { return false; }
senorblanco9ba39722015-03-05 07:13:42 -0800227
senorblanco9ba39722015-03-05 07:13:42 -0800228 TessellatingPathBatch(const GrColor& color,
bsalomonee432412016-06-27 07:18:18 -0700229 const GrShape& shape,
senorblanco9ba39722015-03-05 07:13:42 -0800230 const SkMatrix& viewMatrix,
231 const SkRect& clipBounds)
reed1b55a962015-09-17 20:16:13 -0700232 : INHERITED(ClassID())
233 , fColor(color)
bsalomonee432412016-06-27 07:18:18 -0700234 , fShape(shape)
bsalomondb4758c2015-11-23 11:14:20 -0800235 , fViewMatrix(viewMatrix) {
bsalomonee432412016-06-27 07:18:18 -0700236 const SkRect& pathBounds = shape.bounds();
bsalomondb4758c2015-11-23 11:14:20 -0800237 fClipBounds = clipBounds;
238 // Because the clip bounds are used to add a contour for inverse fills, they must also
239 // include the path bounds.
240 fClipBounds.join(pathBounds);
bsalomonee432412016-06-27 07:18:18 -0700241 if (shape.inverseFilled()) {
bsalomondb4758c2015-11-23 11:14:20 -0800242 fBounds = fClipBounds;
243 } else {
bsalomonee432412016-06-27 07:18:18 -0700244 fBounds = pathBounds;
bsalomondb4758c2015-11-23 11:14:20 -0800245 }
joshualitt99c7c072015-05-01 13:43:30 -0700246 viewMatrix.mapRect(&fBounds);
senorblanco9ba39722015-03-05 07:13:42 -0800247 }
248
bsalomon91d844d2015-08-10 10:47:29 -0700249 GrColor fColor;
bsalomonee432412016-06-27 07:18:18 -0700250 GrShape fShape;
bsalomon91d844d2015-08-10 10:47:29 -0700251 SkMatrix fViewMatrix;
252 SkRect fClipBounds; // in source space
ethannicholasff210322015-11-24 12:10:10 -0800253 GrXPOverridesForBatch fPipelineInfo;
reed1b55a962015-09-17 20:16:13 -0700254
255 typedef GrVertexBatch INHERITED;
senorblanco9ba39722015-03-05 07:13:42 -0800256};
257
bsalomon0aff2fa2015-07-31 06:48:27 -0700258bool GrTessellatingPathRenderer::onDrawPath(const DrawPathArgs& args) {
robertphillips976f5f02016-06-03 10:59:20 -0700259 GR_AUDIT_TRAIL_AUTO_FRAME(args.fDrawContext->auditTrail(),
joshualittde83b412016-01-14 09:58:36 -0800260 "GrTessellatingPathRenderer::onDrawPath");
bsalomon0aff2fa2015-07-31 06:48:27 -0700261 SkASSERT(!args.fAntiAlias);
senorblancod6ed19c2015-02-26 06:58:17 -0800262
senorblancod6ed19c2015-02-26 06:58:17 -0800263 SkIRect clipBoundsI;
robertphillips976f5f02016-06-03 10:59:20 -0700264 args.fClip->getConservativeBounds(args.fDrawContext->width(), args.fDrawContext->height(),
265 &clipBoundsI);
senorblancod6ed19c2015-02-26 06:58:17 -0800266 SkRect clipBounds = SkRect::Make(clipBoundsI);
267 SkMatrix vmi;
bsalomon0aff2fa2015-07-31 06:48:27 -0700268 if (!args.fViewMatrix->invert(&vmi)) {
senorblancod6ed19c2015-02-26 06:58:17 -0800269 return false;
270 }
271 vmi.mapRect(&clipBounds);
bsalomon8acedde2016-06-24 10:42:16 -0700272 SkPath path;
273 args.fShape->asPath(&path);
bsalomonee432412016-06-27 07:18:18 -0700274 SkAutoTUnref<GrDrawBatch> batch(TessellatingPathBatch::Create(args.fColor, *args.fShape,
275 *args.fViewMatrix, clipBounds));
robertphillips976f5f02016-06-03 10:59:20 -0700276
csmartdaltonecbc12b2016-06-08 10:08:43 -0700277 GrPipelineBuilder pipelineBuilder(*args.fPaint, args.fDrawContext->mustUseHWAA(*args.fPaint));
robertphillips976f5f02016-06-03 10:59:20 -0700278 pipelineBuilder.setUserStencil(args.fUserStencilSettings);
279
280 args.fDrawContext->drawBatch(pipelineBuilder, *args.fClip, batch);
senorblancod6ed19c2015-02-26 06:58:17 -0800281
282 return true;
283}
joshualitt2fbd4062015-05-07 13:06:41 -0700284
285///////////////////////////////////////////////////////////////////////////////////////////////////
286
287#ifdef GR_TEST_UTILS
288
bsalomonabd30f52015-08-13 13:34:48 -0700289DRAW_BATCH_TEST_DEFINE(TesselatingPathBatch) {
joshualitt2fbd4062015-05-07 13:06:41 -0700290 GrColor color = GrRandomColor(random);
291 SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random);
292 SkPath path = GrTest::TestPath(random);
293 SkRect clipBounds = GrTest::TestRect(random);
294 SkMatrix vmi;
295 bool result = viewMatrix.invert(&vmi);
296 if (!result) {
297 SkFAIL("Cannot invert matrix\n");
298 }
299 vmi.mapRect(&clipBounds);
bsalomon6663acf2016-05-10 09:14:17 -0700300 GrStyle style;
301 do {
302 GrTest::TestStyle(random, &style);
303 } while (style.strokeRec().isHairlineStyle());
bsalomonee432412016-06-27 07:18:18 -0700304 GrShape shape(path, style);
305 return TessellatingPathBatch::Create(color, shape, viewMatrix, clipBounds);
joshualitt2fbd4062015-05-07 13:06:41 -0700306}
307
308#endif