image caching must maintain original image information and pass it around

This commit is contained in:
HifiExperiments 2021-03-19 22:32:29 -07:00
parent 5677b5d5dc
commit 35e836d34d
8 changed files with 128 additions and 121 deletions

View file

@ -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;

View file

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

View file

@ -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<TexturePointer, glm::ivec2> build(const ktx::KTXDescriptor& descriptor);
static std::pair<TexturePointer, glm::ivec2> unserialize(const std::string& ktxFile);
static std::pair<TexturePointer, glm::ivec2> 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);

View file

@ -13,6 +13,7 @@
#include "Texture.h"
#include <QtCore/QByteArray>
#include <glm/gtc/type_ptr.hpp>
#include <ktx/KTX.h>
@ -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<TexturePointer, glm::ivec2> 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<SphericalHarmonics>(irradianceKtxKeyValue._irradianceSH));
}
return texture;
return { texture, gpuktxKeyValue._originalSize };
}
TexturePointer Texture::unserialize(const cache::FilePointer& cacheEntry, const std::string& source) {
std::pair<TexturePointer, glm::ivec2> Texture::unserialize(const cache::FilePointer& cacheEntry, const std::string& source) {
std::unique_ptr<ktx::KTX> ktxPointer = ktx::KTX::create(std::make_shared<storage::FileStorage>(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<TexturePointer, glm::ivec2> Texture::unserialize(const std::string& ktxfile) {
std::unique_ptr<ktx::KTX> ktxPointer = ktx::KTX::create(std::make_shared<storage::FileStorage>(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) {

View file

@ -338,9 +338,9 @@ void mapToRedChannel(Image& image, ColorChannel sourceChannel) {
}
}
gpu::TexturePointer processImage(std::shared_ptr<QIODevice> content, const std::string& filename, ColorChannel sourceChannel,
int maxNumPixels, TextureUsage::Type textureType,
bool compress, BackendTarget target, const std::atomic<bool>& abortProcessing) {
std::pair<gpu::TexturePointer, glm::ivec2> processImage(std::shared_ptr<QIODevice> content, const std::string& filename, ColorChannel sourceChannel,
int maxNumPixels, TextureUsage::Type textureType,
bool compress, BackendTarget target, const std::atomic<bool>& 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<QIODevice> 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<QIODevice> 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) {

View file

@ -121,9 +121,9 @@ gpu::TexturePointer processCubeTextureColorFromImage(Image&& srcImage, const std
const QStringList getSupportedFormats();
gpu::TexturePointer processImage(std::shared_ptr<QIODevice> content, const std::string& url, ColorChannel sourceChannel,
int maxNumPixels, TextureUsage::Type textureType,
bool compress, gpu::BackendTarget target, const std::atomic<bool>& abortProcessing = false);
std::pair<gpu::TexturePointer, glm::ivec2> processImage(std::shared_ptr<QIODevice> content, const std::string& url, ColorChannel sourceChannel,
int maxNumPixels, TextureUsage::Type textureType,
bool compress, gpu::BackendTarget target, const std::atomic<bool>& abortProcessing = false);
void convertToTextureWithMips(gpu::Texture* texture, Image&& image, gpu::BackendTarget target, const std::atomic<bool>& abortProcessing = false, int face = -1);
void convertToTexture(gpu::Texture* texture, Image&& image, gpu::BackendTarget target, const std::atomic<bool>& abortProcessing = false, int face = -1, int mipLevel = 0);

View file

@ -266,23 +266,24 @@ NetworkTexturePointer TextureCache::getTexture(const QUrl& url, image::TextureUs
return ResourceCache::getResource(modifiedUrl, QUrl(), &extra, std::hash<TextureExtra>()(extra)).staticCast<NetworkTexture>();
}
gpu::TexturePointer TextureCache::getTextureByHash(const std::string& hash) {
std::weak_ptr<gpu::Texture> weakPointer;
std::pair<gpu::TexturePointer, glm::ivec2> TextureCache::getTextureByHash(const std::string& hash) {
std::pair<gpu::TextureWeakPointer, glm::ivec2> weakPointer;
{
std::unique_lock<std::mutex> 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<gpu::TexturePointer, glm::ivec2> TextureCache::cacheTextureByHash(const std::string& hash, const std::pair<gpu::TexturePointer, glm::ivec2>& textureAndSize) {
std::pair<gpu::TexturePointer, glm::ivec2> result;
{
std::unique_lock<std::mutex> 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<ktx::KTXDescriptor>(ktxFile->toDescriptor());
}
gpu::TexturePointer texture;
std::pair<gpu::TexturePointer, glm::ivec2> textureAndSize;
if (ktxDescriptor) {
std::string hash;
// Create bare ktx in memory
@ -634,18 +635,18 @@ void NetworkTexture::makeLocalRequest() {
}
auto textureCache = DependencyManager::get<TextureCache>();
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<TextureCache>();
gpu::TexturePointer texture = textureCache->getTextureByHash(hash);
std::pair<gpu::TexturePointer, glm::ivec2> 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<gpu::uint16>(level), image._imageSize, ktxData);
textureAndSize.first->assignStoredMip(static_cast<gpu::uint16>(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<TextureCache>();
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<gpu::TexturePointer, ivec2> 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) {

View file

@ -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<gpu::TexturePointer, glm::ivec2> getTextureByHash(const std::string& hash);
std::pair<gpu::TexturePointer, glm::ivec2> cacheTextureByHash(const std::string& hash, const std::pair<gpu::TexturePointer, glm::ivec2>& textureAndSize);
NetworkTexturePointer getResourceTexture(const QUrl& resourceTextureUrl);
const gpu::FramebufferPointer& getHmdPreviewFramebuffer(int width, int height);
@ -226,7 +226,7 @@ private:
std::shared_ptr<cache::FileCache> _ktxCache { std::make_shared<KTXCache>(KTX_DIRNAME, KTX_EXT) };
// Map from image hashes to texture weak pointers
std::unordered_map<std::string, std::weak_ptr<gpu::Texture>> _texturesByHashes;
std::unordered_map<std::string, std::pair<std::weak_ptr<gpu::Texture>, glm::ivec2>> _texturesByHashes;
std::mutex _texturesByHashesMutex;
gpu::TexturePointer _permutationNormalTexture;