Merge pull request #13186 from SamGondelman/etc2comp

Android: Local texture compression using Etc2Comp
This commit is contained in:
Sam Gateau 2018-06-06 11:13:50 -07:00 committed by GitHub
commit c145c0c1b8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 296 additions and 68 deletions

View file

@ -66,19 +66,19 @@ ext {
def baseFolder = new File(HIFI_ANDROID_PRECOMPILED)
def appDir = new File(projectDir, 'app')
def jniFolder = new File(appDir, 'src/main/jniLibs/arm64-v8a')
def baseUrl = ''
def baseUrl = 'https://hifi-public.s3.amazonaws.com/dependencies/android/'
def qtFile='https://hifi-public.s3.amazonaws.com/austin/android/qt-5.9.3_linux_armv8-libcpp_openssl.tgz'
def qtFile='qt-5.9.3_linux_armv8-libcpp_openssl.tgz'
def qtChecksum='04599670ccca84bd2b15f6915568eb2d'
def qtVersionId='PeoqzN31n.YvLfs9JE2SgHgZ4.IaKAlt'
def qtVersionId='8QbCma4ryEPgBYn_8kgYgB10IvNx9I1W'
if (Os.isFamily(Os.FAMILY_MAC)) {
qtFile = 'https://hifi-public.s3.amazonaws.com/austin/android/qt-5.9.3_osx_armv8-libcpp_openssl.tgz'
qtFile = 'qt-5.9.3_osx_armv8-libcpp_openssl.tgz'
qtChecksum='4b02de9d67d6bfb202355a808d2d9c59'
qtVersionId='HygCmtMLPYioyil0DfXckGVzhw2SXZA9'
qtVersionId='2gfgoYCggJGyXxKiazaPGsMs1Gn9j4og'
} else if (Os.isFamily(Os.FAMILY_WINDOWS)) {
qtFile = 'https://hifi-public.s3.amazonaws.com/austin/android/qt-5.9.3_win_armv8-libcpp_openssl.tgz'
qtFile = 'qt-5.9.3_win_armv8-libcpp_openssl.tgz'
qtChecksum='c3e25db64002d0f43cf565e0ef708911'
qtVersionId='HeVObSVLCBoc7yY7He1oBMvPIH0VkClT'
qtVersionId='xKIteC6HO0xrmcWeMmhQcmKyPEsnUrcZ'
}
def packages = [
@ -88,79 +88,84 @@ def packages = [
checksum: qtChecksum,
],
bullet: [
file: 'https://hifi-public.s3.amazonaws.com/dependencies/android/bullet-2.88_armv8-libcpp.tgz',
file: 'bullet-2.88_armv8-libcpp.tgz',
versionId: 'S8YaoED0Cl8sSb8fSV7Q2G1lQJSNDxqg',
checksum: '81642779ccb110f8c7338e8739ac38a0',
],
draco: [
file: 'https://hifi-public.s3.amazonaws.com/austin/android/draco_armv8-libcpp.tgz',
versionId: 'cA3tVJSmkvb1naA3l6D_Jv2Noh.4yc4m',
file: 'draco_armv8-libcpp.tgz',
versionId: '3.B.uBj31kWlgND3_R2xwQzT_TP6Dz_8',
checksum: '617a80d213a5ec69fbfa21a1f2f738cd',
],
glad: [
file: 'https://hifi-public.s3.amazonaws.com/austin/android/glad_armv8-libcpp.zip',
versionId: 'Q9szthzeye8fFyAA.cY26Lgn2B8kezEE',
file: 'glad_armv8-libcpp.zip',
versionId: 'r5Zran.JSCtvrrB6Q4KaqfIoALPw3lYY',
checksum: 'a8ee8584cf1ccd34766c7ddd9d5e5449',
],
glm: [
file: 'https://hifi-public.s3.amazonaws.com/dependencies/android/glm-0.9.8.5-patched.tgz',
file: 'glm-0.9.8.5-patched.tgz',
versionId: 'cskfMoJrFlAeqI3WPxemyO_Cxt7rT9EJ',
checksum: '067b5fe16b220b5b1a1039ba51b062ae',
],
gvr: [
file: 'https://hifi-public.s3.amazonaws.com/austin/android/gvrsdk_v1.101.0.tgz',
versionId: 'UTberAIFraEfF9IVjoV66u1DTPTopgeY',
file: 'gvrsdk_v1.101.0.tgz',
versionId: 'nqBV_j81Uc31rC7bKIrlya_Hah4v3y5r',
checksum: '57fd02baa069176ba18597a29b6b4fc7',
],
nvtt: [
file: 'https://hifi-public.s3.amazonaws.com/austin/android/nvtt_armv8-libcpp.zip',
versionId: 'vLqrqThvpq4gp75BHMAqO6HhfTXaa0An',
file: 'nvtt_armv8-libcpp.zip',
versionId: 'lmkBVR5t4UF1UUwMwEirnk9H_8Nt90IO',
checksum: 'eb46d0b683e66987190ed124aabf8910',
sharedLibFolder: 'lib',
includeLibs: ['libnvtt.so', 'libnvmath.so', 'libnvimage.so', 'libnvcore.so'],
],
openssl: [
file: 'https://hifi-public.s3.amazonaws.com/austin/android/openssl-1.1.0g_armv8.tgz',
versionId: 'DmahmSGFS4ltpHyTdyQvv35WOeUOiib9',
file: 'openssl-1.1.0g_armv8.tgz',
versionId: 'AiiPjmgUZTgNj7YV1EEx2lL47aDvvvAW',
checksum: 'cabb681fbccd79594f65fcc266e02f32',
],
polyvox: [
file: 'https://hifi-public.s3.amazonaws.com/austin/android/polyvox_armv8-libcpp.tgz',
versionId: 'LDJtzMTvdm4SAc2KYg8Cg6uwWk4Vq3e3',
checksum: '349ad5b72aaf2749ca95d847e60c5314',
file: 'polyvox_armv8-libcpp.tgz',
versionId: 'A2kbKiNhpIenGq23bKRRzg7IMAI5BI92',
checksum: 'dba88b3a098747af4bb169e9eb9af57e',
sharedLibFolder: 'lib',
includeLibs: ['Release/libPolyVoxCore.so', 'libPolyVoxUtil.so'],
],
tbb: [
file: 'https://hifi-public.s3.amazonaws.com/austin/android/tbb-2018_U1_armv8_libcpp.tgz',
versionId: 'YZliDD8.Menh1IVXKEuLPeO3xAjJ1UdF',
file: 'tbb-2018_U1_armv8_libcpp.tgz',
versionId: 'mrRbWnv4O4evcM1quRH43RJqimlRtaKB',
checksum: '20768f298f53b195e71b414b0ae240c4',
sharedLibFolder: 'lib/release',
includeLibs: ['libtbb.so', 'libtbbmalloc.so'],
],
hifiAC: [
file: 'https://hifi-public.s3.amazonaws.com/austin/android/libplugins_libhifiCodec.zip',
versionId: 'mzKhsRCgVmloqq5bvE.0IwYK1NjGQc_G',
file: 'libplugins_libhifiCodec.zip',
versionId: 'i31pW.qNbvFOXRxbyiJUxg3sphaFNmZU',
checksum: '9412a8e12c88a4096c1fc843bb9fe52d',
sharedLibFolder: '',
includeLibs: ['libplugins_libhifiCodec.so']
],
etc2comp: [
file: 'etc2comp-patched-armv8-libcpp.tgz',
versionId: 'bHhGECRAQR1vkpshBcK6ByNc1BQIM8gU',
checksum: '14b02795d774457a33bbc60e00a786bc'
]
]
def scribeLocalFile='scribe' + EXEC_SUFFIX
def scribeFile='https://hifi-public.s3.amazonaws.com/austin/android/scribe_linux_x86_64'
def scribeFile='scribe_linux_x86_64'
def scribeChecksum='ca4b904f52f4f993c29175ba96798fa6'
def scribeVersion='wgpf4dB2Ltzg4Lb2jJ4nPFsHoDkmK_OO'
def scribeVersion='u_iTrJDaE95i2abTPXOpPZckGBIim53G'
if (Os.isFamily(Os.FAMILY_MAC)) {
scribeFile = 'https://hifi-public.s3.amazonaws.com/austin/android/scribe_osx_x86_64'
scribeFile = 'scribe_osx_x86_64'
scribeChecksum='72db9d32d4e1e50add755570ac5eb749'
scribeVersion='o_NbPrktzEYtBkQf3Tn7zc1nZWzM52w6'
scribeVersion='DAW0DmnjCRib4MD8x93bgc2Z2MpPojZC'
} else if (Os.isFamily(Os.FAMILY_WINDOWS)) {
scribeFile = 'https://hifi-public.s3.amazonaws.com/austin/android/scribe_win32_x86_64.exe'
scribeFile = 'scribe_win32_x86_64.exe'
scribeChecksum='678e43d290c90fda670c6fefe038a06d'
scribeVersion='GCCJxlmd2irvNOFWfZR0U1UCLHndHQrC'
scribeVersion='PuullrA_bPlO9kXZRt8rLe536X1UI.m7'
}
def options = [
@ -361,6 +366,7 @@ task verifyOpenSSL(type: Verify) { def p = packages['openssl']; src new File(bas
task verifyPolyvox(type: Verify) { def p = packages['polyvox']; src new File(baseFolder, p['file']); checksum p['checksum'] }
task verifyTBB(type: Verify) { def p = packages['tbb']; src new File(baseFolder, p['file']); checksum p['checksum'] }
task verifyHifiAC(type: Verify) { def p = packages['hifiAC']; src new File(baseFolder, p['file']); checksum p['checksum'] }
task verifyEtc2Comp(type: Verify) { def p = packages['etc2comp']; src new File(baseFolder, p['file']); checksum p['checksum'] }
task verifyDependencyDownloads(dependsOn: downloadDependencies) { }
verifyDependencyDownloads.dependsOn verifyQt
@ -371,6 +377,7 @@ verifyDependencyDownloads.dependsOn verifyOpenSSL
verifyDependencyDownloads.dependsOn verifyPolyvox
verifyDependencyDownloads.dependsOn verifyTBB
verifyDependencyDownloads.dependsOn verifyHifiAC
verifyDependencyDownloads.dependsOn verifyEtc2Comp
task extractDependencies(dependsOn: verifyDependencyDownloads) {
doLast {
@ -608,4 +615,4 @@ task testElf (dependsOn: 'externalNativeBuildDebug') {
}
}
}
*/
*/

55
cmake/externals/etc2comp/CMakeLists.txt vendored Normal file
View file

@ -0,0 +1,55 @@
set(EXTERNAL_NAME etc2comp)
if (ANDROID)
set(ANDROID_CMAKE_ARGS "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}" "-DANDROID_NATIVE_API_LEVEL=19")
endif ()
if (APPLE)
set(EXTRA_CMAKE_FLAGS -DCMAKE_CXX_FLAGS=-stdlib=libc++ -DCMAKE_EXE_LINKER_FLAGS=-stdlib=libc++)
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
# We also use part of https://github.com/google/etc2comp/pull/1, which fixes a bug
# that would override CMAKE_CXX_FLAGS
ExternalProject_Add(
${EXTERNAL_NAME}
URL https://hifi-public.s3.amazonaws.com/dependencies/etc2comp-patched.zip
URL_MD5 4c96153eb179acbe619e3d99d3330595
CMAKE_ARGS ${ANDROID_CMAKE_ARGS} ${EXTRA_CMAKE_FLAGS}
BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build
INSTALL_COMMAND ""
LOG_DOWNLOAD 1
LOG_CONFIGURE 1
LOG_BUILD 1
)
# Hide this external target (for ide users)
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR)
ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR)
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
if (WIN32)
set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/build/EtcLib/Debug/EtcLib.lib CACHE FILEPATH "Path to Etc2Comp debug library")
# use generator expression to ensure the correct library is found when building different configurations in VS
set(_LIB_FOLDER "$<$<CONFIG:RelWithDebInfo>:build/EtcLib/RelWithDebInfo>")
set(_LIB_FOLDER "${_LIB_FOLDER}$<$<CONFIG:MinSizeRel>:build/EtcLib/MinSizeRel>")
set(_LIB_FOLDER "${_LIB_FOLDER}$<$<OR:$<CONFIG:Release>,$<CONFIG:Debug>>:build/EtcLib/Release>")
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/${_LIB_FOLDER}/EtcLib.lib CACHE FILEPATH "Path to Etc2Comp release library")
elseif (APPLE)
set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/build/EtcLib/Debug/libEtcLib.a CACHE FILEPATH "Path to EtcLib debug library")
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/build/EtcLib/Release/libEtcLib.a CACHE FILEPATH "Path to EtcLib release library")
else ()
set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG "" CACHE FILEPATH "Path to EtcLib debug library")
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/build/EtcLib/libEtcLib.a CACHE FILEPATH "Path to EtcLib release library")
endif ()
set(ETC_INCLUDE_DIR ${SOURCE_DIR}/EtcLib/Etc CACHE FILEPATH "Path to Etc2Comp/Etc include directory")
set(ETCCODEC_INCLUDE_DIR ${SOURCE_DIR}/EtcLib/EtcCodec CACHE FILEPATH "Path to Etc2Comp/EtcCodec include directory")
# ETC2COMP_INCLUDE_DIRS will be set later by FindEtc2Comp

View file

@ -0,0 +1,22 @@
#
# Copyright 2018 High Fidelity, Inc.
# Created by Sam Gondelman on 5/2/2018
#
# Distributed under the Apache License, Version 2.0.
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
#
macro(TARGET_ETC2COMP)
if (ANDROID)
set(INSTALL_DIR ${HIFI_ANDROID_PRECOMPILED}/etc2comp)
set(ETC2COMP_INCLUDE_DIRS "${INSTALL_DIR}/include/Etc" "${INSTALL_DIR}/include/EtcCodec")
set(ETC2COMP_LIBRARY_DEBUG ${INSTALL_DIR}/lib/libEtcLib.a)
set(ETC2COMP_LIBRARY_RELEASE ${INSTALL_DIR}/lib/libEtcLib.a)
select_library_configurations(ETC2COMP)
else()
add_dependency_external_projects(etc2comp)
find_package(Etc2Comp REQUIRED)
endif()
target_include_directories(${TARGET_NAME} PRIVATE ${ETC2COMP_INCLUDE_DIRS})
target_link_libraries(${TARGET_NAME} ${ETC2COMP_LIBRARIES})
endmacro()

View file

@ -0,0 +1,37 @@
#
# FindEtc2Comp.cmake
#
# Try to find the Etc2Comp compression library.
#
# Once done this will define
#
# ETC2COMP_FOUND - system found Etc2Comp
# ETC2COMP_INCLUDE_DIRS - the Etc2Comp include directory
# ETC2COMP_LIBRARIES - link to this to use Etc2Comp
#
# Created on 5/2/2018 by Sam Gondelman
# Copyright 2018 High Fidelity, Inc.
#
# Distributed under the Apache License, Version 2.0.
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
#
include("${MACRO_DIR}/HifiLibrarySearchHints.cmake")
hifi_library_search_hints("etc2comp")
find_path(ETC_INCLUDE_DIR NAMES Etc.h HINTS ${ETC2COMP_SEARCH_DIRS})
find_path(ETCCODEC_INCLUDE_DIR NAMES EtcBlock4x4.h HINTS ${ETC2COMP_SEARCH_DIRS})
set(ETC2COMP_INCLUDE_DIRS "${ETC_INCLUDE_DIR}" "${ETCCODEC_INCLUDE_DIR}")
find_library(ETC2COMP_LIBRARY_DEBUG NAMES ETC2COMP ETC2COMP_LIB PATH_SUFFIXES EtcLib/Debug HINTS ${ETC2COMP_SEARCH_DIRS})
find_library(ETC2COMP_LIBRARY_RELEASE NAMES ETC2COMP ETC2COMP_LIB PATH_SUFFIXES EtcLib/Release EtcLib HINTS ${ETC2COMP_SEARCH_DIRS})
include(SelectLibraryConfigurations)
select_library_configurations(ETC2COMP)
set(ETC2COMP_LIBRARIES ${ETC2COMP_LIBRARY})
find_package_handle_standard_args(ETC2COMP "Could NOT find ETC2COMP, try to set the path to ETC2COMP root folder in the system variable ETC2COMP_ROOT_DIR or create a directory etc2comp in HIFI_LIB_DIR and paste the necessary files there"
ETC2COMP_INCLUDE_DIRS ETC2COMP_LIBRARIES)
mark_as_advanced(ETC2COMP_INCLUDE_DIRS ETC2COMP_LIBRARIES ETC2COMP_SEARCH_DIRS)

View file

@ -684,9 +684,9 @@ bool sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture, std::vector<
PROFILE_RANGE(render_gpu, "sphericalHarmonicsFromTexture");
#ifndef USE_GLES
auto mipFormat = cubeTexture.getStoredMipFormat();
std::function<glm::vec3(uint32)> unpackFunc;
switch (mipFormat.getSemantic()) {
case gpu::R11G11B10:
unpackFunc = glm::unpackF2x11_1x10;
@ -698,6 +698,7 @@ bool sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture, std::vector<
assert(false);
break;
}
#endif
const uint sqOrder = order*order;
@ -732,7 +733,11 @@ bool sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture, std::vector<
for(int face=0; face < gpu::Texture::NUM_CUBE_FACES; face++) {
PROFILE_RANGE(render_gpu, "ProcessFace");
#ifndef USE_GLES
auto data = reinterpret_cast<const uint32*>( cubeTexture.accessStoredMipFace(0, face)->readData() );
#else
auto data = cubeTexture.accessStoredMipFace(0, face)->readData();
#endif
if (data == nullptr) {
continue;
}
@ -816,8 +821,15 @@ bool sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture, std::vector<
glm::vec3 color{ 0.0f, 0.0f, 0.0f };
for (int i = 0; i < stride; ++i) {
for (int j = 0; j < stride; ++j) {
#ifndef USE_GLES
int k = (int)(x + i - halfStride + (y + j - halfStride) * width);
color += unpackFunc(data[k]);
#else
const int NUM_COMPONENTS_PER_PIXEL = 4;
int k = NUM_COMPONENTS_PER_PIXEL * (int)(x + i - halfStride + (y + j - halfStride) * width);
// BGRA -> RGBA
color += glm::pow(glm::vec3(data[k + 2], data[k + 1], data[k]) / 255.0f, glm::vec3(2.2f));
#endif
}
}

View file

@ -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()) {

View file

@ -2,3 +2,4 @@ set(TARGET_NAME image)
setup_hifi_library()
link_hifi_libraries(shared gpu)
target_nvtt()
target_etc2comp()

View file

@ -31,8 +31,17 @@ using namespace gpu;
#define CPU_MIPMAPS 1
#include <nvtt/nvtt.h>
#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(2048);
#else
static const glm::uvec2 MAX_TEXTURE_SIZE(4096);
#endif
bool DEV_DECIMATE_TEXTURES = false;
std::atomic<size_t> DECIMATED_TEXTURE_COUNT{ 0 };
std::atomic<size_t> RECTIFIED_TEXTURE_COUNT{ 0 };
@ -75,7 +84,13 @@ const QStringList getSupportedFormats() {
return stringFormats;
}
// On GLES, we don't use HDR skyboxes
#ifndef USE_GLES
QImage::Format QIMAGE_HDR_FORMAT = QImage::Format_RGB30;
#else
QImage::Format QIMAGE_HDR_FORMAT = QImage::Format_RGB32;
#endif
TextureUsage::TextureLoader TextureUsage::getTextureLoaderForType(Type type, const QVariantMap& options) {
switch (type) {
@ -548,13 +563,15 @@ void generateLDRMips(gpu::Texture* texture, QImage&& image, const std::atomic<bo
// https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#f18-for-consume-parameters-pass-by-x-and-stdmove-the-parameter
QImage localCopy = std::move(image);
if (localCopy.format() != QImage::Format_ARGB32) {
if (localCopy.format() != QImage::Format_ARGB32 && localCopy.format() != QIMAGE_HDR_FORMAT) {
localCopy = localCopy.convertToFormat(QImage::Format_ARGB32);
}
const int width = localCopy.width(), height = localCopy.height();
const void* data = static_cast<const void*>(localCopy.constBits());
auto mipFormat = texture->getStoredMipFormat();
#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;
@ -584,8 +601,6 @@ void generateLDRMips(gpu::Texture* texture, QImage&& image, const std::atomic<bo
nvtt::CompressionOptions compressionOptions;
compressionOptions.setQuality(nvtt::Quality_Production);
// TODO: gles: generate ETC mips instead?
auto mipFormat = texture->getStoredMipFormat();
if (mipFormat == gpu::Element::COLOR_COMPRESSED_BCX_SRGB) {
compressionOptions.setFormat(nvtt::Format_BC1);
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_BCX_SRGBA_MASK) {
@ -669,21 +684,94 @@ void generateLDRMips(gpu::Texture* texture, QImage&& image, const std::atomic<bo
nvtt::Compressor compressor;
compressor.setTaskDispatcher(&dispatcher);
compressor.process(inputOptions, compressionOptions, outputOptions);
#else
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;
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_ETC2_SRGB) {
etcFormat = Etc::Image::Format::SRGB8;
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_ETC2_RGB_PUNCHTHROUGH_ALPHA) {
etcFormat = Etc::Image::Format::RGB8A1;
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_ETC2_SRGB_PUNCHTHROUGH_ALPHA) {
etcFormat = Etc::Image::Format::SRGB8A1;
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_ETC2_RGBA) {
etcFormat = Etc::Image::Format::RGBA8;
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_ETC2_SRGBA) {
etcFormat = Etc::Image::Format::SRGBA8;
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_EAC_RED) {
etcFormat = Etc::Image::Format::R11;
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_EAC_RED_SIGNED) {
etcFormat = Etc::Image::Format::SIGNED_R11;
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_EAC_XY) {
etcFormat = Etc::Image::Format::RG11;
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_EAC_XY_SIGNED) {
etcFormat = Etc::Image::Format::SIGNED_RG11;
} else {
qCWarning(imagelogging) << "Unknown mip format";
Q_UNREACHABLE();
return;
}
const Etc::ErrorMetric errorMetric = Etc::ErrorMetric::RGBA;
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);
for (int y = 0; y < height; y++) {
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)) / 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_NONE,
mipMaps, &encodingTime
);
for (int i = 0; i < numMips; i++) {
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()));
}
}
}
delete[] mipMaps;
#endif
}
#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");
#ifndef USE_GLES
if (image.format() == QIMAGE_HDR_FORMAT) {
generateHDRMips(texture, std::move(image), abortProcessing, face);
} else {
generateLDRMips(texture, std::move(image), abortProcessing, face);
}
#else
generateLDRMips(texture, std::move(image), abortProcessing, face);
#endif
#else
texture->setAutoGenerateMips(true);
#endif
@ -738,7 +826,6 @@ gpu::TexturePointer TextureUsage::process2DTextureColorFromImage(QImage&& srcIma
gpu::Element formatMip;
gpu::Element formatGPU;
if (isColorTexturesCompressionEnabled()) {
#ifndef USE_GLES
if (validAlpha) {
// NOTE: This disables BC1a compression because it was producing odd artifacts on text textures
// for the tutorial. Instead we use BC3 (which is larger) but doesn't produce the same artifacts).
@ -746,22 +833,15 @@ gpu::TexturePointer TextureUsage::process2DTextureColorFromImage(QImage&& srcIma
} else {
formatGPU = gpu::Element::COLOR_COMPRESSED_BCX_SRGB;
}
#else
if (validAlpha) {
formatGPU = gpu::Element::COLOR_COMPRESSED_ETC2_SRGBA;
} else {
formatGPU = gpu::Element::COLOR_COMPRESSED_ETC2_SRGB;
}
#endif
formatMip = formatGPU;
} else {
#ifdef USE_GLES
// GLES does not support GL_BGRA
formatMip = gpu::Element::COLOR_SRGBA_32;
formatGPU = gpu::Element::COLOR_COMPRESSED_ETC2_SRGBA;
formatMip = formatGPU;
#else
formatGPU = gpu::Element::COLOR_SRGBA_32;
formatMip = gpu::Element::COLOR_SBGRA_32;
#endif
formatGPU = gpu::Element::COLOR_SRGBA_32;
}
if (isStrict) {
@ -876,16 +956,18 @@ gpu::TexturePointer TextureUsage::process2DTextureNormalMapFromImage(QImage&& sr
gpu::TexturePointer theTexture = nullptr;
if ((image.width() > 0) && (image.height() > 0)) {
gpu::Element formatMip = gpu::Element::VEC2NU8_XY;
gpu::Element formatGPU = gpu::Element::VEC2NU8_XY;
gpu::Element formatMip;
gpu::Element formatGPU;
if (isNormalTexturesCompressionEnabled()) {
#ifndef USE_GLES
formatGPU = gpu::Element::COLOR_COMPRESSED_BCX_XY;
#else
} else {
#ifdef USE_GLES
formatGPU = gpu::Element::COLOR_COMPRESSED_EAC_XY;
#else
formatGPU = gpu::Element::VEC2NU8_XY;
#endif
formatMip = formatGPU;
}
formatMip = formatGPU;
theTexture = gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR));
theTexture->setSource(srcImageName);
@ -917,16 +999,15 @@ gpu::TexturePointer TextureUsage::process2DTextureGrayscaleFromImage(QImage&& sr
gpu::Element formatMip;
gpu::Element formatGPU;
if (isGrayscaleTexturesCompressionEnabled()) {
#ifndef USE_GLES
formatGPU = gpu::Element::COLOR_COMPRESSED_BCX_RED;
#else
formatGPU = gpu::Element::COLOR_COMPRESSED_EAC_RED;
#endif
formatMip = formatGPU;
} else {
formatMip = gpu::Element::COLOR_R_8;
#ifdef USE_GLES
formatGPU = gpu::Element::COLOR_COMPRESSED_EAC_RED;
#else
formatGPU = gpu::Element::COLOR_R_8;
#endif
}
formatMip = formatGPU;
theTexture = gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR));
theTexture->setSource(srcImageName);
@ -1283,19 +1364,25 @@ gpu::TexturePointer TextureUsage::processCubeTextureColorFromImage(QImage&& srcI
QImage image = processSourceImage(std::move(localCopy), true);
if (image.format() != QIMAGE_HDR_FORMAT) {
#ifndef USE_GLES
image = convertToHDRFormat(std::move(image), HDR_FORMAT);
#else
image = image.convertToFormat(QImage::Format_RGB32);
#endif
}
gpu::Element formatMip;
gpu::Element formatGPU;
if (isCubeTexturesCompressionEnabled()) {
// TODO: gles: pick HDR ETC format
formatMip = gpu::Element::COLOR_COMPRESSED_BCX_HDR_RGB;
formatGPU = gpu::Element::COLOR_COMPRESSED_BCX_HDR_RGB;
} else {
formatMip = HDR_FORMAT;
#ifdef USE_GLES
formatGPU = gpu::Element::COLOR_COMPRESSED_ETC2_SRGB;
#else
formatGPU = HDR_FORMAT;
#endif
}
formatMip = formatGPU;
// Find the layout of the cubemap in the 2D image
// Use the original image size since processSourceImage may have altered the size / aspect ratio
@ -1342,9 +1429,16 @@ gpu::TexturePointer TextureUsage::processCubeTextureColorFromImage(QImage&& srcI
// Generate irradiance while we are at it
if (generateIrradiance) {
PROFILE_RANGE(resource_parse, "generateIrradiance");
auto irradianceTexture = gpu::Texture::createCube(HDR_FORMAT, faces[0].width(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR, gpu::Sampler::WRAP_CLAMP));
gpu::Element irradianceFormat;
// TODO: we could locally compress the irradiance texture on Android, but we don't need to
#ifndef USE_GLES
irradianceFormat = HDR_FORMAT;
#else
irradianceFormat = gpu::Element::COLOR_SRGBA_32;
#endif
auto irradianceTexture = gpu::Texture::createCube(irradianceFormat, faces[0].width(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR, gpu::Sampler::WRAP_CLAMP));
irradianceTexture->setSource(srcImageName);
irradianceTexture->setStoredMipFormat(HDR_FORMAT);
irradianceTexture->setStoredMipFormat(irradianceFormat);
for (uint8 face = 0; face < faces.size(); ++face) {
irradianceTexture->assignStoredMipFace(0, face, faces[face].byteCount(), faces[face].constBits());
}