Initial work to get ETC1 data up to the GPU
authorkrajcevski <krajcevski@google.com>
Mon, 2 Jun 2014 14:38:14 +0000 (07:38 -0700)
committerCommit bot <commit-bot@chromium.org>
Mon, 2 Jun 2014 14:38:15 +0000 (07:38 -0700)
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

gm/etc1bitmap.cpp
gyp/common_conditions.gypi
gyp/gpu.gyp
include/gpu/GrContext.h
include/gpu/GrTypes.h
src/gpu/GrContext.cpp
src/gpu/GrGpu.cpp
src/gpu/GrGpu.h
src/gpu/SkGr.cpp
src/gpu/gl/GrGpuGL.cpp
src/gpu/gl/GrGpuGL.h

index d2cd726..bb8ca6f 100644 (file)
@@ -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);
     }
 
index 2e607e3..46afef5 100644 (file)
@@ -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"', {
index c69a94a..fd81d60 100644 (file)
@@ -84,6 +84,7 @@
       'dependencies': [
         'core.gyp:*',
         'utils.gyp:*',
+        'etc1.gyp:libetc1',
       ],
       'includes': [
         'gpu.gypi',
index 5b7ef25..c54f2e6 100644 (file)
@@ -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.
index 53e633d..34f4e28 100644 (file)
@@ -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;
index 5a59bc1..66588c4 100644 (file)
@@ -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) {
index 111f632..36a9cf1 100644 (file)
@@ -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;
 }
index 11f87e0..cd7502e 100644 (file)
@@ -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;
index 6bd04de..9579866 100644 (file)
@@ -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()) {
index 7275f6f..7568ba3 100644 (file)
@@ -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;
index f548af5..cfb8b52 100644 (file)
@@ -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);