blob: 35d1f619773166d536ecd16355af5a859f729def [file] [log] [blame]
jvanverthfa38a302014-10-06 05:59:05 -07001/*
2 * Copyright 2014 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 "GrAADistanceFieldPathRenderer.h"
9
bsalomon75398562015-08-17 12:55:38 -070010#include "GrBatchFlushState.h"
joshualitt21279c72015-05-11 07:21:37 -070011#include "GrBatchTest.h"
cdalton397536c2016-03-25 12:15:03 -070012#include "GrBuffer.h"
jvanverthfa38a302014-10-06 05:59:05 -070013#include "GrContext.h"
egdaniel8dd688b2015-01-22 10:16:09 -080014#include "GrPipelineBuilder.h"
bsalomonb5238a72015-05-05 07:49:49 -070015#include "GrResourceProvider.h"
jvanverthfa38a302014-10-06 05:59:05 -070016#include "GrSurfacePriv.h"
17#include "GrSWMaskHelper.h"
18#include "GrTexturePriv.h"
bsalomon16b99132015-08-13 14:55:50 -070019#include "batches/GrVertexBatch.h"
jvanverth8ed3b9a2015-04-09 08:00:49 -070020#include "effects/GrDistanceFieldGeoProc.h"
jvanverthfa38a302014-10-06 05:59:05 -070021
22#include "SkDistanceFieldGen.h"
23#include "SkRTConf.h"
24
jvanverthfb1e2fc2015-09-15 13:11:11 -070025#define ATLAS_TEXTURE_WIDTH 2048
jvanverthb61283f2014-10-30 05:57:21 -070026#define ATLAS_TEXTURE_HEIGHT 2048
jvanverthfb1e2fc2015-09-15 13:11:11 -070027#define PLOT_WIDTH 512
reede4ef1ca2015-02-17 18:38:38 -080028#define PLOT_HEIGHT 256
jvanverthfa38a302014-10-06 05:59:05 -070029
30#define NUM_PLOTS_X (ATLAS_TEXTURE_WIDTH / PLOT_WIDTH)
31#define NUM_PLOTS_Y (ATLAS_TEXTURE_HEIGHT / PLOT_HEIGHT)
32
jvanverthb3eb6872014-10-24 07:12:51 -070033#ifdef DF_PATH_TRACKING
34static int g_NumCachedPaths = 0;
35static int g_NumFreedPaths = 0;
36#endif
37
jvanverthb61283f2014-10-30 05:57:21 -070038// mip levels
39static const int kSmallMIP = 32;
jvanverth512e4372015-11-23 11:50:02 -080040static const int kMediumMIP = 73;
jvanverthfb1e2fc2015-09-15 13:11:11 -070041static const int kLargeMIP = 162;
jvanverthb61283f2014-10-30 05:57:21 -070042
joshualitt5bf99f12015-03-13 11:47:42 -070043// Callback to clear out internal path cache when eviction occurs
44void GrAADistanceFieldPathRenderer::HandleEviction(GrBatchAtlas::AtlasID id, void* pr) {
45 GrAADistanceFieldPathRenderer* dfpr = (GrAADistanceFieldPathRenderer*)pr;
46 // remove any paths that use this plot
47 PathDataList::Iter iter;
48 iter.init(dfpr->fPathList, PathDataList::Iter::kHead_IterStart);
49 PathData* pathData;
50 while ((pathData = iter.get())) {
51 iter.next();
52 if (id == pathData->fID) {
53 dfpr->fPathCache.remove(pathData->fKey);
54 dfpr->fPathList.remove(pathData);
halcanary385fe4d2015-08-26 13:07:48 -070055 delete pathData;
joshualitt5bf99f12015-03-13 11:47:42 -070056#ifdef DF_PATH_TRACKING
57 ++g_NumFreedPaths;
58#endif
59 }
60 }
61}
62
jvanverthfa38a302014-10-06 05:59:05 -070063////////////////////////////////////////////////////////////////////////////////
halcanary96fcdcc2015-08-27 07:41:13 -070064GrAADistanceFieldPathRenderer::GrAADistanceFieldPathRenderer() : fAtlas(nullptr) {}
jvanverth6d22eca2014-10-28 11:10:48 -070065
jvanverthfa38a302014-10-06 05:59:05 -070066GrAADistanceFieldPathRenderer::~GrAADistanceFieldPathRenderer() {
67 PathDataList::Iter iter;
68 iter.init(fPathList, PathDataList::Iter::kHead_IterStart);
69 PathData* pathData;
70 while ((pathData = iter.get())) {
71 iter.next();
72 fPathList.remove(pathData);
halcanary385fe4d2015-08-26 13:07:48 -070073 delete pathData;
jvanverthfa38a302014-10-06 05:59:05 -070074 }
halcanary385fe4d2015-08-26 13:07:48 -070075 delete fAtlas;
jvanverthb3eb6872014-10-24 07:12:51 -070076
77#ifdef DF_PATH_TRACKING
78 SkDebugf("Cached paths: %d, freed paths: %d\n", g_NumCachedPaths, g_NumFreedPaths);
79#endif
jvanverthfa38a302014-10-06 05:59:05 -070080}
81
82////////////////////////////////////////////////////////////////////////////////
bsalomon0aff2fa2015-07-31 06:48:27 -070083bool GrAADistanceFieldPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
jvanverthfb1e2fc2015-09-15 13:11:11 -070084
jvanverthfa38a302014-10-06 05:59:05 -070085 // TODO: Support inverse fill
robertphillipse7d4b2f2015-08-13 07:57:10 -070086 if (!args.fShaderCaps->shaderDerivativeSupport() || !args.fAntiAlias ||
jvanverth512e4372015-11-23 11:50:02 -080087 SkStrokeRec::kHairline_Style == args.fStroke->getStyle() ||
bsalomon6266dca2016-03-11 06:22:00 -080088 args.fPath->isInverseFillType() || args.fPath->isVolatile() ||
89 // We don't currently apply the dash or factor it into the DF key. (skbug.com/5082)
90 args.fStroke->isDashed()) {
jvanverthfa38a302014-10-06 05:59:05 -070091 return false;
92 }
93
jvanverthb61283f2014-10-30 05:57:21 -070094 // currently don't support perspective
bsalomon0aff2fa2015-07-31 06:48:27 -070095 if (args.fViewMatrix->hasPerspective()) {
jvanverthfa38a302014-10-06 05:59:05 -070096 return false;
97 }
halcanary9d524f22016-03-29 09:03:52 -070098
jvanverth512e4372015-11-23 11:50:02 -080099 // only support paths with bounds within kMediumMIP by kMediumMIP,
100 // scaled to have bounds within 2.0f*kLargeMIP by 2.0f*kLargeMIP
jvanverthb61283f2014-10-30 05:57:21 -0700101 // the goal is to accelerate rendering of lots of small paths that may be scaling
bsalomon0aff2fa2015-07-31 06:48:27 -0700102 SkScalar maxScale = args.fViewMatrix->getMaxScale();
103 const SkRect& bounds = args.fPath->getBounds();
jvanverthb61283f2014-10-30 05:57:21 -0700104 SkScalar maxDim = SkMaxScalar(bounds.width(), bounds.height());
jvanverth512e4372015-11-23 11:50:02 -0800105 // Approximate stroked size by adding the maximum of the stroke width or 2x the miter limit
106 if (!args.fStroke->isFillStyle()) {
107 SkScalar extraWidth = args.fStroke->getWidth();
108 if (SkPaint::kMiter_Join == args.fStroke->getJoin()) {
109 extraWidth = SkTMax(extraWidth, 2.0f*args.fStroke->getMiter());
110 }
111 maxDim += extraWidth;
112 }
bsalomon6266dca2016-03-11 06:22:00 -0800113
jvanverth512e4372015-11-23 11:50:02 -0800114 return maxDim <= kMediumMIP && maxDim * maxScale <= 2.0f*kLargeMIP;
jvanverthfa38a302014-10-06 05:59:05 -0700115}
116
jvanverthfa38a302014-10-06 05:59:05 -0700117////////////////////////////////////////////////////////////////////////////////
118
joshualitt5bf99f12015-03-13 11:47:42 -0700119// padding around path bounds to allow for antialiased pixels
120static const SkScalar kAntiAliasPad = 1.0f;
121
bsalomonabd30f52015-08-13 13:34:48 -0700122class AADistanceFieldPathBatch : public GrVertexBatch {
joshualitt5bf99f12015-03-13 11:47:42 -0700123public:
reed1b55a962015-09-17 20:16:13 -0700124 DEFINE_BATCH_CLASS_ID
125
joshualitt5bf99f12015-03-13 11:47:42 -0700126 typedef GrAADistanceFieldPathRenderer::PathData PathData;
127 typedef SkTDynamicHash<PathData, PathData::Key> PathCache;
128 typedef GrAADistanceFieldPathRenderer::PathDataList PathDataList;
129
130 struct Geometry {
jvanverth512e4372015-11-23 11:50:02 -0800131 Geometry(const SkStrokeRec& stroke) : fStroke(stroke) {
132 if (!stroke.needToApply()) {
133 // purify unused values to ensure binary equality
134 fStroke.setStrokeParams(SkPaint::kDefault_Cap, SkPaint::kDefault_Join,
135 SkIntToScalar(4));
136 if (fStroke.getWidth() < 0) {
137 fStroke.setStrokeStyle(-1.0f);
138 }
139 }
140 }
joshualitt5bf99f12015-03-13 11:47:42 -0700141 SkPath fPath;
jvanverth512e4372015-11-23 11:50:02 -0800142 // The unique ID of the path involved in this draw. This may be different than the ID
143 // in fPath since that path may have resulted from a SkStrokeRec::applyToPath call.
144 uint32_t fGenID;
joshualitt5bf99f12015-03-13 11:47:42 -0700145 SkStrokeRec fStroke;
joshualitt53f26aa2015-12-10 07:29:54 -0800146 GrColor fColor;
joshualitt5bf99f12015-03-13 11:47:42 -0700147 bool fAntiAlias;
joshualitt5bf99f12015-03-13 11:47:42 -0700148 };
149
joshualitt53f26aa2015-12-10 07:29:54 -0800150 static GrDrawBatch* Create(const Geometry& geometry, const SkMatrix& viewMatrix,
bsalomonabd30f52015-08-13 13:34:48 -0700151 GrBatchAtlas* atlas, PathCache* pathCache, PathDataList* pathList) {
joshualitt53f26aa2015-12-10 07:29:54 -0800152 return new AADistanceFieldPathBatch(geometry, viewMatrix, atlas, pathCache, pathList);
joshualitt5bf99f12015-03-13 11:47:42 -0700153 }
154
mtklein36352bf2015-03-25 18:17:31 -0700155 const char* name() const override { return "AADistanceFieldPathBatch"; }
joshualitt5bf99f12015-03-13 11:47:42 -0700156
halcanary9d524f22016-03-29 09:03:52 -0700157 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -0800158 GrInitInvariantOutput* coverage,
159 GrBatchToXPOverrides* overrides) const override {
joshualitt53f26aa2015-12-10 07:29:54 -0800160 color->setKnownFourComponents(fGeoData[0].fColor);
ethannicholasff210322015-11-24 12:10:10 -0800161 coverage->setUnknownSingleComponent();
joshualitt5bf99f12015-03-13 11:47:42 -0700162 }
163
bsalomone46f9fe2015-08-18 06:05:14 -0700164private:
ethannicholasff210322015-11-24 12:10:10 -0800165 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
joshualitt5bf99f12015-03-13 11:47:42 -0700166 // Handle any color overrides
ethannicholasff210322015-11-24 12:10:10 -0800167 if (!overrides.readsColor()) {
joshualitt53f26aa2015-12-10 07:29:54 -0800168 fGeoData[0].fColor = GrColor_ILLEGAL;
joshualitt5bf99f12015-03-13 11:47:42 -0700169 }
joshualitt53f26aa2015-12-10 07:29:54 -0800170 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
joshualitt5bf99f12015-03-13 11:47:42 -0700171
172 // setup batch properties
ethannicholasff210322015-11-24 12:10:10 -0800173 fBatch.fColorIgnored = !overrides.readsColor();
174 fBatch.fUsesLocalCoords = overrides.readsLocalCoords();
175 fBatch.fCoverageIgnored = !overrides.readsCoverage();
joshualitt5bf99f12015-03-13 11:47:42 -0700176 }
177
bsalomonb5238a72015-05-05 07:49:49 -0700178 struct FlushInfo {
bsalomon342bfc22016-04-01 06:06:20 -0700179 SkAutoTUnref<const GrBuffer> fVertexBuffer;
180 SkAutoTUnref<const GrBuffer> fIndexBuffer;
181 SkAutoTUnref<const GrGeometryProcessor> fGeometryProcessor;
bsalomonb5238a72015-05-05 07:49:49 -0700182 int fVertexOffset;
183 int fInstancesToFlush;
184 };
185
joshualitt144c3c82015-11-30 12:30:13 -0800186 void onPrepareDraws(Target* target) const override {
joshualitt5bf99f12015-03-13 11:47:42 -0700187 int instanceCount = fGeoData.count();
188
189 SkMatrix invert;
190 if (this->usesLocalCoords() && !this->viewMatrix().invert(&invert)) {
191 SkDebugf("Could not invert viewmatrix\n");
192 return;
193 }
194
jvanverthcf371bb2016-03-10 11:10:43 -0800195 const SkMatrix& ctm = this->viewMatrix();
joshualitt5bf99f12015-03-13 11:47:42 -0700196 uint32_t flags = 0;
jvanverthcf371bb2016-03-10 11:10:43 -0800197 flags |= ctm.isScaleTranslate() ? kScaleOnly_DistanceFieldEffectFlag : 0;
198 flags |= ctm.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
joshualitt5bf99f12015-03-13 11:47:42 -0700199
200 GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kBilerp_FilterMode);
201
bsalomon342bfc22016-04-01 06:06:20 -0700202 FlushInfo flushInfo;
203
joshualitt5bf99f12015-03-13 11:47:42 -0700204 // Setup GrGeometryProcessor
205 GrBatchAtlas* atlas = fAtlas;
bsalomon342bfc22016-04-01 06:06:20 -0700206 flushInfo.fGeometryProcessor.reset(
jvanverth502286d2015-04-08 12:37:51 -0700207 GrDistanceFieldPathGeoProc::Create(this->color(),
208 this->viewMatrix(),
209 atlas->getTexture(),
210 params,
joshualittb8c241a2015-05-19 08:23:30 -0700211 flags,
212 this->usesLocalCoords()));
joshualitt5bf99f12015-03-13 11:47:42 -0700213
joshualitt5bf99f12015-03-13 11:47:42 -0700214 // allocate vertices
bsalomon342bfc22016-04-01 06:06:20 -0700215 size_t vertexStride = flushInfo.fGeometryProcessor->getVertexStride();
joshualitt53f26aa2015-12-10 07:29:54 -0800216 SkASSERT(vertexStride == 2 * sizeof(SkPoint) + sizeof(GrColor));
bsalomonb5238a72015-05-05 07:49:49 -0700217
cdalton397536c2016-03-25 12:15:03 -0700218 const GrBuffer* vertexBuffer;
bsalomon75398562015-08-17 12:55:38 -0700219 void* vertices = target->makeVertexSpace(vertexStride,
220 kVerticesPerQuad * instanceCount,
221 &vertexBuffer,
222 &flushInfo.fVertexOffset);
bsalomonb5238a72015-05-05 07:49:49 -0700223 flushInfo.fVertexBuffer.reset(SkRef(vertexBuffer));
bsalomon75398562015-08-17 12:55:38 -0700224 flushInfo.fIndexBuffer.reset(target->resourceProvider()->refQuadIndexBuffer());
bsalomonb5238a72015-05-05 07:49:49 -0700225 if (!vertices || !flushInfo.fIndexBuffer) {
joshualitt5bf99f12015-03-13 11:47:42 -0700226 SkDebugf("Could not allocate vertices\n");
227 return;
228 }
229
bsalomonb5238a72015-05-05 07:49:49 -0700230 flushInfo.fInstancesToFlush = 0;
joshualitt5bf99f12015-03-13 11:47:42 -0700231 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -0800232 const Geometry& args = fGeoData[i];
joshualitt5bf99f12015-03-13 11:47:42 -0700233
234 // get mip level
235 SkScalar maxScale = this->viewMatrix().getMaxScale();
236 const SkRect& bounds = args.fPath.getBounds();
237 SkScalar maxDim = SkMaxScalar(bounds.width(), bounds.height());
238 SkScalar size = maxScale * maxDim;
239 uint32_t desiredDimension;
240 if (size <= kSmallMIP) {
241 desiredDimension = kSmallMIP;
242 } else if (size <= kMediumMIP) {
243 desiredDimension = kMediumMIP;
244 } else {
245 desiredDimension = kLargeMIP;
246 }
247
248 // check to see if path is cached
jvanverth512e4372015-11-23 11:50:02 -0800249 PathData::Key key(args.fGenID, desiredDimension, args.fStroke);
joshualitt144c3c82015-11-30 12:30:13 -0800250 PathData* pathData = fPathCache->find(key);
251 if (nullptr == pathData || !atlas->hasID(pathData->fID)) {
joshualitt5bf99f12015-03-13 11:47:42 -0700252 // Remove the stale cache entry
joshualitt144c3c82015-11-30 12:30:13 -0800253 if (pathData) {
254 fPathCache->remove(pathData->fKey);
255 fPathList->remove(pathData);
256 delete pathData;
joshualitt5bf99f12015-03-13 11:47:42 -0700257 }
258 SkScalar scale = desiredDimension/maxDim;
joshualitt144c3c82015-11-30 12:30:13 -0800259 pathData = new PathData;
bsalomon75398562015-08-17 12:55:38 -0700260 if (!this->addPathToAtlas(target,
bsalomonb5238a72015-05-05 07:49:49 -0700261 &flushInfo,
joshualitt5bf99f12015-03-13 11:47:42 -0700262 atlas,
joshualitt144c3c82015-11-30 12:30:13 -0800263 pathData,
joshualitt5bf99f12015-03-13 11:47:42 -0700264 args.fPath,
jvanverth512e4372015-11-23 11:50:02 -0800265 args.fGenID,
joshualitt5bf99f12015-03-13 11:47:42 -0700266 args.fStroke,
267 args.fAntiAlias,
268 desiredDimension,
269 scale)) {
270 SkDebugf("Can't rasterize path\n");
271 return;
272 }
273 }
274
bsalomon342bfc22016-04-01 06:06:20 -0700275 atlas->setLastUseToken(pathData->fID, target->nextDrawToken());
joshualitt5bf99f12015-03-13 11:47:42 -0700276
277 // Now set vertices
278 intptr_t offset = reinterpret_cast<intptr_t>(vertices);
bsalomonb5238a72015-05-05 07:49:49 -0700279 offset += i * kVerticesPerQuad * vertexStride;
bsalomon75398562015-08-17 12:55:38 -0700280 this->writePathVertices(target,
bsalomonb5238a72015-05-05 07:49:49 -0700281 atlas,
joshualitt53f26aa2015-12-10 07:29:54 -0800282 offset,
283 args.fColor,
bsalomonb5238a72015-05-05 07:49:49 -0700284 vertexStride,
285 this->viewMatrix(),
286 args.fPath,
joshualitt144c3c82015-11-30 12:30:13 -0800287 pathData);
bsalomonb5238a72015-05-05 07:49:49 -0700288 flushInfo.fInstancesToFlush++;
joshualitt5bf99f12015-03-13 11:47:42 -0700289 }
290
bsalomon75398562015-08-17 12:55:38 -0700291 this->flush(target, &flushInfo);
joshualitt5bf99f12015-03-13 11:47:42 -0700292 }
293
294 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
295
joshualitt53f26aa2015-12-10 07:29:54 -0800296 AADistanceFieldPathBatch(const Geometry& geometry,
297 const SkMatrix& viewMatrix,
joshualitt5bf99f12015-03-13 11:47:42 -0700298 GrBatchAtlas* atlas,
reed1b55a962015-09-17 20:16:13 -0700299 PathCache* pathCache, PathDataList* pathList)
300 : INHERITED(ClassID()) {
joshualitt5bf99f12015-03-13 11:47:42 -0700301 fBatch.fViewMatrix = viewMatrix;
302 fGeoData.push_back(geometry);
joshualitt5bf99f12015-03-13 11:47:42 -0700303
304 fAtlas = atlas;
305 fPathCache = pathCache;
306 fPathList = pathList;
joshualitt99c7c072015-05-01 13:43:30 -0700307
308 // Compute bounds
309 fBounds = geometry.fPath.getBounds();
310 viewMatrix.mapRect(&fBounds);
joshualitt5bf99f12015-03-13 11:47:42 -0700311 }
312
bsalomon75398562015-08-17 12:55:38 -0700313 bool addPathToAtlas(GrVertexBatch::Target* target,
bsalomonb5238a72015-05-05 07:49:49 -0700314 FlushInfo* flushInfo,
joshualitt5bf99f12015-03-13 11:47:42 -0700315 GrBatchAtlas* atlas,
316 PathData* pathData,
317 const SkPath& path,
jvanverth512e4372015-11-23 11:50:02 -0800318 uint32_t genID,
319 const SkStrokeRec& stroke,
320 bool antiAlias,
joshualitt5bf99f12015-03-13 11:47:42 -0700321 uint32_t dimension,
joshualitt144c3c82015-11-30 12:30:13 -0800322 SkScalar scale) const {
joshualitt5bf99f12015-03-13 11:47:42 -0700323 const SkRect& bounds = path.getBounds();
324
325 // generate bounding rect for bitmap draw
326 SkRect scaledBounds = bounds;
327 // scale to mip level size
328 scaledBounds.fLeft *= scale;
329 scaledBounds.fTop *= scale;
330 scaledBounds.fRight *= scale;
331 scaledBounds.fBottom *= scale;
332 // move the origin to an integer boundary (gives better results)
333 SkScalar dx = SkScalarFraction(scaledBounds.fLeft);
334 SkScalar dy = SkScalarFraction(scaledBounds.fTop);
335 scaledBounds.offset(-dx, -dy);
336 // get integer boundary
337 SkIRect devPathBounds;
338 scaledBounds.roundOut(&devPathBounds);
339 // pad to allow room for antialiasing
jvanverthecbed9d2015-12-18 10:07:52 -0800340 const int intPad = SkScalarCeilToInt(kAntiAliasPad);
341 // pre-move origin (after outset, will be 0,0)
342 int width = devPathBounds.width();
343 int height = devPathBounds.height();
344 devPathBounds.fLeft = intPad;
345 devPathBounds.fTop = intPad;
346 devPathBounds.fRight = intPad + width;
347 devPathBounds.fBottom = intPad + height;
348 devPathBounds.outset(intPad, intPad);
joshualitt5bf99f12015-03-13 11:47:42 -0700349
350 // draw path to bitmap
351 SkMatrix drawMatrix;
352 drawMatrix.setTranslate(-bounds.left(), -bounds.top());
353 drawMatrix.postScale(scale, scale);
354 drawMatrix.postTranslate(kAntiAliasPad, kAntiAliasPad);
355
robertphillips2a7cf5f2016-03-02 05:36:30 -0800356 // setup bitmap backing
jvanverth512e4372015-11-23 11:50:02 -0800357 SkASSERT(devPathBounds.fLeft == 0);
358 SkASSERT(devPathBounds.fTop == 0);
robertphillips2a7cf5f2016-03-02 05:36:30 -0800359 SkAutoPixmapStorage dst;
360 if (!dst.tryAlloc(SkImageInfo::MakeA8(devPathBounds.width(),
361 devPathBounds.height()))) {
362 return false;
363 }
364 sk_bzero(dst.writable_addr(), dst.getSafeSize());
joshualitt5bf99f12015-03-13 11:47:42 -0700365
robertphillips2a7cf5f2016-03-02 05:36:30 -0800366 // rasterize path
367 SkPaint paint;
368 paint.setStyle(SkPaint::kFill_Style);
369 paint.setAntiAlias(antiAlias);
370
371 SkDraw draw;
372 sk_bzero(&draw, sizeof(draw));
373
374 SkRasterClip rasterClip;
375 rasterClip.setRect(devPathBounds);
376 draw.fRC = &rasterClip;
377 draw.fClip = &rasterClip.bwRgn();
378 draw.fMatrix = &drawMatrix;
379 draw.fDst = dst;
380
381 draw.drawPathCoverage(path, paint);
382
383 // generate signed distance field
384 devPathBounds.outset(SK_DistanceFieldPad, SK_DistanceFieldPad);
385 width = devPathBounds.width();
386 height = devPathBounds.height();
joshualitt5bf99f12015-03-13 11:47:42 -0700387 // TODO We should really generate this directly into the plot somehow
388 SkAutoSMalloc<1024> dfStorage(width * height * sizeof(unsigned char));
389
robertphillips2a7cf5f2016-03-02 05:36:30 -0800390 // Generate signed distance field
391 SkGenerateDistanceFieldFromA8Image((unsigned char*)dfStorage.get(),
392 (const unsigned char*)dst.addr(),
393 dst.width(), dst.height(), dst.rowBytes());
joshualitt5bf99f12015-03-13 11:47:42 -0700394
395 // add to atlas
396 SkIPoint16 atlasLocation;
397 GrBatchAtlas::AtlasID id;
bsalomon75398562015-08-17 12:55:38 -0700398 bool success = atlas->addToAtlas(&id, target, width, height, dfStorage.get(),
joshualitt5bf99f12015-03-13 11:47:42 -0700399 &atlasLocation);
400 if (!success) {
bsalomon75398562015-08-17 12:55:38 -0700401 this->flush(target, flushInfo);
joshualitt5bf99f12015-03-13 11:47:42 -0700402
bsalomon75398562015-08-17 12:55:38 -0700403 SkDEBUGCODE(success =) atlas->addToAtlas(&id, target, width, height,
joshualitt5bf99f12015-03-13 11:47:42 -0700404 dfStorage.get(), &atlasLocation);
405 SkASSERT(success);
406
407 }
408
409 // add to cache
jvanverth512e4372015-11-23 11:50:02 -0800410 pathData->fKey = PathData::Key(genID, dimension, stroke);
joshualitt5bf99f12015-03-13 11:47:42 -0700411 pathData->fScale = scale;
412 pathData->fID = id;
413 // change the scaled rect to match the size of the inset distance field
414 scaledBounds.fRight = scaledBounds.fLeft +
415 SkIntToScalar(devPathBounds.width() - 2*SK_DistanceFieldInset);
416 scaledBounds.fBottom = scaledBounds.fTop +
417 SkIntToScalar(devPathBounds.height() - 2*SK_DistanceFieldInset);
418 // shift the origin to the correct place relative to the distance field
419 // need to also restore the fractional translation
420 scaledBounds.offset(-SkIntToScalar(SK_DistanceFieldInset) - kAntiAliasPad + dx,
421 -SkIntToScalar(SK_DistanceFieldInset) - kAntiAliasPad + dy);
422 pathData->fBounds = scaledBounds;
423 // origin we render from is inset from distance field edge
424 atlasLocation.fX += SK_DistanceFieldInset;
425 atlasLocation.fY += SK_DistanceFieldInset;
426 pathData->fAtlasLocation = atlasLocation;
427
428 fPathCache->add(pathData);
429 fPathList->addToTail(pathData);
430#ifdef DF_PATH_TRACKING
431 ++g_NumCachedPaths;
432#endif
433 return true;
434 }
435
bsalomon75398562015-08-17 12:55:38 -0700436 void writePathVertices(GrDrawBatch::Target* target,
bsalomonb5238a72015-05-05 07:49:49 -0700437 GrBatchAtlas* atlas,
joshualitt53f26aa2015-12-10 07:29:54 -0800438 intptr_t offset,
439 GrColor color,
bsalomonb5238a72015-05-05 07:49:49 -0700440 size_t vertexStride,
441 const SkMatrix& viewMatrix,
442 const SkPath& path,
joshualitt144c3c82015-11-30 12:30:13 -0800443 const PathData* pathData) const {
joshualitt5bf99f12015-03-13 11:47:42 -0700444 GrTexture* texture = atlas->getTexture();
445
446 SkScalar dx = pathData->fBounds.fLeft;
447 SkScalar dy = pathData->fBounds.fTop;
448 SkScalar width = pathData->fBounds.width();
449 SkScalar height = pathData->fBounds.height();
450
451 SkScalar invScale = 1.0f / pathData->fScale;
452 dx *= invScale;
453 dy *= invScale;
454 width *= invScale;
455 height *= invScale;
456
joshualitt53f26aa2015-12-10 07:29:54 -0800457 SkPoint* positions = reinterpret_cast<SkPoint*>(offset);
458
joshualitt5bf99f12015-03-13 11:47:42 -0700459 // vertex positions
460 // TODO make the vertex attributes a struct
461 SkRect r = SkRect::MakeXYWH(dx, dy, width, height);
462 positions->setRectFan(r.left(), r.top(), r.right(), r.bottom(), vertexStride);
463
joshualitt53f26aa2015-12-10 07:29:54 -0800464 // colors
465 for (int i = 0; i < kVerticesPerQuad; i++) {
466 GrColor* colorPtr = (GrColor*)(offset + sizeof(SkPoint) + i * vertexStride);
467 *colorPtr = color;
468 }
469
benjaminwagner01e58382016-02-22 11:10:33 -0800470 const SkScalar tx = SkIntToScalar(pathData->fAtlasLocation.fX);
471 const SkScalar ty = SkIntToScalar(pathData->fAtlasLocation.fY);
472
joshualitt5bf99f12015-03-13 11:47:42 -0700473 // vertex texture coords
joshualitt53f26aa2015-12-10 07:29:54 -0800474 SkPoint* textureCoords = (SkPoint*)(offset + sizeof(SkPoint) + sizeof(GrColor));
benjaminwagner01e58382016-02-22 11:10:33 -0800475 textureCoords->setRectFan(tx / texture->width(),
476 ty / texture->height(),
477 (tx + pathData->fBounds.width()) / texture->width(),
478 (ty + pathData->fBounds.height()) / texture->height(),
joshualitt5bf99f12015-03-13 11:47:42 -0700479 vertexStride);
480 }
481
joshualitt144c3c82015-11-30 12:30:13 -0800482 void flush(GrVertexBatch::Target* target, FlushInfo* flushInfo) const {
egdaniel0e1853c2016-03-17 11:35:45 -0700483 GrMesh mesh;
cdalton397536c2016-03-25 12:15:03 -0700484 int maxInstancesPerDraw =
485 static_cast<int>(flushInfo->fIndexBuffer->gpuMemorySize() / sizeof(uint16_t) / 6);
egdaniel0e1853c2016-03-17 11:35:45 -0700486 mesh.initInstanced(kTriangles_GrPrimitiveType, flushInfo->fVertexBuffer,
bsalomonb5238a72015-05-05 07:49:49 -0700487 flushInfo->fIndexBuffer, flushInfo->fVertexOffset, kVerticesPerQuad,
bsalomone64eb572015-05-07 11:35:55 -0700488 kIndicesPerQuad, flushInfo->fInstancesToFlush, maxInstancesPerDraw);
bsalomon342bfc22016-04-01 06:06:20 -0700489 target->draw(flushInfo->fGeometryProcessor, mesh);
bsalomonb5238a72015-05-05 07:49:49 -0700490 flushInfo->fVertexOffset += kVerticesPerQuad * flushInfo->fInstancesToFlush;
491 flushInfo->fInstancesToFlush = 0;
joshualitt5bf99f12015-03-13 11:47:42 -0700492 }
493
joshualitt53f26aa2015-12-10 07:29:54 -0800494 GrColor color() const { return fGeoData[0].fColor; }
joshualitt5bf99f12015-03-13 11:47:42 -0700495 const SkMatrix& viewMatrix() const { return fBatch.fViewMatrix; }
496 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
497
bsalomoncb02b382015-08-12 11:14:50 -0700498 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -0700499 AADistanceFieldPathBatch* that = t->cast<AADistanceFieldPathBatch>();
500 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
501 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -0700502 return false;
503 }
504
joshualitt53f26aa2015-12-10 07:29:54 -0800505 // TODO We can position on the cpu
joshualitt5bf99f12015-03-13 11:47:42 -0700506 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
507 return false;
508 }
509
510 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
joshualitt99c7c072015-05-01 13:43:30 -0700511 this->joinBounds(that->bounds());
joshualitt5bf99f12015-03-13 11:47:42 -0700512 return true;
513 }
514
515 struct BatchTracker {
joshualitt5bf99f12015-03-13 11:47:42 -0700516 SkMatrix fViewMatrix;
517 bool fUsesLocalCoords;
518 bool fColorIgnored;
519 bool fCoverageIgnored;
520 };
521
522 BatchTracker fBatch;
523 SkSTArray<1, Geometry, true> fGeoData;
524 GrBatchAtlas* fAtlas;
525 PathCache* fPathCache;
526 PathDataList* fPathList;
reed1b55a962015-09-17 20:16:13 -0700527
528 typedef GrVertexBatch INHERITED;
joshualitt5bf99f12015-03-13 11:47:42 -0700529};
530
bsalomon0aff2fa2015-07-31 06:48:27 -0700531bool GrAADistanceFieldPathRenderer::onDrawPath(const DrawPathArgs& args) {
joshualittde83b412016-01-14 09:58:36 -0800532 GR_AUDIT_TRAIL_AUTO_FRAME(args.fTarget->getAuditTrail(),
533 "GrAADistanceFieldPathRenderer::onDrawPath");
jvanverthfa38a302014-10-06 05:59:05 -0700534 // we've already bailed on inverse filled paths, so this is safe
bsalomon0aff2fa2015-07-31 06:48:27 -0700535 if (args.fPath->isEmpty()) {
jvanverthfa38a302014-10-06 05:59:05 -0700536 return true;
537 }
538
joshualitt5bf99f12015-03-13 11:47:42 -0700539 if (!fAtlas) {
joshualittb356cbc2015-08-05 06:36:39 -0700540 fAtlas = args.fResourceProvider->createAtlas(kAlpha_8_GrPixelConfig,
541 ATLAS_TEXTURE_WIDTH, ATLAS_TEXTURE_HEIGHT,
542 NUM_PLOTS_X, NUM_PLOTS_Y,
543 &GrAADistanceFieldPathRenderer::HandleEviction,
544 (void*)this);
joshualitt21279c72015-05-11 07:21:37 -0700545 if (!fAtlas) {
jvanverthfa38a302014-10-06 05:59:05 -0700546 return false;
547 }
548 }
549
bsalomon0aff2fa2015-07-31 06:48:27 -0700550 AADistanceFieldPathBatch::Geometry geometry(*args.fStroke);
jvanverth512e4372015-11-23 11:50:02 -0800551 if (SkStrokeRec::kFill_Style == args.fStroke->getStyle()) {
552 geometry.fPath = *args.fPath;
553 } else {
554 args.fStroke->applyToPath(&geometry.fPath, *args.fPath);
555 }
joshualitt53f26aa2015-12-10 07:29:54 -0800556 geometry.fColor = args.fColor;
bsalomon0aff2fa2015-07-31 06:48:27 -0700557 geometry.fAntiAlias = args.fAntiAlias;
jvanverth512e4372015-11-23 11:50:02 -0800558 // Note: this is the generation ID of the _original_ path. When a new path is
559 // generated due to stroking it is important that the original path's id is used
560 // for caching.
561 geometry.fGenID = args.fPath->getGenerationID();
halcanary9d524f22016-03-29 09:03:52 -0700562
joshualitt53f26aa2015-12-10 07:29:54 -0800563 SkAutoTUnref<GrDrawBatch> batch(AADistanceFieldPathBatch::Create(geometry,
bsalomonabd30f52015-08-13 13:34:48 -0700564 *args.fViewMatrix, fAtlas,
565 &fPathCache, &fPathList));
bsalomon0aff2fa2015-07-31 06:48:27 -0700566 args.fTarget->drawBatch(*args.fPipelineBuilder, batch);
joshualitt9491f7f2015-02-11 11:33:38 -0800567
jvanverthfa38a302014-10-06 05:59:05 -0700568 return true;
569}
570
joshualitt21279c72015-05-11 07:21:37 -0700571///////////////////////////////////////////////////////////////////////////////////////////////////
572
573#ifdef GR_TEST_UTILS
574
575struct PathTestStruct {
576 typedef GrAADistanceFieldPathRenderer::PathCache PathCache;
577 typedef GrAADistanceFieldPathRenderer::PathData PathData;
578 typedef GrAADistanceFieldPathRenderer::PathDataList PathDataList;
halcanary96fcdcc2015-08-27 07:41:13 -0700579 PathTestStruct() : fContextID(SK_InvalidGenID), fAtlas(nullptr) {}
joshualitt21279c72015-05-11 07:21:37 -0700580 ~PathTestStruct() { this->reset(); }
581
582 void reset() {
583 PathDataList::Iter iter;
584 iter.init(fPathList, PathDataList::Iter::kHead_IterStart);
585 PathData* pathData;
586 while ((pathData = iter.get())) {
587 iter.next();
588 fPathList.remove(pathData);
halcanary385fe4d2015-08-26 13:07:48 -0700589 delete pathData;
joshualitt21279c72015-05-11 07:21:37 -0700590 }
halcanary385fe4d2015-08-26 13:07:48 -0700591 delete fAtlas;
joshualitt6c891102015-05-13 08:51:49 -0700592 fPathCache.reset();
joshualitt21279c72015-05-11 07:21:37 -0700593 }
594
595 static void HandleEviction(GrBatchAtlas::AtlasID id, void* pr) {
596 PathTestStruct* dfpr = (PathTestStruct*)pr;
597 // remove any paths that use this plot
598 PathDataList::Iter iter;
599 iter.init(dfpr->fPathList, PathDataList::Iter::kHead_IterStart);
600 PathData* pathData;
601 while ((pathData = iter.get())) {
602 iter.next();
603 if (id == pathData->fID) {
604 dfpr->fPathCache.remove(pathData->fKey);
605 dfpr->fPathList.remove(pathData);
halcanary385fe4d2015-08-26 13:07:48 -0700606 delete pathData;
joshualitt21279c72015-05-11 07:21:37 -0700607 }
608 }
609 }
610
611 uint32_t fContextID;
612 GrBatchAtlas* fAtlas;
613 PathCache fPathCache;
614 PathDataList fPathList;
615};
616
bsalomonabd30f52015-08-13 13:34:48 -0700617DRAW_BATCH_TEST_DEFINE(AADistanceFieldPathBatch) {
joshualitt21279c72015-05-11 07:21:37 -0700618 static PathTestStruct gTestStruct;
619
620 if (context->uniqueID() != gTestStruct.fContextID) {
621 gTestStruct.fContextID = context->uniqueID();
622 gTestStruct.reset();
joshualittb356cbc2015-08-05 06:36:39 -0700623 gTestStruct.fAtlas =
624 context->resourceProvider()->createAtlas(kAlpha_8_GrPixelConfig,
625 ATLAS_TEXTURE_WIDTH, ATLAS_TEXTURE_HEIGHT,
626 NUM_PLOTS_X, NUM_PLOTS_Y,
627 &PathTestStruct::HandleEviction,
628 (void*)&gTestStruct);
joshualitt21279c72015-05-11 07:21:37 -0700629 }
630
631 SkMatrix viewMatrix = GrTest::TestMatrix(random);
632 GrColor color = GrRandomColor(random);
633
634 AADistanceFieldPathBatch::Geometry geometry(GrTest::TestStrokeRec(random));
joshualitt53f26aa2015-12-10 07:29:54 -0800635 geometry.fColor = color;
joshualitt21279c72015-05-11 07:21:37 -0700636 geometry.fPath = GrTest::TestPath(random);
637 geometry.fAntiAlias = random->nextBool();
jvanverth512e4372015-11-23 11:50:02 -0800638 geometry.fGenID = random->nextU();
joshualitt21279c72015-05-11 07:21:37 -0700639
joshualitt53f26aa2015-12-10 07:29:54 -0800640 return AADistanceFieldPathBatch::Create(geometry, viewMatrix,
joshualitt21279c72015-05-11 07:21:37 -0700641 gTestStruct.fAtlas,
642 &gTestStruct.fPathCache,
643 &gTestStruct.fPathList);
644}
645
646#endif