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:
parent
40c37426b3
commit
6f72bbc52b
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user