Index: src/gpu/SkGr.cpp |
diff --git a/src/gpu/SkGr.cpp b/src/gpu/SkGr.cpp |
index 9083860db84420fc0bac47c4edaaa2ce3185e189..885b453df5f353c88b74ed9381241db01c4dc26a 100644 |
--- a/src/gpu/SkGr.cpp |
+++ b/src/gpu/SkGr.cpp |
@@ -9,6 +9,7 @@ |
#include "GrDrawTargetCaps.h" |
#include "GrGpu.h" |
+#include "GrGpuResourceCacheAccess.h" |
#include "GrXferProcessor.h" |
#include "SkColorFilter.h" |
#include "SkConfig8888.h" |
@@ -86,7 +87,45 @@ static void build_index8_data(void* buffer, const SkBitmap& bitmap) { |
//////////////////////////////////////////////////////////////////////////////// |
-static void generate_bitmap_key(const SkBitmap& bitmap, GrContentKey* key) { |
+enum Stretch { |
+ kNo_Stretch, |
+ kBilerp_Stretch, |
+ kNearest_Stretch |
+}; |
+ |
+static Stretch get_stretch_type(const GrContext* ctx, int width, int height, |
+ const GrTextureParams* params) { |
+ if (params && params->isTiled()) { |
+ const GrDrawTargetCaps* caps = ctx->getGpu()->caps(); |
+ if (!caps->npotTextureTileSupport() && (!SkIsPow2(width) || !SkIsPow2(height))) { |
+ switch(params->filterMode()) { |
+ case GrTextureParams::kNone_FilterMode: |
+ return kNearest_Stretch; |
+ case GrTextureParams::kBilerp_FilterMode: |
+ case GrTextureParams::kMipMap_FilterMode: |
+ return kBilerp_Stretch; |
+ } |
+ } |
+ } |
+ return kNo_Stretch; |
+} |
+ |
+static bool make_resize_key(const GrContentKey& origKey, Stretch stretch, GrContentKey* resizeKey) { |
+ if (origKey.isValid() && kNo_Stretch != stretch) { |
+ static const GrContentKey::Domain kDomain = GrContentKey::GenerateDomain(); |
+ GrContentKey::Builder builder(resizeKey, origKey, kDomain, 1); |
+ builder[0] = stretch; |
+ builder.finish(); |
+ return true; |
+ } |
+ SkASSERT(!resizeKey->isValid()); |
+ return false; |
+} |
+ |
+static void generate_bitmap_keys(const SkBitmap& bitmap, |
+ Stretch stretch, |
+ GrContentKey* key, |
+ GrContentKey* resizedKey) { |
// Our id includes the offset, width, and height so that bitmaps created by extractSubset() |
// are unique. |
uint32_t genID = bitmap.getGenerationID(); |
@@ -100,6 +139,11 @@ static void generate_bitmap_key(const SkBitmap& bitmap, GrContentKey* key) { |
builder[1] = origin.fX; |
builder[2] = origin.fY; |
builder[3] = width | (height << 16); |
+ builder.finish(); |
+ |
+ if (kNo_Stretch != stretch) { |
+ make_resize_key(*key, stretch, resizedKey); |
+ } |
} |
static void generate_bitmap_texture_desc(const SkBitmap& bitmap, GrSurfaceDesc* desc) { |
@@ -127,45 +171,101 @@ private: |
} // namespace |
+#if 0 // TODO: plug this back up |
static void add_genID_listener(const GrContentKey& key, SkPixelRef* pixelRef) { |
SkASSERT(pixelRef); |
pixelRef->addGenIDChangeListener(SkNEW_ARGS(GrResourceInvalidator, (key))); |
} |
+#endif |
-static GrTexture* sk_gr_allocate_texture(GrContext* ctx, |
- bool cache, |
- const GrTextureParams* params, |
- const SkBitmap& bm, |
+static GrTexture* create_texture_for_bmp(GrContext* ctx, |
+ const GrContentKey& optionalKey, |
GrSurfaceDesc desc, |
const void* pixels, |
size_t rowBytes) { |
GrTexture* result; |
- if (cache) { |
- // This texture is likely to be used again so leave it in the cache |
- GrContentKey key; |
- generate_bitmap_key(bm, &key); |
- |
- result = ctx->createTexture(params, desc, key, pixels, rowBytes, &key); |
+ if (optionalKey.isValid()) { |
+ result = ctx->createTexture(desc, pixels, rowBytes); |
if (result) { |
- add_genID_listener(key, bm.pixelRef()); |
+ SkAssertResult(ctx->addResourceToCache(optionalKey, result)); |
} |
- } else { |
- // This texture is unlikely to be used again (in its present form) so |
- // just use a scratch texture. This will remove the texture from the |
- // cache so no one else can find it. Additionally, once unlocked, the |
- // scratch texture will go to the end of the list for purging so will |
- // likely be available for this volatile bitmap the next time around. |
+ } else { |
result = ctx->refScratchTexture(desc, GrContext::kExact_ScratchTexMatch); |
- if (pixels) { |
- result->writePixels(0, 0, bm.width(), bm.height(), desc.fConfig, pixels, rowBytes); |
+ if (pixels && result) { |
+ result->writePixels(0, 0, desc.fWidth, desc.fHeight, desc.fConfig, pixels, rowBytes); |
} |
} |
return result; |
} |
+// creates a new texture that is the input texture scaled up to the next power of two in |
+// width or height. If optionalKey is valid it will be set on the new texture. stretch |
+// controls whether the scaling is done using nearest or bilerp filtering. |
+GrTexture* resize_texture(GrTexture* inputTexture, Stretch stretch, |
+ const GrContentKey& optionalKey) { |
+ SkASSERT(kNo_Stretch != stretch); |
+ |
+ GrContext* context = inputTexture->getContext(); |
+ SkASSERT(context); |
+ |
+ // Either it's a cache miss or the original wasn't cached to begin with. |
+ GrSurfaceDesc rtDesc = inputTexture->desc(); |
+ rtDesc.fFlags = rtDesc.fFlags | |
+ kRenderTarget_GrSurfaceFlag | |
+ kNoStencil_GrSurfaceFlag; |
+ rtDesc.fWidth = GrNextPow2(rtDesc.fWidth); |
+ rtDesc.fHeight = GrNextPow2(rtDesc.fHeight); |
+ rtDesc.fConfig = GrMakePixelConfigUncompressed(rtDesc.fConfig); |
+ |
+ // If the config isn't renderable try converting to either A8 or an 32 bit config. Otherwise, |
+ // fail. |
+ if (!context->isConfigRenderable(rtDesc.fConfig, false)) { |
+ if (GrPixelConfigIsAlphaOnly(rtDesc.fConfig)) { |
+ if (context->isConfigRenderable(kAlpha_8_GrPixelConfig, false)) { |
+ rtDesc.fConfig = kAlpha_8_GrPixelConfig; |
+ } else if (context->isConfigRenderable(kSkia8888_GrPixelConfig, false)) { |
+ rtDesc.fConfig = kSkia8888_GrPixelConfig; |
+ } else { |
+ return NULL; |
+ } |
+ } else if (kRGB_GrColorComponentFlags == |
+ (kRGB_GrColorComponentFlags & GrPixelConfigComponentMask(rtDesc.fConfig))) { |
+ if (context->isConfigRenderable(kSkia8888_GrPixelConfig, false)) { |
+ rtDesc.fConfig = kSkia8888_GrPixelConfig; |
+ } else { |
+ return NULL; |
+ } |
+ } else { |
+ return NULL; |
+ } |
+ } |
+ |
+ GrTexture* resized = create_texture_for_bmp(context, optionalKey, rtDesc, NULL, 0); |
+ |
+ if (!resized) { |
+ return NULL; |
+ } |
+ GrPaint paint; |
+ |
+ // If filtering is not desired then we want to ensure all texels in the resampled image are |
+ // copies of texels from the original. |
+ GrTextureParams params(SkShader::kClamp_TileMode, |
+ kBilerp_Stretch == stretch ? GrTextureParams::kBilerp_FilterMode : |
+ GrTextureParams::kNone_FilterMode); |
+ paint.addColorTextureProcessor(inputTexture, SkMatrix::I(), params); |
+ |
+ SkRect rect = SkRect::MakeWH(SkIntToScalar(rtDesc.fWidth), SkIntToScalar(rtDesc.fHeight)); |
+ SkRect localRect = SkRect::MakeWH(1.f, 1.f); |
+ |
+ GrContext::AutoRenderTarget autoRT(context, resized->asRenderTarget()); |
+ GrContext::AutoClip ac(context, GrContext::AutoClip::kWideOpen_InitialClip); |
+ context->drawNonAARectToRect(paint, SkMatrix::I(), rect, localRect); |
+ |
+ return resized; |
+} |
+ |
#ifndef SK_IGNORE_ETC1_SUPPORT |
-static GrTexture *load_etc1_texture(GrContext* ctx, bool cache, |
- const GrTextureParams* params, |
+static GrTexture *load_etc1_texture(GrContext* ctx, const GrContentKey& optionalKey, |
const SkBitmap &bm, GrSurfaceDesc desc) { |
SkAutoTUnref<SkData> data(bm.pixelRef()->refEncodedData()); |
@@ -210,11 +310,11 @@ static GrTexture *load_etc1_texture(GrContext* ctx, bool cache, |
return NULL; |
} |
- return sk_gr_allocate_texture(ctx, cache, params, bm, desc, bytes, 0); |
+ return create_texture_for_bmp(ctx, optionalKey, desc, bytes, 0); |
} |
#endif // SK_IGNORE_ETC1_SUPPORT |
-static GrTexture *load_yuv_texture(GrContext* ctx, bool cache, const GrTextureParams* params, |
+static GrTexture* load_yuv_texture(GrContext* ctx, const GrContentKey& optionalKey, |
const SkBitmap& bm, const GrSurfaceDesc& desc) { |
// Subsets are not supported, the whole pixelRef is loaded when using YUV decoding |
SkPixelRef* pixelRef = bm.pixelRef(); |
@@ -282,30 +382,31 @@ static GrTexture *load_yuv_texture(GrContext* ctx, bool cache, const GrTexturePa |
kRenderTarget_GrSurfaceFlag | |
kNoStencil_GrSurfaceFlag; |
- GrTexture* result = sk_gr_allocate_texture(ctx, cache, params, bm, rtDesc, NULL, 0); |
- |
- GrRenderTarget* renderTarget = result ? result->asRenderTarget() : NULL; |
- if (renderTarget) { |
- SkAutoTUnref<GrFragmentProcessor> yuvToRgbProcessor(GrYUVtoRGBEffect::Create( |
- yuvTextures[0], yuvTextures[1], yuvTextures[2], yuvInfo.fColorSpace)); |
- GrPaint paint; |
- paint.addColorProcessor(yuvToRgbProcessor); |
- SkRect r = SkRect::MakeWH(SkIntToScalar(yuvInfo.fSize[0].fWidth), |
- SkIntToScalar(yuvInfo.fSize[0].fHeight)); |
- GrContext::AutoRenderTarget autoRT(ctx, renderTarget); |
- GrContext::AutoClip ac(ctx, GrContext::AutoClip::kWideOpen_InitialClip); |
- ctx->drawRect(paint, SkMatrix::I(), r); |
- } else { |
- SkSafeSetNull(result); |
+ GrTexture* result = create_texture_for_bmp(ctx, optionalKey, rtDesc, NULL, 0); |
+ if (!result) { |
+ return NULL; |
} |
+ GrRenderTarget* renderTarget = result->asRenderTarget(); |
+ SkASSERT(renderTarget); |
+ |
+ SkAutoTUnref<GrFragmentProcessor> |
+ yuvToRgbProcessor(GrYUVtoRGBEffect::Create(yuvTextures[0], yuvTextures[1], yuvTextures[2], |
+ yuvInfo.fColorSpace)); |
+ GrPaint paint; |
+ paint.addColorProcessor(yuvToRgbProcessor); |
+ SkRect r = SkRect::MakeWH(SkIntToScalar(yuvInfo.fSize[0].fWidth), |
+ SkIntToScalar(yuvInfo.fSize[0].fHeight)); |
+ GrContext::AutoRenderTarget autoRT(ctx, renderTarget); |
+ GrContext::AutoClip ac(ctx, GrContext::AutoClip::kWideOpen_InitialClip); |
+ ctx->drawRect(paint, SkMatrix::I(), r); |
+ |
return result; |
} |
-static GrTexture* sk_gr_create_bitmap_texture(GrContext* ctx, |
- bool cache, |
- const GrTextureParams* params, |
- const SkBitmap& origBitmap) { |
+static GrTexture* create_unstretched_bitmap_texture(GrContext* ctx, |
+ const SkBitmap& origBitmap, |
+ const GrContentKey& optionalKey) { |
SkBitmap tmpBitmap; |
const SkBitmap* bitmap = &origBitmap; |
@@ -314,9 +415,7 @@ static GrTexture* sk_gr_create_bitmap_texture(GrContext* ctx, |
generate_bitmap_texture_desc(*bitmap, &desc); |
if (kIndex_8_SkColorType == bitmap->colorType()) { |
- // build_compressed_data doesn't do npot->pot expansion |
- // and paletted textures can't be sub-updated |
- if (cache && ctx->supportsIndex8PixelConfig(params, bitmap->width(), bitmap->height())) { |
+ if (ctx->supportsIndex8PixelConfig()) { |
size_t imageSize = GrCompressedFormatDataSize(kIndex_8_GrPixelConfig, |
bitmap->width(), bitmap->height()); |
SkAutoMalloc storage(imageSize); |
@@ -324,8 +423,7 @@ static GrTexture* 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 sk_gr_allocate_texture(ctx, cache, params, origBitmap, |
- desc, storage.get(), bitmap->width()); |
+ return create_texture_for_bmp(ctx, optionalKey, desc, storage.get(), bitmap->width()); |
} else { |
origBitmap.copyTo(&tmpBitmap, kN32_SkColorType); |
// now bitmap points to our temp, which has been promoted to 32bits |
@@ -339,7 +437,7 @@ static GrTexture* sk_gr_create_bitmap_texture(GrContext* ctx, |
else if ( |
// We do not support scratch ETC1 textures, hence they should all be at least |
// trying to go to the cache. |
- cache |
+ optionalKey.isValid() |
// Make sure that the underlying device supports ETC1 textures before we go ahead |
// and check the data. |
&& ctx->getGpu()->caps()->isConfigTexturable(kETC1_GrPixelConfig) |
@@ -348,7 +446,7 @@ static GrTexture* sk_gr_create_bitmap_texture(GrContext* ctx, |
// the bitmap has available pixels, then they might not be what the decompressed |
// data is. |
&& !(bitmap->readyToDraw())) { |
- GrTexture *texture = load_etc1_texture(ctx, cache, params, *bitmap, desc); |
+ GrTexture *texture = load_etc1_texture(ctx, optionalKey, *bitmap, desc); |
if (texture) { |
return texture; |
} |
@@ -356,7 +454,7 @@ static GrTexture* sk_gr_create_bitmap_texture(GrContext* ctx, |
#endif // SK_IGNORE_ETC1_SUPPORT |
else { |
- GrTexture *texture = load_yuv_texture(ctx, cache, params, *bitmap, desc); |
+ GrTexture *texture = load_yuv_texture(ctx, optionalKey, *bitmap, desc); |
if (texture) { |
return texture; |
} |
@@ -366,8 +464,32 @@ static GrTexture* sk_gr_create_bitmap_texture(GrContext* ctx, |
return NULL; |
} |
- return sk_gr_allocate_texture(ctx, cache, params, origBitmap, desc, |
- bitmap->getPixels(), bitmap->rowBytes()); |
+ return create_texture_for_bmp(ctx, optionalKey, desc, bitmap->getPixels(), bitmap->rowBytes()); |
+} |
+ |
+static GrTexture* create_bitmap_texture(GrContext* ctx, |
+ const SkBitmap& bmp, |
+ Stretch stretch, |
+ const GrContentKey& unstretchedKey, |
+ const GrContentKey& stretchedKey) { |
+ if (kNo_Stretch != stretch) { |
+ SkAutoTUnref<GrTexture> unstretched; |
+ // Check if we have the unstretched version in the cache, if not create it. |
+ if (unstretchedKey.isValid()) { |
+ unstretched.reset(ctx->findAndRefCachedTexture(unstretchedKey)); |
+ } |
+ if (!unstretched) { |
+ unstretched.reset(create_unstretched_bitmap_texture(ctx, bmp, unstretchedKey)); |
+ if (!unstretched) { |
+ return NULL; |
+ } |
+ } |
+ GrTexture* resized = resize_texture(unstretched, stretch, stretchedKey); |
+ return resized; |
+ } |
+ |
+ return create_unstretched_bitmap_texture(ctx, bmp, unstretchedKey); |
+ |
} |
static GrTexture* get_texture_backing_bmp(const SkBitmap& bitmap, const GrContext* context, |
@@ -393,12 +515,23 @@ bool GrIsBitmapInCache(const GrContext* ctx, |
return true; |
} |
- GrContentKey key; |
- generate_bitmap_key(bitmap, &key); |
+ // We don't cache volatile bitmaps |
+ if (bitmap.isVolatile()) { |
+ return false; |
+ } |
+ |
+ // If it is inherently texture backed, consider it in the cache |
+ if (bitmap.getTexture()) { |
+ return true; |
+ } |
+ |
+ Stretch stretch = get_stretch_type(ctx, bitmap.width(), bitmap.height(), params); |
+ GrContentKey key, resizedKey; |
+ generate_bitmap_keys(bitmap, stretch, &key, &resizedKey); |
GrSurfaceDesc desc; |
generate_bitmap_texture_desc(bitmap, &desc); |
- return ctx->isTextureInCache(desc, key, params); |
+ return ctx->isResourceInCache((kNo_Stretch == stretch) ? key : resizedKey); |
} |
GrTexture* GrRefCachedBitmapTexture(GrContext* ctx, |
@@ -409,29 +542,29 @@ GrTexture* GrRefCachedBitmapTexture(GrContext* ctx, |
return SkRef(result); |
} |
- bool cache = !bitmap.isVolatile(); |
+ Stretch stretch = get_stretch_type(ctx, bitmap.width(), bitmap.height(), params); |
+ GrContentKey key, resizedKey; |
- if (cache) { |
+ if (!bitmap.isVolatile()) { |
// If the bitmap isn't changing try to find a cached copy first. |
+ generate_bitmap_keys(bitmap, stretch, &key, &resizedKey); |
- GrContentKey key; |
- generate_bitmap_key(bitmap, &key); |
- |
- GrSurfaceDesc desc; |
- generate_bitmap_texture_desc(bitmap, &desc); |
- |
- result = ctx->findAndRefTexture(desc, key, params); |
- } |
- if (NULL == result) { |
- result = sk_gr_create_bitmap_texture(ctx, cache, params, bitmap); |
+ result = ctx->findAndRefCachedTexture(resizedKey.isValid() ? resizedKey : key); |
+ if (result) { |
+ return result; |
+ } |
} |
- if (NULL == result) { |
- SkDebugf("---- failed to create texture for cache [%d %d]\n", |
- bitmap.width(), bitmap.height()); |
+ |
+ result = create_bitmap_texture(ctx, bitmap, stretch, key, resizedKey); |
+ if (result) { |
+ return result; |
} |
- return result; |
-} |
+ SkDebugf("---- failed to create texture for cache [%d %d]\n", |
+ bitmap.width(), bitmap.height()); |
+ |
+ return NULL; |
+} |
/////////////////////////////////////////////////////////////////////////////// |
// alphatype is ignore for now, but if GrPixelConfig is expanded to encompass |