Do not apply hairline optimization for paths if nv_path_rendering is used

Do not convert thin, non-hairline paths to hairline paths if
nv_path_rendering is used.

The current nv_path_rendering implementation does not render
hairlines. Rendering the hairlines with normal renderers cause
unneccessary gl program changes, which is quite slow.

Changes the behavior of non-nv_path_rendering paths to always perform
the optimization if the shape ends up being painted by the
path-drawing code (GrContext::drawPathInternal). Previously the
optimization was applied only when callgraph started with
SkCanvas::drawPath. This changes drawlooper_msaa4, dashing3_msaa4 and
dashing3_gpu

R=bsalomon@google.com

Author: kkinnunen@nvidia.com

Review URL: https://codereview.chromium.org/38573007

git-svn-id: http://skia.googlecode.com/svn/trunk@12185 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
commit-bot@chromium.org 2013-11-08 06:19:34 +00:00
parent 0d3341c2db
commit 44fb865e81
12 changed files with 128 additions and 105 deletions

View File

@ -987,24 +987,12 @@ static bool xfermodeSupportsCoverageAsAlpha(SkXfermode* xfer) {
}
}
bool SkDrawTreatAsHairline(const SkPaint& paint, const SkMatrix& matrix,
SkScalar* coverage) {
bool SkDrawTreatAAStrokeAsHairline(SkScalar strokeWidth, const SkMatrix& matrix,
SkScalar* coverage) {
SkASSERT(coverage);
if (SkPaint::kStroke_Style != paint.getStyle()) {
return false;
}
SkScalar strokeWidth = paint.getStrokeWidth();
if (0 == strokeWidth) {
*coverage = SK_Scalar1;
return true;
}
SkASSERT(strokeWidth > 0);
// We need to try to fake a thick-stroke with a modulated hairline.
// if we get here, we need to try to fake a thick-stroke with a modulated
// hairline
if (!paint.isAntiAlias()) {
return false;
}
if (matrix.hasPerspective()) {
return false;
}

View File

@ -59,12 +59,32 @@ struct SkDrawProcs {
SkDraw1Glyph::Proc fD1GProc;
};
bool SkDrawTreatAAStrokeAsHairline(SkScalar strokeWidth, const SkMatrix&,
SkScalar* coverage);
/**
* If the current paint is set to stroke and the stroke-width when applied to
* the matrix is <= 1.0, then this returns true, and sets coverage (simulating
* a stroke by drawing a hairline with partial coverage). If any of these
* conditions are false, then this returns false and coverage is ignored.
*/
bool SkDrawTreatAsHairline(const SkPaint&, const SkMatrix&, SkScalar* coverage);
inline bool SkDrawTreatAsHairline(const SkPaint& paint, const SkMatrix& matrix,
SkScalar* coverage) {
if (SkPaint::kStroke_Style != paint.getStyle()) {
return false;
}
SkScalar strokeWidth = paint.getStrokeWidth();
if (0 == strokeWidth) {
*coverage = SK_Scalar1;
return true;
}
if (!paint.isAntiAlias()) {
return false;
}
return SkDrawTreatAAStrokeAsHairline(strokeWidth, matrix, coverage);
}
#endif

View File

@ -759,7 +759,7 @@ bool GrAAHairLinePathRenderer::createLineGeom(const SkPath& path,
}
devBounds->set(lines.begin(), lines.count());
for (int i = 0; i < lineCnt; ++i) {
add_line(&lines[2*i], toSrc, drawState->getCoverage(), &verts);
add_line(&lines[2*i], toSrc, drawState->getCoverageColor(), &verts);
}
// All the verts computed by add_line are within sqrt(1^2 + 0.5^2) of the end points.
static const SkScalar kSqrtOfOneAndAQuarter = SkFloatToScalar(1.118f);

View File

@ -25,6 +25,7 @@
#include "GrSoftwarePathRenderer.h"
#include "GrStencilBuffer.h"
#include "GrTextStrike.h"
#include "SkDrawProcs.h"
#include "SkRTConf.h"
#include "SkRRect.h"
#include "SkStrokeRec.h"
@ -43,10 +44,6 @@ SK_CONF_DECLARE(bool, c_Defer, "gpu.deferContext", true,
#define BUFFERED_DRAW (c_Defer ? kYes_BufferedDraw : kNo_BufferedDraw)
// When we're using coverage AA but the blend is incompatible (given gpu
// limitations) should we disable AA or draw wrong?
#define DISABLE_COVERAGE_AA_FOR_BLEND 1
#ifdef SK_DEBUG
// change this to a 1 to see notifications when partial coverage fails
#define GR_DEBUG_PARTIAL_COVERAGE_CHECK 0
@ -695,14 +692,6 @@ void GrContext::dumpFontCache() const {
////////////////////////////////////////////////////////////////////////////////
namespace {
inline bool disable_coverage_aa_for_blend(GrDrawTarget* target) {
return DISABLE_COVERAGE_AA_FOR_BLEND && !target->canApplyCoverage();
}
}
////////////////////////////////////////////////////////////////////////////////
/* create a triangle strip that strokes the specified triangle. There are 8
unique vertices, but we repreat the last 2 to close up. Alternatively we
could use an indices array, and then only send 8 verts, but not sure that
@ -746,7 +735,7 @@ static bool apply_aa_to_rect(GrDrawTarget* target,
// TODO: remove this ugliness when we drop the fixed-pipe impl
*useVertexCoverage = false;
if (!target->getDrawState().canTweakAlphaForCoverage()) {
if (disable_coverage_aa_for_blend(target)) {
if (target->shouldDisableCoverageAAForBlend()) {
#ifdef SK_DEBUG
//GrPrintf("Turning off AA to correctly apply blend.\n");
#endif
@ -1034,14 +1023,10 @@ void GrContext::drawRRect(const GrPaint& paint,
AutoCheckFlush acf(this);
GrDrawTarget* target = this->prepareToDraw(&paint, BUFFERED_DRAW, &are, &acf);
bool useAA = paint.isAntiAlias() &&
!target->getDrawState().getRenderTarget()->isMultisampled() &&
!disable_coverage_aa_for_blend(target);
if (!fOvalRenderer->drawSimpleRRect(target, this, useAA, rect, stroke)) {
if (!fOvalRenderer->drawSimpleRRect(target, this, paint.isAntiAlias(), rect, stroke)) {
SkPath path;
path.addRRect(rect);
this->internalDrawPath(target, useAA, path, stroke);
this->internalDrawPath(target, paint.isAntiAlias(), path, stroke);
}
}
@ -1058,14 +1043,10 @@ void GrContext::drawOval(const GrPaint& paint,
AutoCheckFlush acf(this);
GrDrawTarget* target = this->prepareToDraw(&paint, BUFFERED_DRAW, &are, &acf);
bool useAA = paint.isAntiAlias() &&
!target->getDrawState().getRenderTarget()->isMultisampled() &&
!disable_coverage_aa_for_blend(target);
if (!fOvalRenderer->drawOval(target, this, useAA, oval, stroke)) {
if (!fOvalRenderer->drawOval(target, this, paint.isAntiAlias(), oval, stroke)) {
SkPath path;
path.addOval(oval);
this->internalDrawPath(target, useAA, path, stroke);
this->internalDrawPath(target, paint.isAntiAlias(), path, stroke);
}
}
@ -1091,7 +1072,7 @@ static bool is_nested_rects(GrDrawTarget* target,
*useVertexCoverage = false;
if (!target->getDrawState().canTweakAlphaForCoverage()) {
if (disable_coverage_aa_for_blend(target)) {
if (target->shouldDisableCoverageAAForBlend()) {
return false;
} else {
*useVertexCoverage = true;
@ -1141,15 +1122,17 @@ void GrContext::drawPath(const GrPaint& paint, const SkPath& path, const SkStrok
AutoRestoreEffects are;
AutoCheckFlush acf(this);
GrDrawTarget* target = this->prepareToDraw(&paint, BUFFERED_DRAW, &are, &acf);
GrDrawState* drawState = target->drawState();
bool useAA = paint.isAntiAlias() && !target->getDrawState().getRenderTarget()->isMultisampled();
if (useAA && stroke.getWidth() < 0 && !path.isConvex()) {
bool useCoverageAA = paint.isAntiAlias() && !drawState->getRenderTarget()->isMultisampled();
if (useCoverageAA && stroke.getWidth() < 0 && !path.isConvex()) {
// Concave AA paths are expensive - try to avoid them for special cases
bool useVertexCoverage;
SkRect rects[2];
if (is_nested_rects(target, path, stroke, rects, &useVertexCoverage)) {
SkMatrix origViewMatrix = target->getDrawState().getViewMatrix();
SkMatrix origViewMatrix = drawState->getViewMatrix();
GrDrawState::AutoViewMatrixRestore avmr;
if (!avmr.setIdentity(target->drawState())) {
return;
@ -1167,42 +1150,73 @@ void GrContext::drawPath(const GrPaint& paint, const SkPath& path, const SkStrok
bool isOval = path.isOval(&ovalRect);
if (!isOval || path.isInverseFillType()
|| !fOvalRenderer->drawOval(target, this, useAA, ovalRect, stroke)) {
this->internalDrawPath(target, useAA, path, stroke);
|| !fOvalRenderer->drawOval(target, this, paint.isAntiAlias(), ovalRect, stroke)) {
this->internalDrawPath(target, paint.isAntiAlias(), path, stroke);
}
}
void GrContext::internalDrawPath(GrDrawTarget* target, bool useAA, const SkPath& path,
const SkStrokeRec& stroke) {
namespace {
// See also: SkDrawTreatAsHairline.
static inline bool should_convert_to_hairline(bool useAA, const SkStrokeRec& stroke,
const SkMatrix& matrix, SkScalar* coverage) {
if (stroke.getStyle() != SkStrokeRec::kStroke_Style) {
return false;
}
SkASSERT(0 != stroke.getWidth());
if (!useAA) {
return false;
}
return SkDrawTreatAAStrokeAsHairline(stroke.getWidth(), matrix, coverage);
}
}
void GrContext::internalDrawPath(GrDrawTarget* target, bool useAA,
const SkPath& path, const SkStrokeRec& origStroke) {
SkASSERT(!path.isEmpty());
// An Assumption here is that path renderer would use some form of tweaking
// the src color (either the input alpha or in the frag shader) to implement
// aa. If we have some future driver-mojo path AA that can do the right
// thing WRT to the blend then we'll need some query on the PR.
if (disable_coverage_aa_for_blend(target)) {
#ifdef SK_DEBUG
//GrPrintf("Turning off AA to correctly apply blend.\n");
#endif
useAA = false;
bool useCoverageAA = useAA &&
!target->getDrawState().getRenderTarget()->isMultisampled() &&
!target->shouldDisableCoverageAAForBlend();
SkTCopyOnFirstWrite<SkStrokeRec> stroke(origStroke);
// Can we treat a thin stroke as a hairline w/ coverage? If we can, we draw lots faster (raster
// device does this same test).
// Do not do this if gpu supports path rendering natively and we might be using the support
// (useCoverageAA == false). Hairline renderer is likely to be slow due to program switches.
if (!useCoverageAA || !fGpu->caps()->pathRenderingSupport()) {
SkScalar hairlineCoverage;
if (should_convert_to_hairline(useAA, *stroke, this->getMatrix(), &hairlineCoverage)) {
target->drawState()->setCoverage(SkScalarRoundToInt(hairlineCoverage * target->getDrawState().getCoverage()));
stroke.writable()->setHairlineStyle();
}
}
GrPathRendererChain::DrawType type = useAA ? GrPathRendererChain::kColorAntiAlias_DrawType :
GrPathRendererChain::kColor_DrawType;
GrPathRendererChain::DrawType type =
useCoverageAA ? GrPathRendererChain::kColorAntiAlias_DrawType :
GrPathRendererChain::kColor_DrawType;
const SkPath* pathPtr = &path;
SkPath tmpPath;
SkStrokeRec strokeRec(stroke);
// Try a 1st time without stroking the path and without allowing the SW renderer
GrPathRenderer* pr = this->getPathRenderer(*pathPtr, strokeRec, target, false, type);
GrPathRenderer* pr = this->getPathRenderer(*pathPtr, *stroke, target, false, type);
if (NULL == pr) {
if (!strokeRec.isHairlineStyle()) {
if (!stroke->isHairlineStyle()) {
// It didn't work the 1st time, so try again with the stroked path
if (strokeRec.applyToPath(&tmpPath, *pathPtr)) {
if (stroke->applyToPath(&tmpPath, *pathPtr)) {
pathPtr = &tmpPath;
strokeRec.setFillStyle();
stroke.writable()->setFillStyle();
}
}
if (pathPtr->isEmpty()) {
@ -1210,7 +1224,7 @@ void GrContext::internalDrawPath(GrDrawTarget* target, bool useAA, const SkPath&
}
// This time, allow SW renderer
pr = this->getPathRenderer(*pathPtr, strokeRec, target, true, type);
pr = this->getPathRenderer(*pathPtr, *stroke, target, true, type);
}
if (NULL == pr) {
@ -1220,7 +1234,7 @@ void GrContext::internalDrawPath(GrDrawTarget* target, bool useAA, const SkPath&
return;
}
pr->drawPath(*pathPtr, strokeRec, target, useAA);
pr->drawPath(*pathPtr, *stroke, target, useCoverageAA);
}
////////////////////////////////////////////////////////////////////////////////

View File

@ -54,7 +54,6 @@ void GrDrawState::setFromPaint(const GrPaint& paint, const SkMatrix& vm, GrRende
this->enableState(GrDrawState::kClip_StateBit);
this->setColor(paint.getColor());
this->setCoverage4(paint.getCoverage());
this->setState(GrDrawState::kDither_StateBit, paint.isDither());
this->setState(GrDrawState::kHWAntialias_StateBit, paint.isAntiAlias());
@ -220,7 +219,7 @@ bool GrDrawState::srcAlphaWillBeOne() const {
// Check whether coverage is treated as color. If so we run through the coverage computation.
if (this->isCoverageDrawing()) {
GrColor coverageColor = this->getCoverage();
GrColor coverageColor = this->getCoverageColor();
GrColor oldColor = color;
color = 0;
for (int c = 0; c < 4; ++c) {
@ -312,7 +311,7 @@ GrDrawState::BlendOptFlags GrDrawState::getBlendOpts(bool forceCoverage,
bool covIsZero = !this->isCoverageDrawing() &&
!this->hasCoverageVertexAttribute() &&
0 == this->getCoverage();
0 == this->getCoverageColor();
// When coeffs are (0,1) there is no reason to draw at all, unless
// stenciling is enabled. Having color writes disabled is effectively
// (0,1). The same applies when coverage is known to be 0.
@ -327,7 +326,7 @@ GrDrawState::BlendOptFlags GrDrawState::getBlendOpts(bool forceCoverage,
// check for coverage due to constant coverage, per-vertex coverage, or coverage stage
bool hasCoverage = forceCoverage ||
0xffffffff != this->getCoverage() ||
0xffffffff != this->getCoverageColor() ||
this->hasCoverageVertexAttribute() ||
fCoverageStages.count() > 0;

View File

@ -311,15 +311,11 @@ public:
fCommon.fCoverage = GrColorPackRGBA(coverage, coverage, coverage, coverage);
}
/**
* Version of above that specifies 4 channel per-vertex color. The value
* should be premultiplied.
*/
void setCoverage4(GrColor coverage) {
fCommon.fCoverage = coverage;
uint8_t getCoverage() const {
return GrColorUnpackR(fCommon.fCoverage);
}
GrColor getCoverage() const {
GrColor getCoverageColor() const {
return fCommon.fCoverage;
}

View File

@ -100,6 +100,15 @@ public:
*/
bool canApplyCoverage() const;
/** When we're using coverage AA but the blend is incompatible (given gpu
* limitations) we should disable AA. */
bool shouldDisableCoverageAAForBlend() {
// Enable below if we should draw with AA even when it produces
// incorrect blending.
// return false;
return !this->canApplyCoverage();
}
/**
* Given the current draw state and hw support, will HW AA lines be used (if
* a line primitive type is drawn)?

View File

@ -460,7 +460,11 @@ void GrOvalRenderer::reset() {
bool GrOvalRenderer::drawOval(GrDrawTarget* target, const GrContext* context, bool useAA,
const SkRect& oval, const SkStrokeRec& stroke)
{
if (!useAA) {
bool useCoverageAA = useAA &&
!target->getDrawState().getRenderTarget()->isMultisampled() &&
!target->shouldDisableCoverageAAForBlend();
if (!useCoverageAA) {
return false;
}
@ -469,13 +473,13 @@ bool GrOvalRenderer::drawOval(GrDrawTarget* target, const GrContext* context, bo
// we can draw circles
if (SkScalarNearlyEqual(oval.width(), oval.height())
&& circle_stays_circle(vm)) {
this->drawCircle(target, useAA, oval, stroke);
this->drawCircle(target, useCoverageAA, oval, stroke);
// if we have shader derivative support, render as device-independent
} else if (target->caps()->shaderDerivativeSupport()) {
return this->drawDIEllipse(target, useAA, oval, stroke);
return this->drawDIEllipse(target, useCoverageAA, oval, stroke);
// otherwise axis-aligned ellipses only
} else if (vm.rectStaysRect()) {
return this->drawEllipse(target, useAA, oval, stroke);
return this->drawEllipse(target, useCoverageAA, oval, stroke);
} else {
return false;
}
@ -492,7 +496,7 @@ extern const GrVertexAttrib gCircleVertexAttribs[] = {
};
void GrOvalRenderer::drawCircle(GrDrawTarget* target,
bool useAA,
bool useCoverageAA,
const SkRect& circle,
const SkStrokeRec& stroke)
{
@ -597,7 +601,7 @@ extern const GrVertexAttrib gDIEllipseVertexAttribs[] = {
};
bool GrOvalRenderer::drawEllipse(GrDrawTarget* target,
bool useAA,
bool useCoverageAA,
const SkRect& ellipse,
const SkStrokeRec& stroke)
{
@ -606,7 +610,7 @@ bool GrOvalRenderer::drawEllipse(GrDrawTarget* target,
{
// we should have checked for this previously
bool isAxisAlignedEllipse = drawState->getViewMatrix().rectStaysRect();
SkASSERT(useAA && isAxisAlignedEllipse);
SkASSERT(useCoverageAA && isAxisAlignedEllipse);
}
#endif
@ -729,7 +733,7 @@ bool GrOvalRenderer::drawEllipse(GrDrawTarget* target,
}
bool GrOvalRenderer::drawDIEllipse(GrDrawTarget* target,
bool useAA,
bool useCoverageAA,
const SkRect& ellipse,
const SkStrokeRec& stroke)
{
@ -882,8 +886,12 @@ GrIndexBuffer* GrOvalRenderer::rRectIndexBuffer(GrGpu* gpu) {
bool GrOvalRenderer::drawSimpleRRect(GrDrawTarget* target, GrContext* context, bool useAA,
const SkRRect& rrect, const SkStrokeRec& stroke)
{
bool useCoverageAA = useAA &&
!target->getDrawState().getRenderTarget()->isMultisampled() &&
!target->shouldDisableCoverageAAForBlend();
// only anti-aliased rrects for now
if (!useAA) {
if (!useCoverageAA) {
return false;
}
@ -891,7 +899,7 @@ bool GrOvalRenderer::drawSimpleRRect(GrDrawTarget* target, GrContext* context, b
#ifdef SK_DEBUG
{
// we should have checked for this previously
SkASSERT(useAA && vm.rectStaysRect() && rrect.isSimple());
SkASSERT(useCoverageAA && vm.rectStaysRect() && rrect.isSimple());
}
#endif

View File

@ -37,13 +37,13 @@ public:
const SkRRect& rrect, const SkStrokeRec& stroke);
private:
bool drawEllipse(GrDrawTarget* target, bool useAA,
bool drawEllipse(GrDrawTarget* target, bool useCoverageAA,
const SkRect& ellipse,
const SkStrokeRec& stroke);
bool drawDIEllipse(GrDrawTarget* target, bool useAA,
bool drawDIEllipse(GrDrawTarget* target, bool useCoverageAA,
const SkRect& ellipse,
const SkStrokeRec& stroke);
void drawCircle(GrDrawTarget* target, bool useAA,
void drawCircle(GrDrawTarget* target, bool useCoverageAA,
const SkRect& circle,
const SkStrokeRec& stroke);

View File

@ -12,7 +12,8 @@
#include "GrContext.h"
#include "GrBitmapTextContext.h"
#include "GrGpu.h"
#include "GrDrawTargetCaps.h"
#include "SkGrTexturePixelRef.h"
#include "SkColorFilter.h"
@ -850,14 +851,6 @@ void SkGpuDevice::drawPath(const SkDraw& draw, const SkPath& origSrcPath,
return;
}
// can we cheat, and treat a thin stroke as a hairline w/ coverage
// if we can, we draw lots faster (raster device does this same test)
SkScalar hairlineCoverage;
bool doHairLine = SkDrawTreatAsHairline(paint, fContext->getMatrix(), &hairlineCoverage);
if (doHairLine) {
grPaint.setCoverage(SkScalarRoundToInt(hairlineCoverage * grPaint.getCoverage()));
}
// If we have a prematrix, apply it to the path, optimizing for the case
// where the original path can in fact be modified in place (even though
// its parameter type is const).
@ -887,10 +880,6 @@ void SkGpuDevice::drawPath(const SkDraw& draw, const SkPath& origSrcPath,
pathPtr = &effectPath;
}
if (!pathEffect && doHairLine) {
stroke.setHairlineStyle();
}
if (paint.getMaskFilter()) {
if (!stroke.isHairlineStyle()) {
if (stroke.applyToPath(&tmpPath, *pathPtr)) {

View File

@ -207,10 +207,10 @@ void GrGLProgram::setData(GrDrawState::BlendOptFlags blendOpts,
coverage = 0;
} else if (blendOpts & GrDrawState::kEmitCoverage_BlendOptFlag) {
color = 0xffffffff;
coverage = drawState.getCoverage();
coverage = drawState.getCoverageColor();
} else {
color = drawState.getColor();
coverage = drawState.getCoverage();
coverage = drawState.getCoverageColor();
}
this->setColor(drawState, color, sharedState);

View File

@ -156,7 +156,7 @@ void GrGLProgramDesc::Build(const GrDrawState& drawState,
header->fHasVertexCode = true;
}
bool covIsSolidWhite = !requiresCoverageAttrib && 0xffffffff == drawState.getCoverage();
bool covIsSolidWhite = !requiresCoverageAttrib && 0xffffffff == drawState.getCoverageColor();
if (skipCoverage) {
header->fCoverageInput = kTransBlack_ColorInput;