From 6b268191c82bcdcfa1f17b589ba8143b2cacc970 Mon Sep 17 00:00:00 2001
From: SamGondelman <samuel_gondelman@alumni.brown.edu>
Date: Mon, 14 May 2018 15:29:36 -0700
Subject: [PATCH] local compression, patched etc2comp

---
 android/build.gradle                    |  6 +--
 cmake/externals/etc2comp/CMakeLists.txt |  6 ++-
 libraries/gpu/src/gpu/Texture_ktx.cpp   |  2 +-
 libraries/image/src/image/Image.cpp     | 71 ++++++++++++-------------
 4 files changed, 42 insertions(+), 43 deletions(-)

diff --git a/android/build.gradle b/android/build.gradle
index e041142282..71d48e28d4 100644
--- a/android/build.gradle
+++ b/android/build.gradle
@@ -146,9 +146,9 @@ def packages = [
         includeLibs: ['libplugins_libhifiCodec.so']
     ],
     etc2comp: [
-        file: 'etc2comp-armv8-libcpp.tgz',
-        versionId: '1WfqJYWLOP0g22eikYjBzn62gza7MKE6',
-        checksum: '0b4c2e6de728546845f0e471dfdeb604'
+        file: 'etc2comp-patched-armv8-libcpp.tgz',
+        versionId: 'bHhGECRAQR1vkpshBcK6ByNc1BQIM8gU',
+        checksum: '14b02795d774457a33bbc60e00a786bc'
     ]
 ]
 
diff --git a/cmake/externals/etc2comp/CMakeLists.txt b/cmake/externals/etc2comp/CMakeLists.txt
index 9cee0652bc..1de4932380 100644
--- a/cmake/externals/etc2comp/CMakeLists.txt
+++ b/cmake/externals/etc2comp/CMakeLists.txt
@@ -5,10 +5,12 @@ if (ANDROID)
 endif ()
 
 include(ExternalProject)
+# We use a patched version of etc2comp that properly generates all the necessary mips
+# See https://github.com/google/etc2comp/pull/29
 ExternalProject_Add(
   ${EXTERNAL_NAME}
-  URL https://hifi-public.s3.amazonaws.com/dependencies/etc2comp-master.zip
-  URL_MD5 d0969e14af6b10b3306f880c8e2b1184
+  URL https://hifi-public.s3.amazonaws.com/dependencies/etc2comp-patched.zip
+  URL_MD5 6fe3629de8ff99bc99f8c14558d841a3
   CMAKE_ARGS ${ANDROID_CMAKE_ARGS}
   BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build
   INSTALL_COMMAND ""
diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp
index 129c125411..839cb915e2 100644
--- a/libraries/gpu/src/gpu/Texture_ktx.cpp
+++ b/libraries/gpu/src/gpu/Texture_ktx.cpp
@@ -352,7 +352,7 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) {
     if (!Texture::evalKTXFormat(mipFormat, texelFormat, header)) {
         return nullptr;
     }
- 
+
     // Set Dimensions
     uint32_t numFaces = 1;
     switch (texture.getType()) {
diff --git a/libraries/image/src/image/Image.cpp b/libraries/image/src/image/Image.cpp
index 55cafe9571..2fc22b4eac 100644
--- a/libraries/image/src/image/Image.cpp
+++ b/libraries/image/src/image/Image.cpp
@@ -31,14 +31,14 @@ using namespace gpu;
 #define CPU_MIPMAPS 1
 #include <nvtt/nvtt.h>
 
-#ifdef Q_OS_ANDROID
+#ifdef USE_GLES
 #include <Etc.h>
 #include <EtcFilter.h>
 #endif
 
 static const glm::uvec2 SPARSE_PAGE_SIZE(128);
 #ifdef Q_OS_ANDROID
-static const glm::uvec2 MAX_TEXTURE_SIZE(1024);
+static const glm::uvec2 MAX_TEXTURE_SIZE(2048);
 #else
 static const glm::uvec2 MAX_TEXTURE_SIZE(4096);
 #endif
@@ -46,7 +46,6 @@ bool DEV_DECIMATE_TEXTURES = false;
 std::atomic<size_t> DECIMATED_TEXTURE_COUNT{ 0 };
 std::atomic<size_t> RECTIFIED_TEXTURE_COUNT{ 0 };
 
-// TODO: pick compressed android hdr format
 static const auto HDR_FORMAT = gpu::Element::COLOR_R11G11B10;
 
 static std::atomic<bool> compressColorTextures { false };
@@ -563,10 +562,10 @@ void generateLDRMips(gpu::Texture* texture, QImage&& image, const std::atomic<bo
     }
 
     const int width = localCopy.width(), height = localCopy.height();
-    const void* data = static_cast<const void*>(localCopy.constBits());
     auto mipFormat = texture->getStoredMipFormat();
 
-#ifndef Q_OS_ANDROID
+#ifndef USE_GLES
+    const void* data = static_cast<const void*>(localCopy.constBits());
     nvtt::TextureType textureType = nvtt::TextureType_2D;
     nvtt::InputFormat inputFormat = nvtt::InputFormat_BGRA_8UB;
     nvtt::WrapMode wrapMode = nvtt::WrapMode_Mirror;
@@ -681,43 +680,41 @@ void generateLDRMips(gpu::Texture* texture, QImage&& image, const std::atomic<bo
     compressor.process(inputOptions, compressionOptions, outputOptions);
 
 #else
-
-    // TODO: calculate number of mips
-    int numMips = 1;
+    int numMips = 1 + (int)log2(std::max(width, height));
     Etc::RawImage *mipMaps = new Etc::RawImage[numMips];
     Etc::Image::Format etcFormat = Etc::Image::Format::DEFAULT;
 
     if (mipFormat == gpu::Element::COLOR_COMPRESSED_ETC2_RGB) {
-        etcFormat == Etc::Image::Format::RGB8;
+        etcFormat = Etc::Image::Format::RGB8;
     } else if (mipFormat == gpu::Element::COLOR_COMPRESSED_ETC2_SRGB) {
-        etcFormat == Etc::Image::Format::SRGB8;
+        etcFormat = Etc::Image::Format::SRGB8;
     } else if (mipFormat == gpu::Element::COLOR_COMPRESSED_ETC2_RGB_PUNCHTHROUGH_ALPHA) {
-        etcFormat == Etc::Image::Format::RGB8A1;
+        etcFormat = Etc::Image::Format::RGB8A1;
     } else if (mipFormat == gpu::Element::COLOR_COMPRESSED_ETC2_SRGB_PUNCHTHROUGH_ALPHA) {
-        etcFormat == Etc::Image::Format::SRGB8A1;
+        etcFormat = Etc::Image::Format::SRGB8A1;
     } else if (mipFormat == gpu::Element::COLOR_COMPRESSED_ETC2_RGBA) {
-        etcFormat == Etc::Image::Format::RGBA8;
+        etcFormat = Etc::Image::Format::RGBA8;
     } else if (mipFormat == gpu::Element::COLOR_COMPRESSED_ETC2_SRGBA) {
-        etcFormat == Etc::Image::Format::SRGBA8;
+        etcFormat = Etc::Image::Format::SRGBA8;
     } else if (mipFormat == gpu::Element::COLOR_COMPRESSED_EAC_RED) {
-        etcFormat == Etc::Image::Format::R11;
+        etcFormat = Etc::Image::Format::R11;
     } else if (mipFormat == gpu::Element::COLOR_COMPRESSED_EAC_RED_SIGNED) {
-        etcFormat == Etc::Image::Format::SIGNED_R11;
+        etcFormat = Etc::Image::Format::SIGNED_R11;
     } else if (mipFormat == gpu::Element::COLOR_COMPRESSED_EAC_XY) {
-        etcFormat == Etc::Image::Format::RG11;
+        etcFormat = Etc::Image::Format::RG11;
     } else if (mipFormat == gpu::Element::COLOR_COMPRESSED_EAC_XY_SIGNED) {
-        etcFormat == Etc::Image::Format::SIGNED_RG11;
+        etcFormat = Etc::Image::Format::SIGNED_RG11;
     } else {
         qCWarning(imagelogging) << "Unknown mip format";
         Q_UNREACHABLE();
         return;
     }
 
-    // TODO: tune these parameters
     const Etc::ErrorMetric errorMetric = Etc::ErrorMetric::RGBA;
-    const float effort = ETCCOMP_DEFAULT_EFFORT_LEVEL;
+    const float effort = 1.0f;
     const int numEncodeThreads = 4;
     int encodingTime;
+    const float MAX_COLOR = 255.0f;
 
     std::vector<vec4> floatData;
     floatData.resize(width * height);
@@ -725,27 +722,28 @@ void generateLDRMips(gpu::Texture* texture, QImage&& image, const std::atomic<bo
         QRgb *line = (QRgb *) localCopy.scanLine(y);
         for (int x = 0; x < width; x++) {
             QRgb &pixel = line[x];
-            floatData[x + y * width] = vec4(qRed(pixel), qGreen(pixel), qBlue(pixel), qAlpha(pixel)) / 255.0f;
+            floatData[x + y * width] = vec4(qRed(pixel), qGreen(pixel), qBlue(pixel), qAlpha(pixel)) / MAX_COLOR;
         }
     }
 
+    // free up the memory afterward to avoid bloating the heap
+    localCopy = QImage(); // QImage doesn't have a clear function, so override it with an empty one.
+
     Etc::EncodeMipmaps(
         (float *)floatData.data(), width, height,
         etcFormat, errorMetric, effort,
         numEncodeThreads, numEncodeThreads,
-        numMips, Etc::FILTER_WRAP_X | Etc::FILTER_WRAP_Y,
+        numMips, Etc::FILTER_WRAP_NONE,
         mipMaps, &encodingTime
     );
 
-    // free up the memory afterward to avoid bloating the heap
-    data = nullptr;
-    localCopy = QImage(); // QImage doesn't have a clear function, so override it with an empty one.
-
     for (int i = 0; i < numMips; i++) {
-        if (face >= 0) {
-            texture->assignStoredMipFace(i, face, mipMaps[i].uiEncodingBitsBytes, static_cast<const gpu::Byte*>(mipMaps[i].paucEncodingBits.get()));
-        } else {
-            texture->assignStoredMip(i, mipMaps[i].uiEncodingBitsBytes, static_cast<const gpu::Byte*>(mipMaps[i].paucEncodingBits.get()));
+        if (mipMaps[i].paucEncodingBits.get()) {
+            if (face >= 0) {
+                texture->assignStoredMipFace(i, face, mipMaps[i].uiEncodingBitsBytes, static_cast<const gpu::Byte*>(mipMaps[i].paucEncodingBits.get()));
+            } else {
+                texture->assignStoredMip(i, mipMaps[i].uiEncodingBitsBytes, static_cast<const gpu::Byte*>(mipMaps[i].paucEncodingBits.get()));
+            }
         }
     }
 
@@ -755,8 +753,6 @@ void generateLDRMips(gpu::Texture* texture, QImage&& image, const std::atomic<bo
 
 #endif
 
-
-
 void generateMips(gpu::Texture* texture, QImage&& image, const std::atomic<bool>& abortProcessing = false, int face = -1) {
 #if CPU_MIPMAPS
     PROFILE_RANGE(resource_parse, "generateMips");
@@ -828,14 +824,15 @@ gpu::TexturePointer TextureUsage::process2DTextureColorFromImage(QImage&& srcIma
                 formatGPU = gpu::Element::COLOR_COMPRESSED_BCX_SRGB;
             }
         } else {
-#ifdef Q_OS_ANDROID
+#ifdef USE_GLES
             // GLES does not support GL_BGRA
             formatGPU = gpu::Element::COLOR_COMPRESSED_ETC2_SRGBA;
+            formatMip = formatGPU;
 #else
-            formatGPU = gpu::Element::COLOR_SBGRA_32;
+            formatGPU = gpu::Element::COLOR_SRGBA_32;
+            formatMip = gpu::Element::COLOR_SBGRA_32;
 #endif
         }
-        formatMip = formatGPU;
 
         if (isStrict) {
             theTexture = gpu::Texture::createStrict(formatGPU, image.width(), image.height(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR));
@@ -954,7 +951,7 @@ gpu::TexturePointer TextureUsage::process2DTextureNormalMapFromImage(QImage&& sr
         if (isNormalTexturesCompressionEnabled()) {
             formatGPU = gpu::Element::COLOR_COMPRESSED_BCX_XY;
         } else {
-#ifdef Q_OS_ANDROID
+#ifdef USE_GLES
             formatGPU = gpu::Element::COLOR_COMPRESSED_EAC_XY;
 #else
             formatGPU = gpu::Element::VEC2NU8_XY;
@@ -994,7 +991,7 @@ gpu::TexturePointer TextureUsage::process2DTextureGrayscaleFromImage(QImage&& sr
         if (isGrayscaleTexturesCompressionEnabled()) {
             formatGPU = gpu::Element::COLOR_COMPRESSED_BCX_RED;
         } else {
-#ifdef Q_OS_ANDROID
+#ifdef USE_GLES
             formatGPU = gpu::Element::COLOR_COMPRESSED_EAC_RED;
 #else
             formatGPU = gpu::Element::COLOR_R_8;