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 "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
     29 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 ////////////////////////////////////////////////////////////////////////////////
     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.
     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 
     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 
    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 
    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 
    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:
    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 
    209     const SkIRect& getMaskBounds() const { return fMaskBounds; }
    210     const SkMatrix* getViewMatrix() const { return &fViewMatrix; }
    211     const GrShape& getShape() const { return fShape; }
    212     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:
    224     PathInvalidator(const GrUniqueKey& key, uint32_t contextUniqueID)
    225             : fMsg(key, contextUniqueID) {}
    226 
    227 private:
    228     GrUniqueKeyInvalidatedMessage fMsg;
    229 
    230     void onChange() override {
    231         SkMessageBus<GrUniqueKeyInvalidatedMessage>::Post(fMsg);
    232     }
    233 };
    234 
    235 }
    236 
    237 ////////////////////////////////////////////////////////////////////////////////
    238 // return true on success; false on failure
    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