[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
This commit is contained in:
bsalomon@google.com 2011-11-30 14:13:48 +00:00
parent a069c8ff9a
commit fb30951cd9
8 changed files with 307 additions and 8 deletions

155
gm/drawbitmaprect.cpp Normal file
View File

@ -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);
}

View File

@ -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',

View File

@ -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

View File

@ -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);

View File

@ -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];

View File

@ -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

View File

@ -231,6 +231,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

View File

@ -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,