4x4 SSAA with improvements in determination of when to apply. Still disabled at compile time.
Review URL: http://codereview.appspot.com/4445075/
git-svn-id: http://skia.googlecode.com/svn/trunk@1218 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gpu/src/GrContext.cpp b/gpu/src/GrContext.cpp
index d3904e0..2263172 100644
--- a/gpu/src/GrContext.cpp
+++ b/gpu/src/GrContext.cpp
@@ -26,7 +26,7 @@
#include "GrBufferAllocPool.h"
#include "GrPathRenderer.h"
-#define ENABLE_SSAA 0
+#define ENABLE_OFFSCREEN_AA 0
#define DEFER_TEXT_RENDERING 1
@@ -414,20 +414,49 @@
////////////////////////////////////////////////////////////////////////////////
+bool GrContext::doOffscreenAA(GrDrawTarget* target,
+ const GrPaint& paint,
+ bool isLines) const {
+#if !ENABLE_OFFSCREEN_AA
+ return false;
+#else
+ if (!paint.fAntiAlias) {
+ return false;
+ }
+ if (isLines && fGpu->supportsAALines()) {
+ return false;
+ }
+ if (target->getRenderTarget()->isMultisampled()) {
+ return false;
+ }
+ // we have to be sure that the blend equation is expressible
+ // as simple src / dst coeffecients when the source
+ // is already modulated by the coverage fraction.
+ // We could use dual-source blending to get the correct per-pixel
+ // dst coeffecient for the remaining cases.
+ if (kISC_BlendCoeff != paint.fDstBlendCoeff &&
+ kOne_BlendCoeff != paint.fDstBlendCoeff &&
+ kISA_BlendCoeff != paint.fDstBlendCoeff) {
+ return false;
+ }
+ return true;
+#endif
+}
+
bool GrContext::setupOffscreenAAPass1(GrDrawTarget* target,
bool requireStencil,
+ const GrIRect& boundRect,
OffscreenRecord* record) {
-#if !ENABLE_SSAA
- return false;
-#endif
+ GrAssert(ENABLE_OFFSCREEN_AA);
- GrAssert(NULL == record->fEntry);
+ GrAssert(NULL == record->fEntry0);
+ GrAssert(NULL == record->fEntry1);
- int width = this->getRenderTarget()->width();
- int height = this->getRenderTarget()->height();
+ int boundW = boundRect.width();
+ int boundH = boundRect.height();
+ int size = GrMax(64, (int)GrNextPow2(GrMax(boundW, boundH)));
GrTextureDesc desc;
- desc.fAALevel = kNone_GrAALevel;
if (requireStencil) {
desc.fFlags = kRenderTarget_GrTextureFlagBit;
} else {
@@ -435,63 +464,133 @@
kNoStencil_GrTextureFlagBit;
}
- desc.fWidth = 2 * width;
- desc.fHeight = 2 * height;
desc.fFormat = kRGBA_8888_GrPixelConfig;
- record->fEntry = this->lockKeylessTexture(desc);
- if (NULL == record->fEntry) {
+ int scale;
+ // Using MSAA seems to be slower for some yet unknown reason.
+ if (false && fGpu->supportsFullsceneAA()) {
+ scale = GR_Scalar1;
+ desc.fAALevel = kMed_GrAALevel;
+ } else {
+ scale = 4;
+ desc.fAALevel = kNone_GrAALevel;
+ }
+
+ desc.fWidth = scale * size;
+ desc.fHeight = scale * size;
+
+ record->fEntry0 = this->lockKeylessTexture(desc);
+
+ if (NULL == record->fEntry0) {
return false;
}
- GrRenderTarget* offscreen = record->fEntry->texture()->asRenderTarget();
- GrAssert(NULL != offscreen);
+
+ if (scale > 1) {
+ desc.fWidth /= 2;
+ desc.fHeight /= 2;
+ record->fEntry1 = this->lockKeylessTexture(desc);
+ if (NULL == record->fEntry1) {
+ this->unlockTexture(record->fEntry0);
+ record->fEntry0 = NULL;
+ return false;
+ }
+ }
+
+ GrRenderTarget* offRT0 = record->fEntry0->texture()->asRenderTarget();
+ GrAssert(NULL != offRT0);
target->saveCurrentDrawState(&record->fSavedState);
GrPaint tempPaint;
tempPaint.reset();
SetPaint(tempPaint, target);
- target->setRenderTarget(offscreen);
+ target->setRenderTarget(offRT0);
+ GrMatrix transM;
+ transM.setTranslate(-boundRect.fLeft, -boundRect.fTop);
+ target->postConcatViewMatrix(transM);
GrMatrix scaleM;
- scaleM.setScale(2 * GR_Scalar1, 2 * GR_Scalar1);
+ scaleM.setScale(scale * GR_Scalar1, scale * GR_Scalar1);
target->postConcatViewMatrix(scaleM);
// clip gets applied in second pass
target->disableState(GrDrawTarget::kClip_StateBit);
- target->clear(NULL, 0x0);
+ GrIRect clear(0, 0, scale * boundW, scale * boundH);
+ target->clear(&clear, 0x0);
+
return true;
}
-void GrContext::setupOffscreenAAPass2(GrDrawTarget* target,
- const GrPaint& paint,
- OffscreenRecord* record) {
+void GrContext::offscreenAAPass2(GrDrawTarget* target,
+ const GrPaint& paint,
+ const GrIRect& boundRect,
+ OffscreenRecord* record) {
- GrAssert(NULL != record->fEntry);
- GrTexture* offscreen = record->fEntry->texture();
- GrAssert(NULL != offscreen);
+ GrAssert(NULL != record->fEntry0);
- target->restoreDrawState(record->fSavedState);
-
- target->setTexture(kOffscreenStage, offscreen);
+ bool downsample = NULL != record->fEntry1;
+
GrMatrix sampleM;
- sampleM.setScale(GR_Scalar1 / target->getRenderTarget()->width(),
- GR_Scalar1 / target->getRenderTarget()->height());
- sampleM.preConcat(target->getViewMatrix());
-
- // use bilinear filtering to get downsample
GrSamplerState sampler(GrSamplerState::kClamp_WrapMode,
- GrSamplerState::kClamp_WrapMode,
- sampleM, true);
+ GrSamplerState::kClamp_WrapMode, true);
+
+ GrTexture* src = record->fEntry0->texture();
+ int scale;
+
+ if (downsample) {
+ scale = 2;
+ GrRenderTarget* dst = record->fEntry1->texture()->asRenderTarget();
+
+ // Do 2x2 downsample from first to second
+ target->setTexture(kOffscreenStage, src);
+ target->setRenderTarget(dst);
+ target->setViewMatrix(GrMatrix::I());
+ sampleM.setScale(scale * GR_Scalar1 / src->width(),
+ scale * GR_Scalar1 / src->height());
+ sampler.setMatrix(sampleM);
+ target->setSamplerState(kOffscreenStage, sampler);
+ GrRect rect(0, 0,
+ scale * boundRect.width(),
+ scale * boundRect.height());
+ target->drawSimpleRect(rect, NULL, 1 << kOffscreenStage);
+
+ src = record->fEntry1->texture();
+ } else {
+ scale = 1;
+ GrIRect rect(0, 0, boundRect.width(), boundRect.height());
+ src->asRenderTarget()->overrideResolveRect(rect);
+ }
+
+ // setup for draw back to main RT
+ target->restoreDrawState(record->fSavedState);
+ if (NULL != paint.getTexture()) {
+ GrMatrix invVM;
+ if (target->getViewInverse(&invVM)) {
+ target->preConcatSamplerMatrix(0, invVM);
+ }
+ }
+ target->setViewMatrix(GrMatrix::I());
+
+ target->setTexture(kOffscreenStage, src);
+ sampleM.setScale(scale * GR_Scalar1 / src->width(),
+ scale * GR_Scalar1 / src->height());
+ sampler.setMatrix(sampleM);
+ sampleM.setTranslate(-boundRect.fLeft, -boundRect.fTop);
+ sampler.preConcatMatrix(sampleM);
target->setSamplerState(kOffscreenStage, sampler);
-}
-void GrContext::endOffscreenAA(GrDrawTarget* target, OffscreenRecord* record) {
- this->unlockTexture(record->fEntry);
- record->fEntry = NULL;
+ GrRect dstRect(boundRect);
+ int stages = (1 << kOffscreenStage) | (NULL == paint.getTexture() ? 0 : 1);
+ target->drawSimpleRect(dstRect, NULL, stages);
- target->restoreDrawState(record->fSavedState);
+ this->unlockTexture(record->fEntry0);
+ record->fEntry0 = NULL;
+ if (downsample) {
+ this->unlockTexture(record->fEntry1);
+ record->fEntry1 = NULL;
+ }
+ target->restoreDrawState(record->fSavedState);
}
////////////////////////////////////////////////////////////////////////////////
@@ -838,7 +937,7 @@
fGpu->getUnitSquareVertexBuffer());
GrDrawTarget::AutoViewMatrixRestore avmr(target);
GrMatrix m;
- m.setAll(rect.width(), 0, rect.fLeft,
+ m.setAll(rect.width(), 0, rect.fLeft,
0, rect.height(), rect.fTop,
0, 0, GrMatrix::I()[8]);
@@ -944,15 +1043,9 @@
vertexSize += sizeof(GrColor);
}
- bool doOffscreenAA = false;
+ bool doAA = false;
OffscreenRecord record;
- if (paint.fAntiAlias &&
- !this->getRenderTarget()->isMultisampled() &&
- !(GrIsPrimTypeLines(primitiveType) && fGpu->supportsAALines()) &&
- this->setupOffscreenAAPass1(target, false, &record)) {
- doOffscreenAA = true;
- layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(kOffscreenStage);
- }
+ GrIRect bounds;
if (sizeof(GrPoint) != vertexSize) {
if (!geo.set(target, layout, vertexCount, 0)) {
@@ -978,6 +1071,17 @@
curVertex = (void*)((intptr_t)curVertex + vsize);
}
} else {
+ // we don't do offscreen AA when we have per-vertex tex coords or colors
+ if (this->doOffscreenAA(target, paint, GrIsPrimTypeLines(primitiveType))) {
+ GrRect b;
+ b.setBounds(positions, vertexCount);
+ target->getViewMatrix().mapRect(&b);
+ b.roundOut(&bounds);
+
+ if (this->setupOffscreenAAPass1(target, false, bounds, &record)) {
+ doAA = true;
+ }
+ }
target->setVertexSourceToArray(layout, positions, vertexCount);
}
@@ -985,45 +1089,14 @@
target->setIndexSourceToArray(indices, indexCount);
}
- if (doOffscreenAA) {
- // draw to the offscreen
- if (NULL != indices) {
- target->drawIndexed(primitiveType, 0, 0, vertexCount, indexCount);
- } else {
- target->drawNonIndexed(primitiveType, 0, vertexCount);
- }
- // When there are custom texture coordinates we can't just draw
- // a quad to sample the offscreen. Instead we redraw the geometry to
- // specify the texture coords. This isn't quite right either, primitives
- // will only be eroded at the edges, not expanded into partial pixels.
- bool useRect = 0 == (layout & GrDrawTarget::StageTexCoordVertexLayoutBit(0,0));
- if (useRect) {
- target->setViewMatrix(GrMatrix::I());
- }
- this->setupOffscreenAAPass2(target, paint, &record);
- if (useRect) {
- geo.set(NULL, 0, 0, 0);
- int stages = (NULL != paint.getTexture()) ? 0x1 : 0x0;
- stages |= (1 << kOffscreenStage);
- GrRect dstRect(0, 0,
- target->getRenderTarget()->width(),
- target->getRenderTarget()->height());
- target->drawSimpleRect(dstRect, NULL, stages);
- target->drawSimpleRect(dstRect, NULL, stages);
- } else {
- if (NULL != indices) {
- target->drawIndexed (primitiveType, 0, 0, vertexCount, indexCount);
- } else {
- target->drawNonIndexed(primitiveType, 0, vertexCount);
- }
- }
- this->endOffscreenAA(target, &record);
+ if (NULL != indices) {
+ target->drawIndexed(primitiveType, 0, 0, vertexCount, indexCount);
} else {
- if (NULL != indices) {
- target->drawIndexed(primitiveType, 0, 0, vertexCount, indexCount);
- } else {
- target->drawNonIndexed(primitiveType, 0, vertexCount);
- }
+ target->drawNonIndexed(primitiveType, 0, vertexCount);
+ }
+
+ if (doAA) {
+ this->offscreenAAPass2(target, paint, bounds, &record);
}
}
@@ -1038,28 +1111,39 @@
GrDrawTarget* target = this->prepareToDraw(paint, kUnbuffered_DrawCategory);
GrPathRenderer* pr = this->getPathRenderer(target, path, fill);
- if (paint.fAntiAlias &&
- !this->getRenderTarget()->isMultisampled() &&
- !pr->supportsAA()) {
+ if (!IsFillInverted(fill) && // will be relaxed soon
+ !pr->supportsAA(target, path, fill) &&
+ this->doOffscreenAA(target, paint, kHairLine_PathFill == fill)) {
- OffscreenRecord record;
- bool needsStencil = pr->requiresStencilPass(target, path, fill);
- if (this->setupOffscreenAAPass1(target, needsStencil, &record)) {
- pr->drawPath(target, 0, path, fill, translate);
-
- target->setViewMatrix(GrMatrix::I());
- this->setupOffscreenAAPass2(target, paint, &record);
+ OffscreenRecord record;
+ bool needsStencil = pr->requiresStencilPass(target, path, fill);
- int stages = (NULL != paint.getTexture()) ? 0x1 : 0x0;
- stages |= (1 << kOffscreenStage);
- GrRect dstRect(0, 0,
- target->getRenderTarget()->width(),
- target->getRenderTarget()->height());
- target->drawSimpleRect(dstRect, NULL, stages);
+ // compute bounds as intersection of rt size, clip, and path
+ GrIRect bound(0, 0,
+ target->getRenderTarget()->width(),
+ target->getRenderTarget()->height());
+ if (target->getClip().hasConservativeBounds()) {
+ GrIRect clipIBounds;
+ target->getClip().getConservativeBounds().roundOut(&clipIBounds);
+ if (!bound.intersectWith(clipIBounds)) {
+ return;
+ }
+ }
+ GrRect pathBounds;
+ if (path->getConservativeBounds(&pathBounds)) {
+ GrIRect pathIBounds;
+ target->getViewMatrix().mapRect(&pathBounds, pathBounds);
+ pathBounds.roundOut(&pathIBounds);
+ if (!bound.intersectWith(pathIBounds)) {
+ return;
+ }
+ }
- this->endOffscreenAA(target, &record);
- return;
- }
+ if (this->setupOffscreenAAPass1(target, needsStencil, bound, &record)) {
+ pr->drawPath(target, 0, path, fill, translate);
+ this->offscreenAAPass2(target, paint, bound, &record);
+ return;
+ }
}
GrDrawTarget::StageBitfield enabledStages = 0;
if (NULL != paint.getTexture()) {
@@ -1068,6 +1152,7 @@
pr->drawPath(target, enabledStages, path, fill, translate);
}
+
void GrContext::drawPath(const GrPaint& paint,
const GrPath& path,
GrPathFill fill,