Add blend optimization helpers and use to convert rect draws to clears.

R=robertphillips@google.com, jvanverth@google.com

Author: bsalomon@google.com

Review URL: https://chromiumcodereview.appspot.com/22558003

git-svn-id: http://skia.googlecode.com/svn/trunk@10723 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
commit-bot@chromium.org 2013-08-14 21:56:37 +00:00
parent 2865c44570
commit 24ab3b0ce5
10 changed files with 398 additions and 60 deletions

View File

@ -55,6 +55,8 @@
'<(skia_src_path)/gpu/GrAtlas.cpp',
'<(skia_src_path)/gpu/GrAtlas.h',
'<(skia_src_path)/gpu/GrBinHashKey.h',
'<(skia_src_path)/gpu/GrBlend.cpp',
'<(skia_src_path)/gpu/GrBlend.h',
'<(skia_src_path)/gpu/GrBufferAllocPool.cpp',
'<(skia_src_path)/gpu/GrBufferAllocPool.h',
'<(skia_src_path)/gpu/GrCacheID.cpp',

View File

@ -470,6 +470,18 @@ public:
return this->mapRect(rect, *rect);
}
/** Apply this matrix to the src rectangle, and write the four transformed
points into dst. The points written to dst will be the original top-left, top-right,
bottom-right, and bottom-left points transformed by the matrix.
@param dst Where the transformed quad is written.
@param rect The original rectangle to be transformed.
*/
void mapRectToQuad(SkPoint dst[4], const SkRect& rect) const {
// This could potentially be faster if we only transformed each x and y of the rect once.
rect.toQuad(dst);
this->mapPoints(dst, 4);
}
/** Return the mean radius of a circle after it has been mapped by
this matrix. NOTE: in perspective this value assumes the circle
has its center at the origin.

View File

@ -466,8 +466,9 @@ struct SK_API SkRect {
return !SkScalarsEqual((SkScalar*)&a, (SkScalar*)&b, 4);
}
/** return the 4 points that enclose the rectangle
*/
/** return the 4 points that enclose the rectangle (top-left, top-right, bottom-right,
bottom-left). TODO: Consider adding param to control whether quad is CW or CCW.
*/
void toQuad(SkPoint quad[4]) const;
/** Set this rectangle to the empty rectangle (0,0,0,0)

View File

@ -174,7 +174,28 @@ public:
this->resetColorFilter();
}
/**
* Determines whether the drawing with this paint is opaque with respect to both color blending
* and fractional coverage. It does not consider whether AA has been enabled on the paint or
* not. Depending upon whether multisampling or coverage-based AA is in use, AA may make the
* result only apply to the interior of primitives.
*
*/
bool isOpaque() const;
/**
* Returns true if isOpaque would return true and the paint represents a solid constant color
* draw. If the result is true, constantColor will be updated to contain the constant color.
*/
bool isOpaqueAndConstantColor(GrColor* constantColor) const;
private:
/**
* Helper for isOpaque and isOpaqueAndConstantColor.
*/
bool getOpaqueAndKnownColor(GrColor* solidColor, uint32_t* solidColorKnownComponents) const;
/**
* Called when the source coord system from which geometry is rendered changes. It ensures that
* the local coordinates seen by effects remains unchanged. oldToNew gives the transformation

View File

@ -11,6 +11,7 @@
#include "GrTypes.h"
#include "GrResource.h"
#include "SkRect.h"
class GrTexture;
class GrRenderTarget;
@ -33,6 +34,12 @@ public:
*/
int height() const { return fDesc.fHeight; }
/**
* Helper that gets the width and height of the surface as a bounding rectangle.
*/
void getBoundsRect(SkRect* rect) const { rect->setWH(SkIntToScalar(this->width()),
SkIntToScalar(this->height())); }
GrSurfaceOrigin origin() const {
GrAssert(kTopLeft_GrSurfaceOrigin == fDesc.fOrigin || kBottomLeft_GrSurfaceOrigin == fDesc.fOrigin);
return fDesc.fOrigin;

154
src/gpu/GrBlend.cpp Normal file
View File

@ -0,0 +1,154 @@
/*
* Copyright 2013 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "GrBlend.h"
static inline GrBlendCoeff swap_coeff_src_dst(GrBlendCoeff coeff) {
switch (coeff) {
case kDC_GrBlendCoeff:
return kSC_GrBlendCoeff;
case kIDC_GrBlendCoeff:
return kISC_GrBlendCoeff;
case kDA_GrBlendCoeff:
return kSA_GrBlendCoeff;
case kIDA_GrBlendCoeff:
return kISA_GrBlendCoeff;
case kSC_GrBlendCoeff:
return kDC_GrBlendCoeff;
case kISC_GrBlendCoeff:
return kIDC_GrBlendCoeff;
case kSA_GrBlendCoeff:
return kDA_GrBlendCoeff;
case kISA_GrBlendCoeff:
return kIDA_GrBlendCoeff;
default:
return coeff;
}
}
static inline unsigned saturated_add(unsigned a, unsigned b) {
SkASSERT(a <= 255);
SkASSERT(b <= 255);
unsigned sum = a + b;
if (sum > 255) {
sum = 255;
}
return sum;
}
static GrColor add_colors(GrColor src, GrColor dst) {
unsigned r = saturated_add(GrColorUnpackR(src), GrColorUnpackR(dst));
unsigned g = saturated_add(GrColorUnpackG(src), GrColorUnpackG(dst));
unsigned b = saturated_add(GrColorUnpackB(src), GrColorUnpackB(dst));
unsigned a = saturated_add(GrColorUnpackA(src), GrColorUnpackA(dst));
return GrColorPackRGBA(r, g, b, a);
}
static inline bool valid_color(uint32_t compFlags) {
return (kRGBA_GrColorComponentFlags & compFlags) == kRGBA_GrColorComponentFlags;
}
static GrColor simplify_blend_term(GrBlendCoeff* srcCoeff,
GrColor srcColor, uint32_t srcCompFlags,
GrColor dstColor, uint32_t dstCompFlags,
GrColor constantColor) {
GrAssert(!GrBlendCoeffRefsSrc(*srcCoeff));
GrAssert(NULL != srcCoeff);
// Check whether srcCoeff can be reduced to kOne or kZero based on known color inputs.
// We could pick out the coeff r,g,b,a values here and use them to compute the blend term color,
// if possible, below but that is not implemented now.
switch (*srcCoeff) {
case kIDC_GrBlendCoeff:
dstColor = ~dstColor; // fallthrough
case kDC_GrBlendCoeff:
if (valid_color(dstCompFlags)) {
if (0xffffffff == dstColor) {
*srcCoeff = kOne_GrBlendCoeff;
} else if (0 == dstColor) {
*srcCoeff = kZero_GrBlendCoeff;
}
}
break;
case kIDA_GrBlendCoeff:
dstColor = ~dstColor; // fallthrough
case kDA_GrBlendCoeff:
if (kA_GrColorComponentFlag & dstCompFlags) {
if (0xff == GrColorUnpackA(dstColor)) {
*srcCoeff = kOne_GrBlendCoeff;
} else if (0 == GrColorUnpackA(dstColor)) {
*srcCoeff = kZero_GrBlendCoeff;
}
}
break;
case kIConstC_GrBlendCoeff:
constantColor = ~constantColor; // fallthrough
case kConstC_GrBlendCoeff:
if (0xffffffff == constantColor) {
*srcCoeff = kOne_GrBlendCoeff;
} else if (0 == constantColor) {
*srcCoeff = kZero_GrBlendCoeff;
}
break;
case kIConstA_GrBlendCoeff:
constantColor = ~constantColor; // fallthrough
case kConstA_GrBlendCoeff:
if (0xff == GrColorUnpackA(constantColor)) {
*srcCoeff = kOne_GrBlendCoeff;
} else if (0 == GrColorUnpackA(constantColor)) {
*srcCoeff = kZero_GrBlendCoeff;
}
break;
default:
break;
}
// We may have invalidated these above and shouldn't read them again.
GR_DEBUGCODE(dstColor = constantColor = GrColor_ILLEGAL;)
if (kZero_GrBlendCoeff == *srcCoeff || (valid_color(srcCompFlags) && 0 == srcColor)) {
*srcCoeff = kZero_GrBlendCoeff;
return 0;
}
if (kOne_GrBlendCoeff == *srcCoeff && valid_color(srcCompFlags)) {
return srcColor;
} else {
return GrColor_ILLEGAL;
}
}
GrColor GrSimplifyBlend(GrBlendCoeff* srcCoeff,
GrBlendCoeff* dstCoeff,
GrColor srcColor, uint32_t srcCompFlags,
GrColor dstColor, uint32_t dstCompFlags,
GrColor constantColor) {
GrColor srcTermColor = simplify_blend_term(srcCoeff,
srcColor, srcCompFlags,
dstColor, dstCompFlags,
constantColor);
// We call the same function to simplify the dst blend coeff. We trick it out by swapping the
// src and dst.
GrBlendCoeff spoofedCoeff = swap_coeff_src_dst(*dstCoeff);
GrColor dstTermColor = simplify_blend_term(&spoofedCoeff,
dstColor, dstCompFlags,
srcColor, srcCompFlags,
constantColor);
*dstCoeff = swap_coeff_src_dst(spoofedCoeff);
if (GrColor_ILLEGAL != srcTermColor && GrColor_ILLEGAL != dstTermColor) {
return add_colors(srcTermColor, dstTermColor);
} else {
return GrColor_ILLEGAL;
}
}

45
src/gpu/GrBlend.h Normal file
View File

@ -0,0 +1,45 @@
/*
* Copyright 2013 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "GrTypes.h"
#include "GrColor.h"
#ifndef GrBlend_DEFINED
#define GrBlend_DEFINED
static inline bool GrBlendCoeffRefsSrc(GrBlendCoeff coeff) {
switch (coeff) {
case kSC_GrBlendCoeff:
case kISC_GrBlendCoeff:
case kSA_GrBlendCoeff:
case kISA_GrBlendCoeff:
return true;
default:
return false;
}
}
static inline bool GrBlendCoeffRefsDst(GrBlendCoeff coeff) {
switch (coeff) {
case kDC_GrBlendCoeff:
case kIDC_GrBlendCoeff:
case kDA_GrBlendCoeff:
case kIDA_GrBlendCoeff:
return true;
default:
return false;
}
}
GrColor GrSimplifyBlend(GrBlendCoeff* srcCoeff,
GrBlendCoeff* dstCoeff,
GrColor srcColor, uint32_t srcCompFlags,
GrColor dstColor, uint32_t dstCompFlags,
GrColor constantColor);
#endif

View File

@ -681,9 +681,8 @@ static bool isIRect(const SkRect& r) {
static bool apply_aa_to_rect(GrDrawTarget* target,
const SkRect& rect,
SkScalar strokeWidth,
const SkMatrix* matrix,
SkMatrix* combinedMatrix,
SkRect* devRect,
const SkMatrix& combinedMatrix,
SkRect* devBoundRect,
bool* useVertexCoverage) {
// we use a simple coverage ramp to do aa on axis-aligned rects
// we check if the rect will be axis-aligned, and the rect won't land on
@ -716,52 +715,32 @@ static bool apply_aa_to_rect(GrDrawTarget* target,
#if defined(SHADER_AA_FILL_RECT) || !defined(IGNORE_ROT_AA_RECT_OPT)
if (strokeWidth >= 0) {
#endif
if (!drawState.getViewMatrix().preservesAxisAlignment()) {
if (!combinedMatrix.preservesAxisAlignment()) {
return false;
}
if (NULL != matrix && !matrix->preservesAxisAlignment()) {
return false;
}
#if defined(SHADER_AA_FILL_RECT) || !defined(IGNORE_ROT_AA_RECT_OPT)
} else {
if (!drawState.getViewMatrix().preservesAxisAlignment() &&
!drawState.getViewMatrix().preservesRightAngles()) {
return false;
}
if (NULL != matrix && !matrix->preservesRightAngles()) {
if (!combinedMatrix.preservesRightAngles()) {
return false;
}
}
#endif
*combinedMatrix = drawState.getViewMatrix();
if (NULL != matrix) {
combinedMatrix->preConcat(*matrix);
#if GR_DEBUG
#if defined(SHADER_AA_FILL_RECT) || !defined(IGNORE_ROT_AA_RECT_OPT)
if (strokeWidth >= 0) {
#endif
GrAssert(combinedMatrix->preservesAxisAlignment());
#if defined(SHADER_AA_FILL_RECT) || !defined(IGNORE_ROT_AA_RECT_OPT)
} else {
GrAssert(combinedMatrix->preservesRightAngles());
}
#endif
#endif
}
combinedMatrix->mapRect(devRect, rect);
combinedMatrix.mapRect(devBoundRect, rect);
if (strokeWidth < 0) {
return !isIRect(*devRect);
return !isIRect(*devBoundRect);
} else {
return true;
}
}
static inline bool rect_contains_inclusive(const SkRect& rect, const SkPoint& point) {
return point.fX >= rect.fLeft && point.fX <= rect.fRight &&
point.fY >= rect.fTop && point.fY <= rect.fBottom;
}
void GrContext::drawRect(const GrPaint& paint,
const SkRect& rect,
SkScalar width,
@ -771,13 +750,51 @@ void GrContext::drawRect(const GrPaint& paint,
AutoRestoreEffects are;
GrDrawTarget* target = this->prepareToDraw(&paint, BUFFERED_DRAW, &are);
SkRect devRect;
SkMatrix combinedMatrix;
SkMatrix combinedMatrix = target->drawState()->getViewMatrix();
if (NULL != matrix) {
combinedMatrix.preConcat(*matrix);
}
// Check if this is a full RT draw and can be replaced with a clear. We don't bother checking
// cases where the RT is fully inside a stroke.
if (width < 0) {
SkRect rtRect;
target->getDrawState().getRenderTarget()->getBoundsRect(&rtRect);
SkRect clipSpaceRTRect = rtRect;
bool checkClip = false;
if (NULL != this->getClip()) {
checkClip = true;
clipSpaceRTRect.offset(SkIntToScalar(this->getClip()->fOrigin.fX),
SkIntToScalar(this->getClip()->fOrigin.fY));
}
// Does the clip contain the entire RT?
if (!checkClip || target->getClip()->fClipStack->quickContains(clipSpaceRTRect)) {
SkMatrix invM;
if (!combinedMatrix.invert(&invM)) {
return;
}
// Does the rect bound the RT?
SkPoint srcSpaceRTQuad[4];
invM.mapRectToQuad(srcSpaceRTQuad, rtRect);
if (rect_contains_inclusive(rect, srcSpaceRTQuad[0]) &&
rect_contains_inclusive(rect, srcSpaceRTQuad[1]) &&
rect_contains_inclusive(rect, srcSpaceRTQuad[2]) &&
rect_contains_inclusive(rect, srcSpaceRTQuad[3])) {
// Will it blend?
GrColor clearColor;
if (paint.isOpaqueAndConstantColor(&clearColor)) {
target->clear(NULL, clearColor);
return;
}
}
}
}
SkRect devBoundRect;
bool useVertexCoverage;
bool needAA = paint.isAntiAlias() &&
!target->getDrawState().getRenderTarget()->isMultisampled();
bool doAA = needAA && apply_aa_to_rect(target, rect, width, matrix,
&combinedMatrix, &devRect,
bool doAA = needAA && apply_aa_to_rect(target, rect, width, combinedMatrix, &devBoundRect,
&useVertexCoverage);
if (doAA) {
GrDrawState::AutoViewMatrixRestore avmr;
@ -786,12 +803,12 @@ void GrContext::drawRect(const GrPaint& paint,
}
if (width >= 0) {
fAARectRenderer->strokeAARect(this->getGpu(), target,
rect, combinedMatrix, devRect,
rect, combinedMatrix, devBoundRect,
width, useVertexCoverage);
} else {
// filled AA rect
fAARectRenderer->fillAARect(this->getGpu(), target,
rect, combinedMatrix, devRect,
rect, combinedMatrix, devBoundRect,
useVertexCoverage);
}
return;

View File

@ -9,6 +9,7 @@
#define GrDrawState_DEFINED
#include "GrBackendEffectFactory.h"
#include "GrBlend.h"
#include "GrColor.h"
#include "GrEffectStage.h"
#include "GrPaint.h"
@ -469,27 +470,11 @@ public:
fCommon.fSrcBlend = srcCoeff;
fCommon.fDstBlend = dstCoeff;
#if GR_DEBUG
switch (dstCoeff) {
case kDC_GrBlendCoeff:
case kIDC_GrBlendCoeff:
case kDA_GrBlendCoeff:
case kIDA_GrBlendCoeff:
GrPrintf("Unexpected dst blend coeff. Won't work correctly with"
"coverage stages.\n");
break;
default:
break;
if (GrBlendCoeffRefsDst(dstCoeff)) {
GrPrintf("Unexpected dst blend coeff. Won't work correctly with coverage stages.\n");
}
switch (srcCoeff) {
case kSC_GrBlendCoeff:
case kISC_GrBlendCoeff:
case kSA_GrBlendCoeff:
case kISA_GrBlendCoeff:
GrPrintf("Unexpected src blend coeff. Won't work correctly with"
"coverage stages.\n");
break;
default:
break;
if (GrBlendCoeffRefsSrc(srcCoeff)) {
GrPrintf("Unexpected src blend coeff. Won't work correctly with coverage stages.\n");
}
#endif
}

View File

@ -8,6 +8,7 @@
#include "GrPaint.h"
#include "GrBlend.h"
#include "effects/GrSimpleTextureEffect.h"
void GrPaint::addColorTextureEffect(GrTexture* texture, const SkMatrix& matrix) {
@ -33,3 +34,96 @@ void GrPaint::addCoverageTextureEffect(GrTexture* texture,
GrEffectRef* effect = GrSimpleTextureEffect::Create(texture, matrix, params);
this->addCoverageEffect(effect)->unref();
}
bool GrPaint::isOpaque() const {
return this->getOpaqueAndKnownColor(NULL, NULL);
}
bool GrPaint::isOpaqueAndConstantColor(GrColor* color) const {
GrColor tempColor;
uint32_t colorComps;
if (this->getOpaqueAndKnownColor(&tempColor, &colorComps)) {
if (kRGBA_GrColorComponentFlags == colorComps) {
*color = tempColor;
return true;
}
}
return false;
}
bool GrPaint::getOpaqueAndKnownColor(GrColor* solidColor,
uint32_t* solidColorKnownComponents) const {
// TODO: Share this implementation with GrDrawState
// Since fColorFilterXfermode is going away soon, we aren't attempting to handle anything but
// the default setting.
if (SkXfermode::kDst_Mode != fColorFilterXfermode) {
return false;
}
GrColor coverage = GrColorPackRGBA(fCoverage, fCoverage, fCoverage, fCoverage);
uint32_t coverageComps = kRGBA_GrColorComponentFlags;
int count = fCoverageStages.count();
for (int i = 0; i < count; ++i) {
(*fCoverageStages[i].getEffect())->getConstantColorComponents(&coverage, &coverageComps);
}
if (kRGBA_GrColorComponentFlags != coverageComps || 0xffffffff != coverage) {
return false;
}
GrColor color = fColor;
uint32_t colorComps = kRGBA_GrColorComponentFlags;
count = fColorStages.count();
for (int i = 0; i < count; ++i) {
(*fColorStages[i].getEffect())->getConstantColorComponents(&color, &colorComps);
}
GrAssert((NULL == solidColor) == (NULL == solidColorKnownComponents));
GrBlendCoeff srcCoeff = fSrcBlendCoeff;
GrBlendCoeff dstCoeff = fDstBlendCoeff;
GrSimplifyBlend(&srcCoeff, &dstCoeff, color, colorComps, 0, 0, 0);
bool opaque = kZero_GrBlendCoeff == dstCoeff && !GrBlendCoeffRefsDst(srcCoeff);
if (NULL != solidColor) {
if (opaque) {
switch (srcCoeff) {
case kZero_GrBlendCoeff:
*solidColor = 0;
*solidColorKnownComponents = kRGBA_GrColorComponentFlags;
break;
case kOne_GrBlendCoeff:
*solidColor = color;
*solidColorKnownComponents = colorComps;
break;
// The src coeff should never refer to the src and if it refers to dst then opaque
// should have been false.
case kSC_GrBlendCoeff:
case kISC_GrBlendCoeff:
case kDC_GrBlendCoeff:
case kIDC_GrBlendCoeff:
case kSA_GrBlendCoeff:
case kISA_GrBlendCoeff:
case kDA_GrBlendCoeff:
case kIDA_GrBlendCoeff:
default:
GrCrash("srcCoeff should not refer to src or dst.");
break;
// TODO: update this once GrPaint actually has a const color.
case kConstC_GrBlendCoeff:
case kIConstC_GrBlendCoeff:
case kConstA_GrBlendCoeff:
case kIConstA_GrBlendCoeff:
*solidColorKnownComponents = 0;
break;
}
} else {
solidColorKnownComponents = 0;
}
}
return opaque;
}