aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar commit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>2014-05-30 18:41:39 +0000
committerGravatar commit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>2014-05-30 18:41:39 +0000
commitaf091a176dbf900a9ff4526ef3b6966a1de44775 (patch)
tree744c1bb783b141ea1e9c333cd6c1b38bdaaab82f
parentb3281585bb6ee3ad8385bf4ba46513af609fa11b (diff)
Initial work to get ETC1 data up to the GPU
R=bsalomon@google.com, robertphillips@google.com Author: krajcevski@google.com Review URL: https://codereview.chromium.org/302783002 git-svn-id: http://skia.googlecode.com/svn/trunk@15001 2bbb7eff-a529-9590-31e7-b0007b416f81
-rw-r--r--gm/etc1bitmap.cpp19
-rw-r--r--gyp/gpu.gyp1
-rw-r--r--include/gpu/GrContext.h6
-rw-r--r--include/gpu/GrTypes.h20
-rw-r--r--src/gpu/GrContext.cpp5
-rw-r--r--src/gpu/GrGpu.cpp35
-rw-r--r--src/gpu/GrGpu.h19
-rw-r--r--src/gpu/SkGr.cpp54
-rw-r--r--src/gpu/gl/GrGpuGL.cpp109
-rw-r--r--src/gpu/gl/GrGpuGL.h6
10 files changed, 252 insertions, 22 deletions
diff --git a/gm/etc1bitmap.cpp b/gm/etc1bitmap.cpp
index d2cd726886..bb8ca6f406 100644
--- a/gm/etc1bitmap.cpp
+++ b/gm/etc1bitmap.cpp
@@ -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);
}
diff --git a/gyp/gpu.gyp b/gyp/gpu.gyp
index c69a94a732..fd81d60314 100644
--- a/gyp/gpu.gyp
+++ b/gyp/gpu.gyp
@@ -84,6 +84,7 @@
'dependencies': [
'core.gyp:*',
'utils.gyp:*',
+ 'etc1.gyp:libetc1',
],
'includes': [
'gpu.gypi',
diff --git a/include/gpu/GrContext.h b/include/gpu/GrContext.h
index 5b7ef25ced..c54f2e6b23 100644
--- a/include/gpu/GrContext.h
+++ b/include/gpu/GrContext.h
@@ -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.
diff --git a/include/gpu/GrTypes.h b/include/gpu/GrTypes.h
index 53e633da57..34f4e2827d 100644
--- a/include/gpu/GrTypes.h
+++ b/include/gpu/GrTypes.h
@@ -641,6 +641,26 @@ enum GrGLBackendState {
};
/**
+ * 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.
*/
static const uint32_t kAll_GrBackendState = 0xffffffff;
diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp
index 5a59bc17fd..66588c46bc 100644
--- a/src/gpu/GrContext.cpp
+++ b/src/gpu/GrContext.cpp
@@ -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) {
diff --git a/src/gpu/GrGpu.cpp b/src/gpu/GrGpu.cpp
index 111f632502..36a9cf19b4 100644
--- a/src/gpu/GrGpu.cpp
+++ b/src/gpu/GrGpu.cpp
@@ -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;
}
diff --git a/src/gpu/GrGpu.h b/src/gpu/GrGpu.h
index 11f87e0458..cd7502e18a 100644
--- a/src/gpu/GrGpu.h
+++ b/src/gpu/GrGpu.h
@@ -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;
diff --git a/src/gpu/SkGr.cpp b/src/gpu/SkGr.cpp
index 6bd04dec3a..6a68c44be4 100644
--- a/src/gpu/SkGr.cpp
+++ b/src/gpu/SkGr.cpp
@@ -8,9 +8,14 @@
#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"
+
+#include "etc1.h"
/* Fill out buffer with the compressed format Ganesh expects from a colortable
based bitmap. [palette (colortable) + indices].
@@ -124,6 +129,48 @@ static void add_genID_listener(GrResourceKey key, SkPixelRef* pixelRef) {
pixelRef->addGenIDChangeListener(SkNEW_ARGS(GrResourceInvalidator, (key)));
}
+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;
+}
+
static GrTexture* sk_gr_create_bitmap_texture(GrContext* ctx,
bool cache,
const GrTextureParams* params,
@@ -172,6 +219,13 @@ static GrTexture* sk_gr_create_bitmap_texture(GrContext* ctx,
bitmap = &tmpBitmap;
desc.fConfig = SkImageInfo2GrPixelConfig(bitmap->info());
}
+
+ // Is this an ETC1 encoded texture?
+ } else if (cache && ctx->getGpu()->caps()->isConfigTexturable(kETC1_GrPixelConfig)) {
+ GrTexture *texture = load_etc1_texture(ctx, params, *bitmap, desc);
+ if (NULL != texture) {
+ return texture;
+ }
}
SkAutoLockPixels alp(*bitmap);
diff --git a/src/gpu/gl/GrGpuGL.cpp b/src/gpu/gl/GrGpuGL.cpp
index 7275f6f73b..7568ba32cf 100644
--- a/src/gpu/gl/GrGpuGL.cpp
+++ b/src/gpu/gl/GrGpuGL.cpp
@@ -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;
diff --git a/src/gpu/gl/GrGpuGL.h b/src/gpu/gl/GrGpuGL.h
index f548af5f6a..cfb8b520ac 100644
--- a/src/gpu/gl/GrGpuGL.h
+++ b/src/gpu/gl/GrGpuGL.h
@@ -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);