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