diff options
author | sugoi <sugoi@chromium.org> | 2015-01-19 10:10:27 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-01-19 10:10:27 -0800 |
commit | 692135f9689d4dcb5ba91ff8f4899e268c0bfe11 (patch) | |
tree | 73d0911ff57b27e75df7c5a1da554b959a2e6ca1 | |
parent | 89499d76b9616a455dfe8257cef9e4489938ea22 (diff) |
YUV planes cache
- Added new classes to contain YUV planes of memory, along with the associated data.
- Used these classes in load_yuv_texture() to enable YUV planes caching
- Added a unit test for the new cache
BUG=450021
Review URL: https://codereview.chromium.org/851273003
-rw-r--r-- | gyp/core.gypi | 2 | ||||
-rw-r--r-- | gyp/tests.gypi | 1 | ||||
-rw-r--r-- | src/core/SkMaskCache.cpp | 2 | ||||
-rw-r--r-- | src/core/SkYUVPlanesCache.cpp | 83 | ||||
-rw-r--r-- | src/core/SkYUVPlanesCache.h | 50 | ||||
-rw-r--r-- | src/gpu/SkGr.cpp | 74 | ||||
-rw-r--r-- | tests/YUVCacheTest.cpp | 76 |
7 files changed, 258 insertions, 30 deletions
diff --git a/gyp/core.gypi b/gyp/core.gypi index c5cecb666e..e7738af8bb 100644 --- a/gyp/core.gypi +++ b/gyp/core.gypi @@ -218,6 +218,8 @@ '<(skia_src_path)/core/SkWriteBuffer.cpp', '<(skia_src_path)/core/SkWriter32.cpp', '<(skia_src_path)/core/SkXfermode.cpp', + '<(skia_src_path)/core/SkYUVPlanesCache.cpp', + '<(skia_src_path)/core/SkYUVPlanesCache.h', '<(skia_src_path)/doc/SkDocument.cpp', diff --git a/gyp/tests.gypi b/gyp/tests.gypi index d59b26204a..ac71d36169 100644 --- a/gyp/tests.gypi +++ b/gyp/tests.gypi @@ -218,6 +218,7 @@ '../tests/WritePixelsTest.cpp', '../tests/Writer32Test.cpp', '../tests/XfermodeTest.cpp', + '../tests/YUVCacheTest.cpp', '../tests/MatrixClipCollapseTest.cpp', '../src/utils/debugger/SkDrawCommand.h', diff --git a/src/core/SkMaskCache.cpp b/src/core/SkMaskCache.cpp index a360fc56d7..02d355d4f1 100644 --- a/src/core/SkMaskCache.cpp +++ b/src/core/SkMaskCache.cpp @@ -147,7 +147,7 @@ struct RectsBlurRec : public SkResourceCache::Rec { static bool Visitor(const SkResourceCache::Rec& baseRec, void* contextData) { const RectsBlurRec& rec = static_cast<const RectsBlurRec&>(baseRec); - MaskValue* result = (MaskValue*)contextData; + MaskValue* result = static_cast<MaskValue*>(contextData); SkCachedData* tmpData = rec.fValue.fData; tmpData->ref(); diff --git a/src/core/SkYUVPlanesCache.cpp b/src/core/SkYUVPlanesCache.cpp new file mode 100644 index 0000000000..69885fe46a --- /dev/null +++ b/src/core/SkYUVPlanesCache.cpp @@ -0,0 +1,83 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkYUVPlanesCache.h" +#include "SkResourceCache.h" + +#define CHECK_LOCAL(localCache, localName, globalName, ...) \ + ((localCache) ? localCache->localName(__VA_ARGS__) : SkResourceCache::globalName(__VA_ARGS__)) + +namespace { +static unsigned gYUVPlanesKeyNamespaceLabel; + +struct YUVValue { + SkYUVPlanesCache::Info fInfo; + SkCachedData* fData; +}; + +struct YUVPlanesKey : public SkResourceCache::Key { + YUVPlanesKey(uint32_t genID) + : fGenID(genID) + { + this->init(&gYUVPlanesKeyNamespaceLabel, sizeof(genID)); + } + + uint32_t fGenID; +}; + +struct YUVPlanesRec : public SkResourceCache::Rec { + YUVPlanesRec(YUVPlanesKey key, SkCachedData* data, SkYUVPlanesCache::Info* info) + : fKey(key) + { + fValue.fData = data; + fValue.fInfo = *info; + fValue.fData->attachToCacheAndRef(); + } + ~YUVPlanesRec() { + fValue.fData->detachFromCacheAndUnref(); + } + + YUVPlanesKey fKey; + YUVValue fValue; + + const Key& getKey() const SK_OVERRIDE { return fKey; } + size_t bytesUsed() const SK_OVERRIDE { return sizeof(*this) + fValue.fData->size(); } + + static bool Visitor(const SkResourceCache::Rec& baseRec, void* contextData) { + const YUVPlanesRec& rec = static_cast<const YUVPlanesRec&>(baseRec); + YUVValue* result = static_cast<YUVValue*>(contextData); + + SkCachedData* tmpData = rec.fValue.fData; + tmpData->ref(); + if (NULL == tmpData->data()) { + tmpData->unref(); + return false; + } + result->fData = tmpData; + result->fInfo = rec.fValue.fInfo; + return true; + } +}; +} // namespace + +SkCachedData* SkYUVPlanesCache::FindAndRef(uint32_t genID, Info* info, + SkResourceCache* localCache) { + YUVValue result; + YUVPlanesKey key(genID); + if (!CHECK_LOCAL(localCache, find, Find, key, YUVPlanesRec::Visitor, &result)) { + return NULL; + } + + *info = result.fInfo; + return result.fData; +} + +void SkYUVPlanesCache::Add(uint32_t genID, SkCachedData* data, Info* info, + SkResourceCache* localCache) { + YUVPlanesKey key(genID); + return CHECK_LOCAL(localCache, add, Add, SkNEW_ARGS(YUVPlanesRec, (key, data, info))); +} diff --git a/src/core/SkYUVPlanesCache.h b/src/core/SkYUVPlanesCache.h new file mode 100644 index 0000000000..aa9b89e2a5 --- /dev/null +++ b/src/core/SkYUVPlanesCache.h @@ -0,0 +1,50 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkYUVPlanesCache_DEFINED +#define SkYUVPlanesCache_DEFINED + +#include "SkCachedData.h" +#include "SkImageInfo.h" + +class SkResourceCache; + +class SkYUVPlanesCache { +public: + /** + * The Info struct contains data about the 3 Y, U and V planes of memory stored + * contiguously, in that order, as a single block of memory within SkYUVPlanesCache. + * + * fSize: Width and height of each of the 3 planes (in pixels). + * fSizeInMemory: Amount of memory allocated for each plane (may be different from + "height * rowBytes", depending on the jpeg decoder's block size). + * The sum of these is the total size stored within SkYUVPlanesCache. + * fRowBytes: rowBytes for each of the 3 planes (in bytes). + * fColorSpace: color space that will be used for the YUV -> RGB conversion. + */ + struct Info { + SkISize fSize[3]; + size_t fSizeInMemory[3]; + size_t fRowBytes[3]; + SkYUVColorSpace fColorSpace; + }; + /** + * On success, return a ref to the SkCachedData that holds the pixels. + * + * On failure, return NULL. + */ + static SkCachedData* FindAndRef(uint32_t genID, Info* info, + SkResourceCache* localCache = NULL); + + /** + * Add a pixelRef ID and its YUV planes data to the cache. + */ + static void Add(uint32_t genID, SkCachedData* data, Info* info, + SkResourceCache* localCache = NULL); +}; + +#endif diff --git a/src/gpu/SkGr.cpp b/src/gpu/SkGr.cpp index 49d3c6bb48..18b27534bc 100644 --- a/src/gpu/SkGr.cpp +++ b/src/gpu/SkGr.cpp @@ -15,7 +15,9 @@ #include "SkData.h" #include "SkMessageBus.h" #include "SkPixelRef.h" +#include "SkResourceCache.h" #include "SkTextureCompressor.h" +#include "SkYUVPlanesCache.h" #include "effects/GrDitherEffect.h" #include "effects/GrPorterDuffXferProcessor.h" #include "effects/GrYUVtoRGBEffect.h" @@ -221,48 +223,62 @@ static GrTexture *load_etc1_texture(GrContext* ctx, bool cache, static GrTexture *load_yuv_texture(GrContext* ctx, bool cache, const GrTextureParams* params, const SkBitmap& bm, const GrSurfaceDesc& desc) { // Subsets are not supported, the whole pixelRef is loaded when using YUV decoding - if ((bm.pixelRef()->info().width() != bm.info().width()) || - (bm.pixelRef()->info().height() != bm.info().height())) { - return NULL; - } - SkPixelRef* pixelRef = bm.pixelRef(); - SkISize yuvSizes[3]; - if ((NULL == pixelRef) || !pixelRef->getYUV8Planes(yuvSizes, NULL, NULL, NULL)) { + if ((NULL == pixelRef) || + (pixelRef->info().width() != bm.info().width()) || + (pixelRef->info().height() != bm.info().height())) { return NULL; } - // Allocate the memory for YUV - size_t totalSize(0); - size_t sizes[3], rowBytes[3]; - for (int i = 0; i < 3; ++i) { - rowBytes[i] = yuvSizes[i].fWidth; - totalSize += sizes[i] = rowBytes[i] * yuvSizes[i].fHeight; - } - SkAutoMalloc storage(totalSize); + SkYUVPlanesCache::Info yuvInfo; + SkAutoTUnref<SkCachedData> cachedData( + SkYUVPlanesCache::FindAndRef(pixelRef->getGenerationID(), &yuvInfo)); + void* planes[3]; - planes[0] = storage.get(); - planes[1] = (uint8_t*)planes[0] + sizes[0]; - planes[2] = (uint8_t*)planes[1] + sizes[1]; + if (cachedData->data()) { + planes[0] = (void*)cachedData->data(); + planes[1] = (uint8_t*)planes[0] + yuvInfo.fSizeInMemory[0]; + planes[2] = (uint8_t*)planes[1] + yuvInfo.fSizeInMemory[1]; + } else { + // Fetch yuv plane sizes for memory allocation. Here, width and height can be + // rounded up to JPEG block size and be larger than the image's width and height. + if (!pixelRef->getYUV8Planes(yuvInfo.fSize, NULL, NULL, NULL)) { + return NULL; + } - SkYUVColorSpace colorSpace; + // Allocate the memory for YUV + size_t totalSize(0); + for (int i = 0; i < 3; ++i) { + yuvInfo.fRowBytes[i] = yuvInfo.fSize[i].fWidth; + yuvInfo.fSizeInMemory[i] = yuvInfo.fRowBytes[i] * yuvInfo.fSize[i].fHeight; + totalSize += yuvInfo.fSizeInMemory[i]; + } + cachedData.reset(SkResourceCache::NewCachedData(totalSize)); + planes[0] = cachedData->writable_data(); + planes[1] = (uint8_t*)planes[0] + yuvInfo.fSizeInMemory[0]; + planes[2] = (uint8_t*)planes[1] + yuvInfo.fSizeInMemory[1]; + + // Get the YUV planes and update plane sizes to actual image size + if (!pixelRef->getYUV8Planes(yuvInfo.fSize, planes, yuvInfo.fRowBytes, + &yuvInfo.fColorSpace)) { + return NULL; + } - // Get the YUV planes - if (!pixelRef->getYUV8Planes(yuvSizes, planes, rowBytes, &colorSpace)) { - return NULL; + // Decoding is done, cache the resulting YUV planes + SkYUVPlanesCache::Add(pixelRef->getGenerationID(), cachedData, &yuvInfo); } GrSurfaceDesc yuvDesc; yuvDesc.fConfig = kAlpha_8_GrPixelConfig; SkAutoTUnref<GrTexture> yuvTextures[3]; for (int i = 0; i < 3; ++i) { - yuvDesc.fWidth = yuvSizes[i].fWidth; - yuvDesc.fHeight = yuvSizes[i].fHeight; + yuvDesc.fWidth = yuvInfo.fSize[i].fWidth; + yuvDesc.fHeight = yuvInfo.fSize[i].fHeight; yuvTextures[i].reset( ctx->refScratchTexture(yuvDesc, GrContext::kApprox_ScratchTexMatch)); if (!yuvTextures[i] || !yuvTextures[i]->writePixels(0, 0, yuvDesc.fWidth, yuvDesc.fHeight, - yuvDesc.fConfig, planes[i], rowBytes[i])) { + yuvDesc.fConfig, planes[i], yuvInfo.fRowBytes[i])) { return NULL; } } @@ -276,12 +292,12 @@ static GrTexture *load_yuv_texture(GrContext* ctx, bool cache, const GrTexturePa GrRenderTarget* renderTarget = result ? result->asRenderTarget() : NULL; if (renderTarget) { - SkAutoTUnref<GrFragmentProcessor> yuvToRgbProcessor( - GrYUVtoRGBEffect::Create(yuvTextures[0], yuvTextures[1], yuvTextures[2], colorSpace)); + SkAutoTUnref<GrFragmentProcessor> yuvToRgbProcessor(GrYUVtoRGBEffect::Create( + yuvTextures[0], yuvTextures[1], yuvTextures[2], yuvInfo.fColorSpace)); GrPaint paint; paint.addColorProcessor(yuvToRgbProcessor); - SkRect r = SkRect::MakeWH(SkIntToScalar(yuvSizes[0].fWidth), - SkIntToScalar(yuvSizes[0].fHeight)); + 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); diff --git a/tests/YUVCacheTest.cpp b/tests/YUVCacheTest.cpp new file mode 100644 index 0000000000..f5b5897334 --- /dev/null +++ b/tests/YUVCacheTest.cpp @@ -0,0 +1,76 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkCachedData.h" +#include "SkYUVPlanesCache.h" +#include "SkResourceCache.h" +#include "Test.h" + +enum LockedState { + kUnlocked, + kLocked, +}; + +enum CachedState { + kNotInCache, + kInCache, +}; + +static void check_data(skiatest::Reporter* reporter, SkCachedData* data, + int refcnt, CachedState cacheState, LockedState lockedState) { + REPORTER_ASSERT(reporter, data->testing_only_getRefCnt() == refcnt); + REPORTER_ASSERT(reporter, data->testing_only_isInCache() == (kInCache == cacheState)); + bool isLocked = (data->data() != NULL); + REPORTER_ASSERT(reporter, isLocked == (lockedState == kLocked)); +} + +DEF_TEST(YUVPlanesCache, reporter) { + SkResourceCache cache(1024); + + SkYUVPlanesCache::Info yuvInfo; + for (int i = 0; i < 3; ++i) { + yuvInfo.fSize[i].fWidth = 20 * i; + yuvInfo.fSize[i].fHeight = 10 * i; + yuvInfo.fSizeInMemory[i] = 800 * i; + yuvInfo.fRowBytes[i] = 80 * i; + } + yuvInfo.fColorSpace = kRec601_SkYUVColorSpace; + + const uint32_t genID = 12345678; + + SkCachedData* data = SkYUVPlanesCache::FindAndRef(genID, &yuvInfo, &cache); + REPORTER_ASSERT(reporter, NULL == data); + + size_t size = 256; + data = cache.newCachedData(size); + memset(data->writable_data(), 0xff, size); + + SkYUVPlanesCache::Add(genID, data, &yuvInfo, &cache); + check_data(reporter, data, 2, kInCache, kLocked); + + data->unref(); + check_data(reporter, data, 1, kInCache, kUnlocked); + + SkYUVPlanesCache::Info yuvInfoRead; + data = SkYUVPlanesCache::FindAndRef(genID, &yuvInfoRead, &cache); + + REPORTER_ASSERT(reporter, data); + REPORTER_ASSERT(reporter, data->size() == size); + for (int i = 0; i < 3; ++i) { + REPORTER_ASSERT(reporter, yuvInfo.fSize[i].fWidth == yuvInfoRead.fSize[i].fWidth); + REPORTER_ASSERT(reporter, yuvInfo.fSize[i].fHeight == yuvInfoRead.fSize[i].fHeight); + REPORTER_ASSERT(reporter, yuvInfo.fSizeInMemory[i] == yuvInfoRead.fSizeInMemory[i]); + REPORTER_ASSERT(reporter, yuvInfo.fRowBytes[i] == yuvInfoRead.fRowBytes[i]); + } + REPORTER_ASSERT(reporter, yuvInfo.fColorSpace == yuvInfoRead.fColorSpace); + + check_data(reporter, data, 2, kInCache, kLocked); + + cache.purgeAll(); + check_data(reporter, data, 1, kNotInCache, kLocked); + data->unref(); +} |