[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:
parent
a069c8ff9a
commit
fb30951cd9
155
gm/drawbitmaprect.cpp
Normal file
155
gm/drawbitmaprect.cpp
Normal 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);
|
||||
|
||||
}
|
||||
|
@ -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',
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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];
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user