First pass at improving temporary tex/rt reuse
Review URL: http://codereview.appspot.com/4625043/
git-svn-id: http://skia.googlecode.com/svn/trunk@1616 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gpu/include/GrContext.h b/gpu/include/GrContext.h
index 5dfe2c9..8d482e9 100644
--- a/gpu/include/GrContext.h
+++ b/gpu/include/GrContext.h
@@ -116,6 +116,16 @@
GrTextureEntry* lockKeylessTexture(const GrTextureDesc& desc);
/**
+ * Finds a texture that approximately matches the descriptor. Will be
+ * at least as large in width and height as desc specifies. If desc
+ * specifies that texture is a render target then result will be a
+ * render target. If desc specifies a render target and doesn't set the
+ * no stencil flag then result will have a stencil. Format and aa level
+ * will always match.
+ */
+ GrTextureEntry* findApproximateKeylessTexture(const GrTextureDesc& desc);
+
+ /**
* When done with an entry, call unlockTexture(entry) on it, which returns
* it to the cache, where it may be purged.
*/
diff --git a/gpu/include/GrTypes.h b/gpu/include/GrTypes.h
index 01b8e25..76e01b0 100644
--- a/gpu/include/GrTypes.h
+++ b/gpu/include/GrTypes.h
@@ -146,6 +146,11 @@
return n ? (1 << (32 - Gr_clz(n - 1))) : 1;
}
+static inline int GrNextPow2(int n) {
+ GrAssert(n >= 0); // this impl only works for non-neg.
+ return n ? (1 << (32 - Gr_clz(n - 1))) : 1;
+}
+
///////////////////////////////////////////////////////////////////////////////
/**
diff --git a/gpu/src/GrContext.cpp b/gpu/src/GrContext.cpp
index ab5ac06..56ebb68 100644
--- a/gpu/src/GrContext.cpp
+++ b/gpu/src/GrContext.cpp
@@ -337,6 +337,75 @@
return entry;
}
+GrTextureEntry* GrContext::findApproximateKeylessTexture(
+ const GrTextureDesc& inDesc) {
+ GrTextureDesc desc = inDesc;
+ // bin by pow2 with a reasonable min
+ static const int MIN_SIZE = 256;
+ desc.fWidth = GrMax(MIN_SIZE, GrNextPow2(desc.fWidth));
+ desc.fHeight = GrMax(MIN_SIZE, GrNextPow2(desc.fHeight));
+
+ uint32_t p0 = desc.fFormat;
+ uint32_t p1 = (desc.fAALevel << 16) | desc.fFlags;
+
+ GrTextureEntry* entry;
+ bool keepTrying = true;
+ int origWidth = desc.fWidth;
+ int origHeight = desc.fHeight;
+ bool doubledW = false;
+ bool doubledH = false;
+
+ do {
+ GrTextureKey key(p0, p1, desc.fWidth, desc.fHeight);
+ this->finalizeTextureKey(&key, GrSamplerState::ClampNoFilter(), true);
+ entry = fTextureCache->findAndLock(key);
+
+ // if we miss, relax the fit of the flags...
+ // then try doubling width... then height.
+ if (NULL != entry) {
+ break;
+ }
+ if (!(desc.fFlags & kRenderTarget_GrTextureFlagBit)) {
+ desc.fFlags = desc.fFlags | kRenderTarget_GrTextureFlagBit;
+ } else if (desc.fFlags & kNoStencil_GrTextureFlagBit) {
+ desc.fFlags = desc.fFlags & ~kNoStencil_GrTextureFlagBit;
+ } else if (!doubledW) {
+ desc.fFlags = inDesc.fFlags;
+ desc.fWidth *= 2;
+ doubledW = true;
+ } else if (!doubledH) {
+ desc.fFlags = inDesc.fFlags;
+ desc.fWidth = origWidth;
+ desc.fHeight *= 2;
+ doubledH = true;
+ } else {
+ break;
+ }
+
+ } while (true);
+
+ if (NULL == entry) {
+ desc.fFlags = inDesc.fFlags;
+ desc.fWidth = origWidth;
+ desc.fHeight = origHeight;
+ GrTexture* texture = fGpu->createTexture(desc, NULL, 0);
+ if (NULL != texture) {
+ GrTextureKey key(p0, p1, desc.fWidth, desc.fHeight);
+ this->finalizeTextureKey(&key, GrSamplerState::ClampNoFilter(),
+ true);
+ entry = fTextureCache->createAndLock(key, texture);
+ }
+ }
+
+ // If the caller gives us the same desc/sampler twice we don't want
+ // to return the same texture the second time (unless it was previously
+ // released). So we detach the entry from the cache and reattach at release.
+ if (NULL != entry) {
+ fTextureCache->detach(entry);
+ }
+ return entry;
+}
+
void GrContext::unlockTexture(GrTextureEntry* entry) {
if (kKeylessBit & entry->key().getPrivateBits()) {
fTextureCache->reattachAndUnlock(entry);
@@ -466,7 +535,8 @@
k4x4SinglePass_Downsample,
kFSAA_Downsample
} fDownsample;
- int fTileSize;
+ int fTileSizeX;
+ int fTileSizeY;
int fTileCountX;
int fTileCountY;
int fScale;
@@ -518,14 +588,11 @@
int boundW = boundRect.width();
int boundH = boundRect.height();
- record->fTileSize = (int)GrNextPow2(GrMax(boundW, boundH));
- record->fTileSize = GrMax(64, record->fTileSize);
- record->fTileSize = GrMin(fMaxOffscreenAASize, record->fTileSize);
-
- record->fTileCountX = GrIDivRoundUp(boundW, record->fTileSize);
- record->fTileCountY = GrIDivRoundUp(boundH, record->fTileSize);
-
GrTextureDesc desc;
+
+ desc.fWidth = GrMin(fMaxOffscreenAASize, boundW);
+ desc.fHeight = GrMin(fMaxOffscreenAASize, boundH);
+
if (requireStencil) {
desc.fFlags = kRenderTarget_GrTextureFlagBit;
} else {
@@ -548,26 +615,41 @@
GR_STATIC_ASSERT(4 == OFFSCREEN_SSAA_SCALE);
desc.fAALevel = kNone_GrAALevel;
}
-
- desc.fWidth = record->fScale * record->fTileSize;
- desc.fHeight = record->fScale * record->fTileSize;
- record->fEntry0 = this->lockKeylessTexture(desc);
+ desc.fWidth *= record->fScale;
+ desc.fHeight *= record->fScale;
+ record->fEntry0 = this->findApproximateKeylessTexture(desc);
if (NULL == record->fEntry0) {
return false;
}
+ // the approximate lookup might have given us some slop space, might as well
+ // use it when computing the tiles size.
+ // these are scale values, will adjust after considering
+ // the possible second offscreen.
+ record->fTileSizeX = record->fEntry0->texture()->width();
+ record->fTileSizeY = record->fEntry0->texture()->height();
if (OffscreenRecord::k4x4TwoPass_Downsample == record->fDownsample) {
desc.fWidth /= 2;
desc.fHeight /= 2;
- record->fEntry1 = this->lockKeylessTexture(desc);
+ record->fEntry1 = this->findApproximateKeylessTexture(desc);
if (NULL == record->fEntry1) {
this->unlockTexture(record->fEntry0);
record->fEntry0 = NULL;
return false;
}
+ record->fTileSizeX = GrMin(record->fTileSizeX,
+ 2 * record->fEntry0->texture()->width());
+ record->fTileSizeY = GrMin(record->fTileSizeY,
+ 2 * record->fEntry0->texture()->height());
}
+ record->fTileSizeX /= record->fScale;
+ record->fTileSizeY /= record->fScale;
+
+ record->fTileCountX = GrIDivRoundUp(boundW, record->fTileSizeX);
+ record->fTileCountY = GrIDivRoundUp(boundH, record->fTileSizeY);
+
target->saveCurrentDrawState(&record->fSavedState);
return true;
}
@@ -586,8 +668,8 @@
target->setRenderTarget(offRT0);
GrMatrix transM;
- int left = boundRect.fLeft + tileX * record->fTileSize;
- int top = boundRect.fTop + tileY * record->fTileSize;
+ int left = boundRect.fLeft + tileX * record->fTileSizeX;
+ int top = boundRect.fTop + tileY * record->fTileSizeY;
transM.setTranslate(-left * GR_Scalar1, -top * GR_Scalar1);
target->postConcatViewMatrix(transM);
GrMatrix scaleM;
@@ -598,9 +680,9 @@
target->disableState(GrDrawTarget::kClip_StateBit);
int w = (tileX == record->fTileCountX-1) ? boundRect.fRight - left :
- record->fTileSize;
+ record->fTileSizeX;
int h = (tileY == record->fTileCountY-1) ? boundRect.fBottom - top :
- record->fTileSize;
+ record->fTileSizeY;
GrIRect clear = SkIRect::MakeWH(record->fScale * w,
record->fScale * h);
#if 0
@@ -627,14 +709,14 @@
GrAssert(NULL != record->fEntry0);
GrIRect tileRect;
- tileRect.fLeft = boundRect.fLeft + tileX * record->fTileSize;
- tileRect.fTop = boundRect.fTop + tileY * record->fTileSize,
+ tileRect.fLeft = boundRect.fLeft + tileX * record->fTileSizeX;
+ tileRect.fTop = boundRect.fTop + tileY * record->fTileSizeY,
tileRect.fRight = (tileX == record->fTileCountX-1) ?
boundRect.fRight :
- tileRect.fLeft + record->fTileSize;
+ tileRect.fLeft + record->fTileSizeX;
tileRect.fBottom = (tileY == record->fTileCountY-1) ?
boundRect.fBottom :
- tileRect.fTop + record->fTileSize;
+ tileRect.fTop + record->fTileSizeY;
GrSamplerState::Filter filter;
if (OffscreenRecord::k4x4SinglePass_Downsample == record->fDownsample) {
diff --git a/gpu/src/GrPathUtils.cpp b/gpu/src/GrPathUtils.cpp
index 1fb043c..b93c8a9 100644
--- a/gpu/src/GrPathUtils.cpp
+++ b/gpu/src/GrPathUtils.cpp
@@ -19,7 +19,7 @@
const GrScalar GrPathUtils::gTolerance = GR_Scalar1;
-static const uint32_t MAX_POINTS_PER_CURVE = 1 << 10;
+static const int MAX_POINTS_PER_CURVE = 1 << 10;
uint32_t GrPathUtils::quadraticPointCount(const GrPoint points[],
GrScalar tol) {
diff --git a/include/gpu/SkGpuDevice.h b/include/gpu/SkGpuDevice.h
index 601da09..96afa08 100644
--- a/include/gpu/SkGpuDevice.h
+++ b/include/gpu/SkGpuDevice.h
@@ -35,11 +35,13 @@
/**
* The SkGpuDevice will render to the GrRenderTarget, or if the paremeter is
* null it will create its own render target and manage that target's
- * lifetime.
+ * lifetime. Setting isSaveLayer to true is for internal use and may cause
+ * problems when using the device's bitmap as a src if used externally.
*/
SkGpuDevice(GrContext*,
const SkBitmap& bitmap,
- GrRenderTarget* renderTargetOrNull);
+ GrRenderTarget* renderTargetOrNull,
+ bool isSaveLayer = false);
/**
* Magic value that can be passed to constructor. Causes
@@ -128,7 +130,8 @@
TexCache* lockCachedTexture(const SkBitmap& bitmap,
const GrSamplerState& sampler,
GrTexture** texture,
- bool forDeviceRenderTarget = false);
+ bool forDeviceRenderTarget = false,
+ bool isSaveLayer = false);
void unlockCachedTexture(TexCache*);
class SkAutoCachedTexture {
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index d1cb5b9..114573a 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -106,7 +106,8 @@
SkGpuDevice::SkGpuDevice(GrContext* context,
const SkBitmap& bitmap,
- GrRenderTarget* renderTargetOrNull)
+ GrRenderTarget* renderTargetOrNull,
+ bool isSaveLayer)
: SkDevice(NULL, bitmap, (NULL == renderTargetOrNull)) {
fNeedPrepareRenderTarget = false;
@@ -1087,7 +1088,8 @@
return;
}
- SkASSERT(NULL != grPaint.getTexture(0));
+ GrTexture* devTex = grPaint.getTexture(0);
+ SkASSERT(NULL != devTex);
const SkBitmap& bm = dev->accessBitmap(false);
int w = bm.width();
@@ -1097,12 +1099,16 @@
grPaint.getTextureSampler(kBitmapTextureIdx)->setClampNoFilter();
- fContext->drawRectToRect(grPaint,
- GrRect::MakeXYWH(GrIntToScalar(x),
- GrIntToScalar(y),
- GrIntToScalar(w),
- GrIntToScalar(h)),
- GrRect::MakeWH(GR_Scalar1, GR_Scalar1));
+ GrRect dstRect = GrRect::MakeXYWH(GrIntToScalar(x),
+ GrIntToScalar(y),
+ GrIntToScalar(w),
+ GrIntToScalar(h));
+ // The device being drawn may not fill up its texture (saveLayer uses
+ // the approximate ).
+ GrRect srcRect = GrRect::MakeWH(GR_Scalar1 * w / devTex->width(),
+ GR_Scalar1 * h / devTex->height());
+
+ fContext->drawRectToRect(grPaint, dstRect, srcRect);
}
///////////////////////////////////////////////////////////////////////////////
@@ -1342,7 +1348,8 @@
SkGpuDevice::TexCache* SkGpuDevice::lockCachedTexture(const SkBitmap& bitmap,
const GrSamplerState& sampler,
GrTexture** texture,
- bool forDeviceRenderTarget) {
+ bool forDeviceRenderTarget,
+ bool isSaveLayer) {
GrTexture* newTexture = NULL;
GrTextureEntry* entry = NULL;
GrContext* ctx = this->context();
@@ -1355,7 +1362,14 @@
bitmap.height(),
SkGr::Bitmap2PixelConfig(bitmap)
};
- entry = ctx->lockKeylessTexture(desc);
+ if (isSaveLayer) {
+ // we know layers will only be drawn through drawDevice.
+ // drawDevice has been made to work with content embedded in a
+ // larger texture so its okay to use the approximate version.
+ entry = ctx->findApproximateKeylessTexture(desc);
+ } else {
+ entry = ctx->lockKeylessTexture(desc);
+ }
} else {
uint32_t p0, p1;
p0 = bitmap.getGenerationID();