Add ETC2 support to Oven

This commit is contained in:
Ryan Huffman 2018-07-12 16:45:04 -07:00
parent e9f23a43f6
commit 9ea08f1850
4 changed files with 348 additions and 324 deletions

View file

@ -138,7 +138,7 @@ void TextureBaker::processTexture() {
// IMPORTANT: _originalTexture is empty past this point // IMPORTANT: _originalTexture is empty past this point
_originalTexture.clear(); _originalTexture.clear();
_outputFiles.push_back(originalCopyFilePath); _outputFiles.push_back(originalCopyFilePath);
meta.original = _metaTexturePathPrefix +_textureURL.fileName(); meta.original = _metaTexturePathPrefix + _textureURL.fileName();
} }
auto buffer = std::static_pointer_cast<QIODevice>(std::make_shared<QFile>(originalCopyFilePath)); auto buffer = std::static_pointer_cast<QIODevice>(std::make_shared<QFile>(originalCopyFilePath));
@ -149,49 +149,56 @@ void TextureBaker::processTexture() {
// Compressed KTX // Compressed KTX
if (_compressionEnabled) { if (_compressionEnabled) {
auto processedTexture = image::processImage(buffer, _textureURL.toString().toStdString(), constexpr std::array<image::BackendTarget, 2> BACKEND_TARGETS {
ABSOLUTE_MAX_TEXTURE_NUM_PIXELS, _textureType, true, _abortProcessing); image::BackendTarget::GL,
if (!processedTexture) { image::BackendTarget::GLES
handleError("Could not process texture " + _textureURL.toString()); };
return; for (auto target : BACKEND_TARGETS) {
} auto processedTexture = image::processImage(buffer, _textureURL.toString().toStdString(),
processedTexture->setSourceHash(hash); ABSOLUTE_MAX_TEXTURE_NUM_PIXELS, _textureType, true,
target, _abortProcessing);
if (!processedTexture) {
handleError("Could not process texture " + _textureURL.toString());
return;
}
processedTexture->setSourceHash(hash);
if (shouldStop()) { if (shouldStop()) {
return; return;
} }
auto memKTX = gpu::Texture::serialize(*processedTexture); auto memKTX = gpu::Texture::serialize(*processedTexture);
if (!memKTX) { if (!memKTX) {
handleError("Could not serialize " + _textureURL.toString() + " to KTX"); handleError("Could not serialize " + _textureURL.toString() + " to KTX");
return; return;
} }
const char* name = khronos::gl::texture::toString(memKTX->_header.getGLInternaFormat()); const char* name = khronos::gl::texture::toString(memKTX->_header.getGLInternaFormat());
if (name == nullptr) { if (name == nullptr) {
handleError("Could not determine internal format for compressed KTX: " + _textureURL.toString()); handleError("Could not determine internal format for compressed KTX: " + _textureURL.toString());
return; return;
} }
const char* data = reinterpret_cast<const char*>(memKTX->_storage->data()); const char* data = reinterpret_cast<const char*>(memKTX->_storage->data());
const size_t length = memKTX->_storage->size(); const size_t length = memKTX->_storage->size();
auto fileName = _baseFilename + "_" + name + ".ktx"; auto fileName = _baseFilename + "_" + name + ".ktx";
auto filePath = _outputDirectory.absoluteFilePath(fileName); auto filePath = _outputDirectory.absoluteFilePath(fileName);
QFile bakedTextureFile { filePath }; QFile bakedTextureFile { filePath };
if (!bakedTextureFile.open(QIODevice::WriteOnly) || bakedTextureFile.write(data, length) == -1) { if (!bakedTextureFile.open(QIODevice::WriteOnly) || bakedTextureFile.write(data, length) == -1) {
handleError("Could not write baked texture for " + _textureURL.toString()); handleError("Could not write baked texture for " + _textureURL.toString());
return; return;
}
_outputFiles.push_back(filePath);
meta.availableTextureTypes[memKTX->_header.getGLInternaFormat()] = _metaTexturePathPrefix + fileName;
} }
_outputFiles.push_back(filePath);
meta.availableTextureTypes[memKTX->_header.getGLInternaFormat()] = _metaTexturePathPrefix + fileName;
} }
// Uncompressed KTX // Uncompressed KTX
if (_textureType == image::TextureUsage::Type::CUBE_TEXTURE) { if (_textureType == image::TextureUsage::Type::CUBE_TEXTURE) {
buffer->reset(); buffer->reset();
auto processedTexture = image::processImage(std::move(buffer), _textureURL.toString().toStdString(), auto processedTexture = image::processImage(std::move(buffer), _textureURL.toString().toStdString(),
ABSOLUTE_MAX_TEXTURE_NUM_PIXELS, _textureType, false, _abortProcessing); ABSOLUTE_MAX_TEXTURE_NUM_PIXELS, _textureType, false, image::BackendTarget::GL, _abortProcessing);
if (!processedTexture) { if (!processedTexture) {
handleError("Could not process texture " + _textureURL.toString()); handleError("Could not process texture " + _textureURL.toString());
return; return;

View file

@ -31,17 +31,13 @@ using namespace gpu;
#define CPU_MIPMAPS 1 #define CPU_MIPMAPS 1
#include <nvtt/nvtt.h> #include <nvtt/nvtt.h>
#ifdef USE_GLES #undef _CRT_SECURE_NO_WARNINGS
#include <Etc.h> #include <Etc.h>
#include <EtcFilter.h> #include <EtcFilter.h>
#endif
static const glm::uvec2 SPARSE_PAGE_SIZE(128); static const glm::uvec2 SPARSE_PAGE_SIZE(128);
#ifdef Q_OS_ANDROID static const glm::uvec2 MAX_TEXTURE_SIZE_GLES(2048);
static const glm::uvec2 MAX_TEXTURE_SIZE(2048); static const glm::uvec2 MAX_TEXTURE_SIZE_GL(4096);
#else
static const glm::uvec2 MAX_TEXTURE_SIZE(4096);
#endif
bool DEV_DECIMATE_TEXTURES = false; bool DEV_DECIMATE_TEXTURES = false;
std::atomic<size_t> DECIMATED_TEXTURE_COUNT{ 0 }; std::atomic<size_t> DECIMATED_TEXTURE_COUNT{ 0 };
std::atomic<size_t> RECTIFIED_TEXTURE_COUNT{ 0 }; std::atomic<size_t> RECTIFIED_TEXTURE_COUNT{ 0 };
@ -83,11 +79,12 @@ const QStringList getSupportedFormats() {
// On GLES, we don't use HDR skyboxes // On GLES, we don't use HDR skyboxes
#ifndef USE_GLES QImage::Format hdrFormatForTarget(BackendTarget target) {
QImage::Format QIMAGE_HDR_FORMAT = QImage::Format_RGB30; if (target == BackendTarget::GLES) {
#else return QImage::Format_RGB32;
QImage::Format QIMAGE_HDR_FORMAT = QImage::Format_RGB32; }
#endif return QImage::Format_RGB30;
}
TextureUsage::TextureLoader TextureUsage::getTextureLoaderForType(Type type, const QVariantMap& options) { TextureUsage::TextureLoader TextureUsage::getTextureLoaderForType(Type type, const QVariantMap& options) {
switch (type) { switch (type) {
@ -123,63 +120,63 @@ TextureUsage::TextureLoader TextureUsage::getTextureLoaderForType(Type type, con
} }
gpu::TexturePointer TextureUsage::createStrict2DTextureFromImage(QImage&& srcImage, const std::string& srcImageName, gpu::TexturePointer TextureUsage::createStrict2DTextureFromImage(QImage&& srcImage, const std::string& srcImageName,
bool compress, const std::atomic<bool>& abortProcessing) { bool compress, BackendTarget target, const std::atomic<bool>& abortProcessing) {
return process2DTextureColorFromImage(std::move(srcImage), srcImageName, compress, true, abortProcessing); return process2DTextureColorFromImage(std::move(srcImage), srcImageName, compress, target, true, abortProcessing);
} }
gpu::TexturePointer TextureUsage::create2DTextureFromImage(QImage&& srcImage, const std::string& srcImageName, gpu::TexturePointer TextureUsage::create2DTextureFromImage(QImage&& srcImage, const std::string& srcImageName,
bool compress, const std::atomic<bool>& abortProcessing) { bool compress, BackendTarget target, const std::atomic<bool>& abortProcessing) {
return process2DTextureColorFromImage(std::move(srcImage), srcImageName, compress, false, abortProcessing); return process2DTextureColorFromImage(std::move(srcImage), srcImageName, compress, target, false, abortProcessing);
} }
gpu::TexturePointer TextureUsage::createAlbedoTextureFromImage(QImage&& srcImage, const std::string& srcImageName, gpu::TexturePointer TextureUsage::createAlbedoTextureFromImage(QImage&& srcImage, const std::string& srcImageName,
bool compress, const std::atomic<bool>& abortProcessing) { bool compress, BackendTarget target, const std::atomic<bool>& abortProcessing) {
return process2DTextureColorFromImage(std::move(srcImage), srcImageName, compress, false, abortProcessing); return process2DTextureColorFromImage(std::move(srcImage), srcImageName, compress, target, false, abortProcessing);
} }
gpu::TexturePointer TextureUsage::createEmissiveTextureFromImage(QImage&& srcImage, const std::string& srcImageName, gpu::TexturePointer TextureUsage::createEmissiveTextureFromImage(QImage&& srcImage, const std::string& srcImageName,
bool compress, const std::atomic<bool>& abortProcessing) { bool compress, BackendTarget target, const std::atomic<bool>& abortProcessing) {
return process2DTextureColorFromImage(std::move(srcImage), srcImageName, compress, false, abortProcessing); return process2DTextureColorFromImage(std::move(srcImage), srcImageName, compress, target, false, abortProcessing);
} }
gpu::TexturePointer TextureUsage::createLightmapTextureFromImage(QImage&& srcImage, const std::string& srcImageName, gpu::TexturePointer TextureUsage::createLightmapTextureFromImage(QImage&& srcImage, const std::string& srcImageName,
bool compress, const std::atomic<bool>& abortProcessing) { bool compress, BackendTarget target, const std::atomic<bool>& abortProcessing) {
return process2DTextureColorFromImage(std::move(srcImage), srcImageName, compress, false, abortProcessing); return process2DTextureColorFromImage(std::move(srcImage), srcImageName, compress, target, false, abortProcessing);
} }
gpu::TexturePointer TextureUsage::createNormalTextureFromNormalImage(QImage&& srcImage, const std::string& srcImageName, gpu::TexturePointer TextureUsage::createNormalTextureFromNormalImage(QImage&& srcImage, const std::string& srcImageName,
bool compress, const std::atomic<bool>& abortProcessing) { bool compress, BackendTarget target, const std::atomic<bool>& abortProcessing) {
return process2DTextureNormalMapFromImage(std::move(srcImage), srcImageName, compress, false, abortProcessing); return process2DTextureNormalMapFromImage(std::move(srcImage), srcImageName, compress, target, false, abortProcessing);
} }
gpu::TexturePointer TextureUsage::createNormalTextureFromBumpImage(QImage&& srcImage, const std::string& srcImageName, gpu::TexturePointer TextureUsage::createNormalTextureFromBumpImage(QImage&& srcImage, const std::string& srcImageName,
bool compress, const std::atomic<bool>& abortProcessing) { bool compress, BackendTarget target, const std::atomic<bool>& abortProcessing) {
return process2DTextureNormalMapFromImage(std::move(srcImage), srcImageName, compress, true, abortProcessing); return process2DTextureNormalMapFromImage(std::move(srcImage), srcImageName, compress, target, true, abortProcessing);
} }
gpu::TexturePointer TextureUsage::createRoughnessTextureFromImage(QImage&& srcImage, const std::string& srcImageName, gpu::TexturePointer TextureUsage::createRoughnessTextureFromImage(QImage&& srcImage, const std::string& srcImageName,
bool compress, const std::atomic<bool>& abortProcessing) { bool compress, BackendTarget target, const std::atomic<bool>& abortProcessing) {
return process2DTextureGrayscaleFromImage(std::move(srcImage), srcImageName, compress, false, abortProcessing); return process2DTextureGrayscaleFromImage(std::move(srcImage), srcImageName, compress, target, false, abortProcessing);
} }
gpu::TexturePointer TextureUsage::createRoughnessTextureFromGlossImage(QImage&& srcImage, const std::string& srcImageName, gpu::TexturePointer TextureUsage::createRoughnessTextureFromGlossImage(QImage&& srcImage, const std::string& srcImageName,
bool compress, const std::atomic<bool>& abortProcessing) { bool compress, BackendTarget target, const std::atomic<bool>& abortProcessing) {
return process2DTextureGrayscaleFromImage(std::move(srcImage), srcImageName, compress, true, abortProcessing); return process2DTextureGrayscaleFromImage(std::move(srcImage), srcImageName, compress, target, true, abortProcessing);
} }
gpu::TexturePointer TextureUsage::createMetallicTextureFromImage(QImage&& srcImage, const std::string& srcImageName, gpu::TexturePointer TextureUsage::createMetallicTextureFromImage(QImage&& srcImage, const std::string& srcImageName,
bool compress, const std::atomic<bool>& abortProcessing) { bool compress, BackendTarget target, const std::atomic<bool>& abortProcessing) {
return process2DTextureGrayscaleFromImage(std::move(srcImage), srcImageName, compress, false, abortProcessing); return process2DTextureGrayscaleFromImage(std::move(srcImage), srcImageName, compress, target, false, abortProcessing);
} }
gpu::TexturePointer TextureUsage::createCubeTextureFromImage(QImage&& srcImage, const std::string& srcImageName, gpu::TexturePointer TextureUsage::createCubeTextureFromImage(QImage&& srcImage, const std::string& srcImageName,
bool compress, const std::atomic<bool>& abortProcessing) { bool compress, BackendTarget target, const std::atomic<bool>& abortProcessing) {
return processCubeTextureColorFromImage(std::move(srcImage), srcImageName, compress, true, abortProcessing); return processCubeTextureColorFromImage(std::move(srcImage), srcImageName, compress, target, true, abortProcessing);
} }
gpu::TexturePointer TextureUsage::createCubeTextureFromImageWithoutIrradiance(QImage&& srcImage, const std::string& srcImageName, gpu::TexturePointer TextureUsage::createCubeTextureFromImageWithoutIrradiance(QImage&& srcImage, const std::string& srcImageName,
bool compress, const std::atomic<bool>& abortProcessing) { bool compress, BackendTarget target, const std::atomic<bool>& abortProcessing) {
return processCubeTextureColorFromImage(std::move(srcImage), srcImageName, compress, false, abortProcessing); return processCubeTextureColorFromImage(std::move(srcImage), srcImageName, compress, target, false, abortProcessing);
} }
static float denormalize(float value, const float minValue) { static float denormalize(float value, const float minValue) {
@ -228,7 +225,7 @@ QImage processRawImageData(QIODevice& content, const std::string& filename) {
gpu::TexturePointer processImage(std::shared_ptr<QIODevice> content, const std::string& filename, gpu::TexturePointer processImage(std::shared_ptr<QIODevice> content, const std::string& filename,
int maxNumPixels, TextureUsage::Type textureType, int maxNumPixels, TextureUsage::Type textureType,
bool compress, const std::atomic<bool>& abortProcessing) { bool compress, BackendTarget target, const std::atomic<bool>& abortProcessing) {
QImage image = processRawImageData(*content.get(), filename); QImage image = processRawImageData(*content.get(), filename);
// Texture content can take up a lot of memory. Here we release our ownership of that content // Texture content can take up a lot of memory. Here we release our ownership of that content
@ -259,12 +256,12 @@ gpu::TexturePointer processImage(std::shared_ptr<QIODevice> content, const std::
} }
auto loader = TextureUsage::getTextureLoaderForType(textureType); auto loader = TextureUsage::getTextureLoaderForType(textureType);
auto texture = loader(std::move(image), filename, compress, abortProcessing); auto texture = loader(std::move(image), filename, compress, target, abortProcessing);
return texture; return texture;
} }
QImage processSourceImage(QImage&& srcImage, bool cubemap) { QImage processSourceImage(QImage&& srcImage, bool cubemap, BackendTarget target) {
PROFILE_RANGE(resource_parse, "processSourceImage"); PROFILE_RANGE(resource_parse, "processSourceImage");
// Take a local copy to force move construction // Take a local copy to force move construction
@ -274,7 +271,8 @@ QImage processSourceImage(QImage&& srcImage, bool cubemap) {
const glm::uvec2 srcImageSize = toGlm(localCopy.size()); const glm::uvec2 srcImageSize = toGlm(localCopy.size());
glm::uvec2 targetSize = srcImageSize; glm::uvec2 targetSize = srcImageSize;
while (glm::any(glm::greaterThan(targetSize, MAX_TEXTURE_SIZE))) { const auto maxTextureSize = target == BackendTarget::GL ? MAX_TEXTURE_SIZE_GL : MAX_TEXTURE_SIZE_GLES;
while (glm::any(glm::greaterThan(targetSize, maxTextureSize))) {
targetSize /= 2; targetSize /= 2;
} }
if (targetSize != srcImageSize) { if (targetSize != srcImageSize) {
@ -406,12 +404,12 @@ public:
} }
}; };
void generateHDRMips(gpu::Texture* texture, QImage&& image, const std::atomic<bool>& abortProcessing, int face) { void generateHDRMips(gpu::Texture* texture, QImage&& image, BackendTarget target, const std::atomic<bool>& abortProcessing, int face) {
// Take a local copy to force move construction // Take a local copy to force move construction
// https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#f18-for-consume-parameters-pass-by-x-and-stdmove-the-parameter // 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); QImage localCopy = std::move(image);
assert(localCopy.format() == QIMAGE_HDR_FORMAT); assert(localCopy.format() == hdrFormatForTarget(target));
const int width = localCopy.width(), height = localCopy.height(); const int width = localCopy.width(), height = localCopy.height();
std::vector<glm::vec4> data; std::vector<glm::vec4> data;
@ -503,220 +501,219 @@ void generateHDRMips(gpu::Texture* texture, QImage&& image, const std::atomic<bo
} }
} }
void generateLDRMips(gpu::Texture* texture, QImage&& image, const std::atomic<bool>& abortProcessing, int face) { void generateLDRMips(gpu::Texture* texture, QImage&& image, BackendTarget target, const std::atomic<bool>& abortProcessing, int face) {
// Take a local copy to force move construction // Take a local copy to force move construction
// https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#f18-for-consume-parameters-pass-by-x-and-stdmove-the-parameter // 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); QImage localCopy = std::move(image);
if (localCopy.format() != QImage::Format_ARGB32 && localCopy.format() != QIMAGE_HDR_FORMAT) { if (localCopy.format() != QImage::Format_ARGB32 && localCopy.format() != hdrFormatForTarget(target)) {
localCopy = localCopy.convertToFormat(QImage::Format_ARGB32); localCopy = localCopy.convertToFormat(QImage::Format_ARGB32);
} }
const int width = localCopy.width(), height = localCopy.height(); const int width = localCopy.width(), height = localCopy.height();
auto mipFormat = texture->getStoredMipFormat(); auto mipFormat = texture->getStoredMipFormat();
#ifndef USE_GLES if (target != BackendTarget::GLES) {
const void* data = static_cast<const void*>(localCopy.constBits()); const void* data = static_cast<const void*>(localCopy.constBits());
nvtt::TextureType textureType = nvtt::TextureType_2D; nvtt::TextureType textureType = nvtt::TextureType_2D;
nvtt::InputFormat inputFormat = nvtt::InputFormat_BGRA_8UB; nvtt::InputFormat inputFormat = nvtt::InputFormat_BGRA_8UB;
nvtt::WrapMode wrapMode = nvtt::WrapMode_Mirror; nvtt::WrapMode wrapMode = nvtt::WrapMode_Mirror;
nvtt::RoundMode roundMode = nvtt::RoundMode_None; nvtt::RoundMode roundMode = nvtt::RoundMode_None;
nvtt::AlphaMode alphaMode = nvtt::AlphaMode_None; nvtt::AlphaMode alphaMode = nvtt::AlphaMode_None;
float inputGamma = 2.2f; float inputGamma = 2.2f;
float outputGamma = 2.2f; float outputGamma = 2.2f;
nvtt::InputOptions inputOptions; nvtt::InputOptions inputOptions;
inputOptions.setTextureLayout(textureType, width, height); inputOptions.setTextureLayout(textureType, width, height);
inputOptions.setMipmapData(data, width, height); inputOptions.setMipmapData(data, width, height);
// setMipmapData copies the memory, so free up the memory afterward to avoid bloating the heap // setMipmapData copies the memory, so free up the memory afterward to avoid bloating the heap
data = nullptr; data = nullptr;
localCopy = QImage(); // QImage doesn't have a clear function, so override it with an empty one. localCopy = QImage(); // QImage doesn't have a clear function, so override it with an empty one.
inputOptions.setFormat(inputFormat); inputOptions.setFormat(inputFormat);
inputOptions.setGamma(inputGamma, outputGamma); inputOptions.setGamma(inputGamma, outputGamma);
inputOptions.setAlphaMode(alphaMode); inputOptions.setAlphaMode(alphaMode);
inputOptions.setWrapMode(wrapMode); inputOptions.setWrapMode(wrapMode);
inputOptions.setRoundMode(roundMode); inputOptions.setRoundMode(roundMode);
inputOptions.setMipmapGeneration(true); inputOptions.setMipmapGeneration(true);
inputOptions.setMipmapFilter(nvtt::MipmapFilter_Box); inputOptions.setMipmapFilter(nvtt::MipmapFilter_Box);
nvtt::CompressionOptions compressionOptions; nvtt::CompressionOptions compressionOptions;
compressionOptions.setQuality(nvtt::Quality_Production); compressionOptions.setQuality(nvtt::Quality_Production);
if (mipFormat == gpu::Element::COLOR_COMPRESSED_BCX_SRGB) { if (mipFormat == gpu::Element::COLOR_COMPRESSED_BCX_SRGB) {
compressionOptions.setFormat(nvtt::Format_BC1); compressionOptions.setFormat(nvtt::Format_BC1);
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_BCX_SRGBA_MASK) { } else if (mipFormat == gpu::Element::COLOR_COMPRESSED_BCX_SRGBA_MASK) {
alphaMode = nvtt::AlphaMode_Transparency; alphaMode = nvtt::AlphaMode_Transparency;
compressionOptions.setFormat(nvtt::Format_BC1a); compressionOptions.setFormat(nvtt::Format_BC1a);
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_BCX_SRGBA) { } else if (mipFormat == gpu::Element::COLOR_COMPRESSED_BCX_SRGBA) {
alphaMode = nvtt::AlphaMode_Transparency; alphaMode = nvtt::AlphaMode_Transparency;
compressionOptions.setFormat(nvtt::Format_BC3); compressionOptions.setFormat(nvtt::Format_BC3);
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_BCX_RED) { } else if (mipFormat == gpu::Element::COLOR_COMPRESSED_BCX_RED) {
compressionOptions.setFormat(nvtt::Format_BC4); compressionOptions.setFormat(nvtt::Format_BC4);
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_BCX_XY) { } else if (mipFormat == gpu::Element::COLOR_COMPRESSED_BCX_XY) {
compressionOptions.setFormat(nvtt::Format_BC5); compressionOptions.setFormat(nvtt::Format_BC5);
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_BCX_SRGBA_HIGH) { } else if (mipFormat == gpu::Element::COLOR_COMPRESSED_BCX_SRGBA_HIGH) {
alphaMode = nvtt::AlphaMode_Transparency; alphaMode = nvtt::AlphaMode_Transparency;
compressionOptions.setFormat(nvtt::Format_BC7); compressionOptions.setFormat(nvtt::Format_BC7);
} else if (mipFormat == gpu::Element::COLOR_RGBA_32) { } else if (mipFormat == gpu::Element::COLOR_RGBA_32) {
compressionOptions.setFormat(nvtt::Format_RGBA); compressionOptions.setFormat(nvtt::Format_RGBA);
compressionOptions.setPixelType(nvtt::PixelType_UnsignedNorm); compressionOptions.setPixelType(nvtt::PixelType_UnsignedNorm);
compressionOptions.setPitchAlignment(4); compressionOptions.setPitchAlignment(4);
compressionOptions.setPixelFormat(32, compressionOptions.setPixelFormat(32,
0x000000FF, 0x000000FF,
0x0000FF00, 0x0000FF00,
0x00FF0000, 0x00FF0000,
0xFF000000); 0xFF000000);
inputGamma = 1.0f; inputGamma = 1.0f;
outputGamma = 1.0f; outputGamma = 1.0f;
} else if (mipFormat == gpu::Element::COLOR_BGRA_32) { } else if (mipFormat == gpu::Element::COLOR_BGRA_32) {
compressionOptions.setFormat(nvtt::Format_RGBA); compressionOptions.setFormat(nvtt::Format_RGBA);
compressionOptions.setPixelType(nvtt::PixelType_UnsignedNorm); compressionOptions.setPixelType(nvtt::PixelType_UnsignedNorm);
compressionOptions.setPitchAlignment(4); compressionOptions.setPitchAlignment(4);
compressionOptions.setPixelFormat(32, compressionOptions.setPixelFormat(32,
0x00FF0000, 0x00FF0000,
0x0000FF00, 0x0000FF00,
0x000000FF, 0x000000FF,
0xFF000000); 0xFF000000);
inputGamma = 1.0f; inputGamma = 1.0f;
outputGamma = 1.0f; outputGamma = 1.0f;
} else if (mipFormat == gpu::Element::COLOR_SRGBA_32) { } else if (mipFormat == gpu::Element::COLOR_SRGBA_32) {
compressionOptions.setFormat(nvtt::Format_RGBA); compressionOptions.setFormat(nvtt::Format_RGBA);
compressionOptions.setPixelType(nvtt::PixelType_UnsignedNorm); compressionOptions.setPixelType(nvtt::PixelType_UnsignedNorm);
compressionOptions.setPitchAlignment(4); compressionOptions.setPitchAlignment(4);
compressionOptions.setPixelFormat(32, compressionOptions.setPixelFormat(32,
0x000000FF, 0x000000FF,
0x0000FF00, 0x0000FF00,
0x00FF0000, 0x00FF0000,
0xFF000000); 0xFF000000);
} else if (mipFormat == gpu::Element::COLOR_SBGRA_32) { } else if (mipFormat == gpu::Element::COLOR_SBGRA_32) {
compressionOptions.setFormat(nvtt::Format_RGBA); compressionOptions.setFormat(nvtt::Format_RGBA);
compressionOptions.setPixelType(nvtt::PixelType_UnsignedNorm); compressionOptions.setPixelType(nvtt::PixelType_UnsignedNorm);
compressionOptions.setPitchAlignment(4); compressionOptions.setPitchAlignment(4);
compressionOptions.setPixelFormat(32, compressionOptions.setPixelFormat(32,
0x00FF0000, 0x00FF0000,
0x0000FF00, 0x0000FF00,
0x000000FF, 0x000000FF,
0xFF000000); 0xFF000000);
} else if (mipFormat == gpu::Element::COLOR_R_8) { } else if (mipFormat == gpu::Element::COLOR_R_8) {
compressionOptions.setFormat(nvtt::Format_RGB); compressionOptions.setFormat(nvtt::Format_RGB);
compressionOptions.setPixelType(nvtt::PixelType_UnsignedNorm); compressionOptions.setPixelType(nvtt::PixelType_UnsignedNorm);
compressionOptions.setPitchAlignment(4); compressionOptions.setPitchAlignment(4);
compressionOptions.setPixelFormat(8, 0, 0, 0); compressionOptions.setPixelFormat(8, 0, 0, 0);
} else if (mipFormat == gpu::Element::VEC2NU8_XY) { } else if (mipFormat == gpu::Element::VEC2NU8_XY) {
inputOptions.setNormalMap(true); inputOptions.setNormalMap(true);
compressionOptions.setFormat(nvtt::Format_RGBA); compressionOptions.setFormat(nvtt::Format_RGBA);
compressionOptions.setPixelType(nvtt::PixelType_UnsignedNorm); compressionOptions.setPixelType(nvtt::PixelType_UnsignedNorm);
compressionOptions.setPitchAlignment(4); compressionOptions.setPitchAlignment(4);
compressionOptions.setPixelFormat(8, 8, 0, 0); compressionOptions.setPixelFormat(8, 8, 0, 0);
} else { } else {
qCWarning(imagelogging) << "Unknown mip format"; qCWarning(imagelogging) << "Unknown mip format";
Q_UNREACHABLE(); Q_UNREACHABLE();
return; return;
}
nvtt::OutputOptions outputOptions;
outputOptions.setOutputHeader(false);
OutputHandler outputHandler(texture, face);
outputOptions.setOutputHandler(&outputHandler);
MyErrorHandler errorHandler;
outputOptions.setErrorHandler(&errorHandler);
SequentialTaskDispatcher dispatcher(abortProcessing);
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 nvtt::OutputOptions outputOptions;
localCopy = QImage(); // QImage doesn't have a clear function, so override it with an empty one. outputOptions.setOutputHeader(false);
OutputHandler outputHandler(texture, face);
outputOptions.setOutputHandler(&outputHandler);
MyErrorHandler errorHandler;
outputOptions.setErrorHandler(&errorHandler);
Etc::EncodeMipmaps( SequentialTaskDispatcher dispatcher(abortProcessing);
(float *)floatData.data(), width, height, nvtt::Compressor compressor;
etcFormat, errorMetric, effort, compressor.setTaskDispatcher(&dispatcher);
numEncodeThreads, numEncodeThreads, compressor.process(inputOptions, compressionOptions, outputOptions);
numMips, Etc::FILTER_WRAP_NONE, } else {
mipMaps, &encodingTime int numMips = 1 + (int)log2(std::max(width, height));
); Etc::RawImage *mipMaps = new Etc::RawImage[numMips];
Etc::Image::Format etcFormat = Etc::Image::Format::DEFAULT;
for (int i = 0; i < numMips; i++) { if (mipFormat == gpu::Element::COLOR_COMPRESSED_ETC2_RGB) {
if (mipMaps[i].paucEncodingBits.get()) { etcFormat = Etc::Image::Format::RGB8;
if (face >= 0) { } else if (mipFormat == gpu::Element::COLOR_COMPRESSED_ETC2_SRGB) {
texture->assignStoredMipFace(i, face, mipMaps[i].uiEncodingBitsBytes, static_cast<const gpu::Byte*>(mipMaps[i].paucEncodingBits.get())); etcFormat = Etc::Image::Format::SRGB8;
} else { } else if (mipFormat == gpu::Element::COLOR_COMPRESSED_ETC2_RGB_PUNCHTHROUGH_ALPHA) {
texture->assignStoredMip(i, mipMaps[i].uiEncodingBitsBytes, static_cast<const gpu::Byte*>(mipMaps[i].paucEncodingBits.get())); 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;
} }
} }
}
delete[] mipMaps; // free up the memory afterward to avoid bloating the heap
#endif 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) { void generateMips(gpu::Texture* texture, QImage&& image, BackendTarget target, const std::atomic<bool>& abortProcessing = false, int face = -1) {
#if CPU_MIPMAPS #if CPU_MIPMAPS
PROFILE_RANGE(resource_parse, "generateMips"); PROFILE_RANGE(resource_parse, "generateMips");
#ifndef USE_GLES if (target == BackendTarget::GLES) {
if (image.format() == QIMAGE_HDR_FORMAT) { generateLDRMips(texture, std::move(image), target, abortProcessing, face);
generateHDRMips(texture, std::move(image), abortProcessing, face); } else {
} else { if (image.format() == hdrFormatForTarget(target)) {
generateLDRMips(texture, std::move(image), abortProcessing, face); generateHDRMips(texture, std::move(image), target, abortProcessing, face);
} else {
generateLDRMips(texture, std::move(image), target, abortProcessing, face);
}
} }
#else
generateLDRMips(texture, std::move(image), abortProcessing, face);
#endif
#else #else
texture->setAutoGenerateMips(true); texture->setAutoGenerateMips(true);
#endif #endif
@ -750,9 +747,9 @@ void processTextureAlpha(const QImage& srcImage, bool& validAlpha, bool& alphaAs
} }
gpu::TexturePointer TextureUsage::process2DTextureColorFromImage(QImage&& srcImage, const std::string& srcImageName, bool compress, gpu::TexturePointer TextureUsage::process2DTextureColorFromImage(QImage&& srcImage, const std::string& srcImageName, bool compress,
bool isStrict, const std::atomic<bool>& abortProcessing) { BackendTarget target, bool isStrict, const std::atomic<bool>& abortProcessing) {
PROFILE_RANGE(resource_parse, "process2DTextureColorFromImage"); PROFILE_RANGE(resource_parse, "process2DTextureColorFromImage");
QImage image = processSourceImage(std::move(srcImage), false); QImage image = processSourceImage(std::move(srcImage), false, target);
bool validAlpha = image.hasAlphaChannel(); bool validAlpha = image.hasAlphaChannel();
bool alphaAsMask = false; bool alphaAsMask = false;
@ -770,7 +767,11 @@ gpu::TexturePointer TextureUsage::process2DTextureColorFromImage(QImage&& srcIma
if ((image.width() > 0) && (image.height() > 0)) { if ((image.width() > 0) && (image.height() > 0)) {
gpu::Element formatMip; gpu::Element formatMip;
gpu::Element formatGPU; gpu::Element formatGPU;
if (compress) { if (target == BackendTarget::GLES) {
// GLES does not support GL_BGRA
formatGPU = gpu::Element::COLOR_COMPRESSED_ETC2_SRGBA;
formatMip = formatGPU;
} else if (compress) {
if (validAlpha) { if (validAlpha) {
// NOTE: This disables BC1a compression because it was producing odd artifacts on text textures // 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). // for the tutorial. Instead we use BC3 (which is larger) but doesn't produce the same artifacts).
@ -780,14 +781,8 @@ gpu::TexturePointer TextureUsage::process2DTextureColorFromImage(QImage&& srcIma
} }
formatMip = formatGPU; formatMip = formatGPU;
} else { } else {
#ifdef USE_GLES
// GLES does not support GL_BGRA
formatGPU = gpu::Element::COLOR_COMPRESSED_ETC2_SRGBA;
formatMip = formatGPU;
#else
formatGPU = gpu::Element::COLOR_SRGBA_32; formatGPU = gpu::Element::COLOR_SRGBA_32;
formatMip = gpu::Element::COLOR_SBGRA_32; formatMip = gpu::Element::COLOR_SBGRA_32;
#endif
} }
if (isStrict) { if (isStrict) {
@ -806,7 +801,7 @@ gpu::TexturePointer TextureUsage::process2DTextureColorFromImage(QImage&& srcIma
theTexture->setUsage(usage.build()); theTexture->setUsage(usage.build());
theTexture->setStoredMipFormat(formatMip); theTexture->setStoredMipFormat(formatMip);
theTexture->assignStoredMip(0, image.byteCount(), image.constBits()); theTexture->assignStoredMip(0, image.byteCount(), image.constBits());
generateMips(theTexture.get(), std::move(image), abortProcessing); generateMips(theTexture.get(), std::move(image), target, abortProcessing);
} }
return theTexture; return theTexture;
@ -887,10 +882,10 @@ QImage processBumpMap(QImage&& image) {
return result; return result;
} }
gpu::TexturePointer TextureUsage::process2DTextureNormalMapFromImage(QImage&& srcImage, const std::string& srcImageName, gpu::TexturePointer TextureUsage::process2DTextureNormalMapFromImage(QImage&& srcImage, const std::string& srcImageName,
bool compress, bool isBumpMap, bool compress, BackendTarget target, bool isBumpMap,
const std::atomic<bool>& abortProcessing) { const std::atomic<bool>& abortProcessing) {
PROFILE_RANGE(resource_parse, "process2DTextureNormalMapFromImage"); PROFILE_RANGE(resource_parse, "process2DTextureNormalMapFromImage");
QImage image = processSourceImage(std::move(srcImage), false); QImage image = processSourceImage(std::move(srcImage), false, target);
if (isBumpMap) { if (isBumpMap) {
image = processBumpMap(std::move(image)); image = processBumpMap(std::move(image));
@ -908,11 +903,11 @@ gpu::TexturePointer TextureUsage::process2DTextureNormalMapFromImage(QImage&& sr
if (compress) { if (compress) {
formatGPU = gpu::Element::COLOR_COMPRESSED_BCX_XY; formatGPU = gpu::Element::COLOR_COMPRESSED_BCX_XY;
} else { } else {
#ifdef USE_GLES if (target == BackendTarget::GLES) {
formatGPU = gpu::Element::COLOR_COMPRESSED_EAC_XY; formatGPU = gpu::Element::COLOR_COMPRESSED_EAC_XY;
#else } else {
formatGPU = gpu::Element::VEC2NU8_XY; formatGPU = gpu::Element::VEC2NU8_XY;
#endif }
} }
formatMip = formatGPU; formatMip = formatGPU;
@ -920,17 +915,17 @@ gpu::TexturePointer TextureUsage::process2DTextureNormalMapFromImage(QImage&& sr
theTexture->setSource(srcImageName); theTexture->setSource(srcImageName);
theTexture->setStoredMipFormat(formatMip); theTexture->setStoredMipFormat(formatMip);
theTexture->assignStoredMip(0, image.byteCount(), image.constBits()); theTexture->assignStoredMip(0, image.byteCount(), image.constBits());
generateMips(theTexture.get(), std::move(image), abortProcessing); generateMips(theTexture.get(), std::move(image), target, abortProcessing);
} }
return theTexture; return theTexture;
} }
gpu::TexturePointer TextureUsage::process2DTextureGrayscaleFromImage(QImage&& srcImage, const std::string& srcImageName, gpu::TexturePointer TextureUsage::process2DTextureGrayscaleFromImage(QImage&& srcImage, const std::string& srcImageName,
bool compress, bool isInvertedPixels, bool compress, BackendTarget target, bool isInvertedPixels,
const std::atomic<bool>& abortProcessing) { const std::atomic<bool>& abortProcessing) {
PROFILE_RANGE(resource_parse, "process2DTextureGrayscaleFromImage"); PROFILE_RANGE(resource_parse, "process2DTextureGrayscaleFromImage");
QImage image = processSourceImage(std::move(srcImage), false); QImage image = processSourceImage(std::move(srcImage), false, target);
if (image.format() != QImage::Format_ARGB32) { if (image.format() != QImage::Format_ARGB32) {
image = image.convertToFormat(QImage::Format_ARGB32); image = image.convertToFormat(QImage::Format_ARGB32);
@ -948,11 +943,11 @@ gpu::TexturePointer TextureUsage::process2DTextureGrayscaleFromImage(QImage&& sr
if (compress) { if (compress) {
formatGPU = gpu::Element::COLOR_COMPRESSED_BCX_RED; formatGPU = gpu::Element::COLOR_COMPRESSED_BCX_RED;
} else { } else {
#ifdef USE_GLES if (target == BackendTarget::GLES) {
formatGPU = gpu::Element::COLOR_COMPRESSED_EAC_RED; formatGPU = gpu::Element::COLOR_COMPRESSED_EAC_RED;
#else } else {
formatGPU = gpu::Element::COLOR_R_8; formatGPU = gpu::Element::COLOR_R_8;
#endif }
} }
formatMip = formatGPU; formatMip = formatGPU;
@ -960,7 +955,7 @@ gpu::TexturePointer TextureUsage::process2DTextureGrayscaleFromImage(QImage&& sr
theTexture->setSource(srcImageName); theTexture->setSource(srcImageName);
theTexture->setStoredMipFormat(formatMip); theTexture->setStoredMipFormat(formatMip);
theTexture->assignStoredMip(0, image.byteCount(), image.constBits()); theTexture->assignStoredMip(0, image.byteCount(), image.constBits());
generateMips(theTexture.get(), std::move(image), abortProcessing); generateMips(theTexture.get(), std::move(image), target, abortProcessing);
} }
return theTexture; return theTexture;
@ -1233,12 +1228,12 @@ const int CubeLayout::NUM_CUBEMAP_LAYOUTS = sizeof(CubeLayout::CUBEMAP_LAYOUTS)
//#define DEBUG_COLOR_PACKING //#define DEBUG_COLOR_PACKING
QImage convertToHDRFormat(QImage&& srcImage, gpu::Element format) { QImage convertToHDRFormat(QImage&& srcImage, gpu::Element format, BackendTarget target) {
// Take a local copy to force move construction // Take a local copy to force move construction
// https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#f18-for-consume-parameters-pass-by-x-and-stdmove-the-parameter // https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#f18-for-consume-parameters-pass-by-x-and-stdmove-the-parameter
QImage localCopy = std::move(srcImage); QImage localCopy = std::move(srcImage);
QImage hdrImage(localCopy.width(), localCopy.height(), (QImage::Format)QIMAGE_HDR_FORMAT); QImage hdrImage(localCopy.width(), localCopy.height(), hdrFormatForTarget(target));
std::function<uint32(const glm::vec3&)> packFunc; std::function<uint32(const glm::vec3&)> packFunc;
#ifdef DEBUG_COLOR_PACKING #ifdef DEBUG_COLOR_PACKING
std::function<glm::vec3(uint32)> unpackFunc; std::function<glm::vec3(uint32)> unpackFunc;
@ -1292,7 +1287,7 @@ QImage convertToHDRFormat(QImage&& srcImage, gpu::Element format) {
} }
gpu::TexturePointer TextureUsage::processCubeTextureColorFromImage(QImage&& srcImage, const std::string& srcImageName, gpu::TexturePointer TextureUsage::processCubeTextureColorFromImage(QImage&& srcImage, const std::string& srcImageName,
bool compress, bool generateIrradiance, bool compress, BackendTarget target, bool generateIrradiance,
const std::atomic<bool>& abortProcessing) { const std::atomic<bool>& abortProcessing) {
PROFILE_RANGE(resource_parse, "processCubeTextureColorFromImage"); PROFILE_RANGE(resource_parse, "processCubeTextureColorFromImage");
@ -1308,14 +1303,14 @@ gpu::TexturePointer TextureUsage::processCubeTextureColorFromImage(QImage&& srcI
gpu::TexturePointer theTexture = nullptr; gpu::TexturePointer theTexture = nullptr;
QImage image = processSourceImage(std::move(localCopy), true); QImage image = processSourceImage(std::move(localCopy), true, target);
if (image.format() != QIMAGE_HDR_FORMAT) { if (image.format() != hdrFormatForTarget(target)) {
#ifndef USE_GLES if (target == BackendTarget::GLES) {
image = convertToHDRFormat(std::move(image), HDR_FORMAT); image = image.convertToFormat(QImage::Format_RGB32);
#else } else {
image = image.convertToFormat(QImage::Format_RGB32); image = convertToHDRFormat(std::move(image), HDR_FORMAT, target);
#endif }
} }
gpu::Element formatMip; gpu::Element formatMip;
@ -1323,11 +1318,11 @@ gpu::TexturePointer TextureUsage::processCubeTextureColorFromImage(QImage&& srcI
if (compress) { if (compress) {
formatGPU = gpu::Element::COLOR_COMPRESSED_BCX_HDR_RGB; formatGPU = gpu::Element::COLOR_COMPRESSED_BCX_HDR_RGB;
} else { } else {
#ifdef USE_GLES if (target == BackendTarget::GLES) {
formatGPU = gpu::Element::COLOR_COMPRESSED_ETC2_SRGB; formatGPU = gpu::Element::COLOR_COMPRESSED_ETC2_SRGB;
#else } else {
formatGPU = HDR_FORMAT; formatGPU = HDR_FORMAT;
#endif }
} }
formatMip = formatGPU; formatMip = formatGPU;
@ -1378,11 +1373,12 @@ gpu::TexturePointer TextureUsage::processCubeTextureColorFromImage(QImage&& srcI
PROFILE_RANGE(resource_parse, "generateIrradiance"); PROFILE_RANGE(resource_parse, "generateIrradiance");
gpu::Element irradianceFormat; gpu::Element irradianceFormat;
// TODO: we could locally compress the irradiance texture on Android, but we don't need to // TODO: we could locally compress the irradiance texture on Android, but we don't need to
#ifndef USE_GLES if (target == BackendTarget::GLES) {
irradianceFormat = HDR_FORMAT; irradianceFormat = gpu::Element::COLOR_SRGBA_32;
#else } else {
irradianceFormat = gpu::Element::COLOR_SRGBA_32; irradianceFormat = HDR_FORMAT;
#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)); 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->setSource(srcImageName);
irradianceTexture->setStoredMipFormat(irradianceFormat); irradianceTexture->setStoredMipFormat(irradianceFormat);
@ -1397,7 +1393,7 @@ gpu::TexturePointer TextureUsage::processCubeTextureColorFromImage(QImage&& srcI
} }
for (uint8 face = 0; face < faces.size(); ++face) { for (uint8 face = 0; face < faces.size(); ++face) {
generateMips(theTexture.get(), std::move(faces[face]), abortProcessing, face); generateMips(theTexture.get(), std::move(faces[face]), target, abortProcessing, face);
} }
} }

View file

@ -21,6 +21,11 @@ class QImage;
namespace image { namespace image {
enum class BackendTarget {
GL,
GLES
};
namespace TextureUsage { namespace TextureUsage {
enum Type { enum Type {
@ -41,42 +46,41 @@ enum Type {
UNUSED_TEXTURE UNUSED_TEXTURE
}; };
using TextureLoader = std::function<gpu::TexturePointer(QImage&&, const std::string&, bool, const std::atomic<bool>&)>; using TextureLoader = std::function<gpu::TexturePointer(QImage&&, const std::string&, bool, BackendTarget, const std::atomic<bool>&)>;
TextureLoader getTextureLoaderForType(Type type, const QVariantMap& options = QVariantMap()); TextureLoader getTextureLoaderForType(Type type, const QVariantMap& options = QVariantMap());
gpu::TexturePointer create2DTextureFromImage(QImage&& image, const std::string& srcImageName, gpu::TexturePointer create2DTextureFromImage(QImage&& image, const std::string& srcImageName,
bool compress, const std::atomic<bool>& abortProcessing); bool compress, BackendTarget target, const std::atomic<bool>& abortProcessing);
gpu::TexturePointer createStrict2DTextureFromImage(QImage&& image, const std::string& srcImageName, gpu::TexturePointer createStrict2DTextureFromImage(QImage&& image, const std::string& srcImageName,
bool compress, const std::atomic<bool>& abortProcessing); bool compress, BackendTarget target, const std::atomic<bool>& abortProcessing);
gpu::TexturePointer createAlbedoTextureFromImage(QImage&& image, const std::string& srcImageName, gpu::TexturePointer createAlbedoTextureFromImage(QImage&& image, const std::string& srcImageName,
bool compress, const std::atomic<bool>& abortProcessing); bool compress, BackendTarget target, const std::atomic<bool>& abortProcessing);
gpu::TexturePointer createEmissiveTextureFromImage(QImage&& image, const std::string& srcImageName, gpu::TexturePointer createEmissiveTextureFromImage(QImage&& image, const std::string& srcImageName,
bool compress, const std::atomic<bool>& abortProcessing); bool compress, BackendTarget target, const std::atomic<bool>& abortProcessing);
gpu::TexturePointer createNormalTextureFromNormalImage(QImage&& image, const std::string& srcImageName, gpu::TexturePointer createNormalTextureFromNormalImage(QImage&& image, const std::string& srcImageName,
bool compress, const std::atomic<bool>& abortProcessing); bool compress, BackendTarget target, const std::atomic<bool>& abortProcessing);
gpu::TexturePointer createNormalTextureFromBumpImage(QImage&& image, const std::string& srcImageName, gpu::TexturePointer createNormalTextureFromBumpImage(QImage&& image, const std::string& srcImageName,
bool compress, const std::atomic<bool>& abortProcessing); bool compress, BackendTarget target, const std::atomic<bool>& abortProcessing);
gpu::TexturePointer createRoughnessTextureFromImage(QImage&& image, const std::string& srcImageName, gpu::TexturePointer createRoughnessTextureFromImage(QImage&& image, const std::string& srcImageName,
bool compress, const std::atomic<bool>& abortProcessing); bool compress, BackendTarget target, const std::atomic<bool>& abortProcessing);
gpu::TexturePointer createRoughnessTextureFromGlossImage(QImage&& image, const std::string& srcImageName, gpu::TexturePointer createRoughnessTextureFromGlossImage(QImage&& image, const std::string& srcImageName,
bool compress, const std::atomic<bool>& abortProcessing); bool compress, BackendTarget target, const std::atomic<bool>& abortProcessing);
gpu::TexturePointer createMetallicTextureFromImage(QImage&& image, const std::string& srcImageName, gpu::TexturePointer createMetallicTextureFromImage(QImage&& image, const std::string& srcImageName,
bool compress, const std::atomic<bool>& abortProcessing); bool compress, BackendTarget target, const std::atomic<bool>& abortProcessing);
gpu::TexturePointer createCubeTextureFromImage(QImage&& image, const std::string& srcImageName, gpu::TexturePointer createCubeTextureFromImage(QImage&& image, const std::string& srcImageName,
bool compress, const std::atomic<bool>& abortProcessing); bool compress, BackendTarget target, const std::atomic<bool>& abortProcessing);
gpu::TexturePointer createCubeTextureFromImageWithoutIrradiance(QImage&& image, const std::string& srcImageName, gpu::TexturePointer createCubeTextureFromImageWithoutIrradiance(QImage&& image, const std::string& srcImageName,
bool compress, const std::atomic<bool>& abortProcessing); bool compress, BackendTarget target, const std::atomic<bool>& abortProcessing);
gpu::TexturePointer createLightmapTextureFromImage(QImage&& image, const std::string& srcImageName, gpu::TexturePointer createLightmapTextureFromImage(QImage&& image, const std::string& srcImageName,
bool compress, const std::atomic<bool>& abortProcessing); bool compress, BackendTarget target, const std::atomic<bool>& abortProcessing);
gpu::TexturePointer process2DTextureColorFromImage(QImage&& srcImage, const std::string& srcImageName, bool compress, gpu::TexturePointer process2DTextureColorFromImage(QImage&& srcImage, const std::string& srcImageName, bool compress,
bool isStrict, const std::atomic<bool>& abortProcessing); BackendTarget target, bool isStrict, const std::atomic<bool>& abortProcessing);
gpu::TexturePointer process2DTextureNormalMapFromImage(QImage&& srcImage, const std::string& srcImageName, bool compress, gpu::TexturePointer process2DTextureNormalMapFromImage(QImage&& srcImage, const std::string& srcImageName, bool compress,
bool isBumpMap, const std::atomic<bool>& abortProcessing); BackendTarget target, bool isBumpMap, const std::atomic<bool>& abortProcessing);
gpu::TexturePointer process2DTextureGrayscaleFromImage(QImage&& srcImage, const std::string& srcImageName, bool compress, gpu::TexturePointer process2DTextureGrayscaleFromImage(QImage&& srcImage, const std::string& srcImageName, bool compress,
bool isInvertedPixels, const std::atomic<bool>& abortProcessing); BackendTarget target, bool isInvertedPixels, const std::atomic<bool>& abortProcessing);
gpu::TexturePointer processCubeTextureColorFromImage(QImage&& srcImage, const std::string& srcImageName, bool compress, gpu::TexturePointer processCubeTextureColorFromImage(QImage&& srcImage, const std::string& srcImageName, bool compress,
bool generateIrradiance, const std::atomic<bool>& abortProcessing); BackendTarget target, bool generateIrradiance, const std::atomic<bool>& abortProcessing);
} // namespace TextureUsage } // namespace TextureUsage
@ -84,7 +88,13 @@ const QStringList getSupportedFormats();
gpu::TexturePointer processImage(std::shared_ptr<QIODevice> content, const std::string& url, gpu::TexturePointer processImage(std::shared_ptr<QIODevice> content, const std::string& url,
int maxNumPixels, TextureUsage::Type textureType, int maxNumPixels, TextureUsage::Type textureType,
bool compress = false, const std::atomic<bool>& abortProcessing = false); bool compress = false,
#ifdef USE_GLES
BackendTarget target = BackendTarget::GLES,
#else
BackendTarget target = BackendTarget::GL,
#endif
const std::atomic<bool>& abortProcessing = false);
} // namespace image } // namespace image

View file

@ -279,7 +279,12 @@ gpu::TexturePointer TextureCache::getImageTexture(const QString& path, image::Te
return nullptr; return nullptr;
} }
auto loader = image::TextureUsage::getTextureLoaderForType(type, options); auto loader = image::TextureUsage::getTextureLoaderForType(type, options);
return gpu::TexturePointer(loader(std::move(image), path.toStdString(), false, false)); #ifdef USE_GLES
image::BackendTarget target = image::BackendTarget::GLES;
#else
image::BackendTarget target = image::BackendTarget::GL;
#endif
return gpu::TexturePointer(loader(std::move(image), path.toStdString(), false, target, false));
} }
QSharedPointer<Resource> TextureCache::createResource(const QUrl& url, const QSharedPointer<Resource>& fallback, QSharedPointer<Resource> TextureCache::createResource(const QUrl& url, const QSharedPointer<Resource>& fallback,
@ -1160,7 +1165,13 @@ void ImageReader::read() {
// IMPORTANT: _content is empty past this point // IMPORTANT: _content is empty past this point
auto buffer = std::shared_ptr<QIODevice>((QIODevice*)new OwningBuffer(std::move(_content))); auto buffer = std::shared_ptr<QIODevice>((QIODevice*)new OwningBuffer(std::move(_content)));
texture = image::processImage(std::move(buffer), _url.toString().toStdString(), _maxNumPixels, networkTexture->getTextureType());
#ifdef USE_GLES
constexpr bool shouldCompress = true;
#else
constexpr bool shouldCompress = false;
#endif
texture = image::processImage(std::move(buffer), _url.toString().toStdString(), _maxNumPixels, networkTexture->getTextureType(), shouldCompress);
if (!texture) { if (!texture) {
qCWarning(modelnetworking) << "Could not process:" << _url; qCWarning(modelnetworking) << "Could not process:" << _url;