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:
parent
0c5cf5d7a2
commit
57061eea44
@ -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
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user