blob: 8aaabbc229d4ef205bc66a9071647645e93caad8 [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
bsalomonee432412016-06-27 07:18:18 -070034static int g_NumCachedShapes = 0;
35static int g_NumFreedShapes = 0;
jvanverthb3eb6872014-10-24 07:12:51 -070036#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
bsalomonee432412016-06-27 07:18:18 -070047 ShapeDataList::Iter iter;
48 iter.init(dfpr->fShapeList, ShapeDataList::Iter::kHead_IterStart);
49 ShapeData* shapeData;
50 while ((shapeData = iter.get())) {
joshualitt5bf99f12015-03-13 11:47:42 -070051 iter.next();
bsalomonee432412016-06-27 07:18:18 -070052 if (id == shapeData->fID) {
53 dfpr->fShapeCache.remove(shapeData->fKey);
54 dfpr->fShapeList.remove(shapeData);
55 delete shapeData;
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() {
bsalomonee432412016-06-27 07:18:18 -070067 ShapeDataList::Iter iter;
68 iter.init(fShapeList, ShapeDataList::Iter::kHead_IterStart);
69 ShapeData* shapeData;
70 while ((shapeData = iter.get())) {
jvanverthfa38a302014-10-06 05:59:05 -070071 iter.next();
bsalomonee432412016-06-27 07:18:18 -070072 delete shapeData;
jvanverthfa38a302014-10-06 05:59:05 -070073 }
halcanary385fe4d2015-08-26 13:07:48 -070074 delete fAtlas;
jvanverthb3eb6872014-10-24 07:12:51 -070075
76#ifdef DF_PATH_TRACKING
bsalomonee432412016-06-27 07:18:18 -070077 SkDebugf("Cached shapes: %d, freed shapes: %d\n", g_NumCachedShapes, g_NumFreedShapes);
jvanverthb3eb6872014-10-24 07:12:51 -070078#endif
jvanverthfa38a302014-10-06 05:59:05 -070079}
80
81////////////////////////////////////////////////////////////////////////////////
bsalomon0aff2fa2015-07-31 06:48:27 -070082bool GrAADistanceFieldPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
bsalomonee432412016-06-27 07:18:18 -070083 if (!args.fShaderCaps->shaderDerivativeSupport()) {
84 return false;
85 }
86 // If the shape has no key then we won't get any reuse.
87 if (!args.fShape->hasUnstyledKey()) {
88 return false;
89 }
90 // This only supports filled paths, however, the caller may apply the style to make a filled
91 // path and try again.
92 if (!args.fShape->style().isSimpleFill()) {
93 return false;
94 }
95 // This does non-inverse antialiased fills.
96 if (!args.fAntiAlias) {
bsalomon6663acf2016-05-10 09:14:17 -070097 return false;
98 }
jvanverthfa38a302014-10-06 05:59:05 -070099 // TODO: Support inverse fill
bsalomonee432412016-06-27 07:18:18 -0700100 if (!args.fShape->inverseFilled()) {
jvanverthfa38a302014-10-06 05:59:05 -0700101 return false;
102 }
jvanverthb61283f2014-10-30 05:57:21 -0700103 // currently don't support perspective
bsalomon0aff2fa2015-07-31 06:48:27 -0700104 if (args.fViewMatrix->hasPerspective()) {
jvanverthfa38a302014-10-06 05:59:05 -0700105 return false;
106 }
halcanary9d524f22016-03-29 09:03:52 -0700107
jvanverth512e4372015-11-23 11:50:02 -0800108 // only support paths with bounds within kMediumMIP by kMediumMIP,
109 // scaled to have bounds within 2.0f*kLargeMIP by 2.0f*kLargeMIP
jvanverthb61283f2014-10-30 05:57:21 -0700110 // the goal is to accelerate rendering of lots of small paths that may be scaling
bsalomon0aff2fa2015-07-31 06:48:27 -0700111 SkScalar maxScale = args.fViewMatrix->getMaxScale();
bsalomon6663acf2016-05-10 09:14:17 -0700112 SkRect bounds;
bsalomon8acedde2016-06-24 10:42:16 -0700113 args.fShape->styledBounds(&bounds);
bsalomon6663acf2016-05-10 09:14:17 -0700114 SkScalar maxDim = SkMaxScalar(bounds.width(), bounds.height());
bsalomon6266dca2016-03-11 06:22:00 -0800115
jvanverth512e4372015-11-23 11:50:02 -0800116 return maxDim <= kMediumMIP && maxDim * maxScale <= 2.0f*kLargeMIP;
jvanverthfa38a302014-10-06 05:59:05 -0700117}
118
jvanverthfa38a302014-10-06 05:59:05 -0700119////////////////////////////////////////////////////////////////////////////////
120
joshualitt5bf99f12015-03-13 11:47:42 -0700121// padding around path bounds to allow for antialiased pixels
122static const SkScalar kAntiAliasPad = 1.0f;
123
bsalomonabd30f52015-08-13 13:34:48 -0700124class AADistanceFieldPathBatch : public GrVertexBatch {
joshualitt5bf99f12015-03-13 11:47:42 -0700125public:
reed1b55a962015-09-17 20:16:13 -0700126 DEFINE_BATCH_CLASS_ID
127
bsalomonee432412016-06-27 07:18:18 -0700128 typedef GrAADistanceFieldPathRenderer::ShapeData ShapeData;
129 typedef SkTDynamicHash<ShapeData, ShapeData::Key> ShapeCache;
130 typedef GrAADistanceFieldPathRenderer::ShapeDataList ShapeDataList;
joshualitt5bf99f12015-03-13 11:47:42 -0700131
132 struct Geometry {
bsalomonee432412016-06-27 07:18:18 -0700133 GrShape fShape;
joshualitt53f26aa2015-12-10 07:29:54 -0800134 GrColor fColor;
joshualitt5bf99f12015-03-13 11:47:42 -0700135 bool fAntiAlias;
joshualitt5bf99f12015-03-13 11:47:42 -0700136 };
137
joshualitt53f26aa2015-12-10 07:29:54 -0800138 static GrDrawBatch* Create(const Geometry& geometry, const SkMatrix& viewMatrix,
bsalomonee432412016-06-27 07:18:18 -0700139 GrBatchAtlas* atlas, ShapeCache* shapeCache,
140 ShapeDataList* shapeList, bool gammaCorrect) {
141 return new AADistanceFieldPathBatch(geometry, viewMatrix, atlas, shapeCache, shapeList,
brianosman0e3c5542016-04-13 13:56:21 -0700142 gammaCorrect);
joshualitt5bf99f12015-03-13 11:47:42 -0700143 }
144
mtklein36352bf2015-03-25 18:17:31 -0700145 const char* name() const override { return "AADistanceFieldPathBatch"; }
joshualitt5bf99f12015-03-13 11:47:42 -0700146
halcanary9d524f22016-03-29 09:03:52 -0700147 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -0800148 GrInitInvariantOutput* coverage,
149 GrBatchToXPOverrides* overrides) const override {
joshualitt53f26aa2015-12-10 07:29:54 -0800150 color->setKnownFourComponents(fGeoData[0].fColor);
ethannicholasff210322015-11-24 12:10:10 -0800151 coverage->setUnknownSingleComponent();
joshualitt5bf99f12015-03-13 11:47:42 -0700152 }
153
bsalomone46f9fe2015-08-18 06:05:14 -0700154private:
ethannicholasff210322015-11-24 12:10:10 -0800155 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
joshualitt5bf99f12015-03-13 11:47:42 -0700156 // Handle any color overrides
ethannicholasff210322015-11-24 12:10:10 -0800157 if (!overrides.readsColor()) {
joshualitt53f26aa2015-12-10 07:29:54 -0800158 fGeoData[0].fColor = GrColor_ILLEGAL;
joshualitt5bf99f12015-03-13 11:47:42 -0700159 }
joshualitt53f26aa2015-12-10 07:29:54 -0800160 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
joshualitt5bf99f12015-03-13 11:47:42 -0700161
162 // setup batch properties
ethannicholasff210322015-11-24 12:10:10 -0800163 fBatch.fColorIgnored = !overrides.readsColor();
164 fBatch.fUsesLocalCoords = overrides.readsLocalCoords();
165 fBatch.fCoverageIgnored = !overrides.readsCoverage();
joshualitt5bf99f12015-03-13 11:47:42 -0700166 }
167
bsalomonb5238a72015-05-05 07:49:49 -0700168 struct FlushInfo {
bungeman06ca8ec2016-06-09 08:01:03 -0700169 SkAutoTUnref<const GrBuffer> fVertexBuffer;
170 SkAutoTUnref<const GrBuffer> fIndexBuffer;
171 sk_sp<GrGeometryProcessor> fGeometryProcessor;
bsalomonb5238a72015-05-05 07:49:49 -0700172 int fVertexOffset;
173 int fInstancesToFlush;
174 };
175
joshualitt144c3c82015-11-30 12:30:13 -0800176 void onPrepareDraws(Target* target) const override {
joshualitt5bf99f12015-03-13 11:47:42 -0700177 int instanceCount = fGeoData.count();
178
179 SkMatrix invert;
180 if (this->usesLocalCoords() && !this->viewMatrix().invert(&invert)) {
181 SkDebugf("Could not invert viewmatrix\n");
182 return;
183 }
184
jvanverthcf371bb2016-03-10 11:10:43 -0800185 const SkMatrix& ctm = this->viewMatrix();
joshualitt5bf99f12015-03-13 11:47:42 -0700186 uint32_t flags = 0;
jvanverthcf371bb2016-03-10 11:10:43 -0800187 flags |= ctm.isScaleTranslate() ? kScaleOnly_DistanceFieldEffectFlag : 0;
188 flags |= ctm.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
brianosman0e3c5542016-04-13 13:56:21 -0700189 flags |= fGammaCorrect ? kGammaCorrect_DistanceFieldEffectFlag : 0;
joshualitt5bf99f12015-03-13 11:47:42 -0700190
191 GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kBilerp_FilterMode);
192
bsalomon342bfc22016-04-01 06:06:20 -0700193 FlushInfo flushInfo;
194
joshualitt5bf99f12015-03-13 11:47:42 -0700195 // Setup GrGeometryProcessor
196 GrBatchAtlas* atlas = fAtlas;
bungeman06ca8ec2016-06-09 08:01:03 -0700197 flushInfo.fGeometryProcessor = GrDistanceFieldPathGeoProc::Make(this->color(),
198 this->viewMatrix(),
199 atlas->getTexture(),
200 params,
201 flags,
202 this->usesLocalCoords());
joshualitt5bf99f12015-03-13 11:47:42 -0700203
joshualitt5bf99f12015-03-13 11:47:42 -0700204 // allocate vertices
bsalomon342bfc22016-04-01 06:06:20 -0700205 size_t vertexStride = flushInfo.fGeometryProcessor->getVertexStride();
joshualitt53f26aa2015-12-10 07:29:54 -0800206 SkASSERT(vertexStride == 2 * sizeof(SkPoint) + sizeof(GrColor));
bsalomonb5238a72015-05-05 07:49:49 -0700207
cdalton397536c2016-03-25 12:15:03 -0700208 const GrBuffer* vertexBuffer;
bsalomon75398562015-08-17 12:55:38 -0700209 void* vertices = target->makeVertexSpace(vertexStride,
210 kVerticesPerQuad * instanceCount,
211 &vertexBuffer,
212 &flushInfo.fVertexOffset);
bsalomonb5238a72015-05-05 07:49:49 -0700213 flushInfo.fVertexBuffer.reset(SkRef(vertexBuffer));
bsalomon75398562015-08-17 12:55:38 -0700214 flushInfo.fIndexBuffer.reset(target->resourceProvider()->refQuadIndexBuffer());
bsalomonb5238a72015-05-05 07:49:49 -0700215 if (!vertices || !flushInfo.fIndexBuffer) {
joshualitt5bf99f12015-03-13 11:47:42 -0700216 SkDebugf("Could not allocate vertices\n");
217 return;
218 }
219
bsalomonb5238a72015-05-05 07:49:49 -0700220 flushInfo.fInstancesToFlush = 0;
joshualitt5bf99f12015-03-13 11:47:42 -0700221 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -0800222 const Geometry& args = fGeoData[i];
joshualitt5bf99f12015-03-13 11:47:42 -0700223
224 // get mip level
225 SkScalar maxScale = this->viewMatrix().getMaxScale();
bsalomonee432412016-06-27 07:18:18 -0700226 const SkRect& bounds = args.fShape.bounds();
joshualitt5bf99f12015-03-13 11:47:42 -0700227 SkScalar maxDim = SkMaxScalar(bounds.width(), bounds.height());
228 SkScalar size = maxScale * maxDim;
229 uint32_t desiredDimension;
230 if (size <= kSmallMIP) {
231 desiredDimension = kSmallMIP;
232 } else if (size <= kMediumMIP) {
233 desiredDimension = kMediumMIP;
234 } else {
235 desiredDimension = kLargeMIP;
236 }
237
238 // check to see if path is cached
bsalomonee432412016-06-27 07:18:18 -0700239 ShapeData::Key key(args.fShape, desiredDimension);
240 ShapeData* shapeData = fShapeCache->find(key);
241 if (nullptr == shapeData || !atlas->hasID(shapeData->fID)) {
joshualitt5bf99f12015-03-13 11:47:42 -0700242 // Remove the stale cache entry
bsalomonee432412016-06-27 07:18:18 -0700243 if (shapeData) {
244 fShapeCache->remove(shapeData->fKey);
245 fShapeList->remove(shapeData);
246 delete shapeData;
joshualitt5bf99f12015-03-13 11:47:42 -0700247 }
248 SkScalar scale = desiredDimension/maxDim;
bsalomonee432412016-06-27 07:18:18 -0700249 shapeData = new ShapeData;
bsalomon75398562015-08-17 12:55:38 -0700250 if (!this->addPathToAtlas(target,
bsalomonb5238a72015-05-05 07:49:49 -0700251 &flushInfo,
joshualitt5bf99f12015-03-13 11:47:42 -0700252 atlas,
bsalomonee432412016-06-27 07:18:18 -0700253 shapeData,
254 args.fShape,
joshualitt5bf99f12015-03-13 11:47:42 -0700255 args.fAntiAlias,
256 desiredDimension,
257 scale)) {
258 SkDebugf("Can't rasterize path\n");
259 return;
260 }
261 }
262
bsalomonee432412016-06-27 07:18:18 -0700263 atlas->setLastUseToken(shapeData->fID, target->nextDrawToken());
joshualitt5bf99f12015-03-13 11:47:42 -0700264
265 // Now set vertices
266 intptr_t offset = reinterpret_cast<intptr_t>(vertices);
bsalomonb5238a72015-05-05 07:49:49 -0700267 offset += i * kVerticesPerQuad * vertexStride;
bsalomon75398562015-08-17 12:55:38 -0700268 this->writePathVertices(target,
bsalomonb5238a72015-05-05 07:49:49 -0700269 atlas,
joshualitt53f26aa2015-12-10 07:29:54 -0800270 offset,
271 args.fColor,
bsalomonb5238a72015-05-05 07:49:49 -0700272 vertexStride,
273 this->viewMatrix(),
bsalomonee432412016-06-27 07:18:18 -0700274 args.fShape,
275 shapeData);
bsalomonb5238a72015-05-05 07:49:49 -0700276 flushInfo.fInstancesToFlush++;
joshualitt5bf99f12015-03-13 11:47:42 -0700277 }
278
bsalomon75398562015-08-17 12:55:38 -0700279 this->flush(target, &flushInfo);
joshualitt5bf99f12015-03-13 11:47:42 -0700280 }
281
joshualitt53f26aa2015-12-10 07:29:54 -0800282 AADistanceFieldPathBatch(const Geometry& geometry,
283 const SkMatrix& viewMatrix,
joshualitt5bf99f12015-03-13 11:47:42 -0700284 GrBatchAtlas* atlas,
bsalomonee432412016-06-27 07:18:18 -0700285 ShapeCache* shapeCache, ShapeDataList* shapeList,
brianosman0e3c5542016-04-13 13:56:21 -0700286 bool gammaCorrect)
reed1b55a962015-09-17 20:16:13 -0700287 : INHERITED(ClassID()) {
bsalomonee432412016-06-27 07:18:18 -0700288 SkASSERT(geometry.fShape.hasUnstyledKey());
joshualitt5bf99f12015-03-13 11:47:42 -0700289 fBatch.fViewMatrix = viewMatrix;
290 fGeoData.push_back(geometry);
bsalomonee432412016-06-27 07:18:18 -0700291 SkASSERT(fGeoData[0].fShape.hasUnstyledKey());
joshualitt5bf99f12015-03-13 11:47:42 -0700292
293 fAtlas = atlas;
bsalomonee432412016-06-27 07:18:18 -0700294 fShapeCache = shapeCache;
295 fShapeList = shapeList;
brianosman0e3c5542016-04-13 13:56:21 -0700296 fGammaCorrect = gammaCorrect;
joshualitt99c7c072015-05-01 13:43:30 -0700297
298 // Compute bounds
bsalomonee432412016-06-27 07:18:18 -0700299 fBounds = geometry.fShape.bounds();
joshualitt99c7c072015-05-01 13:43:30 -0700300 viewMatrix.mapRect(&fBounds);
joshualitt5bf99f12015-03-13 11:47:42 -0700301 }
302
bsalomon75398562015-08-17 12:55:38 -0700303 bool addPathToAtlas(GrVertexBatch::Target* target,
bsalomonb5238a72015-05-05 07:49:49 -0700304 FlushInfo* flushInfo,
joshualitt5bf99f12015-03-13 11:47:42 -0700305 GrBatchAtlas* atlas,
bsalomonee432412016-06-27 07:18:18 -0700306 ShapeData* shapeData,
307 const GrShape& shape,
jvanverth512e4372015-11-23 11:50:02 -0800308 bool antiAlias,
joshualitt5bf99f12015-03-13 11:47:42 -0700309 uint32_t dimension,
joshualitt144c3c82015-11-30 12:30:13 -0800310 SkScalar scale) const {
bsalomonee432412016-06-27 07:18:18 -0700311 const SkRect& bounds = shape.bounds();
joshualitt5bf99f12015-03-13 11:47:42 -0700312
313 // generate bounding rect for bitmap draw
314 SkRect scaledBounds = bounds;
315 // scale to mip level size
316 scaledBounds.fLeft *= scale;
317 scaledBounds.fTop *= scale;
318 scaledBounds.fRight *= scale;
319 scaledBounds.fBottom *= scale;
320 // move the origin to an integer boundary (gives better results)
321 SkScalar dx = SkScalarFraction(scaledBounds.fLeft);
322 SkScalar dy = SkScalarFraction(scaledBounds.fTop);
323 scaledBounds.offset(-dx, -dy);
324 // get integer boundary
325 SkIRect devPathBounds;
326 scaledBounds.roundOut(&devPathBounds);
327 // pad to allow room for antialiasing
jvanverthecbed9d2015-12-18 10:07:52 -0800328 const int intPad = SkScalarCeilToInt(kAntiAliasPad);
329 // pre-move origin (after outset, will be 0,0)
330 int width = devPathBounds.width();
331 int height = devPathBounds.height();
332 devPathBounds.fLeft = intPad;
333 devPathBounds.fTop = intPad;
334 devPathBounds.fRight = intPad + width;
335 devPathBounds.fBottom = intPad + height;
336 devPathBounds.outset(intPad, intPad);
joshualitt5bf99f12015-03-13 11:47:42 -0700337
338 // draw path to bitmap
339 SkMatrix drawMatrix;
340 drawMatrix.setTranslate(-bounds.left(), -bounds.top());
341 drawMatrix.postScale(scale, scale);
342 drawMatrix.postTranslate(kAntiAliasPad, kAntiAliasPad);
343
robertphillips2a7cf5f2016-03-02 05:36:30 -0800344 // setup bitmap backing
jvanverth512e4372015-11-23 11:50:02 -0800345 SkASSERT(devPathBounds.fLeft == 0);
346 SkASSERT(devPathBounds.fTop == 0);
robertphillips2a7cf5f2016-03-02 05:36:30 -0800347 SkAutoPixmapStorage dst;
348 if (!dst.tryAlloc(SkImageInfo::MakeA8(devPathBounds.width(),
349 devPathBounds.height()))) {
350 return false;
351 }
352 sk_bzero(dst.writable_addr(), dst.getSafeSize());
joshualitt5bf99f12015-03-13 11:47:42 -0700353
robertphillips2a7cf5f2016-03-02 05:36:30 -0800354 // rasterize path
355 SkPaint paint;
356 paint.setStyle(SkPaint::kFill_Style);
357 paint.setAntiAlias(antiAlias);
358
359 SkDraw draw;
360 sk_bzero(&draw, sizeof(draw));
361
362 SkRasterClip rasterClip;
363 rasterClip.setRect(devPathBounds);
364 draw.fRC = &rasterClip;
robertphillips2a7cf5f2016-03-02 05:36:30 -0800365 draw.fMatrix = &drawMatrix;
366 draw.fDst = dst;
367
bsalomonee432412016-06-27 07:18:18 -0700368 SkPath path;
369 shape.asPath(&path);
robertphillips2a7cf5f2016-03-02 05:36:30 -0800370 draw.drawPathCoverage(path, paint);
371
372 // generate signed distance field
373 devPathBounds.outset(SK_DistanceFieldPad, SK_DistanceFieldPad);
374 width = devPathBounds.width();
375 height = devPathBounds.height();
joshualitt5bf99f12015-03-13 11:47:42 -0700376 // TODO We should really generate this directly into the plot somehow
377 SkAutoSMalloc<1024> dfStorage(width * height * sizeof(unsigned char));
378
robertphillips2a7cf5f2016-03-02 05:36:30 -0800379 // Generate signed distance field
380 SkGenerateDistanceFieldFromA8Image((unsigned char*)dfStorage.get(),
381 (const unsigned char*)dst.addr(),
382 dst.width(), dst.height(), dst.rowBytes());
joshualitt5bf99f12015-03-13 11:47:42 -0700383
384 // add to atlas
385 SkIPoint16 atlasLocation;
386 GrBatchAtlas::AtlasID id;
bsalomon75398562015-08-17 12:55:38 -0700387 bool success = atlas->addToAtlas(&id, target, width, height, dfStorage.get(),
joshualitt5bf99f12015-03-13 11:47:42 -0700388 &atlasLocation);
389 if (!success) {
bsalomon75398562015-08-17 12:55:38 -0700390 this->flush(target, flushInfo);
joshualitt5bf99f12015-03-13 11:47:42 -0700391
bsalomon75398562015-08-17 12:55:38 -0700392 SkDEBUGCODE(success =) atlas->addToAtlas(&id, target, width, height,
joshualitt5bf99f12015-03-13 11:47:42 -0700393 dfStorage.get(), &atlasLocation);
394 SkASSERT(success);
395
396 }
397
398 // add to cache
bsalomonee432412016-06-27 07:18:18 -0700399 shapeData->fKey.set(shape, dimension);
400 shapeData->fScale = scale;
401 shapeData->fID = id;
joshualitt5bf99f12015-03-13 11:47:42 -0700402 // change the scaled rect to match the size of the inset distance field
403 scaledBounds.fRight = scaledBounds.fLeft +
404 SkIntToScalar(devPathBounds.width() - 2*SK_DistanceFieldInset);
405 scaledBounds.fBottom = scaledBounds.fTop +
406 SkIntToScalar(devPathBounds.height() - 2*SK_DistanceFieldInset);
407 // shift the origin to the correct place relative to the distance field
408 // need to also restore the fractional translation
409 scaledBounds.offset(-SkIntToScalar(SK_DistanceFieldInset) - kAntiAliasPad + dx,
410 -SkIntToScalar(SK_DistanceFieldInset) - kAntiAliasPad + dy);
bsalomonee432412016-06-27 07:18:18 -0700411 shapeData->fBounds = scaledBounds;
joshualitt5bf99f12015-03-13 11:47:42 -0700412 // origin we render from is inset from distance field edge
413 atlasLocation.fX += SK_DistanceFieldInset;
414 atlasLocation.fY += SK_DistanceFieldInset;
bsalomonee432412016-06-27 07:18:18 -0700415 shapeData->fAtlasLocation = atlasLocation;
joshualitt5bf99f12015-03-13 11:47:42 -0700416
bsalomonee432412016-06-27 07:18:18 -0700417 fShapeCache->add(shapeData);
418 fShapeList->addToTail(shapeData);
joshualitt5bf99f12015-03-13 11:47:42 -0700419#ifdef DF_PATH_TRACKING
420 ++g_NumCachedPaths;
421#endif
422 return true;
423 }
424
bsalomon75398562015-08-17 12:55:38 -0700425 void writePathVertices(GrDrawBatch::Target* target,
bsalomonb5238a72015-05-05 07:49:49 -0700426 GrBatchAtlas* atlas,
joshualitt53f26aa2015-12-10 07:29:54 -0800427 intptr_t offset,
428 GrColor color,
bsalomonb5238a72015-05-05 07:49:49 -0700429 size_t vertexStride,
430 const SkMatrix& viewMatrix,
bsalomonee432412016-06-27 07:18:18 -0700431 const GrShape& shape,
432 const ShapeData* shapeData) const {
joshualitt5bf99f12015-03-13 11:47:42 -0700433 GrTexture* texture = atlas->getTexture();
434
bsalomonee432412016-06-27 07:18:18 -0700435 SkScalar dx = shapeData->fBounds.fLeft;
436 SkScalar dy = shapeData->fBounds.fTop;
437 SkScalar width = shapeData->fBounds.width();
438 SkScalar height = shapeData->fBounds.height();
joshualitt5bf99f12015-03-13 11:47:42 -0700439
bsalomonee432412016-06-27 07:18:18 -0700440 SkScalar invScale = 1.0f / shapeData->fScale;
joshualitt5bf99f12015-03-13 11:47:42 -0700441 dx *= invScale;
442 dy *= invScale;
443 width *= invScale;
444 height *= invScale;
445
joshualitt53f26aa2015-12-10 07:29:54 -0800446 SkPoint* positions = reinterpret_cast<SkPoint*>(offset);
447
joshualitt5bf99f12015-03-13 11:47:42 -0700448 // vertex positions
449 // TODO make the vertex attributes a struct
450 SkRect r = SkRect::MakeXYWH(dx, dy, width, height);
451 positions->setRectFan(r.left(), r.top(), r.right(), r.bottom(), vertexStride);
452
joshualitt53f26aa2015-12-10 07:29:54 -0800453 // colors
454 for (int i = 0; i < kVerticesPerQuad; i++) {
455 GrColor* colorPtr = (GrColor*)(offset + sizeof(SkPoint) + i * vertexStride);
456 *colorPtr = color;
457 }
458
bsalomonee432412016-06-27 07:18:18 -0700459 const SkScalar tx = SkIntToScalar(shapeData->fAtlasLocation.fX);
460 const SkScalar ty = SkIntToScalar(shapeData->fAtlasLocation.fY);
benjaminwagner01e58382016-02-22 11:10:33 -0800461
joshualitt5bf99f12015-03-13 11:47:42 -0700462 // vertex texture coords
joshualitt53f26aa2015-12-10 07:29:54 -0800463 SkPoint* textureCoords = (SkPoint*)(offset + sizeof(SkPoint) + sizeof(GrColor));
benjaminwagner01e58382016-02-22 11:10:33 -0800464 textureCoords->setRectFan(tx / texture->width(),
465 ty / texture->height(),
bsalomonee432412016-06-27 07:18:18 -0700466 (tx + shapeData->fBounds.width()) / texture->width(),
467 (ty + shapeData->fBounds.height()) / texture->height(),
joshualitt5bf99f12015-03-13 11:47:42 -0700468 vertexStride);
469 }
470
joshualitt144c3c82015-11-30 12:30:13 -0800471 void flush(GrVertexBatch::Target* target, FlushInfo* flushInfo) const {
egdaniel0e1853c2016-03-17 11:35:45 -0700472 GrMesh mesh;
cdalton397536c2016-03-25 12:15:03 -0700473 int maxInstancesPerDraw =
474 static_cast<int>(flushInfo->fIndexBuffer->gpuMemorySize() / sizeof(uint16_t) / 6);
egdaniel0e1853c2016-03-17 11:35:45 -0700475 mesh.initInstanced(kTriangles_GrPrimitiveType, flushInfo->fVertexBuffer,
bsalomonb5238a72015-05-05 07:49:49 -0700476 flushInfo->fIndexBuffer, flushInfo->fVertexOffset, kVerticesPerQuad,
bsalomone64eb572015-05-07 11:35:55 -0700477 kIndicesPerQuad, flushInfo->fInstancesToFlush, maxInstancesPerDraw);
bungeman06ca8ec2016-06-09 08:01:03 -0700478 target->draw(flushInfo->fGeometryProcessor.get(), mesh);
bsalomonb5238a72015-05-05 07:49:49 -0700479 flushInfo->fVertexOffset += kVerticesPerQuad * flushInfo->fInstancesToFlush;
480 flushInfo->fInstancesToFlush = 0;
joshualitt5bf99f12015-03-13 11:47:42 -0700481 }
482
joshualitt53f26aa2015-12-10 07:29:54 -0800483 GrColor color() const { return fGeoData[0].fColor; }
joshualitt5bf99f12015-03-13 11:47:42 -0700484 const SkMatrix& viewMatrix() const { return fBatch.fViewMatrix; }
485 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
486
bsalomoncb02b382015-08-12 11:14:50 -0700487 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -0700488 AADistanceFieldPathBatch* that = t->cast<AADistanceFieldPathBatch>();
489 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
490 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -0700491 return false;
492 }
493
joshualitt53f26aa2015-12-10 07:29:54 -0800494 // TODO We can position on the cpu
joshualitt5bf99f12015-03-13 11:47:42 -0700495 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
496 return false;
497 }
498
bsalomonee432412016-06-27 07:18:18 -0700499 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
joshualitt99c7c072015-05-01 13:43:30 -0700500 this->joinBounds(that->bounds());
joshualitt5bf99f12015-03-13 11:47:42 -0700501 return true;
502 }
503
504 struct BatchTracker {
joshualitt5bf99f12015-03-13 11:47:42 -0700505 SkMatrix fViewMatrix;
506 bool fUsesLocalCoords;
507 bool fColorIgnored;
508 bool fCoverageIgnored;
509 };
510
511 BatchTracker fBatch;
bsalomonee432412016-06-27 07:18:18 -0700512 SkSTArray<1, Geometry> fGeoData;
joshualitt5bf99f12015-03-13 11:47:42 -0700513 GrBatchAtlas* fAtlas;
bsalomonee432412016-06-27 07:18:18 -0700514 ShapeCache* fShapeCache;
515 ShapeDataList* fShapeList;
brianosman0e3c5542016-04-13 13:56:21 -0700516 bool fGammaCorrect;
reed1b55a962015-09-17 20:16:13 -0700517
518 typedef GrVertexBatch INHERITED;
joshualitt5bf99f12015-03-13 11:47:42 -0700519};
520
bsalomon0aff2fa2015-07-31 06:48:27 -0700521bool GrAADistanceFieldPathRenderer::onDrawPath(const DrawPathArgs& args) {
robertphillips976f5f02016-06-03 10:59:20 -0700522 GR_AUDIT_TRAIL_AUTO_FRAME(args.fDrawContext->auditTrail(),
joshualittde83b412016-01-14 09:58:36 -0800523 "GrAADistanceFieldPathRenderer::onDrawPath");
csmartdaltonecbc12b2016-06-08 10:08:43 -0700524 SkASSERT(!args.fDrawContext->isUnifiedMultisampled());
bsalomonee432412016-06-27 07:18:18 -0700525 SkASSERT(args.fShape->style().isSimpleFill());
csmartdaltonecbc12b2016-06-08 10:08:43 -0700526
jvanverthfa38a302014-10-06 05:59:05 -0700527 // we've already bailed on inverse filled paths, so this is safe
bsalomon8acedde2016-06-24 10:42:16 -0700528 SkASSERT(!args.fShape->isEmpty());
bsalomonee432412016-06-27 07:18:18 -0700529 SkASSERT(args.fShape->hasUnstyledKey());
joshualitt5bf99f12015-03-13 11:47:42 -0700530 if (!fAtlas) {
joshualittb356cbc2015-08-05 06:36:39 -0700531 fAtlas = args.fResourceProvider->createAtlas(kAlpha_8_GrPixelConfig,
532 ATLAS_TEXTURE_WIDTH, ATLAS_TEXTURE_HEIGHT,
533 NUM_PLOTS_X, NUM_PLOTS_Y,
534 &GrAADistanceFieldPathRenderer::HandleEviction,
535 (void*)this);
joshualitt21279c72015-05-11 07:21:37 -0700536 if (!fAtlas) {
jvanverthfa38a302014-10-06 05:59:05 -0700537 return false;
538 }
539 }
540
bsalomonee432412016-06-27 07:18:18 -0700541 AADistanceFieldPathBatch::Geometry geometry;
542 geometry.fShape = *args.fShape;
bsalomon8acedde2016-06-24 10:42:16 -0700543 geometry.fColor = args.fColor;
544 geometry.fAntiAlias = args.fAntiAlias;
halcanary9d524f22016-03-29 09:03:52 -0700545
joshualitt53f26aa2015-12-10 07:29:54 -0800546 SkAutoTUnref<GrDrawBatch> batch(AADistanceFieldPathBatch::Create(geometry,
bsalomonabd30f52015-08-13 13:34:48 -0700547 *args.fViewMatrix, fAtlas,
bsalomonee432412016-06-27 07:18:18 -0700548 &fShapeCache, &fShapeList,
brianosman0e3c5542016-04-13 13:56:21 -0700549 args.fGammaCorrect));
robertphillips976f5f02016-06-03 10:59:20 -0700550
csmartdaltonecbc12b2016-06-08 10:08:43 -0700551 GrPipelineBuilder pipelineBuilder(*args.fPaint);
robertphillips976f5f02016-06-03 10:59:20 -0700552 pipelineBuilder.setUserStencil(args.fUserStencilSettings);
553
554 args.fDrawContext->drawBatch(pipelineBuilder, *args.fClip, batch);
joshualitt9491f7f2015-02-11 11:33:38 -0800555
jvanverthfa38a302014-10-06 05:59:05 -0700556 return true;
557}
558
joshualitt21279c72015-05-11 07:21:37 -0700559///////////////////////////////////////////////////////////////////////////////////////////////////
560
561#ifdef GR_TEST_UTILS
562
563struct PathTestStruct {
bsalomonee432412016-06-27 07:18:18 -0700564 typedef GrAADistanceFieldPathRenderer::ShapeCache ShapeCache;
565 typedef GrAADistanceFieldPathRenderer::ShapeData ShapeData;
566 typedef GrAADistanceFieldPathRenderer::ShapeDataList ShapeDataList;
halcanary96fcdcc2015-08-27 07:41:13 -0700567 PathTestStruct() : fContextID(SK_InvalidGenID), fAtlas(nullptr) {}
joshualitt21279c72015-05-11 07:21:37 -0700568 ~PathTestStruct() { this->reset(); }
569
570 void reset() {
bsalomonee432412016-06-27 07:18:18 -0700571 ShapeDataList::Iter iter;
572 iter.init(fShapeList, ShapeDataList::Iter::kHead_IterStart);
573 ShapeData* shapeData;
574 while ((shapeData = iter.get())) {
joshualitt21279c72015-05-11 07:21:37 -0700575 iter.next();
bsalomonee432412016-06-27 07:18:18 -0700576 fShapeList.remove(shapeData);
577 delete shapeData;
joshualitt21279c72015-05-11 07:21:37 -0700578 }
halcanary385fe4d2015-08-26 13:07:48 -0700579 delete fAtlas;
bsalomonee432412016-06-27 07:18:18 -0700580 fShapeCache.reset();
joshualitt21279c72015-05-11 07:21:37 -0700581 }
582
583 static void HandleEviction(GrBatchAtlas::AtlasID id, void* pr) {
584 PathTestStruct* dfpr = (PathTestStruct*)pr;
585 // remove any paths that use this plot
bsalomonee432412016-06-27 07:18:18 -0700586 ShapeDataList::Iter iter;
587 iter.init(dfpr->fShapeList, ShapeDataList::Iter::kHead_IterStart);
588 ShapeData* shapeData;
589 while ((shapeData = iter.get())) {
joshualitt21279c72015-05-11 07:21:37 -0700590 iter.next();
bsalomonee432412016-06-27 07:18:18 -0700591 if (id == shapeData->fID) {
592 dfpr->fShapeCache.remove(shapeData->fKey);
593 dfpr->fShapeList.remove(shapeData);
594 delete shapeData;
joshualitt21279c72015-05-11 07:21:37 -0700595 }
596 }
597 }
598
599 uint32_t fContextID;
600 GrBatchAtlas* fAtlas;
bsalomonee432412016-06-27 07:18:18 -0700601 ShapeCache fShapeCache;
602 ShapeDataList fShapeList;
joshualitt21279c72015-05-11 07:21:37 -0700603};
604
bsalomonabd30f52015-08-13 13:34:48 -0700605DRAW_BATCH_TEST_DEFINE(AADistanceFieldPathBatch) {
joshualitt21279c72015-05-11 07:21:37 -0700606 static PathTestStruct gTestStruct;
607
608 if (context->uniqueID() != gTestStruct.fContextID) {
609 gTestStruct.fContextID = context->uniqueID();
610 gTestStruct.reset();
joshualittb356cbc2015-08-05 06:36:39 -0700611 gTestStruct.fAtlas =
612 context->resourceProvider()->createAtlas(kAlpha_8_GrPixelConfig,
613 ATLAS_TEXTURE_WIDTH, ATLAS_TEXTURE_HEIGHT,
614 NUM_PLOTS_X, NUM_PLOTS_Y,
615 &PathTestStruct::HandleEviction,
616 (void*)&gTestStruct);
joshualitt21279c72015-05-11 07:21:37 -0700617 }
618
619 SkMatrix viewMatrix = GrTest::TestMatrix(random);
620 GrColor color = GrRandomColor(random);
brianosman0e3c5542016-04-13 13:56:21 -0700621 bool gammaCorrect = random->nextBool();
joshualitt21279c72015-05-11 07:21:37 -0700622
bsalomonee432412016-06-27 07:18:18 -0700623 AADistanceFieldPathBatch::Geometry geometry;
624 // This path renderer only allows fill styles.
625 GrShape shape(GrTest::TestPath(random), GrStyle::SimpleFill());
626 geometry.fShape = shape;
joshualitt53f26aa2015-12-10 07:29:54 -0800627 geometry.fColor = color;
joshualitt21279c72015-05-11 07:21:37 -0700628 geometry.fAntiAlias = random->nextBool();
629
joshualitt53f26aa2015-12-10 07:29:54 -0800630 return AADistanceFieldPathBatch::Create(geometry, viewMatrix,
joshualitt21279c72015-05-11 07:21:37 -0700631 gTestStruct.fAtlas,
bsalomonee432412016-06-27 07:18:18 -0700632 &gTestStruct.fShapeCache,
633 &gTestStruct.fShapeList,
brianosman0e3c5542016-04-13 13:56:21 -0700634 gammaCorrect);
joshualitt21279c72015-05-11 07:21:37 -0700635}
636
637#endif