Initial work to get ETC1 data up to the GPU
Committed: http://code.google.com/p/skia/source/detail?r=15001 R=bsalomon@google.com, robertphillips@google.com Author: krajcevski@google.com Review URL: https://codereview.chromium.org/302783002
This commit is contained in:
parent
b30aa62294
commit
9c0e629c64
@ -7,6 +7,8 @@
|
||||
|
||||
#include "gm.h"
|
||||
#include "SkCanvas.h"
|
||||
#include "SkData.h"
|
||||
#include "SkDecodingImageGenerator.h"
|
||||
#include "SkImageDecoder.h"
|
||||
#include "SkOSFile.h"
|
||||
|
||||
@ -35,13 +37,20 @@ protected:
|
||||
SkBitmap bm;
|
||||
SkString filename = SkOSPath::SkPathJoin(
|
||||
INHERITED::gResourcePath.c_str(), "mandrill_512.pkm");
|
||||
if (!SkImageDecoder::DecodeFile(filename.c_str(), &bm,
|
||||
SkBitmap::kARGB_8888_Config,
|
||||
SkImageDecoder::kDecodePixels_Mode)) {
|
||||
SkDebugf("Could not decode the file. Did you forget to set the "
|
||||
"resourcePath?\n");
|
||||
|
||||
SkData *fileData = SkData::NewFromFileName(filename.c_str());
|
||||
if (NULL == fileData) {
|
||||
SkDebugf("Could not open the file. Did you forget to set the resourcePath?\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!SkInstallDiscardablePixelRef(
|
||||
SkDecodingImageGenerator::Create(
|
||||
fileData, SkDecodingImageGenerator::Options()), &bm)) {
|
||||
SkDebugf("Could not install discardable pixel ref.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
canvas->drawBitmap(bm, 0, 0);
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
'SK_SUPPORT_GPU=<(skia_gpu)',
|
||||
'SK_SUPPORT_OPENCL=<(skia_opencl)',
|
||||
'SK_FORCE_DISTANCEFIELD_FONTS=<(skia_force_distancefield_fonts)',
|
||||
'SK_SUPPORT_ETC1'
|
||||
],
|
||||
'conditions' : [
|
||||
[ 'skia_arch_type == "arm64"', {
|
||||
|
@ -84,6 +84,7 @@
|
||||
'dependencies': [
|
||||
'core.gyp:*',
|
||||
'utils.gyp:*',
|
||||
'etc1.gyp:libetc1',
|
||||
],
|
||||
'includes': [
|
||||
'gpu.gypi',
|
||||
|
@ -204,10 +204,11 @@ public:
|
||||
* for different wrap modes on GPUs with limited NPOT
|
||||
* texture support). NULL implies clamp wrap modes.
|
||||
* @param desc Description of the texture properties.
|
||||
* @param cacheID Cache-specific properties (e.g., texture gen ID)
|
||||
* @param cacheID Cache-specific properties (e.g., texture gen ID)
|
||||
* @param srcData Pointer to the pixel values.
|
||||
* @param rowBytes The number of bytes between rows of the texture. Zero
|
||||
* implies tightly packed rows.
|
||||
* implies tightly packed rows. For compressed pixel configs, this
|
||||
* field is ignored.
|
||||
* @param cacheKey (optional) If non-NULL, we'll write the cache key we used to cacheKey.
|
||||
*/
|
||||
GrTexture* createTexture(const GrTextureParams* params,
|
||||
@ -216,7 +217,6 @@ public:
|
||||
const void* srcData,
|
||||
size_t rowBytes,
|
||||
GrResourceKey* cacheKey = NULL);
|
||||
|
||||
/**
|
||||
* Search for an entry based on key and dimensions. If found, ref it and return it. The return
|
||||
* value will be NULL if not found. The caller must balance with a call to unref.
|
||||
|
@ -640,6 +640,26 @@ enum GrGLBackendState {
|
||||
kALL_GrGLBackendState = 0xffff
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the data size for the given compressed pixel config
|
||||
*/
|
||||
static inline size_t GrCompressedFormatDataSize(GrPixelConfig config,
|
||||
int width, int height) {
|
||||
SkASSERT(GrPixelConfigIsCompressed(config));
|
||||
|
||||
switch (config) {
|
||||
case kLATC_GrPixelConfig:
|
||||
case kETC1_GrPixelConfig:
|
||||
SkASSERT((width & 3) == 0);
|
||||
SkASSERT((height & 3) == 0);
|
||||
return (width >> 2) * (height >> 2) * 8;
|
||||
|
||||
default:
|
||||
SkFAIL("Unknown compressed pixel config");
|
||||
return 4 * width * height;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This value translates to reseting all the context state for any backend.
|
||||
*/
|
||||
|
@ -388,11 +388,14 @@ GrTexture* GrContext::createTexture(const GrTextureParams* params,
|
||||
|
||||
GrTexture* texture;
|
||||
if (GrTextureImpl::NeedsResizing(resourceKey)) {
|
||||
// We do not know how to resize compressed textures.
|
||||
SkASSERT(!GrPixelConfigIsCompressed(desc.fConfig));
|
||||
|
||||
texture = this->createResizedTexture(desc, cacheID,
|
||||
srcData, rowBytes,
|
||||
GrTextureImpl::NeedsBilerp(resourceKey));
|
||||
} else {
|
||||
texture= fGpu->createTexture(desc, srcData, rowBytes);
|
||||
texture = fGpu->createTexture(desc, srcData, rowBytes);
|
||||
}
|
||||
|
||||
if (NULL != texture) {
|
||||
|
@ -110,25 +110,40 @@ void GrGpu::unimpl(const char msg[]) {
|
||||
|
||||
GrTexture* GrGpu::createTexture(const GrTextureDesc& desc,
|
||||
const void* srcData, size_t rowBytes) {
|
||||
if (kUnknown_GrPixelConfig == desc.fConfig) {
|
||||
if (!this->caps()->isConfigTexturable(desc.fConfig)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ((desc.fFlags & kRenderTarget_GrTextureFlagBit) &&
|
||||
!this->caps()->isConfigRenderable(desc.fConfig, desc.fSampleCnt > 0)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
this->handleDirtyContext();
|
||||
GrTexture* tex = this->onCreateTexture(desc, srcData, rowBytes);
|
||||
if (NULL != tex &&
|
||||
(kRenderTarget_GrTextureFlagBit & desc.fFlags) &&
|
||||
!(kNoStencil_GrTextureFlagBit & desc.fFlags)) {
|
||||
SkASSERT(NULL != tex->asRenderTarget());
|
||||
// TODO: defer this and attach dynamically
|
||||
if (!this->attachStencilBufferToRenderTarget(tex->asRenderTarget())) {
|
||||
tex->unref();
|
||||
GrTexture *tex = NULL;
|
||||
if (GrPixelConfigIsCompressed(desc.fConfig)) {
|
||||
// We shouldn't be rendering into this
|
||||
SkASSERT((desc.fFlags & kRenderTarget_GrTextureFlagBit) == 0);
|
||||
|
||||
if (!this->caps()->npotTextureTileSupport() &&
|
||||
(!GrIsPow2(desc.fWidth) || !GrIsPow2(desc.fHeight))) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
this->handleDirtyContext();
|
||||
tex = this->onCreateCompressedTexture(desc, srcData);
|
||||
} else {
|
||||
this->handleDirtyContext();
|
||||
tex = this->onCreateTexture(desc, srcData, rowBytes);
|
||||
if (NULL != tex &&
|
||||
(kRenderTarget_GrTextureFlagBit & desc.fFlags) &&
|
||||
!(kNoStencil_GrTextureFlagBit & desc.fFlags)) {
|
||||
SkASSERT(NULL != tex->asRenderTarget());
|
||||
// TODO: defer this and attach dynamically
|
||||
if (!this->attachStencilBufferToRenderTarget(tex->asRenderTarget())) {
|
||||
tex->unref();
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
return tex;
|
||||
}
|
||||
|
@ -71,15 +71,26 @@ public:
|
||||
* two but underlying API requires a power of two texture then srcData
|
||||
* will be embedded in a power of two texture. The extra width and height
|
||||
* is filled as though srcData were rendered clamped into the texture.
|
||||
* The exception is when using compressed data formats. In this case, the
|
||||
* desc width and height must be a multiple of the compressed format block
|
||||
* size otherwise this function returns NULL. Similarly, if the underlying
|
||||
* API requires a power of two texture and the source width and height are not
|
||||
* a power of two, then this function returns NULL.
|
||||
*
|
||||
* If kRenderTarget_TextureFlag is specified the GrRenderTarget is
|
||||
* accessible via GrTexture::asRenderTarget(). The texture will hold a ref
|
||||
* on the render target until the texture is destroyed.
|
||||
* on the render target until the texture is destroyed. Compressed textures
|
||||
* cannot have the kRenderTarget_TextureFlag set.
|
||||
*
|
||||
* @param desc describes the texture to be created.
|
||||
* @param srcData texel data to load texture. Begins with full-size
|
||||
* palette data for paletted textures. Contains width*
|
||||
* height texels. If NULL texture data is uninitialized.
|
||||
* palette data for paletted textures. For compressed
|
||||
* formats it contains the compressed pixel data. Otherwise,
|
||||
* it contains width*height texels. If NULL texture data
|
||||
* is uninitialized.
|
||||
* @param rowBytes the number of bytes between consecutive rows. Zero
|
||||
* means rows are tightly packed. This field is ignored
|
||||
* for compressed formats.
|
||||
*
|
||||
* @return The texture object if successful, otherwise NULL.
|
||||
*/
|
||||
@ -414,6 +425,8 @@ private:
|
||||
virtual GrTexture* onCreateTexture(const GrTextureDesc& desc,
|
||||
const void* srcData,
|
||||
size_t rowBytes) = 0;
|
||||
virtual GrTexture* onCreateCompressedTexture(const GrTextureDesc& desc,
|
||||
const void* srcData) = 0;
|
||||
virtual GrTexture* onWrapBackendTexture(const GrBackendTextureDesc&) = 0;
|
||||
virtual GrRenderTarget* onWrapBackendRenderTarget(const GrBackendRenderTargetDesc&) = 0;
|
||||
virtual GrVertexBuffer* onCreateVertexBuffer(size_t size, bool dynamic) = 0;
|
||||
|
@ -8,9 +8,16 @@
|
||||
#include "SkGr.h"
|
||||
#include "SkColorFilter.h"
|
||||
#include "SkConfig8888.h"
|
||||
#include "SkData.h"
|
||||
#include "SkMessageBus.h"
|
||||
#include "SkPixelRef.h"
|
||||
#include "GrResourceCache.h"
|
||||
#include "GrGpu.h"
|
||||
#include "GrDrawTargetCaps.h"
|
||||
|
||||
#if SK_SUPPORT_ETC1
|
||||
# include "etc1.h"
|
||||
#endif
|
||||
|
||||
/* Fill out buffer with the compressed format Ganesh expects from a colortable
|
||||
based bitmap. [palette (colortable) + indices].
|
||||
@ -124,6 +131,50 @@ static void add_genID_listener(GrResourceKey key, SkPixelRef* pixelRef) {
|
||||
pixelRef->addGenIDChangeListener(SkNEW_ARGS(GrResourceInvalidator, (key)));
|
||||
}
|
||||
|
||||
#if SK_SUPPORT_ETC1
|
||||
static GrTexture *load_etc1_texture(GrContext* ctx,
|
||||
const GrTextureParams* params,
|
||||
const SkBitmap &bm, GrTextureDesc desc) {
|
||||
SkData *data = bm.pixelRef()->refEncodedData();
|
||||
|
||||
// Is this even encoded data?
|
||||
if (NULL == data) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Is this a valid PKM encoded data?
|
||||
const uint8_t *bytes = data->bytes();
|
||||
if (!etc1_pkm_is_valid(bytes)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint32_t encodedWidth = etc1_pkm_get_width(bytes);
|
||||
uint32_t encodedHeight = etc1_pkm_get_height(bytes);
|
||||
|
||||
// Does the data match the dimensions of the bitmap? If not,
|
||||
// then we don't know how to scale the image to match it...
|
||||
if (encodedWidth != static_cast<uint32_t>(bm.width()) ||
|
||||
encodedHeight != static_cast<uint32_t>(bm.height())) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Everything seems good... skip ahead to the data.
|
||||
bytes += ETC_PKM_HEADER_SIZE;
|
||||
desc.fConfig = kETC1_GrPixelConfig;
|
||||
|
||||
// This texture is likely to be used again so leave it in the cache
|
||||
GrCacheID cacheID;
|
||||
generate_bitmap_cache_id(bm, &cacheID);
|
||||
|
||||
GrResourceKey key;
|
||||
GrTexture* result = ctx->createTexture(params, desc, cacheID, bytes, 0, &key);
|
||||
if (NULL != result) {
|
||||
add_genID_listener(key, bm.pixelRef());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
#endif // SK_SUPPORT_ETC1
|
||||
|
||||
static GrTexture* sk_gr_create_bitmap_texture(GrContext* ctx,
|
||||
bool cache,
|
||||
const GrTextureParams* params,
|
||||
@ -172,7 +223,16 @@ static GrTexture* sk_gr_create_bitmap_texture(GrContext* ctx,
|
||||
bitmap = &tmpBitmap;
|
||||
desc.fConfig = SkImageInfo2GrPixelConfig(bitmap->info());
|
||||
}
|
||||
|
||||
// Is this an ETC1 encoded texture?
|
||||
#if SK_SUPPORT_ETC1
|
||||
} else if (cache && ctx->getGpu()->caps()->isConfigTexturable(kETC1_GrPixelConfig)) {
|
||||
GrTexture *texture = load_etc1_texture(ctx, params, *bitmap, desc);
|
||||
if (NULL != texture) {
|
||||
return texture;
|
||||
}
|
||||
}
|
||||
#endif // SK_SUPPORT_ETC1
|
||||
|
||||
SkAutoLockPixels alp(*bitmap);
|
||||
if (!bitmap->readyToDraw()) {
|
||||
|
@ -702,6 +702,41 @@ bool GrGpuGL::uploadTexData(const GrGLTexture::Desc& desc,
|
||||
return succeeded;
|
||||
}
|
||||
|
||||
bool GrGpuGL::uploadCompressedTexData(const GrGLTexture::Desc& desc,
|
||||
const void* data) {
|
||||
SkASSERT(NULL != data);
|
||||
|
||||
// No support for software flip y, yet...
|
||||
SkASSERT(kBottomLeft_GrSurfaceOrigin != desc.fOrigin);
|
||||
|
||||
// Make sure that the width and height that we pass to OpenGL
|
||||
// is a multiple of the block size.
|
||||
int dataSize = GrCompressedFormatDataSize(desc.fConfig, desc.fWidth, desc.fHeight);
|
||||
|
||||
// We only need the internal format for compressed 2D textures.
|
||||
GrGLenum internalFormat = 0;
|
||||
if (!this->configToGLFormats(desc.fConfig, false, &internalFormat, NULL, NULL)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool succeeded = true;
|
||||
CLEAR_ERROR_BEFORE_ALLOC(this->glInterface());
|
||||
GL_ALLOC_CALL(this->glInterface(),
|
||||
CompressedTexImage2D(GR_GL_TEXTURE_2D,
|
||||
0, // level
|
||||
internalFormat,
|
||||
desc.fWidth, desc.fHeight,
|
||||
0, // border
|
||||
dataSize,
|
||||
data));
|
||||
|
||||
GrGLenum error = check_alloc_error(desc, this->glInterface());
|
||||
if (error != GR_GL_NO_ERROR) {
|
||||
succeeded = false;
|
||||
}
|
||||
return succeeded;
|
||||
}
|
||||
|
||||
static bool renderbuffer_storage_msaa(GrGLContext& ctx,
|
||||
int sampleCount,
|
||||
GrGLenum format,
|
||||
@ -981,6 +1016,80 @@ GrTexture* GrGpuGL::onCreateTexture(const GrTextureDesc& desc,
|
||||
return tex;
|
||||
}
|
||||
|
||||
GrTexture* GrGpuGL::onCreateCompressedTexture(const GrTextureDesc& desc,
|
||||
const void* srcData) {
|
||||
|
||||
if(SkToBool(desc.fFlags & kRenderTarget_GrTextureFlagBit)) {
|
||||
return return_null_texture();
|
||||
}
|
||||
|
||||
// Make sure that we're not flipping Y.
|
||||
GrSurfaceOrigin texOrigin = resolve_origin(desc.fOrigin, false);
|
||||
if (kBottomLeft_GrSurfaceOrigin == texOrigin) {
|
||||
return return_null_texture();
|
||||
}
|
||||
|
||||
GrGLTexture::Desc glTexDesc;
|
||||
|
||||
glTexDesc.fFlags = desc.fFlags;
|
||||
glTexDesc.fWidth = desc.fWidth;
|
||||
glTexDesc.fHeight = desc.fHeight;
|
||||
glTexDesc.fConfig = desc.fConfig;
|
||||
glTexDesc.fIsWrapped = false;
|
||||
glTexDesc.fOrigin = texOrigin;
|
||||
|
||||
int maxSize = this->caps()->maxTextureSize();
|
||||
if (glTexDesc.fWidth > maxSize || glTexDesc.fHeight > maxSize) {
|
||||
return return_null_texture();
|
||||
}
|
||||
|
||||
GL_CALL(GenTextures(1, &glTexDesc.fTextureID));
|
||||
|
||||
if (!glTexDesc.fTextureID) {
|
||||
return return_null_texture();
|
||||
}
|
||||
|
||||
this->setScratchTextureUnit();
|
||||
GL_CALL(BindTexture(GR_GL_TEXTURE_2D, glTexDesc.fTextureID));
|
||||
|
||||
// Some drivers like to know filter/wrap before seeing glTexImage2D. Some
|
||||
// drivers have a bug where an FBO won't be complete if it includes a
|
||||
// texture that is not mipmap complete (considering the filter in use).
|
||||
GrGLTexture::TexParams initialTexParams;
|
||||
// we only set a subset here so invalidate first
|
||||
initialTexParams.invalidate();
|
||||
initialTexParams.fMinFilter = GR_GL_NEAREST;
|
||||
initialTexParams.fMagFilter = GR_GL_NEAREST;
|
||||
initialTexParams.fWrapS = GR_GL_CLAMP_TO_EDGE;
|
||||
initialTexParams.fWrapT = GR_GL_CLAMP_TO_EDGE;
|
||||
GL_CALL(TexParameteri(GR_GL_TEXTURE_2D,
|
||||
GR_GL_TEXTURE_MAG_FILTER,
|
||||
initialTexParams.fMagFilter));
|
||||
GL_CALL(TexParameteri(GR_GL_TEXTURE_2D,
|
||||
GR_GL_TEXTURE_MIN_FILTER,
|
||||
initialTexParams.fMinFilter));
|
||||
GL_CALL(TexParameteri(GR_GL_TEXTURE_2D,
|
||||
GR_GL_TEXTURE_WRAP_S,
|
||||
initialTexParams.fWrapS));
|
||||
GL_CALL(TexParameteri(GR_GL_TEXTURE_2D,
|
||||
GR_GL_TEXTURE_WRAP_T,
|
||||
initialTexParams.fWrapT));
|
||||
|
||||
if (!this->uploadCompressedTexData(glTexDesc, srcData)) {
|
||||
GL_CALL(DeleteTextures(1, &glTexDesc.fTextureID));
|
||||
return return_null_texture();
|
||||
}
|
||||
|
||||
GrGLTexture* tex;
|
||||
tex = SkNEW_ARGS(GrGLTexture, (this, glTexDesc));
|
||||
tex->setCachedTexParams(initialTexParams, this->getResetTimestamp());
|
||||
#ifdef TRACE_TEXTURE_CREATION
|
||||
GrPrintf("--- new compressed texture [%d] size=(%d %d) config=%d\n",
|
||||
glTexDesc.fTextureID, desc.fWidth, desc.fHeight, desc.fConfig);
|
||||
#endif
|
||||
return tex;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
const GrGLuint kUnknownBitCount = GrGLStencilBuffer::kUnknownBitCount;
|
||||
|
@ -124,6 +124,8 @@ private:
|
||||
virtual GrTexture* onCreateTexture(const GrTextureDesc& desc,
|
||||
const void* srcData,
|
||||
size_t rowBytes) SK_OVERRIDE;
|
||||
virtual GrTexture* onCreateCompressedTexture(const GrTextureDesc& desc,
|
||||
const void* srcData) SK_OVERRIDE;
|
||||
virtual GrVertexBuffer* onCreateVertexBuffer(size_t size, bool dynamic) SK_OVERRIDE;
|
||||
virtual GrIndexBuffer* onCreateIndexBuffer(size_t size, bool dynamic) SK_OVERRIDE;
|
||||
virtual GrPath* onCreatePath(const SkPath&, const SkStrokeRec&) SK_OVERRIDE;
|
||||
@ -265,6 +267,10 @@ private:
|
||||
const void* data,
|
||||
size_t rowBytes);
|
||||
|
||||
// helper for onCreateCompressedTexture
|
||||
bool uploadCompressedTexData(const GrGLTexture::Desc& desc,
|
||||
const void* data);
|
||||
|
||||
bool createRenderTargetObjects(int width, int height,
|
||||
GrGLuint texID,
|
||||
GrGLRenderTarget::Desc* desc);
|
||||
|
Loading…
Reference in New Issue
Block a user