mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-08 19:23:28 +02:00
Merge pull request #1050 from HifiExperiments/imageAspectFix
Image Entities: fix keepAspectRatio, implement naturalDimensions
This commit is contained in:
commit
409cc5835c
12 changed files with 169 additions and 128 deletions
|
@ -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;
|
||||
|
|
|
@ -59,10 +59,24 @@ 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->getOriginalWidth();
|
||||
float height = _texture->getOriginalHeight();
|
||||
glm::vec3 naturalDimensions = glm::vec3(1.0f, 1.0f, 0.01f);
|
||||
if (width < height) {
|
||||
naturalDimensions.x = width / height;
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
_textureIsLoaded = _texture && (_texture->isLoaded() || _texture->isFailed());
|
||||
_textureIsLoaded = nextTextureLoaded;
|
||||
}
|
||||
|
||||
ShapeKey ImageEntityRenderer::getShapeKey() {
|
||||
|
@ -100,18 +114,19 @@ 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();
|
||||
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());
|
||||
}
|
||||
|
@ -120,20 +135,30 @@ 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());
|
||||
}
|
||||
|
||||
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 = originalWidth / originalHeight;
|
||||
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<GeometryCache>()->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
|
||||
);
|
||||
|
||||
|
|
|
@ -723,7 +723,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}. <em>Read-only.</em>
|
||||
* @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}. <em>Read-only.</em>
|
||||
*
|
||||
* @property {Vec3} velocity=0,0,0 - The linear velocity of the entity in m/s with respect to world coordinates.
|
||||
|
|
|
@ -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<PulsePropertyGroup>([&] {
|
||||
return _pulseProperties;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void ImageEntityItem::setNaturalDimension(const glm::vec3& naturalDimensions) const {
|
||||
withWriteLock([&] {
|
||||
_naturalDimensions = naturalDimensions;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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";
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue