Adding the notion of a volatile bitmap to SkBitmap.
Volatility is a hint that indicates that the contents of a bitmap are ephemeral. SkGpuDevice will not preserve volatile bitmaps in its texture cache, and will use textures from a pool of keyless (recyclable) textures to avoid the performance hit of texture allocation and release. A subsequent change is required in webkit in order to take advantage of this optimization. putImageData, and other methods that create temporary bitmaps will have to mark their bitmaps as volatile. before rendering them through skia. git-svn-id: http://skia.googlecode.com/svn/trunk@1769 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
parent
72798e4808
commit
4ee7ae5dcf
@ -93,13 +93,15 @@ class BitmapBench : public SkBenchmark {
|
||||
SkBitmap fBitmap;
|
||||
SkPaint fPaint;
|
||||
bool fIsOpaque;
|
||||
bool fForceUpdate; //bitmap marked as dirty before each draw. forces bitmap to be updated on device cache
|
||||
int fTileX, fTileY; // -1 means don't use shader
|
||||
SkString fName;
|
||||
enum { N = 300 };
|
||||
public:
|
||||
BitmapBench(void* param, bool isOpaque, SkBitmap::Config c,
|
||||
bool forceUpdate = false, bool bitmapVolatile = false,
|
||||
int tx = -1, int ty = -1)
|
||||
: INHERITED(param), fIsOpaque(isOpaque), fTileX(tx), fTileY(ty) {
|
||||
: INHERITED(param), fIsOpaque(isOpaque), fForceUpdate(forceUpdate), fTileX(tx), fTileY(ty) {
|
||||
const int w = 128;
|
||||
const int h = 128;
|
||||
SkBitmap bm;
|
||||
@ -124,6 +126,7 @@ public:
|
||||
fBitmap.getColorTable()->setIsOpaque(isOpaque);
|
||||
}
|
||||
fBitmap.setIsOpaque(isOpaque);
|
||||
fBitmap.setIsVolatile(bitmapVolatile);
|
||||
}
|
||||
|
||||
protected:
|
||||
@ -137,6 +140,11 @@ protected:
|
||||
}
|
||||
fName.appendf("_%s%s", gConfigName[fBitmap.config()],
|
||||
fIsOpaque ? "" : "_A");
|
||||
if (fForceUpdate)
|
||||
fName.append("_update");
|
||||
if (fBitmap.isVolatile())
|
||||
fName.append("_volatile");
|
||||
|
||||
return fName.c_str();
|
||||
}
|
||||
|
||||
@ -154,6 +162,10 @@ protected:
|
||||
for (int i = 0; i < N; i++) {
|
||||
SkScalar x = x0 + rand.nextUScalar1() * dim.fX;
|
||||
SkScalar y = y0 + rand.nextUScalar1() * dim.fY;
|
||||
|
||||
if (fForceUpdate)
|
||||
bitmap.notifyPixelsChanged();
|
||||
|
||||
canvas->drawBitmap(bitmap, x, y, &paint);
|
||||
}
|
||||
}
|
||||
@ -169,6 +181,8 @@ static SkBenchmark* Fact3(void* p) { return new BitmapBench(p, false, SkBitmap::
|
||||
static SkBenchmark* Fact4(void* p) { return new BitmapBench(p, true, SkBitmap::kARGB_4444_Config); }
|
||||
static SkBenchmark* Fact5(void* p) { return new BitmapBench(p, false, SkBitmap::kIndex8_Config); }
|
||||
static SkBenchmark* Fact6(void* p) { return new BitmapBench(p, true, SkBitmap::kIndex8_Config); }
|
||||
static SkBenchmark* Fact7(void* p) { return new BitmapBench(p, true, SkBitmap::kARGB_8888_Config, true, true); }
|
||||
static SkBenchmark* Fact8(void* p) { return new BitmapBench(p, true, SkBitmap::kARGB_8888_Config, true, false); }
|
||||
|
||||
static BenchRegistry gReg0(Fact0);
|
||||
static BenchRegistry gReg1(Fact1);
|
||||
@ -177,3 +191,5 @@ static BenchRegistry gReg3(Fact3);
|
||||
static BenchRegistry gReg4(Fact4);
|
||||
static BenchRegistry gReg5(Fact5);
|
||||
static BenchRegistry gReg6(Fact6);
|
||||
static BenchRegistry gReg7(Fact7);
|
||||
static BenchRegistry gReg8(Fact8);
|
||||
|
@ -169,7 +169,8 @@ public:
|
||||
int y,
|
||||
int width,
|
||||
int height,
|
||||
const void* srcData);
|
||||
const void* srcData,
|
||||
size_t rowBytes);
|
||||
virtual intptr_t getTextureHandle();
|
||||
|
||||
const TexParams& getTexParams() const { return fTexParams; }
|
||||
|
@ -201,18 +201,21 @@ public:
|
||||
/**
|
||||
* Updates a subrectangle of texels in the texture.
|
||||
*
|
||||
* @param x left edge of rectangle to update
|
||||
* @param y top edge of rectangle to update
|
||||
* @param width width of rectangle to update
|
||||
* @param height height of rectangle to update
|
||||
* @param srcData width*height texels of data in same format that was used
|
||||
* at texture creation.
|
||||
* @param x left edge of rectangle to update
|
||||
* @param y top edge of rectangle to update
|
||||
* @param width width of rectangle to update
|
||||
* @param height height of rectangle to update
|
||||
* @param srcData width*height texels of data in same format that was
|
||||
* used at texture creation.
|
||||
* @param rowBytes number of bytes per row in srcData, 0 means rows are
|
||||
* packed
|
||||
*/
|
||||
virtual void uploadTextureData(int x,
|
||||
int y,
|
||||
int width,
|
||||
int height,
|
||||
const void* srcData) = 0;
|
||||
const void* srcData,
|
||||
size_t rowBytes) = 0;
|
||||
|
||||
/**
|
||||
* Reads a rectangle of pixels from the texture.
|
||||
|
@ -117,7 +117,7 @@ bool GrAtlas::addSubImage(int width, int height, const void* image,
|
||||
image = storage.get();
|
||||
}
|
||||
adjustForPlot(loc, fPlot);
|
||||
fTexture->uploadTextureData(loc->fX, loc->fY, dstW, dstH, image);
|
||||
fTexture->uploadTextureData(loc->fX, loc->fY, dstW, dstH, image, 0);
|
||||
|
||||
// now tell the caller to skip the top/left BORDER
|
||||
loc->fX += BORDER;
|
||||
|
@ -17,6 +17,7 @@
|
||||
|
||||
#include "GrGLTexture.h"
|
||||
#include "GrGpuGL.h"
|
||||
#include "GrMemory.h"
|
||||
|
||||
#define GPUGL static_cast<GrGpuGL*>(getGpu())
|
||||
|
||||
@ -157,7 +158,8 @@ void GrGLTexture::uploadTextureData(int x,
|
||||
int y,
|
||||
int width,
|
||||
int height,
|
||||
const void* srcData) {
|
||||
const void* srcData,
|
||||
size_t rowBytes) {
|
||||
|
||||
GPUGL->setSpareTextureUnit();
|
||||
|
||||
@ -165,6 +167,37 @@ void GrGLTexture::uploadTextureData(int x,
|
||||
// (at least without extensions)
|
||||
GrAssert(fUploadFormat != GR_GL_PALETTE8_RGBA8);
|
||||
|
||||
// in case we need a temporary, trimmed copy of the src pixels
|
||||
GrAutoSMalloc<128 * 128> trimStorage;
|
||||
|
||||
/*
|
||||
* check if our srcData has extra bytes past each row. If so, we need
|
||||
* to trim those off here, since GL doesn't let us pass the rowBytes as
|
||||
* a parameter to glTexImage2D
|
||||
*/
|
||||
|
||||
if (GR_GL_SUPPORT_DESKTOP) {
|
||||
if (srcData && rowBytes) {
|
||||
GR_GL(PixelStorei(GR_GL_UNPACK_ROW_LENGTH,
|
||||
rowBytes / fUploadByteCount));
|
||||
}
|
||||
} else {
|
||||
size_t trimRowBytes = width * fUploadByteCount;
|
||||
if (srcData && (trimRowBytes < rowBytes)) {
|
||||
// copy the data into our new storage, skipping the trailing bytes
|
||||
size_t trimSize = height * trimRowBytes;
|
||||
const char* src = (const char*)srcData;
|
||||
char* dst = (char*)trimStorage.realloc(trimSize);
|
||||
for (int y = 0; y < height; y++) {
|
||||
memcpy(dst, src, trimRowBytes);
|
||||
src += rowBytes;
|
||||
dst += trimRowBytes;
|
||||
}
|
||||
// now point srcData to our trimmed version
|
||||
srcData = trimStorage.get();
|
||||
}
|
||||
}
|
||||
|
||||
// If we need to update textures that are created upside down
|
||||
// then we have to modify this code to flip the srcData
|
||||
GrAssert(kTopDown_Orientation == fOrientation);
|
||||
@ -173,6 +206,11 @@ void GrGLTexture::uploadTextureData(int x,
|
||||
GR_GL(TexSubImage2D(GR_GL_TEXTURE_2D, 0, x, y, width, height,
|
||||
fUploadFormat, fUploadType, srcData));
|
||||
|
||||
if (GR_GL_SUPPORT_DESKTOP) {
|
||||
if (srcData && rowBytes) {
|
||||
GR_GL(PixelStorei(GR_GL_UNPACK_ROW_LENGTH, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
intptr_t GrGLTexture::getTextureHandle() {
|
||||
|
@ -168,11 +168,25 @@ public:
|
||||
/** Returns true if the bitmap is opaque (has no translucent/transparent pixels).
|
||||
*/
|
||||
bool isOpaque() const;
|
||||
|
||||
/** Specify if this bitmap's pixels are all opaque or not. Is only meaningful for configs
|
||||
that support per-pixel alpha (RGB32, A1, A8).
|
||||
*/
|
||||
void setIsOpaque(bool);
|
||||
|
||||
/** Returns true if the bitmap is volatile (i.e. should not be cached by devices.)
|
||||
*/
|
||||
bool isVolatile() const;
|
||||
|
||||
/** Specify whether this bitmap is volatile. Bitmaps are not volatile by
|
||||
default. Temporary bitmaps that are discarded after use should be
|
||||
marked as volatile. This provides a hint to the device that the bitmap
|
||||
should not be cached. Providing this hint when appropriate can
|
||||
improve performance by avoiding unnecessary overhead and resource
|
||||
consumption on the device.
|
||||
*/
|
||||
void setIsVolatile(bool);
|
||||
|
||||
/** Reset the bitmap to its initial state (see default constructor). If we are a (shared)
|
||||
owner of the pixels, that ownership is decremented.
|
||||
*/
|
||||
@ -580,7 +594,8 @@ private:
|
||||
mutable int fRawPixelGenerationID;
|
||||
|
||||
enum Flags {
|
||||
kImageIsOpaque_Flag = 0x01
|
||||
kImageIsOpaque_Flag = 0x01,
|
||||
kImageIsVolatile_Flag = 0x02
|
||||
};
|
||||
|
||||
uint32_t fRowBytes;
|
||||
|
@ -583,6 +583,18 @@ void SkBitmap::setIsOpaque(bool isOpaque) {
|
||||
}
|
||||
}
|
||||
|
||||
bool SkBitmap::isVolatile() const {
|
||||
return (fFlags & kImageIsVolatile_Flag) != 0;
|
||||
}
|
||||
|
||||
void SkBitmap::setIsVolatile(bool isVolatile) {
|
||||
if (isVolatile) {
|
||||
fFlags |= kImageIsVolatile_Flag;
|
||||
} else {
|
||||
fFlags &= ~kImageIsVolatile_Flag;
|
||||
}
|
||||
}
|
||||
|
||||
void* SkBitmap::getAddr(int x, int y) const {
|
||||
SkASSERT((unsigned)x < (unsigned)this->width());
|
||||
SkASSERT((unsigned)y < (unsigned)this->height());
|
||||
@ -1510,7 +1522,7 @@ SkBitmap::RLEPixels::~RLEPixels() {
|
||||
void SkBitmap::validate() const {
|
||||
SkASSERT(fConfig < kConfigCount);
|
||||
SkASSERT(fRowBytes >= (unsigned)ComputeRowBytes((Config)fConfig, fWidth));
|
||||
SkASSERT(fFlags <= kImageIsOpaque_Flag);
|
||||
SkASSERT(fFlags <= (kImageIsOpaque_Flag | kImageIsVolatile_Flag));
|
||||
SkASSERT(fPixelLockCount >= 0);
|
||||
SkASSERT(NULL == fColorTable || (unsigned)fColorTable->getRefCnt() < 10000);
|
||||
SkASSERT((uint8_t)ComputeBytesPerPixel((Config)fConfig) == fBytesPerPixel);
|
||||
|
@ -1459,19 +1459,22 @@ SkGpuDevice::TexCache* SkGpuDevice::lockCachedTexture(const SkBitmap& bitmap,
|
||||
entry = ctx->lockKeylessTexture(desc);
|
||||
}
|
||||
} else {
|
||||
uint32_t p0, p1;
|
||||
p0 = bitmap.getGenerationID();
|
||||
p1 = bitmap.pixelRefOffset();
|
||||
|
||||
GrTextureKey key(p0, p1, bitmap.width(), bitmap.height());
|
||||
entry = ctx->findAndLockTexture(&key, sampler);
|
||||
|
||||
if (!bitmap.isVolatile()) {
|
||||
uint32_t p0, p1;
|
||||
p0 = bitmap.getGenerationID();
|
||||
p1 = bitmap.pixelRefOffset();
|
||||
GrTextureKey key(p0, p1, bitmap.width(), bitmap.height());
|
||||
|
||||
entry = ctx->findAndLockTexture(&key, sampler);
|
||||
if (NULL == entry)
|
||||
entry = sk_gr_create_bitmap_texture(ctx, &key, sampler,
|
||||
bitmap);
|
||||
} else {
|
||||
entry = sk_gr_create_bitmap_texture(ctx, NULL, sampler, bitmap);
|
||||
}
|
||||
if (NULL == entry) {
|
||||
entry = sk_gr_create_bitmap_texture(ctx, &key, sampler, bitmap);
|
||||
if (NULL == entry) {
|
||||
GrPrintf("---- failed to create texture for cache [%d %d]\n",
|
||||
bitmap.width(), bitmap.height());
|
||||
}
|
||||
GrPrintf("---- failed to create texture for cache [%d %d]\n",
|
||||
bitmap.width(), bitmap.height());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -97,8 +97,16 @@ GrTextureEntry* sk_gr_create_bitmap_texture(GrContext* ctx,
|
||||
|
||||
// our compressed data will be trimmed, so pass width() for its
|
||||
// "rowBytes", since they are the same now.
|
||||
return ctx->createAndLockTexture(key, sampler, desc, storage.get(),
|
||||
bitmap->width());
|
||||
|
||||
if (NULL != key) {
|
||||
return ctx->createAndLockTexture(key, sampler, desc, storage.get(),
|
||||
bitmap->width());
|
||||
} else {
|
||||
GrTextureEntry* entry = ctx->lockKeylessTexture(desc);
|
||||
entry->texture()->uploadTextureData(0, 0, bitmap->width(),
|
||||
bitmap->height(), storage.get(), 0);
|
||||
return entry;
|
||||
}
|
||||
|
||||
} else {
|
||||
origBitmap.copyTo(&tmpBitmap, SkBitmap::kARGB_8888_Config);
|
||||
@ -108,8 +116,15 @@ GrTextureEntry* sk_gr_create_bitmap_texture(GrContext* ctx,
|
||||
}
|
||||
|
||||
desc.fFormat = SkGr::Bitmap2PixelConfig(*bitmap);
|
||||
return ctx->createAndLockTexture(key, sampler, desc, bitmap->getPixels(),
|
||||
bitmap->rowBytes());
|
||||
if (NULL != key) {
|
||||
return ctx->createAndLockTexture(key, sampler, desc,
|
||||
bitmap->getPixels(), bitmap->rowBytes());
|
||||
} else {
|
||||
GrTextureEntry* entry = ctx->lockKeylessTexture(desc);
|
||||
entry->texture()->uploadTextureData(0, 0, bitmap->width(),
|
||||
bitmap->height(), bitmap->getPixels(), bitmap->rowBytes());
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
Loading…
Reference in New Issue
Block a user