Revert "Add GPU-side caching of mask-filtered path masks"

This reverts commit 0b4a2538dd.

Reason for revert: breaking lots of bots

Original change's description:
> Add GPU-side caching of mask-filtered path masks
> 
> This takes about half the time on the instagram skp
> 
> Change-Id: If3085633665638849f3445d2eae5d2f187853cf4
> Reviewed-on: https://skia-review.googlesource.com/146767
> Reviewed-by: Brian Salomon <bsalomon@google.com>
> Commit-Queue: Robert Phillips <robertphillips@google.com>

TBR=bsalomon@google.com,robertphillips@google.com

Change-Id: Ia4abc94f5283f5e5e41f71af9ffda2f370f47c0c
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Reviewed-on: https://skia-review.googlesource.com/148920
Reviewed-by: Greg Daniel <egdaniel@google.com>
Commit-Queue: Greg Daniel <egdaniel@google.com>
This commit is contained in:
Greg Daniel 2018-08-23 13:17:09 +00:00 committed by Skia Commit-Bot
parent 40c37426b3
commit 6f72bbc52b
4 changed files with 88 additions and 193 deletions

View File

@ -51,10 +51,10 @@ public:
#if SK_SUPPORT_GPU
bool canFilterMaskGPU(const GrShape& shape,
const SkIRect& devSpaceShapeBounds,
const SkRect& devSpaceShapeBounds,
const SkIRect& clipBounds,
const SkMatrix& ctm,
SkIRect* maskRect) const override;
SkRect* maskRect) const override;
bool directFilterMaskGPU(GrContext*,
GrRenderTargetContext* renderTargetContext,
GrPaint&&,
@ -891,29 +891,15 @@ bool SkBlurMaskFilterImpl::directFilterRRectMaskGPU(GrContext* context,
}
bool SkBlurMaskFilterImpl::canFilterMaskGPU(const GrShape& shape,
const SkIRect& devSpaceShapeBounds,
const SkRect& devSpaceShapeBounds,
const SkIRect& clipBounds,
const SkMatrix& ctm,
SkIRect* maskRect) const {
SkRect* maskRect) const {
SkScalar xformedSigma = this->computeXformedSigma(ctm);
if (xformedSigma <= 0) {
maskRect->setEmpty();
return false;
}
if (maskRect) {
float sigma3 = 3 * SkScalarToFloat(xformedSigma);
// Outset srcRect and clipRect by 3 * sigma, to compute affected blur area.
SkIRect clipRect = clipBounds.makeOutset(sigma3, sigma3);
SkIRect srcRect = devSpaceShapeBounds.makeOutset(sigma3, sigma3);
if (!srcRect.intersect(clipRect)) {
srcRect.setEmpty();
}
*maskRect = srcRect;
}
// We prefer to blur paths with small blur radii on the CPU.
if (ctm.rectStaysRect()) {
static const SkScalar kMIN_GPU_BLUR_SIZE = SkIntToScalar(64);
@ -926,6 +912,23 @@ bool SkBlurMaskFilterImpl::canFilterMaskGPU(const GrShape& shape,
}
}
if (nullptr == maskRect) {
// don't need to compute maskRect
return true;
}
float sigma3 = 3 * SkScalarToFloat(xformedSigma);
SkRect clipRect = SkRect::Make(clipBounds);
SkRect srcRect = devSpaceShapeBounds;
// Outset srcRect and clipRect by 3 * sigma, to compute affected blur area.
srcRect.outset(sigma3, sigma3);
clipRect.outset(sigma3, sigma3);
if (!srcRect.intersect(clipRect)) {
srcRect.setEmpty();
}
*maskRect = srcRect;
return true;
}

View File

@ -324,10 +324,10 @@ SkMaskFilterBase::onAsFragmentProcessor(const GrFPArgs&) const {
bool SkMaskFilterBase::onHasFragmentProcessor() const { return false; }
bool SkMaskFilterBase::canFilterMaskGPU(const GrShape& shape,
const SkIRect& devSpaceShapeBounds,
const SkRect& devSpaceShapeBounds,
const SkIRect& clipBounds,
const SkMatrix& ctm,
SkIRect* maskRect) const {
SkRect* maskRect) const {
return false;
}

View File

@ -99,10 +99,10 @@ public:
* this would hide the RRect special case and the mask generation
*/
virtual bool canFilterMaskGPU(const GrShape&,
const SkIRect& devSpaceShapeBounds,
const SkRect& devSpaceShapeBounds,
const SkIRect& clipBounds,
const SkMatrix& ctm,
SkIRect* maskRect) const;
SkRect* maskRect) const;
/**
* Try to directly render the mask filter into the target. Returns true if drawing was

View File

@ -65,8 +65,7 @@ static bool sw_draw_with_mask_filter(GrContext* context,
const GrShape& shape,
const SkMaskFilter* filter,
const SkIRect& clipBounds,
GrPaint&& paint,
const GrUniqueKey& key) {
GrPaint&& paint) {
SkASSERT(filter);
SkASSERT(!shape.style().applies());
@ -78,91 +77,57 @@ static bool sw_draw_with_mask_filter(GrContext* context,
? SkStrokeRec::kHairline_InitStyle
: SkStrokeRec::kFill_InitStyle;
if (key.isValid()) {
// TODO: this cache look up is duplicated in draw_shape_with_mask_filter for gpu
maskProxy = proxyProvider->findProxyByUniqueKey(key, kTopLeft_GrSurfaceOrigin);
// TODO: it seems like we could create an SkDraw here and set its fMatrix field rather
// than explicitly transforming the path to device space.
SkPath devPath;
shape.asPath(&devPath);
devPath.transform(viewMatrix);
SkMask srcM, dstM;
if (!SkDraw::DrawToMask(devPath, &clipBounds, filter, &viewMatrix, &srcM,
SkMask::kComputeBoundsAndRenderImage_CreateMode, fillOrHairline)) {
return false;
}
SkAutoMaskFreeImage autoSrc(srcM.fImage);
SkASSERT(SkMask::kA8_Format == srcM.fFormat);
if (!as_MFB(filter)->filterMask(&dstM, srcM, viewMatrix, nullptr)) {
return false;
}
// this will free-up dstM when we're done (allocated in filterMask())
SkAutoMaskFreeImage autoDst(dstM.fImage);
if (clip_bounds_quick_reject(clipBounds, dstM.fBounds)) {
return false;
}
SkIRect drawRect;
if (maskProxy) {
SkRect devBounds = shape.bounds();
viewMatrix.mapRect(&devBounds);
// we now have a device-aligned 8bit mask in dstM, ready to be drawn using
// the current clip (and identity matrix) and GrPaint settings
SkBitmap bm;
if (!bm.installPixels(SkImageInfo::MakeA8(dstM.fBounds.width(), dstM.fBounds.height()),
autoDst.release(), dstM.fRowBytes, mask_release_proc, nullptr)) {
return false;
}
bm.setImmutable();
// Here we need to recompute the destination bounds in order to draw the mask correctly
SkMask srcM, dstM;
if (!SkDraw::ComputeMaskBounds(devBounds, &clipBounds, filter, &viewMatrix,
&srcM.fBounds)) {
return false;
}
srcM.fFormat = SkMask::kA8_Format;
if (!as_MFB(filter)->filterMask(&dstM, srcM, viewMatrix, nullptr)) {
return false;
}
SkASSERT(dstM.fBounds.width() == maskProxy->width());
SkASSERT(dstM.fBounds.height() == maskProxy->height());
drawRect = dstM.fBounds;
} else {
// TODO: it seems like we could create an SkDraw here and set its fMatrix field rather
// than explicitly transforming the path to device space.
SkPath devPath;
shape.asPath(&devPath);
devPath.transform(viewMatrix);
SkMask srcM, dstM;
if (!SkDraw::DrawToMask(devPath, &clipBounds, filter, &viewMatrix, &srcM,
SkMask::kComputeBoundsAndRenderImage_CreateMode, fillOrHairline)) {
return false;
}
SkAutoMaskFreeImage autoSrc(srcM.fImage);
SkASSERT(SkMask::kA8_Format == srcM.fFormat);
if (!as_MFB(filter)->filterMask(&dstM, srcM, viewMatrix, nullptr)) {
return false;
}
// this will free-up dstM when we're done (allocated in filterMask())
SkAutoMaskFreeImage autoDst(dstM.fImage);
if (clip_bounds_quick_reject(clipBounds, dstM.fBounds)) {
return false;
}
// we now have a device-aligned 8bit mask in dstM, ready to be drawn using
// the current clip (and identity matrix) and GrPaint settings
SkBitmap bm;
if (!bm.installPixels(SkImageInfo::MakeA8(dstM.fBounds.width(), dstM.fBounds.height()),
autoDst.release(), dstM.fRowBytes, mask_release_proc, nullptr)) {
return false;
}
bm.setImmutable();
sk_sp<SkImage> image = SkImage::MakeFromBitmap(bm);
if (!image) {
return false;
}
maskProxy = proxyProvider->createTextureProxy(std::move(image),
kNone_GrSurfaceFlags,
1, SkBudgeted::kYes,
SkBackingFit::kApprox);
if (!maskProxy) {
return false;
}
drawRect = dstM.fBounds;
if (key.isValid()) {
proxyProvider->assignUniqueKeyToProxy(key, maskProxy.get());
}
sk_sp<SkImage> image = SkImage::MakeFromBitmap(bm);
if (!image) {
return false;
}
return draw_mask(renderTargetContext, clipData, viewMatrix, drawRect,
std::move(paint), std::move(maskProxy));
maskProxy = proxyProvider->createTextureProxy(std::move(image),
kNone_GrSurfaceFlags,
1, SkBudgeted::kYes,
SkBackingFit::kApprox);
if (!maskProxy) {
return false;
}
return draw_mask(renderTargetContext, clipData, viewMatrix,
dstM.fBounds, std::move(paint), std::move(maskProxy));
}
// Create a mask of 'shape' and place the result in 'mask'.
@ -292,7 +257,9 @@ static void draw_shape_with_mask_filter(GrContext* context,
viewMatrix, nullptr);
SkIRect unclippedDevShapeBounds, devClipBounds;
if (!get_shape_and_clip_bounds(renderTargetContext, clip, *shape, viewMatrix,
if (!get_shape_and_clip_bounds(renderTargetContext,
clip, *shape,
viewMatrix,
&unclippedDevShapeBounds,
&devClipBounds)) {
// TODO: just cons up an opaque mask here
@ -301,100 +268,28 @@ static void draw_shape_with_mask_filter(GrContext* context,
}
}
// 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 = !inverseFilled && viewMatrix.preservesAxisAlignment() &&
shape->hasUnstyledKey() && as_MFB(maskFilter)->asABlur(nullptr);
const SkIRect* boundsForClip = &devClipBounds;
if (useCache) {
SkIRect clippedMaskRect, unClippedMaskRect;
maskFilter->canFilterMaskGPU(*shape, unclippedDevShapeBounds, devClipBounds,
viewMatrix, &clippedMaskRect);
maskFilter->canFilterMaskGPU(*shape, unclippedDevShapeBounds, unclippedDevShapeBounds,
viewMatrix, &unClippedMaskRect);
if (clippedMaskRect.isEmpty()) {
return;
}
// Use the cache only if >50% of the filtered mask is visible.
int unclippedWidth = unClippedMaskRect.width();
int unclippedHeight = unClippedMaskRect.height();
int64_t unclippedArea = sk_64_mul(unclippedWidth, unclippedHeight);
int64_t clippedArea = sk_64_mul(clippedMaskRect.width(), clippedMaskRect.height());
int maxTextureSize = renderTargetContext->caps()->maxTextureSize();
if (unclippedArea > 2 * clippedArea || unclippedWidth > maxTextureSize ||
unclippedHeight > maxTextureSize) {
useCache = false;
} else {
// Make the clip not affect the mask
boundsForClip = &unclippedDevShapeBounds;
}
}
GrUniqueKey maskKey;
if (useCache) {
static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
GrUniqueKey::Builder builder(&maskKey, kDomain, 5 + 2 + shape->unstyledKeySize(),
"Mask Filtered Masks");
// We require the upper left 2x2 of the matrix to match exactly for a cache hit.
SkScalar sx = viewMatrix.get(SkMatrix::kMScaleX);
SkScalar sy = viewMatrix.get(SkMatrix::kMScaleY);
SkScalar kx = viewMatrix.get(SkMatrix::kMSkewX);
SkScalar ky = viewMatrix.get(SkMatrix::kMSkewY);
SkScalar tx = viewMatrix.get(SkMatrix::kMTransX);
SkScalar ty = viewMatrix.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;
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 = shape->style().isSimpleHairline()
? ((shape->style().strokeRec().getCap() << 1) | 1)
: 0;
builder[4] = fracX | (fracY >> 8) | (styleBits << 16);
SkMaskFilterBase::BlurRec rec;
SkAssertResult(as_MFB(maskFilter)->asABlur(&rec));
builder[5] = rec.fStyle; // TODO: we could put this with the other style bits
builder[6] = rec.fSigma;
shape->writeUnstyledKey(&builder[7]);
}
SkIRect maskRect;
SkRect maskRect;
if (maskFilter->canFilterMaskGPU(*shape,
unclippedDevShapeBounds,
*boundsForClip,
SkRect::Make(unclippedDevShapeBounds),
devClipBounds,
viewMatrix,
&maskRect)) {
if (clip_bounds_quick_reject(*boundsForClip, maskRect)) {
// This mask will ultimately be drawn as a non-AA rect (see draw_mask).
// Non-AA rects have a bad habit of snapping arbitrarily. Integerize here
// so the mask draws in a reproducible manner.
SkIRect finalIRect;
maskRect.roundOut(&finalIRect);
if (clip_bounds_quick_reject(devClipBounds, finalIRect)) {
// clipped out
return;
}
sk_sp<GrTextureProxy> filteredMask;
GrProxyProvider* proxyProvider = context->contextPriv().proxyProvider();
if (maskKey.isValid()) {
// TODO: this cache look up is duplicated in sw_draw_with_mask_filter for raster
filteredMask = proxyProvider->findOrCreateProxyByUniqueKey(
maskKey, renderTargetContext->origin());
}
if (!filteredMask) {
sk_sp<GrTextureProxy> maskProxy(create_mask_GPU(
context,
maskRect,
finalIRect,
viewMatrix,
*shape,
aa,
@ -403,16 +298,13 @@ static void draw_shape_with_mask_filter(GrContext* context,
filteredMask = maskFilter->filterMaskGPU(context,
std::move(maskProxy),
viewMatrix,
maskRect);
if (filteredMask && maskKey.isValid()) {
proxyProvider->assignUniqueKeyToProxy(maskKey, filteredMask.get());
}
finalIRect);
}
}
if (filteredMask) {
if (draw_mask(renderTargetContext, clip, viewMatrix,
maskRect, std::move(paint), std::move(filteredMask))) {
finalIRect, std::move(paint), std::move(filteredMask))) {
// This path is completely drawn
return;
}
@ -420,7 +312,7 @@ static void draw_shape_with_mask_filter(GrContext* context,
}
sw_draw_with_mask_filter(context, renderTargetContext, clip, viewMatrix, *shape,
maskFilter, *boundsForClip, std::move(paint), maskKey);
maskFilter, devClipBounds, std::move(paint));
}
void GrBlurUtils::drawShapeWithMaskFilter(GrContext* context,