mirror of
https://github.com/overte-org/overte.git
synced 2025-08-08 16:38:27 +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
|
gpu::BackendTarget::GLES32
|
||||||
}};
|
}};
|
||||||
for (auto target : BACKEND_TARGETS) {
|
for (auto target : BACKEND_TARGETS) {
|
||||||
auto processedTexture = image::processImage(buffer, _textureURL.toString().toStdString(), image::ColorChannel::NONE,
|
auto processedTextureAndSize = image::processImage(buffer, _textureURL.toString().toStdString(), image::ColorChannel::NONE,
|
||||||
ABSOLUTE_MAX_TEXTURE_NUM_PIXELS, _textureType, true,
|
ABSOLUTE_MAX_TEXTURE_NUM_PIXELS, _textureType, true,
|
||||||
target, _abortProcessing);
|
target, _abortProcessing);
|
||||||
if (!processedTexture) {
|
if (!processedTextureAndSize.first) {
|
||||||
handleError("Could not process texture " + _textureURL.toString());
|
handleError("Could not process texture " + _textureURL.toString());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
processedTexture->setSourceHash(hash);
|
processedTextureAndSize.first->setSourceHash(hash);
|
||||||
|
|
||||||
if (shouldStop()) {
|
if (shouldStop()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto memKTX = gpu::Texture::serialize(*processedTexture);
|
auto memKTX = gpu::Texture::serialize(*processedTextureAndSize.first, processedTextureAndSize.second);
|
||||||
if (!memKTX) {
|
if (!memKTX) {
|
||||||
handleError("Could not serialize " + _textureURL.toString() + " to KTX");
|
handleError("Could not serialize " + _textureURL.toString() + " to KTX");
|
||||||
return;
|
return;
|
||||||
|
@ -211,19 +211,19 @@ void TextureBaker::processTexture() {
|
||||||
// Uncompressed KTX
|
// Uncompressed KTX
|
||||||
if (_textureType == image::TextureUsage::Type::SKY_TEXTURE || _textureType == image::TextureUsage::Type::AMBIENT_TEXTURE) {
|
if (_textureType == image::TextureUsage::Type::SKY_TEXTURE || _textureType == image::TextureUsage::Type::AMBIENT_TEXTURE) {
|
||||||
buffer->reset();
|
buffer->reset();
|
||||||
auto processedTexture = image::processImage(std::move(buffer), _textureURL.toString().toStdString(), image::ColorChannel::NONE,
|
auto processedTextureAndSize = image::processImage(std::move(buffer), _textureURL.toString().toStdString(), image::ColorChannel::NONE,
|
||||||
ABSOLUTE_MAX_TEXTURE_NUM_PIXELS, _textureType, false, gpu::BackendTarget::GL45, _abortProcessing);
|
ABSOLUTE_MAX_TEXTURE_NUM_PIXELS, _textureType, false, gpu::BackendTarget::GL45, _abortProcessing);
|
||||||
if (!processedTexture) {
|
if (!processedTextureAndSize.first) {
|
||||||
handleError("Could not process texture " + _textureURL.toString());
|
handleError("Could not process texture " + _textureURL.toString());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
processedTexture->setSourceHash(hash);
|
processedTextureAndSize.first->setSourceHash(hash);
|
||||||
|
|
||||||
if (shouldStop()) {
|
if (shouldStop()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto memKTX = gpu::Texture::serialize(*processedTexture);
|
auto memKTX = gpu::Texture::serialize(*processedTextureAndSize.first, processedTextureAndSize.second);
|
||||||
if (!memKTX) {
|
if (!memKTX) {
|
||||||
handleError("Could not serialize " + _textureURL.toString() + " to KTX");
|
handleError("Could not serialize " + _textureURL.toString() + " to KTX");
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -59,10 +59,24 @@ void ImageEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPoint
|
||||||
_alpha = entity->getAlpha();
|
_alpha = entity->getAlpha();
|
||||||
_pulseProperties = entity->getPulseProperties();
|
_pulseProperties = entity->getPulseProperties();
|
||||||
|
|
||||||
|
bool nextTextureLoaded = _texture && (_texture->isLoaded() || _texture->isFailed());
|
||||||
if (!_textureIsLoaded) {
|
if (!_textureIsLoaded) {
|
||||||
emit requestRenderUpdate();
|
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() {
|
ShapeKey ImageEntityRenderer::getShapeKey() {
|
||||||
|
@ -100,18 +114,19 @@ void ImageEntityRenderer::doRender(RenderArgs* args) {
|
||||||
transform.setRotation(BillboardModeHelpers::getBillboardRotation(transform.getTranslation(), transform.getRotation(), _billboardMode,
|
transform.setRotation(BillboardModeHelpers::getBillboardRotation(transform.getTranslation(), transform.getRotation(), _billboardMode,
|
||||||
args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition()));
|
args->_renderMode == RenderArgs::RenderMode::SHADOW_RENDER_MODE ? BillboardModeHelpers::getPrimaryViewFrustumPosition() : args->getViewFrustum().getPosition()));
|
||||||
|
|
||||||
batch->setModelTransform(transform);
|
|
||||||
batch->setResourceTexture(0, _texture->getGPUTexture());
|
batch->setResourceTexture(0, _texture->getGPUTexture());
|
||||||
|
|
||||||
float imageWidth = _texture->getWidth();
|
float imageWidth = _texture->getWidth();
|
||||||
float imageHeight = _texture->getHeight();
|
float imageHeight = _texture->getHeight();
|
||||||
|
float originalWidth = _texture->getOriginalWidth();
|
||||||
|
float originalHeight = _texture->getOriginalHeight();
|
||||||
|
|
||||||
QRect fromImage;
|
QRect fromImage;
|
||||||
if (_subImage.width() <= 0) {
|
if (_subImage.width() <= 0) {
|
||||||
fromImage.setX(0);
|
fromImage.setX(0);
|
||||||
fromImage.setWidth(imageWidth);
|
fromImage.setWidth(imageWidth);
|
||||||
} else {
|
} else {
|
||||||
float scaleX = imageWidth / _texture->getOriginalWidth();
|
float scaleX = imageWidth / originalWidth;
|
||||||
fromImage.setX(scaleX * _subImage.x());
|
fromImage.setX(scaleX * _subImage.x());
|
||||||
fromImage.setWidth(scaleX * _subImage.width());
|
fromImage.setWidth(scaleX * _subImage.width());
|
||||||
}
|
}
|
||||||
|
@ -120,20 +135,30 @@ void ImageEntityRenderer::doRender(RenderArgs* args) {
|
||||||
fromImage.setY(0);
|
fromImage.setY(0);
|
||||||
fromImage.setHeight(imageHeight);
|
fromImage.setHeight(imageHeight);
|
||||||
} else {
|
} else {
|
||||||
float scaleY = imageHeight / _texture->getOriginalHeight();
|
float scaleY = imageHeight / originalHeight;
|
||||||
fromImage.setY(scaleY * _subImage.y());
|
fromImage.setY(scaleY * _subImage.y());
|
||||||
fromImage.setHeight(scaleY * _subImage.height());
|
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 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);
|
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(
|
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
|
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
|
* @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>
|
* {@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>
|
* {@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.
|
* @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);
|
COPY_ENTITY_PROPERTY_TO_PROPERTIES(alpha, getAlpha);
|
||||||
withReadLock([&] {
|
withReadLock([&] {
|
||||||
_pulseProperties.getProperties(properties);
|
_pulseProperties.getProperties(properties);
|
||||||
|
properties.setNaturalDimensions(_naturalDimensions);
|
||||||
});
|
});
|
||||||
|
|
||||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(imageURL, getImageURL);
|
COPY_ENTITY_PROPERTY_TO_PROPERTIES(imageURL, getImageURL);
|
||||||
|
@ -217,4 +218,10 @@ PulsePropertyGroup ImageEntityItem::getPulseProperties() const {
|
||||||
return resultWithReadLock<PulsePropertyGroup>([&] {
|
return resultWithReadLock<PulsePropertyGroup>([&] {
|
||||||
return _pulseProperties;
|
return _pulseProperties;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ImageEntityItem::setNaturalDimension(const glm::vec3& naturalDimensions) const {
|
||||||
|
withWriteLock([&] {
|
||||||
|
_naturalDimensions = naturalDimensions;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
|
@ -63,6 +63,8 @@ public:
|
||||||
|
|
||||||
PulsePropertyGroup getPulseProperties() const;
|
PulsePropertyGroup getPulseProperties() const;
|
||||||
|
|
||||||
|
void setNaturalDimension(const glm::vec3& naturalDimensions) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
glm::u8vec3 _color;
|
glm::u8vec3 _color;
|
||||||
float _alpha;
|
float _alpha;
|
||||||
|
@ -72,6 +74,8 @@ protected:
|
||||||
bool _emissive { false };
|
bool _emissive { false };
|
||||||
bool _keepAspectRatio { true };
|
bool _keepAspectRatio { true };
|
||||||
QRect _subImage;
|
QRect _subImage;
|
||||||
|
|
||||||
|
mutable glm::vec3 _naturalDimensions;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_ImageEntityItem_h
|
#endif // hifi_ImageEntityItem_h
|
||||||
|
|
|
@ -579,11 +579,11 @@ public:
|
||||||
ExternalUpdates getUpdates() const;
|
ExternalUpdates getUpdates() const;
|
||||||
|
|
||||||
// Serialize a texture into a KTX file
|
// 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 std::pair<TexturePointer, glm::ivec2> build(const ktx::KTXDescriptor& descriptor);
|
||||||
static TexturePointer unserialize(const std::string& ktxFile);
|
static std::pair<TexturePointer, glm::ivec2> unserialize(const std::string& ktxFile);
|
||||||
static TexturePointer unserialize(const cache::FilePointer& cacheEntry, const std::string& source = std::string());
|
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 evalKTXFormat(const Element& mipFormat, const Element& texelFormat, ktx::Header& header);
|
||||||
static bool evalTextureFormat(const ktx::Header& header, Element& mipFormat, Element& texelFormat);
|
static bool evalTextureFormat(const ktx::Header& header, Element& mipFormat, Element& texelFormat);
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include "Texture.h"
|
#include "Texture.h"
|
||||||
|
|
||||||
#include <QtCore/QByteArray>
|
#include <QtCore/QByteArray>
|
||||||
|
#include <glm/gtc/type_ptr.hpp>
|
||||||
|
|
||||||
#include <ktx/KTX.h>
|
#include <ktx/KTX.h>
|
||||||
|
|
||||||
|
@ -30,15 +31,16 @@ struct GPUKTXPayload {
|
||||||
using Version = uint8;
|
using Version = uint8;
|
||||||
|
|
||||||
static const std::string KEY;
|
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 PADDING { 2 };
|
||||||
static const size_t SIZE { sizeof(Version) + sizeof(Sampler::Desc) + sizeof(uint32) + sizeof(TextureUsageType) + PADDING };
|
static const size_t SIZE { sizeof(Version) + sizeof(Sampler::Desc) + sizeof(uint32) + sizeof(TextureUsageType) + sizeof(glm::ivec2) + PADDING };
|
||||||
static_assert(GPUKTXPayload::SIZE == 36, "Packing size may differ between platforms");
|
static_assert(GPUKTXPayload::SIZE == 44, "Packing size may differ between platforms");
|
||||||
static_assert(GPUKTXPayload::SIZE % 4 == 0, "GPUKTXPayload is not 4 bytes aligned");
|
static_assert(GPUKTXPayload::SIZE % 4 == 0, "GPUKTXPayload is not 4 bytes aligned");
|
||||||
|
|
||||||
Sampler::Desc _samplerDesc;
|
Sampler::Desc _samplerDesc;
|
||||||
Texture::Usage _usage;
|
Texture::Usage _usage;
|
||||||
TextureUsageType _usageType;
|
TextureUsageType _usageType;
|
||||||
|
glm::ivec2 _originalSize;
|
||||||
|
|
||||||
Byte* serialize(Byte* data) const {
|
Byte* serialize(Byte* data) const {
|
||||||
*(Version*)data = CURRENT_VERSION;
|
*(Version*)data = CURRENT_VERSION;
|
||||||
|
@ -56,6 +58,9 @@ struct GPUKTXPayload {
|
||||||
memcpy(data, &_usageType, sizeof(TextureUsageType));
|
memcpy(data, &_usageType, sizeof(TextureUsageType));
|
||||||
data += sizeof(TextureUsageType);
|
data += sizeof(TextureUsageType);
|
||||||
|
|
||||||
|
memcpy(data, glm::value_ptr(_originalSize), sizeof(glm::ivec2));
|
||||||
|
data += sizeof(glm::ivec2);
|
||||||
|
|
||||||
return data + PADDING;
|
return data + PADDING;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,13 +71,7 @@ struct GPUKTXPayload {
|
||||||
|
|
||||||
Version version = *(const Version*)data;
|
Version version = *(const Version*)data;
|
||||||
if (version != CURRENT_VERSION) {
|
if (version != CURRENT_VERSION) {
|
||||||
glm::vec4 borderColor(1.0f);
|
return false;
|
||||||
if (memcmp(&borderColor, data, sizeof(glm::vec4)) == 0) {
|
|
||||||
memcpy(this, data, sizeof(GPUKTXPayload));
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
data += sizeof(Version);
|
data += sizeof(Version);
|
||||||
|
|
||||||
|
@ -87,6 +86,10 @@ struct GPUKTXPayload {
|
||||||
data += sizeof(uint32);
|
data += sizeof(uint32);
|
||||||
|
|
||||||
memcpy(&_usageType, data, sizeof(TextureUsageType));
|
memcpy(&_usageType, data, sizeof(TextureUsageType));
|
||||||
|
data += sizeof(TextureUsageType);
|
||||||
|
|
||||||
|
memcpy(&_originalSize, data, sizeof(glm::ivec2));
|
||||||
|
data += sizeof(glm::ivec2);
|
||||||
return true;
|
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;
|
ktx::Header header;
|
||||||
|
|
||||||
// From texture format to ktx format description
|
// From texture format to ktx format description
|
||||||
|
@ -459,6 +462,7 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) {
|
||||||
gpuKeyval._samplerDesc = texture.getSampler().getDesc();
|
gpuKeyval._samplerDesc = texture.getSampler().getDesc();
|
||||||
gpuKeyval._usage = texture.getUsage();
|
gpuKeyval._usage = texture.getUsage();
|
||||||
gpuKeyval._usageType = texture.getUsageType();
|
gpuKeyval._usageType = texture.getUsageType();
|
||||||
|
gpuKeyval._originalSize = originalSize;
|
||||||
|
|
||||||
Byte keyvalPayload[GPUKTXPayload::SIZE];
|
Byte keyvalPayload[GPUKTXPayload::SIZE];
|
||||||
gpuKeyval.serialize(keyvalPayload);
|
gpuKeyval.serialize(keyvalPayload);
|
||||||
|
@ -514,19 +518,19 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) {
|
||||||
return ktxBuffer;
|
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 mipFormat = Format::COLOR_BGRA_32;
|
||||||
Format texelFormat = Format::COLOR_SRGBA_32;
|
Format texelFormat = Format::COLOR_SRGBA_32;
|
||||||
const auto& header = descriptor.header;
|
const auto& header = descriptor.header;
|
||||||
|
|
||||||
if (!Texture::evalTextureFormat(header, mipFormat, texelFormat)) {
|
if (!Texture::evalTextureFormat(header, mipFormat, texelFormat)) {
|
||||||
return nullptr;
|
return { nullptr, { 0, 0 } };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find Texture Type based on dimensions
|
// Find Texture Type based on dimensions
|
||||||
Type type = TEX_1D;
|
Type type = TEX_1D;
|
||||||
if (header.pixelWidth == 0) {
|
if (header.pixelWidth == 0) {
|
||||||
return nullptr;
|
return { nullptr, { 0, 0 } };
|
||||||
} else if (header.pixelHeight == 0) {
|
} else if (header.pixelHeight == 0) {
|
||||||
type = TEX_1D;
|
type = TEX_1D;
|
||||||
} else if (header.pixelDepth == 0) {
|
} else if (header.pixelDepth == 0) {
|
||||||
|
@ -569,39 +573,39 @@ TexturePointer Texture::build(const ktx::KTXDescriptor& descriptor) {
|
||||||
texture->overrideIrradiance(std::make_shared<SphericalHarmonics>(irradianceKtxKeyValue._irradianceSH));
|
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()));
|
std::unique_ptr<ktx::KTX> ktxPointer = ktx::KTX::create(std::make_shared<storage::FileStorage>(cacheEntry->getFilepath().c_str()));
|
||||||
if (!ktxPointer) {
|
if (!ktxPointer) {
|
||||||
return nullptr;
|
return { nullptr, { 0, 0 } };
|
||||||
}
|
}
|
||||||
|
|
||||||
auto texture = build(ktxPointer->toDescriptor());
|
auto textureAndSize = build(ktxPointer->toDescriptor());
|
||||||
if (texture) {
|
if (textureAndSize.first) {
|
||||||
texture->setKtxBacking(cacheEntry);
|
textureAndSize.first->setKtxBacking(cacheEntry);
|
||||||
if (texture->source().empty()) {
|
if (textureAndSize.first->source().empty()) {
|
||||||
texture->setSource(source);
|
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()));
|
std::unique_ptr<ktx::KTX> ktxPointer = ktx::KTX::create(std::make_shared<storage::FileStorage>(ktxfile.c_str()));
|
||||||
if (!ktxPointer) {
|
if (!ktxPointer) {
|
||||||
return nullptr;
|
return { nullptr, { 0, 0 } };
|
||||||
}
|
}
|
||||||
|
|
||||||
auto texture = build(ktxPointer->toDescriptor());
|
auto textureAndSize = build(ktxPointer->toDescriptor());
|
||||||
if (texture) {
|
if (textureAndSize.first) {
|
||||||
texture->setKtxBacking(ktxfile);
|
textureAndSize.first->setKtxBacking(ktxfile);
|
||||||
texture->setSource(ktxfile);
|
textureAndSize.first->setSource(ktxfile);
|
||||||
}
|
}
|
||||||
|
|
||||||
return texture;
|
return { textureAndSize.first, textureAndSize.second };
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Texture::evalKTXFormat(const Element& mipFormat, const Element& texelFormat, ktx::Header& header) {
|
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,
|
std::pair<gpu::TexturePointer, glm::ivec2> processImage(std::shared_ptr<QIODevice> content, const std::string& filename, ColorChannel sourceChannel,
|
||||||
int maxNumPixels, TextureUsage::Type textureType,
|
int maxNumPixels, TextureUsage::Type textureType,
|
||||||
bool compress, BackendTarget target, const std::atomic<bool>& abortProcessing) {
|
bool compress, BackendTarget target, const std::atomic<bool>& abortProcessing) {
|
||||||
|
|
||||||
Image image = processRawImageData(*content.get(), filename);
|
Image image = processRawImageData(*content.get(), filename);
|
||||||
// Texture content can take up a lot of memory. Here we release our ownership of that content
|
// Texture content can take up a lot of memory. Here we release our ownership of that content
|
||||||
|
@ -354,7 +354,7 @@ gpu::TexturePointer processImage(std::shared_ptr<QIODevice> content, const std::
|
||||||
if (imageWidth == 0 || imageHeight == 0 || image.getFormat() == Image::Format_Invalid) {
|
if (imageWidth == 0 || imageHeight == 0 || image.getFormat() == Image::Format_Invalid) {
|
||||||
QString reason(image.getFormat() == Image::Format_Invalid ? "(Invalid Format)" : "(Size is invalid)");
|
QString reason(image.getFormat() == Image::Format_Invalid ? "(Invalid Format)" : "(Size is invalid)");
|
||||||
qCWarning(imagelogging) << "Failed to load:" << qPrintable(reason);
|
qCWarning(imagelogging) << "Failed to load:" << qPrintable(reason);
|
||||||
return nullptr;
|
return { nullptr, { imageWidth, imageHeight } };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate the image is less than _maxNumPixels, and downscale if necessary
|
// 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 loader = TextureUsage::getTextureLoaderForType(textureType);
|
||||||
auto texture = loader(std::move(image), filename, compress, target, abortProcessing);
|
auto texture = loader(std::move(image), filename, compress, target, abortProcessing);
|
||||||
|
|
||||||
return texture;
|
return { texture, { imageWidth, imageHeight } };
|
||||||
}
|
}
|
||||||
|
|
||||||
Image processSourceImage(Image&& srcImage, bool cubemap, BackendTarget target) {
|
Image processSourceImage(Image&& srcImage, bool cubemap, BackendTarget target) {
|
||||||
|
|
|
@ -121,9 +121,9 @@ gpu::TexturePointer processCubeTextureColorFromImage(Image&& srcImage, const std
|
||||||
|
|
||||||
const QStringList getSupportedFormats();
|
const QStringList getSupportedFormats();
|
||||||
|
|
||||||
gpu::TexturePointer processImage(std::shared_ptr<QIODevice> content, const std::string& url, ColorChannel sourceChannel,
|
std::pair<gpu::TexturePointer, glm::ivec2> processImage(std::shared_ptr<QIODevice> content, const std::string& url, ColorChannel sourceChannel,
|
||||||
int maxNumPixels, TextureUsage::Type textureType,
|
int maxNumPixels, TextureUsage::Type textureType,
|
||||||
bool compress, gpu::BackendTarget target, const std::atomic<bool>& abortProcessing = false);
|
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 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);
|
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,
|
// 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
|
// 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 int KTXCache::INVALID_VERSION = 0x00;
|
||||||
const char* KTXCache::SETTING_VERSION_NAME = "hifi.ktx.cache_version";
|
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>();
|
return ResourceCache::getResource(modifiedUrl, QUrl(), &extra, std::hash<TextureExtra>()(extra)).staticCast<NetworkTexture>();
|
||||||
}
|
}
|
||||||
|
|
||||||
gpu::TexturePointer TextureCache::getTextureByHash(const std::string& hash) {
|
std::pair<gpu::TexturePointer, glm::ivec2> TextureCache::getTextureByHash(const std::string& hash) {
|
||||||
std::weak_ptr<gpu::Texture> weakPointer;
|
std::pair<gpu::TextureWeakPointer, glm::ivec2> weakPointer;
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lock(_texturesByHashesMutex);
|
std::unique_lock<std::mutex> lock(_texturesByHashesMutex);
|
||||||
weakPointer = _texturesByHashes[hash];
|
weakPointer = _texturesByHashes[hash];
|
||||||
}
|
}
|
||||||
return weakPointer.lock();
|
return { weakPointer.first.lock(), weakPointer.second };
|
||||||
}
|
}
|
||||||
|
|
||||||
gpu::TexturePointer TextureCache::cacheTextureByHash(const std::string& hash, const gpu::TexturePointer& texture) {
|
std::pair<gpu::TexturePointer, glm::ivec2> TextureCache::cacheTextureByHash(const std::string& hash, const std::pair<gpu::TexturePointer, glm::ivec2>& textureAndSize) {
|
||||||
gpu::TexturePointer result;
|
std::pair<gpu::TexturePointer, glm::ivec2> result;
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lock(_texturesByHashesMutex);
|
std::unique_lock<std::mutex> lock(_texturesByHashesMutex);
|
||||||
result = _texturesByHashes[hash].lock();
|
auto& value = _texturesByHashes[hash];
|
||||||
if (!result) {
|
result = { value.first.lock(), value.second };
|
||||||
_texturesByHashes[hash] = texture;
|
if (!result.first) {
|
||||||
result = texture;
|
_texturesByHashes[hash] = textureAndSize;
|
||||||
|
result = textureAndSize;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
@ -616,7 +617,7 @@ void NetworkTexture::makeLocalRequest() {
|
||||||
ktxDescriptor = std::make_shared<ktx::KTXDescriptor>(ktxFile->toDescriptor());
|
ktxDescriptor = std::make_shared<ktx::KTXDescriptor>(ktxFile->toDescriptor());
|
||||||
}
|
}
|
||||||
|
|
||||||
gpu::TexturePointer texture;
|
std::pair<gpu::TexturePointer, glm::ivec2> textureAndSize;
|
||||||
if (ktxDescriptor) {
|
if (ktxDescriptor) {
|
||||||
std::string hash;
|
std::string hash;
|
||||||
// Create bare ktx in memory
|
// Create bare ktx in memory
|
||||||
|
@ -634,18 +635,18 @@ void NetworkTexture::makeLocalRequest() {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto textureCache = DependencyManager::get<TextureCache>();
|
auto textureCache = DependencyManager::get<TextureCache>();
|
||||||
texture = textureCache->getTextureByHash(hash);
|
textureAndSize = textureCache->getTextureByHash(hash);
|
||||||
if (!texture) {
|
if (!textureAndSize.first) {
|
||||||
texture = gpu::Texture::build(*ktxDescriptor);
|
textureAndSize = gpu::Texture::build(*ktxDescriptor);
|
||||||
if (texture) {
|
if (textureAndSize.first) {
|
||||||
texture->setKtxBacking(path.toStdString());
|
textureAndSize.first->setKtxBacking(path.toStdString());
|
||||||
texture->setSource(path.toStdString());
|
textureAndSize.first->setSource(path.toStdString());
|
||||||
texture = textureCache->cacheTextureByHash(hash, texture);
|
textureAndSize = textureCache->cacheTextureByHash(hash, textureAndSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!texture) {
|
if (!textureAndSize.first) {
|
||||||
qCDebug(networking).noquote() << "Failed load local KTX from" << path;
|
qCDebug(networking).noquote() << "Failed load local KTX from" << path;
|
||||||
QMetaObject::invokeMethod(this, "setImage",
|
QMetaObject::invokeMethod(this, "setImage",
|
||||||
Q_ARG(gpu::TexturePointer, nullptr),
|
Q_ARG(gpu::TexturePointer, nullptr),
|
||||||
|
@ -655,11 +656,11 @@ void NetworkTexture::makeLocalRequest() {
|
||||||
}
|
}
|
||||||
|
|
||||||
_ktxResourceState = PENDING_MIP_REQUEST;
|
_ktxResourceState = PENDING_MIP_REQUEST;
|
||||||
_lowestKnownPopulatedMip = texture->minAvailableMipLevel();
|
_lowestKnownPopulatedMip = textureAndSize.first->minAvailableMipLevel();
|
||||||
QMetaObject::invokeMethod(this, "setImage",
|
QMetaObject::invokeMethod(this, "setImage",
|
||||||
Q_ARG(gpu::TexturePointer, texture),
|
Q_ARG(gpu::TexturePointer, textureAndSize.first),
|
||||||
Q_ARG(int, texture->getWidth()),
|
Q_ARG(int, textureAndSize.second.x),
|
||||||
Q_ARG(int, texture->getHeight()));
|
Q_ARG(int, textureAndSize.second.y));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -968,22 +969,22 @@ void NetworkTexture::handleFinishedInitialLoad() {
|
||||||
|
|
||||||
auto textureCache = DependencyManager::get<TextureCache>();
|
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);
|
auto ktxFile = textureCache->_ktxCache->getFile(hash);
|
||||||
if (ktxFile) {
|
if (ktxFile) {
|
||||||
texture = gpu::Texture::unserialize(ktxFile);
|
textureAndSize = gpu::Texture::unserialize(ktxFile);
|
||||||
if (texture) {
|
if (textureAndSize.first) {
|
||||||
texture = textureCache->cacheTextureByHash(hash, texture);
|
textureAndSize = textureCache->cacheTextureByHash(hash, textureAndSize);
|
||||||
if (texture->source().empty()) {
|
if (textureAndSize.first->source().empty()) {
|
||||||
texture->setSource(url.toString().toStdString());
|
textureAndSize.first->setSource(url.toString().toStdString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!texture) {
|
if (!textureAndSize.first) {
|
||||||
auto memKtx = ktx::KTX::createBare(*header, keyValues);
|
auto memKtx = ktx::KTX::createBare(*header, keyValues);
|
||||||
if (!memKtx) {
|
if (!memKtx) {
|
||||||
qWarning() << " Ktx could not be created, bailing";
|
qWarning() << " Ktx could not be created, bailing";
|
||||||
|
@ -1010,9 +1011,9 @@ void NetworkTexture::handleFinishedInitialLoad() {
|
||||||
|
|
||||||
auto newKtxDescriptor = memKtx->toDescriptor();
|
auto newKtxDescriptor = memKtx->toDescriptor();
|
||||||
|
|
||||||
texture = gpu::Texture::build(newKtxDescriptor);
|
textureAndSize = gpu::Texture::build(newKtxDescriptor);
|
||||||
texture->setKtxBacking(file);
|
textureAndSize.first->setKtxBacking(file);
|
||||||
texture->setSource(filename);
|
textureAndSize.first->setSource(filename);
|
||||||
|
|
||||||
auto& images = originalKtxDescriptor->images;
|
auto& images = originalKtxDescriptor->images;
|
||||||
size_t imageSizeRemaining = ktxHighMipData.size();
|
size_t imageSizeRemaining = ktxHighMipData.size();
|
||||||
|
@ -1025,7 +1026,7 @@ void NetworkTexture::handleFinishedInitialLoad() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
ktxData -= image._imageSize;
|
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;
|
ktxData -= ktx::IMAGE_SIZE_WIDTH;
|
||||||
imageSizeRemaining -= (image._imageSize + 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
|
// 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
|
// 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
|
// be the winner
|
||||||
texture = textureCache->cacheTextureByHash(filename, texture);
|
textureAndSize = textureCache->cacheTextureByHash(filename, textureAndSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
QMetaObject::invokeMethod(resource.data(), "setImage",
|
QMetaObject::invokeMethod(resource.data(), "setImage",
|
||||||
Q_ARG(gpu::TexturePointer, texture),
|
Q_ARG(gpu::TexturePointer, textureAndSize.first),
|
||||||
Q_ARG(int, texture->getWidth()),
|
Q_ARG(int, textureAndSize.second.x),
|
||||||
Q_ARG(int, texture->getHeight()));
|
Q_ARG(int, textureAndSize.second.y));
|
||||||
|
|
||||||
QMetaObject::invokeMethod(resource.data(), "startRequestForNextMipLevel");
|
QMetaObject::invokeMethod(resource.data(), "startRequestForNextMipLevel");
|
||||||
});
|
});
|
||||||
|
@ -1229,15 +1230,15 @@ void ImageReader::read() {
|
||||||
auto textureCache = DependencyManager::get<TextureCache>();
|
auto textureCache = DependencyManager::get<TextureCache>();
|
||||||
if (textureCache) {
|
if (textureCache) {
|
||||||
// If we already have a live texture with the same hash, use it
|
// 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 there is no live texture, check if there's an existing KTX file
|
||||||
if (!texture) {
|
if (!textureAndSize.first) {
|
||||||
auto ktxFile = textureCache->_ktxCache->getFile(hash);
|
auto ktxFile = textureCache->_ktxCache->getFile(hash);
|
||||||
if (ktxFile) {
|
if (ktxFile) {
|
||||||
texture = gpu::Texture::unserialize(ktxFile, _url.toString().toStdString());
|
textureAndSize = gpu::Texture::unserialize(ktxFile, _url.toString().toStdString());
|
||||||
if (texture) {
|
if (textureAndSize.first) {
|
||||||
texture = textureCache->cacheTextureByHash(hash, texture);
|
textureAndSize = textureCache->cacheTextureByHash(hash, textureAndSize);
|
||||||
} else {
|
} else {
|
||||||
qCWarning(materialnetworking) << "Invalid cached KTX " << _url << " under hash " << hash.c_str() << ", recreating...";
|
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,
|
// If we found the texture either because it's in use or via KTX deserialization,
|
||||||
// set the image and return immediately.
|
// set the image and return immediately.
|
||||||
if (texture) {
|
if (textureAndSize.first) {
|
||||||
QMetaObject::invokeMethod(resource.data(), "setImage",
|
QMetaObject::invokeMethod(resource.data(), "setImage",
|
||||||
Q_ARG(gpu::TexturePointer, texture),
|
Q_ARG(gpu::TexturePointer, textureAndSize.first),
|
||||||
Q_ARG(int, texture->getWidth()),
|
Q_ARG(int, textureAndSize.second.x),
|
||||||
Q_ARG(int, texture->getHeight()));
|
Q_ARG(int, textureAndSize.second.y));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Proccess new texture
|
// Proccess new texture
|
||||||
gpu::TexturePointer texture;
|
std::pair<gpu::TexturePointer, ivec2> textureAndSize;
|
||||||
{
|
{
|
||||||
PROFILE_RANGE_EX(resource_parse_image_raw, __FUNCTION__, 0xffff0000, 0);
|
PROFILE_RANGE_EX(resource_parse_image_raw, __FUNCTION__, 0xffff0000, 0);
|
||||||
|
|
||||||
|
@ -1269,23 +1270,23 @@ void ImageReader::read() {
|
||||||
constexpr bool shouldCompress = false;
|
constexpr bool shouldCompress = false;
|
||||||
#endif
|
#endif
|
||||||
auto target = getBackendTarget();
|
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",
|
QMetaObject::invokeMethod(resource.data(), "setImage",
|
||||||
Q_ARG(gpu::TexturePointer, texture),
|
Q_ARG(gpu::TexturePointer, textureAndSize.first),
|
||||||
Q_ARG(int, 0),
|
Q_ARG(int, 0),
|
||||||
Q_ARG(int, 0));
|
Q_ARG(int, 0));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
texture->setSourceHash(hash);
|
textureAndSize.first->setSourceHash(hash);
|
||||||
texture->setFallbackTexture(networkTexture->getFallbackTexture());
|
textureAndSize.first->setFallbackTexture(networkTexture->getFallbackTexture());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save the image into a KTXFile
|
// Save the image into a KTXFile
|
||||||
if (texture && textureCache) {
|
if (textureAndSize.first && textureCache) {
|
||||||
auto memKtx = gpu::Texture::serialize(*texture);
|
auto memKtx = gpu::Texture::serialize(*textureAndSize.first, textureAndSize.second);
|
||||||
|
|
||||||
// Move the texture into a memory mapped file
|
// Move the texture into a memory mapped file
|
||||||
if (memKtx) {
|
if (memKtx) {
|
||||||
|
@ -1294,20 +1295,20 @@ void ImageReader::read() {
|
||||||
auto& ktxCache = textureCache->_ktxCache;
|
auto& ktxCache = textureCache->_ktxCache;
|
||||||
auto file = ktxCache->writeFile(data, KTXCache::Metadata(hash, length));
|
auto file = ktxCache->writeFile(data, KTXCache::Metadata(hash, length));
|
||||||
if (file) {
|
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
|
// 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
|
// 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
|
// be the winner
|
||||||
texture = textureCache->cacheTextureByHash(hash, texture);
|
textureAndSize = textureCache->cacheTextureByHash(hash, textureAndSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
QMetaObject::invokeMethod(resource.data(), "setImage",
|
QMetaObject::invokeMethod(resource.data(), "setImage",
|
||||||
Q_ARG(gpu::TexturePointer, texture),
|
Q_ARG(gpu::TexturePointer, textureAndSize.first),
|
||||||
Q_ARG(int, texture->getWidth()),
|
Q_ARG(int, textureAndSize.second.x),
|
||||||
Q_ARG(int, texture->getHeight()));
|
Q_ARG(int, textureAndSize.second.y));
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkTexturePointer TextureCache::getResourceTexture(const QUrl& resourceTextureUrl) {
|
NetworkTexturePointer TextureCache::getResourceTexture(const QUrl& resourceTextureUrl) {
|
||||||
|
|
|
@ -183,8 +183,8 @@ public:
|
||||||
const QByteArray& content = QByteArray(), int maxNumPixels = ABSOLUTE_MAX_TEXTURE_NUM_PIXELS,
|
const QByteArray& content = QByteArray(), int maxNumPixels = ABSOLUTE_MAX_TEXTURE_NUM_PIXELS,
|
||||||
image::ColorChannel sourceChannel = image::ColorChannel::NONE);
|
image::ColorChannel sourceChannel = image::ColorChannel::NONE);
|
||||||
|
|
||||||
gpu::TexturePointer getTextureByHash(const std::string& hash);
|
std::pair<gpu::TexturePointer, glm::ivec2> getTextureByHash(const std::string& hash);
|
||||||
gpu::TexturePointer cacheTextureByHash(const std::string& hash, const gpu::TexturePointer& texture);
|
std::pair<gpu::TexturePointer, glm::ivec2> cacheTextureByHash(const std::string& hash, const std::pair<gpu::TexturePointer, glm::ivec2>& textureAndSize);
|
||||||
|
|
||||||
NetworkTexturePointer getResourceTexture(const QUrl& resourceTextureUrl);
|
NetworkTexturePointer getResourceTexture(const QUrl& resourceTextureUrl);
|
||||||
const gpu::FramebufferPointer& getHmdPreviewFramebuffer(int width, int height);
|
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) };
|
std::shared_ptr<cache::FileCache> _ktxCache { std::make_shared<KTXCache>(KTX_DIRNAME, KTX_EXT) };
|
||||||
|
|
||||||
// Map from image hashes to texture weak pointers
|
// 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;
|
std::mutex _texturesByHashesMutex;
|
||||||
|
|
||||||
gpu::TexturePointer _permutationNormalTexture;
|
gpu::TexturePointer _permutationNormalTexture;
|
||||||
|
|
Loading…
Reference in a new issue