blob: 06f31cd8db7147fcb542524cfe16768baa9c36c1 [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
bsalomon75398562015-08-17 12:55:38 -070010#include "GrBatchFlushState.h"
joshualitt2fbd4062015-05-07 13:06:41 -070011#include "GrBatchTest.h"
senorblancod6ed19c2015-02-26 06:58:17 -080012#include "GrDefaultGeoProcFactory.h"
13#include "GrPathUtils.h"
bsalomoncb8979d2015-05-05 09:51:38 -070014#include "GrVertices.h"
senorblanco84cd6212015-08-04 10:01:58 -070015#include "GrResourceCache.h"
16#include "GrResourceProvider.h"
ethannicholase9709e82016-01-07 13:34:16 -080017#include "GrTessellator.h"
senorblancod6ed19c2015-02-26 06:58:17 -080018#include "SkGeometry.h"
19
bsalomon16b99132015-08-13 14:55:50 -070020#include "batches/GrVertexBatch.h"
joshualitt74417822015-08-07 11:42:16 -070021
senorblancod6ed19c2015-02-26 06:58:17 -080022#include <stdio.h>
23
24/*
ethannicholase9709e82016-01-07 13:34:16 -080025 * This path renderer tessellates the path into triangles using GrTessellator, uploads the triangles
26 * to a vertex buffer, and renders them with a single draw call. It does not currently do
senorblancod6ed19c2015-02-26 06:58:17 -080027 * antialiasing, so it must be used in conjunction with multisampling.
senorblancod6ed19c2015-02-26 06:58:17 -080028 */
senorblancod6ed19c2015-02-26 06:58:17 -080029namespace {
30
senorblanco84cd6212015-08-04 10:01:58 -070031struct TessInfo {
32 SkScalar fTolerance;
senorblanco06f989a2015-09-02 09:05:17 -070033 int fCount;
senorblanco84cd6212015-08-04 10:01:58 -070034};
35
ethannicholase9709e82016-01-07 13:34:16 -080036// When the SkPathRef genID changes, invalidate a corresponding GrResource described by key.
37class PathInvalidator : public SkPathRef::GenIDChangeListener {
38public:
39 explicit PathInvalidator(const GrUniqueKey& key) : fMsg(key) {}
40private:
41 GrUniqueKeyInvalidatedMessage fMsg;
42
43 void onChange() override {
44 SkMessageBus<GrUniqueKeyInvalidatedMessage>::Post(fMsg);
45 }
46};
47
senorblanco06f989a2015-09-02 09:05:17 -070048bool cache_match(GrVertexBuffer* vertexBuffer, SkScalar tol, int* actualCount) {
senorblanco84cd6212015-08-04 10:01:58 -070049 if (!vertexBuffer) {
50 return false;
51 }
52 const SkData* data = vertexBuffer->getUniqueKey().getCustomData();
53 SkASSERT(data);
54 const TessInfo* info = static_cast<const TessInfo*>(data->data());
55 if (info->fTolerance == 0 || info->fTolerance < 3.0f * tol) {
senorblanco06f989a2015-09-02 09:05:17 -070056 *actualCount = info->fCount;
senorblanco84cd6212015-08-04 10:01:58 -070057 return true;
58 }
59 return false;
60}
61
senorblanco6599eff2016-03-10 08:38:45 -080062class StaticVertexAllocator : public GrTessellator::VertexAllocator {
63public:
senorblancoaf1e21e2016-03-16 10:25:58 -070064 StaticVertexAllocator(GrResourceProvider* resourceProvider, bool canMapVB)
65 : fResourceProvider(resourceProvider)
senorblanco6599eff2016-03-10 08:38:45 -080066 , fCanMapVB(canMapVB)
67 , fVertices(nullptr) {
68 }
69 SkPoint* lock(int vertexCount) override {
70 size_t size = vertexCount * sizeof(SkPoint);
senorblancoaf1e21e2016-03-16 10:25:58 -070071 fVertexBuffer.reset(fResourceProvider->createVertexBuffer(
72 size, GrResourceProvider::kStatic_BufferUsage, 0));
senorblanco6599eff2016-03-10 08:38:45 -080073 if (!fVertexBuffer.get()) {
74 return nullptr;
75 }
76 if (fCanMapVB) {
77 fVertices = static_cast<SkPoint*>(fVertexBuffer->map());
78 } else {
79 fVertices = new SkPoint[vertexCount];
80 }
81 return fVertices;
82 }
83 void unlock(int actualCount) override {
84 if (fCanMapVB) {
85 fVertexBuffer->unmap();
86 } else {
87 fVertexBuffer->updateData(fVertices, actualCount * sizeof(SkPoint));
88 delete[] fVertices;
89 }
90 fVertices = nullptr;
91 }
senorblancoaf1e21e2016-03-16 10:25:58 -070092 GrVertexBuffer* vertexBuffer() { return fVertexBuffer.get(); }
senorblanco6599eff2016-03-10 08:38:45 -080093private:
senorblancoaf1e21e2016-03-16 10:25:58 -070094 SkAutoTUnref<GrVertexBuffer> fVertexBuffer;
senorblanco6599eff2016-03-10 08:38:45 -080095 GrResourceProvider* fResourceProvider;
96 bool fCanMapVB;
97 SkPoint* fVertices;
98};
99
ethannicholase9709e82016-01-07 13:34:16 -0800100} // namespace
senorblancod6ed19c2015-02-26 06:58:17 -0800101
102GrTessellatingPathRenderer::GrTessellatingPathRenderer() {
103}
104
bsalomon0aff2fa2015-07-31 06:48:27 -0700105bool GrTessellatingPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
senorblancob4f9d0e2015-08-06 10:28:55 -0700106 // This path renderer can draw all fill styles, all stroke styles except hairlines, but does
107 // not do antialiasing. It can do convex and concave paths, but we'll leave the convex ones to
108 // simpler algorithms.
halcanary96fcdcc2015-08-27 07:41:13 -0700109 return !IsStrokeHairlineOrEquivalent(*args.fStroke, *args.fViewMatrix, nullptr) &&
senorblancob4f9d0e2015-08-06 10:28:55 -0700110 !args.fAntiAlias && !args.fPath->isConvex();
senorblancod6ed19c2015-02-26 06:58:17 -0800111}
112
bsalomonabd30f52015-08-13 13:34:48 -0700113class TessellatingPathBatch : public GrVertexBatch {
senorblanco9ba39722015-03-05 07:13:42 -0800114public:
reed1b55a962015-09-17 20:16:13 -0700115 DEFINE_BATCH_CLASS_ID
senorblanco9ba39722015-03-05 07:13:42 -0800116
bsalomonabd30f52015-08-13 13:34:48 -0700117 static GrDrawBatch* Create(const GrColor& color,
118 const SkPath& path,
119 const GrStrokeInfo& stroke,
120 const SkMatrix& viewMatrix,
121 SkRect clipBounds) {
halcanary385fe4d2015-08-26 13:07:48 -0700122 return new TessellatingPathBatch(color, path, stroke, viewMatrix, clipBounds);
senorblanco9ba39722015-03-05 07:13:42 -0800123 }
124
mtklein36352bf2015-03-25 18:17:31 -0700125 const char* name() const override { return "TessellatingPathBatch"; }
senorblanco9ba39722015-03-05 07:13:42 -0800126
ethannicholasff210322015-11-24 12:10:10 -0800127 void computePipelineOptimizations(GrInitInvariantOutput* color,
128 GrInitInvariantOutput* coverage,
129 GrBatchToXPOverrides* overrides) const override {
130 color->setKnownFourComponents(fColor);
131 coverage->setUnknownSingleComponent();
senorblanco9ba39722015-03-05 07:13:42 -0800132 }
133
bsalomone46f9fe2015-08-18 06:05:14 -0700134private:
ethannicholasff210322015-11-24 12:10:10 -0800135 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
senorblanco9ba39722015-03-05 07:13:42 -0800136 // Handle any color overrides
ethannicholasff210322015-11-24 12:10:10 -0800137 if (!overrides.readsColor()) {
senorblanco9ba39722015-03-05 07:13:42 -0800138 fColor = GrColor_ILLEGAL;
senorblanco9ba39722015-03-05 07:13:42 -0800139 }
ethannicholasff210322015-11-24 12:10:10 -0800140 overrides.getOverrideColorIfSet(&fColor);
141 fPipelineInfo = overrides;
senorblanco9ba39722015-03-05 07:13:42 -0800142 }
143
senorblanco6599eff2016-03-10 08:38:45 -0800144 void draw(Target* target, const GrGeometryProcessor* gp) const {
senorblanco84cd6212015-08-04 10:01:58 -0700145 // construct a cache key from the path's genID and the view matrix
146 static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
147 GrUniqueKey key;
148 int clipBoundsSize32 =
149 fPath.isInverseFillType() ? sizeof(fClipBounds) / sizeof(uint32_t) : 0;
senorblancob4f9d0e2015-08-06 10:28:55 -0700150 int strokeDataSize32 = fStroke.computeUniqueKeyFragmentData32Cnt();
151 GrUniqueKey::Builder builder(&key, kDomain, 2 + clipBoundsSize32 + strokeDataSize32);
senorblanco84cd6212015-08-04 10:01:58 -0700152 builder[0] = fPath.getGenerationID();
153 builder[1] = fPath.getFillType();
154 // For inverse fills, the tessellation is dependent on clip bounds.
155 if (fPath.isInverseFillType()) {
156 memcpy(&builder[2], &fClipBounds, sizeof(fClipBounds));
157 }
senorblancob4f9d0e2015-08-06 10:28:55 -0700158 fStroke.asUniqueKeyFragment(&builder[2 + clipBoundsSize32]);
senorblanco84cd6212015-08-04 10:01:58 -0700159 builder.finish();
bsalomon75398562015-08-17 12:55:38 -0700160 GrResourceProvider* rp = target->resourceProvider();
senorblancoaf1e21e2016-03-16 10:25:58 -0700161 SkAutoTUnref<GrVertexBuffer> cachedVertexBuffer(
162 rp->findAndRefTByUniqueKey<GrVertexBuffer>(key));
senorblanco06f989a2015-09-02 09:05:17 -0700163 int actualCount;
senorblanco84cd6212015-08-04 10:01:58 -0700164 SkScalar screenSpaceTol = GrPathUtils::kDefaultTolerance;
165 SkScalar tol = GrPathUtils::scaleToleranceToSrc(
166 screenSpaceTol, fViewMatrix, fPath.getBounds());
senorblancoaf1e21e2016-03-16 10:25:58 -0700167 if (cache_match(cachedVertexBuffer.get(), tol, &actualCount)) {
168 this->drawVertices(target, gp, cachedVertexBuffer.get(), 0, actualCount);
senorblanco84cd6212015-08-04 10:01:58 -0700169 return;
170 }
171
senorblanco6599eff2016-03-10 08:38:45 -0800172 SkPath path;
173 GrStrokeInfo stroke(fStroke);
174 if (stroke.isDashed()) {
175 if (!stroke.applyDashToPath(&path, &stroke, fPath)) {
176 return;
177 }
178 } else {
179 path = fPath;
180 }
181 if (!stroke.isFillStyle()) {
182 stroke.setResScale(SkScalarAbs(fViewMatrix.getMaxScale()));
183 if (!stroke.applyToPath(&path, path)) {
184 return;
185 }
186 stroke.setFillStyle();
187 }
188 bool isLinear;
189 bool canMapVB = GrCaps::kNone_MapFlags != target->caps().mapBufferFlags();
senorblancoaf1e21e2016-03-16 10:25:58 -0700190 StaticVertexAllocator allocator(rp, canMapVB);
senorblanco6599eff2016-03-10 08:38:45 -0800191 int count = GrTessellator::PathToTriangles(path, tol, fClipBounds, &allocator, &isLinear);
192 if (count == 0) {
193 return;
194 }
senorblancoaf1e21e2016-03-16 10:25:58 -0700195 this->drawVertices(target, gp, allocator.vertexBuffer(), 0, count);
senorblanco6599eff2016-03-10 08:38:45 -0800196 if (!fPath.isVolatile()) {
197 TessInfo info;
198 info.fTolerance = isLinear ? 0 : tol;
199 info.fCount = count;
200 SkAutoTUnref<SkData> data(SkData::NewWithCopy(&info, sizeof(info)));
201 key.setCustomData(data.get());
senorblancoaf1e21e2016-03-16 10:25:58 -0700202 rp->assignUniqueKeyToResource(key, allocator.vertexBuffer());
senorblanco6599eff2016-03-10 08:38:45 -0800203 SkPathPriv::AddGenIDChangeListener(fPath, new PathInvalidator(key));
204 }
205 }
206
207 void onPrepareDraws(Target* target) const override {
joshualittdf0c5572015-08-03 11:35:28 -0700208 SkAutoTUnref<const GrGeometryProcessor> gp;
209 {
210 using namespace GrDefaultGeoProcFactory;
211
212 Color color(fColor);
213 LocalCoords localCoords(fPipelineInfo.readsLocalCoords() ?
214 LocalCoords::kUsePosition_Type :
215 LocalCoords::kUnused_Type);
216 Coverage::Type coverageType;
217 if (fPipelineInfo.readsCoverage()) {
218 coverageType = Coverage::kSolid_Type;
219 } else {
220 coverageType = Coverage::kNone_Type;
221 }
222 Coverage coverage(coverageType);
223 gp.reset(GrDefaultGeoProcFactory::Create(color, coverage, localCoords,
224 fViewMatrix));
225 }
senorblanco6599eff2016-03-10 08:38:45 -0800226 this->draw(target, gp.get());
227 }
senorblanco84cd6212015-08-04 10:01:58 -0700228
senorblanco6599eff2016-03-10 08:38:45 -0800229 void drawVertices(Target* target, const GrGeometryProcessor* gp, const GrVertexBuffer* vb,
230 int firstVertex, int count) const {
bsalomon75398562015-08-17 12:55:38 -0700231 target->initDraw(gp, this->pipeline());
senorblanco84cd6212015-08-04 10:01:58 -0700232 SkASSERT(gp->getVertexStride() == sizeof(SkPoint));
senorblanco9ba39722015-03-05 07:13:42 -0800233
ethannicholase9709e82016-01-07 13:34:16 -0800234 GrPrimitiveType primitiveType = TESSELLATOR_WIREFRAME ? kLines_GrPrimitiveType
235 : kTriangles_GrPrimitiveType;
bsalomoncb8979d2015-05-05 09:51:38 -0700236 GrVertices vertices;
senorblanco6599eff2016-03-10 08:38:45 -0800237 vertices.init(primitiveType, vb, firstVertex, count);
bsalomon75398562015-08-17 12:55:38 -0700238 target->draw(vertices);
senorblanco9ba39722015-03-05 07:13:42 -0800239 }
240
bsalomoncb02b382015-08-12 11:14:50 -0700241 bool onCombineIfPossible(GrBatch*, const GrCaps&) override { return false; }
senorblanco9ba39722015-03-05 07:13:42 -0800242
senorblanco9ba39722015-03-05 07:13:42 -0800243 TessellatingPathBatch(const GrColor& color,
244 const SkPath& path,
senorblancob4f9d0e2015-08-06 10:28:55 -0700245 const GrStrokeInfo& stroke,
senorblanco9ba39722015-03-05 07:13:42 -0800246 const SkMatrix& viewMatrix,
247 const SkRect& clipBounds)
reed1b55a962015-09-17 20:16:13 -0700248 : INHERITED(ClassID())
249 , fColor(color)
senorblanco9ba39722015-03-05 07:13:42 -0800250 , fPath(path)
senorblancob4f9d0e2015-08-06 10:28:55 -0700251 , fStroke(stroke)
bsalomondb4758c2015-11-23 11:14:20 -0800252 , fViewMatrix(viewMatrix) {
253 const SkRect& pathBounds = path.getBounds();
254 fClipBounds = clipBounds;
255 // Because the clip bounds are used to add a contour for inverse fills, they must also
256 // include the path bounds.
257 fClipBounds.join(pathBounds);
258 if (path.isInverseFillType()) {
259 fBounds = fClipBounds;
260 } else {
261 fBounds = path.getBounds();
262 }
senorblancob4f9d0e2015-08-06 10:28:55 -0700263 if (!stroke.isFillStyle()) {
264 SkScalar radius = SkScalarHalf(stroke.getWidth());
265 if (stroke.getJoin() == SkPaint::kMiter_Join) {
266 SkScalar scale = stroke.getMiter();
267 if (scale > SK_Scalar1) {
268 radius = SkScalarMul(radius, scale);
269 }
270 }
271 fBounds.outset(radius, radius);
272 }
joshualitt99c7c072015-05-01 13:43:30 -0700273 viewMatrix.mapRect(&fBounds);
senorblanco9ba39722015-03-05 07:13:42 -0800274 }
275
bsalomon91d844d2015-08-10 10:47:29 -0700276 GrColor fColor;
277 SkPath fPath;
278 GrStrokeInfo fStroke;
279 SkMatrix fViewMatrix;
280 SkRect fClipBounds; // in source space
ethannicholasff210322015-11-24 12:10:10 -0800281 GrXPOverridesForBatch fPipelineInfo;
reed1b55a962015-09-17 20:16:13 -0700282
283 typedef GrVertexBatch INHERITED;
senorblanco9ba39722015-03-05 07:13:42 -0800284};
285
bsalomon0aff2fa2015-07-31 06:48:27 -0700286bool GrTessellatingPathRenderer::onDrawPath(const DrawPathArgs& args) {
joshualittde83b412016-01-14 09:58:36 -0800287 GR_AUDIT_TRAIL_AUTO_FRAME(args.fTarget->getAuditTrail(),
288 "GrTessellatingPathRenderer::onDrawPath");
bsalomon0aff2fa2015-07-31 06:48:27 -0700289 SkASSERT(!args.fAntiAlias);
290 const GrRenderTarget* rt = args.fPipelineBuilder->getRenderTarget();
halcanary96fcdcc2015-08-27 07:41:13 -0700291 if (nullptr == rt) {
senorblancod6ed19c2015-02-26 06:58:17 -0800292 return false;
293 }
294
senorblancod6ed19c2015-02-26 06:58:17 -0800295 SkIRect clipBoundsI;
robertphillips7bceedc2015-12-01 12:51:26 -0800296 args.fPipelineBuilder->clip().getConservativeBounds(rt->width(), rt->height(), &clipBoundsI);
senorblancod6ed19c2015-02-26 06:58:17 -0800297 SkRect clipBounds = SkRect::Make(clipBoundsI);
298 SkMatrix vmi;
bsalomon0aff2fa2015-07-31 06:48:27 -0700299 if (!args.fViewMatrix->invert(&vmi)) {
senorblancod6ed19c2015-02-26 06:58:17 -0800300 return false;
301 }
302 vmi.mapRect(&clipBounds);
bsalomonabd30f52015-08-13 13:34:48 -0700303 SkAutoTUnref<GrDrawBatch> batch(TessellatingPathBatch::Create(args.fColor, *args.fPath,
304 *args.fStroke, *args.fViewMatrix,
305 clipBounds));
bsalomon0aff2fa2015-07-31 06:48:27 -0700306 args.fTarget->drawBatch(*args.fPipelineBuilder, batch);
senorblancod6ed19c2015-02-26 06:58:17 -0800307
308 return true;
309}
joshualitt2fbd4062015-05-07 13:06:41 -0700310
311///////////////////////////////////////////////////////////////////////////////////////////////////
312
313#ifdef GR_TEST_UTILS
314
bsalomonabd30f52015-08-13 13:34:48 -0700315DRAW_BATCH_TEST_DEFINE(TesselatingPathBatch) {
joshualitt2fbd4062015-05-07 13:06:41 -0700316 GrColor color = GrRandomColor(random);
317 SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random);
318 SkPath path = GrTest::TestPath(random);
319 SkRect clipBounds = GrTest::TestRect(random);
320 SkMatrix vmi;
321 bool result = viewMatrix.invert(&vmi);
322 if (!result) {
323 SkFAIL("Cannot invert matrix\n");
324 }
325 vmi.mapRect(&clipBounds);
senorblancob4f9d0e2015-08-06 10:28:55 -0700326 GrStrokeInfo strokeInfo = GrTest::TestStrokeInfo(random);
327 return TessellatingPathBatch::Create(color, path, strokeInfo, viewMatrix, clipBounds);
joshualitt2fbd4062015-05-07 13:06:41 -0700328}
329
330#endif