blob: bea604d702710b187d1694c8a057f1b8e62de3b7 [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"
bsalomonbb243832016-07-22 07:10:19 -070014#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"
jvanverthfa38a302014-10-06 05:59:05 -070023
jvanverthfb1e2fc2015-09-15 13:11:11 -070024#define ATLAS_TEXTURE_WIDTH 2048
jvanverthb61283f2014-10-30 05:57:21 -070025#define ATLAS_TEXTURE_HEIGHT 2048
jvanverthfb1e2fc2015-09-15 13:11:11 -070026#define PLOT_WIDTH 512
reede4ef1ca2015-02-17 18:38:38 -080027#define PLOT_HEIGHT 256
jvanverthfa38a302014-10-06 05:59:05 -070028
29#define NUM_PLOTS_X (ATLAS_TEXTURE_WIDTH / PLOT_WIDTH)
30#define NUM_PLOTS_Y (ATLAS_TEXTURE_HEIGHT / PLOT_HEIGHT)
31
jvanverthb3eb6872014-10-24 07:12:51 -070032#ifdef DF_PATH_TRACKING
bsalomonee432412016-06-27 07:18:18 -070033static int g_NumCachedShapes = 0;
34static int g_NumFreedShapes = 0;
jvanverthb3eb6872014-10-24 07:12:51 -070035#endif
36
jvanverthb61283f2014-10-30 05:57:21 -070037// mip levels
38static const int kSmallMIP = 32;
jvanverth512e4372015-11-23 11:50:02 -080039static const int kMediumMIP = 73;
jvanverthfb1e2fc2015-09-15 13:11:11 -070040static const int kLargeMIP = 162;
jvanverthb61283f2014-10-30 05:57:21 -070041
joshualitt5bf99f12015-03-13 11:47:42 -070042// Callback to clear out internal path cache when eviction occurs
43void GrAADistanceFieldPathRenderer::HandleEviction(GrBatchAtlas::AtlasID id, void* pr) {
44 GrAADistanceFieldPathRenderer* dfpr = (GrAADistanceFieldPathRenderer*)pr;
45 // remove any paths that use this plot
bsalomonee432412016-06-27 07:18:18 -070046 ShapeDataList::Iter iter;
47 iter.init(dfpr->fShapeList, ShapeDataList::Iter::kHead_IterStart);
48 ShapeData* shapeData;
49 while ((shapeData = iter.get())) {
joshualitt5bf99f12015-03-13 11:47:42 -070050 iter.next();
bsalomonee432412016-06-27 07:18:18 -070051 if (id == shapeData->fID) {
52 dfpr->fShapeCache.remove(shapeData->fKey);
53 dfpr->fShapeList.remove(shapeData);
54 delete shapeData;
joshualitt5bf99f12015-03-13 11:47:42 -070055#ifdef DF_PATH_TRACKING
56 ++g_NumFreedPaths;
57#endif
58 }
59 }
60}
61
jvanverthfa38a302014-10-06 05:59:05 -070062////////////////////////////////////////////////////////////////////////////////
halcanary96fcdcc2015-08-27 07:41:13 -070063GrAADistanceFieldPathRenderer::GrAADistanceFieldPathRenderer() : fAtlas(nullptr) {}
jvanverth6d22eca2014-10-28 11:10:48 -070064
jvanverthfa38a302014-10-06 05:59:05 -070065GrAADistanceFieldPathRenderer::~GrAADistanceFieldPathRenderer() {
bsalomonee432412016-06-27 07:18:18 -070066 ShapeDataList::Iter iter;
67 iter.init(fShapeList, ShapeDataList::Iter::kHead_IterStart);
68 ShapeData* shapeData;
69 while ((shapeData = iter.get())) {
jvanverthfa38a302014-10-06 05:59:05 -070070 iter.next();
bsalomonee432412016-06-27 07:18:18 -070071 delete shapeData;
jvanverthfa38a302014-10-06 05:59:05 -070072 }
jvanverthb3eb6872014-10-24 07:12:51 -070073
74#ifdef DF_PATH_TRACKING
bsalomonee432412016-06-27 07:18:18 -070075 SkDebugf("Cached shapes: %d, freed shapes: %d\n", g_NumCachedShapes, g_NumFreedShapes);
jvanverthb3eb6872014-10-24 07:12:51 -070076#endif
jvanverthfa38a302014-10-06 05:59:05 -070077}
78
79////////////////////////////////////////////////////////////////////////////////
bsalomon0aff2fa2015-07-31 06:48:27 -070080bool GrAADistanceFieldPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
bsalomonee432412016-06-27 07:18:18 -070081 if (!args.fShaderCaps->shaderDerivativeSupport()) {
82 return false;
83 }
84 // If the shape has no key then we won't get any reuse.
85 if (!args.fShape->hasUnstyledKey()) {
86 return false;
87 }
88 // This only supports filled paths, however, the caller may apply the style to make a filled
89 // path and try again.
90 if (!args.fShape->style().isSimpleFill()) {
91 return false;
92 }
93 // This does non-inverse antialiased fills.
94 if (!args.fAntiAlias) {
bsalomon6663acf2016-05-10 09:14:17 -070095 return false;
96 }
jvanverthfa38a302014-10-06 05:59:05 -070097 // TODO: Support inverse fill
bsalomondb7979a2016-06-27 11:08:43 -070098 if (args.fShape->inverseFilled()) {
jvanverthfa38a302014-10-06 05:59:05 -070099 return false;
100 }
jvanverthb61283f2014-10-30 05:57:21 -0700101 // currently don't support perspective
bsalomon0aff2fa2015-07-31 06:48:27 -0700102 if (args.fViewMatrix->hasPerspective()) {
jvanverthfa38a302014-10-06 05:59:05 -0700103 return false;
104 }
halcanary9d524f22016-03-29 09:03:52 -0700105
jvanverth512e4372015-11-23 11:50:02 -0800106 // only support paths with bounds within kMediumMIP by kMediumMIP,
107 // scaled to have bounds within 2.0f*kLargeMIP by 2.0f*kLargeMIP
jvanverthb61283f2014-10-30 05:57:21 -0700108 // the goal is to accelerate rendering of lots of small paths that may be scaling
bsalomon0aff2fa2015-07-31 06:48:27 -0700109 SkScalar maxScale = args.fViewMatrix->getMaxScale();
bsalomon0a0f67e2016-06-28 11:56:42 -0700110 SkRect bounds = args.fShape->styledBounds();
bsalomon6663acf2016-05-10 09:14:17 -0700111 SkScalar maxDim = SkMaxScalar(bounds.width(), bounds.height());
bsalomon6266dca2016-03-11 06:22:00 -0800112
bsalomonf93f5152016-10-26 08:00:00 -0700113 return maxDim <= kMediumMIP && maxDim * maxScale <= 2.0f*kLargeMIP;
jvanverthfa38a302014-10-06 05:59:05 -0700114}
115
jvanverthfa38a302014-10-06 05:59:05 -0700116////////////////////////////////////////////////////////////////////////////////
117
joshualitt5bf99f12015-03-13 11:47:42 -0700118// padding around path bounds to allow for antialiased pixels
119static const SkScalar kAntiAliasPad = 1.0f;
120
bsalomonabd30f52015-08-13 13:34:48 -0700121class AADistanceFieldPathBatch : public GrVertexBatch {
joshualitt5bf99f12015-03-13 11:47:42 -0700122public:
reed1b55a962015-09-17 20:16:13 -0700123 DEFINE_BATCH_CLASS_ID
124
bsalomonee432412016-06-27 07:18:18 -0700125 typedef GrAADistanceFieldPathRenderer::ShapeData ShapeData;
126 typedef SkTDynamicHash<ShapeData, ShapeData::Key> ShapeCache;
127 typedef GrAADistanceFieldPathRenderer::ShapeDataList ShapeDataList;
joshualitt5bf99f12015-03-13 11:47:42 -0700128
bsalomonf1703092016-06-29 18:41:53 -0700129 AADistanceFieldPathBatch(GrColor color,
130 const GrShape& shape,
131 bool antiAlias,
132 const SkMatrix& viewMatrix,
133 GrBatchAtlas* atlas,
134 ShapeCache* shapeCache, ShapeDataList* shapeList,
135 bool gammaCorrect)
136 : INHERITED(ClassID()) {
137 SkASSERT(shape.hasUnstyledKey());
138 fBatch.fViewMatrix = viewMatrix;
139 fGeoData.emplace_back(Geometry{color, shape, antiAlias});
joshualitt5bf99f12015-03-13 11:47:42 -0700140
bsalomonf1703092016-06-29 18:41:53 -0700141 fAtlas = atlas;
142 fShapeCache = shapeCache;
143 fShapeList = shapeList;
144 fGammaCorrect = gammaCorrect;
145
146 // Compute bounds
bsalomon88cf17d2016-07-08 06:40:56 -0700147 this->setTransformedBounds(shape.bounds(), viewMatrix, HasAABloat::kYes, IsZeroArea::kNo);
joshualitt5bf99f12015-03-13 11:47:42 -0700148 }
149
mtklein36352bf2015-03-25 18:17:31 -0700150 const char* name() const override { return "AADistanceFieldPathBatch"; }
joshualitt5bf99f12015-03-13 11:47:42 -0700151
halcanary9d524f22016-03-29 09:03:52 -0700152 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -0800153 GrInitInvariantOutput* coverage,
154 GrBatchToXPOverrides* overrides) const override {
joshualitt53f26aa2015-12-10 07:29:54 -0800155 color->setKnownFourComponents(fGeoData[0].fColor);
ethannicholasff210322015-11-24 12:10:10 -0800156 coverage->setUnknownSingleComponent();
joshualitt5bf99f12015-03-13 11:47:42 -0700157 }
158
bsalomone46f9fe2015-08-18 06:05:14 -0700159private:
ethannicholasff210322015-11-24 12:10:10 -0800160 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
joshualitt5bf99f12015-03-13 11:47:42 -0700161 // Handle any color overrides
ethannicholasff210322015-11-24 12:10:10 -0800162 if (!overrides.readsColor()) {
joshualitt53f26aa2015-12-10 07:29:54 -0800163 fGeoData[0].fColor = GrColor_ILLEGAL;
joshualitt5bf99f12015-03-13 11:47:42 -0700164 }
joshualitt53f26aa2015-12-10 07:29:54 -0800165 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
joshualitt5bf99f12015-03-13 11:47:42 -0700166
167 // setup batch properties
ethannicholasff210322015-11-24 12:10:10 -0800168 fBatch.fColorIgnored = !overrides.readsColor();
169 fBatch.fUsesLocalCoords = overrides.readsLocalCoords();
170 fBatch.fCoverageIgnored = !overrides.readsCoverage();
joshualitt5bf99f12015-03-13 11:47:42 -0700171 }
172
bsalomonb5238a72015-05-05 07:49:49 -0700173 struct FlushInfo {
Hal Canary144caf52016-11-07 17:57:18 -0500174 sk_sp<const GrBuffer> fVertexBuffer;
175 sk_sp<const GrBuffer> fIndexBuffer;
bungeman06ca8ec2016-06-09 08:01:03 -0700176 sk_sp<GrGeometryProcessor> fGeometryProcessor;
bsalomonb5238a72015-05-05 07:49:49 -0700177 int fVertexOffset;
178 int fInstancesToFlush;
179 };
180
joshualitt144c3c82015-11-30 12:30:13 -0800181 void onPrepareDraws(Target* target) const override {
joshualitt5bf99f12015-03-13 11:47:42 -0700182 int instanceCount = fGeoData.count();
183
184 SkMatrix invert;
185 if (this->usesLocalCoords() && !this->viewMatrix().invert(&invert)) {
186 SkDebugf("Could not invert viewmatrix\n");
187 return;
188 }
189
jvanverthcf371bb2016-03-10 11:10:43 -0800190 const SkMatrix& ctm = this->viewMatrix();
joshualitt5bf99f12015-03-13 11:47:42 -0700191 uint32_t flags = 0;
jvanverthcf371bb2016-03-10 11:10:43 -0800192 flags |= ctm.isScaleTranslate() ? kScaleOnly_DistanceFieldEffectFlag : 0;
193 flags |= ctm.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
brianosman0e3c5542016-04-13 13:56:21 -0700194 flags |= fGammaCorrect ? kGammaCorrect_DistanceFieldEffectFlag : 0;
joshualitt5bf99f12015-03-13 11:47:42 -0700195
196 GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kBilerp_FilterMode);
197
bsalomon342bfc22016-04-01 06:06:20 -0700198 FlushInfo flushInfo;
199
joshualitt5bf99f12015-03-13 11:47:42 -0700200 // Setup GrGeometryProcessor
201 GrBatchAtlas* atlas = fAtlas;
bungeman06ca8ec2016-06-09 08:01:03 -0700202 flushInfo.fGeometryProcessor = GrDistanceFieldPathGeoProc::Make(this->color(),
203 this->viewMatrix(),
204 atlas->getTexture(),
205 params,
206 flags,
207 this->usesLocalCoords());
joshualitt5bf99f12015-03-13 11:47:42 -0700208
joshualitt5bf99f12015-03-13 11:47:42 -0700209 // allocate vertices
bsalomon342bfc22016-04-01 06:06:20 -0700210 size_t vertexStride = flushInfo.fGeometryProcessor->getVertexStride();
joshualitt53f26aa2015-12-10 07:29:54 -0800211 SkASSERT(vertexStride == 2 * sizeof(SkPoint) + sizeof(GrColor));
bsalomonb5238a72015-05-05 07:49:49 -0700212
cdalton397536c2016-03-25 12:15:03 -0700213 const GrBuffer* vertexBuffer;
bsalomon75398562015-08-17 12:55:38 -0700214 void* vertices = target->makeVertexSpace(vertexStride,
215 kVerticesPerQuad * instanceCount,
216 &vertexBuffer,
217 &flushInfo.fVertexOffset);
bsalomonb5238a72015-05-05 07:49:49 -0700218 flushInfo.fVertexBuffer.reset(SkRef(vertexBuffer));
bsalomon75398562015-08-17 12:55:38 -0700219 flushInfo.fIndexBuffer.reset(target->resourceProvider()->refQuadIndexBuffer());
bsalomonb5238a72015-05-05 07:49:49 -0700220 if (!vertices || !flushInfo.fIndexBuffer) {
joshualitt5bf99f12015-03-13 11:47:42 -0700221 SkDebugf("Could not allocate vertices\n");
222 return;
223 }
224
bsalomonb5238a72015-05-05 07:49:49 -0700225 flushInfo.fInstancesToFlush = 0;
bsalomon6d6b6ad2016-07-13 14:45:28 -0700226 // Pointer to the next set of vertices to write.
227 intptr_t offset = reinterpret_cast<intptr_t>(vertices);
joshualitt5bf99f12015-03-13 11:47:42 -0700228 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -0800229 const Geometry& args = fGeoData[i];
joshualitt5bf99f12015-03-13 11:47:42 -0700230
231 // get mip level
232 SkScalar maxScale = this->viewMatrix().getMaxScale();
bsalomonee432412016-06-27 07:18:18 -0700233 const SkRect& bounds = args.fShape.bounds();
joshualitt5bf99f12015-03-13 11:47:42 -0700234 SkScalar maxDim = SkMaxScalar(bounds.width(), bounds.height());
235 SkScalar size = maxScale * maxDim;
236 uint32_t desiredDimension;
237 if (size <= kSmallMIP) {
238 desiredDimension = kSmallMIP;
239 } else if (size <= kMediumMIP) {
240 desiredDimension = kMediumMIP;
241 } else {
242 desiredDimension = kLargeMIP;
243 }
244
245 // check to see if path is cached
bsalomonee432412016-06-27 07:18:18 -0700246 ShapeData::Key key(args.fShape, desiredDimension);
247 ShapeData* shapeData = fShapeCache->find(key);
248 if (nullptr == shapeData || !atlas->hasID(shapeData->fID)) {
joshualitt5bf99f12015-03-13 11:47:42 -0700249 // Remove the stale cache entry
bsalomonee432412016-06-27 07:18:18 -0700250 if (shapeData) {
251 fShapeCache->remove(shapeData->fKey);
252 fShapeList->remove(shapeData);
253 delete shapeData;
joshualitt5bf99f12015-03-13 11:47:42 -0700254 }
255 SkScalar scale = desiredDimension/maxDim;
bsalomonee432412016-06-27 07:18:18 -0700256 shapeData = new ShapeData;
bsalomon75398562015-08-17 12:55:38 -0700257 if (!this->addPathToAtlas(target,
bsalomonb5238a72015-05-05 07:49:49 -0700258 &flushInfo,
joshualitt5bf99f12015-03-13 11:47:42 -0700259 atlas,
bsalomonee432412016-06-27 07:18:18 -0700260 shapeData,
261 args.fShape,
joshualitt5bf99f12015-03-13 11:47:42 -0700262 args.fAntiAlias,
263 desiredDimension,
264 scale)) {
bsalomon67c6c8e2016-07-14 07:22:19 -0700265 delete shapeData;
joshualitt5bf99f12015-03-13 11:47:42 -0700266 SkDebugf("Can't rasterize path\n");
bsalomon6d6b6ad2016-07-13 14:45:28 -0700267 continue;
joshualitt5bf99f12015-03-13 11:47:42 -0700268 }
269 }
270
bsalomonee432412016-06-27 07:18:18 -0700271 atlas->setLastUseToken(shapeData->fID, target->nextDrawToken());
joshualitt5bf99f12015-03-13 11:47:42 -0700272
bsalomon75398562015-08-17 12:55:38 -0700273 this->writePathVertices(target,
bsalomonb5238a72015-05-05 07:49:49 -0700274 atlas,
joshualitt53f26aa2015-12-10 07:29:54 -0800275 offset,
276 args.fColor,
bsalomonb5238a72015-05-05 07:49:49 -0700277 vertexStride,
278 this->viewMatrix(),
bsalomonee432412016-06-27 07:18:18 -0700279 shapeData);
bsalomon6d6b6ad2016-07-13 14:45:28 -0700280 offset += kVerticesPerQuad * vertexStride;
bsalomonb5238a72015-05-05 07:49:49 -0700281 flushInfo.fInstancesToFlush++;
joshualitt5bf99f12015-03-13 11:47:42 -0700282 }
283
bsalomon75398562015-08-17 12:55:38 -0700284 this->flush(target, &flushInfo);
joshualitt5bf99f12015-03-13 11:47:42 -0700285 }
286
bsalomon75398562015-08-17 12:55:38 -0700287 bool addPathToAtlas(GrVertexBatch::Target* target,
bsalomonb5238a72015-05-05 07:49:49 -0700288 FlushInfo* flushInfo,
joshualitt5bf99f12015-03-13 11:47:42 -0700289 GrBatchAtlas* atlas,
bsalomonee432412016-06-27 07:18:18 -0700290 ShapeData* shapeData,
291 const GrShape& shape,
jvanverth512e4372015-11-23 11:50:02 -0800292 bool antiAlias,
joshualitt5bf99f12015-03-13 11:47:42 -0700293 uint32_t dimension,
joshualitt144c3c82015-11-30 12:30:13 -0800294 SkScalar scale) const {
bsalomonee432412016-06-27 07:18:18 -0700295 const SkRect& bounds = shape.bounds();
joshualitt5bf99f12015-03-13 11:47:42 -0700296
297 // generate bounding rect for bitmap draw
298 SkRect scaledBounds = bounds;
299 // scale to mip level size
300 scaledBounds.fLeft *= scale;
301 scaledBounds.fTop *= scale;
302 scaledBounds.fRight *= scale;
303 scaledBounds.fBottom *= scale;
304 // move the origin to an integer boundary (gives better results)
305 SkScalar dx = SkScalarFraction(scaledBounds.fLeft);
306 SkScalar dy = SkScalarFraction(scaledBounds.fTop);
307 scaledBounds.offset(-dx, -dy);
308 // get integer boundary
309 SkIRect devPathBounds;
310 scaledBounds.roundOut(&devPathBounds);
311 // pad to allow room for antialiasing
jvanverthecbed9d2015-12-18 10:07:52 -0800312 const int intPad = SkScalarCeilToInt(kAntiAliasPad);
313 // pre-move origin (after outset, will be 0,0)
314 int width = devPathBounds.width();
315 int height = devPathBounds.height();
316 devPathBounds.fLeft = intPad;
317 devPathBounds.fTop = intPad;
318 devPathBounds.fRight = intPad + width;
319 devPathBounds.fBottom = intPad + height;
320 devPathBounds.outset(intPad, intPad);
joshualitt5bf99f12015-03-13 11:47:42 -0700321
322 // draw path to bitmap
323 SkMatrix drawMatrix;
324 drawMatrix.setTranslate(-bounds.left(), -bounds.top());
325 drawMatrix.postScale(scale, scale);
326 drawMatrix.postTranslate(kAntiAliasPad, kAntiAliasPad);
327
bsalomonf93f5152016-10-26 08:00:00 -0700328 // setup bitmap backing
jvanverth512e4372015-11-23 11:50:02 -0800329 SkASSERT(devPathBounds.fLeft == 0);
330 SkASSERT(devPathBounds.fTop == 0);
bsalomonf93f5152016-10-26 08:00:00 -0700331 SkAutoPixmapStorage dst;
332 if (!dst.tryAlloc(SkImageInfo::MakeA8(devPathBounds.width(),
333 devPathBounds.height()))) {
334 return false;
335 }
336 sk_bzero(dst.writable_addr(), dst.getSafeSize());
337
338 // rasterize path
339 SkPaint paint;
340 paint.setStyle(SkPaint::kFill_Style);
341 paint.setAntiAlias(antiAlias);
342
343 SkDraw draw;
344 sk_bzero(&draw, sizeof(draw));
345
346 SkRasterClip rasterClip;
347 rasterClip.setRect(devPathBounds);
348 draw.fRC = &rasterClip;
349 draw.fMatrix = &drawMatrix;
350 draw.fDst = dst;
robertphillips2a7cf5f2016-03-02 05:36:30 -0800351
bsalomonee432412016-06-27 07:18:18 -0700352 SkPath path;
353 shape.asPath(&path);
bsalomonf93f5152016-10-26 08:00:00 -0700354 draw.drawPathCoverage(path, paint);
robertphillips2a7cf5f2016-03-02 05:36:30 -0800355
bsalomonf93f5152016-10-26 08:00:00 -0700356 // generate signed distance field
robertphillips2a7cf5f2016-03-02 05:36:30 -0800357 devPathBounds.outset(SK_DistanceFieldPad, SK_DistanceFieldPad);
358 width = devPathBounds.width();
359 height = devPathBounds.height();
joshualitt5bf99f12015-03-13 11:47:42 -0700360 // TODO We should really generate this directly into the plot somehow
361 SkAutoSMalloc<1024> dfStorage(width * height * sizeof(unsigned char));
362
bsalomonf93f5152016-10-26 08:00:00 -0700363 // Generate signed distance field
364 SkGenerateDistanceFieldFromA8Image((unsigned char*)dfStorage.get(),
365 (const unsigned char*)dst.addr(),
366 dst.width(), dst.height(), dst.rowBytes());
joshualitt5bf99f12015-03-13 11:47:42 -0700367
368 // add to atlas
369 SkIPoint16 atlasLocation;
370 GrBatchAtlas::AtlasID id;
bsalomon6d6b6ad2016-07-13 14:45:28 -0700371 if (!atlas->addToAtlas(&id, target, width, height, dfStorage.get(), &atlasLocation)) {
bsalomon75398562015-08-17 12:55:38 -0700372 this->flush(target, flushInfo);
bsalomon6d6b6ad2016-07-13 14:45:28 -0700373 if (!atlas->addToAtlas(&id, target, width, height, dfStorage.get(), &atlasLocation)) {
374 return false;
375 }
joshualitt5bf99f12015-03-13 11:47:42 -0700376 }
377
378 // add to cache
bsalomonee432412016-06-27 07:18:18 -0700379 shapeData->fKey.set(shape, dimension);
380 shapeData->fScale = scale;
381 shapeData->fID = id;
joshualitt5bf99f12015-03-13 11:47:42 -0700382 // change the scaled rect to match the size of the inset distance field
383 scaledBounds.fRight = scaledBounds.fLeft +
384 SkIntToScalar(devPathBounds.width() - 2*SK_DistanceFieldInset);
385 scaledBounds.fBottom = scaledBounds.fTop +
386 SkIntToScalar(devPathBounds.height() - 2*SK_DistanceFieldInset);
387 // shift the origin to the correct place relative to the distance field
388 // need to also restore the fractional translation
389 scaledBounds.offset(-SkIntToScalar(SK_DistanceFieldInset) - kAntiAliasPad + dx,
390 -SkIntToScalar(SK_DistanceFieldInset) - kAntiAliasPad + dy);
bsalomonee432412016-06-27 07:18:18 -0700391 shapeData->fBounds = scaledBounds;
joshualitt5bf99f12015-03-13 11:47:42 -0700392 // origin we render from is inset from distance field edge
393 atlasLocation.fX += SK_DistanceFieldInset;
394 atlasLocation.fY += SK_DistanceFieldInset;
bsalomonee432412016-06-27 07:18:18 -0700395 shapeData->fAtlasLocation = atlasLocation;
joshualitt5bf99f12015-03-13 11:47:42 -0700396
bsalomonee432412016-06-27 07:18:18 -0700397 fShapeCache->add(shapeData);
398 fShapeList->addToTail(shapeData);
joshualitt5bf99f12015-03-13 11:47:42 -0700399#ifdef DF_PATH_TRACKING
400 ++g_NumCachedPaths;
401#endif
402 return true;
403 }
404
bsalomon75398562015-08-17 12:55:38 -0700405 void writePathVertices(GrDrawBatch::Target* target,
bsalomonb5238a72015-05-05 07:49:49 -0700406 GrBatchAtlas* atlas,
joshualitt53f26aa2015-12-10 07:29:54 -0800407 intptr_t offset,
408 GrColor color,
bsalomonb5238a72015-05-05 07:49:49 -0700409 size_t vertexStride,
410 const SkMatrix& viewMatrix,
bsalomonee432412016-06-27 07:18:18 -0700411 const ShapeData* shapeData) const {
joshualitt5bf99f12015-03-13 11:47:42 -0700412 GrTexture* texture = atlas->getTexture();
413
bsalomonee432412016-06-27 07:18:18 -0700414 SkScalar dx = shapeData->fBounds.fLeft;
415 SkScalar dy = shapeData->fBounds.fTop;
416 SkScalar width = shapeData->fBounds.width();
417 SkScalar height = shapeData->fBounds.height();
joshualitt5bf99f12015-03-13 11:47:42 -0700418
bsalomonee432412016-06-27 07:18:18 -0700419 SkScalar invScale = 1.0f / shapeData->fScale;
joshualitt5bf99f12015-03-13 11:47:42 -0700420 dx *= invScale;
421 dy *= invScale;
422 width *= invScale;
423 height *= invScale;
424
joshualitt53f26aa2015-12-10 07:29:54 -0800425 SkPoint* positions = reinterpret_cast<SkPoint*>(offset);
426
joshualitt5bf99f12015-03-13 11:47:42 -0700427 // vertex positions
428 // TODO make the vertex attributes a struct
429 SkRect r = SkRect::MakeXYWH(dx, dy, width, height);
430 positions->setRectFan(r.left(), r.top(), r.right(), r.bottom(), vertexStride);
431
joshualitt53f26aa2015-12-10 07:29:54 -0800432 // colors
433 for (int i = 0; i < kVerticesPerQuad; i++) {
434 GrColor* colorPtr = (GrColor*)(offset + sizeof(SkPoint) + i * vertexStride);
435 *colorPtr = color;
436 }
437
bsalomonee432412016-06-27 07:18:18 -0700438 const SkScalar tx = SkIntToScalar(shapeData->fAtlasLocation.fX);
439 const SkScalar ty = SkIntToScalar(shapeData->fAtlasLocation.fY);
benjaminwagner01e58382016-02-22 11:10:33 -0800440
joshualitt5bf99f12015-03-13 11:47:42 -0700441 // vertex texture coords
joshualitt53f26aa2015-12-10 07:29:54 -0800442 SkPoint* textureCoords = (SkPoint*)(offset + sizeof(SkPoint) + sizeof(GrColor));
benjaminwagner01e58382016-02-22 11:10:33 -0800443 textureCoords->setRectFan(tx / texture->width(),
444 ty / texture->height(),
bsalomonee432412016-06-27 07:18:18 -0700445 (tx + shapeData->fBounds.width()) / texture->width(),
446 (ty + shapeData->fBounds.height()) / texture->height(),
joshualitt5bf99f12015-03-13 11:47:42 -0700447 vertexStride);
448 }
449
joshualitt144c3c82015-11-30 12:30:13 -0800450 void flush(GrVertexBatch::Target* target, FlushInfo* flushInfo) const {
bsalomon6d6b6ad2016-07-13 14:45:28 -0700451 if (flushInfo->fInstancesToFlush) {
452 GrMesh mesh;
453 int maxInstancesPerDraw =
454 static_cast<int>(flushInfo->fIndexBuffer->gpuMemorySize() / sizeof(uint16_t) / 6);
Hal Canary144caf52016-11-07 17:57:18 -0500455 mesh.initInstanced(kTriangles_GrPrimitiveType, flushInfo->fVertexBuffer.get(),
456 flushInfo->fIndexBuffer.get(), flushInfo->fVertexOffset, kVerticesPerQuad,
bsalomon6d6b6ad2016-07-13 14:45:28 -0700457 kIndicesPerQuad, flushInfo->fInstancesToFlush, maxInstancesPerDraw);
458 target->draw(flushInfo->fGeometryProcessor.get(), mesh);
459 flushInfo->fVertexOffset += kVerticesPerQuad * flushInfo->fInstancesToFlush;
460 flushInfo->fInstancesToFlush = 0;
461 }
joshualitt5bf99f12015-03-13 11:47:42 -0700462 }
463
joshualitt53f26aa2015-12-10 07:29:54 -0800464 GrColor color() const { return fGeoData[0].fColor; }
joshualitt5bf99f12015-03-13 11:47:42 -0700465 const SkMatrix& viewMatrix() const { return fBatch.fViewMatrix; }
466 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
467
bsalomoncb02b382015-08-12 11:14:50 -0700468 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -0700469 AADistanceFieldPathBatch* that = t->cast<AADistanceFieldPathBatch>();
470 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
471 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -0700472 return false;
473 }
474
joshualitt53f26aa2015-12-10 07:29:54 -0800475 // TODO We can position on the cpu
joshualitt5bf99f12015-03-13 11:47:42 -0700476 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
477 return false;
478 }
479
bsalomonee432412016-06-27 07:18:18 -0700480 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
bsalomon88cf17d2016-07-08 06:40:56 -0700481 this->joinBounds(*that);
joshualitt5bf99f12015-03-13 11:47:42 -0700482 return true;
483 }
484
485 struct BatchTracker {
joshualitt5bf99f12015-03-13 11:47:42 -0700486 SkMatrix fViewMatrix;
487 bool fUsesLocalCoords;
488 bool fColorIgnored;
489 bool fCoverageIgnored;
490 };
491
bsalomonf1703092016-06-29 18:41:53 -0700492 struct Geometry {
493 GrColor fColor;
494 GrShape fShape;
495 bool fAntiAlias;
496 };
497
joshualitt5bf99f12015-03-13 11:47:42 -0700498 BatchTracker fBatch;
bsalomonee432412016-06-27 07:18:18 -0700499 SkSTArray<1, Geometry> fGeoData;
joshualitt5bf99f12015-03-13 11:47:42 -0700500 GrBatchAtlas* fAtlas;
bsalomonee432412016-06-27 07:18:18 -0700501 ShapeCache* fShapeCache;
502 ShapeDataList* fShapeList;
brianosman0e3c5542016-04-13 13:56:21 -0700503 bool fGammaCorrect;
reed1b55a962015-09-17 20:16:13 -0700504
505 typedef GrVertexBatch INHERITED;
joshualitt5bf99f12015-03-13 11:47:42 -0700506};
507
bsalomon0aff2fa2015-07-31 06:48:27 -0700508bool GrAADistanceFieldPathRenderer::onDrawPath(const DrawPathArgs& args) {
Brian Osman11052242016-10-27 14:47:55 -0400509 GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(),
joshualittde83b412016-01-14 09:58:36 -0800510 "GrAADistanceFieldPathRenderer::onDrawPath");
Brian Osman11052242016-10-27 14:47:55 -0400511 SkASSERT(!args.fRenderTargetContext->isUnifiedMultisampled());
bsalomonee432412016-06-27 07:18:18 -0700512 SkASSERT(args.fShape->style().isSimpleFill());
csmartdaltonecbc12b2016-06-08 10:08:43 -0700513
jvanverthfa38a302014-10-06 05:59:05 -0700514 // we've already bailed on inverse filled paths, so this is safe
bsalomon8acedde2016-06-24 10:42:16 -0700515 SkASSERT(!args.fShape->isEmpty());
bsalomonee432412016-06-27 07:18:18 -0700516 SkASSERT(args.fShape->hasUnstyledKey());
joshualitt5bf99f12015-03-13 11:47:42 -0700517 if (!fAtlas) {
Ben Wagner594f9ed2016-11-08 14:13:39 -0500518 fAtlas = args.fResourceProvider->makeAtlas(kAlpha_8_GrPixelConfig,
519 ATLAS_TEXTURE_WIDTH, ATLAS_TEXTURE_HEIGHT,
520 NUM_PLOTS_X, NUM_PLOTS_Y,
521 &GrAADistanceFieldPathRenderer::HandleEviction,
522 (void*)this);
joshualitt21279c72015-05-11 07:21:37 -0700523 if (!fAtlas) {
jvanverthfa38a302014-10-06 05:59:05 -0700524 return false;
525 }
526 }
527
Hal Canary144caf52016-11-07 17:57:18 -0500528 sk_sp<GrDrawBatch> batch(new AADistanceFieldPathBatch(args.fPaint->getColor(),
529 *args.fShape,
530 args.fAntiAlias, *args.fViewMatrix,
Ben Wagner594f9ed2016-11-08 14:13:39 -0500531 fAtlas.get(), &fShapeCache, &fShapeList,
Hal Canary144caf52016-11-07 17:57:18 -0500532 args.fGammaCorrect));
bsalomonbb243832016-07-22 07:10:19 -0700533 GrPipelineBuilder pipelineBuilder(*args.fPaint);
534 pipelineBuilder.setUserStencil(args.fUserStencilSettings);
535
Hal Canary144caf52016-11-07 17:57:18 -0500536 args.fRenderTargetContext->drawBatch(pipelineBuilder, *args.fClip, batch.get());
joshualitt9491f7f2015-02-11 11:33:38 -0800537
jvanverthfa38a302014-10-06 05:59:05 -0700538 return true;
539}
540
joshualitt21279c72015-05-11 07:21:37 -0700541///////////////////////////////////////////////////////////////////////////////////////////////////
542
543#ifdef GR_TEST_UTILS
544
545struct PathTestStruct {
bsalomonee432412016-06-27 07:18:18 -0700546 typedef GrAADistanceFieldPathRenderer::ShapeCache ShapeCache;
547 typedef GrAADistanceFieldPathRenderer::ShapeData ShapeData;
548 typedef GrAADistanceFieldPathRenderer::ShapeDataList ShapeDataList;
halcanary96fcdcc2015-08-27 07:41:13 -0700549 PathTestStruct() : fContextID(SK_InvalidGenID), fAtlas(nullptr) {}
joshualitt21279c72015-05-11 07:21:37 -0700550 ~PathTestStruct() { this->reset(); }
551
552 void reset() {
bsalomonee432412016-06-27 07:18:18 -0700553 ShapeDataList::Iter iter;
554 iter.init(fShapeList, ShapeDataList::Iter::kHead_IterStart);
555 ShapeData* shapeData;
556 while ((shapeData = iter.get())) {
joshualitt21279c72015-05-11 07:21:37 -0700557 iter.next();
bsalomonee432412016-06-27 07:18:18 -0700558 fShapeList.remove(shapeData);
559 delete shapeData;
joshualitt21279c72015-05-11 07:21:37 -0700560 }
Ben Wagner594f9ed2016-11-08 14:13:39 -0500561 fAtlas = nullptr;
bsalomonee432412016-06-27 07:18:18 -0700562 fShapeCache.reset();
joshualitt21279c72015-05-11 07:21:37 -0700563 }
564
565 static void HandleEviction(GrBatchAtlas::AtlasID id, void* pr) {
566 PathTestStruct* dfpr = (PathTestStruct*)pr;
567 // remove any paths that use this plot
bsalomonee432412016-06-27 07:18:18 -0700568 ShapeDataList::Iter iter;
569 iter.init(dfpr->fShapeList, ShapeDataList::Iter::kHead_IterStart);
570 ShapeData* shapeData;
571 while ((shapeData = iter.get())) {
joshualitt21279c72015-05-11 07:21:37 -0700572 iter.next();
bsalomonee432412016-06-27 07:18:18 -0700573 if (id == shapeData->fID) {
574 dfpr->fShapeCache.remove(shapeData->fKey);
575 dfpr->fShapeList.remove(shapeData);
576 delete shapeData;
joshualitt21279c72015-05-11 07:21:37 -0700577 }
578 }
579 }
580
581 uint32_t fContextID;
Ben Wagner594f9ed2016-11-08 14:13:39 -0500582 std::unique_ptr<GrBatchAtlas> fAtlas;
bsalomonee432412016-06-27 07:18:18 -0700583 ShapeCache fShapeCache;
584 ShapeDataList fShapeList;
joshualitt21279c72015-05-11 07:21:37 -0700585};
586
bsalomonabd30f52015-08-13 13:34:48 -0700587DRAW_BATCH_TEST_DEFINE(AADistanceFieldPathBatch) {
joshualitt21279c72015-05-11 07:21:37 -0700588 static PathTestStruct gTestStruct;
589
590 if (context->uniqueID() != gTestStruct.fContextID) {
591 gTestStruct.fContextID = context->uniqueID();
592 gTestStruct.reset();
joshualittb356cbc2015-08-05 06:36:39 -0700593 gTestStruct.fAtlas =
Ben Wagner594f9ed2016-11-08 14:13:39 -0500594 context->resourceProvider()->makeAtlas(kAlpha_8_GrPixelConfig,
595 ATLAS_TEXTURE_WIDTH, ATLAS_TEXTURE_HEIGHT,
596 NUM_PLOTS_X, NUM_PLOTS_Y,
597 &PathTestStruct::HandleEviction,
598 (void*)&gTestStruct);
joshualitt21279c72015-05-11 07:21:37 -0700599 }
600
601 SkMatrix viewMatrix = GrTest::TestMatrix(random);
602 GrColor color = GrRandomColor(random);
brianosman0e3c5542016-04-13 13:56:21 -0700603 bool gammaCorrect = random->nextBool();
joshualitt21279c72015-05-11 07:21:37 -0700604
bsalomonee432412016-06-27 07:18:18 -0700605 // This path renderer only allows fill styles.
606 GrShape shape(GrTest::TestPath(random), GrStyle::SimpleFill());
bsalomonf1703092016-06-29 18:41:53 -0700607 bool antiAlias = random->nextBool();
joshualitt21279c72015-05-11 07:21:37 -0700608
bsalomonf1703092016-06-29 18:41:53 -0700609 return new AADistanceFieldPathBatch(color,
610 shape,
611 antiAlias,
612 viewMatrix,
Ben Wagner594f9ed2016-11-08 14:13:39 -0500613 gTestStruct.fAtlas.get(),
bsalomonf1703092016-06-29 18:41:53 -0700614 &gTestStruct.fShapeCache,
615 &gTestStruct.fShapeList,
616 gammaCorrect);
joshualitt21279c72015-05-11 07:21:37 -0700617}
618
619#endif