aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar bsalomon@google.com <bsalomon@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2012-11-07 16:17:24 +0000
committerGravatar bsalomon@google.com <bsalomon@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2012-11-07 16:17:24 +0000
commit7b7cdd147f5528865238e5ed98c79e6d319fde9b (patch)
tree8d42e48f75699dbeb53f7ed7a6a4f3f820abe988
parent8f0a7b8e7334187a5d7d5ab7fde5a3c3009555f5 (diff)
Some improvements to reduce the number of pixels touched in generating alpha clip masks
Review URL: https://codereview.appspot.com/6828043 git-svn-id: http://skia.googlecode.com/svn/trunk@6329 2bbb7eff-a529-9590-31e7-b0007b416f81
-rw-r--r--include/core/SkClipStack.h5
-rw-r--r--include/core/SkMatrix.h2
-rw-r--r--include/core/SkRandom.h2
-rw-r--r--include/core/SkRect.h9
-rw-r--r--src/core/SkClipStack.cpp11
-rw-r--r--src/gpu/GrClipMaskManager.cpp109
-rw-r--r--src/gpu/GrClipMaskManager.h7
-rw-r--r--src/gpu/SkGpuDevice.cpp6
-rw-r--r--src/gpu/effects/GrTextureDomainEffect.cpp76
-rw-r--r--src/gpu/effects/GrTextureDomainEffect.h53
10 files changed, 198 insertions, 82 deletions
diff --git a/include/core/SkClipStack.h b/include/core/SkClipStack.h
index a67e0a5dd9..79fd2c9dd4 100644
--- a/include/core/SkClipStack.h
+++ b/include/core/SkClipStack.h
@@ -134,6 +134,11 @@ public:
fDoAA(false) {}
friend bool operator==(const Clip& a, const Clip& b);
friend bool operator!=(const Clip& a, const Clip& b);
+ /**
+ * Gets the bounds of the clip element, either the rect or path bounds.
+ */
+ const SkRect& getBounds() const;
+
const SkRect* fRect; // if non-null, this is a rect clip
const SkPath* fPath; // if non-null, this is a path clip
SkRegion::Op fOp;
diff --git a/include/core/SkMatrix.h b/include/core/SkMatrix.h
index 980bcaa27a..2d3786cf31 100644
--- a/include/core/SkMatrix.h
+++ b/include/core/SkMatrix.h
@@ -173,6 +173,8 @@ public:
/** Set the matrix to translate by (dx, dy).
*/
void setTranslate(SkScalar dx, SkScalar dy);
+ void setTranslate(const SkVector& v) { this->setTranslate(v.fX, v.fY); }
+
/** Set the matrix to scale by sx and sy, with a pivot point at (px, py).
The pivot point is the coordinate that should remain unchanged by the
specified transformation.
diff --git a/include/core/SkRandom.h b/include/core/SkRandom.h
index 0f9b9aa003..4731bbed4b 100644
--- a/include/core/SkRandom.h
+++ b/include/core/SkRandom.h
@@ -84,7 +84,7 @@ public:
in the range [min..max).
*/
SkScalar nextRangeScalar(SkScalar min, SkScalar max) {
- return SkScalarMul(this->nextSScalar1(), (max - min)) + min;
+ return SkScalarMul(this->nextUScalar1(), (max - min)) + min;
}
/** Return the next pseudo random number expressed as a SkScalar
diff --git a/include/core/SkRect.h b/include/core/SkRect.h
index 7210bf9cd6..f5e8c3cc3a 100644
--- a/include/core/SkRect.h
+++ b/include/core/SkRect.h
@@ -366,6 +366,15 @@ struct SK_API SkRect {
return r;
}
+ static SkRect SK_WARN_UNUSED_RESULT MakeFromIRect(const SkIRect& irect) {
+ SkRect r;
+ r.set(SkIntToScalar(irect.fLeft),
+ SkIntToScalar(irect.fTop),
+ SkIntToScalar(irect.fRight),
+ SkIntToScalar(irect.fBottom));
+ return r;
+ }
+
/**
* Return true if the rectangle's width or height are <= 0
*/
diff --git a/src/core/SkClipStack.cpp b/src/core/SkClipStack.cpp
index 0f2d632a84..a5b591e72a 100644
--- a/src/core/SkClipStack.cpp
+++ b/src/core/SkClipStack.cpp
@@ -724,6 +724,17 @@ bool operator!=(const SkClipStack::Iter::Clip& a,
return !(a == b);
}
+const SkRect& SkClipStack::Iter::Clip::getBounds() const {
+ if (NULL != fRect) {
+ return *fRect;
+ } else if (NULL != fPath) {
+ return fPath->getBounds();
+ } else {
+ static const SkRect kEmpty = {0, 0, 0, 0};
+ return kEmpty;
+ }
+}
+
SkClipStack::Iter::Iter(const SkClipStack& stack, IterStart startLoc)
: fStack(&stack) {
this->reset(stack, startLoc);
diff --git a/src/gpu/GrClipMaskManager.cpp b/src/gpu/GrClipMaskManager.cpp
index 2be92dc24a..606b90eeeb 100644
--- a/src/gpu/GrClipMaskManager.cpp
+++ b/src/gpu/GrClipMaskManager.cpp
@@ -7,6 +7,7 @@
*/
#include "GrClipMaskManager.h"
+#include "effects/GrTextureDomainEffect.h"
#include "GrGpu.h"
#include "GrRenderTarget.h"
#include "GrStencilBuffer.h"
@@ -42,7 +43,13 @@ void setup_drawstate_aaclip(GrGpu* gpu,
mat.preConcat(drawState->getViewMatrix());
drawState->stage(kMaskStage)->reset();
- drawState->createTextureEffect(kMaskStage, result, mat);
+
+ SkIRect domainTexels = SkIRect::MakeWH(devBound.width(), devBound.height());
+ drawState->stage(kMaskStage)->setEffect(
+ GrTextureDomainEffect::Create(result,
+ mat,
+ GrTextureDomainEffect::MakeTexelDomain(result, domainTexels),
+ GrTextureDomainEffect::kDecal_WrapMode))->unref();
}
bool path_needs_SW_renderer(GrContext* context,
@@ -152,8 +159,7 @@ bool GrClipMaskManager::setupClipping(const GrClipData* clipDataIn) {
GrIRect devClipBounds;
bool isIntersectionOfRects = false;
- clipDataIn->getConservativeBounds(rt, &devClipBounds,
- &isIntersectionOfRects);
+ clipDataIn->getConservativeBounds(rt, &devClipBounds, &isIntersectionOfRects);
if (devClipBounds.isEmpty()) {
return false;
}
@@ -483,25 +489,31 @@ bool GrClipMaskManager::drawClipShape(GrTexture* target,
return true;
}
-void GrClipMaskManager::drawTexture(GrTexture* target,
- GrTexture* texture) {
+void GrClipMaskManager::mergeMask(GrTexture* dstMask,
+ GrTexture* srcMask,
+ SkRegion::Op op,
+ const GrIRect& dstBound,
+ const GrIRect& srcBound) {
GrDrawState* drawState = fGpu->drawState();
GrAssert(NULL != drawState);
+ SkMatrix oldMatrix = drawState->getViewMatrix();
+ drawState->viewMatrix()->reset();
- // no AA here since it is encoded in the texture
- drawState->setRenderTarget(target->asRenderTarget());
+ drawState->setRenderTarget(dstMask->asRenderTarget());
+ setup_boolean_blendcoeffs(drawState, op);
+
SkMatrix sampleM;
- sampleM.setIDiv(texture->width(), texture->height());
-
- drawState->createTextureEffect(0, texture, sampleM);
-
- GrRect rect = GrRect::MakeWH(SkIntToScalar(target->width()),
- SkIntToScalar(target->height()));
-
- fGpu->drawSimpleRect(rect, NULL);
+ sampleM.setIDiv(srcMask->width(), srcMask->height());
+ drawState->stage(0)->setEffect(
+ GrTextureDomainEffect::Create(srcMask,
+ sampleM,
+ GrTextureDomainEffect::MakeTexelDomain(srcMask, srcBound),
+ GrTextureDomainEffect::kDecal_WrapMode))->unref();
+ fGpu->drawSimpleRect(SkRect::MakeFromIRect(dstBound), NULL);
drawState->disableStage(0);
+ drawState->setViewMatrix(oldMatrix);
}
// get a texture to act as a temporary buffer for AA clip boolean operations
@@ -599,14 +611,18 @@ bool GrClipMaskManager::createAlphaClipMask(const GrClipData& clipDataIn,
GrDrawTarget::AutoGeometryPush agp(fGpu);
- if (0 != devResultBounds->fTop || 0 != devResultBounds->fLeft ||
- 0 != clipDataIn.fOrigin.fX || 0 != clipDataIn.fOrigin.fY) {
- // if we were able to trim down the size of the mask we need to
- // offset the paths & rects that will be used to compute it
- drawState->viewMatrix()->setTranslate(
- SkIntToScalar(-devResultBounds->fLeft-clipDataIn.fOrigin.fX),
- SkIntToScalar(-devResultBounds->fTop-clipDataIn.fOrigin.fY));
- }
+ // The mask we generate is translated so that its upper-left corner is at devResultBounds
+ // upper-left corner in device space.
+ GrIRect maskResultBounds = GrIRect::MakeWH(devResultBounds->width(), devResultBounds->height());
+
+ // Set the matrix so that rendered clip elements are transformed from the space of the clip
+ // stack to the alpha-mask. This accounts for both translation due to the clip-origin and the
+ // placement of the mask within the device.
+ SkVector clipToMaskOffset = {
+ SkIntToScalar(-devResultBounds->fLeft - clipDataIn.fOrigin.fX),
+ SkIntToScalar(-devResultBounds->fTop - clipDataIn.fOrigin.fY)
+ };
+ drawState->viewMatrix()->setTranslate(clipToMaskOffset);
bool clearToInside;
SkRegion::Op firstOp = SkRegion::kReplace_Op; // suppress warning
@@ -618,10 +634,12 @@ bool GrClipMaskManager::createAlphaClipMask(const GrClipData& clipDataIn,
&clearToInside,
&firstOp,
clipDataIn);
-
- fGpu->clear(NULL,
+ // The scratch texture that we are drawing into can be substantially larger than the mask. Only
+ // clear the part that we care about.
+ fGpu->clear(&maskResultBounds,
clearToInside ? 0xffffffff : 0x00000000,
accum->asRenderTarget());
+ bool accumClearedToZero = !clearToInside;
GrAutoScratchTexture temp;
bool first = true;
@@ -636,8 +654,10 @@ bool GrClipMaskManager::createAlphaClipMask(const GrClipData& clipDataIn,
if (SkRegion::kReplace_Op == op) {
// clear the accumulator and draw the new object directly into it
- fGpu->clear(NULL, 0x00000000, accum->asRenderTarget());
-
+ if (!accumClearedToZero) {
+ fGpu->clear(&maskResultBounds, 0x00000000, accum->asRenderTarget());
+ }
+
setup_boolean_blendcoeffs(drawState, op);
this->drawClipShape(accum, clip, *devResultBounds);
@@ -655,40 +675,31 @@ bool GrClipMaskManager::createAlphaClipMask(const GrClipData& clipDataIn,
return false;
}
+ // this is the bounds of the clip element in the space of the alpha-mask. The temporary
+ // mask buffer can be substantially larger than the actually clip stack element. We
+ // touch the minimum number of pixels necessary and use decal mode to combine it with
+ // the accumulator
+ GrRect elementMaskBounds = clip->getBounds();
+ elementMaskBounds.offset(clipToMaskOffset);
+ GrIRect elementMaskIBounds;
+ elementMaskBounds.roundOut(&elementMaskIBounds);
+
// clear the temp target & draw into it
- fGpu->clear(NULL, 0x00000000, temp.texture()->asRenderTarget());
+ fGpu->clear(&elementMaskIBounds, 0x00000000, temp.texture()->asRenderTarget());
setup_boolean_blendcoeffs(drawState, SkRegion::kReplace_Op);
- this->drawClipShape(temp.texture(), clip, *devResultBounds);
-
- // TODO: rather than adding these two translations here
- // compute the bounding box needed to render the texture
- // into temp
- if (0 != devResultBounds->fTop || 0 != devResultBounds->fLeft ||
- 0 != clipDataIn.fOrigin.fX || 0 != clipDataIn.fOrigin.fY) {
- // In order for the merge of the temp clip into the accumulator
- // to work we need to disable the translation
- drawState->viewMatrix()->reset();
- }
+ this->drawClipShape(temp.texture(), clip, elementMaskIBounds);
// Now draw into the accumulator using the real operation
// and the temp buffer as a texture
- setup_boolean_blendcoeffs(drawState, op);
- this->drawTexture(accum, temp.texture());
-
- if (0 != devResultBounds->fTop || 0 != devResultBounds->fLeft ||
- 0 != clipDataIn.fOrigin.fX || 0 != clipDataIn.fOrigin.fY) {
- drawState->viewMatrix()->setTranslate(
- SkIntToScalar(-devResultBounds->fLeft-clipDataIn.fOrigin.fX),
- SkIntToScalar(-devResultBounds->fTop-clipDataIn.fOrigin.fY));
- }
-
+ this->mergeMask(accum, temp.texture(), op, maskResultBounds, elementMaskIBounds);
} else {
// all the remaining ops can just be directly draw into
// the accumulation buffer
setup_boolean_blendcoeffs(drawState, op);
this->drawClipShape(accum, clip, *devResultBounds);
}
+ accumClearedToZero = false;
}
*result = accum;
diff --git a/src/gpu/GrClipMaskManager.h b/src/gpu/GrClipMaskManager.h
index d23a525e7a..89b1ef8249 100644
--- a/src/gpu/GrClipMaskManager.h
+++ b/src/gpu/GrClipMaskManager.h
@@ -128,8 +128,11 @@ private:
const SkClipStack::Iter::Clip* clip,
const GrIRect& resultBounds);
- void drawTexture(GrTexture* target,
- GrTexture* texture);
+ void mergeMask(GrTexture* dstMask,
+ GrTexture* srcMask,
+ SkRegion::Op op,
+ const GrIRect& dstBound,
+ const GrIRect& srcBound);
void getTemp(const GrIRect& bounds, GrAutoScratchTexture* temp);
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index 14eeace563..2d79991b3f 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -1435,7 +1435,11 @@ void SkGpuDevice::internalDrawBitmap(const SkBitmap& bitmap,
top = bottom = SkScalarHalf(paintRect.top() + paintRect.bottom());
}
textureDomain.setLTRB(left, top, right, bottom);
- effect.reset(SkNEW_ARGS(GrTextureDomainEffect, (texture, textureDomain, params)));
+ effect.reset(GrTextureDomainEffect::Create(texture,
+ SkMatrix::I(),
+ textureDomain,
+ GrTextureDomainEffect::kClamp_WrapMode,
+ params.isBilerp()));
} else {
effect.reset(SkNEW_ARGS(GrSingleTextureEffect, (texture, params)));
}
diff --git a/src/gpu/effects/GrTextureDomainEffect.cpp b/src/gpu/effects/GrTextureDomainEffect.cpp
index 823b0722a1..e500fad4a8 100644
--- a/src/gpu/effects/GrTextureDomainEffect.cpp
+++ b/src/gpu/effects/GrTextureDomainEffect.cpp
@@ -43,28 +43,40 @@ GrGLTextureDomainEffect::GrGLTextureDomainEffect(const GrBackendEffectFactory& f
}
void GrGLTextureDomainEffect::emitCode(GrGLShaderBuilder* builder,
- const GrEffectStage&,
+ const GrEffectStage& stage,
EffectKey key,
const char* vertexCoords,
const char* outputColor,
const char* inputColor,
const TextureSamplerArray& samplers) {
+ const GrTextureDomainEffect& effect =
+ static_cast<const GrTextureDomainEffect&>(*stage.getEffect());
+
const char* coords;
fEffectMatrix.emitCodeMakeFSCoords2D(builder, key, vertexCoords, &coords);
+ const char* domain;
fNameUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
- kVec4f_GrSLType, "TexDom");
-
- builder->fFSCode.appendf("\tvec2 clampCoord = clamp(%s, %s.xy, %s.zw);\n",
- coords,
- builder->getUniformCStr(fNameUni),
- builder->getUniformCStr(fNameUni));
-
- builder->fFSCode.appendf("\t%s = ", outputColor);
- builder->appendTextureLookupAndModulate(&builder->fFSCode,
- inputColor,
- samplers[0],
- "clampCoord");
- builder->fFSCode.append(";\n");
+ kVec4f_GrSLType, "TexDom", &domain);
+ if (GrTextureDomainEffect::kClamp_WrapMode == effect.wrapMode()) {
+
+ builder->fFSCode.appendf("\tvec2 clampCoord = clamp(%s, %s.xy, %s.zw);\n",
+ coords, domain, domain);
+
+ builder->fFSCode.appendf("\t%s = ", outputColor);
+ builder->appendTextureLookupAndModulate(&builder->fFSCode,
+ inputColor,
+ samplers[0],
+ "clampCoord");
+ builder->fFSCode.append(";\n");
+ } else {
+ GrAssert(GrTextureDomainEffect::kDecal_WrapMode == effect.wrapMode());
+ builder->fFSCode.append("\tbvec4 outside;\n");
+ builder->fFSCode.appendf("\toutside.xy = lessThan(%s, %s.xy);\n", coords, domain);
+ builder->fFSCode.appendf("\toutside.zw = greaterThan(%s, %s.zw);\n", coords, domain);
+ builder->fFSCode.appendf("\t%s = any(outside) ? vec4(0.0, 0.0, 0.0, 0.0) : ", outputColor);
+ builder->appendTextureLookupAndModulate(&builder->fFSCode, inputColor, samplers[0], coords);
+ builder->fFSCode.append(";\n");
+ }
}
void GrGLTextureDomainEffect::setData(const GrGLUniformManager& uman, const GrEffectStage& stage) {
@@ -98,21 +110,41 @@ void GrGLTextureDomainEffect::setData(const GrGLUniformManager& uman, const GrEf
GrGLEffect::EffectKey GrGLTextureDomainEffect::GenKey(const GrEffectStage& stage, const GrGLCaps&) {
const GrTextureDomainEffect& effect =
static_cast<const GrTextureDomainEffect&>(*stage.getEffect());
- return GrGLEffectMatrix::GenKey(effect.getMatrix(), stage.getCoordChangeMatrix(), effect.texture(0));
+ EffectKey key = effect.wrapMode();
+ key <<= GrGLEffectMatrix::kKeyBits;
+ EffectKey matrixKey = GrGLEffectMatrix::GenKey(effect.getMatrix(),
+ stage.getCoordChangeMatrix(),
+ effect.texture(0));
+ return key | matrixKey;
}
///////////////////////////////////////////////////////////////////////////////
-GrTextureDomainEffect::GrTextureDomainEffect(GrTexture* texture, const GrRect& domain)
- : GrSingleTextureEffect(texture)
- , fTextureDomain(domain) {
+GrEffect* GrTextureDomainEffect::Create(GrTexture* texture,
+ const SkMatrix& matrix,
+ const GrRect& domain,
+ WrapMode wrapMode,
+ bool bilerp) {
+ static const SkRect kFullRect = {0, 0, SK_Scalar1, SK_Scalar1};
+ if (kClamp_WrapMode == wrapMode && domain.contains(kFullRect)) {
+ return SkNEW_ARGS(GrSingleTextureEffect, (texture, matrix, bilerp));
+ } else {
+ SkRect clippedDomain;
+ // We don't currently handle domains that are empty or don't intersect the texture.
+ SkAssertResult(clippedDomain.intersect(kFullRect, domain));
+ return SkNEW_ARGS(GrTextureDomainEffect,
+ (texture, matrix, clippedDomain, wrapMode, bilerp));
+ }
}
GrTextureDomainEffect::GrTextureDomainEffect(GrTexture* texture,
+ const SkMatrix& matrix,
const GrRect& domain,
- const GrTextureParams& params)
- : GrSingleTextureEffect(texture, params)
+ WrapMode wrapMode,
+ bool bilerp)
+ : GrSingleTextureEffect(texture, matrix, bilerp)
+ , fWrapMode(wrapMode)
, fTextureDomain(domain) {
}
@@ -143,5 +175,7 @@ GrEffect* GrTextureDomainEffect::TestCreate(SkRandom* random,
domain.fRight = random->nextRangeScalar(domain.fLeft, SK_Scalar1);
domain.fTop = random->nextUScalar1();
domain.fBottom = random->nextRangeScalar(domain.fTop, SK_Scalar1);
- return SkNEW_ARGS(GrTextureDomainEffect, (textures[texIdx], domain));
+ WrapMode wrapMode = random->nextBool() ? kClamp_WrapMode : kDecal_WrapMode;
+ const SkMatrix& matrix = GrEffectUnitTest::TestMatrix(random);
+ return GrTextureDomainEffect::Create(textures[texIdx], matrix, domain, wrapMode);
}
diff --git a/src/gpu/effects/GrTextureDomainEffect.h b/src/gpu/effects/GrTextureDomainEffect.h
index 322f8d05cf..c1ce6d11a6 100644
--- a/src/gpu/effects/GrTextureDomainEffect.h
+++ b/src/gpu/effects/GrTextureDomainEffect.h
@@ -14,15 +14,31 @@
class GrGLTextureDomainEffect;
/**
- * Limits a texture's lookup coordinates to a domain.
+ * Limits a texture's lookup coordinates to a domain. Samples outside the domain are either clamped
+ * the edge of the domain or result in a vec4 of zeros. The domain is clipped to normalized texture
+ * coords ([0,1]x[0,1] square). Bilinear filtering can cause texels outside the domain to affect the
+ * read value unless the caller considers this when calculating the domain. TODO: This should be a
+ * helper that can assist an effect rather than effect unto itself.
*/
class GrTextureDomainEffect : public GrSingleTextureEffect {
public:
- /** Uses default texture params (no filter, clamp) */
- GrTextureDomainEffect(GrTexture*, const GrRect& domain);
-
- GrTextureDomainEffect(GrTexture*, const GrRect& domain, const GrTextureParams& params);
+ /**
+ * If SkShader::kDecal_TileMode sticks then this enum could be replaced by SkShader::TileMode.
+ * We could also consider replacing/augmenting Decal mode with Border mode where the color
+ * outside of the domain is user-specifiable. Decal mode currently has a hard (non-lerped)
+ * transition between the border and the interior.
+ */
+ enum WrapMode {
+ kClamp_WrapMode,
+ kDecal_WrapMode,
+ };
+
+ static GrEffect* Create(GrTexture*,
+ const SkMatrix&,
+ const SkRect& domain,
+ WrapMode,
+ bool bilerp = false);
virtual ~GrTextureDomainEffect();
@@ -33,13 +49,34 @@ public:
virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
virtual bool isEqual(const GrEffect&) const SK_OVERRIDE;
- const GrRect& domain() const { return fTextureDomain; }
+ const SkRect& domain() const { return fTextureDomain; }
+ WrapMode wrapMode() const { return fWrapMode; }
+
+ /* Computes a domain that bounds all the texels in texelRect. Note that with bilerp enabled
+ texels neighboring the domain may be read. */
+ static const SkRect MakeTexelDomain(const GrTexture* texture, const SkIRect& texelRect) {
+ SkScalar wInv = SK_Scalar1 / texture->width();
+ SkScalar hInv = SK_Scalar1 / texture->height();
+ SkRect result = {
+ texelRect.fLeft * wInv,
+ texelRect.fTop * hInv,
+ texelRect.fRight * wInv,
+ texelRect.fBottom * hInv
+ };
+ return result;
+ }
protected:
-
- GrRect fTextureDomain;
+ WrapMode fWrapMode;
+ SkRect fTextureDomain;
private:
+ GrTextureDomainEffect(GrTexture*,
+ const SkMatrix&,
+ const GrRect& domain,
+ WrapMode,
+ bool bilerp);
+
GR_DECLARE_EFFECT_TEST;
typedef GrSingleTextureEffect INHERITED;