aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar robertphillips@google.com <robertphillips@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2012-04-27 14:29:26 +0000
committerGravatar robertphillips@google.com <robertphillips@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2012-04-27 14:29:26 +0000
commitf294b773f0b2b9f05eb45b52dd3cb4d97b96af06 (patch)
tree91c255e1a907f9b06b5d8dc7cbed221ee118ed60
parentf6b070da5e337d13bd08ca488a0140d8facef8b8 (diff)
Initial version of AA clip A8/R8 mask creation
-rw-r--r--src/gpu/GrClipMaskManager.cpp312
-rw-r--r--src/gpu/GrClipMaskManager.h25
2 files changed, 329 insertions, 8 deletions
diff --git a/src/gpu/GrClipMaskManager.cpp b/src/gpu/GrClipMaskManager.cpp
index a6c17052e6..be6e19e704 100644
--- a/src/gpu/GrClipMaskManager.cpp
+++ b/src/gpu/GrClipMaskManager.cpp
@@ -12,6 +12,7 @@
#include "GrStencilBuffer.h"
#include "GrPathRenderer.h"
+////////////////////////////////////////////////////////////////////////////////
void ScissoringSettings::setupScissoring(GrGpu* gpu) {
if (!fEnableScissoring) {
gpu->disableScissor();
@@ -21,7 +22,9 @@ void ScissoringSettings::setupScissoring(GrGpu* gpu) {
gpu->enableScissoring(fScissorRect);
}
-// sort out what kind of clip mask needs to be created: A8/R8, stencil or scissor
+////////////////////////////////////////////////////////////////////////////////
+// sort out what kind of clip mask needs to be created: alpha, stencil
+// or scissor
bool GrClipMaskManager::createClipMask(GrGpu* gpu,
const GrClip& clipIn,
ScissoringSettings* scissorSettings) {
@@ -30,6 +33,7 @@ bool GrClipMaskManager::createClipMask(GrGpu* gpu,
scissorSettings->fEnableScissoring = false;
fClipMaskInStencil = false;
+ fClipMaskInAlpha = false;
GrDrawState* drawState = gpu->drawState();
if (!drawState->isClipState()) {
@@ -41,6 +45,24 @@ bool GrClipMaskManager::createClipMask(GrGpu* gpu,
// GrDrawTarget should have filtered this for us
GrAssert(NULL != rt);
+#if GR_AA_CLIP
+ // If MSAA is enabled use the (faster) stencil path for AA clipping
+ // otherwise the alpha clip mask is our only option
+ if (clipIn.requiresAA() && 0 == rt->numSamples()) {
+ // Since we are going to create a destination texture of the correct
+ // size for the mask (rather than being bound by the size of the
+ // render target) we aren't going to use scissoring like the stencil
+ // path does (see scissorSettings below)
+ if (this->createAlphaClipMask(gpu, clipIn)) {
+ fClipMaskInAlpha = true;
+ return true;
+ }
+
+ // if alpha clip mask creation fails fall through to the stencil
+ // buffer method
+ }
+#endif // GR_AA_CLIP
+
GrRect bounds;
GrRect rtRect;
rtRect.setLTRB(0, 0,
@@ -85,6 +107,7 @@ bool GrClipMaskManager::createClipMask(GrGpu* gpu,
#endif
namespace {
+////////////////////////////////////////////////////////////////////////////////
// determines how many elements at the head of the clip can be skipped and
// whether the initial clear should be to the inside- or outside-the-clip value,
// and what op should be used to draw the first element that isn't skipped.
@@ -172,8 +195,279 @@ int process_initial_clip_elements(const GrClip& clip,
}
return done ? curr-1 : count;
}
+
+}
+
+
+namespace {
+
+////////////////////////////////////////////////////////////////////////////////
+// set up the OpenGL blend function to perform the specified
+// boolean operation for alpha clip mask creation
+void setUpBooleanBlendCoeffs(GrDrawState* drawState, SkRegion::Op op) {
+
+ switch (op) {
+ case SkRegion::kReplace_Op:
+ drawState->setBlendFunc(kOne_BlendCoeff, kZero_BlendCoeff);
+ break;
+ case SkRegion::kIntersect_Op:
+ drawState->setBlendFunc(kDC_BlendCoeff, kZero_BlendCoeff);
+ break;
+ case SkRegion::kUnion_Op:
+ drawState->setBlendFunc(kOne_BlendCoeff, kISC_BlendCoeff);
+ break;
+ case SkRegion::kXOR_Op:
+ drawState->setBlendFunc(kIDC_BlendCoeff, kISC_BlendCoeff);
+ break;
+ case SkRegion::kDifference_Op:
+ drawState->setBlendFunc(kZero_BlendCoeff, kISC_BlendCoeff);
+ break;
+ case SkRegion::kReverseDifference_Op:
+ drawState->setBlendFunc(kIDC_BlendCoeff, kZero_BlendCoeff);
+ break;
+ default:
+ GrAssert(false);
+ break;
+ }
+}
+
+}
+
+////////////////////////////////////////////////////////////////////////////////
+bool GrClipMaskManager::drawPath(GrGpu* gpu,
+ const GrPath& path,
+ GrPathFill fill,
+ bool doAA) {
+
+ GrPathRenderer* pr = this->getClipPathRenderer(gpu, path, fill, doAA);
+ if (NULL == pr) {
+ return false;
+ }
+
+ pr->drawPath(path, fill, NULL, gpu, 0, doAA);
+ return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+bool GrClipMaskManager::drawClipShape(GrGpu* gpu,
+ GrTexture* target,
+ const GrClip& clipIn,
+ int index) {
+ GrDrawState* drawState = gpu->drawState();
+ GrAssert(NULL != drawState);
+
+ drawState->setRenderTarget(target->asRenderTarget());
+
+ if (kRect_ClipType == clipIn.getElementType(index)) {
+ if (clipIn.getDoAA(index)) {
+ // convert the rect to a path for AA
+ SkPath temp;
+ temp.addRect(clipIn.getRect(index));
+
+ return this->drawPath(gpu, temp,
+ kEvenOdd_PathFill, clipIn.getDoAA(index));
+ } else {
+ gpu->drawSimpleRect(clipIn.getRect(index), NULL, 0);
+ }
+ } else {
+ return this->drawPath(gpu,
+ clipIn.getPath(index),
+ clipIn.getPathFill(index),
+ clipIn.getDoAA(index));
+ }
+ return true;
+}
+
+void GrClipMaskManager::drawTexture(GrGpu* gpu,
+ GrTexture* target,
+ const GrRect& rect,
+ GrTexture* texture) {
+ GrDrawState* drawState = gpu->drawState();
+ GrAssert(NULL != drawState);
+
+ // no AA here since it is encoded in the texture
+ drawState->setRenderTarget(target->asRenderTarget());
+
+ GrMatrix sampleM;
+ sampleM.setIDiv(texture->width(), texture->height());
+ drawState->setTexture(0, texture);
+
+ drawState->sampler(0)->reset(GrSamplerState::kClamp_WrapMode,
+ GrSamplerState::kNearest_Filter,
+ sampleM);
+
+ gpu->drawSimpleRect(rect, NULL, 1 << 0);
+
+ drawState->setTexture(0, NULL);
+}
+
+namespace {
+
+void clear(GrGpu* gpu,
+ GrTexture* target,
+ const GrRect& bounds,
+ GrColor color) {
+ GrDrawState* drawState = gpu->drawState();
+ GrAssert(NULL != drawState);
+
+ // zap entire target to specified color
+ drawState->setRenderTarget(target->asRenderTarget());
+ gpu->clear(NULL, color);
+}
+
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Create a 8-bit clip mask in alpha
+bool GrClipMaskManager::createAlphaClipMask(GrGpu* gpu, const GrClip& clipIn) {
+
+ GrDrawState* origDrawState = gpu->drawState();
+ GrAssert(origDrawState->isClipState());
+
+ GrRenderTarget* rt = origDrawState->getRenderTarget();
+ GrAssert(NULL != rt);
+
+ GrRect rtRect;
+ rtRect.setLTRB(0, 0,
+ GrIntToScalar(rt->width()), GrIntToScalar(rt->height()));
+
+ // unlike the stencil path the alpha path is not bound to the size of the
+ // render target - determine the minimum size required for the mask
+ GrRect bounds;
+
+ if (clipIn.hasConservativeBounds()) {
+ bounds = clipIn.getConservativeBounds();
+ if (!bounds.intersect(rtRect)) {
+ // the mask will be empty in this case
+ GrAssert(false);
+ bounds.setEmpty();
+ }
+ } else {
+ // still locked to the size of the render target
+ bounds = rtRect;
+ }
+
+ bounds.roundOut();
+
+ // need to outset a pixel since the standard bounding box computation
+ // path doesn't leave any room for antialiasing (esp. w.r.t. rects)
+ bounds.outset(SkIntToScalar(1), SkIntToScalar(1));
+
+ GrAssert(SkScalarIsInt(bounds.width()));
+ GrAssert(SkScalarIsInt(bounds.height()));
+
+ GrTextureDesc desc = {
+ kRenderTarget_GrTextureFlagBit,
+ SkScalarCeilToInt(bounds.width()),
+ SkScalarCeilToInt(bounds.height()),
+ kAlpha_8_GrPixelConfig,
+ 0 // samples
+ };
+
+ GrRect newRTBounds;
+ newRTBounds.setLTRB(0, 0, bounds.width(), bounds.height());
+
+ GrTexture* accum = gpu->createTexture(desc, NULL, 0);
+ GrTexture* temp = gpu->createTexture(desc, NULL, 0);
+ if (NULL == accum || NULL == temp) {
+ // TODO: free up accum & temp here!
+ fClipMaskInAlpha = false;
+ return false;
+ }
+
+ GrDrawTarget::AutoStateRestore asr(gpu, GrDrawTarget::kReset_ASRInit);
+ GrDrawState* drawState = gpu->drawState();
+
+ GrDrawTarget::AutoGeometryPush agp(gpu);
+
+ int count = clipIn.getElementCount();
+
+ if (0 != bounds.fTop || 0 != bounds.fLeft) {
+ // 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
+ GrMatrix m;
+
+ m.setTranslate(-bounds.fLeft, -bounds.fTop);
+
+ drawState->preConcatViewMatrix(m);
+ }
+
+ bool clearToInside;
+ SkRegion::Op startOp = SkRegion::kReplace_Op; // suppress warning
+ int start = process_initial_clip_elements(clipIn,
+ bounds,
+ &clearToInside,
+ &startOp);
+
+ clear(gpu, accum, newRTBounds, clearToInside ? 0xffffffff : 0x00000000);
+
+ // walk through each clip element and perform its set op
+ for (int c = start; c < count; ++c) {
+
+ SkRegion::Op op = (c == start) ? startOp : clipIn.getOp(c);
+
+ if (SkRegion::kReplace_Op == op) {
+ // TODO: replace is actually a lot faster then intersection
+ // for this path - refactor the stencil path so it can handle
+ // replace ops and alter GrClip to allow them through
+
+ // clear the accumulator and draw the new object directly into it
+ clear(gpu, accum, newRTBounds, 0x00000000);
+
+ setUpBooleanBlendCoeffs(drawState, op);
+ this->drawClipShape(gpu, accum, clipIn, c);
+
+ } else if (SkRegion::kReverseDifference_Op == op ||
+ SkRegion::kIntersect_Op == op) {
+ // there is no point in intersecting a screen filling rectangle.
+ if (SkRegion::kIntersect_Op == op &&
+ kRect_ClipType == clipIn.getElementType(c) &&
+ clipIn.getRect(c).contains(bounds)) {
+ continue;
+ }
+
+ // clear the temp target & draw into it
+ clear(gpu, temp, newRTBounds, 0x00000000);
+
+ setUpBooleanBlendCoeffs(drawState, SkRegion::kReplace_Op);
+ this->drawClipShape(gpu, temp, clipIn, c);
+
+ // TODO: rather than adding these two translations here
+ // compute the bounding box needed to render the texture
+ // into temp
+ if (0 != bounds.fTop || 0 != bounds.fLeft) {
+ GrMatrix m;
+
+ m.setTranslate(bounds.fLeft, bounds.fTop);
+
+ drawState->preConcatViewMatrix(m);
+ }
+
+ // Now draw into the accumulator using the real operation
+ // and the temp buffer as a texture
+ setUpBooleanBlendCoeffs(drawState, op);
+ this->drawTexture(gpu, accum, newRTBounds, temp);
+
+ if (0 != bounds.fTop || 0 != bounds.fLeft) {
+ GrMatrix m;
+
+ m.setTranslate(-bounds.fLeft, -bounds.fTop);
+
+ drawState->preConcatViewMatrix(m);
+ }
+
+ } else {
+ // all the remaining ops can just be directly draw into
+ // the accumulation buffer
+ setUpBooleanBlendCoeffs(drawState, op);
+ this->drawClipShape(gpu, accum, clipIn, c);
+ }
+ }
+
+ return true;
}
+////////////////////////////////////////////////////////////////////////////////
// Create a 1-bit clip mask in the stencil buffer
bool GrClipMaskManager::createStencilClipMask(GrGpu* gpu,
const GrClip& clipIn,
@@ -248,6 +542,8 @@ bool GrClipMaskManager::createStencilClipMask(GrGpu* gpu,
// without extra passes to
// resolve in/out status.
+ SkRegion::Op op = (c == start) ? startOp : clipCopy.getOp(c);
+
GrPathRenderer* pr = NULL;
const GrPath* clipPath = NULL;
if (kRect_ClipType == clipCopy.getElementType(c)) {
@@ -256,7 +552,7 @@ bool GrClipMaskManager::createStencilClipMask(GrGpu* gpu,
fillInverted = false;
// there is no point in intersecting a screen filling
// rectangle.
- if (SkRegion::kIntersect_Op == clipCopy.getOp(c) &&
+ if (SkRegion::kIntersect_Op == op &&
clipCopy.getRect(c).contains(rtRect)) {
continue;
}
@@ -265,7 +561,7 @@ bool GrClipMaskManager::createStencilClipMask(GrGpu* gpu,
fillInverted = GrIsFillInverted(fill);
fill = GrNonInvertedFill(fill);
clipPath = &clipCopy.getPath(c);
- pr = this->getClipPathRenderer(gpu, *clipPath, fill);
+ pr = this->getClipPathRenderer(gpu, *clipPath, fill, false);
if (NULL == pr) {
fClipMaskInStencil = false;
gpu->setClip(clipCopy); // restore to the original
@@ -275,7 +571,6 @@ bool GrClipMaskManager::createStencilClipMask(GrGpu* gpu,
!pr->requiresStencilPass(*clipPath, fill, gpu);
}
- SkRegion::Op op = (c == start) ? startOp : clipCopy.getOp(c);
int passes;
GrStencilSettings stencilSettings[GrStencilSettings::kMaxStencilClipPasses];
@@ -342,17 +637,20 @@ bool GrClipMaskManager::createStencilClipMask(GrGpu* gpu,
return true;
}
+////////////////////////////////////////////////////////////////////////////////
GrPathRenderer* GrClipMaskManager::getClipPathRenderer(GrGpu* gpu,
const GrPath& path,
- GrPathFill fill) {
+ GrPathFill fill,
+ bool antiAlias) {
if (NULL == fPathRendererChain) {
fPathRendererChain =
new GrPathRendererChain(gpu->getContext(),
- GrPathRendererChain::kNonAAOnly_UsageFlag);
+ GrPathRendererChain::kNone_UsageFlag);
}
- return fPathRendererChain->getPathRenderer(path, fill, gpu, false);
+ return fPathRendererChain->getPathRenderer(path, fill, gpu, antiAlias);
}
+////////////////////////////////////////////////////////////////////////////////
void GrClipMaskManager::freeResources() {
// in case path renderer has any GrResources, start from scratch
GrSafeSetNull(fPathRendererChain);
diff --git a/src/gpu/GrClipMaskManager.h b/src/gpu/GrClipMaskManager.h
index d3f0d159e9..3e7b88eaab 100644
--- a/src/gpu/GrClipMaskManager.h
+++ b/src/gpu/GrClipMaskManager.h
@@ -10,12 +10,15 @@
#define GrClipMaskManager_DEFINED
#include "GrRect.h"
+#include "GrPath.h"
class GrGpu;
class GrClip;
class GrPathRenderer;
class GrPathRendererChain;
class SkPath;
+class GrTexture;
+class GrDrawState;
/**
* Scissoring needs special handling during stencil clip mask creation
@@ -43,6 +46,7 @@ class GrClipMaskManager {
public:
GrClipMaskManager()
: fClipMaskInStencil(false)
+ , fClipMaskInAlpha(false)
, fPathRendererChain(NULL) {
}
@@ -53,6 +57,7 @@ public:
void freeResources();
bool isClipInStencil() const { return fClipMaskInStencil; }
+ bool isClipInAlpha() const { return fClipMaskInAlpha; }
void resetMask() {
fClipMaskInStencil = false;
@@ -61,6 +66,7 @@ public:
protected:
private:
bool fClipMaskInStencil; // is the clip mask in the stencil buffer?
+ bool fClipMaskInAlpha; // is the clip mask in an alpha texture?
// must be instantiated after GrGpu object has been given its owning
// GrContext ptr. (GrGpu is constructed first then handed off to GrContext).
@@ -70,11 +76,28 @@ private:
const GrClip& clip,
const GrRect& bounds,
ScissoringSettings* scissorSettings);
+ bool createAlphaClipMask(GrGpu* gpu, const GrClip& clipIn);
+
+ bool drawPath(GrGpu* gpu,
+ const GrPath& path,
+ GrPathFill fill,
+ bool doAA);
+
+ bool drawClipShape(GrGpu* gpu,
+ GrTexture* target,
+ const GrClip& clipIn,
+ int index);
+
+ void drawTexture(GrGpu* gpu,
+ GrTexture* target,
+ const GrRect& rect,
+ GrTexture* texture);
// determines the path renderer used to draw a clip path element.
GrPathRenderer* getClipPathRenderer(GrGpu* gpu,
const SkPath& path,
- GrPathFill fill);
+ GrPathFill fill,
+ bool antiAlias);
};