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