blob: b022e40330d45c01c50fa3a837a480faf7d643a9 [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);
bsalomon88cf17d2016-07-08 06:40:56 -0700241 const SkRect& srcBounds = shape.inverseFilled() ? fClipBounds : pathBounds;
242 this->setTransformedBounds(srcBounds, viewMatrix, HasAABloat::kNo, IsZeroArea::kNo);
senorblanco9ba39722015-03-05 07:13:42 -0800243 }
244
bsalomon91d844d2015-08-10 10:47:29 -0700245 GrColor fColor;
bsalomonee432412016-06-27 07:18:18 -0700246 GrShape fShape;
bsalomon91d844d2015-08-10 10:47:29 -0700247 SkMatrix fViewMatrix;
248 SkRect fClipBounds; // in source space
ethannicholasff210322015-11-24 12:10:10 -0800249 GrXPOverridesForBatch fPipelineInfo;
reed1b55a962015-09-17 20:16:13 -0700250
251 typedef GrVertexBatch INHERITED;
senorblanco9ba39722015-03-05 07:13:42 -0800252};
253
bsalomon0aff2fa2015-07-31 06:48:27 -0700254bool GrTessellatingPathRenderer::onDrawPath(const DrawPathArgs& args) {
robertphillips976f5f02016-06-03 10:59:20 -0700255 GR_AUDIT_TRAIL_AUTO_FRAME(args.fDrawContext->auditTrail(),
joshualittde83b412016-01-14 09:58:36 -0800256 "GrTessellatingPathRenderer::onDrawPath");
bsalomon0aff2fa2015-07-31 06:48:27 -0700257 SkASSERT(!args.fAntiAlias);
senorblancod6ed19c2015-02-26 06:58:17 -0800258
senorblancod6ed19c2015-02-26 06:58:17 -0800259 SkIRect clipBoundsI;
robertphillips976f5f02016-06-03 10:59:20 -0700260 args.fClip->getConservativeBounds(args.fDrawContext->width(), args.fDrawContext->height(),
261 &clipBoundsI);
senorblancod6ed19c2015-02-26 06:58:17 -0800262 SkRect clipBounds = SkRect::Make(clipBoundsI);
263 SkMatrix vmi;
bsalomon0aff2fa2015-07-31 06:48:27 -0700264 if (!args.fViewMatrix->invert(&vmi)) {
senorblancod6ed19c2015-02-26 06:58:17 -0800265 return false;
266 }
267 vmi.mapRect(&clipBounds);
bsalomon8acedde2016-06-24 10:42:16 -0700268 SkPath path;
269 args.fShape->asPath(&path);
robertphillips3950f0d2016-07-07 07:33:13 -0700270 SkAutoTUnref<GrDrawBatch> batch(TessellatingPathBatch::Create(args.fPaint->getColor(),
271 *args.fShape,
bsalomonee432412016-06-27 07:18:18 -0700272 *args.fViewMatrix, clipBounds));
robertphillips976f5f02016-06-03 10:59:20 -0700273
csmartdaltonecbc12b2016-06-08 10:08:43 -0700274 GrPipelineBuilder pipelineBuilder(*args.fPaint, args.fDrawContext->mustUseHWAA(*args.fPaint));
robertphillips976f5f02016-06-03 10:59:20 -0700275 pipelineBuilder.setUserStencil(args.fUserStencilSettings);
276
277 args.fDrawContext->drawBatch(pipelineBuilder, *args.fClip, batch);
senorblancod6ed19c2015-02-26 06:58:17 -0800278
279 return true;
280}
joshualitt2fbd4062015-05-07 13:06:41 -0700281
282///////////////////////////////////////////////////////////////////////////////////////////////////
283
284#ifdef GR_TEST_UTILS
285
bsalomonabd30f52015-08-13 13:34:48 -0700286DRAW_BATCH_TEST_DEFINE(TesselatingPathBatch) {
joshualitt2fbd4062015-05-07 13:06:41 -0700287 GrColor color = GrRandomColor(random);
288 SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random);
289 SkPath path = GrTest::TestPath(random);
290 SkRect clipBounds = GrTest::TestRect(random);
291 SkMatrix vmi;
292 bool result = viewMatrix.invert(&vmi);
293 if (!result) {
294 SkFAIL("Cannot invert matrix\n");
295 }
296 vmi.mapRect(&clipBounds);
bsalomon6663acf2016-05-10 09:14:17 -0700297 GrStyle style;
298 do {
299 GrTest::TestStyle(random, &style);
300 } while (style.strokeRec().isHairlineStyle());
bsalomonee432412016-06-27 07:18:18 -0700301 GrShape shape(path, style);
302 return TessellatingPathBatch::Create(color, shape, viewMatrix, clipBounds);
joshualitt2fbd4062015-05-07 13:06:41 -0700303}
304
305#endif