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