Optimizations for analytic shadows.

Lots of changes here:
* Batch circle shadows with rrect shadows
* Avoid checking matrix and path conditions twice
* Remove lots of checks for 1/2 pixel radii
  (needed before to force the rrect through the
   regular GPU path)
* Fix scaling effect on ambient blur width
* Remove unused flags

Bug: skia:6119
Change-Id: If0eb78ec4d19d9f978b19bdbc3a7e558a4db2ed9
Reviewed-on: https://skia-review.googlesource.com/14654
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Jim Van Verth <jvanverth@google.com>
This commit is contained in:
Jim Van Verth 2017-04-28 17:30:30 -04:00 committed by Skia Commit-Bot
parent 0c5cf5d7a2
commit 57061eea44
6 changed files with 379 additions and 633 deletions

View File

@ -14,12 +14,8 @@ enum SkShadowFlags {
/** The occluding object is not opaque. Knowing that the occluder is opaque allows
* us to cull shadow geometry behind it and improve performance. */
kTransparentOccluder_ShadowFlag = 0x01,
/** Use a larger umbra for a darker shadow */
kLargerUmbra_ShadowFlag = 0x02,
/** Use a Gaussian for the edge function rather than smoothstep */
kGaussianEdge_ShadowFlag = 0x04,
/** mask for all shadow flags */
kAll_ShadowFlag = 0x07
kAll_ShadowFlag = 0x01
};
#endif

View File

@ -187,7 +187,6 @@ bool SkAmbientShadowMaskFilterImpl::directFilterRRectMaskGPU(GrContext*,
const SkRRect& rrect,
const SkRRect& devRRect) const {
// It's likely the caller has already done these checks, but we have to be sure.
// TODO: support analytic blurring of general rrect
// Fast path only supports filled rrects for now.
// TODO: fill and stroke as well.
@ -195,52 +194,43 @@ bool SkAmbientShadowMaskFilterImpl::directFilterRRectMaskGPU(GrContext*,
return false;
}
// Fast path only supports simple rrects with circular corners.
SkASSERT(devRRect.allCornersCircular());
if (!rrect.isRect() && !rrect.isOval() && !rrect.isSimple()) {
if (!devRRect.isRect() && !devRRect.isCircle() &&
(!devRRect.isSimple() || !devRRect.allCornersCircular())) {
return false;
}
// Fast path only supports uniform scale.
SkScalar scaleFactors[2];
if (!viewMatrix.getMinMaxScales(scaleFactors)) {
// matrix is degenerate
if (!viewMatrix.isSimilarity()) {
return false;
}
if (scaleFactors[0] != scaleFactors[1]) {
return false;
}
SkScalar scaleFactor = scaleFactors[0];
// For all of these, we need to ensure we have a rrect with radius >= 0.5f in device space
const SkScalar minRadius = 0.5f / scaleFactor;
bool isRect = rrect.getSimpleRadii().fX <= minRadius;
// TODO: take flags into account when generating shadow data
// 1/scale
SkScalar scaleFactor = viewMatrix.isScaleTranslate() ?
SkScalarInvert(viewMatrix[SkMatrix::kMScaleX]) :
sk_float_rsqrt(viewMatrix[SkMatrix::kMScaleX] * viewMatrix[SkMatrix::kMScaleX] +
viewMatrix[SkMatrix::kMSkewX] * viewMatrix[SkMatrix::kMSkewX]);
if (fAmbientAlpha > 0.0f) {
SkScalar srcSpaceStrokeWidth = fOccluderHeight * kHeightFactor * kGeomFactor;
SkScalar devSpaceStrokeWidth = fOccluderHeight * kHeightFactor * kGeomFactor;
const float umbraAlpha = (1.0f + SkTMax(fOccluderHeight * kHeightFactor, 0.0f));
const SkScalar blurWidth = srcSpaceStrokeWidth * umbraAlpha;
const SkScalar devSpaceAmbientBlur = devSpaceStrokeWidth * umbraAlpha;
// For the ambient rrect, we outset the offset rect by srcSpaceAmbientRadius
// minus half the strokeWidth to get our stroke shape.
SkScalar ambientPathOutset = SkTMax(srcSpaceStrokeWidth * 0.5f,
minRadius);
// For the ambient rrect, we outset the offset rect
// by half the strokeWidth to get our stroke shape.
SkScalar srcSpaceStrokeWidth = devSpaceStrokeWidth * scaleFactor;
SkScalar ambientPathOutset = 0.5f*srcSpaceStrokeWidth;
SkRRect ambientRRect;
if (isRect) {
if (rrect.isRect()) {
const SkRect temp = rrect.rect().makeOutset(ambientPathOutset, ambientPathOutset);
ambientRRect = SkRRect::MakeRectXY(temp, ambientPathOutset, ambientPathOutset);
} else {
rrect.outset(ambientPathOutset, ambientPathOutset, &ambientRRect);
}
const SkScalar devSpaceAmbientBlur = blurWidth * scaleFactor;
GrPaint newPaint(paint);
GrColor4f color = newPaint.getColor4f();
color.fRGBA[3] *= fAmbientAlpha;
newPaint.setColor4f(color);
SkStrokeRec ambientStrokeRec(SkStrokeRec::kHairline_InitStyle);
SkStrokeRec ambientStrokeRec(SkStrokeRec::kFill_InitStyle);
bool transparent = SkToBool(fFlags & SkShadowFlags::kTransparentOccluder_ShadowFlag);
ambientStrokeRec.setStrokeStyle(srcSpaceStrokeWidth, transparent);
@ -280,12 +270,6 @@ void SkAmbientShadowMaskFilterImpl::toString(SkString* str) const {
SkAddFlagToString(str,
SkToBool(fFlags & SkShadowFlags::kTransparentOccluder_ShadowFlag),
"TransparentOccluder", &needSeparator);
SkAddFlagToString(str,
SkToBool(fFlags & SkShadowFlags::kGaussianEdge_ShadowFlag),
"GaussianEdge", &needSeparator);
SkAddFlagToString(str,
SkToBool(fFlags & SkShadowFlags::kLargerUmbra_ShadowFlag),
"LargerUmbra", &needSeparator);
} else {
str->append("None");
}

View File

@ -203,53 +203,38 @@ bool SkSpotShadowMaskFilterImpl::directFilterRRectMaskGPU(GrContext*,
const SkRRect& rrect,
const SkRRect& devRRect) const {
// It's likely the caller has already done these checks, but we have to be sure.
// TODO: support analytic blurring of general rrect
// Fast path only supports filled rrects for now.
// TODO: fill and stroke as well.
if (SkStrokeRec::kFill_Style != strokeRec.getStyle()) {
return false;
}
// Fast path only supports simple rrects with near-circular corners.
SkASSERT(devRRect.allCornersCircular());
if (!rrect.isRect() && !rrect.isOval() && !rrect.isSimple()) {
// Fast path only supports simple rrects with circular corners.
if (!devRRect.isRect() && !devRRect.isCircle() &&
(!devRRect.isSimple() || !devRRect.allCornersCircular())) {
return false;
}
// Fast path only supports uniform scale.
SkScalar scaleFactors[2];
if (!viewMatrix.getMinMaxScales(scaleFactors)) {
// matrix is degenerate
if (!viewMatrix.isSimilarity()) {
return false;
}
if (scaleFactors[0] != scaleFactors[1]) {
return false;
}
SkScalar scaleFactor = scaleFactors[0];
// For all of these, we need to ensure we have a rrect with radius >= 0.5f in device space
const SkScalar minRadius = 0.5f / scaleFactor;
bool isRect = rrect.getSimpleRadii().fX <= minRadius;
// TODO: take flags into account when generating shadow data
// 1/scale
SkScalar scaleFactor = viewMatrix.isScaleTranslate() ?
SkScalarInvert(viewMatrix[SkMatrix::kMScaleX]) :
sk_float_rsqrt(viewMatrix[SkMatrix::kMScaleX] * viewMatrix[SkMatrix::kMScaleX] +
viewMatrix[SkMatrix::kMSkewX] * viewMatrix[SkMatrix::kMSkewX]);
if (fSpotAlpha > 0.0f) {
float zRatio = SkTPin(fOccluderHeight / (fLightPos.fZ - fOccluderHeight), 0.0f, 0.95f);
SkScalar devSpaceSpotRadius = 2.0f * fLightRadius * zRatio;
// handle scale of radius and pad due to CTM
const SkScalar srcSpaceSpotRadius = devSpaceSpotRadius / scaleFactor;
SkRRect spotRRect;
if (isRect) {
spotRRect = SkRRect::MakeRectXY(rrect.rect(), minRadius, minRadius);
} else {
spotRRect = rrect;
}
const SkScalar srcSpaceSpotRadius = devSpaceSpotRadius * scaleFactor;
SkRRect spotShadowRRect;
// Compute the scale and translation for the spot shadow.
const SkScalar scale = fLightPos.fZ / (fLightPos.fZ - fOccluderHeight);
spotRRect.transform(SkMatrix::MakeScale(scale, scale), &spotShadowRRect);
rrect.transform(SkMatrix::MakeScale(scale, scale), &spotShadowRRect);
SkPoint spotOffset = SkPoint::Make(zRatio*(-fLightPos.fX), zRatio*(-fLightPos.fY));
// Adjust for the effect of the scale.
@ -258,8 +243,8 @@ bool SkSpotShadowMaskFilterImpl::directFilterRRectMaskGPU(GrContext*,
// This offset is in dev space, need to transform it into source space.
SkMatrix ctmInverse;
if (!viewMatrix.invert(&ctmInverse)) {
// Since the matrix is a similarity, this should never happen, but just in case...
SkDebugf("Matrix is degenerate. Will not render spot shadow!\n");
//**** TODO: this is not good
return true;
}
ctmInverse.mapPoints(&spotOffset, 1);
@ -300,8 +285,7 @@ bool SkSpotShadowMaskFilterImpl::directFilterRRectMaskGPU(GrContext*,
if (spotShadowRRect.isOval()) {
spotShadowRRect = SkRRect::MakeOval(insetRect);
} else {
SkScalar insetRad = SkTMax(spotShadowRRect.getSimpleRadii().fX - insetAmount,
minRadius);
SkScalar insetRad = spotShadowRRect.getSimpleRadii().fX - insetAmount;
spotShadowRRect = SkRRect::MakeRectXY(insetRect, insetRad, insetRad);
}
spotStrokeRec.setStrokeStyle(strokeWidth, false);
@ -356,12 +340,6 @@ void SkSpotShadowMaskFilterImpl::toString(SkString* str) const {
SkAddFlagToString(str,
SkToBool(fFlags & SkShadowFlags::kTransparentOccluder_ShadowFlag),
"TransparentOccluder", &needSeparator);
SkAddFlagToString(str,
SkToBool(fFlags & SkShadowFlags::kGaussianEdge_ShadowFlag),
"GaussianEdge", &needSeparator);
SkAddFlagToString(str,
SkToBool(fFlags & SkShadowFlags::kLargerUmbra_ShadowFlag),
"LargerUmbra", &needSeparator);
} else {
str->append("None");
}

View File

@ -1000,9 +1000,8 @@ void GrRenderTargetContext::drawShadowRRect(const GrClip& clip,
const SkStrokeRec stroke = style.strokeRec();
// TODO: add instancing support?
const GrShaderCaps* shaderCaps = fContext->caps()->shaderCaps();
std::unique_ptr<GrLegacyMeshDrawOp> op = GrShadowRRectOp::Make(
paint.getColor(), viewMatrix, rrect, blurRadius, stroke, shaderCaps);
std::unique_ptr<GrLegacyMeshDrawOp> op = GrShadowRRectOp::Make(paint.getColor(), viewMatrix,
rrect, blurRadius, stroke);
if (op) {
GrPipelineBuilder pipelineBuilder(std::move(paint), GrAAType::kNone);
this->addLegacyMeshDrawOp(std::move(pipelineBuilder), clip, std::move(op));

File diff suppressed because it is too large Load Diff

View File

@ -20,8 +20,7 @@ class SkStrokeRec;
namespace GrShadowRRectOp {
std::unique_ptr<GrLegacyMeshDrawOp> Make(GrColor, const SkMatrix& viewMatrix, const SkRRect& rrect,
const SkScalar blurRadius, const SkStrokeRec& stroke,
const GrShaderCaps* shaderCaps);
const SkScalar blurRadius, const SkStrokeRec& stroke);
}
#endif