1 /*
2 * Copyright 2012 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 "GrSoftwarePathRenderer.h"
9 #include "GrAuditTrail.h"
10 #include "GrClip.h"
11 #include "GrContextPriv.h"
12 #include "GrDeferredProxyUploader.h"
13 #include "GrGpuResourcePriv.h"
14 #include "GrOpFlushState.h"
15 #include "GrOpList.h"
16 #include "GrProxyProvider.h"
17 #include "GrSWMaskHelper.h"
18 #include "GrShape.h"
19 #include "GrSurfaceContextPriv.h"
20 #include "SkMakeUnique.h"
21 #include "SkSemaphore.h"
22 #include "SkTaskGroup.h"
23 #include "SkTraceEvent.h"
24 #include "ops/GrDrawOp.h"
25 #include "ops/GrFillRectOp.h"
26
27 ////////////////////////////////////////////////////////////////////////////////
28 GrPathRenderer::CanDrawPath
onCanDrawPath(const CanDrawPathArgs & args) const29 GrSoftwarePathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
30 // Pass on any style that applies. The caller will apply the style if a suitable renderer is
31 // not found and try again with the new GrShape.
32 if (!args.fShape->style().applies() && SkToBool(fProxyProvider) &&
33 (args.fAAType == GrAAType::kCoverage || args.fAAType == GrAAType::kNone)) {
34 // This is the fallback renderer for when a path is too complicated for the GPU ones.
35 return CanDrawPath::kAsBackup;
36 }
37 return CanDrawPath::kNo;
38 }
39
40 ////////////////////////////////////////////////////////////////////////////////
get_unclipped_shape_dev_bounds(const GrShape & shape,const SkMatrix & matrix,SkIRect * devBounds)41 static bool get_unclipped_shape_dev_bounds(const GrShape& shape, const SkMatrix& matrix,
42 SkIRect* devBounds) {
43 SkRect shapeBounds = shape.styledBounds();
44 if (shapeBounds.isEmpty()) {
45 return false;
46 }
47 SkRect shapeDevBounds;
48 matrix.mapRect(&shapeDevBounds, shapeBounds);
49 // Even though these are "unclipped" bounds we still clip to the int32_t range.
50 // This is the largest int32_t that is representable exactly as a float. The next 63 larger ints
51 // would round down to this value when cast to a float, but who really cares.
52 // INT32_MIN is exactly representable.
53 static constexpr int32_t kMaxInt = 2147483520;
54 if (!shapeDevBounds.intersect(SkRect::MakeLTRB(INT32_MIN, INT32_MIN, kMaxInt, kMaxInt))) {
55 return false;
56 }
57 // Make sure that the resulting SkIRect can have representable width and height
58 if (SkScalarRoundToInt(shapeDevBounds.width()) > kMaxInt ||
59 SkScalarRoundToInt(shapeDevBounds.height()) > kMaxInt) {
60 return false;
61 }
62 shapeDevBounds.roundOut(devBounds);
63 return true;
64 }
65
66 // Gets the shape bounds, the clip bounds, and the intersection (if any). Returns false if there
67 // is no intersection.
GetShapeAndClipBounds(GrRenderTargetContext * renderTargetContext,const GrClip & clip,const GrShape & shape,const SkMatrix & matrix,SkIRect * unclippedDevShapeBounds,SkIRect * clippedDevShapeBounds,SkIRect * devClipBounds)68 bool GrSoftwarePathRenderer::GetShapeAndClipBounds(GrRenderTargetContext* renderTargetContext,
69 const GrClip& clip,
70 const GrShape& shape,
71 const SkMatrix& matrix,
72 SkIRect* unclippedDevShapeBounds,
73 SkIRect* clippedDevShapeBounds,
74 SkIRect* devClipBounds) {
75 // compute bounds as intersection of rt size, clip, and path
76 clip.getConservativeBounds(renderTargetContext->width(),
77 renderTargetContext->height(),
78 devClipBounds);
79
80 if (!get_unclipped_shape_dev_bounds(shape, matrix, unclippedDevShapeBounds)) {
81 *unclippedDevShapeBounds = SkIRect::EmptyIRect();
82 *clippedDevShapeBounds = SkIRect::EmptyIRect();
83 return false;
84 }
85 if (!clippedDevShapeBounds->intersect(*devClipBounds, *unclippedDevShapeBounds)) {
86 *clippedDevShapeBounds = SkIRect::EmptyIRect();
87 return false;
88 }
89 return true;
90 }
91
92 ////////////////////////////////////////////////////////////////////////////////
93
DrawNonAARect(GrRenderTargetContext * renderTargetContext,GrPaint && paint,const GrUserStencilSettings & userStencilSettings,const GrClip & clip,const SkMatrix & viewMatrix,const SkRect & rect,const SkMatrix & localMatrix)94 void GrSoftwarePathRenderer::DrawNonAARect(GrRenderTargetContext* renderTargetContext,
95 GrPaint&& paint,
96 const GrUserStencilSettings& userStencilSettings,
97 const GrClip& clip,
98 const SkMatrix& viewMatrix,
99 const SkRect& rect,
100 const SkMatrix& localMatrix) {
101 GrContext* context = renderTargetContext->surfPriv().getContext();
102 renderTargetContext->addDrawOp(clip,
103 GrFillRectOp::MakeWithLocalMatrix(
104 context, std::move(paint), GrAAType::kNone, viewMatrix,
105 localMatrix, rect, &userStencilSettings));
106 }
107
DrawAroundInvPath(GrRenderTargetContext * renderTargetContext,GrPaint && paint,const GrUserStencilSettings & userStencilSettings,const GrClip & clip,const SkMatrix & viewMatrix,const SkIRect & devClipBounds,const SkIRect & devPathBounds)108 void GrSoftwarePathRenderer::DrawAroundInvPath(GrRenderTargetContext* renderTargetContext,
109 GrPaint&& paint,
110 const GrUserStencilSettings& userStencilSettings,
111 const GrClip& clip,
112 const SkMatrix& viewMatrix,
113 const SkIRect& devClipBounds,
114 const SkIRect& devPathBounds) {
115 SkMatrix invert;
116 if (!viewMatrix.invert(&invert)) {
117 return;
118 }
119
120 SkRect rect;
121 if (devClipBounds.fTop < devPathBounds.fTop) {
122 rect.iset(devClipBounds.fLeft, devClipBounds.fTop,
123 devClipBounds.fRight, devPathBounds.fTop);
124 DrawNonAARect(renderTargetContext, GrPaint::Clone(paint), userStencilSettings, clip,
125 SkMatrix::I(), rect, invert);
126 }
127 if (devClipBounds.fLeft < devPathBounds.fLeft) {
128 rect.iset(devClipBounds.fLeft, devPathBounds.fTop,
129 devPathBounds.fLeft, devPathBounds.fBottom);
130 DrawNonAARect(renderTargetContext, GrPaint::Clone(paint), userStencilSettings, clip,
131 SkMatrix::I(), rect, invert);
132 }
133 if (devClipBounds.fRight > devPathBounds.fRight) {
134 rect.iset(devPathBounds.fRight, devPathBounds.fTop,
135 devClipBounds.fRight, devPathBounds.fBottom);
136 DrawNonAARect(renderTargetContext, GrPaint::Clone(paint), userStencilSettings, clip,
137 SkMatrix::I(), rect, invert);
138 }
139 if (devClipBounds.fBottom > devPathBounds.fBottom) {
140 rect.iset(devClipBounds.fLeft, devPathBounds.fBottom,
141 devClipBounds.fRight, devClipBounds.fBottom);
142 DrawNonAARect(renderTargetContext, std::move(paint), userStencilSettings, clip,
143 SkMatrix::I(), rect, invert);
144 }
145 }
146
DrawToTargetWithShapeMask(sk_sp<GrTextureProxy> proxy,GrRenderTargetContext * renderTargetContext,GrPaint && paint,const GrUserStencilSettings & userStencilSettings,const GrClip & clip,const SkMatrix & viewMatrix,const SkIPoint & textureOriginInDeviceSpace,const SkIRect & deviceSpaceRectToDraw)147 void GrSoftwarePathRenderer::DrawToTargetWithShapeMask(
148 sk_sp<GrTextureProxy> proxy,
149 GrRenderTargetContext* renderTargetContext,
150 GrPaint&& paint,
151 const GrUserStencilSettings& userStencilSettings,
152 const GrClip& clip,
153 const SkMatrix& viewMatrix,
154 const SkIPoint& textureOriginInDeviceSpace,
155 const SkIRect& deviceSpaceRectToDraw) {
156 SkMatrix invert;
157 if (!viewMatrix.invert(&invert)) {
158 return;
159 }
160
161 SkRect dstRect = SkRect::Make(deviceSpaceRectToDraw);
162
163 // We use device coords to compute the texture coordinates. We take the device coords and apply
164 // a translation so that the top-left of the device bounds maps to 0,0, and then a scaling
165 // matrix to normalized coords.
166 SkMatrix maskMatrix = SkMatrix::MakeTrans(SkIntToScalar(-textureOriginInDeviceSpace.fX),
167 SkIntToScalar(-textureOriginInDeviceSpace.fY));
168 maskMatrix.preConcat(viewMatrix);
169 paint.addCoverageFragmentProcessor(GrSimpleTextureEffect::Make(
170 std::move(proxy), maskMatrix, GrSamplerState::Filter::kNearest));
171 DrawNonAARect(renderTargetContext, std::move(paint), userStencilSettings, clip, SkMatrix::I(),
172 dstRect, invert);
173 }
174
make_deferred_mask_texture_proxy(GrContext * context,SkBackingFit fit,int width,int height)175 static sk_sp<GrTextureProxy> make_deferred_mask_texture_proxy(GrContext* context, SkBackingFit fit,
176 int width, int height) {
177 GrProxyProvider* proxyProvider = context->contextPriv().proxyProvider();
178
179 GrSurfaceDesc desc;
180 desc.fWidth = width;
181 desc.fHeight = height;
182 desc.fConfig = kAlpha_8_GrPixelConfig;
183
184 const GrBackendFormat format =
185 context->contextPriv().caps()->getBackendFormatFromColorType(kAlpha_8_SkColorType);
186
187 // MDB TODO: We're going to fill this proxy with an ASAP upload (which is out of order wrt to
188 // ops), so it can't have any pending IO.
189 return proxyProvider->createProxy(format, desc, kTopLeft_GrSurfaceOrigin, fit, SkBudgeted::kYes,
190 GrInternalSurfaceFlags::kNoPendingIO);
191 }
192
193 namespace {
194
195 /**
196 * Payload class for use with GrTDeferredProxyUploader. The software path renderer only draws
197 * a single path into the mask texture. This stores all of the information needed by the worker
198 * thread's call to drawShape (see below, in onDrawPath).
199 */
200 class SoftwarePathData {
201 public:
SoftwarePathData(const SkIRect & maskBounds,const SkMatrix & viewMatrix,const GrShape & shape,GrAA aa)202 SoftwarePathData(const SkIRect& maskBounds, const SkMatrix& viewMatrix, const GrShape& shape,
203 GrAA aa)
204 : fMaskBounds(maskBounds)
205 , fViewMatrix(viewMatrix)
206 , fShape(shape)
207 , fAA(aa) {}
208
getMaskBounds() const209 const SkIRect& getMaskBounds() const { return fMaskBounds; }
getViewMatrix() const210 const SkMatrix* getViewMatrix() const { return &fViewMatrix; }
getShape() const211 const GrShape& getShape() const { return fShape; }
getAA() const212 GrAA getAA() const { return fAA; }
213
214 private:
215 SkIRect fMaskBounds;
216 SkMatrix fViewMatrix;
217 GrShape fShape;
218 GrAA fAA;
219 };
220
221 // When the SkPathRef genID changes, invalidate a corresponding GrResource described by key.
222 class PathInvalidator : public SkPathRef::GenIDChangeListener {
223 public:
PathInvalidator(const GrUniqueKey & key,uint32_t contextUniqueID)224 PathInvalidator(const GrUniqueKey& key, uint32_t contextUniqueID)
225 : fMsg(key, contextUniqueID) {}
226
227 private:
228 GrUniqueKeyInvalidatedMessage fMsg;
229
onChange()230 void onChange() override {
231 SkMessageBus<GrUniqueKeyInvalidatedMessage>::Post(fMsg);
232 }
233 };
234
235 }
236
237 ////////////////////////////////////////////////////////////////////////////////
238 // return true on success; false on failure
onDrawPath(const DrawPathArgs & args)239 bool GrSoftwarePathRenderer::onDrawPath(const DrawPathArgs& args) {
240 GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(),
241 "GrSoftwarePathRenderer::onDrawPath");
242 if (!fProxyProvider) {
243 return false;
244 }
245
246 SkASSERT(!args.fShape->style().applies());
247 // We really need to know if the shape will be inverse filled or not
248 // If the path is hairline, ignore inverse fill.
249 bool inverseFilled = args.fShape->inverseFilled() &&
250 !IsStrokeHairlineOrEquivalent(args.fShape->style(),
251 *args.fViewMatrix, nullptr);
252
253 SkIRect unclippedDevShapeBounds, clippedDevShapeBounds, devClipBounds;
254 // To prevent overloading the cache with entries during animations we limit the cache of masks
255 // to cases where the matrix preserves axis alignment.
256 bool useCache = fAllowCaching && !inverseFilled && args.fViewMatrix->preservesAxisAlignment() &&
257 args.fShape->hasUnstyledKey() && GrAAType::kCoverage == args.fAAType;
258
259 if (!GetShapeAndClipBounds(args.fRenderTargetContext,
260 *args.fClip, *args.fShape,
261 *args.fViewMatrix, &unclippedDevShapeBounds,
262 &clippedDevShapeBounds,
263 &devClipBounds)) {
264 if (inverseFilled) {
265 DrawAroundInvPath(args.fRenderTargetContext, std::move(args.fPaint),
266 *args.fUserStencilSettings, *args.fClip, *args.fViewMatrix,
267 devClipBounds, unclippedDevShapeBounds);
268 }
269 return true;
270 }
271
272 const SkIRect* boundsForMask = &clippedDevShapeBounds;
273 if (useCache) {
274 // Use the cache only if >50% of the path is visible.
275 int unclippedWidth = unclippedDevShapeBounds.width();
276 int unclippedHeight = unclippedDevShapeBounds.height();
277 int64_t unclippedArea = sk_64_mul(unclippedWidth, unclippedHeight);
278 int64_t clippedArea = sk_64_mul(clippedDevShapeBounds.width(),
279 clippedDevShapeBounds.height());
280 int maxTextureSize = args.fRenderTargetContext->caps()->maxTextureSize();
281 if (unclippedArea > 2 * clippedArea || unclippedWidth > maxTextureSize ||
282 unclippedHeight > maxTextureSize) {
283 useCache = false;
284 } else {
285 boundsForMask = &unclippedDevShapeBounds;
286 }
287 }
288
289 GrUniqueKey maskKey;
290 if (useCache) {
291 // We require the upper left 2x2 of the matrix to match exactly for a cache hit.
292 SkScalar sx = args.fViewMatrix->get(SkMatrix::kMScaleX);
293 SkScalar sy = args.fViewMatrix->get(SkMatrix::kMScaleY);
294 SkScalar kx = args.fViewMatrix->get(SkMatrix::kMSkewX);
295 SkScalar ky = args.fViewMatrix->get(SkMatrix::kMSkewY);
296 static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
297 GrUniqueKey::Builder builder(&maskKey, kDomain, 5 + args.fShape->unstyledKeySize(),
298 "SW Path Mask");
299 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
300 // Fractional translate does not affect caching on Android. This is done for better cache
301 // hit ratio and speed, but it is matching HWUI behavior, which doesn't consider the matrix
302 // at all when caching paths.
303 SkFixed fracX = 0;
304 SkFixed fracY = 0;
305 #else
306 SkScalar tx = args.fViewMatrix->get(SkMatrix::kMTransX);
307 SkScalar ty = args.fViewMatrix->get(SkMatrix::kMTransY);
308 // Allow 8 bits each in x and y of subpixel positioning.
309 SkFixed fracX = SkScalarToFixed(SkScalarFraction(tx)) & 0x0000FF00;
310 SkFixed fracY = SkScalarToFixed(SkScalarFraction(ty)) & 0x0000FF00;
311 #endif
312 builder[0] = SkFloat2Bits(sx);
313 builder[1] = SkFloat2Bits(sy);
314 builder[2] = SkFloat2Bits(kx);
315 builder[3] = SkFloat2Bits(ky);
316 // Distinguish between hairline and filled paths. For hairlines, we also need to include
317 // the cap. (SW grows hairlines by 0.5 pixel with round and square caps). Note that
318 // stroke-and-fill of hairlines is turned into pure fill by SkStrokeRec, so this covers
319 // all cases we might see.
320 uint32_t styleBits = args.fShape->style().isSimpleHairline() ?
321 ((args.fShape->style().strokeRec().getCap() << 1) | 1) : 0;
322 builder[4] = fracX | (fracY >> 8) | (styleBits << 16);
323 args.fShape->writeUnstyledKey(&builder[5]);
324 }
325
326 sk_sp<GrTextureProxy> proxy;
327 if (useCache) {
328 proxy = fProxyProvider->findOrCreateProxyByUniqueKey(maskKey, kTopLeft_GrSurfaceOrigin);
329 }
330 if (!proxy) {
331 SkBackingFit fit = useCache ? SkBackingFit::kExact : SkBackingFit::kApprox;
332 GrAA aa = GrAAType::kCoverage == args.fAAType ? GrAA::kYes : GrAA::kNo;
333
334 SkTaskGroup* taskGroup = args.fContext->contextPriv().getTaskGroup();
335 if (taskGroup) {
336 proxy = make_deferred_mask_texture_proxy(args.fContext, fit,
337 boundsForMask->width(),
338 boundsForMask->height());
339 if (!proxy) {
340 return false;
341 }
342
343 auto uploader = skstd::make_unique<GrTDeferredProxyUploader<SoftwarePathData>>(
344 *boundsForMask, *args.fViewMatrix, *args.fShape, aa);
345 GrTDeferredProxyUploader<SoftwarePathData>* uploaderRaw = uploader.get();
346
347 auto drawAndUploadMask = [uploaderRaw] {
348 TRACE_EVENT0("skia", "Threaded SW Mask Render");
349 GrSWMaskHelper helper(uploaderRaw->getPixels());
350 if (helper.init(uploaderRaw->data().getMaskBounds())) {
351 helper.drawShape(uploaderRaw->data().getShape(),
352 *uploaderRaw->data().getViewMatrix(),
353 SkRegion::kReplace_Op, uploaderRaw->data().getAA(), 0xFF);
354 } else {
355 SkDEBUGFAIL("Unable to allocate SW mask.");
356 }
357 uploaderRaw->signalAndFreeData();
358 };
359 taskGroup->add(std::move(drawAndUploadMask));
360 proxy->texPriv().setDeferredUploader(std::move(uploader));
361 } else {
362 GrSWMaskHelper helper;
363 if (!helper.init(*boundsForMask)) {
364 return false;
365 }
366 helper.drawShape(*args.fShape, *args.fViewMatrix, SkRegion::kReplace_Op, aa, 0xFF);
367 proxy = helper.toTextureProxy(args.fContext, fit);
368 }
369
370 if (!proxy) {
371 return false;
372 }
373 if (useCache) {
374 SkASSERT(proxy->origin() == kTopLeft_GrSurfaceOrigin);
375 fProxyProvider->assignUniqueKeyToProxy(maskKey, proxy.get());
376 args.fShape->addGenIDChangeListener(
377 sk_make_sp<PathInvalidator>(maskKey, args.fContext->contextPriv().contextID()));
378 }
379 }
380 if (inverseFilled) {
381 DrawAroundInvPath(args.fRenderTargetContext, GrPaint::Clone(args.fPaint),
382 *args.fUserStencilSettings, *args.fClip, *args.fViewMatrix, devClipBounds,
383 unclippedDevShapeBounds);
384 }
385 DrawToTargetWithShapeMask(
386 std::move(proxy), args.fRenderTargetContext, std::move(args.fPaint),
387 *args.fUserStencilSettings, *args.fClip, *args.fViewMatrix,
388 SkIPoint{boundsForMask->fLeft, boundsForMask->fTop}, *boundsForMask);
389
390 return true;
391 }
392