From 30cf1555e414d43e0a7ccd58b973e8da528db483 Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Sat, 20 Feb 2021 22:47:33 -0800 Subject: [PATCH 1/5] fix keepAspectRatio, give text entities naturalDimensions --- .../src/RenderableImageEntityItem.cpp | 35 +++++++++++++++---- libraries/entities/src/ImageEntityItem.cpp | 9 ++++- libraries/entities/src/ImageEntityItem.h | 4 +++ 3 files changed, 40 insertions(+), 8 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableImageEntityItem.cpp b/libraries/entities-renderer/src/RenderableImageEntityItem.cpp index e03655f09c..dd6381953f 100644 --- a/libraries/entities-renderer/src/RenderableImageEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableImageEntityItem.cpp @@ -59,10 +59,22 @@ void ImageEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPoint _alpha = entity->getAlpha(); _pulseProperties = entity->getPulseProperties(); + bool nextTextureLoaded = _texture && (_texture->isLoaded() || _texture->isFailed()); if (!_textureIsLoaded) { emit requestRenderUpdate(); + if (nextTextureLoaded) { + float width = _texture->getWidth(); + float height = _texture->getHeight(); + glm::vec3 naturalDimensions = glm::vec3(1.0f, 1.0f, 0.01f); + if (width < height) { + naturalDimensions.x = width / height; + } else { + naturalDimensions.y = height / width; + } + entity->setNaturalDimension(naturalDimensions); + } } - _textureIsLoaded = _texture && (_texture->isLoaded() || _texture->isFailed()); + _textureIsLoaded = nextTextureLoaded; } ShapeKey ImageEntityRenderer::getShapeKey() { @@ -100,7 +112,6 @@ void ImageEntityRenderer::doRender(RenderArgs* args) { transform.setRotation(BillboardModeHelpers::getBillboardRotation(transform.getTranslation(), transform.getRotation(), _billboardMode, args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition())); - batch->setModelTransform(transform); batch->setResourceTexture(0, _texture->getGPUTexture()); float imageWidth = _texture->getWidth(); @@ -125,15 +136,25 @@ void ImageEntityRenderer::doRender(RenderArgs* args) { fromImage.setHeight(scaleY * _subImage.height()); } - float maxSize = glm::max(fromImage.width(), fromImage.height()); - float x = _keepAspectRatio ? fromImage.width() / (2.0f * maxSize) : 0.5f; - float y = _keepAspectRatio ? fromImage.height() / (2.0f * maxSize) : 0.5f; - glm::vec2 texCoordBottomLeft((fromImage.x() + 0.5f) / imageWidth, (fromImage.y() + fromImage.height() - 0.5f) / imageHeight); glm::vec2 texCoordTopRight((fromImage.x() + fromImage.width() - 0.5f) / imageWidth, (fromImage.y() + 0.5f) / imageHeight); + if (_keepAspectRatio) { + glm::vec3 scale = transform.getScale(); + float targetAspectRatio = imageWidth / imageHeight; + float currentAspectRatio = scale.x / scale.y; + + if (targetAspectRatio < currentAspectRatio) { + scale.x *= targetAspectRatio / currentAspectRatio; + } else { + scale.y /= targetAspectRatio / currentAspectRatio; + } + transform.setScale(scale); + } + batch->setModelTransform(transform); + DependencyManager::get()->renderQuad( - *batch, glm::vec2(-x, -y), glm::vec2(x, y), texCoordBottomLeft, texCoordTopRight, + *batch, glm::vec2(-0.5f), glm::vec2(0.5f), texCoordBottomLeft, texCoordTopRight, color, _geometryId ); diff --git a/libraries/entities/src/ImageEntityItem.cpp b/libraries/entities/src/ImageEntityItem.cpp index afa6d9ae69..41cdde7676 100644 --- a/libraries/entities/src/ImageEntityItem.cpp +++ b/libraries/entities/src/ImageEntityItem.cpp @@ -34,6 +34,7 @@ EntityItemProperties ImageEntityItem::getProperties(const EntityPropertyFlags& d COPY_ENTITY_PROPERTY_TO_PROPERTIES(alpha, getAlpha); withReadLock([&] { _pulseProperties.getProperties(properties); + properties.setNaturalDimensions(_naturalDimensions); }); COPY_ENTITY_PROPERTY_TO_PROPERTIES(imageURL, getImageURL); @@ -217,4 +218,10 @@ PulsePropertyGroup ImageEntityItem::getPulseProperties() const { return resultWithReadLock([&] { return _pulseProperties; }); -} \ No newline at end of file +} + +void ImageEntityItem::setNaturalDimension(const glm::vec3& naturalDimensions) const { + withWriteLock([&] { + _naturalDimensions = naturalDimensions; + }); +} diff --git a/libraries/entities/src/ImageEntityItem.h b/libraries/entities/src/ImageEntityItem.h index 4f7aac0c13..0cc4eae05a 100644 --- a/libraries/entities/src/ImageEntityItem.h +++ b/libraries/entities/src/ImageEntityItem.h @@ -63,6 +63,8 @@ public: PulsePropertyGroup getPulseProperties() const; + void setNaturalDimension(const glm::vec3& naturalDimensions) const; + protected: glm::u8vec3 _color; float _alpha; @@ -72,6 +74,8 @@ protected: bool _emissive { false }; bool _keepAspectRatio { true }; QRect _subImage; + + mutable glm::vec3 _naturalDimensions; }; #endif // hifi_ImageEntityItem_h From 5677b5d5dcf7c79c3a4b157d818838ad7d2c49b1 Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Sat, 20 Feb 2021 23:03:30 -0800 Subject: [PATCH 2/5] docs --- libraries/entities/src/EntityItemProperties.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 5f925c4b19..44a9019197 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -706,7 +706,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * * @property {Vec3} naturalPosition=0,0,0 - The center of the entity's unscaled mesh model if it has one, otherwise * {@link Vec3(0)|Vec3.ZERO}. Read-only. - * @property {Vec3} naturalDimensions - The dimensions of the entity's unscaled mesh model if it has one, otherwise + * @property {Vec3} naturalDimensions - The dimensions of the entity's unscaled mesh model or image if it has one, otherwise * {@link Vec3(0)|Vec3.ONE}. Read-only. * * @property {Vec3} velocity=0,0,0 - The linear velocity of the entity in m/s with respect to world coordinates. From 35e836d34d481a32728fd0a53b143b88c4b8d768 Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Fri, 19 Mar 2021 22:32:29 -0700 Subject: [PATCH 3/5] image caching must maintain original image information and pass it around --- libraries/baking/src/TextureBaker.cpp | 22 ++-- .../src/RenderableImageEntityItem.cpp | 12 +- libraries/gpu/src/gpu/Texture.h | 8 +- libraries/gpu/src/gpu/Texture_ktx.cpp | 64 ++++----- .../image/src/image/TextureProcessing.cpp | 10 +- libraries/image/src/image/TextureProcessing.h | 6 +- .../src/material-networking/TextureCache.cpp | 121 +++++++++--------- .../src/material-networking/TextureCache.h | 6 +- 8 files changed, 128 insertions(+), 121 deletions(-) diff --git a/libraries/baking/src/TextureBaker.cpp b/libraries/baking/src/TextureBaker.cpp index d70c6586b7..d9b63d6b7a 100644 --- a/libraries/baking/src/TextureBaker.cpp +++ b/libraries/baking/src/TextureBaker.cpp @@ -168,20 +168,20 @@ void TextureBaker::processTexture() { gpu::BackendTarget::GLES32 }}; for (auto target : BACKEND_TARGETS) { - auto processedTexture = image::processImage(buffer, _textureURL.toString().toStdString(), image::ColorChannel::NONE, - ABSOLUTE_MAX_TEXTURE_NUM_PIXELS, _textureType, true, - target, _abortProcessing); - if (!processedTexture) { + auto processedTextureAndSize = image::processImage(buffer, _textureURL.toString().toStdString(), image::ColorChannel::NONE, + ABSOLUTE_MAX_TEXTURE_NUM_PIXELS, _textureType, true, + target, _abortProcessing); + if (!processedTextureAndSize.first) { handleError("Could not process texture " + _textureURL.toString()); return; } - processedTexture->setSourceHash(hash); + processedTextureAndSize.first->setSourceHash(hash); if (shouldStop()) { return; } - auto memKTX = gpu::Texture::serialize(*processedTexture); + auto memKTX = gpu::Texture::serialize(*processedTextureAndSize.first, processedTextureAndSize.second); if (!memKTX) { handleError("Could not serialize " + _textureURL.toString() + " to KTX"); return; @@ -211,19 +211,19 @@ void TextureBaker::processTexture() { // Uncompressed KTX if (_textureType == image::TextureUsage::Type::SKY_TEXTURE || _textureType == image::TextureUsage::Type::AMBIENT_TEXTURE) { buffer->reset(); - auto processedTexture = image::processImage(std::move(buffer), _textureURL.toString().toStdString(), image::ColorChannel::NONE, - ABSOLUTE_MAX_TEXTURE_NUM_PIXELS, _textureType, false, gpu::BackendTarget::GL45, _abortProcessing); - if (!processedTexture) { + auto processedTextureAndSize = image::processImage(std::move(buffer), _textureURL.toString().toStdString(), image::ColorChannel::NONE, + ABSOLUTE_MAX_TEXTURE_NUM_PIXELS, _textureType, false, gpu::BackendTarget::GL45, _abortProcessing); + if (!processedTextureAndSize.first) { handleError("Could not process texture " + _textureURL.toString()); return; } - processedTexture->setSourceHash(hash); + processedTextureAndSize.first->setSourceHash(hash); if (shouldStop()) { return; } - auto memKTX = gpu::Texture::serialize(*processedTexture); + auto memKTX = gpu::Texture::serialize(*processedTextureAndSize.first, processedTextureAndSize.second); if (!memKTX) { handleError("Could not serialize " + _textureURL.toString() + " to KTX"); return; diff --git a/libraries/entities-renderer/src/RenderableImageEntityItem.cpp b/libraries/entities-renderer/src/RenderableImageEntityItem.cpp index dd6381953f..4d372b17e5 100644 --- a/libraries/entities-renderer/src/RenderableImageEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableImageEntityItem.cpp @@ -63,8 +63,8 @@ void ImageEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPoint if (!_textureIsLoaded) { emit requestRenderUpdate(); if (nextTextureLoaded) { - float width = _texture->getWidth(); - float height = _texture->getHeight(); + float width = _texture->getOriginalWidth(); + float height = _texture->getOriginalHeight(); glm::vec3 naturalDimensions = glm::vec3(1.0f, 1.0f, 0.01f); if (width < height) { naturalDimensions.x = width / height; @@ -116,13 +116,15 @@ void ImageEntityRenderer::doRender(RenderArgs* args) { float imageWidth = _texture->getWidth(); float imageHeight = _texture->getHeight(); + float originalWidth = _texture->getOriginalWidth(); + float originalHeight = _texture->getOriginalHeight(); QRect fromImage; if (_subImage.width() <= 0) { fromImage.setX(0); fromImage.setWidth(imageWidth); } else { - float scaleX = imageWidth / _texture->getOriginalWidth(); + float scaleX = imageWidth / originalWidth; fromImage.setX(scaleX * _subImage.x()); fromImage.setWidth(scaleX * _subImage.width()); } @@ -131,7 +133,7 @@ void ImageEntityRenderer::doRender(RenderArgs* args) { fromImage.setY(0); fromImage.setHeight(imageHeight); } else { - float scaleY = imageHeight / _texture->getOriginalHeight(); + float scaleY = imageHeight / originalHeight; fromImage.setY(scaleY * _subImage.y()); fromImage.setHeight(scaleY * _subImage.height()); } @@ -141,7 +143,7 @@ void ImageEntityRenderer::doRender(RenderArgs* args) { if (_keepAspectRatio) { glm::vec3 scale = transform.getScale(); - float targetAspectRatio = imageWidth / imageHeight; + float targetAspectRatio = originalWidth / originalHeight; float currentAspectRatio = scale.x / scale.y; if (targetAspectRatio < currentAspectRatio) { diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index 54c7d49421..a525cda1ab 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -579,11 +579,11 @@ public: ExternalUpdates getUpdates() const; // Serialize a texture into a KTX file - static ktx::KTXUniquePointer serialize(const Texture& texture); + static ktx::KTXUniquePointer serialize(const Texture& texture, const glm::ivec2& originalSize); - static TexturePointer build(const ktx::KTXDescriptor& descriptor); - static TexturePointer unserialize(const std::string& ktxFile); - static TexturePointer unserialize(const cache::FilePointer& cacheEntry, const std::string& source = std::string()); + static std::pair build(const ktx::KTXDescriptor& descriptor); + static std::pair unserialize(const std::string& ktxFile); + static std::pair unserialize(const cache::FilePointer& cacheEntry, const std::string& source = std::string()); static bool evalKTXFormat(const Element& mipFormat, const Element& texelFormat, ktx::Header& header); static bool evalTextureFormat(const ktx::Header& header, Element& mipFormat, Element& texelFormat); diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index a5cea3e60e..9f9955c5a8 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -13,6 +13,7 @@ #include "Texture.h" #include +#include #include @@ -30,15 +31,16 @@ struct GPUKTXPayload { using Version = uint8; static const std::string KEY; - static const Version CURRENT_VERSION { 1 }; + static const Version CURRENT_VERSION { 2 }; static const size_t PADDING { 2 }; - static const size_t SIZE { sizeof(Version) + sizeof(Sampler::Desc) + sizeof(uint32) + sizeof(TextureUsageType) + PADDING }; - static_assert(GPUKTXPayload::SIZE == 36, "Packing size may differ between platforms"); + static const size_t SIZE { sizeof(Version) + sizeof(Sampler::Desc) + sizeof(uint32) + sizeof(TextureUsageType) + sizeof(glm::ivec2) + PADDING }; + static_assert(GPUKTXPayload::SIZE == 44, "Packing size may differ between platforms"); static_assert(GPUKTXPayload::SIZE % 4 == 0, "GPUKTXPayload is not 4 bytes aligned"); Sampler::Desc _samplerDesc; Texture::Usage _usage; TextureUsageType _usageType; + glm::ivec2 _originalSize; Byte* serialize(Byte* data) const { *(Version*)data = CURRENT_VERSION; @@ -56,6 +58,9 @@ struct GPUKTXPayload { memcpy(data, &_usageType, sizeof(TextureUsageType)); data += sizeof(TextureUsageType); + memcpy(data, glm::value_ptr(_originalSize), sizeof(glm::ivec2)); + data += sizeof(glm::ivec2); + return data + PADDING; } @@ -66,13 +71,7 @@ struct GPUKTXPayload { Version version = *(const Version*)data; if (version != CURRENT_VERSION) { - glm::vec4 borderColor(1.0f); - if (memcmp(&borderColor, data, sizeof(glm::vec4)) == 0) { - memcpy(this, data, sizeof(GPUKTXPayload)); - return true; - } else { - return false; - } + return false; } data += sizeof(Version); @@ -87,6 +86,10 @@ struct GPUKTXPayload { data += sizeof(uint32); memcpy(&_usageType, data, sizeof(TextureUsageType)); + data += sizeof(TextureUsageType); + + memcpy(&_originalSize, data, sizeof(glm::ivec2)); + data += sizeof(glm::ivec2); return true; } @@ -382,7 +385,7 @@ void Texture::setKtxBacking(const cache::FilePointer& cacheEntry) { } -ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { +ktx::KTXUniquePointer Texture::serialize(const Texture& texture, const glm::ivec2& originalSize) { ktx::Header header; // From texture format to ktx format description @@ -459,6 +462,7 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { gpuKeyval._samplerDesc = texture.getSampler().getDesc(); gpuKeyval._usage = texture.getUsage(); gpuKeyval._usageType = texture.getUsageType(); + gpuKeyval._originalSize = originalSize; Byte keyvalPayload[GPUKTXPayload::SIZE]; gpuKeyval.serialize(keyvalPayload); @@ -514,19 +518,19 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { return ktxBuffer; } -TexturePointer Texture::build(const ktx::KTXDescriptor& descriptor) { +std::pair Texture::build(const ktx::KTXDescriptor& descriptor) { Format mipFormat = Format::COLOR_BGRA_32; Format texelFormat = Format::COLOR_SRGBA_32; const auto& header = descriptor.header; if (!Texture::evalTextureFormat(header, mipFormat, texelFormat)) { - return nullptr; + return { nullptr, { 0, 0 } }; } // Find Texture Type based on dimensions Type type = TEX_1D; if (header.pixelWidth == 0) { - return nullptr; + return { nullptr, { 0, 0 } }; } else if (header.pixelHeight == 0) { type = TEX_1D; } else if (header.pixelDepth == 0) { @@ -569,39 +573,39 @@ TexturePointer Texture::build(const ktx::KTXDescriptor& descriptor) { texture->overrideIrradiance(std::make_shared(irradianceKtxKeyValue._irradianceSH)); } - return texture; + return { texture, gpuktxKeyValue._originalSize }; } -TexturePointer Texture::unserialize(const cache::FilePointer& cacheEntry, const std::string& source) { +std::pair Texture::unserialize(const cache::FilePointer& cacheEntry, const std::string& source) { std::unique_ptr ktxPointer = ktx::KTX::create(std::make_shared(cacheEntry->getFilepath().c_str())); if (!ktxPointer) { - return nullptr; + return { nullptr, { 0, 0 } }; } - auto texture = build(ktxPointer->toDescriptor()); - if (texture) { - texture->setKtxBacking(cacheEntry); - if (texture->source().empty()) { - texture->setSource(source); + auto textureAndSize = build(ktxPointer->toDescriptor()); + if (textureAndSize.first) { + textureAndSize.first->setKtxBacking(cacheEntry); + if (textureAndSize.first->source().empty()) { + textureAndSize.first->setSource(source); } } - return texture; + return { textureAndSize.first, textureAndSize.second }; } -TexturePointer Texture::unserialize(const std::string& ktxfile) { +std::pair Texture::unserialize(const std::string& ktxfile) { std::unique_ptr ktxPointer = ktx::KTX::create(std::make_shared(ktxfile.c_str())); if (!ktxPointer) { - return nullptr; + return { nullptr, { 0, 0 } }; } - auto texture = build(ktxPointer->toDescriptor()); - if (texture) { - texture->setKtxBacking(ktxfile); - texture->setSource(ktxfile); + auto textureAndSize = build(ktxPointer->toDescriptor()); + if (textureAndSize.first) { + textureAndSize.first->setKtxBacking(ktxfile); + textureAndSize.first->setSource(ktxfile); } - return texture; + return { textureAndSize.first, textureAndSize.second }; } bool Texture::evalKTXFormat(const Element& mipFormat, const Element& texelFormat, ktx::Header& header) { diff --git a/libraries/image/src/image/TextureProcessing.cpp b/libraries/image/src/image/TextureProcessing.cpp index 53991ae431..1b45fc1bae 100644 --- a/libraries/image/src/image/TextureProcessing.cpp +++ b/libraries/image/src/image/TextureProcessing.cpp @@ -338,9 +338,9 @@ void mapToRedChannel(Image& image, ColorChannel sourceChannel) { } } -gpu::TexturePointer processImage(std::shared_ptr content, const std::string& filename, ColorChannel sourceChannel, - int maxNumPixels, TextureUsage::Type textureType, - bool compress, BackendTarget target, const std::atomic& abortProcessing) { +std::pair processImage(std::shared_ptr content, const std::string& filename, ColorChannel sourceChannel, + int maxNumPixels, TextureUsage::Type textureType, + bool compress, BackendTarget target, const std::atomic& abortProcessing) { Image image = processRawImageData(*content.get(), filename); // Texture content can take up a lot of memory. Here we release our ownership of that content @@ -354,7 +354,7 @@ gpu::TexturePointer processImage(std::shared_ptr content, const std:: if (imageWidth == 0 || imageHeight == 0 || image.getFormat() == Image::Format_Invalid) { QString reason(image.getFormat() == Image::Format_Invalid ? "(Invalid Format)" : "(Size is invalid)"); qCWarning(imagelogging) << "Failed to load:" << qPrintable(reason); - return nullptr; + return { nullptr, { imageWidth, imageHeight } }; } // Validate the image is less than _maxNumPixels, and downscale if necessary @@ -378,7 +378,7 @@ gpu::TexturePointer processImage(std::shared_ptr content, const std:: auto loader = TextureUsage::getTextureLoaderForType(textureType); auto texture = loader(std::move(image), filename, compress, target, abortProcessing); - return texture; + return { texture, { imageWidth, imageHeight } }; } Image processSourceImage(Image&& srcImage, bool cubemap, BackendTarget target) { diff --git a/libraries/image/src/image/TextureProcessing.h b/libraries/image/src/image/TextureProcessing.h index 326ec700a9..decb940fda 100644 --- a/libraries/image/src/image/TextureProcessing.h +++ b/libraries/image/src/image/TextureProcessing.h @@ -121,9 +121,9 @@ gpu::TexturePointer processCubeTextureColorFromImage(Image&& srcImage, const std const QStringList getSupportedFormats(); -gpu::TexturePointer processImage(std::shared_ptr content, const std::string& url, ColorChannel sourceChannel, - int maxNumPixels, TextureUsage::Type textureType, - bool compress, gpu::BackendTarget target, const std::atomic& abortProcessing = false); +std::pair processImage(std::shared_ptr content, const std::string& url, ColorChannel sourceChannel, + int maxNumPixels, TextureUsage::Type textureType, + bool compress, gpu::BackendTarget target, const std::atomic& abortProcessing = false); void convertToTextureWithMips(gpu::Texture* texture, Image&& image, gpu::BackendTarget target, const std::atomic& abortProcessing = false, int face = -1); void convertToTexture(gpu::Texture* texture, Image&& image, gpu::BackendTarget target, const std::atomic& abortProcessing = false, int face = -1, int mipLevel = 0); diff --git a/libraries/material-networking/src/material-networking/TextureCache.cpp b/libraries/material-networking/src/material-networking/TextureCache.cpp index 64c3abb0d8..e4e36cad48 100644 --- a/libraries/material-networking/src/material-networking/TextureCache.cpp +++ b/libraries/material-networking/src/material-networking/TextureCache.cpp @@ -266,23 +266,24 @@ NetworkTexturePointer TextureCache::getTexture(const QUrl& url, image::TextureUs return ResourceCache::getResource(modifiedUrl, QUrl(), &extra, std::hash()(extra)).staticCast(); } -gpu::TexturePointer TextureCache::getTextureByHash(const std::string& hash) { - std::weak_ptr weakPointer; +std::pair TextureCache::getTextureByHash(const std::string& hash) { + std::pair weakPointer; { std::unique_lock lock(_texturesByHashesMutex); weakPointer = _texturesByHashes[hash]; } - return weakPointer.lock(); + return { weakPointer.first.lock(), weakPointer.second }; } -gpu::TexturePointer TextureCache::cacheTextureByHash(const std::string& hash, const gpu::TexturePointer& texture) { - gpu::TexturePointer result; +std::pair TextureCache::cacheTextureByHash(const std::string& hash, const std::pair& textureAndSize) { + std::pair result; { std::unique_lock lock(_texturesByHashesMutex); - result = _texturesByHashes[hash].lock(); - if (!result) { - _texturesByHashes[hash] = texture; - result = texture; + auto& value = _texturesByHashes[hash]; + result = { value.first.lock(), value.second }; + if (!result.first) { + _texturesByHashes[hash] = textureAndSize; + result = textureAndSize; } } return result; @@ -616,7 +617,7 @@ void NetworkTexture::makeLocalRequest() { ktxDescriptor = std::make_shared(ktxFile->toDescriptor()); } - gpu::TexturePointer texture; + std::pair textureAndSize; if (ktxDescriptor) { std::string hash; // Create bare ktx in memory @@ -634,18 +635,18 @@ void NetworkTexture::makeLocalRequest() { } auto textureCache = DependencyManager::get(); - texture = textureCache->getTextureByHash(hash); - if (!texture) { - texture = gpu::Texture::build(*ktxDescriptor); - if (texture) { - texture->setKtxBacking(path.toStdString()); - texture->setSource(path.toStdString()); - texture = textureCache->cacheTextureByHash(hash, texture); + textureAndSize = textureCache->getTextureByHash(hash); + if (!textureAndSize.first) { + textureAndSize = gpu::Texture::build(*ktxDescriptor); + if (textureAndSize.first) { + textureAndSize.first->setKtxBacking(path.toStdString()); + textureAndSize.first->setSource(path.toStdString()); + textureAndSize = textureCache->cacheTextureByHash(hash, textureAndSize); } } } - if (!texture) { + if (!textureAndSize.first) { qCDebug(networking).noquote() << "Failed load local KTX from" << path; QMetaObject::invokeMethod(this, "setImage", Q_ARG(gpu::TexturePointer, nullptr), @@ -655,11 +656,11 @@ void NetworkTexture::makeLocalRequest() { } _ktxResourceState = PENDING_MIP_REQUEST; - _lowestKnownPopulatedMip = texture->minAvailableMipLevel(); + _lowestKnownPopulatedMip = textureAndSize.first->minAvailableMipLevel(); QMetaObject::invokeMethod(this, "setImage", - Q_ARG(gpu::TexturePointer, texture), - Q_ARG(int, texture->getWidth()), - Q_ARG(int, texture->getHeight())); + Q_ARG(gpu::TexturePointer, textureAndSize.first), + Q_ARG(int, textureAndSize.second.x), + Q_ARG(int, textureAndSize.second.y)); } @@ -968,22 +969,22 @@ void NetworkTexture::handleFinishedInitialLoad() { auto textureCache = DependencyManager::get(); - gpu::TexturePointer texture = textureCache->getTextureByHash(hash); + std::pair textureAndSize = textureCache->getTextureByHash(hash); - if (!texture) { + if (!textureAndSize.first) { auto ktxFile = textureCache->_ktxCache->getFile(hash); if (ktxFile) { - texture = gpu::Texture::unserialize(ktxFile); - if (texture) { - texture = textureCache->cacheTextureByHash(hash, texture); - if (texture->source().empty()) { - texture->setSource(url.toString().toStdString()); + textureAndSize = gpu::Texture::unserialize(ktxFile); + if (textureAndSize.first) { + textureAndSize = textureCache->cacheTextureByHash(hash, textureAndSize); + if (textureAndSize.first->source().empty()) { + textureAndSize.first->setSource(url.toString().toStdString()); } } } } - if (!texture) { + if (!textureAndSize.first) { auto memKtx = ktx::KTX::createBare(*header, keyValues); if (!memKtx) { qWarning() << " Ktx could not be created, bailing"; @@ -1010,9 +1011,9 @@ void NetworkTexture::handleFinishedInitialLoad() { auto newKtxDescriptor = memKtx->toDescriptor(); - texture = gpu::Texture::build(newKtxDescriptor); - texture->setKtxBacking(file); - texture->setSource(filename); + textureAndSize = gpu::Texture::build(newKtxDescriptor); + textureAndSize.first->setKtxBacking(file); + textureAndSize.first->setSource(filename); auto& images = originalKtxDescriptor->images; size_t imageSizeRemaining = ktxHighMipData.size(); @@ -1025,7 +1026,7 @@ void NetworkTexture::handleFinishedInitialLoad() { break; } ktxData -= image._imageSize; - texture->assignStoredMip(static_cast(level), image._imageSize, ktxData); + textureAndSize.first->assignStoredMip(static_cast(level), image._imageSize, ktxData); ktxData -= ktx::IMAGE_SIZE_WIDTH; imageSizeRemaining -= (image._imageSize + ktx::IMAGE_SIZE_WIDTH); } @@ -1033,13 +1034,13 @@ void NetworkTexture::handleFinishedInitialLoad() { // We replace the texture with the one stored in the cache. This deals with the possible race condition of two different // images with the same hash being loaded concurrently. Only one of them will make it into the cache by hash first and will // be the winner - texture = textureCache->cacheTextureByHash(filename, texture); + textureAndSize = textureCache->cacheTextureByHash(filename, textureAndSize); } QMetaObject::invokeMethod(resource.data(), "setImage", - Q_ARG(gpu::TexturePointer, texture), - Q_ARG(int, texture->getWidth()), - Q_ARG(int, texture->getHeight())); + Q_ARG(gpu::TexturePointer, textureAndSize.first), + Q_ARG(int, textureAndSize.second.x), + Q_ARG(int, textureAndSize.second.y)); QMetaObject::invokeMethod(resource.data(), "startRequestForNextMipLevel"); }); @@ -1229,15 +1230,15 @@ void ImageReader::read() { auto textureCache = DependencyManager::get(); if (textureCache) { // If we already have a live texture with the same hash, use it - auto texture = textureCache->getTextureByHash(hash); + auto textureAndSize = textureCache->getTextureByHash(hash); // If there is no live texture, check if there's an existing KTX file - if (!texture) { + if (!textureAndSize.first) { auto ktxFile = textureCache->_ktxCache->getFile(hash); if (ktxFile) { - texture = gpu::Texture::unserialize(ktxFile, _url.toString().toStdString()); - if (texture) { - texture = textureCache->cacheTextureByHash(hash, texture); + textureAndSize = gpu::Texture::unserialize(ktxFile, _url.toString().toStdString()); + if (textureAndSize.first) { + textureAndSize = textureCache->cacheTextureByHash(hash, textureAndSize); } else { qCWarning(materialnetworking) << "Invalid cached KTX " << _url << " under hash " << hash.c_str() << ", recreating..."; } @@ -1246,17 +1247,17 @@ void ImageReader::read() { // If we found the texture either because it's in use or via KTX deserialization, // set the image and return immediately. - if (texture) { + if (textureAndSize.first) { QMetaObject::invokeMethod(resource.data(), "setImage", - Q_ARG(gpu::TexturePointer, texture), - Q_ARG(int, texture->getWidth()), - Q_ARG(int, texture->getHeight())); + Q_ARG(gpu::TexturePointer, textureAndSize.first), + Q_ARG(int, textureAndSize.second.x), + Q_ARG(int, textureAndSize.second.y)); return; } } // Proccess new texture - gpu::TexturePointer texture; + std::pair textureAndSize; { PROFILE_RANGE_EX(resource_parse_image_raw, __FUNCTION__, 0xffff0000, 0); @@ -1269,23 +1270,23 @@ void ImageReader::read() { constexpr bool shouldCompress = false; #endif auto target = getBackendTarget(); - texture = image::processImage(std::move(buffer), _url.toString().toStdString(), _sourceChannel, _maxNumPixels, networkTexture->getTextureType(), shouldCompress, target); + textureAndSize = image::processImage(std::move(buffer), _url.toString().toStdString(), _sourceChannel, _maxNumPixels, networkTexture->getTextureType(), shouldCompress, target); - if (!texture) { + if (!textureAndSize.first) { QMetaObject::invokeMethod(resource.data(), "setImage", - Q_ARG(gpu::TexturePointer, texture), + Q_ARG(gpu::TexturePointer, textureAndSize.first), Q_ARG(int, 0), Q_ARG(int, 0)); return; } - texture->setSourceHash(hash); - texture->setFallbackTexture(networkTexture->getFallbackTexture()); + textureAndSize.first->setSourceHash(hash); + textureAndSize.first->setFallbackTexture(networkTexture->getFallbackTexture()); } // Save the image into a KTXFile - if (texture && textureCache) { - auto memKtx = gpu::Texture::serialize(*texture); + if (textureAndSize.first && textureCache) { + auto memKtx = gpu::Texture::serialize(*textureAndSize.first, textureAndSize.second); // Move the texture into a memory mapped file if (memKtx) { @@ -1294,20 +1295,20 @@ void ImageReader::read() { auto& ktxCache = textureCache->_ktxCache; auto file = ktxCache->writeFile(data, KTXCache::Metadata(hash, length)); if (file) { - texture->setKtxBacking(file); + textureAndSize.first->setKtxBacking(file); } } // We replace the texture with the one stored in the cache. This deals with the possible race condition of two different // images with the same hash being loaded concurrently. Only one of them will make it into the cache by hash first and will // be the winner - texture = textureCache->cacheTextureByHash(hash, texture); + textureAndSize = textureCache->cacheTextureByHash(hash, textureAndSize); } QMetaObject::invokeMethod(resource.data(), "setImage", - Q_ARG(gpu::TexturePointer, texture), - Q_ARG(int, texture->getWidth()), - Q_ARG(int, texture->getHeight())); + Q_ARG(gpu::TexturePointer, textureAndSize.first), + Q_ARG(int, textureAndSize.second.x), + Q_ARG(int, textureAndSize.second.y)); } NetworkTexturePointer TextureCache::getResourceTexture(const QUrl& resourceTextureUrl) { diff --git a/libraries/material-networking/src/material-networking/TextureCache.h b/libraries/material-networking/src/material-networking/TextureCache.h index 754263566b..aab458e184 100644 --- a/libraries/material-networking/src/material-networking/TextureCache.h +++ b/libraries/material-networking/src/material-networking/TextureCache.h @@ -183,8 +183,8 @@ public: const QByteArray& content = QByteArray(), int maxNumPixels = ABSOLUTE_MAX_TEXTURE_NUM_PIXELS, image::ColorChannel sourceChannel = image::ColorChannel::NONE); - gpu::TexturePointer getTextureByHash(const std::string& hash); - gpu::TexturePointer cacheTextureByHash(const std::string& hash, const gpu::TexturePointer& texture); + std::pair getTextureByHash(const std::string& hash); + std::pair cacheTextureByHash(const std::string& hash, const std::pair& textureAndSize); NetworkTexturePointer getResourceTexture(const QUrl& resourceTextureUrl); const gpu::FramebufferPointer& getHmdPreviewFramebuffer(int width, int height); @@ -226,7 +226,7 @@ private: std::shared_ptr _ktxCache { std::make_shared(KTX_DIRNAME, KTX_EXT) }; // Map from image hashes to texture weak pointers - std::unordered_map> _texturesByHashes; + std::unordered_map, glm::ivec2>> _texturesByHashes; std::mutex _texturesByHashesMutex; gpu::TexturePointer _permutationNormalTexture; From e5f108b0225f0219c95d9d3c0230f702da127fbc Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Sun, 21 Mar 2021 14:51:21 -0700 Subject: [PATCH 4/5] bump ktx version to force clear cache --- .../material-networking/src/material-networking/KTXCache.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/material-networking/src/material-networking/KTXCache.cpp b/libraries/material-networking/src/material-networking/KTXCache.cpp index edb09addb8..5a5a94dcc9 100644 --- a/libraries/material-networking/src/material-networking/KTXCache.cpp +++ b/libraries/material-networking/src/material-networking/KTXCache.cpp @@ -19,7 +19,7 @@ using FilePointer = cache::FilePointer; // Whenever a change is made to the serialized format for the KTX cache that isn't backward compatible, // this value should be incremented. This will force the KTX cache to be wiped -const int KTXCache::CURRENT_VERSION = 0x01; +const int KTXCache::CURRENT_VERSION = 0x02; const int KTXCache::INVALID_VERSION = 0x00; const char* KTXCache::SETTING_VERSION_NAME = "hifi.ktx.cache_version"; From 6b82c7fe907bef76117037d2a9333bbe8bf67652 Mon Sep 17 00:00:00 2001 From: HifiExperiments <53453710+HifiExperiments@users.noreply.github.com> Date: Mon, 22 Mar 2021 18:37:29 -0700 Subject: [PATCH 5/5] Update RenderableImageEntityItem.cpp --- libraries/entities-renderer/src/RenderableImageEntityItem.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libraries/entities-renderer/src/RenderableImageEntityItem.cpp b/libraries/entities-renderer/src/RenderableImageEntityItem.cpp index 4d372b17e5..c0bbc0603a 100644 --- a/libraries/entities-renderer/src/RenderableImageEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableImageEntityItem.cpp @@ -71,6 +71,8 @@ void ImageEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPoint } else { naturalDimensions.y = height / width; } + // Unlike Models (where the Renderer also doubles as the EntityItem), Images need to + // convey this information back to the game object from the Renderer entity->setNaturalDimension(naturalDimensions); } }