aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar bsalomon@google.com <bsalomon@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2011-11-30 14:13:48 +0000
committerGravatar bsalomon@google.com <bsalomon@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2011-11-30 14:13:48 +0000
commitfb30951cd9346a7a2d36e7d5f81f9e7ee792b669 (patch)
tree6ee44277e649b2c9c1e7a0153754dfbdb681804a
parenta069c8ff9abf00efed85ca0a2df37a7a7f30390e (diff)
[GPU] tile when large bitmap pased drawBitmap and only a small fraction is used
Review URL: http://codereview.appspot.com/5450048/ git-svn-id: http://skia.googlecode.com/svn/trunk@2760 2bbb7eff-a529-9590-31e7-b0007b416f81
-rw-r--r--gm/drawbitmaprect.cpp155
-rw-r--r--gyp/gmslides.gypi1
-rw-r--r--include/gpu/GrContext.h9
-rw-r--r--include/gpu/SkGpuDevice.h6
-rw-r--r--src/gpu/GrContext.cpp10
-rw-r--r--src/gpu/GrResourceCache.cpp4
-rw-r--r--src/gpu/GrResourceCache.h6
-rw-r--r--src/gpu/SkGpuDevice.cpp124
8 files changed, 307 insertions, 8 deletions
diff --git a/gm/drawbitmaprect.cpp b/gm/drawbitmaprect.cpp
new file mode 100644
index 0000000000..1f8210b514
--- /dev/null
+++ b/gm/drawbitmaprect.cpp
@@ -0,0 +1,155 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "gm.h"
+#include "SkShader.h"
+#include "SkColorPriv.h"
+
+// effects
+#include "SkGradientShader.h"
+
+
+namespace skiagm {
+
+static void makebm(SkBitmap* bm, SkBitmap::Config config, int w, int h) {
+ bm->setConfig(config, w, h);
+ bm->allocPixels();
+ bm->eraseColor(0);
+
+ SkCanvas canvas(*bm);
+
+ SkScalar wScalar = SkIntToScalar(w);
+ SkScalar hScalar = SkIntToScalar(h);
+
+ SkPoint pt = { wScalar / 2, hScalar / 2 };
+
+ SkScalar radius = 4 * SkMaxScalar(wScalar, hScalar);
+
+ SkColor colors[] = { SK_ColorRED, SK_ColorYELLOW,
+ SK_ColorGREEN, SK_ColorMAGENTA,
+ SK_ColorBLUE, SK_ColorCYAN,
+ SK_ColorRED};
+
+ SkScalar pos[] = {0,
+ SK_Scalar1 / 6,
+ 2 * SK_Scalar1 / 6,
+ 3 * SK_Scalar1 / 6,
+ 4 * SK_Scalar1 / 6,
+ 5 * SK_Scalar1 / 6,
+ SK_Scalar1};
+
+ SkPaint paint;
+ paint.setShader(SkGradientShader::CreateRadial(
+ pt, radius,
+ colors, pos,
+ SK_ARRAY_COUNT(colors),
+ SkShader::kRepeat_TileMode))->unref();
+ SkRect rect = SkRect::MakeWH(wScalar, hScalar);
+ SkMatrix mat = SkMatrix::I();
+ for (int i = 0; i < 4; ++i) {
+ paint.getShader()->setLocalMatrix(mat);
+ canvas.drawRect(rect, paint);
+ rect.inset(wScalar / 8, hScalar / 8);
+ mat.postScale(SK_Scalar1 / 4, SK_Scalar1 / 4);
+ }
+}
+
+static const int gSize = 1024;
+
+class DrawBitmapRectGM : public GM {
+public:
+ DrawBitmapRectGM() {
+ }
+
+ SkBitmap fLargeBitmap;
+
+protected:
+ SkString onShortName() {
+ return SkString("drawbitmaprect");
+ }
+
+ SkISize onISize() { return make_isize(gSize, gSize); }
+
+ virtual void onDraw(SkCanvas* canvas) {
+ static const int kBmpSize = 2048;
+ if (fLargeBitmap.isNull()) {
+ makebm(&fLargeBitmap,
+ SkBitmap::kARGB_8888_Config,
+ kBmpSize, kBmpSize);
+ }
+ SkRect dstRect = { 0, 0, 64, 64};
+ static const int kMaxSrcRectSize = 1 << (SkNextLog2(kBmpSize) + 2);
+
+ static const int kPadX = 30;
+ static const int kPadY = 40;
+ SkPaint paint;
+ paint.setAlpha(0x20);
+ canvas->drawBitmapRect(fLargeBitmap, NULL,
+ SkRect::MakeWH(gSize * SK_Scalar1,
+ gSize * SK_Scalar1),
+ &paint);
+ canvas->translate(SK_Scalar1 * kPadX / 2,
+ SK_Scalar1 * kPadY / 2);
+ SkPaint blackPaint;
+ SkScalar titleHeight = SK_Scalar1 * 24;
+ blackPaint.setColor(SK_ColorBLACK);
+ blackPaint.setTextSize(titleHeight);
+ blackPaint.setAntiAlias(true);
+ SkString title;
+ title.printf("Bitmap size: %d x %d", kBmpSize, kBmpSize);
+ canvas->drawText(title.c_str(), title.size(), 0,
+ titleHeight, blackPaint);
+
+ canvas->translate(0, SK_Scalar1 * kPadY / 2 + titleHeight);
+ int rowCount = 0;
+ canvas->save();
+ for (int w = 1; w <= kMaxSrcRectSize; w *= 4) {
+ for (int h = 1; h <= kMaxSrcRectSize; h *= 4) {
+
+ SkIRect srcRect = SkIRect::MakeXYWH((kBmpSize - w) / 2,
+ (kBmpSize - h) / 2,
+ w, h);
+ canvas->drawBitmapRect(fLargeBitmap, &srcRect, dstRect);
+
+ SkString label;
+ label.appendf("%d x %d", w, h);
+ blackPaint.setAntiAlias(true);
+ blackPaint.setStyle(SkPaint::kFill_Style);
+ blackPaint.setTextSize(SK_Scalar1 * 10);
+ SkScalar baseline = dstRect.height() +
+ blackPaint.getTextSize() + SK_Scalar1 * 3;
+ canvas->drawText(label.c_str(), label.size(),
+ 0, baseline,
+ blackPaint);
+ blackPaint.setStyle(SkPaint::kStroke_Style);
+ blackPaint.setStrokeWidth(SK_Scalar1);
+ blackPaint.setAntiAlias(false);
+ canvas->drawRect(dstRect, blackPaint);
+
+ canvas->translate(dstRect.width() + SK_Scalar1 * kPadX, 0);
+ ++rowCount;
+ if ((dstRect.width() + kPadX) * rowCount > gSize) {
+ canvas->restore();
+ canvas->translate(0, dstRect.height() + SK_Scalar1 * kPadY);
+ canvas->save();
+ rowCount = 0;
+ }
+ }
+ }
+ }
+
+private:
+ typedef GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new DrawBitmapRectGM; }
+static GMRegistry reg(MyFactory);
+
+}
+
diff --git a/gyp/gmslides.gypi b/gyp/gmslides.gypi
index f5d161e335..4c8f4aba0e 100644
--- a/gyp/gmslides.gypi
+++ b/gyp/gmslides.gypi
@@ -7,6 +7,7 @@
'../gm/blurs.cpp',
'../gm/complexclip.cpp',
'../gm/complexclip2.cpp',
+ '../gm/drawbitmaprect.cpp',
'../gm/emptypath.cpp',
'../gm/filltypes.cpp',
'../gm/filltypespersp.cpp',
diff --git a/include/gpu/GrContext.h b/include/gpu/GrContext.h
index 2106a0236c..bc521590b5 100644
--- a/include/gpu/GrContext.h
+++ b/include/gpu/GrContext.h
@@ -116,6 +116,15 @@ public:
int width,
int height,
const GrSamplerState&);
+ /**
+ * Determines whether a texture is in the cache. If the texture is found it
+ * will not be locked or returned. This call does not affect the priority of
+ * the texture for deletion.
+ */
+ bool isTextureInCache(TextureKey key,
+ int width,
+ int height,
+ const GrSamplerState&) const;
/**
* Create a new entry, based on the specified key and texture, and return
diff --git a/include/gpu/SkGpuDevice.h b/include/gpu/SkGpuDevice.h
index c34cd43489..3afea5e9dc 100644
--- a/include/gpu/SkGpuDevice.h
+++ b/include/gpu/SkGpuDevice.h
@@ -122,6 +122,8 @@ protected:
TexCache lockCachedTexture(const SkBitmap& bitmap,
const GrSamplerState& sampler,
TexType type = kBitmap_TexType);
+ bool isBitmapInTextureCache(const SkBitmap& bitmap,
+ const GrSamplerState& sampler) const;
void unlockCachedTexture(TexCache);
class SkAutoCachedTexture {
@@ -196,6 +198,10 @@ private:
bool bindDeviceAsTexture(GrPaint* paint);
void prepareRenderTarget(const SkDraw&);
+ bool shouldTileBitmap(const SkBitmap& bitmap,
+ const GrSamplerState& sampler,
+ const SkIRect* srcRectPtr,
+ int* tileSize) const;
void internalDrawBitmap(const SkDraw&, const SkBitmap&,
const SkIRect&, const SkMatrix&, GrPaint* grPaint);
diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp
index c69c421ab8..57f9a312b1 100644
--- a/src/gpu/GrContext.cpp
+++ b/src/gpu/GrContext.cpp
@@ -246,6 +246,16 @@ GrContext::TextureCacheEntry GrContext::findAndLockTexture(TextureKey key,
GrResourceCache::kNested_LockType));
}
+bool GrContext::isTextureInCache(TextureKey key,
+ int width,
+ int height,
+ const GrSamplerState& sampler) const {
+ uint32_t v[4];
+ gen_texture_key_values(fGpu, sampler, key, width, height, false, v);
+ GrResourceKey resourceKey(v);
+ return fTextureCache->hasKey(resourceKey);
+}
+
GrResourceEntry* GrContext::addAndLockStencilBuffer(GrStencilBuffer* sb) {
ASSERT_OWNED_RESOURCE(sb);
uint32_t v[4];
diff --git a/src/gpu/GrResourceCache.cpp b/src/gpu/GrResourceCache.cpp
index 3094721cee..afbe9b3aa1 100644
--- a/src/gpu/GrResourceCache.cpp
+++ b/src/gpu/GrResourceCache.cpp
@@ -172,6 +172,10 @@ GrResourceEntry* GrResourceCache::findAndLock(const GrResourceKey& key,
return entry;
}
+bool GrResourceCache::hasKey(const GrResourceKey& key) const {
+ return NULL != fCache.find(key);
+}
+
GrResourceEntry* GrResourceCache::createAndLock(const GrResourceKey& key,
GrResource* resource) {
// we don't expect to create new resources during a purge. In theory
diff --git a/src/gpu/GrResourceCache.h b/src/gpu/GrResourceCache.h
index d3a8f03138..e21c6050a1 100644
--- a/src/gpu/GrResourceCache.h
+++ b/src/gpu/GrResourceCache.h
@@ -232,6 +232,12 @@ public:
GrResourceEntry* createAndLock(const GrResourceKey&, GrResource*);
/**
+ * Determines if the cache contains an entry matching a key. If a matching
+ * entry exists but was detached then it will not be found.
+ */
+ bool hasKey(const GrResourceKey& key) const;
+
+ /**
* Detach removes an entry from the cache. This prevents the entry from
* being found by a subsequent findAndLock() until it is reattached. The
* entry still counts against the cache's budget and should be reattached
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index 869189f286..54965a677a 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -1160,6 +1160,105 @@ void SkGpuDevice::drawPath(const SkDraw& draw, const SkPath& origSrcPath,
fContext->drawPath(grPaint, *pathPtr, fill);
}
+namespace {
+
+inline int get_tile_count(int l, int t, int r, int b, int tileSize) {
+ int tilesX = (r / tileSize) - (l / tileSize) + 1;
+ int tilesY = (b / tileSize) - (t / tileSize) + 1;
+ return tilesX * tilesY;
+}
+
+inline int determine_tile_size(const SkBitmap& bitmap,
+ const SkIRect* srcRectPtr,
+ int maxTextureSize) {
+ static const int kSmallTileSize = 1 << 10;
+ if (maxTextureSize <= kSmallTileSize) {
+ return maxTextureSize;
+ }
+
+ size_t maxTexTotalTileSize;
+ size_t smallTotalTileSize;
+
+ if (NULL == srcRectPtr) {
+ int w = bitmap.width();
+ int h = bitmap.height();
+ maxTexTotalTileSize = get_tile_count(0, 0, w, h, maxTextureSize);
+ smallTotalTileSize = get_tile_count(0, 0, w, h, kSmallTileSize);
+ } else {
+ maxTexTotalTileSize = get_tile_count(srcRectPtr->fLeft,
+ srcRectPtr->fTop,
+ srcRectPtr->fRight,
+ srcRectPtr->fBottom,
+ maxTextureSize);
+ smallTotalTileSize = get_tile_count(srcRectPtr->fLeft,
+ srcRectPtr->fTop,
+ srcRectPtr->fRight,
+ srcRectPtr->fBottom,
+ kSmallTileSize);
+ }
+ maxTexTotalTileSize *= maxTextureSize * maxTextureSize;
+ smallTotalTileSize *= kSmallTileSize * kSmallTileSize;
+
+ if (maxTexTotalTileSize > 2 * smallTotalTileSize) {
+ return kSmallTileSize;
+ } else {
+ return maxTextureSize;
+ }
+}
+}
+
+bool SkGpuDevice::shouldTileBitmap(const SkBitmap& bitmap,
+ const GrSamplerState& sampler,
+ const SkIRect* srcRectPtr,
+ int* tileSize) const {
+ SkASSERT(NULL != tileSize);
+
+ // if bitmap is explictly texture backed then just use the texture
+ if (NULL != bitmap.getTexture()) {
+ return false;
+ }
+ // if it's larger than the max texture size, then we have no choice but
+ // tiling
+ const int maxTextureSize = fContext->getMaxTextureSize();
+ if (bitmap.width() > maxTextureSize ||
+ bitmap.height() > maxTextureSize) {
+ *tileSize = determine_tile_size(bitmap, srcRectPtr, maxTextureSize);
+ return true;
+ }
+ // if we are going to have to draw the whole thing, then don't tile
+ if (NULL == srcRectPtr) {
+ return false;
+ }
+ // if the entire texture is already in our cache then no reason to tile it
+ if (this->isBitmapInTextureCache(bitmap, sampler)) {
+ return false;
+ }
+
+ // At this point we know we could do the draw by uploading the entire bitmap
+ // as a texture. However, if the texture would be large compared to the
+ // cache size and we don't require most of it for this draw then tile to
+ // reduce the amount of upload and cache spill.
+
+ // assumption here is that sw bitmap size is a good proxy for its size as
+ // a texture
+ size_t bmpSize = bitmap.getSize();
+ size_t cacheSize;
+ fContext->getTextureCacheLimits(NULL, &cacheSize);
+ if (bmpSize < cacheSize / 2) {
+ return false;
+ }
+
+ SkFixed fracUsed =
+ SkFixedMul((srcRectPtr->width() << 16) / bitmap.width(),
+ (srcRectPtr->height() << 16) / bitmap.height());
+ if (fracUsed <= SK_FixedHalf) {
+ *tileSize = determine_tile_size(bitmap, srcRectPtr, maxTextureSize);
+ return true;
+ } else {
+ return false;
+ }
+}
+
void SkGpuDevice::drawBitmap(const SkDraw& draw,
const SkBitmap& bitmap,
const SkIRect* srcRectPtr,
@@ -1216,10 +1315,9 @@ void SkGpuDevice::drawBitmap(const SkDraw& draw,
sampler->setFilter(GrSamplerState::kNearest_Filter);
}
- const int maxTextureSize = fContext->getMaxTextureSize();
- if (bitmap.getTexture() || (bitmap.width() <= maxTextureSize &&
- bitmap.height() <= maxTextureSize)) {
- // take the fast case
+ int tileSize;
+ if (!this->shouldTileBitmap(bitmap, *sampler, srcRectPtr, &tileSize)) {
+ // take the simple case
this->internalDrawBitmap(draw, bitmap, srcRect, m, &grPaint);
return;
}
@@ -1243,13 +1341,13 @@ void SkGpuDevice::drawBitmap(const SkDraw& draw,
clipRect.offset(DX, DY);
}
- int nx = bitmap.width() / maxTextureSize;
- int ny = bitmap.height() / maxTextureSize;
+ int nx = bitmap.width() / tileSize;
+ int ny = bitmap.height() / tileSize;
for (int x = 0; x <= nx; x++) {
for (int y = 0; y <= ny; y++) {
SkIRect tileR;
- tileR.set(x * maxTextureSize, y * maxTextureSize,
- (x + 1) * maxTextureSize, (y + 1) * maxTextureSize);
+ tileR.set(x * tileSize, y * tileSize,
+ (x + 1) * tileSize, (y + 1) * tileSize);
if (!SkIRect::Intersects(tileR, clipRect)) {
continue;
}
@@ -1699,6 +1797,16 @@ void SkGpuDevice::unlockCachedTexture(TexCache cache) {
this->context()->unlockTexture(cache);
}
+bool SkGpuDevice::isBitmapInTextureCache(const SkBitmap& bitmap,
+ const GrSamplerState& sampler) const {
+ GrContext::TextureKey key = bitmap.getGenerationID();
+ key |= ((uint64_t) bitmap.pixelRefOffset()) << 32;
+ return this->context()->isTextureInCache(key, bitmap.width(),
+ bitmap.height(), sampler);
+
+}
+
+
SkDevice* SkGpuDevice::onCreateCompatibleDevice(SkBitmap::Config config,
int width, int height,
bool isOpaque,