Rewrite GrTextureMaker to disentangle bitmap case from base class and give GPU object a say in what copying needs to be done.
Review URL: https://codereview.chromium.org/1409163002
This commit is contained in:
parent
466c2c48b9
commit
fcffaf22d6
@ -178,6 +178,8 @@
|
||||
'<(skia_src_path)/gpu/GrTextContext.cpp',
|
||||
'<(skia_src_path)/gpu/GrTextContext.h',
|
||||
'<(skia_src_path)/gpu/GrTexture.cpp',
|
||||
'<(skia_src_path)/gpu/GrTextureParamsAdjuster.h',
|
||||
'<(skia_src_path)/gpu/GrTextureParamsAdjuster.cpp',
|
||||
'<(skia_src_path)/gpu/GrTextureProvider.cpp',
|
||||
'<(skia_src_path)/gpu/GrTexturePriv.h',
|
||||
'<(skia_src_path)/gpu/GrTextureAccess.cpp',
|
||||
|
@ -138,8 +138,8 @@ private:
|
||||
|
||||
friend class TestResource; // For unit test to access kMetaDataCnt.
|
||||
|
||||
// bmp textures require 4 uint32_t values.
|
||||
SkAutoSTMalloc<kMetaDataCnt + 4, uint32_t> fKey;
|
||||
// bmp textures require 5 uint32_t values.
|
||||
SkAutoSTMalloc<kMetaDataCnt + 5, uint32_t> fKey;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -65,9 +65,11 @@ static inline GrColor SkPMColorToGrColor(SkPMColor c) {
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/** Returns a texture representing the bitmap that is compatible with the GrTextureParams. The
|
||||
texture is inserted into the cache (unless the bitmap is marked volatile) and can be
|
||||
retrieved again via this function. */
|
||||
GrTexture* GrRefCachedBitmapTexture(GrContext*, const SkBitmap&, const GrTextureParams&);
|
||||
|
||||
|
||||
// TODO: Move SkImageInfo2GrPixelConfig to SkGrPriv.h (requires cleanup to SkWindow its subclasses).
|
||||
GrPixelConfig SkImageInfo2GrPixelConfig(SkColorType, SkAlphaType, SkColorProfileType);
|
||||
|
||||
|
@ -204,7 +204,9 @@ public:
|
||||
};
|
||||
|
||||
static GrTexture* set_key_and_return(GrTexture* tex, const GrUniqueKey& key) {
|
||||
tex->resourcePriv().setUniqueKey(key);
|
||||
if (key.isValid()) {
|
||||
tex->resourcePriv().setUniqueKey(key);
|
||||
}
|
||||
return tex;
|
||||
}
|
||||
|
||||
@ -217,24 +219,16 @@ static GrTexture* set_key_and_return(GrTexture* tex, const GrUniqueKey& key) {
|
||||
* 4. Ask the generator to return YUV planes, which the GPU can convert
|
||||
* 5. Ask the generator to return RGB(A) data, which the GPU can convert
|
||||
*/
|
||||
GrTexture* SkImageCacherator::lockUnstretchedTexture(GrContext* ctx, const SkImage* client) {
|
||||
// textures (at least the texture-key) only support 16bit dimensions, so abort early
|
||||
// if we're too big.
|
||||
if (fInfo.width() > 0xFFFF || fInfo.height() > 0xFFFF) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GrUniqueKey key;
|
||||
const GrTextureParams& noStretchParams = GrTextureParams::ClampNoFilter();
|
||||
GrMakeKeyFromImageID(&key, fUniqueID, SkIRect::MakeWH(fInfo.width(), fInfo.height()),
|
||||
*ctx->caps(), noStretchParams);
|
||||
|
||||
GrTexture* SkImageCacherator::lockTexture(GrContext* ctx, const GrUniqueKey& key,
|
||||
const SkImage* client) {
|
||||
// 1. Check the cache for a pre-existing one
|
||||
if (GrTexture* tex = ctx->textureProvider()->findAndRefTextureByUniqueKey(key)) {
|
||||
return tex;
|
||||
if (key.isValid()) {
|
||||
if (GrTexture* tex = ctx->textureProvider()->findAndRefTextureByUniqueKey(key)) {
|
||||
return tex;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Ask the genreator to natively create one
|
||||
// 2. Ask the generator to natively create one
|
||||
{
|
||||
ScopedGenerator generator(this);
|
||||
SkIRect subset = SkIRect::MakeXYWH(fOrigin.x(), fOrigin.y(), fInfo.width(), fInfo.height());
|
||||
@ -267,54 +261,62 @@ GrTexture* SkImageCacherator::lockUnstretchedTexture(GrContext* ctx, const SkIma
|
||||
// 5. Ask the generator to return RGB(A) data, which the GPU can convert
|
||||
SkBitmap bitmap;
|
||||
if (this->tryLockAsBitmap(&bitmap, client)) {
|
||||
return GrRefCachedBitmapTexture(ctx, bitmap, noStretchParams);
|
||||
GrTexture* tex = GrUploadBitmapToTexture(ctx, bitmap);
|
||||
if (tex) {
|
||||
return set_key_and_return(tex, key);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "GrTextureMaker.h"
|
||||
#include "GrTextureParamsAdjuster.h"
|
||||
|
||||
class Cacherator_GrTextureMaker : public GrTextureMaker {
|
||||
class Cacherator_GrTextureParamsAdjuster : public GrTextureParamsAdjuster {
|
||||
public:
|
||||
Cacherator_GrTextureMaker(SkImageCacherator* cacher, const SkImage* client,
|
||||
const GrUniqueKey& unstretchedKey)
|
||||
Cacherator_GrTextureParamsAdjuster(SkImageCacherator* cacher, const SkImage* client)
|
||||
: INHERITED(cacher->info().width(), cacher->info().height())
|
||||
, fCacher(cacher)
|
||||
, fClient(client)
|
||||
, fUnstretchedKey(unstretchedKey)
|
||||
{}
|
||||
{
|
||||
if (client) {
|
||||
GrMakeKeyFromImageID(&fOriginalKey, client->uniqueID(),
|
||||
SkIRect::MakeWH(this->width(), this->height()));
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
// TODO: consider overriding this, for the case where the underlying generator might be
|
||||
// able to efficiently produce a "stretched" texture natively (e.g. picture-backed)
|
||||
// GrTexture* onGenerateStretchedTexture(GrContext*, const SkGrStretch&) override;
|
||||
// GrTexture* generateTextureForParams(GrContext*, const SkGrStretch&) override;
|
||||
|
||||
GrTexture* onRefUnstretchedTexture(GrContext* ctx) override {
|
||||
return fCacher->lockUnstretchedTexture(ctx, fClient);
|
||||
GrTexture* refOriginalTexture(GrContext* ctx) override {
|
||||
return fCacher->lockTexture(ctx, fOriginalKey, fClient);
|
||||
}
|
||||
|
||||
bool onMakeStretchedKey(const SkGrStretch& stretch, GrUniqueKey* stretchedKey) override {
|
||||
return GrMakeStretchedKey(fUnstretchedKey, stretch, stretchedKey);
|
||||
void makeCopyKey(const CopyParams& stretch, GrUniqueKey* paramsCopyKey) override {
|
||||
if (fOriginalKey.isValid()) {
|
||||
MakeCopyKeyFromOrigKey(fOriginalKey, stretch, paramsCopyKey);
|
||||
}
|
||||
}
|
||||
|
||||
void onNotifyStretchCached(const GrUniqueKey& stretchedKey) override {
|
||||
void didCacheCopy(const GrUniqueKey& copyKey) override {
|
||||
if (fClient) {
|
||||
as_IB(fClient)->notifyAddedToCache();
|
||||
}
|
||||
}
|
||||
|
||||
bool onGetROBitmap(SkBitmap* bitmap) override {
|
||||
bool getROBitmap(SkBitmap* bitmap) override {
|
||||
return fCacher->lockAsBitmap(bitmap, fClient);
|
||||
}
|
||||
|
||||
private:
|
||||
SkImageCacherator* fCacher;
|
||||
const SkImage* fClient;
|
||||
const GrUniqueKey fUnstretchedKey;
|
||||
GrUniqueKey fOriginalKey;
|
||||
|
||||
typedef GrTextureMaker INHERITED;
|
||||
typedef GrTextureParamsAdjuster INHERITED;
|
||||
};
|
||||
|
||||
GrTexture* SkImageCacherator::lockAsTexture(GrContext* ctx, const GrTextureParams& params,
|
||||
@ -323,12 +325,7 @@ GrTexture* SkImageCacherator::lockAsTexture(GrContext* ctx, const GrTextureParam
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GrUniqueKey key;
|
||||
GrMakeKeyFromImageID(&key, this->uniqueID(),
|
||||
SkIRect::MakeWH(this->info().width(), this->info().height()),
|
||||
*ctx->caps(), GrTextureParams::ClampNoFilter());
|
||||
|
||||
return Cacherator_GrTextureMaker(this, client, key).refCachedTexture(ctx, params);
|
||||
return Cacherator_GrTextureParamsAdjuster(this, client).refTextureForParams(ctx, params);
|
||||
}
|
||||
|
||||
#else
|
||||
|
@ -13,7 +13,8 @@
|
||||
#include "SkTemplates.h"
|
||||
|
||||
class GrContext;
|
||||
class GrTextureParams;
|
||||
class GrTextureParams;
|
||||
class GrUniqueKey;
|
||||
class SkBitmap;
|
||||
class SkImage;
|
||||
|
||||
@ -60,7 +61,9 @@ private:
|
||||
bool generateBitmap(SkBitmap*);
|
||||
bool tryLockAsBitmap(SkBitmap*, const SkImage*);
|
||||
#if SK_SUPPORT_GPU
|
||||
GrTexture* lockUnstretchedTexture(GrContext*, const SkImage* client);
|
||||
// Returns the texture. If the cacherator is generating the texture and wants to cache it,
|
||||
// it should use the passed in key (if the key is valid).
|
||||
GrTexture* lockTexture(GrContext*, const GrUniqueKey& key, const SkImage* client);
|
||||
#endif
|
||||
|
||||
class ScopedGenerator {
|
||||
@ -83,7 +86,7 @@ private:
|
||||
const SkIPoint fOrigin;
|
||||
const uint32_t fUniqueID;
|
||||
|
||||
friend class Cacherator_GrTextureMaker;
|
||||
friend class Cacherator_GrTextureParamsAdjuster;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -55,6 +55,37 @@ void GrGpu::contextAbandoned() {}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool GrGpu::makeCopyForTextureParams(int width, int height, const GrTextureParams& textureParams,
|
||||
GrTextureParamsAdjuster::CopyParams* copyParams) const {
|
||||
bool doCopy = false;
|
||||
const GrCaps& caps = *this->caps();
|
||||
if (textureParams.isTiled() && !caps.npotTextureTileSupport() &&
|
||||
(!SkIsPow2(width) || !SkIsPow2(height))) {
|
||||
doCopy = true;
|
||||
copyParams->fWidth = GrNextPow2(SkTMax(width, caps.minTextureSize()));
|
||||
copyParams->fHeight = GrNextPow2(SkTMax(height, caps.minTextureSize()));
|
||||
} else if (width < caps.minTextureSize() || height < caps.minTextureSize()) {
|
||||
// The small texture issues appear to be with tiling. Hence it seems ok to scale
|
||||
// them up using the GPU. If issues persist we may need to CPU-stretch.
|
||||
doCopy = true;
|
||||
copyParams->fWidth = SkTMax(width, caps.minTextureSize());
|
||||
copyParams->fHeight = SkTMax(height, caps.minTextureSize());
|
||||
}
|
||||
if (doCopy) {
|
||||
switch (textureParams.filterMode()) {
|
||||
case GrTextureParams::kNone_FilterMode:
|
||||
copyParams->fFilter = GrTextureParams::kNone_FilterMode;
|
||||
break;
|
||||
case GrTextureParams::kBilerp_FilterMode:
|
||||
case GrTextureParams::kMipMap_FilterMode:
|
||||
// We are only ever scaling up so no reason to ever indicate kMipMap.
|
||||
copyParams->fFilter = GrTextureParams::kBilerp_FilterMode;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return doCopy;
|
||||
}
|
||||
|
||||
static GrSurfaceOrigin resolve_origin(GrSurfaceOrigin origin, bool renderTarget) {
|
||||
// By default, GrRenderTargets are GL's normal orientation so that they
|
||||
// can be drawn to by the outside world without the client having
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "GrPipelineBuilder.h"
|
||||
#include "GrProgramDesc.h"
|
||||
#include "GrStencil.h"
|
||||
#include "GrTextureParamsAdjuster.h"
|
||||
#include "GrXferProcessor.h"
|
||||
#include "SkPath.h"
|
||||
|
||||
@ -378,6 +379,13 @@ public:
|
||||
// clears target's entire stencil buffer to 0
|
||||
virtual void clearStencil(GrRenderTarget* target) = 0;
|
||||
|
||||
|
||||
// Determines whether a copy of a texture must be made in order to be compatible with
|
||||
// a given GrTextureParams. If so, the width, height and filter used for the copy are
|
||||
// output via the CopyParams.
|
||||
bool makeCopyForTextureParams(int width, int height, const GrTextureParams&,
|
||||
GrTextureParamsAdjuster::CopyParams*) const;
|
||||
|
||||
// This is only to be used in GL-specific tests.
|
||||
virtual const GrGLContext* glContextForTesting() const { return nullptr; }
|
||||
|
||||
|
@ -1,76 +0,0 @@
|
||||
/*
|
||||
* 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 GrTextureMaker_DEFINED
|
||||
#define GrTextureMaker_DEFINED
|
||||
|
||||
#include "SkGrPriv.h"
|
||||
|
||||
class GrContext;
|
||||
class GrTexture;
|
||||
class GrTextureParams;
|
||||
class GrUniqueKey;
|
||||
class SkBitmap;
|
||||
|
||||
class GrTextureMaker {
|
||||
public:
|
||||
GrTextureMaker(int width, int height) : fWidth(width), fHeight(height) {}
|
||||
virtual ~GrTextureMaker() {}
|
||||
|
||||
int width() const { return fWidth; }
|
||||
int height() const { return fHeight; }
|
||||
|
||||
GrTexture* refCachedTexture(GrContext*, const GrTextureParams&);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Return the maker's "original" unstretched texture. It is the responsibility of the maker
|
||||
* to make this efficient ... if the texture is being generated, the maker must handle
|
||||
* caching it.
|
||||
*/
|
||||
virtual GrTexture* onRefUnstretchedTexture(GrContext*) = 0;
|
||||
|
||||
/**
|
||||
* If we need to stretch the maker's original texture, the maker is asked to return a key
|
||||
* that identifies its origianl + the stretch parameter. If the maker does not want to cache
|
||||
* the stretched version (e.g. the maker is volatile), this should ignore the key parameter
|
||||
* and return false.
|
||||
*/
|
||||
virtual bool onMakeStretchedKey(const SkGrStretch&, GrUniqueKey* stretchedKey) = 0;
|
||||
|
||||
/**
|
||||
* Return a new (uncached) texture that is the stretch of the maker's original.
|
||||
*
|
||||
* The base-class handles general logic for this, and only needs access to the following
|
||||
* methods:
|
||||
* - onRefUnstretchedTexture()
|
||||
* - onGetROBitmap()
|
||||
*
|
||||
* Subclass may override this if they can handle stretching more efficiently.
|
||||
*/
|
||||
virtual GrTexture* onGenerateStretchedTexture(GrContext*, const SkGrStretch&);
|
||||
|
||||
/**
|
||||
* If a stretched version of the texture is generated, it may be cached (assuming that
|
||||
* onMakeStretchedKey() returns true). In that case, the maker is notified in case it
|
||||
* wants to note that for when the maker is destroyed.
|
||||
*/
|
||||
virtual void onNotifyStretchCached(const GrUniqueKey& stretchedKey) = 0;
|
||||
|
||||
/**
|
||||
* Some GPUs are unreliable w/ very small texture sizes. If we run into that case, this
|
||||
* method will be called (in service of onGenerateStretchedTexture) to return a raster version
|
||||
* of the original texture.
|
||||
*/
|
||||
virtual bool onGetROBitmap(SkBitmap*) = 0;
|
||||
|
||||
private:
|
||||
const int fWidth;
|
||||
const int fHeight;
|
||||
};
|
||||
|
||||
#endif
|
152
src/gpu/GrTextureParamsAdjuster.cpp
Normal file
152
src/gpu/GrTextureParamsAdjuster.cpp
Normal file
@ -0,0 +1,152 @@
|
||||
/*
|
||||
* 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 "GrTextureParamsAdjuster.h"
|
||||
|
||||
#include "GrCaps.h"
|
||||
#include "GrContext.h"
|
||||
#include "GrDrawContext.h"
|
||||
#include "GrGpu.h"
|
||||
#include "GrTexture.h"
|
||||
#include "GrTextureParams.h"
|
||||
#include "GrTextureProvider.h"
|
||||
#include "SkCanvas.h"
|
||||
#include "SkGr.h"
|
||||
#include "SkGrPriv.h"
|
||||
|
||||
typedef GrTextureParamsAdjuster::CopyParams CopyParams;
|
||||
|
||||
static GrTexture* copy_on_gpu(GrTexture* inputTexture, const CopyParams& copyParams) {
|
||||
GrContext* context = inputTexture->getContext();
|
||||
SkASSERT(context);
|
||||
const GrCaps* caps = context->caps();
|
||||
|
||||
// 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;
|
||||
rtDesc.fWidth = copyParams.fWidth;
|
||||
rtDesc.fHeight = copyParams.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 (!caps->isConfigRenderable(rtDesc.fConfig, false)) {
|
||||
if (GrPixelConfigIsAlphaOnly(rtDesc.fConfig)) {
|
||||
if (caps->isConfigRenderable(kAlpha_8_GrPixelConfig, false)) {
|
||||
rtDesc.fConfig = kAlpha_8_GrPixelConfig;
|
||||
} else if (caps->isConfigRenderable(kSkia8888_GrPixelConfig, false)) {
|
||||
rtDesc.fConfig = kSkia8888_GrPixelConfig;
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
} else if (kRGB_GrColorComponentFlags ==
|
||||
(kRGB_GrColorComponentFlags & GrPixelConfigComponentMask(rtDesc.fConfig))) {
|
||||
if (caps->isConfigRenderable(kSkia8888_GrPixelConfig, false)) {
|
||||
rtDesc.fConfig = kSkia8888_GrPixelConfig;
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
SkAutoTUnref<GrTexture> copy(context->textureProvider()->createTexture(rtDesc, true));
|
||||
if (!copy) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
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, copyParams.fFilter);
|
||||
paint.addColorTextureProcessor(inputTexture, SkMatrix::I(), params);
|
||||
|
||||
SkRect rect = SkRect::MakeWH(SkIntToScalar(rtDesc.fWidth), SkIntToScalar(rtDesc.fHeight));
|
||||
SkRect localRect = SkRect::MakeWH(1.f, 1.f);
|
||||
|
||||
SkAutoTUnref<GrDrawContext> drawContext(context->drawContext(copy->asRenderTarget()));
|
||||
if (!drawContext) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
drawContext->drawNonAARectToRect(GrClip::WideOpen(), paint, SkMatrix::I(), rect, localRect);
|
||||
return copy.detach();
|
||||
}
|
||||
|
||||
static SkBitmap copy_on_cpu(const SkBitmap& bmp, const CopyParams& copyParams) {
|
||||
SkBitmap stretched;
|
||||
stretched.allocN32Pixels(copyParams.fWidth, copyParams.fHeight);
|
||||
SkCanvas canvas(stretched);
|
||||
SkPaint paint;
|
||||
switch (copyParams.fFilter) {
|
||||
case GrTextureParams::kNone_FilterMode:
|
||||
paint.setFilterQuality(kNone_SkFilterQuality);
|
||||
break;
|
||||
case GrTextureParams::kBilerp_FilterMode:
|
||||
paint.setFilterQuality(kLow_SkFilterQuality);
|
||||
break;
|
||||
case GrTextureParams::kMipMap_FilterMode:
|
||||
paint.setFilterQuality(kMedium_SkFilterQuality);
|
||||
break;
|
||||
}
|
||||
SkRect dstRect = SkRect::MakeWH(SkIntToScalar(copyParams.fWidth),
|
||||
SkIntToScalar(copyParams.fHeight));
|
||||
canvas.drawBitmapRect(bmp, dstRect, &paint);
|
||||
return stretched;
|
||||
}
|
||||
|
||||
GrTexture* GrTextureParamsAdjuster::refTextureForParams(GrContext* ctx,
|
||||
const GrTextureParams& params) {
|
||||
CopyParams copyParams;
|
||||
if (!ctx->getGpu()->makeCopyForTextureParams(this->width(), this->height(), params,
|
||||
©Params)) {
|
||||
return this->refOriginalTexture(ctx);
|
||||
}
|
||||
GrUniqueKey copyKey;
|
||||
this->makeCopyKey(copyParams, ©Key);
|
||||
if (copyKey.isValid()) {
|
||||
GrTexture* result = ctx->textureProvider()->findAndRefTextureByUniqueKey(copyKey);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
GrTexture* result = this->generateTextureForParams(ctx, copyParams);
|
||||
if (!result) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (copyKey.isValid()) {
|
||||
ctx->textureProvider()->assignUniqueKeyToTexture(copyKey, result);
|
||||
this->didCacheCopy(copyKey);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
GrTexture* GrTextureParamsAdjuster::generateTextureForParams(GrContext* ctx,
|
||||
const CopyParams& copyParams) {
|
||||
if (this->width() < ctx->caps()->minTextureSize() ||
|
||||
this->height() < ctx->caps()->minTextureSize())
|
||||
{
|
||||
// we can't trust our ability to use HW to perform the stretch, so we request
|
||||
// a raster instead, and perform the stretch on the CPU.
|
||||
SkBitmap bitmap;
|
||||
if (!this->getROBitmap(&bitmap)) {
|
||||
return nullptr;
|
||||
}
|
||||
SkBitmap stretchedBmp = copy_on_cpu(bitmap, copyParams);
|
||||
return GrUploadBitmapToTexture(ctx, bitmap);
|
||||
} else {
|
||||
SkAutoTUnref<GrTexture> original(this->refOriginalTexture(ctx));
|
||||
if (!original) {
|
||||
return nullptr;
|
||||
}
|
||||
return copy_on_gpu(original, copyParams);
|
||||
}
|
||||
}
|
107
src/gpu/GrTextureParamsAdjuster.h
Normal file
107
src/gpu/GrTextureParamsAdjuster.h
Normal file
@ -0,0 +1,107 @@
|
||||
/*
|
||||
* 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 GrTextureMaker_DEFINED
|
||||
#define GrTextureMaker_DEFINED
|
||||
|
||||
#include "GrTextureParams.h"
|
||||
#include "GrResourceKey.h"
|
||||
|
||||
class GrContext;
|
||||
class GrTexture;
|
||||
class GrTextureParams;
|
||||
class GrUniqueKey;
|
||||
class SkBitmap;
|
||||
|
||||
/**
|
||||
* Different GPUs and API extensions have different requirements with respect to what texture
|
||||
* sampling parameters may be used with textures of various types. This class facilitates making
|
||||
* texture compatible with a given GrTextureParams. It abstracts the source of the original data
|
||||
* which may be an already existing texture, CPU pixels, a codec, ... so that various sources can
|
||||
* be used with common code that scales or copies the data to make it compatible with a
|
||||
* GrTextureParams.
|
||||
*/
|
||||
class GrTextureParamsAdjuster {
|
||||
public:
|
||||
struct CopyParams {
|
||||
GrTextureParams::FilterMode fFilter;
|
||||
int fWidth;
|
||||
int fHeight;
|
||||
};
|
||||
|
||||
GrTextureParamsAdjuster(int width, int height) : fWidth(width), fHeight(height) {}
|
||||
virtual ~GrTextureParamsAdjuster() {}
|
||||
|
||||
int width() const { return fWidth; }
|
||||
int height() const { return fHeight; }
|
||||
|
||||
/** Returns a texture that is safe for use with the params */
|
||||
GrTexture* refTextureForParams(GrContext*, const GrTextureParams&);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Return the maker's "original" texture. It is the responsibility of the maker
|
||||
* to make this efficient ... if the texture is being generated, the maker must handle
|
||||
* caching it (if desired).
|
||||
*/
|
||||
virtual GrTexture* refOriginalTexture(GrContext*) = 0;
|
||||
|
||||
/**
|
||||
* If we need to copy the maker's original texture, the maker is asked to return a key
|
||||
* that identifies its original + the CopyParms parameter. If the maker does not want to cache
|
||||
* the stretched version (e.g. the maker is volatile), this should simply return without
|
||||
* initializing the copyKey.
|
||||
*/
|
||||
virtual void makeCopyKey(const CopyParams&, GrUniqueKey* copyKey) = 0;
|
||||
|
||||
/**
|
||||
* Return a new (uncached) texture that is the stretch of the maker's original.
|
||||
*
|
||||
* The base-class handles general logic for this, and only needs access to the following
|
||||
* methods:
|
||||
* - onRefOriginalTexture()
|
||||
* - onGetROBitmap()
|
||||
*
|
||||
* Subclass may override this if they can handle creating the texture more directly than
|
||||
* by copying.
|
||||
*/
|
||||
virtual GrTexture* generateTextureForParams(GrContext*, const CopyParams&);
|
||||
|
||||
/**
|
||||
* If a stretched version of the texture is generated, it may be cached (assuming that
|
||||
* onMakeParamsKey() returns true). In that case, the maker is notified in case it
|
||||
* wants to note that for when the maker is destroyed.
|
||||
*/
|
||||
virtual void didCacheCopy(const GrUniqueKey& copyKey) = 0;
|
||||
|
||||
/**
|
||||
* Some GPUs are unreliable w/ very small texture sizes. If we run into that case, this
|
||||
* method will be called (in service of onGenerateParamsTexture) to return a raster version
|
||||
* of the original texture.
|
||||
*/
|
||||
virtual bool getROBitmap(SkBitmap*) = 0;
|
||||
|
||||
/** Helper for creating a key for a copy from an original key. */
|
||||
static void MakeCopyKeyFromOrigKey(const GrUniqueKey& origKey,
|
||||
const CopyParams& copyParams,
|
||||
GrUniqueKey* copyKey) {
|
||||
SkASSERT(!copyKey->isValid());
|
||||
if (origKey.isValid()) {
|
||||
static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
|
||||
GrUniqueKey::Builder builder(copyKey, origKey, kDomain, 3);
|
||||
builder[0] = copyParams.fFilter;
|
||||
builder[1] = copyParams.fWidth;
|
||||
builder[2] = copyParams.fHeight;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
const int fWidth;
|
||||
const int fHeight;
|
||||
};
|
||||
|
||||
#endif
|
424
src/gpu/SkGr.cpp
424
src/gpu/SkGr.cpp
@ -5,13 +5,13 @@
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "GrTextureMaker.h"
|
||||
|
||||
#include "SkGr.h"
|
||||
|
||||
#include "GrCaps.h"
|
||||
#include "GrContext.h"
|
||||
#include "GrDrawContext.h"
|
||||
#include "GrTextureParamsAdjuster.h"
|
||||
#include "GrGpuResourcePriv.h"
|
||||
#include "GrXferProcessor.h"
|
||||
#include "GrYUVProvider.h"
|
||||
|
||||
@ -48,64 +48,17 @@ GrSurfaceDesc GrImageInfoToSurfaceDesc(const SkImageInfo& info) {
|
||||
return desc;
|
||||
}
|
||||
|
||||
static void get_stretch(const GrCaps& caps, int width, int height,
|
||||
const GrTextureParams& params, SkGrStretch* stretch) {
|
||||
stretch->fType = SkGrStretch::kNone_Type;
|
||||
bool doStretch = false;
|
||||
if (params.isTiled() && !caps.npotTextureTileSupport() &&
|
||||
(!SkIsPow2(width) || !SkIsPow2(height))) {
|
||||
doStretch = true;
|
||||
stretch->fWidth = GrNextPow2(SkTMax(width, caps.minTextureSize()));
|
||||
stretch->fHeight = GrNextPow2(SkTMax(height, caps.minTextureSize()));
|
||||
} else if (width < caps.minTextureSize() || height < caps.minTextureSize()) {
|
||||
// The small texture issues appear to be with tiling. Hence it seems ok to scale them
|
||||
// up using the GPU. If issues persist we may need to CPU-stretch.
|
||||
doStretch = true;
|
||||
stretch->fWidth = SkTMax(width, caps.minTextureSize());
|
||||
stretch->fHeight = SkTMax(height, caps.minTextureSize());
|
||||
}
|
||||
if (doStretch) {
|
||||
switch (params.filterMode()) {
|
||||
case GrTextureParams::kNone_FilterMode:
|
||||
stretch->fType = SkGrStretch::kNearest_Type;
|
||||
break;
|
||||
case GrTextureParams::kBilerp_FilterMode:
|
||||
case GrTextureParams::kMipMap_FilterMode:
|
||||
stretch->fType = SkGrStretch::kBilerp_Type;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
stretch->fWidth = -1;
|
||||
stretch->fHeight = -1;
|
||||
stretch->fType = SkGrStretch::kNone_Type;
|
||||
}
|
||||
}
|
||||
|
||||
static void make_unstretched_key(GrUniqueKey* key, uint32_t imageID, const SkIRect& subset) {
|
||||
SkASSERT(SkIsU16(subset.width()));
|
||||
SkASSERT(SkIsU16(subset.height()));
|
||||
|
||||
static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
|
||||
GrUniqueKey::Builder builder(key, kDomain, 4);
|
||||
void GrMakeKeyFromImageID(GrUniqueKey* key, uint32_t imageID, const SkIRect& imageBounds) {
|
||||
SkASSERT(key);
|
||||
SkASSERT(imageID);
|
||||
SkASSERT(!imageBounds.isEmpty());
|
||||
static const GrUniqueKey::Domain kImageIDDomain = GrUniqueKey::GenerateDomain();
|
||||
GrUniqueKey::Builder builder(key, kImageIDDomain, 5);
|
||||
builder[0] = imageID;
|
||||
builder[1] = subset.x();
|
||||
builder[2] = subset.y();
|
||||
builder[3] = subset.width() | (subset.height() << 16);
|
||||
}
|
||||
|
||||
void GrMakeKeyFromImageID(GrUniqueKey* key, uint32_t imageID, const SkIRect& subset,
|
||||
const GrCaps& caps, const GrTextureParams& params) {
|
||||
SkGrStretch stretch;
|
||||
get_stretch(caps, subset.width(), subset.height(), params, &stretch);
|
||||
if (SkGrStretch::kNone_Type != stretch.fType) {
|
||||
GrUniqueKey tmpKey;
|
||||
make_unstretched_key(&tmpKey, imageID, subset);
|
||||
if (!GrMakeStretchedKey(tmpKey, stretch, key)) {
|
||||
*key = tmpKey;
|
||||
}
|
||||
} else {
|
||||
make_unstretched_key(key, imageID, subset);
|
||||
}
|
||||
builder[1] = imageBounds.fLeft;
|
||||
builder[2] = imageBounds.fTop;
|
||||
builder[3] = imageBounds.fRight;
|
||||
builder[4] = imageBounds.fBottom;
|
||||
}
|
||||
|
||||
GrPixelConfig GrIsCompressedTextureDataSupported(GrContext* ctx, SkData* data,
|
||||
@ -150,16 +103,19 @@ GrPixelConfig GrIsCompressedTextureDataSupported(GrContext* ctx, SkData* data,
|
||||
return kUnknown_GrPixelConfig;
|
||||
}
|
||||
|
||||
/* Fill out buffer with the compressed format Ganesh expects from a colortable
|
||||
based bitmap. [palette (colortable) + indices].
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
At the moment Ganesh only supports 8bit version. If Ganesh allowed we others
|
||||
we could detect that the colortable.count is <= 16, and then repack the
|
||||
indices as nibbles to save RAM, but it would take more time (i.e. a lot
|
||||
slower than memcpy), so skipping that for now.
|
||||
|
||||
Ganesh wants a full 256 palette entry, even though Skia's ctable is only as big
|
||||
as the colortable.count says it is.
|
||||
/**
|
||||
* Fill out buffer with the compressed format Ganesh expects from a colortable
|
||||
* based bitmap. [palette (colortable) + indices].
|
||||
*
|
||||
* At the moment Ganesh only supports 8bit version. If Ganesh allowed we others
|
||||
* we could detect that the colortable.count is <= 16, and then repack the
|
||||
* indices as nibbles to save RAM, but it would take more time (i.e. a lot
|
||||
* slower than memcpy), so skipping that for now.
|
||||
*
|
||||
* Ganesh wants a full 256 palette entry, even though Skia's ctable is only as big
|
||||
* as the colortable.count says it is.
|
||||
*/
|
||||
static void build_index8_data(void* buffer, const SkBitmap& bitmap) {
|
||||
SkASSERT(kIndex_8_SkColorType == bitmap.colorType());
|
||||
@ -207,130 +163,7 @@ static void build_index8_data(void* buffer, const SkBitmap& bitmap) {
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
bool GrMakeStretchedKey(const GrUniqueKey& origKey, const SkGrStretch& stretch,
|
||||
GrUniqueKey* stretchedKey) {
|
||||
if (origKey.isValid() && SkGrStretch::kNone_Type != stretch.fType) {
|
||||
uint32_t width = SkToU16(stretch.fWidth);
|
||||
uint32_t height = SkToU16(stretch.fHeight);
|
||||
static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
|
||||
GrUniqueKey::Builder builder(stretchedKey, origKey, kDomain, 2);
|
||||
builder[0] = stretch.fType;
|
||||
builder[1] = width | (height << 16);
|
||||
builder.finish();
|
||||
return true;
|
||||
}
|
||||
SkASSERT(!stretchedKey->isValid());
|
||||
return false;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// When the SkPixelRef genID changes, invalidate a corresponding GrResource described by key.
|
||||
class BitmapInvalidator : public SkPixelRef::GenIDChangeListener {
|
||||
public:
|
||||
explicit BitmapInvalidator(const GrUniqueKey& key) : fMsg(key) {}
|
||||
private:
|
||||
GrUniqueKeyInvalidatedMessage fMsg;
|
||||
|
||||
void onChange() override {
|
||||
SkMessageBus<GrUniqueKeyInvalidatedMessage>::Post(fMsg);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
GrTexture* GrCreateTextureForPixels(GrContext* ctx,
|
||||
const GrUniqueKey& optionalKey,
|
||||
GrSurfaceDesc desc,
|
||||
SkPixelRef* pixelRefForInvalidationNotification,
|
||||
const void* pixels,
|
||||
size_t rowBytes) {
|
||||
GrTexture* result = ctx->textureProvider()->createTexture(desc, true, pixels, rowBytes);
|
||||
if (result && optionalKey.isValid()) {
|
||||
if (pixelRefForInvalidationNotification) {
|
||||
BitmapInvalidator* listener = new BitmapInvalidator(optionalKey);
|
||||
pixelRefForInvalidationNotification->addGenIDChangeListener(listener);
|
||||
}
|
||||
ctx->textureProvider()->assignUniqueKeyToTexture(optionalKey, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// creates a new texture that is the input texture scaled up. If optionalKey is valid it will be
|
||||
// set on the new texture. stretch controls whether the scaling is done using nearest or bilerp
|
||||
// filtering and the size to stretch the texture to.
|
||||
GrTexture* stretch_texture(GrTexture* inputTexture, const SkGrStretch& stretch,
|
||||
SkPixelRef* pixelRef,
|
||||
const GrUniqueKey& optionalKey) {
|
||||
SkASSERT(SkGrStretch::kNone_Type != stretch.fType);
|
||||
|
||||
GrContext* context = inputTexture->getContext();
|
||||
SkASSERT(context);
|
||||
const GrCaps* caps = context->caps();
|
||||
|
||||
// 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;
|
||||
rtDesc.fWidth = stretch.fWidth;
|
||||
rtDesc.fHeight = stretch.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 (!caps->isConfigRenderable(rtDesc.fConfig, false)) {
|
||||
if (GrPixelConfigIsAlphaOnly(rtDesc.fConfig)) {
|
||||
if (caps->isConfigRenderable(kAlpha_8_GrPixelConfig, false)) {
|
||||
rtDesc.fConfig = kAlpha_8_GrPixelConfig;
|
||||
} else if (caps->isConfigRenderable(kSkia8888_GrPixelConfig, false)) {
|
||||
rtDesc.fConfig = kSkia8888_GrPixelConfig;
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
} else if (kRGB_GrColorComponentFlags ==
|
||||
(kRGB_GrColorComponentFlags & GrPixelConfigComponentMask(rtDesc.fConfig))) {
|
||||
if (caps->isConfigRenderable(kSkia8888_GrPixelConfig, false)) {
|
||||
rtDesc.fConfig = kSkia8888_GrPixelConfig;
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
SkAutoTUnref<GrTexture> stretched(GrCreateTextureForPixels(context, optionalKey, rtDesc,
|
||||
pixelRef, nullptr,0));
|
||||
if (!stretched) {
|
||||
return nullptr;
|
||||
}
|
||||
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,
|
||||
SkGrStretch::kBilerp_Type == stretch.fType ?
|
||||
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);
|
||||
|
||||
SkAutoTUnref<GrDrawContext> drawContext(context->drawContext(stretched->asRenderTarget()));
|
||||
if (!drawContext) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
drawContext->drawNonAARectToRect(GrClip::WideOpen(), paint, SkMatrix::I(), rect, localRect);
|
||||
|
||||
return stretched.detach();
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Once we have made SkImages handle all lazy/deferred/generated content, the YUV apis will
|
||||
* be gone from SkPixelRef, and we can remove this subclass entirely.
|
||||
*/
|
||||
@ -350,33 +183,22 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
static GrTexture* load_yuv_texture(GrContext* ctx, const GrUniqueKey& optionalKey,
|
||||
const SkBitmap& bm, const GrSurfaceDesc& desc) {
|
||||
static GrTexture* create_texture_from_yuv(GrContext* ctx, const SkBitmap& bm,
|
||||
const GrSurfaceDesc& desc) {
|
||||
// Subsets are not supported, the whole pixelRef is loaded when using YUV decoding
|
||||
SkPixelRef* pixelRef = bm.pixelRef();
|
||||
if ((nullptr == pixelRef) ||
|
||||
(pixelRef->info().width() != bm.info().width()) ||
|
||||
(pixelRef->info().width() != bm.info().width()) ||
|
||||
(pixelRef->info().height() != bm.info().height())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const bool useCache = optionalKey.isValid();
|
||||
PixelRef_GrYUVProvider provider(pixelRef);
|
||||
GrTexture* texture = provider.refAsTexture(ctx, desc, useCache);
|
||||
if (!texture) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (useCache) {
|
||||
BitmapInvalidator* listener = new BitmapInvalidator(optionalKey);
|
||||
pixelRef->addGenIDChangeListener(listener);
|
||||
ctx->textureProvider()->assignUniqueKeyToTexture(optionalKey, texture);
|
||||
}
|
||||
return texture;
|
||||
return provider.refAsTexture(ctx, desc, !bm.isVolatile());
|
||||
}
|
||||
|
||||
static GrTexture* load_etc1_texture(GrContext* ctx, const GrUniqueKey& optionalKey,
|
||||
const SkBitmap &bm, GrSurfaceDesc desc) {
|
||||
static GrTexture* load_etc1_texture(GrContext* ctx, const SkBitmap &bm, GrSurfaceDesc desc) {
|
||||
SkAutoTUnref<SkData> data(bm.pixelRef()->refEncodedData());
|
||||
if (!data) {
|
||||
return nullptr;
|
||||
@ -389,19 +211,19 @@ static GrTexture* load_etc1_texture(GrContext* ctx, const GrUniqueKey& optionalK
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return GrCreateTextureForPixels(ctx, optionalKey, desc, bm.pixelRef(), startOfTexData, 0);
|
||||
return ctx->textureProvider()->createTexture(desc, true, startOfTexData, 0);
|
||||
}
|
||||
|
||||
static GrTexture* create_unstretched_bitmap_texture(GrContext* ctx,
|
||||
const SkBitmap& origBitmap,
|
||||
const GrUniqueKey& optionalKey) {
|
||||
if (origBitmap.width() < ctx->caps()->minTextureSize() ||
|
||||
origBitmap.height() < ctx->caps()->minTextureSize()) {
|
||||
GrTexture* GrUploadBitmapToTexture(GrContext* ctx, const SkBitmap& bmp) {
|
||||
SkASSERT(!bmp.getTexture());
|
||||
|
||||
if (bmp.width() < ctx->caps()->minTextureSize() ||
|
||||
bmp.height() < ctx->caps()->minTextureSize()) {
|
||||
return nullptr;
|
||||
}
|
||||
SkBitmap tmpBitmap;
|
||||
|
||||
const SkBitmap* bitmap = &origBitmap;
|
||||
SkBitmap tmpBitmap;
|
||||
const SkBitmap* bitmap = &bmp;
|
||||
|
||||
GrSurfaceDesc desc = GrImageInfoToSurfaceDesc(bitmap->info());
|
||||
const GrCaps* caps = ctx->caps();
|
||||
@ -411,14 +233,14 @@ static GrTexture* create_unstretched_bitmap_texture(GrContext* ctx,
|
||||
size_t imageSize = GrCompressedFormatDataSize(kIndex_8_GrPixelConfig,
|
||||
bitmap->width(), bitmap->height());
|
||||
SkAutoMalloc storage(imageSize);
|
||||
build_index8_data(storage.get(), origBitmap);
|
||||
build_index8_data(storage.get(), bmp);
|
||||
|
||||
// our compressed data will be trimmed, so pass width() for its
|
||||
// "rowBytes", since they are the same now.
|
||||
return GrCreateTextureForPixels(ctx, optionalKey, desc, origBitmap.pixelRef(),
|
||||
storage.get(), bitmap->width());
|
||||
return ctx->textureProvider()->createTexture(desc, true, storage.get(),
|
||||
bitmap->width());
|
||||
} else {
|
||||
origBitmap.copyTo(&tmpBitmap, kN32_SkColorType);
|
||||
bmp.copyTo(&tmpBitmap, kN32_SkColorType);
|
||||
// now bitmap points to our temp, which has been promoted to 32bits
|
||||
bitmap = &tmpBitmap;
|
||||
desc.fConfig = SkImageInfo2GrPixelConfig(bitmap->info());
|
||||
@ -428,13 +250,16 @@ static GrTexture* create_unstretched_bitmap_texture(GrContext* ctx,
|
||||
// compressed data on 'refEncodedData' and upload it. Probably not good, since if
|
||||
// the bitmap has available pixels, then they might not be what the decompressed
|
||||
// data is.
|
||||
GrTexture *texture = load_etc1_texture(ctx, optionalKey, *bitmap, desc);
|
||||
|
||||
// Really?? We aren't doing this with YUV.
|
||||
|
||||
GrTexture *texture = load_etc1_texture(ctx, *bitmap, desc);
|
||||
if (texture) {
|
||||
return texture;
|
||||
}
|
||||
}
|
||||
|
||||
GrTexture *texture = load_yuv_texture(ctx, optionalKey, *bitmap, desc);
|
||||
GrTexture *texture = create_texture_from_yuv(ctx, *bitmap, desc);
|
||||
if (texture) {
|
||||
return texture;
|
||||
}
|
||||
@ -444,83 +269,90 @@ static GrTexture* create_unstretched_bitmap_texture(GrContext* ctx,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return GrCreateTextureForPixels(ctx, optionalKey, desc, origBitmap.pixelRef(),
|
||||
bitmap->getPixels(), bitmap->rowBytes());
|
||||
return ctx->textureProvider()->createTexture(desc, true, bitmap->getPixels(),
|
||||
bitmap->rowBytes());
|
||||
}
|
||||
|
||||
static SkBitmap stretch_on_cpu(const SkBitmap& bmp, const SkGrStretch& stretch) {
|
||||
SkBitmap stretched;
|
||||
stretched.allocN32Pixels(stretch.fWidth, stretch.fHeight);
|
||||
SkCanvas canvas(stretched);
|
||||
SkPaint paint;
|
||||
switch (stretch.fType) {
|
||||
case SkGrStretch::kNearest_Type:
|
||||
paint.setFilterQuality(kNone_SkFilterQuality);
|
||||
break;
|
||||
case SkGrStretch::kBilerp_Type:
|
||||
paint.setFilterQuality(kLow_SkFilterQuality);
|
||||
break;
|
||||
case SkGrStretch::kNone_Type:
|
||||
SkDEBUGFAIL("Shouldn't get here.");
|
||||
break;
|
||||
}
|
||||
SkRect dstRect = SkRect::MakeWH(SkIntToScalar(stretch.fWidth), SkIntToScalar(stretch.fHeight));
|
||||
canvas.drawBitmapRect(bmp, dstRect, &paint);
|
||||
return stretched;
|
||||
}
|
||||
|
||||
class Bitmap_GrTextureMaker : public GrTextureMaker {
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class Bitmap_GrTextureParamsAdjuster : public GrTextureParamsAdjuster {
|
||||
public:
|
||||
Bitmap_GrTextureMaker(const SkBitmap& bitmap)
|
||||
Bitmap_GrTextureParamsAdjuster(const SkBitmap& bitmap)
|
||||
: INHERITED(bitmap.width(), bitmap.height())
|
||||
, fBitmap(bitmap)
|
||||
{}
|
||||
{
|
||||
if (!bitmap.isVolatile()) {
|
||||
SkIPoint origin = bitmap.pixelRefOrigin();
|
||||
SkIRect subset = SkIRect::MakeXYWH(origin.fX, origin.fY, bitmap.width(),
|
||||
bitmap.height());
|
||||
GrMakeKeyFromImageID(&fOriginalKey, bitmap.pixelRef()->getGenerationID(), subset);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
GrTexture* onRefUnstretchedTexture(GrContext* ctx) override {
|
||||
GrTexture* refOriginalTexture(GrContext* ctx) {
|
||||
GrTexture* tex = fBitmap.getTexture();
|
||||
if (tex) {
|
||||
return SkRef(tex);
|
||||
}
|
||||
|
||||
GrUniqueKey unstretchedKey;
|
||||
make_unstretched_key(&unstretchedKey, fBitmap.getGenerationID(), fBitmap.getSubset());
|
||||
|
||||
GrTexture* result = ctx->textureProvider()->findAndRefTextureByUniqueKey(unstretchedKey);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
return create_unstretched_bitmap_texture(ctx, fBitmap, unstretchedKey);
|
||||
}
|
||||
|
||||
bool onMakeStretchedKey(const SkGrStretch& stretch, GrUniqueKey* stretchedKey) override {
|
||||
if (fBitmap.isVolatile()) {
|
||||
return false;
|
||||
if (fOriginalKey.isValid()) {
|
||||
tex = ctx->textureProvider()->findAndRefTextureByUniqueKey(fOriginalKey);
|
||||
if (tex) {
|
||||
return tex;
|
||||
}
|
||||
}
|
||||
|
||||
GrUniqueKey unstretchedKey;
|
||||
make_unstretched_key(&unstretchedKey, fBitmap.getGenerationID(), fBitmap.getSubset());
|
||||
return GrMakeStretchedKey(unstretchedKey, stretch, stretchedKey);
|
||||
tex = GrUploadBitmapToTexture(ctx, fBitmap);
|
||||
if (tex) {
|
||||
tex->resourcePriv().setUniqueKey(fOriginalKey);
|
||||
InstallInvalidator(fOriginalKey, fBitmap.pixelRef());
|
||||
}
|
||||
return tex;
|
||||
}
|
||||
|
||||
void onNotifyStretchCached(const GrUniqueKey& stretchedKey) override {
|
||||
fBitmap.pixelRef()->addGenIDChangeListener(new BitmapInvalidator(stretchedKey));
|
||||
void makeCopyKey(const CopyParams& copyParams, GrUniqueKey* copyKey) override {
|
||||
if (fOriginalKey.isValid()) {
|
||||
MakeCopyKeyFromOrigKey(fOriginalKey, copyParams, copyKey);
|
||||
}
|
||||
}
|
||||
|
||||
bool onGetROBitmap(SkBitmap* bitmap) override {
|
||||
void didCacheCopy(const GrUniqueKey& copyKey) override {
|
||||
InstallInvalidator(copyKey, fBitmap.pixelRef());
|
||||
}
|
||||
|
||||
bool getROBitmap(SkBitmap* bitmap) override {
|
||||
SkASSERT(!fBitmap.getTexture());
|
||||
*bitmap = fBitmap;
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
const SkBitmap fBitmap;
|
||||
static void InstallInvalidator(const GrUniqueKey& key, SkPixelRef* pixelRef) {
|
||||
class Invalidator : public SkPixelRef::GenIDChangeListener {
|
||||
public:
|
||||
explicit Invalidator(const GrUniqueKey& key) : fMsg(key) {}
|
||||
private:
|
||||
GrUniqueKeyInvalidatedMessage fMsg;
|
||||
|
||||
typedef GrTextureMaker INHERITED;
|
||||
void onChange() override {
|
||||
SkMessageBus<GrUniqueKeyInvalidatedMessage>::Post(fMsg);
|
||||
}
|
||||
};
|
||||
Invalidator* listener = new Invalidator(key);
|
||||
pixelRef->addGenIDChangeListener(listener);
|
||||
}
|
||||
|
||||
const SkBitmap fBitmap;
|
||||
GrUniqueKey fOriginalKey;
|
||||
|
||||
typedef GrTextureParamsAdjuster INHERITED;
|
||||
};
|
||||
|
||||
GrTexture* GrRefCachedBitmapTexture(GrContext* ctx, const SkBitmap& bitmap,
|
||||
const GrTextureParams& params) {
|
||||
return Bitmap_GrTextureMaker(bitmap).refCachedTexture(ctx, params);
|
||||
return Bitmap_GrTextureParamsAdjuster(bitmap).refTextureForParams(ctx, params);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
@ -592,7 +424,6 @@ bool GrPixelConfig2ColorAndProfileType(GrPixelConfig config, SkColorType* ctOut,
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static inline bool blend_requires_shader(const SkXfermode::Mode mode, bool primitiveIsSrc) {
|
||||
@ -842,54 +673,3 @@ GrTextureParams::FilterMode GrSkFilterQualityToGrFilterMode(SkFilterQuality pain
|
||||
}
|
||||
return textureFilterMode;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
GrTexture* GrTextureMaker::refCachedTexture(GrContext* ctx, const GrTextureParams& params) {
|
||||
SkGrStretch stretch;
|
||||
get_stretch(*ctx->caps(), this->width(), this->height(), params, &stretch);
|
||||
|
||||
if (SkGrStretch::kNone_Type == stretch.fType) {
|
||||
return this->onRefUnstretchedTexture(ctx);
|
||||
}
|
||||
|
||||
GrUniqueKey stretchedKey;
|
||||
if (this->onMakeStretchedKey(stretch, &stretchedKey)) {
|
||||
GrTexture* result = ctx->textureProvider()->findAndRefTextureByUniqueKey(stretchedKey);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
GrTexture* result = this->onGenerateStretchedTexture(ctx, stretch);
|
||||
if (!result) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (stretchedKey.isValid()) {
|
||||
ctx->textureProvider()->assignUniqueKeyToTexture(stretchedKey, result);
|
||||
this->onNotifyStretchCached(stretchedKey);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
GrTexture* GrTextureMaker::onGenerateStretchedTexture(GrContext* ctx, const SkGrStretch& stretch) {
|
||||
if (this->width() < ctx->caps()->minTextureSize() ||
|
||||
this->height() < ctx->caps()->minTextureSize())
|
||||
{
|
||||
// we can't trust our ability to use HW to perform the stretch, so we request
|
||||
// a raster instead, and perform the stretch on the CPU.
|
||||
SkBitmap bitmap;
|
||||
if (!this->onGetROBitmap(&bitmap)) {
|
||||
return nullptr;
|
||||
}
|
||||
SkBitmap stretchedBmp = stretch_on_cpu(bitmap, stretch);
|
||||
return create_unstretched_bitmap_texture(ctx, stretchedBmp, GrUniqueKey());
|
||||
} else {
|
||||
SkAutoTUnref<GrTexture> unstretched(this->onRefUnstretchedTexture(ctx));
|
||||
if (!unstretched) {
|
||||
return nullptr;
|
||||
}
|
||||
return stretch_texture(unstretched, stretch, nullptr, GrUniqueKey());
|
||||
}
|
||||
}
|
||||
|
@ -26,34 +26,18 @@ class SkPaint;
|
||||
class SkPixelRef;
|
||||
struct SkIRect;
|
||||
|
||||
struct SkGrStretch {
|
||||
enum Type {
|
||||
kNone_Type,
|
||||
kBilerp_Type,
|
||||
kNearest_Type
|
||||
} fType;
|
||||
int fWidth;
|
||||
int fHeight;
|
||||
};
|
||||
|
||||
/**
|
||||
* Our key includes the offset, width, and height so that bitmaps created by extractSubset()
|
||||
* are unique.
|
||||
*
|
||||
* The imageID is in the shared namespace (see SkNextID::ImageID()
|
||||
* The imageID is in the shared namespace (see SkNextID::ImageID())
|
||||
* - SkBitmap/SkPixelRef
|
||||
* - SkImage
|
||||
* - SkImageGenerator
|
||||
*
|
||||
* Note: width/height must fit in 16bits for this impl.
|
||||
*/
|
||||
void GrMakeKeyFromImageID(GrUniqueKey* key, uint32_t imageID, const SkIRect& imageBounds,
|
||||
const GrCaps&, const GrTextureParams&);
|
||||
|
||||
/**
|
||||
* Given an "unstretched" key, and a stretch rec, produce a stretched key.
|
||||
*/
|
||||
bool GrMakeStretchedKey(const GrUniqueKey& origKey, const SkGrStretch&, GrUniqueKey* stretchedKey);
|
||||
void GrMakeKeyFromImageID(GrUniqueKey* key, uint32_t imageID, const SkIRect& imageBounds);
|
||||
|
||||
/** Converts an SkPaint to a GrPaint for a given GrContext. The matrix is required in order
|
||||
to convert the SkShader (if any) on the SkPaint. The primitive itself has no color. */
|
||||
@ -103,21 +87,24 @@ GrSurfaceDesc GrImageInfoToSurfaceDesc(const SkImageInfo&);
|
||||
bool GrPixelConfig2ColorAndProfileType(GrPixelConfig, SkColorType*, SkColorProfileType*);
|
||||
|
||||
/**
|
||||
* If the compressed data in the SkData is supported (as a texture format, this returns
|
||||
* the pixel-config that should be used, and sets outStartOfDataToUpload to the ptr into
|
||||
* the data where the actual raw data starts (skipping any header bytes).
|
||||
*
|
||||
* If the compressed data is not supported, this returns kUnknown_GrPixelConfig, and
|
||||
* ignores outStartOfDataToUpload.
|
||||
*/
|
||||
* If the compressed data in the SkData is supported (as a texture format, this returns
|
||||
* the pixel-config that should be used, and sets outStartOfDataToUpload to the ptr into
|
||||
* the data where the actual raw data starts (skipping any header bytes).
|
||||
*
|
||||
* If the compressed data is not supported, this returns kUnknown_GrPixelConfig, and
|
||||
* ignores outStartOfDataToUpload.
|
||||
*/
|
||||
GrPixelConfig GrIsCompressedTextureDataSupported(GrContext* ctx, SkData* data,
|
||||
int expectedW, int expectedH,
|
||||
const void** outStartOfDataToUpload);
|
||||
|
||||
GrTexture* GrCreateTextureForPixels(GrContext*, const GrUniqueKey& optionalKey, GrSurfaceDesc,
|
||||
SkPixelRef* pixelRefForInvalidationNotificationOrNull,
|
||||
const void* pixels, size_t rowBytesOrZero);
|
||||
|
||||
/**
|
||||
* Creates a new texture for the bitmap. Does not concern itself with cache keys or texture params.
|
||||
* The bitmap must have CPU-accessible pixels. Attempts to take advantage of faster paths for
|
||||
* compressed textures and yuv planes.
|
||||
*/
|
||||
GrTexture* GrUploadBitmapToTexture(GrContext*, const SkBitmap&);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
@ -10,7 +10,7 @@
|
||||
#include "GrCaps.h"
|
||||
#include "GrContext.h"
|
||||
#include "GrDrawContext.h"
|
||||
#include "GrTextureMaker.h"
|
||||
#include "GrTextureParamsAdjuster.h"
|
||||
#include "effects/GrYUVtoRGBEffect.h"
|
||||
#include "SkCanvas.h"
|
||||
#include "SkGpuDevice.h"
|
||||
@ -64,56 +64,51 @@ bool SkImage_Gpu::getROPixels(SkBitmap* dst) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
static void make_raw_texture_stretched_key(uint32_t imageID, const SkGrStretch& stretch,
|
||||
static void make_raw_texture_stretched_key(uint32_t imageID,
|
||||
const GrTextureParamsAdjuster::CopyParams& params,
|
||||
GrUniqueKey* stretchedKey) {
|
||||
SkASSERT(SkGrStretch::kNone_Type != stretch.fType);
|
||||
|
||||
uint32_t width = SkToU16(stretch.fWidth);
|
||||
uint32_t height = SkToU16(stretch.fHeight);
|
||||
|
||||
static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
|
||||
GrUniqueKey::Builder builder(stretchedKey, kDomain, 3);
|
||||
GrUniqueKey::Builder builder(stretchedKey, kDomain, 4);
|
||||
builder[0] = imageID;
|
||||
builder[1] = stretch.fType;
|
||||
builder[2] = width | (height << 16);
|
||||
builder.finish();
|
||||
builder[1] = params.fFilter;
|
||||
builder[2] = params.fWidth;
|
||||
builder[3] = params.fHeight;
|
||||
}
|
||||
|
||||
class Texture_GrTextureMaker : public GrTextureMaker {
|
||||
class Texture_GrTextureParamsAdjuster : public GrTextureParamsAdjuster {
|
||||
public:
|
||||
Texture_GrTextureMaker(const SkImage* image, GrTexture* unstretched)
|
||||
Texture_GrTextureParamsAdjuster(const SkImage* image, GrTexture* unstretched)
|
||||
: INHERITED(image->width(), image->height())
|
||||
, fImage(image)
|
||||
, fUnstretched(unstretched)
|
||||
, fOriginal(unstretched)
|
||||
{}
|
||||
|
||||
protected:
|
||||
GrTexture* onRefUnstretchedTexture(GrContext* ctx) override {
|
||||
return SkRef(fUnstretched);
|
||||
GrTexture* refOriginalTexture(GrContext* ctx) override {
|
||||
return SkRef(fOriginal);
|
||||
}
|
||||
|
||||
bool onMakeStretchedKey(const SkGrStretch& stretch, GrUniqueKey* stretchedKey) override {
|
||||
make_raw_texture_stretched_key(fImage->uniqueID(), stretch, stretchedKey);
|
||||
return stretchedKey->isValid();
|
||||
void makeCopyKey(const CopyParams& copyParams, GrUniqueKey* copyKey) override {
|
||||
make_raw_texture_stretched_key(fImage->uniqueID(), copyParams, copyKey);
|
||||
}
|
||||
|
||||
void onNotifyStretchCached(const GrUniqueKey& stretchedKey) override {
|
||||
void didCacheCopy(const GrUniqueKey& copyKey) override {
|
||||
as_IB(fImage)->notifyAddedToCache();
|
||||
}
|
||||
|
||||
bool onGetROBitmap(SkBitmap* bitmap) override {
|
||||
bool getROBitmap(SkBitmap* bitmap) override {
|
||||
return as_IB(fImage)->getROPixels(bitmap);
|
||||
}
|
||||
|
||||
private:
|
||||
const SkImage* fImage;
|
||||
GrTexture* fUnstretched;
|
||||
GrTexture* fOriginal;
|
||||
|
||||
typedef GrTextureMaker INHERITED;
|
||||
typedef GrTextureParamsAdjuster INHERITED;
|
||||
};
|
||||
|
||||
GrTexture* SkImage_Gpu::asTextureRef(GrContext* ctx, const GrTextureParams& params) const {
|
||||
return Texture_GrTextureMaker(this, fTexture).refCachedTexture(ctx, params);
|
||||
return Texture_GrTextureParamsAdjuster(this, fTexture).refTextureForParams(ctx, params);
|
||||
}
|
||||
|
||||
bool SkImage_Gpu::isOpaque() const {
|
||||
|
@ -173,20 +173,6 @@ GrTexture* SkImage_Raster::asTextureRef(GrContext* ctx, const GrTextureParams& p
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// textures (at least the texture-key) only support 16bit dimensions, so abort early
|
||||
// if we're too big.
|
||||
if (fBitmap.width() > 0xFFFF || fBitmap.height() > 0xFFFF) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GrUniqueKey key;
|
||||
GrMakeKeyFromImageID(&key, fBitmap.getGenerationID(),
|
||||
SkIRect::MakeWH(fBitmap.width(), fBitmap.height()),
|
||||
*ctx->caps(), params);
|
||||
|
||||
if (GrTexture* tex = ctx->textureProvider()->findAndRefTextureByUniqueKey(key)) {
|
||||
return tex;
|
||||
}
|
||||
return GrRefCachedBitmapTexture(ctx, fBitmap, params);
|
||||
#endif
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user