From 9ea08f18500ef26b1ce559a27d493a25c86165b7 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 12 Jul 2018 16:45:04 -0700 Subject: [PATCH 01/10] Add ETC2 support to Oven --- libraries/baking/src/TextureBaker.cpp | 71 +-- libraries/image/src/image/Image.cpp | 538 +++++++++--------- libraries/image/src/image/Image.h | 48 +- .../src/model-networking/TextureCache.cpp | 15 +- 4 files changed, 348 insertions(+), 324 deletions(-) diff --git a/libraries/baking/src/TextureBaker.cpp b/libraries/baking/src/TextureBaker.cpp index ecfe724441..24fc331a3b 100644 --- a/libraries/baking/src/TextureBaker.cpp +++ b/libraries/baking/src/TextureBaker.cpp @@ -138,7 +138,7 @@ void TextureBaker::processTexture() { // IMPORTANT: _originalTexture is empty past this point _originalTexture.clear(); _outputFiles.push_back(originalCopyFilePath); - meta.original = _metaTexturePathPrefix +_textureURL.fileName(); + meta.original = _metaTexturePathPrefix + _textureURL.fileName(); } auto buffer = std::static_pointer_cast(std::make_shared(originalCopyFilePath)); @@ -149,49 +149,56 @@ void TextureBaker::processTexture() { // Compressed KTX if (_compressionEnabled) { - auto processedTexture = image::processImage(buffer, _textureURL.toString().toStdString(), - ABSOLUTE_MAX_TEXTURE_NUM_PIXELS, _textureType, true, _abortProcessing); - if (!processedTexture) { - handleError("Could not process texture " + _textureURL.toString()); - return; - } - processedTexture->setSourceHash(hash); + constexpr std::array BACKEND_TARGETS { + image::BackendTarget::GL, + image::BackendTarget::GLES + }; + for (auto target : BACKEND_TARGETS) { + auto processedTexture = image::processImage(buffer, _textureURL.toString().toStdString(), + ABSOLUTE_MAX_TEXTURE_NUM_PIXELS, _textureType, true, + target, _abortProcessing); + if (!processedTexture) { + handleError("Could not process texture " + _textureURL.toString()); + return; + } + processedTexture->setSourceHash(hash); - if (shouldStop()) { - return; - } + if (shouldStop()) { + return; + } - auto memKTX = gpu::Texture::serialize(*processedTexture); - if (!memKTX) { - handleError("Could not serialize " + _textureURL.toString() + " to KTX"); - return; - } + auto memKTX = gpu::Texture::serialize(*processedTexture); + if (!memKTX) { + handleError("Could not serialize " + _textureURL.toString() + " to KTX"); + return; + } - const char* name = khronos::gl::texture::toString(memKTX->_header.getGLInternaFormat()); - if (name == nullptr) { - handleError("Could not determine internal format for compressed KTX: " + _textureURL.toString()); - return; - } + const char* name = khronos::gl::texture::toString(memKTX->_header.getGLInternaFormat()); + if (name == nullptr) { + handleError("Could not determine internal format for compressed KTX: " + _textureURL.toString()); + return; + } - const char* data = reinterpret_cast(memKTX->_storage->data()); - const size_t length = memKTX->_storage->size(); + const char* data = reinterpret_cast(memKTX->_storage->data()); + const size_t length = memKTX->_storage->size(); - auto fileName = _baseFilename + "_" + name + ".ktx"; - auto filePath = _outputDirectory.absoluteFilePath(fileName); - QFile bakedTextureFile { filePath }; - if (!bakedTextureFile.open(QIODevice::WriteOnly) || bakedTextureFile.write(data, length) == -1) { - handleError("Could not write baked texture for " + _textureURL.toString()); - return; + auto fileName = _baseFilename + "_" + name + ".ktx"; + auto filePath = _outputDirectory.absoluteFilePath(fileName); + QFile bakedTextureFile { filePath }; + if (!bakedTextureFile.open(QIODevice::WriteOnly) || bakedTextureFile.write(data, length) == -1) { + handleError("Could not write baked texture for " + _textureURL.toString()); + 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 if (_textureType == image::TextureUsage::Type::CUBE_TEXTURE) { buffer->reset(); 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) { handleError("Could not process texture " + _textureURL.toString()); return; diff --git a/libraries/image/src/image/Image.cpp b/libraries/image/src/image/Image.cpp index 7fc3a73f87..eaf8eacfce 100644 --- a/libraries/image/src/image/Image.cpp +++ b/libraries/image/src/image/Image.cpp @@ -31,17 +31,13 @@ using namespace gpu; #define CPU_MIPMAPS 1 #include -#ifdef USE_GLES +#undef _CRT_SECURE_NO_WARNINGS #include #include -#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 +static const glm::uvec2 MAX_TEXTURE_SIZE_GLES(2048); +static const glm::uvec2 MAX_TEXTURE_SIZE_GL(4096); bool DEV_DECIMATE_TEXTURES = false; std::atomic DECIMATED_TEXTURE_COUNT{ 0 }; std::atomic RECTIFIED_TEXTURE_COUNT{ 0 }; @@ -83,11 +79,12 @@ const QStringList getSupportedFormats() { // 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 +QImage::Format hdrFormatForTarget(BackendTarget target) { + if (target == BackendTarget::GLES) { + return QImage::Format_RGB32; + } + return QImage::Format_RGB30; +} TextureUsage::TextureLoader TextureUsage::getTextureLoaderForType(Type type, const QVariantMap& options) { switch (type) { @@ -123,63 +120,63 @@ TextureUsage::TextureLoader TextureUsage::getTextureLoaderForType(Type type, con } gpu::TexturePointer TextureUsage::createStrict2DTextureFromImage(QImage&& srcImage, const std::string& srcImageName, - bool compress, const std::atomic& abortProcessing) { - return process2DTextureColorFromImage(std::move(srcImage), srcImageName, compress, true, abortProcessing); + bool compress, BackendTarget target, const std::atomic& abortProcessing) { + return process2DTextureColorFromImage(std::move(srcImage), srcImageName, compress, target, true, abortProcessing); } gpu::TexturePointer TextureUsage::create2DTextureFromImage(QImage&& srcImage, const std::string& srcImageName, - bool compress, const std::atomic& abortProcessing) { - return process2DTextureColorFromImage(std::move(srcImage), srcImageName, compress, false, abortProcessing); + bool compress, BackendTarget target, const std::atomic& abortProcessing) { + return process2DTextureColorFromImage(std::move(srcImage), srcImageName, compress, target, false, abortProcessing); } gpu::TexturePointer TextureUsage::createAlbedoTextureFromImage(QImage&& srcImage, const std::string& srcImageName, - bool compress, const std::atomic& abortProcessing) { - return process2DTextureColorFromImage(std::move(srcImage), srcImageName, compress, false, abortProcessing); + bool compress, BackendTarget target, const std::atomic& abortProcessing) { + return process2DTextureColorFromImage(std::move(srcImage), srcImageName, compress, target, false, abortProcessing); } gpu::TexturePointer TextureUsage::createEmissiveTextureFromImage(QImage&& srcImage, const std::string& srcImageName, - bool compress, const std::atomic& abortProcessing) { - return process2DTextureColorFromImage(std::move(srcImage), srcImageName, compress, false, abortProcessing); + bool compress, BackendTarget target, const std::atomic& abortProcessing) { + return process2DTextureColorFromImage(std::move(srcImage), srcImageName, compress, target, false, abortProcessing); } gpu::TexturePointer TextureUsage::createLightmapTextureFromImage(QImage&& srcImage, const std::string& srcImageName, - bool compress, const std::atomic& abortProcessing) { - return process2DTextureColorFromImage(std::move(srcImage), srcImageName, compress, false, abortProcessing); + bool compress, BackendTarget target, const std::atomic& abortProcessing) { + return process2DTextureColorFromImage(std::move(srcImage), srcImageName, compress, target, false, abortProcessing); } gpu::TexturePointer TextureUsage::createNormalTextureFromNormalImage(QImage&& srcImage, const std::string& srcImageName, - bool compress, const std::atomic& abortProcessing) { - return process2DTextureNormalMapFromImage(std::move(srcImage), srcImageName, compress, false, abortProcessing); + bool compress, BackendTarget target, const std::atomic& abortProcessing) { + return process2DTextureNormalMapFromImage(std::move(srcImage), srcImageName, compress, target, false, abortProcessing); } gpu::TexturePointer TextureUsage::createNormalTextureFromBumpImage(QImage&& srcImage, const std::string& srcImageName, - bool compress, const std::atomic& abortProcessing) { - return process2DTextureNormalMapFromImage(std::move(srcImage), srcImageName, compress, true, abortProcessing); + bool compress, BackendTarget target, const std::atomic& abortProcessing) { + return process2DTextureNormalMapFromImage(std::move(srcImage), srcImageName, compress, target, true, abortProcessing); } gpu::TexturePointer TextureUsage::createRoughnessTextureFromImage(QImage&& srcImage, const std::string& srcImageName, - bool compress, const std::atomic& abortProcessing) { - return process2DTextureGrayscaleFromImage(std::move(srcImage), srcImageName, compress, false, abortProcessing); + bool compress, BackendTarget target, const std::atomic& abortProcessing) { + return process2DTextureGrayscaleFromImage(std::move(srcImage), srcImageName, compress, target, false, abortProcessing); } gpu::TexturePointer TextureUsage::createRoughnessTextureFromGlossImage(QImage&& srcImage, const std::string& srcImageName, - bool compress, const std::atomic& abortProcessing) { - return process2DTextureGrayscaleFromImage(std::move(srcImage), srcImageName, compress, true, abortProcessing); + bool compress, BackendTarget target, const std::atomic& abortProcessing) { + return process2DTextureGrayscaleFromImage(std::move(srcImage), srcImageName, compress, target, true, abortProcessing); } gpu::TexturePointer TextureUsage::createMetallicTextureFromImage(QImage&& srcImage, const std::string& srcImageName, - bool compress, const std::atomic& abortProcessing) { - return process2DTextureGrayscaleFromImage(std::move(srcImage), srcImageName, compress, false, abortProcessing); + bool compress, BackendTarget target, const std::atomic& abortProcessing) { + return process2DTextureGrayscaleFromImage(std::move(srcImage), srcImageName, compress, target, false, abortProcessing); } gpu::TexturePointer TextureUsage::createCubeTextureFromImage(QImage&& srcImage, const std::string& srcImageName, - bool compress, const std::atomic& abortProcessing) { - return processCubeTextureColorFromImage(std::move(srcImage), srcImageName, compress, true, abortProcessing); + bool compress, BackendTarget target, const std::atomic& abortProcessing) { + return processCubeTextureColorFromImage(std::move(srcImage), srcImageName, compress, target, true, abortProcessing); } gpu::TexturePointer TextureUsage::createCubeTextureFromImageWithoutIrradiance(QImage&& srcImage, const std::string& srcImageName, - bool compress, const std::atomic& abortProcessing) { - return processCubeTextureColorFromImage(std::move(srcImage), srcImageName, compress, false, abortProcessing); + bool compress, BackendTarget target, const std::atomic& abortProcessing) { + return processCubeTextureColorFromImage(std::move(srcImage), srcImageName, compress, target, false, abortProcessing); } 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 content, const std::string& filename, int maxNumPixels, TextureUsage::Type textureType, - bool compress, const std::atomic& abortProcessing) { + bool compress, BackendTarget target, const std::atomic& abortProcessing) { QImage image = processRawImageData(*content.get(), filename); // 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 content, const std:: } 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; } -QImage processSourceImage(QImage&& srcImage, bool cubemap) { +QImage processSourceImage(QImage&& srcImage, bool cubemap, BackendTarget target) { PROFILE_RANGE(resource_parse, "processSourceImage"); // 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()); 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; } if (targetSize != srcImageSize) { @@ -406,12 +404,12 @@ public: } }; -void generateHDRMips(gpu::Texture* texture, QImage&& image, const std::atomic& abortProcessing, int face) { +void generateHDRMips(gpu::Texture* texture, QImage&& image, BackendTarget target, const std::atomic& abortProcessing, int face) { // 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 QImage localCopy = std::move(image); - assert(localCopy.format() == QIMAGE_HDR_FORMAT); + assert(localCopy.format() == hdrFormatForTarget(target)); const int width = localCopy.width(), height = localCopy.height(); std::vector data; @@ -503,220 +501,219 @@ void generateHDRMips(gpu::Texture* texture, QImage&& image, const std::atomic& abortProcessing, int face) { +void generateLDRMips(gpu::Texture* texture, QImage&& image, BackendTarget target, const std::atomic& abortProcessing, int face) { // 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 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); } const int width = localCopy.width(), height = localCopy.height(); auto mipFormat = texture->getStoredMipFormat(); -#ifndef USE_GLES - const void* data = static_cast(localCopy.constBits()); - nvtt::TextureType textureType = nvtt::TextureType_2D; - nvtt::InputFormat inputFormat = nvtt::InputFormat_BGRA_8UB; - nvtt::WrapMode wrapMode = nvtt::WrapMode_Mirror; - nvtt::RoundMode roundMode = nvtt::RoundMode_None; - nvtt::AlphaMode alphaMode = nvtt::AlphaMode_None; + if (target != BackendTarget::GLES) { + const void* data = static_cast(localCopy.constBits()); + nvtt::TextureType textureType = nvtt::TextureType_2D; + nvtt::InputFormat inputFormat = nvtt::InputFormat_BGRA_8UB; + nvtt::WrapMode wrapMode = nvtt::WrapMode_Mirror; + nvtt::RoundMode roundMode = nvtt::RoundMode_None; + nvtt::AlphaMode alphaMode = nvtt::AlphaMode_None; - float inputGamma = 2.2f; - float outputGamma = 2.2f; + float inputGamma = 2.2f; + float outputGamma = 2.2f; - nvtt::InputOptions inputOptions; - inputOptions.setTextureLayout(textureType, width, height); + nvtt::InputOptions inputOptions; + inputOptions.setTextureLayout(textureType, width, height); - inputOptions.setMipmapData(data, width, height); - // setMipmapData copies the memory, so 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. + inputOptions.setMipmapData(data, width, height); + // setMipmapData copies the memory, so 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. - inputOptions.setFormat(inputFormat); - inputOptions.setGamma(inputGamma, outputGamma); - inputOptions.setAlphaMode(alphaMode); - inputOptions.setWrapMode(wrapMode); - inputOptions.setRoundMode(roundMode); + inputOptions.setFormat(inputFormat); + inputOptions.setGamma(inputGamma, outputGamma); + inputOptions.setAlphaMode(alphaMode); + inputOptions.setWrapMode(wrapMode); + inputOptions.setRoundMode(roundMode); - inputOptions.setMipmapGeneration(true); - inputOptions.setMipmapFilter(nvtt::MipmapFilter_Box); + inputOptions.setMipmapGeneration(true); + inputOptions.setMipmapFilter(nvtt::MipmapFilter_Box); - nvtt::CompressionOptions compressionOptions; - compressionOptions.setQuality(nvtt::Quality_Production); + nvtt::CompressionOptions compressionOptions; + compressionOptions.setQuality(nvtt::Quality_Production); - if (mipFormat == gpu::Element::COLOR_COMPRESSED_BCX_SRGB) { - compressionOptions.setFormat(nvtt::Format_BC1); - } else if (mipFormat == gpu::Element::COLOR_COMPRESSED_BCX_SRGBA_MASK) { - alphaMode = nvtt::AlphaMode_Transparency; - compressionOptions.setFormat(nvtt::Format_BC1a); - } else if (mipFormat == gpu::Element::COLOR_COMPRESSED_BCX_SRGBA) { - alphaMode = nvtt::AlphaMode_Transparency; - compressionOptions.setFormat(nvtt::Format_BC3); - } else if (mipFormat == gpu::Element::COLOR_COMPRESSED_BCX_RED) { - compressionOptions.setFormat(nvtt::Format_BC4); - } else if (mipFormat == gpu::Element::COLOR_COMPRESSED_BCX_XY) { - compressionOptions.setFormat(nvtt::Format_BC5); - } else if (mipFormat == gpu::Element::COLOR_COMPRESSED_BCX_SRGBA_HIGH) { - alphaMode = nvtt::AlphaMode_Transparency; - compressionOptions.setFormat(nvtt::Format_BC7); - } else if (mipFormat == gpu::Element::COLOR_RGBA_32) { - compressionOptions.setFormat(nvtt::Format_RGBA); - compressionOptions.setPixelType(nvtt::PixelType_UnsignedNorm); - compressionOptions.setPitchAlignment(4); - compressionOptions.setPixelFormat(32, - 0x000000FF, - 0x0000FF00, - 0x00FF0000, - 0xFF000000); - inputGamma = 1.0f; - outputGamma = 1.0f; - } else if (mipFormat == gpu::Element::COLOR_BGRA_32) { - compressionOptions.setFormat(nvtt::Format_RGBA); - compressionOptions.setPixelType(nvtt::PixelType_UnsignedNorm); - compressionOptions.setPitchAlignment(4); - compressionOptions.setPixelFormat(32, - 0x00FF0000, - 0x0000FF00, - 0x000000FF, - 0xFF000000); - inputGamma = 1.0f; - outputGamma = 1.0f; - } else if (mipFormat == gpu::Element::COLOR_SRGBA_32) { - compressionOptions.setFormat(nvtt::Format_RGBA); - compressionOptions.setPixelType(nvtt::PixelType_UnsignedNorm); - compressionOptions.setPitchAlignment(4); - compressionOptions.setPixelFormat(32, - 0x000000FF, - 0x0000FF00, - 0x00FF0000, - 0xFF000000); - } else if (mipFormat == gpu::Element::COLOR_SBGRA_32) { - compressionOptions.setFormat(nvtt::Format_RGBA); - compressionOptions.setPixelType(nvtt::PixelType_UnsignedNorm); - compressionOptions.setPitchAlignment(4); - compressionOptions.setPixelFormat(32, - 0x00FF0000, - 0x0000FF00, - 0x000000FF, - 0xFF000000); - } else if (mipFormat == gpu::Element::COLOR_R_8) { - compressionOptions.setFormat(nvtt::Format_RGB); - compressionOptions.setPixelType(nvtt::PixelType_UnsignedNorm); - compressionOptions.setPitchAlignment(4); - compressionOptions.setPixelFormat(8, 0, 0, 0); - } else if (mipFormat == gpu::Element::VEC2NU8_XY) { - inputOptions.setNormalMap(true); - compressionOptions.setFormat(nvtt::Format_RGBA); - compressionOptions.setPixelType(nvtt::PixelType_UnsignedNorm); - compressionOptions.setPitchAlignment(4); - compressionOptions.setPixelFormat(8, 8, 0, 0); - } else { - qCWarning(imagelogging) << "Unknown mip format"; - Q_UNREACHABLE(); - 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 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; + if (mipFormat == gpu::Element::COLOR_COMPRESSED_BCX_SRGB) { + compressionOptions.setFormat(nvtt::Format_BC1); + } else if (mipFormat == gpu::Element::COLOR_COMPRESSED_BCX_SRGBA_MASK) { + alphaMode = nvtt::AlphaMode_Transparency; + compressionOptions.setFormat(nvtt::Format_BC1a); + } else if (mipFormat == gpu::Element::COLOR_COMPRESSED_BCX_SRGBA) { + alphaMode = nvtt::AlphaMode_Transparency; + compressionOptions.setFormat(nvtt::Format_BC3); + } else if (mipFormat == gpu::Element::COLOR_COMPRESSED_BCX_RED) { + compressionOptions.setFormat(nvtt::Format_BC4); + } else if (mipFormat == gpu::Element::COLOR_COMPRESSED_BCX_XY) { + compressionOptions.setFormat(nvtt::Format_BC5); + } else if (mipFormat == gpu::Element::COLOR_COMPRESSED_BCX_SRGBA_HIGH) { + alphaMode = nvtt::AlphaMode_Transparency; + compressionOptions.setFormat(nvtt::Format_BC7); + } else if (mipFormat == gpu::Element::COLOR_RGBA_32) { + compressionOptions.setFormat(nvtt::Format_RGBA); + compressionOptions.setPixelType(nvtt::PixelType_UnsignedNorm); + compressionOptions.setPitchAlignment(4); + compressionOptions.setPixelFormat(32, + 0x000000FF, + 0x0000FF00, + 0x00FF0000, + 0xFF000000); + inputGamma = 1.0f; + outputGamma = 1.0f; + } else if (mipFormat == gpu::Element::COLOR_BGRA_32) { + compressionOptions.setFormat(nvtt::Format_RGBA); + compressionOptions.setPixelType(nvtt::PixelType_UnsignedNorm); + compressionOptions.setPitchAlignment(4); + compressionOptions.setPixelFormat(32, + 0x00FF0000, + 0x0000FF00, + 0x000000FF, + 0xFF000000); + inputGamma = 1.0f; + outputGamma = 1.0f; + } else if (mipFormat == gpu::Element::COLOR_SRGBA_32) { + compressionOptions.setFormat(nvtt::Format_RGBA); + compressionOptions.setPixelType(nvtt::PixelType_UnsignedNorm); + compressionOptions.setPitchAlignment(4); + compressionOptions.setPixelFormat(32, + 0x000000FF, + 0x0000FF00, + 0x00FF0000, + 0xFF000000); + } else if (mipFormat == gpu::Element::COLOR_SBGRA_32) { + compressionOptions.setFormat(nvtt::Format_RGBA); + compressionOptions.setPixelType(nvtt::PixelType_UnsignedNorm); + compressionOptions.setPitchAlignment(4); + compressionOptions.setPixelFormat(32, + 0x00FF0000, + 0x0000FF00, + 0x000000FF, + 0xFF000000); + } else if (mipFormat == gpu::Element::COLOR_R_8) { + compressionOptions.setFormat(nvtt::Format_RGB); + compressionOptions.setPixelType(nvtt::PixelType_UnsignedNorm); + compressionOptions.setPitchAlignment(4); + compressionOptions.setPixelFormat(8, 0, 0, 0); + } else if (mipFormat == gpu::Element::VEC2NU8_XY) { + inputOptions.setNormalMap(true); + compressionOptions.setFormat(nvtt::Format_RGBA); + compressionOptions.setPixelType(nvtt::PixelType_UnsignedNorm); + compressionOptions.setPitchAlignment(4); + compressionOptions.setPixelFormat(8, 8, 0, 0); + } else { + qCWarning(imagelogging) << "Unknown mip format"; + Q_UNREACHABLE(); + return; } - } - // 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. + nvtt::OutputOptions outputOptions; + outputOptions.setOutputHeader(false); + OutputHandler outputHandler(texture, face); + outputOptions.setOutputHandler(&outputHandler); + MyErrorHandler errorHandler; + outputOptions.setErrorHandler(&errorHandler); - Etc::EncodeMipmaps( - (float *)floatData.data(), width, height, - etcFormat, errorMetric, effort, - numEncodeThreads, numEncodeThreads, - numMips, Etc::FILTER_WRAP_NONE, - mipMaps, &encodingTime - ); + 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; - for (int i = 0; i < numMips; i++) { - if (mipMaps[i].paucEncodingBits.get()) { - if (face >= 0) { - texture->assignStoredMipFace(i, face, mipMaps[i].uiEncodingBitsBytes, static_cast(mipMaps[i].paucEncodingBits.get())); - } else { - texture->assignStoredMip(i, mipMaps[i].uiEncodingBitsBytes, static_cast(mipMaps[i].paucEncodingBits.get())); + 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 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; -#endif + // 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(mipMaps[i].paucEncodingBits.get())); + } else { + texture->assignStoredMip(i, mipMaps[i].uiEncodingBitsBytes, static_cast(mipMaps[i].paucEncodingBits.get())); + } + } + } + + delete[] mipMaps; + } } #endif -void generateMips(gpu::Texture* texture, QImage&& image, const std::atomic& abortProcessing = false, int face = -1) { +void generateMips(gpu::Texture* texture, QImage&& image, BackendTarget target, const std::atomic& 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); + if (target == BackendTarget::GLES) { + generateLDRMips(texture, std::move(image), target, abortProcessing, face); + } else { + if (image.format() == hdrFormatForTarget(target)) { + 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 texture->setAutoGenerateMips(true); #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, - bool isStrict, const std::atomic& abortProcessing) { + BackendTarget target, bool isStrict, const std::atomic& abortProcessing) { 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 alphaAsMask = false; @@ -770,7 +767,11 @@ gpu::TexturePointer TextureUsage::process2DTextureColorFromImage(QImage&& srcIma if ((image.width() > 0) && (image.height() > 0)) { gpu::Element formatMip; 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) { // 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). @@ -780,14 +781,8 @@ gpu::TexturePointer TextureUsage::process2DTextureColorFromImage(QImage&& srcIma } formatMip = formatGPU; } 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; formatMip = gpu::Element::COLOR_SBGRA_32; -#endif } if (isStrict) { @@ -806,7 +801,7 @@ gpu::TexturePointer TextureUsage::process2DTextureColorFromImage(QImage&& srcIma theTexture->setUsage(usage.build()); theTexture->setStoredMipFormat(formatMip); theTexture->assignStoredMip(0, image.byteCount(), image.constBits()); - generateMips(theTexture.get(), std::move(image), abortProcessing); + generateMips(theTexture.get(), std::move(image), target, abortProcessing); } return theTexture; @@ -887,10 +882,10 @@ QImage processBumpMap(QImage&& image) { return result; } gpu::TexturePointer TextureUsage::process2DTextureNormalMapFromImage(QImage&& srcImage, const std::string& srcImageName, - bool compress, bool isBumpMap, + bool compress, BackendTarget target, bool isBumpMap, const std::atomic& abortProcessing) { PROFILE_RANGE(resource_parse, "process2DTextureNormalMapFromImage"); - QImage image = processSourceImage(std::move(srcImage), false); + QImage image = processSourceImage(std::move(srcImage), false, target); if (isBumpMap) { image = processBumpMap(std::move(image)); @@ -908,11 +903,11 @@ gpu::TexturePointer TextureUsage::process2DTextureNormalMapFromImage(QImage&& sr if (compress) { formatGPU = gpu::Element::COLOR_COMPRESSED_BCX_XY; } else { -#ifdef USE_GLES - formatGPU = gpu::Element::COLOR_COMPRESSED_EAC_XY; -#else - formatGPU = gpu::Element::VEC2NU8_XY; -#endif + if (target == BackendTarget::GLES) { + formatGPU = gpu::Element::COLOR_COMPRESSED_EAC_XY; + } else { + formatGPU = gpu::Element::VEC2NU8_XY; + } } formatMip = formatGPU; @@ -920,17 +915,17 @@ gpu::TexturePointer TextureUsage::process2DTextureNormalMapFromImage(QImage&& sr theTexture->setSource(srcImageName); theTexture->setStoredMipFormat(formatMip); theTexture->assignStoredMip(0, image.byteCount(), image.constBits()); - generateMips(theTexture.get(), std::move(image), abortProcessing); + generateMips(theTexture.get(), std::move(image), target, abortProcessing); } return theTexture; } gpu::TexturePointer TextureUsage::process2DTextureGrayscaleFromImage(QImage&& srcImage, const std::string& srcImageName, - bool compress, bool isInvertedPixels, + bool compress, BackendTarget target, bool isInvertedPixels, const std::atomic& abortProcessing) { 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) { image = image.convertToFormat(QImage::Format_ARGB32); @@ -948,11 +943,11 @@ gpu::TexturePointer TextureUsage::process2DTextureGrayscaleFromImage(QImage&& sr if (compress) { formatGPU = gpu::Element::COLOR_COMPRESSED_BCX_RED; } else { -#ifdef USE_GLES - formatGPU = gpu::Element::COLOR_COMPRESSED_EAC_RED; -#else - formatGPU = gpu::Element::COLOR_R_8; -#endif + if (target == BackendTarget::GLES) { + formatGPU = gpu::Element::COLOR_COMPRESSED_EAC_RED; + } else { + formatGPU = gpu::Element::COLOR_R_8; + } } formatMip = formatGPU; @@ -960,7 +955,7 @@ gpu::TexturePointer TextureUsage::process2DTextureGrayscaleFromImage(QImage&& sr theTexture->setSource(srcImageName); theTexture->setStoredMipFormat(formatMip); theTexture->assignStoredMip(0, image.byteCount(), image.constBits()); - generateMips(theTexture.get(), std::move(image), abortProcessing); + generateMips(theTexture.get(), std::move(image), target, abortProcessing); } return theTexture; @@ -1233,12 +1228,12 @@ const int CubeLayout::NUM_CUBEMAP_LAYOUTS = sizeof(CubeLayout::CUBEMAP_LAYOUTS) //#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 // 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 hdrImage(localCopy.width(), localCopy.height(), (QImage::Format)QIMAGE_HDR_FORMAT); + QImage hdrImage(localCopy.width(), localCopy.height(), hdrFormatForTarget(target)); std::function packFunc; #ifdef DEBUG_COLOR_PACKING std::function unpackFunc; @@ -1292,7 +1287,7 @@ QImage convertToHDRFormat(QImage&& srcImage, gpu::Element format) { } gpu::TexturePointer TextureUsage::processCubeTextureColorFromImage(QImage&& srcImage, const std::string& srcImageName, - bool compress, bool generateIrradiance, + bool compress, BackendTarget target, bool generateIrradiance, const std::atomic& abortProcessing) { PROFILE_RANGE(resource_parse, "processCubeTextureColorFromImage"); @@ -1308,14 +1303,14 @@ gpu::TexturePointer TextureUsage::processCubeTextureColorFromImage(QImage&& srcI 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) { -#ifndef USE_GLES - image = convertToHDRFormat(std::move(image), HDR_FORMAT); -#else - image = image.convertToFormat(QImage::Format_RGB32); -#endif + if (image.format() != hdrFormatForTarget(target)) { + if (target == BackendTarget::GLES) { + image = image.convertToFormat(QImage::Format_RGB32); + } else { + image = convertToHDRFormat(std::move(image), HDR_FORMAT, target); + } } gpu::Element formatMip; @@ -1323,11 +1318,11 @@ gpu::TexturePointer TextureUsage::processCubeTextureColorFromImage(QImage&& srcI if (compress) { formatGPU = gpu::Element::COLOR_COMPRESSED_BCX_HDR_RGB; } else { -#ifdef USE_GLES - formatGPU = gpu::Element::COLOR_COMPRESSED_ETC2_SRGB; -#else - formatGPU = HDR_FORMAT; -#endif + if (target == BackendTarget::GLES) { + formatGPU = gpu::Element::COLOR_COMPRESSED_ETC2_SRGB; + } else { + formatGPU = HDR_FORMAT; + } } formatMip = formatGPU; @@ -1378,11 +1373,12 @@ gpu::TexturePointer TextureUsage::processCubeTextureColorFromImage(QImage&& srcI PROFILE_RANGE(resource_parse, "generateIrradiance"); 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 + if (target == BackendTarget::GLES) { + irradianceFormat = gpu::Element::COLOR_SRGBA_32; + } else { + irradianceFormat = HDR_FORMAT; + } + 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(irradianceFormat); @@ -1397,7 +1393,7 @@ gpu::TexturePointer TextureUsage::processCubeTextureColorFromImage(QImage&& srcI } 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); } } diff --git a/libraries/image/src/image/Image.h b/libraries/image/src/image/Image.h index ccf4845fca..43b1b613ad 100644 --- a/libraries/image/src/image/Image.h +++ b/libraries/image/src/image/Image.h @@ -21,6 +21,11 @@ class QImage; namespace image { +enum class BackendTarget { + GL, + GLES +}; + namespace TextureUsage { enum Type { @@ -41,42 +46,41 @@ enum Type { UNUSED_TEXTURE }; -using TextureLoader = std::function&)>; +using TextureLoader = std::function&)>; TextureLoader getTextureLoaderForType(Type type, const QVariantMap& options = QVariantMap()); gpu::TexturePointer create2DTextureFromImage(QImage&& image, const std::string& srcImageName, - bool compress, const std::atomic& abortProcessing); + bool compress, BackendTarget target, const std::atomic& abortProcessing); gpu::TexturePointer createStrict2DTextureFromImage(QImage&& image, const std::string& srcImageName, - bool compress, const std::atomic& abortProcessing); + bool compress, BackendTarget target, const std::atomic& abortProcessing); gpu::TexturePointer createAlbedoTextureFromImage(QImage&& image, const std::string& srcImageName, - bool compress, const std::atomic& abortProcessing); + bool compress, BackendTarget target, const std::atomic& abortProcessing); gpu::TexturePointer createEmissiveTextureFromImage(QImage&& image, const std::string& srcImageName, - bool compress, const std::atomic& abortProcessing); + bool compress, BackendTarget target, const std::atomic& abortProcessing); gpu::TexturePointer createNormalTextureFromNormalImage(QImage&& image, const std::string& srcImageName, - bool compress, const std::atomic& abortProcessing); + bool compress, BackendTarget target, const std::atomic& abortProcessing); gpu::TexturePointer createNormalTextureFromBumpImage(QImage&& image, const std::string& srcImageName, - bool compress, const std::atomic& abortProcessing); + bool compress, BackendTarget target, const std::atomic& abortProcessing); gpu::TexturePointer createRoughnessTextureFromImage(QImage&& image, const std::string& srcImageName, - bool compress, const std::atomic& abortProcessing); + bool compress, BackendTarget target, const std::atomic& abortProcessing); gpu::TexturePointer createRoughnessTextureFromGlossImage(QImage&& image, const std::string& srcImageName, - bool compress, const std::atomic& abortProcessing); + bool compress, BackendTarget target, const std::atomic& abortProcessing); gpu::TexturePointer createMetallicTextureFromImage(QImage&& image, const std::string& srcImageName, - bool compress, const std::atomic& abortProcessing); + bool compress, BackendTarget target, const std::atomic& abortProcessing); gpu::TexturePointer createCubeTextureFromImage(QImage&& image, const std::string& srcImageName, - bool compress, const std::atomic& abortProcessing); + bool compress, BackendTarget target, const std::atomic& abortProcessing); gpu::TexturePointer createCubeTextureFromImageWithoutIrradiance(QImage&& image, const std::string& srcImageName, - bool compress, const std::atomic& abortProcessing); + bool compress, BackendTarget target, const std::atomic& abortProcessing); gpu::TexturePointer createLightmapTextureFromImage(QImage&& image, const std::string& srcImageName, - bool compress, const std::atomic& abortProcessing); - + bool compress, BackendTarget target, const std::atomic& abortProcessing); gpu::TexturePointer process2DTextureColorFromImage(QImage&& srcImage, const std::string& srcImageName, bool compress, - bool isStrict, const std::atomic& abortProcessing); + BackendTarget target, bool isStrict, const std::atomic& abortProcessing); gpu::TexturePointer process2DTextureNormalMapFromImage(QImage&& srcImage, const std::string& srcImageName, bool compress, - bool isBumpMap, const std::atomic& abortProcessing); + BackendTarget target, bool isBumpMap, const std::atomic& abortProcessing); gpu::TexturePointer process2DTextureGrayscaleFromImage(QImage&& srcImage, const std::string& srcImageName, bool compress, - bool isInvertedPixels, const std::atomic& abortProcessing); + BackendTarget target, bool isInvertedPixels, const std::atomic& abortProcessing); gpu::TexturePointer processCubeTextureColorFromImage(QImage&& srcImage, const std::string& srcImageName, bool compress, - bool generateIrradiance, const std::atomic& abortProcessing); + BackendTarget target, bool generateIrradiance, const std::atomic& abortProcessing); } // namespace TextureUsage @@ -84,7 +88,13 @@ const QStringList getSupportedFormats(); gpu::TexturePointer processImage(std::shared_ptr content, const std::string& url, int maxNumPixels, TextureUsage::Type textureType, - bool compress = false, const std::atomic& abortProcessing = false); + bool compress = false, +#ifdef USE_GLES + BackendTarget target = BackendTarget::GLES, +#else + BackendTarget target = BackendTarget::GL, +#endif + const std::atomic& abortProcessing = false); } // namespace image diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 40b31cac53..1485766aa2 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -279,7 +279,12 @@ gpu::TexturePointer TextureCache::getImageTexture(const QString& path, image::Te return nullptr; } 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 TextureCache::createResource(const QUrl& url, const QSharedPointer& fallback, @@ -1160,7 +1165,13 @@ void ImageReader::read() { // IMPORTANT: _content is empty past this point auto buffer = std::shared_ptr((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) { qCWarning(modelnetworking) << "Could not process:" << _url; From f1e63f489b8eae069c21d66d90482398a0066157 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 16 Jul 2018 08:47:17 -0700 Subject: [PATCH 02/10] Update image::processImage to use compress flag for GLES --- libraries/image/src/image/Image.cpp | 53 ++++++++++++++++------------- libraries/image/src/image/Image.h | 8 +---- 2 files changed, 31 insertions(+), 30 deletions(-) diff --git a/libraries/image/src/image/Image.cpp b/libraries/image/src/image/Image.cpp index eaf8eacfce..a35aa064ae 100644 --- a/libraries/image/src/image/Image.cpp +++ b/libraries/image/src/image/Image.cpp @@ -767,22 +767,28 @@ gpu::TexturePointer TextureUsage::process2DTextureColorFromImage(QImage&& srcIma if ((image.width() > 0) && (image.height() > 0)) { gpu::Element formatMip; gpu::Element formatGPU; - if (target == BackendTarget::GLES) { - // GLES does not support GL_BGRA - formatGPU = gpu::Element::COLOR_COMPRESSED_ETC2_SRGBA; - formatMip = formatGPU; - } else if (compress) { - 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). - formatGPU = gpu::Element::COLOR_COMPRESSED_BCX_SRGBA; + if (compress) { + if (target == BackendTarget::GLES) { + // GLES does not support GL_BGRA + formatGPU = gpu::Element::COLOR_COMPRESSED_ETC2_SRGBA; + formatMip = formatGPU; } else { - formatGPU = gpu::Element::COLOR_COMPRESSED_BCX_SRGB; + 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). + formatGPU = gpu::Element::COLOR_COMPRESSED_BCX_SRGBA; + } else { + formatGPU = gpu::Element::COLOR_COMPRESSED_BCX_SRGB; + } + formatMip = formatGPU; } - formatMip = formatGPU; } else { - formatGPU = gpu::Element::COLOR_SRGBA_32; - formatMip = gpu::Element::COLOR_SBGRA_32; + if (target == BackendTarget::GLES) { + static_assert(false); + } else { + formatGPU = gpu::Element::COLOR_SRGBA_32; + formatMip = gpu::Element::COLOR_SBGRA_32; + } } if (isStrict) { @@ -901,13 +907,13 @@ gpu::TexturePointer TextureUsage::process2DTextureNormalMapFromImage(QImage&& sr gpu::Element formatMip; gpu::Element formatGPU; if (compress) { - formatGPU = gpu::Element::COLOR_COMPRESSED_BCX_XY; - } else { if (target == BackendTarget::GLES) { formatGPU = gpu::Element::COLOR_COMPRESSED_EAC_XY; } else { - formatGPU = gpu::Element::VEC2NU8_XY; + formatGPU = gpu::Element::COLOR_COMPRESSED_BCX_XY; } + } else { + formatGPU = gpu::Element::VEC2NU8_XY; } formatMip = formatGPU; @@ -941,13 +947,13 @@ gpu::TexturePointer TextureUsage::process2DTextureGrayscaleFromImage(QImage&& sr gpu::Element formatMip; gpu::Element formatGPU; if (compress) { - formatGPU = gpu::Element::COLOR_COMPRESSED_BCX_RED; - } else { if (target == BackendTarget::GLES) { formatGPU = gpu::Element::COLOR_COMPRESSED_EAC_RED; } else { - formatGPU = gpu::Element::COLOR_R_8; - } + formatGPU = gpu::Element::COLOR_COMPRESSED_BCX_RED; + } + } else { + formatGPU = gpu::Element::COLOR_R_8; } formatMip = formatGPU; @@ -1316,14 +1322,15 @@ gpu::TexturePointer TextureUsage::processCubeTextureColorFromImage(QImage&& srcI gpu::Element formatMip; gpu::Element formatGPU; if (compress) { - formatGPU = gpu::Element::COLOR_COMPRESSED_BCX_HDR_RGB; - } else { if (target == BackendTarget::GLES) { formatGPU = gpu::Element::COLOR_COMPRESSED_ETC2_SRGB; } else { - formatGPU = HDR_FORMAT; + formatGPU = gpu::Element::COLOR_COMPRESSED_BCX_HDR_RGB; } + } else { + formatGPU = HDR_FORMAT; } + formatMip = formatGPU; // Find the layout of the cubemap in the 2D image diff --git a/libraries/image/src/image/Image.h b/libraries/image/src/image/Image.h index 43b1b613ad..9e56369ad3 100644 --- a/libraries/image/src/image/Image.h +++ b/libraries/image/src/image/Image.h @@ -88,13 +88,7 @@ const QStringList getSupportedFormats(); gpu::TexturePointer processImage(std::shared_ptr content, const std::string& url, int maxNumPixels, TextureUsage::Type textureType, - bool compress = false, -#ifdef USE_GLES - BackendTarget target = BackendTarget::GLES, -#else - BackendTarget target = BackendTarget::GL, -#endif - const std::atomic& abortProcessing = false); + bool compress, BackendTarget target, const std::atomic& abortProcessing = false); } // namespace image From 140b9be1c43b196dfa576941234bd0a1eabbc180 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 16 Jul 2018 10:00:11 -0700 Subject: [PATCH 03/10] Update BackendTarget to include version --- libraries/baking/src/TextureBaker.cpp | 6 ++--- libraries/image/src/image/Image.cpp | 23 +++++++++---------- libraries/image/src/image/Image.h | 5 ++-- .../src/model-networking/TextureCache.cpp | 11 ++++++--- 4 files changed, 25 insertions(+), 20 deletions(-) diff --git a/libraries/baking/src/TextureBaker.cpp b/libraries/baking/src/TextureBaker.cpp index 24fc331a3b..66c86d8c5f 100644 --- a/libraries/baking/src/TextureBaker.cpp +++ b/libraries/baking/src/TextureBaker.cpp @@ -150,8 +150,8 @@ void TextureBaker::processTexture() { // Compressed KTX if (_compressionEnabled) { constexpr std::array BACKEND_TARGETS { - image::BackendTarget::GL, - image::BackendTarget::GLES + image::BackendTarget::GL45, + image::BackendTarget::GLES32 }; for (auto target : BACKEND_TARGETS) { auto processedTexture = image::processImage(buffer, _textureURL.toString().toStdString(), @@ -198,7 +198,7 @@ void TextureBaker::processTexture() { if (_textureType == image::TextureUsage::Type::CUBE_TEXTURE) { buffer->reset(); auto processedTexture = image::processImage(std::move(buffer), _textureURL.toString().toStdString(), - ABSOLUTE_MAX_TEXTURE_NUM_PIXELS, _textureType, false, image::BackendTarget::GL, _abortProcessing); + ABSOLUTE_MAX_TEXTURE_NUM_PIXELS, _textureType, false, image::BackendTarget::GL45, _abortProcessing); if (!processedTexture) { handleError("Could not process texture " + _textureURL.toString()); return; diff --git a/libraries/image/src/image/Image.cpp b/libraries/image/src/image/Image.cpp index a35aa064ae..2c9255d215 100644 --- a/libraries/image/src/image/Image.cpp +++ b/libraries/image/src/image/Image.cpp @@ -80,7 +80,7 @@ const QStringList getSupportedFormats() { // On GLES, we don't use HDR skyboxes QImage::Format hdrFormatForTarget(BackendTarget target) { - if (target == BackendTarget::GLES) { + if (target == BackendTarget::GLES32) { return QImage::Format_RGB32; } return QImage::Format_RGB30; @@ -271,7 +271,7 @@ QImage processSourceImage(QImage&& srcImage, bool cubemap, BackendTarget target) const glm::uvec2 srcImageSize = toGlm(localCopy.size()); glm::uvec2 targetSize = srcImageSize; - const auto maxTextureSize = target == BackendTarget::GL ? MAX_TEXTURE_SIZE_GL : MAX_TEXTURE_SIZE_GLES; + const auto maxTextureSize = target == BackendTarget::GLES32 ? MAX_TEXTURE_SIZE_GLES : MAX_TEXTURE_SIZE_GL; while (glm::any(glm::greaterThan(targetSize, maxTextureSize))) { targetSize /= 2; } @@ -513,7 +513,7 @@ void generateLDRMips(gpu::Texture* texture, QImage&& image, BackendTarget target const int width = localCopy.width(), height = localCopy.height(); auto mipFormat = texture->getStoredMipFormat(); - if (target != BackendTarget::GLES) { + if (target != BackendTarget::GLES32) { const void* data = static_cast(localCopy.constBits()); nvtt::TextureType textureType = nvtt::TextureType_2D; nvtt::InputFormat inputFormat = nvtt::InputFormat_BGRA_8UB; @@ -705,7 +705,7 @@ void generateMips(gpu::Texture* texture, QImage&& image, BackendTarget target, c #if CPU_MIPMAPS PROFILE_RANGE(resource_parse, "generateMips"); - if (target == BackendTarget::GLES) { + if (target == BackendTarget::GLES32) { generateLDRMips(texture, std::move(image), target, abortProcessing, face); } else { if (image.format() == hdrFormatForTarget(target)) { @@ -768,7 +768,7 @@ gpu::TexturePointer TextureUsage::process2DTextureColorFromImage(QImage&& srcIma gpu::Element formatMip; gpu::Element formatGPU; if (compress) { - if (target == BackendTarget::GLES) { + if (target == BackendTarget::GLES32) { // GLES does not support GL_BGRA formatGPU = gpu::Element::COLOR_COMPRESSED_ETC2_SRGBA; formatMip = formatGPU; @@ -783,8 +783,7 @@ gpu::TexturePointer TextureUsage::process2DTextureColorFromImage(QImage&& srcIma formatMip = formatGPU; } } else { - if (target == BackendTarget::GLES) { - static_assert(false); + if (target == BackendTarget::GLES32) { } else { formatGPU = gpu::Element::COLOR_SRGBA_32; formatMip = gpu::Element::COLOR_SBGRA_32; @@ -907,7 +906,7 @@ gpu::TexturePointer TextureUsage::process2DTextureNormalMapFromImage(QImage&& sr gpu::Element formatMip; gpu::Element formatGPU; if (compress) { - if (target == BackendTarget::GLES) { + if (target == BackendTarget::GLES32) { formatGPU = gpu::Element::COLOR_COMPRESSED_EAC_XY; } else { formatGPU = gpu::Element::COLOR_COMPRESSED_BCX_XY; @@ -947,7 +946,7 @@ gpu::TexturePointer TextureUsage::process2DTextureGrayscaleFromImage(QImage&& sr gpu::Element formatMip; gpu::Element formatGPU; if (compress) { - if (target == BackendTarget::GLES) { + if (target == BackendTarget::GLES32) { formatGPU = gpu::Element::COLOR_COMPRESSED_EAC_RED; } else { formatGPU = gpu::Element::COLOR_COMPRESSED_BCX_RED; @@ -1312,7 +1311,7 @@ gpu::TexturePointer TextureUsage::processCubeTextureColorFromImage(QImage&& srcI QImage image = processSourceImage(std::move(localCopy), true, target); if (image.format() != hdrFormatForTarget(target)) { - if (target == BackendTarget::GLES) { + if (target == BackendTarget::GLES32) { image = image.convertToFormat(QImage::Format_RGB32); } else { image = convertToHDRFormat(std::move(image), HDR_FORMAT, target); @@ -1322,7 +1321,7 @@ gpu::TexturePointer TextureUsage::processCubeTextureColorFromImage(QImage&& srcI gpu::Element formatMip; gpu::Element formatGPU; if (compress) { - if (target == BackendTarget::GLES) { + if (target == BackendTarget::GLES32) { formatGPU = gpu::Element::COLOR_COMPRESSED_ETC2_SRGB; } else { formatGPU = gpu::Element::COLOR_COMPRESSED_BCX_HDR_RGB; @@ -1380,7 +1379,7 @@ gpu::TexturePointer TextureUsage::processCubeTextureColorFromImage(QImage&& srcI PROFILE_RANGE(resource_parse, "generateIrradiance"); gpu::Element irradianceFormat; // TODO: we could locally compress the irradiance texture on Android, but we don't need to - if (target == BackendTarget::GLES) { + if (target == BackendTarget::GLES32) { irradianceFormat = gpu::Element::COLOR_SRGBA_32; } else { irradianceFormat = HDR_FORMAT; diff --git a/libraries/image/src/image/Image.h b/libraries/image/src/image/Image.h index 9e56369ad3..e633cfc600 100644 --- a/libraries/image/src/image/Image.h +++ b/libraries/image/src/image/Image.h @@ -22,8 +22,9 @@ class QImage; namespace image { enum class BackendTarget { - GL, - GLES + GL41, + GL45, + GLES32 }; namespace TextureUsage { diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 1485766aa2..1f536e8f09 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -280,9 +280,9 @@ gpu::TexturePointer TextureCache::getImageTexture(const QString& path, image::Te } auto loader = image::TextureUsage::getTextureLoaderForType(type, options); #ifdef USE_GLES - image::BackendTarget target = image::BackendTarget::GLES; + image::BackendTarget target = image::BackendTarget::GLES32; #else - image::BackendTarget target = image::BackendTarget::GL; + image::BackendTarget target = image::BackendTarget::GL45; #endif return gpu::TexturePointer(loader(std::move(image), path.toStdString(), false, target, false)); } @@ -1171,7 +1171,12 @@ void ImageReader::read() { #else constexpr bool shouldCompress = false; #endif - texture = image::processImage(std::move(buffer), _url.toString().toStdString(), _maxNumPixels, networkTexture->getTextureType(), shouldCompress); + #ifdef USE_GLES + image::BackendTarget target = image::BackendTarget::GLES32; + #else + image::BackendTarget target = image::BackendTarget::GL45; + #endif + texture = image::processImage(std::move(buffer), _url.toString().toStdString(), _maxNumPixels, networkTexture->getTextureType(), shouldCompress, target); if (!texture) { qCWarning(modelnetworking) << "Could not process:" << _url; From 26e69de81ea0cea5da17d2f24329c0203f6cbda9 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 24 Jul 2018 16:37:34 -0700 Subject: [PATCH 04/10] Add braces around std::array intialization to suppress warning --- libraries/baking/src/TextureBaker.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/baking/src/TextureBaker.cpp b/libraries/baking/src/TextureBaker.cpp index 66c86d8c5f..17bdf6fa7d 100644 --- a/libraries/baking/src/TextureBaker.cpp +++ b/libraries/baking/src/TextureBaker.cpp @@ -149,10 +149,10 @@ void TextureBaker::processTexture() { // Compressed KTX if (_compressionEnabled) { - constexpr std::array BACKEND_TARGETS { + constexpr std::array BACKEND_TARGETS {{ image::BackendTarget::GL45, image::BackendTarget::GLES32 - }; + }}; for (auto target : BACKEND_TARGETS) { auto processedTexture = image::processImage(buffer, _textureURL.toString().toStdString(), ABSOLUTE_MAX_TEXTURE_NUM_PIXELS, _textureType, true, From 6be0c43fca4f07180901adae8b6c9ba57cbf2ff5 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 26 Jul 2018 16:40:05 -0700 Subject: [PATCH 05/10] Update generateIrradiance to take a BackendTarget --- libraries/baking/src/TextureBaker.cpp | 8 +-- libraries/gpu/src/gpu/Texture.cpp | 72 +++++++++---------- libraries/gpu/src/gpu/Texture.h | 9 ++- libraries/image/src/image/Image.cpp | 2 +- libraries/image/src/image/Image.h | 42 +++++------ .../src/model-networking/TextureCache.cpp | 8 +-- 6 files changed, 70 insertions(+), 71 deletions(-) diff --git a/libraries/baking/src/TextureBaker.cpp b/libraries/baking/src/TextureBaker.cpp index 17bdf6fa7d..2516323c37 100644 --- a/libraries/baking/src/TextureBaker.cpp +++ b/libraries/baking/src/TextureBaker.cpp @@ -149,9 +149,9 @@ void TextureBaker::processTexture() { // Compressed KTX if (_compressionEnabled) { - constexpr std::array BACKEND_TARGETS {{ - image::BackendTarget::GL45, - image::BackendTarget::GLES32 + constexpr std::array BACKEND_TARGETS {{ + gpu::BackendTarget::GL45, + gpu::BackendTarget::GLES32 }}; for (auto target : BACKEND_TARGETS) { auto processedTexture = image::processImage(buffer, _textureURL.toString().toStdString(), @@ -198,7 +198,7 @@ void TextureBaker::processTexture() { if (_textureType == image::TextureUsage::Type::CUBE_TEXTURE) { buffer->reset(); auto processedTexture = image::processImage(std::move(buffer), _textureURL.toString().toStdString(), - ABSOLUTE_MAX_TEXTURE_NUM_PIXELS, _textureType, false, image::BackendTarget::GL45, _abortProcessing); + ABSOLUTE_MAX_TEXTURE_NUM_PIXELS, _textureType, false, gpu::BackendTarget::GL45, _abortProcessing); if (!processedTexture) { handleError("Could not process texture " + _textureURL.toString()); return; diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index 34262b0cd9..d34fa954c8 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -510,7 +510,7 @@ void Texture::setSampler(const Sampler& sampler) { } -bool Texture::generateIrradiance() { +bool Texture::generateIrradiance(gpu::BackendTarget target) { if (getType() != TEX_CUBE) { return false; } @@ -521,7 +521,7 @@ bool Texture::generateIrradiance() { _irradiance = std::make_shared(); } - _irradiance->evalFromTexture(*this); + _irradiance->evalFromTexture(*this, target); return true; } @@ -684,7 +684,7 @@ void sphericalHarmonicsEvaluateDirection(float * result, int order, const glm:: result[8] = P_2_2 * ((double)dir.x * (double)dir.x - (double)dir.y * (double)dir.y); } -bool sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture, std::vector & output, const uint order) { +bool sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture, std::vector & output, const uint order, gpu::BackendTarget target) { int width = cubeTexture.getWidth(); if(width != cubeTexture.getHeight()) { return false; @@ -692,22 +692,6 @@ bool sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture, std::vector< PROFILE_RANGE(render_gpu, "sphericalHarmonicsFromTexture"); -#ifndef USE_GLES - auto mipFormat = cubeTexture.getStoredMipFormat(); - std::function unpackFunc; - switch (mipFormat.getSemantic()) { - case gpu::R11G11B10: - unpackFunc = glm::unpackF2x11_1x10; - break; - case gpu::RGB9E5: - unpackFunc = glm::unpackF3x9_E1x5; - break; - default: - assert(false); - break; - } -#endif - const uint sqOrder = order*order; // allocate memory for calculations @@ -741,11 +725,7 @@ 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( cubeTexture.accessStoredMipFace(0, face)->readData() ); -#else auto data = cubeTexture.accessStoredMipFace(0, face)->readData(); -#endif if (data == nullptr) { continue; } @@ -827,20 +807,40 @@ bool sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture, std::vector< // get color from texture 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 + + if (target != gpu::BackendTarget::GLES32) { + auto mipFormat = cubeTexture.getStoredMipFormat(); + std::function unpackFunc; + switch (mipFormat.getSemantic()) { + case gpu::R11G11B10: + unpackFunc = glm::unpackF2x11_1x10; + break; + case gpu::RGB9E5: + unpackFunc = glm::unpackF3x9_E1x5; + break; + default: + assert(false); + break; + } + auto data32 = reinterpret_cast(data); + for (int i = 0; i < stride; ++i) { + for (int j = 0; j < stride; ++j) { + int k = (int)(x + i - halfStride + (y + j - halfStride) * width); + color += unpackFunc(data32[k]); + } + } + } else { + // BGRA -> RGBA + const int NUM_COMPONENTS_PER_PIXEL = 4; + for (int i = 0; i < stride; ++i) { + for (int j = 0; j < stride; ++j) { + int k = NUM_COMPONENTS_PER_PIXEL * (int)(x + i - halfStride + (y + j - halfStride) * width); + color += glm::pow(glm::vec3(data[k + 2], data[k + 1], data[k]) / 255.0f, glm::vec3(2.2f)); + } } } + // scale color and add to previously accumulated coefficients // red sphericalHarmonicsScale(shBuffB.data(), order, shBuff.data(), color.r * fDiffSolid); @@ -869,10 +869,10 @@ bool sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture, std::vector< return true; } -void SphericalHarmonics::evalFromTexture(const Texture& texture) { +void SphericalHarmonics::evalFromTexture(const Texture& texture, gpu::BackendTarget target) { if (texture.isDefined()) { std::vector< glm::vec3 > coefs; - sphericalHarmonicsFromTexture(texture, coefs, 3); + sphericalHarmonicsFromTexture(texture, coefs, 3, target); L00 = coefs[0]; L1m1 = coefs[1]; diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index 9ad5dc0816..73ed1b15dc 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -43,6 +43,11 @@ namespace khronos { namespace gl { namespace texture { namespace gpu { +enum class BackendTarget { + GL41, + GL45, + GLES32 +}; const std::string SOURCE_HASH_KEY { "hifi.sourceHash" }; @@ -82,7 +87,7 @@ public: void assignPreset(int p); - void evalFromTexture(const Texture& texture); + void evalFromTexture(const Texture& texture, gpu::BackendTarget target); }; typedef std::shared_ptr< SphericalHarmonics > SHPointer; @@ -541,7 +546,7 @@ public: Usage getUsage() const { return _usage; } // For Cube Texture, it's possible to generate the irradiance spherical harmonics and make them availalbe with the texture - bool generateIrradiance(); + bool generateIrradiance(gpu::BackendTarget target); const SHPointer& getIrradiance(uint16 slice = 0) const { return _irradiance; } void overrideIrradiance(SHPointer irradiance) { _irradiance = irradiance; } bool isIrradianceValid() const { return _isIrradianceValid; } diff --git a/libraries/image/src/image/Image.cpp b/libraries/image/src/image/Image.cpp index 2c9255d215..1355a24bf4 100644 --- a/libraries/image/src/image/Image.cpp +++ b/libraries/image/src/image/Image.cpp @@ -1392,7 +1392,7 @@ gpu::TexturePointer TextureUsage::processCubeTextureColorFromImage(QImage&& srcI irradianceTexture->assignStoredMipFace(0, face, faces[face].byteCount(), faces[face].constBits()); } - irradianceTexture->generateIrradiance(); + irradianceTexture->generateIrradiance(target); auto irradiance = irradianceTexture->getIrradiance(); theTexture->overrideIrradiance(irradiance); diff --git a/libraries/image/src/image/Image.h b/libraries/image/src/image/Image.h index e633cfc600..ae72a183b3 100644 --- a/libraries/image/src/image/Image.h +++ b/libraries/image/src/image/Image.h @@ -21,12 +21,6 @@ class QImage; namespace image { -enum class BackendTarget { - GL41, - GL45, - GLES32 -}; - namespace TextureUsage { enum Type { @@ -47,41 +41,41 @@ enum Type { UNUSED_TEXTURE }; -using TextureLoader = std::function&)>; +using TextureLoader = std::function&)>; TextureLoader getTextureLoaderForType(Type type, const QVariantMap& options = QVariantMap()); gpu::TexturePointer create2DTextureFromImage(QImage&& image, const std::string& srcImageName, - bool compress, BackendTarget target, const std::atomic& abortProcessing); + bool compress, gpu::BackendTarget target, const std::atomic& abortProcessing); gpu::TexturePointer createStrict2DTextureFromImage(QImage&& image, const std::string& srcImageName, - bool compress, BackendTarget target, const std::atomic& abortProcessing); + bool compress, gpu::BackendTarget target, const std::atomic& abortProcessing); gpu::TexturePointer createAlbedoTextureFromImage(QImage&& image, const std::string& srcImageName, - bool compress, BackendTarget target, const std::atomic& abortProcessing); + bool compress, gpu::BackendTarget target, const std::atomic& abortProcessing); gpu::TexturePointer createEmissiveTextureFromImage(QImage&& image, const std::string& srcImageName, - bool compress, BackendTarget target, const std::atomic& abortProcessing); + bool compress, gpu::BackendTarget target, const std::atomic& abortProcessing); gpu::TexturePointer createNormalTextureFromNormalImage(QImage&& image, const std::string& srcImageName, - bool compress, BackendTarget target, const std::atomic& abortProcessing); + bool compress, gpu::BackendTarget target, const std::atomic& abortProcessing); gpu::TexturePointer createNormalTextureFromBumpImage(QImage&& image, const std::string& srcImageName, - bool compress, BackendTarget target, const std::atomic& abortProcessing); + bool compress, gpu::BackendTarget target, const std::atomic& abortProcessing); gpu::TexturePointer createRoughnessTextureFromImage(QImage&& image, const std::string& srcImageName, - bool compress, BackendTarget target, const std::atomic& abortProcessing); + bool compress, gpu::BackendTarget target, const std::atomic& abortProcessing); gpu::TexturePointer createRoughnessTextureFromGlossImage(QImage&& image, const std::string& srcImageName, - bool compress, BackendTarget target, const std::atomic& abortProcessing); + bool compress, gpu::BackendTarget target, const std::atomic& abortProcessing); gpu::TexturePointer createMetallicTextureFromImage(QImage&& image, const std::string& srcImageName, - bool compress, BackendTarget target, const std::atomic& abortProcessing); + bool compress, gpu::BackendTarget target, const std::atomic& abortProcessing); gpu::TexturePointer createCubeTextureFromImage(QImage&& image, const std::string& srcImageName, - bool compress, BackendTarget target, const std::atomic& abortProcessing); + bool compress, gpu::BackendTarget target, const std::atomic& abortProcessing); gpu::TexturePointer createCubeTextureFromImageWithoutIrradiance(QImage&& image, const std::string& srcImageName, - bool compress, BackendTarget target, const std::atomic& abortProcessing); + bool compress, gpu::BackendTarget target, const std::atomic& abortProcessing); gpu::TexturePointer createLightmapTextureFromImage(QImage&& image, const std::string& srcImageName, - bool compress, BackendTarget target, const std::atomic& abortProcessing); + bool compress, gpu::BackendTarget target, const std::atomic& abortProcessing); gpu::TexturePointer process2DTextureColorFromImage(QImage&& srcImage, const std::string& srcImageName, bool compress, - BackendTarget target, bool isStrict, const std::atomic& abortProcessing); + gpu::BackendTarget target, bool isStrict, const std::atomic& abortProcessing); gpu::TexturePointer process2DTextureNormalMapFromImage(QImage&& srcImage, const std::string& srcImageName, bool compress, - BackendTarget target, bool isBumpMap, const std::atomic& abortProcessing); + gpu::BackendTarget target, bool isBumpMap, const std::atomic& abortProcessing); gpu::TexturePointer process2DTextureGrayscaleFromImage(QImage&& srcImage, const std::string& srcImageName, bool compress, - BackendTarget target, bool isInvertedPixels, const std::atomic& abortProcessing); + gpu::BackendTarget target, bool isInvertedPixels, const std::atomic& abortProcessing); gpu::TexturePointer processCubeTextureColorFromImage(QImage&& srcImage, const std::string& srcImageName, bool compress, - BackendTarget target, bool generateIrradiance, const std::atomic& abortProcessing); + gpu::BackendTarget target, bool generateIrradiance, const std::atomic& abortProcessing); } // namespace TextureUsage @@ -89,7 +83,7 @@ const QStringList getSupportedFormats(); gpu::TexturePointer processImage(std::shared_ptr content, const std::string& url, int maxNumPixels, TextureUsage::Type textureType, - bool compress, BackendTarget target, const std::atomic& abortProcessing = false); + bool compress, gpu::BackendTarget target, const std::atomic& abortProcessing = false); } // namespace image diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 1f536e8f09..7f435838b6 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -280,9 +280,9 @@ gpu::TexturePointer TextureCache::getImageTexture(const QString& path, image::Te } auto loader = image::TextureUsage::getTextureLoaderForType(type, options); #ifdef USE_GLES - image::BackendTarget target = image::BackendTarget::GLES32; + gpu::BackendTarget target = gpu::BackendTarget::GLES32; #else - image::BackendTarget target = image::BackendTarget::GL45; + gpu::BackendTarget target = gpu::BackendTarget::GL45; #endif return gpu::TexturePointer(loader(std::move(image), path.toStdString(), false, target, false)); } @@ -1172,9 +1172,9 @@ void ImageReader::read() { constexpr bool shouldCompress = false; #endif #ifdef USE_GLES - image::BackendTarget target = image::BackendTarget::GLES32; + gpu::BackendTarget target = gpu::BackendTarget::GLES32; #else - image::BackendTarget target = image::BackendTarget::GL45; + gpu::BackendTarget target = gpu::BackendTarget::GL45; #endif texture = image::processImage(std::move(buffer), _url.toString().toStdString(), _maxNumPixels, networkTexture->getTextureType(), shouldCompress, target); From 2ba2c4b68a0b9f730a1d62ca9da98f02bdebd39b Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 8 Aug 2018 14:06:34 -0700 Subject: [PATCH 06/10] Fix TextureCache not compressing textures on Android --- .../src/model-networking/TextureCache.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 7f435838b6..b9def7f5b7 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -281,10 +281,12 @@ gpu::TexturePointer TextureCache::getImageTexture(const QString& path, image::Te auto loader = image::TextureUsage::getTextureLoaderForType(type, options); #ifdef USE_GLES gpu::BackendTarget target = gpu::BackendTarget::GLES32; + bool shouldCompress = true; #else gpu::BackendTarget target = gpu::BackendTarget::GL45; + bool shouldCompress = false; #endif - return gpu::TexturePointer(loader(std::move(image), path.toStdString(), false, target, false)); + return gpu::TexturePointer(loader(std::move(image), path.toStdString(), shouldCompress, target, false)); } QSharedPointer TextureCache::createResource(const QUrl& url, const QSharedPointer& fallback, @@ -1168,14 +1170,11 @@ void ImageReader::read() { #ifdef USE_GLES constexpr bool shouldCompress = true; + gpu::BackendTarget target = gpu::BackendTarget::GLES32; #else constexpr bool shouldCompress = false; -#endif - #ifdef USE_GLES - gpu::BackendTarget target = gpu::BackendTarget::GLES32; - #else gpu::BackendTarget target = gpu::BackendTarget::GL45; - #endif +#endif texture = image::processImage(std::move(buffer), _url.toString().toStdString(), _maxNumPixels, networkTexture->getTextureType(), shouldCompress, target); if (!texture) { From 44a9b1df0eb6be8f3d1cf72516ad46d4bce0d8b1 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 9 Aug 2018 12:30:42 -0700 Subject: [PATCH 07/10] Fix choice of target backend in TextureCache on OSX --- .../model-networking/src/model-networking/TextureCache.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index b9def7f5b7..aa00116200 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -1173,7 +1173,11 @@ void ImageReader::read() { gpu::BackendTarget target = gpu::BackendTarget::GLES32; #else constexpr bool shouldCompress = false; +#ifdef Q_OS_MAC + gpu::BackendTarget target = gpu::BackendTarget::GL41; +#else gpu::BackendTarget target = gpu::BackendTarget::GL45; +#endif #endif texture = image::processImage(std::move(buffer), _url.toString().toStdString(), _maxNumPixels, networkTexture->getTextureType(), shouldCompress, target); From a0dfd76f064dd5f53d8c9bf2aa387755cfa03f32 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Mon, 6 Aug 2018 15:13:00 -0700 Subject: [PATCH 08/10] variable texture support --- .../src/gpu/gles/GLESBackendTexture.cpp | 157 +++--------------- 1 file changed, 23 insertions(+), 134 deletions(-) diff --git a/libraries/gpu-gles/src/gpu/gles/GLESBackendTexture.cpp b/libraries/gpu-gles/src/gpu/gles/GLESBackendTexture.cpp index 911dfb8bb8..23dc271af9 100644 --- a/libraries/gpu-gles/src/gpu/gles/GLESBackendTexture.cpp +++ b/libraries/gpu-gles/src/gpu/gles/GLESBackendTexture.cpp @@ -65,20 +65,24 @@ GLTexture* GLESBackend::syncGPUObject(const TexturePointer& texturePointer) { object = new GLESAttachmentTexture(shared_from_this(), texture); break; - case TextureUsageType::RESOURCE: -// FIXME disabling variable allocation textures for now, while debugging android rendering -// and crashes -#if 0 - qCDebug(gpugllogging) << "variable / Strict texture " << texture.source().c_str(); - object = new GLESResourceTexture(shared_from_this(), texture); - GLVariableAllocationSupport::addMemoryManagedTexture(texturePointer); - break; -#endif case TextureUsageType::STRICT_RESOURCE: qCDebug(gpugllogging) << "Strict texture " << texture.source().c_str(); object = new GLESStrictResourceTexture(shared_from_this(), texture); break; + case TextureUsageType::RESOURCE: { + auto &transferEngine = _textureManagement._transferEngine; + if (transferEngine->allowCreate()) { + object = new GLESResourceTexture(shared_from_this(), texture); + transferEngine->addMemoryManagedTexture(texturePointer); + } else { + auto fallback = texturePointer->getFallbackTexture(); + if (fallback) { + object = static_cast(syncGPUObject(fallback)); + } + } + break; + } default: Q_UNREACHABLE(); } @@ -195,7 +199,6 @@ Size GLESTexture::copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const glTexSubImage2D(target, mip, 0, yOffset, size.x, size.y, format, type, sourcePointer); } } else { - // TODO: implement for android assert(false); amountCopied = 0; } @@ -385,7 +388,6 @@ void GLESVariableAllocationTexture::allocateStorage(uint16 allocatedMip) { const auto totalMips = _gpuObject.getNumMips(); const auto mips = totalMips - _allocatedMip; withPreservedTexture([&] { - // FIXME technically GL 4.2, but OSX includes the ARB_texture_storage extension glTexStorage2D(_target, mips, texelFormat.internalFormat, dimensions.x, dimensions.y); CHECK_GL_ERROR(); }); auto mipLevels = _gpuObject.getNumMips(); @@ -426,139 +428,26 @@ void GLESVariableAllocationTexture::syncSampler() const { }); } - -void copyUncompressedTexGPUMem(const gpu::Texture& texture, GLenum texTarget, GLuint srcId, GLuint destId, uint16_t numMips, uint16_t srcMipOffset, uint16_t destMipOffset, uint16_t populatedMips) { - // DestID must be bound to the GLESBackend::RESOURCE_TRANSFER_TEX_UNIT - - GLuint fbo { 0 }; - glGenFramebuffers(1, &fbo); - glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo); - - uint16_t mips = numMips; - // copy pre-existing mips - for (uint16_t mip = populatedMips; mip < mips; ++mip) { +void copyTexGPUMem(const gpu::Texture& texture, GLenum texTarget, GLuint srcId, GLuint destId, uint16_t numMips, uint16_t srcMipOffset, uint16_t destMipOffset, uint16_t populatedMips) { + for (uint16_t mip = populatedMips; mip < numMips; ++mip) { auto mipDimensions = texture.evalMipDimensions(mip); uint16_t targetMip = mip - destMipOffset; uint16_t sourceMip = mip - srcMipOffset; - for (GLenum target : GLTexture::getFaceTargets(texTarget)) { - glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, srcId, sourceMip); - (void)CHECK_GL_ERROR(); - glCopyTexSubImage2D(target, targetMip, 0, 0, 0, 0, mipDimensions.x, mipDimensions.y); + auto faces = GLTexture::getFaceCount(texTarget); + for (uint8_t face = 0; face < faces; ++face) { + glCopyImageSubData( + srcId, texTarget, sourceMip, 0, 0, face, + destId, texTarget, targetMip, 0, 0, face, + mipDimensions.x, mipDimensions.y, 1 + ); (void)CHECK_GL_ERROR(); } } - - // destroy the transfer framebuffer - glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); - glDeleteFramebuffers(1, &fbo); -} - -void copyCompressedTexGPUMem(const gpu::Texture& texture, GLenum texTarget, GLuint srcId, GLuint destId, uint16_t numMips, uint16_t srcMipOffset, uint16_t destMipOffset, uint16_t populatedMips) { - // DestID must be bound to the GLESBackend::RESOURCE_TRANSFER_TEX_UNIT - - struct MipDesc { - GLint _faceSize; - GLint _size; - GLint _offset; - GLint _width; - GLint _height; - }; - std::vector sourceMips(numMips); - - std::vector bytes; - - glActiveTexture(GL_TEXTURE0 + GLESBackend::RESOURCE_TRANSFER_EXTRA_TEX_UNIT); - glBindTexture(texTarget, srcId); - const auto& faceTargets = GLTexture::getFaceTargets(texTarget); - GLint internalFormat { 0 }; - - // Collect the mip description from the source texture - GLint bufferOffset { 0 }; - for (uint16_t mip = populatedMips; mip < numMips; ++mip) { - auto& sourceMip = sourceMips[mip]; - - uint16_t sourceLevel = mip - srcMipOffset; - - // Grab internal format once - if (internalFormat == 0) { - glGetTexLevelParameteriv(faceTargets[0], sourceLevel, GL_TEXTURE_INTERNAL_FORMAT, &internalFormat); - } - - // Collect the size of the first face, and then compute the total size offset needed for this mip level - auto mipDimensions = texture.evalMipDimensions(mip); - sourceMip._width = mipDimensions.x; - sourceMip._height = mipDimensions.y; -#ifdef DEBUG_COPY - glGetTexLevelParameteriv(faceTargets.front(), sourceLevel, GL_TEXTURE_WIDTH, &sourceMip._width); - glGetTexLevelParameteriv(faceTargets.front(), sourceLevel, GL_TEXTURE_HEIGHT, &sourceMip._height); -#endif - // TODO: retrieve the size of a compressed image - assert(false); - //glGetTexLevelParameteriv(faceTargets.front(), sourceLevel, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &sourceMip._faceSize); - sourceMip._size = (GLint)faceTargets.size() * sourceMip._faceSize; - sourceMip._offset = bufferOffset; - bufferOffset += sourceMip._size; - } - (void)CHECK_GL_ERROR(); - - // Allocate the PBO to accomodate for all the mips to copy - GLuint pbo { 0 }; - glGenBuffers(1, &pbo); - glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo); - glBufferData(GL_PIXEL_PACK_BUFFER, bufferOffset, nullptr, GL_STATIC_COPY); - (void)CHECK_GL_ERROR(); - - // Transfer from source texture to pbo - for (uint16_t mip = populatedMips; mip < numMips; ++mip) { - auto& sourceMip = sourceMips[mip]; - - uint16_t sourceLevel = mip - srcMipOffset; - - for (GLint f = 0; f < (GLint)faceTargets.size(); f++) { - // TODO: implement for android - //glGetCompressedTexImage(faceTargets[f], sourceLevel, BUFFER_OFFSET(sourceMip._offset + f * sourceMip._faceSize)); - } - (void)CHECK_GL_ERROR(); - } - - // Now populate the new texture from the pbo - glBindTexture(texTarget, 0); - glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo); - - glActiveTexture(GL_TEXTURE0 + GLESBackend::RESOURCE_TRANSFER_TEX_UNIT); - - // Transfer from pbo to new texture - for (uint16_t mip = populatedMips; mip < numMips; ++mip) { - auto& sourceMip = sourceMips[mip]; - - uint16_t destLevel = mip - destMipOffset; - - for (GLint f = 0; f < (GLint)faceTargets.size(); f++) { -#ifdef DEBUG_COPY - GLint destWidth, destHeight, destSize; - glGetTexLevelParameteriv(faceTargets.front(), destLevel, GL_TEXTURE_WIDTH, &destWidth); - glGetTexLevelParameteriv(faceTargets.front(), destLevel, GL_TEXTURE_HEIGHT, &destHeight); - glGetTexLevelParameteriv(faceTargets.front(), destLevel, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &destSize); -#endif - glCompressedTexSubImage2D(faceTargets[f], destLevel, 0, 0, sourceMip._width, sourceMip._height, internalFormat, - sourceMip._faceSize, BUFFER_OFFSET(sourceMip._offset + f * sourceMip._faceSize)); - } - } - - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); - glDeleteBuffers(1, &pbo); } void GLESVariableAllocationTexture::copyTextureMipsInGPUMem(GLuint srcId, GLuint destId, uint16_t srcMipOffset, uint16_t destMipOffset, uint16_t populatedMips) { uint16_t numMips = _gpuObject.getNumMips(); - withPreservedTexture([&] { - if (_texelFormat.isCompressed()) { - copyCompressedTexGPUMem(_gpuObject, _target, srcId, destId, numMips, srcMipOffset, destMipOffset, populatedMips); - } else { - copyUncompressedTexGPUMem(_gpuObject, _target, srcId, destId, numMips, srcMipOffset, destMipOffset, populatedMips); - } - }); + copyTexGPUMem(_gpuObject, _target, srcId, destId, numMips, srcMipOffset, destMipOffset, populatedMips); } size_t GLESVariableAllocationTexture::promote() { From 9fadf58e7b561f555dda08e03407990a33a0cf1a Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 10 Aug 2018 13:56:48 -0700 Subject: [PATCH 09/10] Add threads lib to image lib --- libraries/image/CMakeLists.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libraries/image/CMakeLists.txt b/libraries/image/CMakeLists.txt index 6bc5c762f5..4db39f2152 100644 --- a/libraries/image/CMakeLists.txt +++ b/libraries/image/CMakeLists.txt @@ -3,3 +3,9 @@ setup_hifi_library() link_hifi_libraries(shared gpu) target_nvtt() target_etc2comp() + +if (UNIX AND NOT APPLE) + set(THREADS_PREFER_PTHREAD_FLAG ON) + find_package(Threads REQUIRED) + target_link_libraries(image Threads::Threads) +endif() From 0fcf1517a543e19446882ca42b4d2427c04198b8 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 10 Aug 2018 14:57:00 -0700 Subject: [PATCH 10/10] Fix TextureCache not correctly choosing gl41 when gl45 is disabled --- libraries/model-networking/CMakeLists.txt | 2 +- .../src/model-networking/TextureCache.cpp | 31 +++++++++++++------ 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/libraries/model-networking/CMakeLists.txt b/libraries/model-networking/CMakeLists.txt index 696f4feb9a..9a4bc780a6 100644 --- a/libraries/model-networking/CMakeLists.txt +++ b/libraries/model-networking/CMakeLists.txt @@ -1,4 +1,4 @@ set(TARGET_NAME model-networking) setup_hifi_library() -link_hifi_libraries(shared networking graphics fbx ktx image) +link_hifi_libraries(shared networking graphics fbx ktx image gl) include_hifi_library_headers(gpu) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index aa00116200..e8aec5e60e 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -31,6 +31,7 @@ #include #include +#include #include #include @@ -271,6 +272,20 @@ gpu::TexturePointer getFallbackTextureForType(image::TextureUsage::Type type) { return result; } +gpu::BackendTarget getBackendTarget() { +#if defined(USE_GLES) + gpu::BackendTarget target = gpu::BackendTarget::GLES32; +#elif defined(Q_OS_MAC) + gpu::BackendTarget target = gpu::BackendTarget::GL41; +#else + gpu::BackendTarget target = gpu::BackendTarget::GL45; + if (gl::disableGl45()) { + target = gpu::BackendTarget::GL41; + } +#endif + return target; +} + /// Returns a texture version of an image file gpu::TexturePointer TextureCache::getImageTexture(const QString& path, image::TextureUsage::Type type, QVariantMap options) { QImage image = QImage(path); @@ -279,13 +294,14 @@ gpu::TexturePointer TextureCache::getImageTexture(const QString& path, image::Te return nullptr; } auto loader = image::TextureUsage::getTextureLoaderForType(type, options); + #ifdef USE_GLES - gpu::BackendTarget target = gpu::BackendTarget::GLES32; - bool shouldCompress = true; + constexpr bool shouldCompress = true; #else - gpu::BackendTarget target = gpu::BackendTarget::GL45; - bool shouldCompress = false; + constexpr bool shouldCompress = false; #endif + auto target = getBackendTarget(); + return gpu::TexturePointer(loader(std::move(image), path.toStdString(), shouldCompress, target, false)); } @@ -1170,15 +1186,10 @@ void ImageReader::read() { #ifdef USE_GLES constexpr bool shouldCompress = true; - gpu::BackendTarget target = gpu::BackendTarget::GLES32; #else constexpr bool shouldCompress = false; -#ifdef Q_OS_MAC - gpu::BackendTarget target = gpu::BackendTarget::GL41; -#else - gpu::BackendTarget target = gpu::BackendTarget::GL45; -#endif #endif + auto target = getBackendTarget(); texture = image::processImage(std::move(buffer), _url.toString().toStdString(), _maxNumPixels, networkTexture->getTextureType(), shouldCompress, target); if (!texture) {