diff options
author | bsalomon@google.com <bsalomon@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2011-11-30 14:13:48 +0000 |
---|---|---|
committer | bsalomon@google.com <bsalomon@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2011-11-30 14:13:48 +0000 |
commit | fb30951cd9346a7a2d36e7d5f81f9e7ee792b669 (patch) | |
tree | 6ee44277e649b2c9c1e7a0153754dfbdb681804a | |
parent | a069c8ff9abf00efed85ca0a2df37a7a7f30390e (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.cpp | 155 | ||||
-rw-r--r-- | gyp/gmslides.gypi | 1 | ||||
-rw-r--r-- | include/gpu/GrContext.h | 9 | ||||
-rw-r--r-- | include/gpu/SkGpuDevice.h | 6 | ||||
-rw-r--r-- | src/gpu/GrContext.cpp | 10 | ||||
-rw-r--r-- | src/gpu/GrResourceCache.cpp | 4 | ||||
-rw-r--r-- | src/gpu/GrResourceCache.h | 6 | ||||
-rw-r--r-- | src/gpu/SkGpuDevice.cpp | 124 |
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, |