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
This commit is contained in:
sugoi 2015-01-19 10:10:27 -08:00 committed by Commit bot
parent 89499d76b9
commit 692135f968
7 changed files with 258 additions and 30 deletions

View File

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

View File

@ -218,6 +218,7 @@
'../tests/WritePixelsTest.cpp',
'../tests/Writer32Test.cpp',
'../tests/XfermodeTest.cpp',
'../tests/YUVCacheTest.cpp',
'../tests/MatrixClipCollapseTest.cpp',
'../src/utils/debugger/SkDrawCommand.h',

View File

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

View File

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

View File

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

View File

@ -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
if (!pixelRef->getYUV8Planes(yuvSizes, planes, rowBytes, &colorSpace)) {
return NULL;
// Get the YUV planes and update plane sizes to actual image size
if (!pixelRef->getYUV8Planes(yuvInfo.fSize, planes, yuvInfo.fRowBytes,
&yuvInfo.fColorSpace)) {
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);

76
tests/YUVCacheTest.cpp Normal file
View File

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