blob: 6dce9f73c839d63432e59fe310ab7c9e77b23141 [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"
bsalomonbb243832016-07-22 07:10:19 -070017#include "GrPipelineBuilder.h"
senorblanco84cd6212015-08-04 10:01:58 -070018#include "GrResourceCache.h"
19#include "GrResourceProvider.h"
ethannicholase9709e82016-01-07 13:34:16 -080020#include "GrTessellator.h"
senorblancod6ed19c2015-02-26 06:58:17 -080021#include "SkGeometry.h"
22
bsalomon16b99132015-08-13 14:55:50 -070023#include "batches/GrVertexBatch.h"
joshualitt74417822015-08-07 11:42:16 -070024
senorblancod6ed19c2015-02-26 06:58:17 -080025#include <stdio.h>
26
27/*
ethannicholase9709e82016-01-07 13:34:16 -080028 * This path renderer tessellates the path into triangles using GrTessellator, uploads the triangles
halcanary9d524f22016-03-29 09:03:52 -070029 * to a vertex buffer, and renders them with a single draw call. It does not currently do
senorblancod6ed19c2015-02-26 06:58:17 -080030 * antialiasing, so it must be used in conjunction with multisampling.
senorblancod6ed19c2015-02-26 06:58:17 -080031 */
senorblancod6ed19c2015-02-26 06:58:17 -080032namespace {
33
senorblanco84cd6212015-08-04 10:01:58 -070034struct TessInfo {
35 SkScalar fTolerance;
senorblanco06f989a2015-09-02 09:05:17 -070036 int fCount;
senorblanco84cd6212015-08-04 10:01:58 -070037};
38
ethannicholase9709e82016-01-07 13:34:16 -080039// When the SkPathRef genID changes, invalidate a corresponding GrResource described by key.
40class PathInvalidator : public SkPathRef::GenIDChangeListener {
41public:
42 explicit PathInvalidator(const GrUniqueKey& key) : fMsg(key) {}
43private:
44 GrUniqueKeyInvalidatedMessage fMsg;
45
46 void onChange() override {
47 SkMessageBus<GrUniqueKeyInvalidatedMessage>::Post(fMsg);
48 }
49};
50
cdalton397536c2016-03-25 12:15:03 -070051bool cache_match(GrBuffer* vertexBuffer, SkScalar tol, int* actualCount) {
senorblanco84cd6212015-08-04 10:01:58 -070052 if (!vertexBuffer) {
53 return false;
54 }
55 const SkData* data = vertexBuffer->getUniqueKey().getCustomData();
56 SkASSERT(data);
57 const TessInfo* info = static_cast<const TessInfo*>(data->data());
58 if (info->fTolerance == 0 || info->fTolerance < 3.0f * tol) {
senorblanco06f989a2015-09-02 09:05:17 -070059 *actualCount = info->fCount;
senorblanco84cd6212015-08-04 10:01:58 -070060 return true;
61 }
62 return false;
63}
64
senorblanco6599eff2016-03-10 08:38:45 -080065class StaticVertexAllocator : public GrTessellator::VertexAllocator {
66public:
senorblancoaf1e21e2016-03-16 10:25:58 -070067 StaticVertexAllocator(GrResourceProvider* resourceProvider, bool canMapVB)
68 : fResourceProvider(resourceProvider)
senorblanco6599eff2016-03-10 08:38:45 -080069 , fCanMapVB(canMapVB)
70 , fVertices(nullptr) {
71 }
72 SkPoint* lock(int vertexCount) override {
73 size_t size = vertexCount * sizeof(SkPoint);
cdalton397536c2016-03-25 12:15:03 -070074 fVertexBuffer.reset(fResourceProvider->createBuffer(
cdaltone2e71c22016-04-07 18:13:29 -070075 size, kVertex_GrBufferType, kStatic_GrAccessPattern, 0));
senorblanco6599eff2016-03-10 08:38:45 -080076 if (!fVertexBuffer.get()) {
77 return nullptr;
78 }
79 if (fCanMapVB) {
80 fVertices = static_cast<SkPoint*>(fVertexBuffer->map());
81 } else {
82 fVertices = new SkPoint[vertexCount];
83 }
84 return fVertices;
85 }
86 void unlock(int actualCount) override {
87 if (fCanMapVB) {
88 fVertexBuffer->unmap();
89 } else {
90 fVertexBuffer->updateData(fVertices, actualCount * sizeof(SkPoint));
91 delete[] fVertices;
92 }
93 fVertices = nullptr;
94 }
cdalton397536c2016-03-25 12:15:03 -070095 GrBuffer* vertexBuffer() { return fVertexBuffer.get(); }
senorblanco6599eff2016-03-10 08:38:45 -080096private:
cdalton397536c2016-03-25 12:15:03 -070097 SkAutoTUnref<GrBuffer> fVertexBuffer;
senorblanco6599eff2016-03-10 08:38:45 -080098 GrResourceProvider* fResourceProvider;
99 bool fCanMapVB;
100 SkPoint* fVertices;
101};
102
ethannicholase9709e82016-01-07 13:34:16 -0800103} // namespace
senorblancod6ed19c2015-02-26 06:58:17 -0800104
105GrTessellatingPathRenderer::GrTessellatingPathRenderer() {
106}
107
bsalomon0aff2fa2015-07-31 06:48:27 -0700108bool GrTessellatingPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
bsalomonee432412016-06-27 07:18:18 -0700109 // This path renderer can draw fill styles but does not do antialiasing. It can do convex and
110 // concave paths, but we'll leave the convex ones to simpler algorithms. We pass on paths that
111 // have styles, though they may come back around after applying the styling information to the
112 // geometry to create a filled path. We also skip paths that don't have a key since the real
113 // advantage of this path renderer comes from caching the tessellated geometry.
114 return !args.fShape->style().applies() && args.fShape->style().isSimpleFill() &&
115 !args.fAntiAlias && args.fShape->hasUnstyledKey() && !args.fShape->knownToBeConvex();
senorblancod6ed19c2015-02-26 06:58:17 -0800116}
117
bsalomonabd30f52015-08-13 13:34:48 -0700118class TessellatingPathBatch : public GrVertexBatch {
senorblanco9ba39722015-03-05 07:13:42 -0800119public:
reed1b55a962015-09-17 20:16:13 -0700120 DEFINE_BATCH_CLASS_ID
senorblanco9ba39722015-03-05 07:13:42 -0800121
bsalomonabd30f52015-08-13 13:34:48 -0700122 static GrDrawBatch* Create(const GrColor& color,
bsalomonee432412016-06-27 07:18:18 -0700123 const GrShape& shape,
bsalomonabd30f52015-08-13 13:34:48 -0700124 const SkMatrix& viewMatrix,
125 SkRect clipBounds) {
bsalomonee432412016-06-27 07:18:18 -0700126 return new TessellatingPathBatch(color, shape, viewMatrix, clipBounds);
senorblanco9ba39722015-03-05 07:13:42 -0800127 }
128
mtklein36352bf2015-03-25 18:17:31 -0700129 const char* name() const override { return "TessellatingPathBatch"; }
senorblanco9ba39722015-03-05 07:13:42 -0800130
halcanary9d524f22016-03-29 09:03:52 -0700131 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -0800132 GrInitInvariantOutput* coverage,
133 GrBatchToXPOverrides* overrides) const override {
134 color->setKnownFourComponents(fColor);
135 coverage->setUnknownSingleComponent();
senorblanco9ba39722015-03-05 07:13:42 -0800136 }
137
bsalomone46f9fe2015-08-18 06:05:14 -0700138private:
ethannicholasff210322015-11-24 12:10:10 -0800139 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
senorblanco9ba39722015-03-05 07:13:42 -0800140 // Handle any color overrides
ethannicholasff210322015-11-24 12:10:10 -0800141 if (!overrides.readsColor()) {
senorblanco9ba39722015-03-05 07:13:42 -0800142 fColor = GrColor_ILLEGAL;
senorblanco9ba39722015-03-05 07:13:42 -0800143 }
ethannicholasff210322015-11-24 12:10:10 -0800144 overrides.getOverrideColorIfSet(&fColor);
145 fPipelineInfo = overrides;
senorblanco9ba39722015-03-05 07:13:42 -0800146 }
147
senorblanco6599eff2016-03-10 08:38:45 -0800148 void draw(Target* target, const GrGeometryProcessor* gp) const {
bsalomon6663acf2016-05-10 09:14:17 -0700149 GrResourceProvider* rp = target->resourceProvider();
150 SkScalar screenSpaceTol = GrPathUtils::kDefaultTolerance;
151 SkScalar tol = GrPathUtils::scaleToleranceToSrc(screenSpaceTol, fViewMatrix,
bsalomonee432412016-06-27 07:18:18 -0700152 fShape.bounds());
bsalomon6663acf2016-05-10 09:14:17 -0700153
bsalomonee432412016-06-27 07:18:18 -0700154 SkPath path;
155 fShape.asPath(&path);
156 bool inverseFill = path.isInverseFillType();
senorblanco84cd6212015-08-04 10:01:58 -0700157 // construct a cache key from the path's genID and the view matrix
158 static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
159 GrUniqueKey key;
bsalomonee432412016-06-27 07:18:18 -0700160 static constexpr int kClipBoundsCnt = sizeof(fClipBounds) / sizeof(uint32_t);
161 int shapeKeyDataCnt = fShape.unstyledKeySize();
162 SkASSERT(shapeKeyDataCnt >= 0);
163 GrUniqueKey::Builder builder(&key, kDomain, shapeKeyDataCnt + kClipBoundsCnt);
164 fShape.writeUnstyledKey(&builder[0]);
165 // For inverse fills, the tessellation is dependent on clip bounds.
166 if (inverseFill) {
167 memcpy(&builder[shapeKeyDataCnt], &fClipBounds, sizeof(fClipBounds));
168 } else {
169 memset(&builder[shapeKeyDataCnt], 0, sizeof(fClipBounds));
170 }
171 builder.finish();
172 SkAutoTUnref<GrBuffer> cachedVertexBuffer(rp->findAndRefTByUniqueKey<GrBuffer>(key));
173 int actualCount;
174 if (cache_match(cachedVertexBuffer.get(), tol, &actualCount)) {
175 this->drawVertices(target, gp, cachedVertexBuffer.get(), 0, actualCount);
176 return;
senorblanco84cd6212015-08-04 10:01:58 -0700177 }
178
senorblanco6599eff2016-03-10 08:38:45 -0800179 bool isLinear;
180 bool canMapVB = GrCaps::kNone_MapFlags != target->caps().mapBufferFlags();
senorblancoaf1e21e2016-03-16 10:25:58 -0700181 StaticVertexAllocator allocator(rp, canMapVB);
senorblanco6599eff2016-03-10 08:38:45 -0800182 int count = GrTessellator::PathToTriangles(path, tol, fClipBounds, &allocator, &isLinear);
183 if (count == 0) {
184 return;
185 }
senorblancoaf1e21e2016-03-16 10:25:58 -0700186 this->drawVertices(target, gp, allocator.vertexBuffer(), 0, count);
bsalomonee432412016-06-27 07:18:18 -0700187 TessInfo info;
188 info.fTolerance = isLinear ? 0 : tol;
189 info.fCount = count;
190 SkAutoTUnref<SkData> data(SkData::NewWithCopy(&info, sizeof(info)));
191 key.setCustomData(data.get());
192 rp->assignUniqueKeyToResource(key, allocator.vertexBuffer());
senorblanco6599eff2016-03-10 08:38:45 -0800193 }
194
195 void onPrepareDraws(Target* target) const override {
bungeman06ca8ec2016-06-09 08:01:03 -0700196 sk_sp<GrGeometryProcessor> gp;
joshualittdf0c5572015-08-03 11:35:28 -0700197 {
198 using namespace GrDefaultGeoProcFactory;
199
200 Color color(fColor);
201 LocalCoords localCoords(fPipelineInfo.readsLocalCoords() ?
202 LocalCoords::kUsePosition_Type :
203 LocalCoords::kUnused_Type);
204 Coverage::Type coverageType;
205 if (fPipelineInfo.readsCoverage()) {
206 coverageType = Coverage::kSolid_Type;
207 } else {
208 coverageType = Coverage::kNone_Type;
209 }
210 Coverage coverage(coverageType);
bungeman06ca8ec2016-06-09 08:01:03 -0700211 gp = GrDefaultGeoProcFactory::Make(color, coverage, localCoords, fViewMatrix);
joshualittdf0c5572015-08-03 11:35:28 -0700212 }
senorblanco6599eff2016-03-10 08:38:45 -0800213 this->draw(target, gp.get());
214 }
senorblanco84cd6212015-08-04 10:01:58 -0700215
cdalton397536c2016-03-25 12:15:03 -0700216 void drawVertices(Target* target, const GrGeometryProcessor* gp, const GrBuffer* vb,
senorblanco6599eff2016-03-10 08:38:45 -0800217 int firstVertex, int count) const {
senorblanco84cd6212015-08-04 10:01:58 -0700218 SkASSERT(gp->getVertexStride() == sizeof(SkPoint));
senorblanco9ba39722015-03-05 07:13:42 -0800219
ethannicholase9709e82016-01-07 13:34:16 -0800220 GrPrimitiveType primitiveType = TESSELLATOR_WIREFRAME ? kLines_GrPrimitiveType
221 : kTriangles_GrPrimitiveType;
egdaniel0e1853c2016-03-17 11:35:45 -0700222 GrMesh mesh;
223 mesh.init(primitiveType, vb, firstVertex, count);
bsalomon342bfc22016-04-01 06:06:20 -0700224 target->draw(gp, mesh);
senorblanco9ba39722015-03-05 07:13:42 -0800225 }
226
bsalomoncb02b382015-08-12 11:14:50 -0700227 bool onCombineIfPossible(GrBatch*, const GrCaps&) override { return false; }
senorblanco9ba39722015-03-05 07:13:42 -0800228
senorblanco9ba39722015-03-05 07:13:42 -0800229 TessellatingPathBatch(const GrColor& color,
bsalomonee432412016-06-27 07:18:18 -0700230 const GrShape& shape,
senorblanco9ba39722015-03-05 07:13:42 -0800231 const SkMatrix& viewMatrix,
232 const SkRect& clipBounds)
reed1b55a962015-09-17 20:16:13 -0700233 : INHERITED(ClassID())
234 , fColor(color)
bsalomonee432412016-06-27 07:18:18 -0700235 , fShape(shape)
bsalomondb4758c2015-11-23 11:14:20 -0800236 , fViewMatrix(viewMatrix) {
bsalomonee432412016-06-27 07:18:18 -0700237 const SkRect& pathBounds = shape.bounds();
bsalomondb4758c2015-11-23 11:14:20 -0800238 fClipBounds = clipBounds;
239 // Because the clip bounds are used to add a contour for inverse fills, they must also
240 // include the path bounds.
241 fClipBounds.join(pathBounds);
bsalomon88cf17d2016-07-08 06:40:56 -0700242 const SkRect& srcBounds = shape.inverseFilled() ? fClipBounds : pathBounds;
243 this->setTransformedBounds(srcBounds, viewMatrix, HasAABloat::kNo, IsZeroArea::kNo);
senorblanco9ba39722015-03-05 07:13:42 -0800244 }
245
bsalomon91d844d2015-08-10 10:47:29 -0700246 GrColor fColor;
bsalomonee432412016-06-27 07:18:18 -0700247 GrShape fShape;
bsalomon91d844d2015-08-10 10:47:29 -0700248 SkMatrix fViewMatrix;
249 SkRect fClipBounds; // in source space
ethannicholasff210322015-11-24 12:10:10 -0800250 GrXPOverridesForBatch fPipelineInfo;
reed1b55a962015-09-17 20:16:13 -0700251
252 typedef GrVertexBatch INHERITED;
senorblanco9ba39722015-03-05 07:13:42 -0800253};
254
bsalomon0aff2fa2015-07-31 06:48:27 -0700255bool GrTessellatingPathRenderer::onDrawPath(const DrawPathArgs& args) {
robertphillips976f5f02016-06-03 10:59:20 -0700256 GR_AUDIT_TRAIL_AUTO_FRAME(args.fDrawContext->auditTrail(),
joshualittde83b412016-01-14 09:58:36 -0800257 "GrTessellatingPathRenderer::onDrawPath");
bsalomon0aff2fa2015-07-31 06:48:27 -0700258 SkASSERT(!args.fAntiAlias);
senorblancod6ed19c2015-02-26 06:58:17 -0800259
senorblancod6ed19c2015-02-26 06:58:17 -0800260 SkIRect clipBoundsI;
robertphillips976f5f02016-06-03 10:59:20 -0700261 args.fClip->getConservativeBounds(args.fDrawContext->width(), args.fDrawContext->height(),
262 &clipBoundsI);
senorblancod6ed19c2015-02-26 06:58:17 -0800263 SkRect clipBounds = SkRect::Make(clipBoundsI);
264 SkMatrix vmi;
bsalomon0aff2fa2015-07-31 06:48:27 -0700265 if (!args.fViewMatrix->invert(&vmi)) {
senorblancod6ed19c2015-02-26 06:58:17 -0800266 return false;
267 }
268 vmi.mapRect(&clipBounds);
bsalomon8acedde2016-06-24 10:42:16 -0700269 SkPath path;
270 args.fShape->asPath(&path);
robertphillips3950f0d2016-07-07 07:33:13 -0700271 SkAutoTUnref<GrDrawBatch> batch(TessellatingPathBatch::Create(args.fPaint->getColor(),
272 *args.fShape,
bsalomonee432412016-06-27 07:18:18 -0700273 *args.fViewMatrix, clipBounds));
robertphillips976f5f02016-06-03 10:59:20 -0700274
bsalomonbb243832016-07-22 07:10:19 -0700275 GrPipelineBuilder pipelineBuilder(*args.fPaint, args.fDrawContext->mustUseHWAA(*args.fPaint));
276 pipelineBuilder.setUserStencil(args.fUserStencilSettings);
277
278 args.fDrawContext->drawBatch(pipelineBuilder, *args.fClip, batch);
senorblancod6ed19c2015-02-26 06:58:17 -0800279
280 return true;
281}
joshualitt2fbd4062015-05-07 13:06:41 -0700282
283///////////////////////////////////////////////////////////////////////////////////////////////////
284
285#ifdef GR_TEST_UTILS
286
bsalomonabd30f52015-08-13 13:34:48 -0700287DRAW_BATCH_TEST_DEFINE(TesselatingPathBatch) {
joshualitt2fbd4062015-05-07 13:06:41 -0700288 GrColor color = GrRandomColor(random);
289 SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random);
290 SkPath path = GrTest::TestPath(random);
291 SkRect clipBounds = GrTest::TestRect(random);
292 SkMatrix vmi;
293 bool result = viewMatrix.invert(&vmi);
294 if (!result) {
295 SkFAIL("Cannot invert matrix\n");
296 }
297 vmi.mapRect(&clipBounds);
bsalomon6663acf2016-05-10 09:14:17 -0700298 GrStyle style;
299 do {
300 GrTest::TestStyle(random, &style);
301 } while (style.strokeRec().isHairlineStyle());
bsalomonee432412016-06-27 07:18:18 -0700302 GrShape shape(path, style);
303 return TessellatingPathBatch::Create(color, shape, viewMatrix, clipBounds);
joshualitt2fbd4062015-05-07 13:06:41 -0700304}
305
306#endif