Home | History | Annotate | Download | only in gpu
      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 "GrGpuResourcePriv.h"
     12 #include "GrResourceProvider.h"
     13 #include "GrSWMaskHelper.h"
     14 #include "ops/GrDrawOp.h"
     15 #include "ops/GrRectOpFactory.h"
     16 
     17 ////////////////////////////////////////////////////////////////////////////////
     18 bool GrSoftwarePathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
     19     // Pass on any style that applies. The caller will apply the style if a suitable renderer is
     20     // not found and try again with the new GrShape.
     21     return !args.fShape->style().applies() && SkToBool(fResourceProvider) &&
     22            (args.fAAType == GrAAType::kCoverage || args.fAAType == GrAAType::kNone);
     23 }
     24 
     25 ////////////////////////////////////////////////////////////////////////////////
     26 static bool get_unclipped_shape_dev_bounds(const GrShape& shape, const SkMatrix& matrix,
     27                                            SkIRect* devBounds) {
     28     SkRect shapeBounds = shape.styledBounds();
     29     if (shapeBounds.isEmpty()) {
     30         return false;
     31     }
     32     SkRect shapeDevBounds;
     33     matrix.mapRect(&shapeDevBounds, shapeBounds);
     34     // Even though these are "unclipped" bounds we still clip to the int32_t range.
     35     // This is the largest int32_t that is representable exactly as a float. The next 63 larger ints
     36     // would round down to this value when cast to a float, but who really cares.
     37     // INT32_MIN is exactly representable.
     38     static constexpr int32_t kMaxInt = 2147483520;
     39     if (!shapeDevBounds.intersect(SkRect::MakeLTRB(INT32_MIN, INT32_MIN, kMaxInt, kMaxInt))) {
     40         return false;
     41     }
     42     shapeDevBounds.roundOut(devBounds);
     43     return true;
     44 }
     45 
     46 // Gets the shape bounds, the clip bounds, and the intersection (if any). Returns false if there
     47 // is no intersection.
     48 static bool get_shape_and_clip_bounds(int width, int height,
     49                                       const GrClip& clip,
     50                                       const GrShape& shape,
     51                                       const SkMatrix& matrix,
     52                                       SkIRect* unclippedDevShapeBounds,
     53                                       SkIRect* clippedDevShapeBounds,
     54                                       SkIRect* devClipBounds) {
     55     // compute bounds as intersection of rt size, clip, and path
     56     clip.getConservativeBounds(width, height, devClipBounds);
     57 
     58     if (!get_unclipped_shape_dev_bounds(shape, matrix, unclippedDevShapeBounds)) {
     59         *unclippedDevShapeBounds = SkIRect::EmptyIRect();
     60         *clippedDevShapeBounds = SkIRect::EmptyIRect();
     61         return false;
     62     }
     63     if (!clippedDevShapeBounds->intersect(*devClipBounds, *unclippedDevShapeBounds)) {
     64         *clippedDevShapeBounds = SkIRect::EmptyIRect();
     65         return false;
     66     }
     67     return true;
     68 }
     69 
     70 ////////////////////////////////////////////////////////////////////////////////
     71 
     72 void GrSoftwarePathRenderer::DrawNonAARect(GrRenderTargetContext* renderTargetContext,
     73                                            GrPaint&& paint,
     74                                            const GrUserStencilSettings& userStencilSettings,
     75                                            const GrClip& clip,
     76                                            const SkMatrix& viewMatrix,
     77                                            const SkRect& rect,
     78                                            const SkMatrix& localMatrix) {
     79     renderTargetContext->addDrawOp(clip,
     80                                    GrRectOpFactory::MakeNonAAFillWithLocalMatrix(
     81                                            std::move(paint), viewMatrix, localMatrix, rect,
     82                                            GrAAType::kNone, &userStencilSettings));
     83 }
     84 
     85 void GrSoftwarePathRenderer::DrawAroundInvPath(GrRenderTargetContext* renderTargetContext,
     86                                                GrPaint&& paint,
     87                                                const GrUserStencilSettings& userStencilSettings,
     88                                                const GrClip& clip,
     89                                                const SkMatrix& viewMatrix,
     90                                                const SkIRect& devClipBounds,
     91                                                const SkIRect& devPathBounds) {
     92     SkMatrix invert;
     93     if (!viewMatrix.invert(&invert)) {
     94         return;
     95     }
     96 
     97     SkRect rect;
     98     if (devClipBounds.fTop < devPathBounds.fTop) {
     99         rect.iset(devClipBounds.fLeft, devClipBounds.fTop,
    100                   devClipBounds.fRight, devPathBounds.fTop);
    101         DrawNonAARect(renderTargetContext, GrPaint(paint), userStencilSettings, clip, SkMatrix::I(),
    102                       rect, invert);
    103     }
    104     if (devClipBounds.fLeft < devPathBounds.fLeft) {
    105         rect.iset(devClipBounds.fLeft, devPathBounds.fTop,
    106                   devPathBounds.fLeft, devPathBounds.fBottom);
    107         DrawNonAARect(renderTargetContext, GrPaint(paint), userStencilSettings, clip, SkMatrix::I(),
    108                       rect, invert);
    109     }
    110     if (devClipBounds.fRight > devPathBounds.fRight) {
    111         rect.iset(devPathBounds.fRight, devPathBounds.fTop,
    112                   devClipBounds.fRight, devPathBounds.fBottom);
    113         DrawNonAARect(renderTargetContext, GrPaint(paint), userStencilSettings, clip, SkMatrix::I(),
    114                       rect, invert);
    115     }
    116     if (devClipBounds.fBottom > devPathBounds.fBottom) {
    117         rect.iset(devClipBounds.fLeft, devPathBounds.fBottom,
    118                   devClipBounds.fRight, devClipBounds.fBottom);
    119         DrawNonAARect(renderTargetContext, std::move(paint), userStencilSettings, clip,
    120                       SkMatrix::I(), rect, invert);
    121     }
    122 }
    123 
    124 ////////////////////////////////////////////////////////////////////////////////
    125 // return true on success; false on failure
    126 bool GrSoftwarePathRenderer::onDrawPath(const DrawPathArgs& args) {
    127     GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(),
    128                               "GrSoftwarePathRenderer::onDrawPath");
    129     if (!fResourceProvider) {
    130         return false;
    131     }
    132 
    133     // We really need to know if the shape will be inverse filled or not
    134     bool inverseFilled = false;
    135     SkTLazy<GrShape> tmpShape;
    136     SkASSERT(!args.fShape->style().applies());
    137     // If the path is hairline, ignore inverse fill.
    138     inverseFilled = args.fShape->inverseFilled() &&
    139                     !IsStrokeHairlineOrEquivalent(args.fShape->style(), *args.fViewMatrix, nullptr);
    140 
    141     SkIRect unclippedDevShapeBounds, clippedDevShapeBounds, devClipBounds;
    142     // To prevent overloading the cache with entries during animations we limit the cache of masks
    143     // to cases where the matrix preserves axis alignment.
    144     bool useCache = fAllowCaching && !inverseFilled && args.fViewMatrix->preservesAxisAlignment() &&
    145                     args.fShape->hasUnstyledKey() && GrAAType::kCoverage == args.fAAType;
    146 
    147     if (!get_shape_and_clip_bounds(args.fRenderTargetContext->width(),
    148                                    args.fRenderTargetContext->height(),
    149                                    *args.fClip, *args.fShape,
    150                                    *args.fViewMatrix, &unclippedDevShapeBounds,
    151                                    &clippedDevShapeBounds,
    152                                    &devClipBounds)) {
    153         if (inverseFilled) {
    154             DrawAroundInvPath(args.fRenderTargetContext, std::move(args.fPaint),
    155                               *args.fUserStencilSettings, *args.fClip, *args.fViewMatrix,
    156                               devClipBounds, unclippedDevShapeBounds);
    157         }
    158         return true;
    159     }
    160 
    161     const SkIRect* boundsForMask = &clippedDevShapeBounds;
    162     if (useCache) {
    163         // Use the cache only if >50% of the path is visible.
    164         int unclippedWidth = unclippedDevShapeBounds.width();
    165         int unclippedHeight = unclippedDevShapeBounds.height();
    166         int unclippedArea = unclippedWidth * unclippedHeight;
    167         int clippedArea = clippedDevShapeBounds.width() * clippedDevShapeBounds.height();
    168         int maxTextureSize = args.fRenderTargetContext->caps()->maxTextureSize();
    169         if (unclippedArea > 2 * clippedArea || unclippedWidth > maxTextureSize ||
    170             unclippedHeight > maxTextureSize) {
    171             useCache = false;
    172         } else {
    173             boundsForMask = &unclippedDevShapeBounds;
    174         }
    175     }
    176 
    177     GrUniqueKey maskKey;
    178     struct KeyData {
    179         SkScalar fFractionalTranslateX;
    180         SkScalar fFractionalTranslateY;
    181     };
    182 
    183     if (useCache) {
    184         // We require the upper left 2x2 of the matrix to match exactly for a cache hit.
    185         SkScalar sx = args.fViewMatrix->get(SkMatrix::kMScaleX);
    186         SkScalar sy = args.fViewMatrix->get(SkMatrix::kMScaleY);
    187         SkScalar kx = args.fViewMatrix->get(SkMatrix::kMSkewX);
    188         SkScalar ky = args.fViewMatrix->get(SkMatrix::kMSkewY);
    189         SkScalar tx = args.fViewMatrix->get(SkMatrix::kMTransX);
    190         SkScalar ty = args.fViewMatrix->get(SkMatrix::kMTransY);
    191         // Allow 8 bits each in x and y of subpixel positioning.
    192         SkFixed fracX = SkScalarToFixed(SkScalarFraction(tx)) & 0x0000FF00;
    193         SkFixed fracY = SkScalarToFixed(SkScalarFraction(ty)) & 0x0000FF00;
    194         static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
    195         GrUniqueKey::Builder builder(&maskKey, kDomain, 5 + args.fShape->unstyledKeySize());
    196         builder[0] = SkFloat2Bits(sx);
    197         builder[1] = SkFloat2Bits(sy);
    198         builder[2] = SkFloat2Bits(kx);
    199         builder[3] = SkFloat2Bits(ky);
    200         builder[4] = fracX | (fracY >> 8);
    201         args.fShape->writeUnstyledKey(&builder[5]);
    202         // FIXME: Doesn't the key need to consider whether we're using AA or not? In practice that
    203         // should always be true, though.
    204     }
    205 
    206     sk_sp<GrTextureProxy> proxy;
    207     if (useCache) {
    208         proxy = fResourceProvider->findProxyByUniqueKey(maskKey);
    209     }
    210     if (!proxy) {
    211         SkBackingFit fit = useCache ? SkBackingFit::kExact : SkBackingFit::kApprox;
    212         GrAA aa = GrAAType::kCoverage == args.fAAType ? GrAA::kYes : GrAA::kNo;
    213         proxy = GrSWMaskHelper::DrawShapeMaskToTexture(args.fContext, *args.fShape,
    214                                                        *boundsForMask, aa,
    215                                                        fit, args.fViewMatrix);
    216         if (!proxy) {
    217             return false;
    218         }
    219         if (useCache) {
    220             fResourceProvider->assignUniqueKeyToProxy(maskKey, proxy.get());
    221         }
    222     }
    223     if (inverseFilled) {
    224         DrawAroundInvPath(args.fRenderTargetContext, GrPaint(args.fPaint),
    225                           *args.fUserStencilSettings, *args.fClip, *args.fViewMatrix, devClipBounds,
    226                           unclippedDevShapeBounds);
    227     }
    228     GrSWMaskHelper::DrawToTargetWithShapeMask(
    229             std::move(proxy), args.fRenderTargetContext, std::move(args.fPaint),
    230             *args.fUserStencilSettings, *args.fClip, *args.fViewMatrix,
    231             SkIPoint{boundsForMask->fLeft, boundsForMask->fTop}, *boundsForMask);
    232 
    233     return true;
    234 }
    235