123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393 |
- /*
- * Copyright 2012 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
- #include "include/private/SkSemaphore.h"
- #include "src/core/SkMakeUnique.h"
- #include "src/core/SkTaskGroup.h"
- #include "src/core/SkTraceEvent.h"
- #include "src/gpu/GrAuditTrail.h"
- #include "src/gpu/GrCaps.h"
- #include "src/gpu/GrClip.h"
- #include "src/gpu/GrContextPriv.h"
- #include "src/gpu/GrDeferredProxyUploader.h"
- #include "src/gpu/GrGpuResourcePriv.h"
- #include "src/gpu/GrOpFlushState.h"
- #include "src/gpu/GrOpList.h"
- #include "src/gpu/GrProxyProvider.h"
- #include "src/gpu/GrRecordingContextPriv.h"
- #include "src/gpu/GrRenderTargetContextPriv.h"
- #include "src/gpu/GrSWMaskHelper.h"
- #include "src/gpu/GrSoftwarePathRenderer.h"
- #include "src/gpu/GrSurfaceContextPriv.h"
- #include "src/gpu/geometry/GrShape.h"
- #include "src/gpu/ops/GrDrawOp.h"
- ////////////////////////////////////////////////////////////////////////////////
- GrPathRenderer::CanDrawPath
- GrSoftwarePathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
- // Pass on any style that applies. The caller will apply the style if a suitable renderer is
- // not found and try again with the new GrShape.
- if (!args.fShape->style().applies() && SkToBool(fProxyProvider) &&
- (args.fAAType == GrAAType::kCoverage || args.fAAType == GrAAType::kNone)) {
- // This is the fallback renderer for when a path is too complicated for the GPU ones.
- return CanDrawPath::kAsBackup;
- }
- return CanDrawPath::kNo;
- }
- ////////////////////////////////////////////////////////////////////////////////
- static bool get_unclipped_shape_dev_bounds(const GrShape& shape, const SkMatrix& matrix,
- SkIRect* devBounds) {
- SkRect shapeBounds = shape.styledBounds();
- if (shapeBounds.isEmpty()) {
- return false;
- }
- SkRect shapeDevBounds;
- matrix.mapRect(&shapeDevBounds, shapeBounds);
- // Even though these are "unclipped" bounds we still clip to the int32_t range.
- // This is the largest int32_t that is representable exactly as a float. The next 63 larger ints
- // would round down to this value when cast to a float, but who really cares.
- // INT32_MIN is exactly representable.
- static constexpr int32_t kMaxInt = 2147483520;
- if (!shapeDevBounds.intersect(SkRect::MakeLTRB(INT32_MIN, INT32_MIN, kMaxInt, kMaxInt))) {
- return false;
- }
- // Make sure that the resulting SkIRect can have representable width and height
- if (SkScalarRoundToInt(shapeDevBounds.width()) > kMaxInt ||
- SkScalarRoundToInt(shapeDevBounds.height()) > kMaxInt) {
- return false;
- }
- shapeDevBounds.roundOut(devBounds);
- return true;
- }
- // Gets the shape bounds, the clip bounds, and the intersection (if any). Returns false if there
- // is no intersection.
- bool GrSoftwarePathRenderer::GetShapeAndClipBounds(GrRenderTargetContext* renderTargetContext,
- const GrClip& clip,
- const GrShape& shape,
- const SkMatrix& matrix,
- SkIRect* unclippedDevShapeBounds,
- SkIRect* clippedDevShapeBounds,
- SkIRect* devClipBounds) {
- // compute bounds as intersection of rt size, clip, and path
- clip.getConservativeBounds(renderTargetContext->width(),
- renderTargetContext->height(),
- devClipBounds);
- if (!get_unclipped_shape_dev_bounds(shape, matrix, unclippedDevShapeBounds)) {
- *unclippedDevShapeBounds = SkIRect::EmptyIRect();
- *clippedDevShapeBounds = SkIRect::EmptyIRect();
- return false;
- }
- if (!clippedDevShapeBounds->intersect(*devClipBounds, *unclippedDevShapeBounds)) {
- *clippedDevShapeBounds = SkIRect::EmptyIRect();
- return false;
- }
- return true;
- }
- ////////////////////////////////////////////////////////////////////////////////
- void GrSoftwarePathRenderer::DrawNonAARect(GrRenderTargetContext* renderTargetContext,
- GrPaint&& paint,
- const GrUserStencilSettings& userStencilSettings,
- const GrClip& clip,
- const SkMatrix& viewMatrix,
- const SkRect& rect,
- const SkMatrix& localMatrix) {
- renderTargetContext->priv().stencilRect(clip, &userStencilSettings, std::move(paint), GrAA::kNo,
- viewMatrix, rect, &localMatrix);
- }
- void GrSoftwarePathRenderer::DrawAroundInvPath(GrRenderTargetContext* renderTargetContext,
- GrPaint&& paint,
- const GrUserStencilSettings& userStencilSettings,
- const GrClip& clip,
- const SkMatrix& viewMatrix,
- const SkIRect& devClipBounds,
- const SkIRect& devPathBounds) {
- SkMatrix invert;
- if (!viewMatrix.invert(&invert)) {
- return;
- }
- SkRect rect;
- if (devClipBounds.fTop < devPathBounds.fTop) {
- rect.iset(devClipBounds.fLeft, devClipBounds.fTop,
- devClipBounds.fRight, devPathBounds.fTop);
- DrawNonAARect(renderTargetContext, GrPaint::Clone(paint), userStencilSettings, clip,
- SkMatrix::I(), rect, invert);
- }
- if (devClipBounds.fLeft < devPathBounds.fLeft) {
- rect.iset(devClipBounds.fLeft, devPathBounds.fTop,
- devPathBounds.fLeft, devPathBounds.fBottom);
- DrawNonAARect(renderTargetContext, GrPaint::Clone(paint), userStencilSettings, clip,
- SkMatrix::I(), rect, invert);
- }
- if (devClipBounds.fRight > devPathBounds.fRight) {
- rect.iset(devPathBounds.fRight, devPathBounds.fTop,
- devClipBounds.fRight, devPathBounds.fBottom);
- DrawNonAARect(renderTargetContext, GrPaint::Clone(paint), userStencilSettings, clip,
- SkMatrix::I(), rect, invert);
- }
- if (devClipBounds.fBottom > devPathBounds.fBottom) {
- rect.iset(devClipBounds.fLeft, devPathBounds.fBottom,
- devClipBounds.fRight, devClipBounds.fBottom);
- DrawNonAARect(renderTargetContext, std::move(paint), userStencilSettings, clip,
- SkMatrix::I(), rect, invert);
- }
- }
- void GrSoftwarePathRenderer::DrawToTargetWithShapeMask(
- sk_sp<GrTextureProxy> proxy,
- GrRenderTargetContext* renderTargetContext,
- GrPaint&& paint,
- const GrUserStencilSettings& userStencilSettings,
- const GrClip& clip,
- const SkMatrix& viewMatrix,
- const SkIPoint& textureOriginInDeviceSpace,
- const SkIRect& deviceSpaceRectToDraw) {
- SkMatrix invert;
- if (!viewMatrix.invert(&invert)) {
- return;
- }
- SkRect dstRect = SkRect::Make(deviceSpaceRectToDraw);
- // We use device coords to compute the texture coordinates. We take the device coords and apply
- // a translation so that the top-left of the device bounds maps to 0,0, and then a scaling
- // matrix to normalized coords.
- SkMatrix maskMatrix = SkMatrix::MakeTrans(SkIntToScalar(-textureOriginInDeviceSpace.fX),
- SkIntToScalar(-textureOriginInDeviceSpace.fY));
- maskMatrix.preConcat(viewMatrix);
- paint.addCoverageFragmentProcessor(GrSimpleTextureEffect::Make(
- std::move(proxy), maskMatrix, GrSamplerState::Filter::kNearest));
- DrawNonAARect(renderTargetContext, std::move(paint), userStencilSettings, clip, SkMatrix::I(),
- dstRect, invert);
- }
- static sk_sp<GrTextureProxy> make_deferred_mask_texture_proxy(GrRecordingContext* context,
- SkBackingFit fit,
- int width, int height) {
- GrProxyProvider* proxyProvider = context->priv().proxyProvider();
- GrSurfaceDesc desc;
- desc.fWidth = width;
- desc.fHeight = height;
- desc.fConfig = kAlpha_8_GrPixelConfig;
- const GrBackendFormat format =
- context->priv().caps()->getBackendFormatFromColorType(GrColorType::kAlpha_8);
- return proxyProvider->createProxy(format, desc, GrRenderable::kNo, 1, kTopLeft_GrSurfaceOrigin,
- fit, SkBudgeted::kYes, GrProtected::kNo);
- }
- namespace {
- /**
- * Payload class for use with GrTDeferredProxyUploader. The software path renderer only draws
- * a single path into the mask texture. This stores all of the information needed by the worker
- * thread's call to drawShape (see below, in onDrawPath).
- */
- class SoftwarePathData {
- public:
- SoftwarePathData(const SkIRect& maskBounds, const SkMatrix& viewMatrix, const GrShape& shape,
- GrAA aa)
- : fMaskBounds(maskBounds)
- , fViewMatrix(viewMatrix)
- , fShape(shape)
- , fAA(aa) {}
- const SkIRect& getMaskBounds() const { return fMaskBounds; }
- const SkMatrix* getViewMatrix() const { return &fViewMatrix; }
- const GrShape& getShape() const { return fShape; }
- GrAA getAA() const { return fAA; }
- private:
- SkIRect fMaskBounds;
- SkMatrix fViewMatrix;
- GrShape fShape;
- GrAA fAA;
- };
- // When the SkPathRef genID changes, invalidate a corresponding GrResource described by key.
- class PathInvalidator : public SkPathRef::GenIDChangeListener {
- public:
- PathInvalidator(const GrUniqueKey& key, uint32_t contextUniqueID)
- : fMsg(key, contextUniqueID) {}
- private:
- GrUniqueKeyInvalidatedMessage fMsg;
- void onChange() override {
- SkMessageBus<GrUniqueKeyInvalidatedMessage>::Post(fMsg);
- }
- };
- }
- ////////////////////////////////////////////////////////////////////////////////
- // return true on success; false on failure
- bool GrSoftwarePathRenderer::onDrawPath(const DrawPathArgs& args) {
- GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(),
- "GrSoftwarePathRenderer::onDrawPath");
- if (!fProxyProvider) {
- return false;
- }
- SkASSERT(!args.fShape->style().applies());
- // We really need to know if the shape will be inverse filled or not
- // If the path is hairline, ignore inverse fill.
- bool inverseFilled = args.fShape->inverseFilled() &&
- !IsStrokeHairlineOrEquivalent(args.fShape->style(),
- *args.fViewMatrix, nullptr);
- SkIRect unclippedDevShapeBounds, clippedDevShapeBounds, devClipBounds;
- // To prevent overloading the cache with entries during animations we limit the cache of masks
- // to cases where the matrix preserves axis alignment.
- bool useCache = fAllowCaching && !inverseFilled && args.fViewMatrix->preservesAxisAlignment() &&
- args.fShape->hasUnstyledKey() && (GrAAType::kCoverage == args.fAAType);
- if (!GetShapeAndClipBounds(args.fRenderTargetContext,
- *args.fClip, *args.fShape,
- *args.fViewMatrix, &unclippedDevShapeBounds,
- &clippedDevShapeBounds,
- &devClipBounds)) {
- if (inverseFilled) {
- DrawAroundInvPath(args.fRenderTargetContext, std::move(args.fPaint),
- *args.fUserStencilSettings, *args.fClip, *args.fViewMatrix,
- devClipBounds, unclippedDevShapeBounds);
- }
- return true;
- }
- const SkIRect* boundsForMask = &clippedDevShapeBounds;
- if (useCache) {
- // Use the cache only if >50% of the path is visible.
- int unclippedWidth = unclippedDevShapeBounds.width();
- int unclippedHeight = unclippedDevShapeBounds.height();
- int64_t unclippedArea = sk_64_mul(unclippedWidth, unclippedHeight);
- int64_t clippedArea = sk_64_mul(clippedDevShapeBounds.width(),
- clippedDevShapeBounds.height());
- int maxTextureSize = args.fRenderTargetContext->caps()->maxTextureSize();
- if (unclippedArea > 2 * clippedArea || unclippedWidth > maxTextureSize ||
- unclippedHeight > maxTextureSize) {
- useCache = false;
- } else {
- boundsForMask = &unclippedDevShapeBounds;
- }
- }
- GrUniqueKey maskKey;
- if (useCache) {
- // We require the upper left 2x2 of the matrix to match exactly for a cache hit.
- SkScalar sx = args.fViewMatrix->get(SkMatrix::kMScaleX);
- SkScalar sy = args.fViewMatrix->get(SkMatrix::kMScaleY);
- SkScalar kx = args.fViewMatrix->get(SkMatrix::kMSkewX);
- SkScalar ky = args.fViewMatrix->get(SkMatrix::kMSkewY);
- static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
- GrUniqueKey::Builder builder(&maskKey, kDomain, 5 + args.fShape->unstyledKeySize(),
- "SW Path Mask");
- #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
- // Fractional translate does not affect caching on Android. This is done for better cache
- // hit ratio and speed, but it is matching HWUI behavior, which doesn't consider the matrix
- // at all when caching paths.
- SkFixed fracX = 0;
- SkFixed fracY = 0;
- #else
- SkScalar tx = args.fViewMatrix->get(SkMatrix::kMTransX);
- SkScalar ty = args.fViewMatrix->get(SkMatrix::kMTransY);
- // Allow 8 bits each in x and y of subpixel positioning.
- SkFixed fracX = SkScalarToFixed(SkScalarFraction(tx)) & 0x0000FF00;
- SkFixed fracY = SkScalarToFixed(SkScalarFraction(ty)) & 0x0000FF00;
- #endif
- builder[0] = SkFloat2Bits(sx);
- builder[1] = SkFloat2Bits(sy);
- builder[2] = SkFloat2Bits(kx);
- builder[3] = SkFloat2Bits(ky);
- // Distinguish between hairline and filled paths. For hairlines, we also need to include
- // the cap. (SW grows hairlines by 0.5 pixel with round and square caps). Note that
- // stroke-and-fill of hairlines is turned into pure fill by SkStrokeRec, so this covers
- // all cases we might see.
- uint32_t styleBits = args.fShape->style().isSimpleHairline() ?
- ((args.fShape->style().strokeRec().getCap() << 1) | 1) : 0;
- builder[4] = fracX | (fracY >> 8) | (styleBits << 16);
- args.fShape->writeUnstyledKey(&builder[5]);
- }
- sk_sp<GrTextureProxy> proxy;
- if (useCache) {
- proxy = fProxyProvider->findOrCreateProxyByUniqueKey(maskKey, kTopLeft_GrSurfaceOrigin);
- }
- if (!proxy) {
- SkBackingFit fit = useCache ? SkBackingFit::kExact : SkBackingFit::kApprox;
- GrAA aa = GrAA(GrAAType::kCoverage == args.fAAType);
- SkTaskGroup* taskGroup = nullptr;
- if (auto direct = args.fContext->priv().asDirectContext()) {
- taskGroup = direct->priv().getTaskGroup();
- }
- if (taskGroup) {
- proxy = make_deferred_mask_texture_proxy(args.fContext, fit,
- boundsForMask->width(),
- boundsForMask->height());
- if (!proxy) {
- return false;
- }
- auto uploader = skstd::make_unique<GrTDeferredProxyUploader<SoftwarePathData>>(
- *boundsForMask, *args.fViewMatrix, *args.fShape, aa);
- GrTDeferredProxyUploader<SoftwarePathData>* uploaderRaw = uploader.get();
- auto drawAndUploadMask = [uploaderRaw] {
- TRACE_EVENT0("skia.gpu", "Threaded SW Mask Render");
- GrSWMaskHelper helper(uploaderRaw->getPixels());
- if (helper.init(uploaderRaw->data().getMaskBounds())) {
- helper.drawShape(uploaderRaw->data().getShape(),
- *uploaderRaw->data().getViewMatrix(),
- SkRegion::kReplace_Op, uploaderRaw->data().getAA(), 0xFF);
- } else {
- SkDEBUGFAIL("Unable to allocate SW mask.");
- }
- uploaderRaw->signalAndFreeData();
- };
- taskGroup->add(std::move(drawAndUploadMask));
- proxy->texPriv().setDeferredUploader(std::move(uploader));
- } else {
- GrSWMaskHelper helper;
- if (!helper.init(*boundsForMask)) {
- return false;
- }
- helper.drawShape(*args.fShape, *args.fViewMatrix, SkRegion::kReplace_Op, aa, 0xFF);
- proxy = helper.toTextureProxy(args.fContext, fit);
- }
- if (!proxy) {
- return false;
- }
- if (useCache) {
- SkASSERT(proxy->origin() == kTopLeft_GrSurfaceOrigin);
- fProxyProvider->assignUniqueKeyToProxy(maskKey, proxy.get());
- args.fShape->addGenIDChangeListener(
- sk_make_sp<PathInvalidator>(maskKey, args.fContext->priv().contextID()));
- }
- }
- if (inverseFilled) {
- DrawAroundInvPath(args.fRenderTargetContext, GrPaint::Clone(args.fPaint),
- *args.fUserStencilSettings, *args.fClip, *args.fViewMatrix, devClipBounds,
- unclippedDevShapeBounds);
- }
- DrawToTargetWithShapeMask(
- std::move(proxy), args.fRenderTargetContext, std::move(args.fPaint),
- *args.fUserStencilSettings, *args.fClip, *args.fViewMatrix,
- SkIPoint{boundsForMask->fLeft, boundsForMask->fTop}, *boundsForMask);
- return true;
- }
|