Merge remote-tracking branch 'upstream/master' into feature/quest

This commit is contained in:
Brad Davis 2019-02-12 15:00:03 -08:00
commit db121957d2
38 changed files with 492 additions and 196 deletions

View file

@ -607,7 +607,7 @@ Rectangle {
} else if (msg.method === "showTrashLightbox") { } else if (msg.method === "showTrashLightbox") {
lightboxPopup.titleText = "Send \"" + msg.itemName + "\" to Trash"; lightboxPopup.titleText = "Send \"" + msg.itemName + "\" to Trash";
lightboxPopup.bodyText = "Sending this item to the Trash means you will no longer own this item " + lightboxPopup.bodyText = "Sending this item to the Trash means you will no longer own this item " +
"and it will be inaccessible to you from Purchases.\n\nThis action cannot be undone."; "and it will be inaccessible to you from Inventory.\n\nThis action cannot be undone.";
lightboxPopup.button1text = "CANCEL"; lightboxPopup.button1text = "CANCEL";
lightboxPopup.button1method = function() { lightboxPopup.button1method = function() {
lightboxPopup.visible = false; lightboxPopup.visible = false;

View file

@ -447,6 +447,10 @@ void OtherAvatar::handleChangedAvatarEntityData() {
EntityItemProperties properties; EntityItemProperties properties;
int32_t bytesLeftToRead = data.size(); int32_t bytesLeftToRead = data.size();
unsigned char* dataAt = (unsigned char*)(data.data()); unsigned char* dataAt = (unsigned char*)(data.data());
// FIXME: This function will cause unintented changes in SpaillyNestable
// E.g overriding the ID index of an exisiting entity to temporary entity
// in the following map QHash<QUuid, SpatiallyNestableWeakPointer> _children;
// Andrew Meadows will address this issue
if (!properties.constructFromBuffer(dataAt, bytesLeftToRead)) { if (!properties.constructFromBuffer(dataAt, bytesLeftToRead)) {
// properties are corrupt // properties are corrupt
continue; continue;
@ -489,6 +493,17 @@ void OtherAvatar::handleChangedAvatarEntityData() {
bool success = true; bool success = true;
if (entity) { if (entity) {
QUuid oldParentID = entity->getParentID(); QUuid oldParentID = entity->getParentID();
// Since has overwrtiiten the back pointer
// from the parent children map (see comment for function call above),
// we need to for reset the back pointer in the map correctly by setting the parentID, but
// since the parentID of the entity has not changed we first need to set it some ither ID,
// then set the the original ID for the changes to take effect
// TODO: This is a horrible hack and once properties.constructFromBuffer no longer causes
// side effects...remove the following three lines
const QUuid NULL_ID = QUuid("{00000000-0000-0000-0000-000000000005}");
entity->setParentID(NULL_ID);
entity->setParentID(oldParentID);
if (entityTree->updateEntity(entityID, properties)) { if (entityTree->updateEntity(entityID, properties)) {
entity->updateLastEditedFromRemote(); entity->updateLastEditedFromRemote();
} else { } else {

View file

@ -36,12 +36,13 @@ AnimationPointer AnimationCache::getAnimation(const QUrl& url) {
return getResource(url).staticCast<Animation>(); return getResource(url).staticCast<Animation>();
} }
QSharedPointer<Resource> AnimationCache::createResource(const QUrl& url, const QSharedPointer<Resource>& fallback, QSharedPointer<Resource> AnimationCache::createResource(const QUrl& url) {
const void* extra) {
return QSharedPointer<Resource>(new Animation(url), &Resource::deleter); return QSharedPointer<Resource>(new Animation(url), &Resource::deleter);
} }
Animation::Animation(const QUrl& url) : Resource(url) {} QSharedPointer<Resource> AnimationCache::createResourceCopy(const QSharedPointer<Resource>& resource) {
return QSharedPointer<Resource>(new Animation(*resource.staticCast<Animation>().data()), &Resource::deleter);
}
AnimationReader::AnimationReader(const QUrl& url, const QByteArray& data) : AnimationReader::AnimationReader(const QUrl& url, const QByteArray& data) :
_url(url), _url(url),

View file

@ -34,9 +34,9 @@ public:
Q_INVOKABLE AnimationPointer getAnimation(const QUrl& url); Q_INVOKABLE AnimationPointer getAnimation(const QUrl& url);
protected: protected:
virtual QSharedPointer<Resource> createResource(const QUrl& url) override;
QSharedPointer<Resource> createResourceCopy(const QSharedPointer<Resource>& resource) override;
virtual QSharedPointer<Resource> createResource(const QUrl& url, const QSharedPointer<Resource>& fallback,
const void* extra) override;
private: private:
explicit AnimationCache(QObject* parent = NULL); explicit AnimationCache(QObject* parent = NULL);
virtual ~AnimationCache() { } virtual ~AnimationCache() { }
@ -62,7 +62,8 @@ class Animation : public Resource {
public: public:
explicit Animation(const QUrl& url); Animation(const Animation& other) : Resource(other), _hfmModel(other._hfmModel) {}
Animation(const QUrl& url) : Resource(url) {}
QString getType() const override { return "Animation"; } QString getType() const override { return "Animation"; }

View file

@ -33,9 +33,12 @@ SharedSoundPointer SoundCache::getSound(const QUrl& url) {
return getResource(url).staticCast<Sound>(); return getResource(url).staticCast<Sound>();
} }
QSharedPointer<Resource> SoundCache::createResource(const QUrl& url, const QSharedPointer<Resource>& fallback, QSharedPointer<Resource> SoundCache::createResource(const QUrl& url) {
const void* extra) {
auto resource = QSharedPointer<Resource>(new Sound(url), &Resource::deleter); auto resource = QSharedPointer<Resource>(new Sound(url), &Resource::deleter);
resource->setLoadPriority(this, SOUNDS_LOADING_PRIORITY); resource->setLoadPriority(this, SOUNDS_LOADING_PRIORITY);
return resource; return resource;
} }
QSharedPointer<Resource> SoundCache::createResourceCopy(const QSharedPointer<Resource>& resource) {
return QSharedPointer<Resource>(new Sound(*resource.staticCast<Sound>().data()), &Resource::deleter);
}

View file

@ -24,8 +24,9 @@ public:
Q_INVOKABLE SharedSoundPointer getSound(const QUrl& url); Q_INVOKABLE SharedSoundPointer getSound(const QUrl& url);
protected: protected:
virtual QSharedPointer<Resource> createResource(const QUrl& url, const QSharedPointer<Resource>& fallback, virtual QSharedPointer<Resource> createResource(const QUrl& url) override;
const void* extra) override; QSharedPointer<Resource> createResourceCopy(const QSharedPointer<Resource>& resource) override;
private: private:
SoundCache(QObject* parent = NULL); SoundCache(QObject* parent = NULL);
}; };

View file

@ -280,11 +280,7 @@ bool RenderableModelEntityItem::findDetailedParabolaIntersection(const glm::vec3
} }
void RenderableModelEntityItem::fetchCollisionGeometryResource() { void RenderableModelEntityItem::fetchCollisionGeometryResource() {
QUrl hullURL(getCollisionShapeURL()); _compoundShapeResource = DependencyManager::get<ModelCache>()->getCollisionGeometryResource(getCollisionShapeURL());
QUrlQuery queryArgs(hullURL);
queryArgs.addQueryItem("collision-hull", "");
hullURL.setQuery(queryArgs);
_compoundShapeResource = DependencyManager::get<ModelCache>()->getCollisionGeometryResource(hullURL);
} }
bool RenderableModelEntityItem::computeShapeFailedToLoad() { bool RenderableModelEntityItem::computeShapeFailedToLoad() {

View file

@ -76,13 +76,12 @@ HFMTexture FBXSerializer::getTexture(const QString& textureID, const QString& ma
} }
void FBXSerializer::consolidateHFMMaterials(const QVariantHash& mapping) { void FBXSerializer::consolidateHFMMaterials(const QVariantHash& mapping) {
QJsonObject materialMap;
QString materialMapString = mapping.value("materialMap").toString(); if (mapping.contains("materialMap")) {
QJsonDocument materialMapDocument = QJsonDocument::fromJson(materialMapString.toUtf8()); QByteArray materialMapValue = mapping.value("materialMap").toByteArray();
QJsonObject materialMap = materialMapDocument.object(); materialMap = QJsonDocument::fromJson(materialMapValue).object();
if (!materialMapString.isEmpty()) { if (materialMap.isEmpty()) {
if (materialMapDocument.isEmpty() || materialMap.isEmpty()) { qCDebug(modelformat) << "fbx Material Map found but did not produce valid JSON:" << materialMapValue;
qCDebug(modelformat) << "fbx Material Map found but did not produce valid JSON:" << materialMapString;
} }
} }
for (QHash<QString, HFMMaterial>::iterator it = _hfmMaterials.begin(); it != _hfmMaterials.end(); it++) { for (QHash<QString, HFMMaterial>::iterator it = _hfmMaterials.begin(); it != _hfmMaterials.end(); it++) {

View file

@ -124,6 +124,31 @@ bool GLTFSerializer::getObjectArrayVal(const QJsonObject& object, const QString&
return _defined; return _defined;
} }
QByteArray GLTFSerializer::setGLBChunks(const QByteArray& data) {
int byte = 4;
int jsonStart = data.indexOf("JSON", Qt::CaseSensitive);
int binStart = data.indexOf("BIN", Qt::CaseSensitive);
int jsonLength, binLength;
QByteArray jsonLengthChunk, binLengthChunk;
jsonLengthChunk = data.mid(jsonStart - byte, byte);
QDataStream tempJsonLen(jsonLengthChunk);
tempJsonLen.setByteOrder(QDataStream::LittleEndian);
tempJsonLen >> jsonLength;
QByteArray jsonChunk = data.mid(jsonStart + byte, jsonLength);
if (binStart != -1) {
binLengthChunk = data.mid(binStart - byte, byte);
QDataStream tempBinLen(binLengthChunk);
tempBinLen.setByteOrder(QDataStream::LittleEndian);
tempBinLen >> binLength;
_glbBinary = data.mid(binStart + byte, binLength);
}
return jsonChunk;
}
int GLTFSerializer::getMeshPrimitiveRenderingMode(const QString& type) int GLTFSerializer::getMeshPrimitiveRenderingMode(const QString& type)
{ {
if (type == "POINTS") { if (type == "POINTS") {
@ -309,6 +334,14 @@ bool GLTFSerializer::addBuffer(const QJsonObject& object) {
GLTFBuffer buffer; GLTFBuffer buffer;
getIntVal(object, "byteLength", buffer.byteLength, buffer.defined); getIntVal(object, "byteLength", buffer.byteLength, buffer.defined);
if (_url.toString().endsWith("glb")) {
if (!_glbBinary.isEmpty()) {
buffer.blob = _glbBinary;
} else {
return false;
}
}
if (getStringVal(object, "uri", buffer.uri, buffer.defined)) { if (getStringVal(object, "uri", buffer.uri, buffer.defined)) {
if (!readBinary(buffer.uri, buffer.blob)) { if (!readBinary(buffer.uri, buffer.blob)) {
return false; return false;
@ -536,8 +569,15 @@ bool GLTFSerializer::addTexture(const QJsonObject& object) {
bool GLTFSerializer::parseGLTF(const QByteArray& data) { bool GLTFSerializer::parseGLTF(const QByteArray& data) {
PROFILE_RANGE_EX(resource_parse, __FUNCTION__, 0xffff0000, nullptr); PROFILE_RANGE_EX(resource_parse, __FUNCTION__, 0xffff0000, nullptr);
QJsonDocument d = QJsonDocument::fromJson(data); QByteArray jsonChunk = data;
if (_url.toString().endsWith("glb") && data.indexOf("glTF") == 0 && data.contains("JSON")) {
jsonChunk = setGLBChunks(data);
}
QJsonDocument d = QJsonDocument::fromJson(jsonChunk);
QJsonObject jsFile = d.object(); QJsonObject jsFile = d.object();
bool success = setAsset(jsFile); bool success = setAsset(jsFile);
if (success) { if (success) {
QJsonArray accessors; QJsonArray accessors;
@ -924,6 +964,10 @@ MediaType GLTFSerializer::getMediaType() const {
MediaType mediaType("gltf"); MediaType mediaType("gltf");
mediaType.extensions.push_back("gltf"); mediaType.extensions.push_back("gltf");
mediaType.webMediaTypes.push_back("model/gltf+json"); mediaType.webMediaTypes.push_back("model/gltf+json");
mediaType.extensions.push_back("glb");
mediaType.webMediaTypes.push_back("model/gltf-binary");
return mediaType; return mediaType;
} }
@ -1042,6 +1086,17 @@ HFMTexture GLTFSerializer::getHFMTexture(const GLTFTexture& texture) {
fbxtex.name = fname; fbxtex.name = fname;
fbxtex.filename = textureUrl.toEncoded(); fbxtex.filename = textureUrl.toEncoded();
if (_url.toString().endsWith("glb") && !_glbBinary.isEmpty()) {
int bufferView = _file.images[texture.source].bufferView;
GLTFBufferView& imagesBufferview = _file.bufferviews[bufferView];
int offset = imagesBufferview.byteOffset;
int length = imagesBufferview.byteLength;
fbxtex.content = _glbBinary.mid(offset, length);
fbxtex.filename = textureUrl.toEncoded().append(texture.source);
}
if (url.contains("data:image/jpeg;base64,") || url.contains("data:image/png;base64,")) { if (url.contains("data:image/jpeg;base64,") || url.contains("data:image/png;base64,")) {
fbxtex.content = requestEmbeddedData(url); fbxtex.content = requestEmbeddedData(url);
} }

View file

@ -709,6 +709,7 @@ public:
private: private:
GLTFFile _file; GLTFFile _file;
QUrl _url; QUrl _url;
QByteArray _glbBinary;
glm::mat4 getModelTransform(const GLTFNode& node); glm::mat4 getModelTransform(const GLTFNode& node);
@ -732,6 +733,8 @@ private:
bool getObjectArrayVal(const QJsonObject& object, const QString& fieldname, bool getObjectArrayVal(const QJsonObject& object, const QString& fieldname,
QJsonArray& objects, QMap<QString, bool>& defined); QJsonArray& objects, QMap<QString, bool>& defined);
QByteArray setGLBChunks(const QByteArray& data);
int getMaterialAlphaMode(const QString& type); int getMaterialAlphaMode(const QString& type);
int getAccessorType(const QString& type); int getAccessorType(const QString& type);
int getAnimationSamplerInterpolation(const QString& interpolation); int getAnimationSamplerInterpolation(const QString& interpolation);

View file

@ -60,9 +60,17 @@ GLenum GLTexture::getGLTextureType(const Texture& texture) {
switch (texture.getType()) { switch (texture.getType()) {
case Texture::TEX_2D: case Texture::TEX_2D:
if (!texture.isArray()) { if (!texture.isArray()) {
if (!texture.isMultisample()) {
return GL_TEXTURE_2D; return GL_TEXTURE_2D;
} else { } else {
return GL_TEXTURE_2D_MULTISAMPLE;
}
} else {
if (!texture.isMultisample()) {
return GL_TEXTURE_2D_ARRAY; return GL_TEXTURE_2D_ARRAY;
} else {
return GL_TEXTURE_2D_MULTISAMPLE_ARRAY;
}
} }
break; break;
@ -81,7 +89,9 @@ GLenum GLTexture::getGLTextureType(const Texture& texture) {
uint8_t GLTexture::getFaceCount(GLenum target) { uint8_t GLTexture::getFaceCount(GLenum target) {
switch (target) { switch (target) {
case GL_TEXTURE_2D: case GL_TEXTURE_2D:
case GL_TEXTURE_2D_MULTISAMPLE:
case GL_TEXTURE_2D_ARRAY: case GL_TEXTURE_2D_ARRAY:
case GL_TEXTURE_2D_MULTISAMPLE_ARRAY:
return TEXTURE_2D_NUM_FACES; return TEXTURE_2D_NUM_FACES;
case GL_TEXTURE_CUBE_MAP: case GL_TEXTURE_CUBE_MAP:
return TEXTURE_CUBE_NUM_FACES; return TEXTURE_CUBE_NUM_FACES;
@ -96,15 +106,20 @@ const std::vector<GLenum>& GLTexture::getFaceTargets(GLenum target) {
GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
GL_TEXTURE_CUBE_MAP_POSITIVE_Z, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z GL_TEXTURE_CUBE_MAP_POSITIVE_Z, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z
}; };
static const std::vector<GLenum> faceTargets { static const std::vector<GLenum> face2DTargets {
GL_TEXTURE_2D GL_TEXTURE_2D
}; };
static const std::vector<GLenum> face2DMSTargets{
GL_TEXTURE_2D_MULTISAMPLE
};
static const std::vector<GLenum> arrayFaceTargets{ static const std::vector<GLenum> arrayFaceTargets{
GL_TEXTURE_2D_ARRAY GL_TEXTURE_2D_ARRAY
}; };
switch (target) { switch (target) {
case GL_TEXTURE_2D: case GL_TEXTURE_2D:
return faceTargets; return face2DTargets;
case GL_TEXTURE_2D_MULTISAMPLE:
return face2DMSTargets;
case GL_TEXTURE_2D_ARRAY: case GL_TEXTURE_2D_ARRAY:
return arrayFaceTargets; return arrayFaceTargets;
case GL_TEXTURE_CUBE_MAP: case GL_TEXTURE_CUBE_MAP:
@ -114,7 +129,7 @@ const std::vector<GLenum>& GLTexture::getFaceTargets(GLenum target) {
break; break;
} }
Q_UNREACHABLE(); Q_UNREACHABLE();
return faceTargets; return face2DTargets;
} }
GLTexture::GLTexture(const std::weak_ptr<GLBackend>& backend, const Texture& texture, GLuint id) : GLTexture::GLTexture(const std::weak_ptr<GLBackend>& backend, const Texture& texture, GLuint id) :

View file

@ -66,6 +66,8 @@ public:
if (gltexture) { if (gltexture) {
if (gltexture->_target == GL_TEXTURE_2D) { if (gltexture->_target == GL_TEXTURE_2D) {
glFramebufferTexture2D(GL_FRAMEBUFFER, colorAttachments[unit], GL_TEXTURE_2D, gltexture->_texture, 0); glFramebufferTexture2D(GL_FRAMEBUFFER, colorAttachments[unit], GL_TEXTURE_2D, gltexture->_texture, 0);
} else if (gltexture->_target == GL_TEXTURE_2D_MULTISAMPLE) {
glFramebufferTexture2D(GL_FRAMEBUFFER, colorAttachments[unit], GL_TEXTURE_2D_MULTISAMPLE, gltexture->_texture, 0);
} else { } else {
glFramebufferTextureLayer(GL_FRAMEBUFFER, colorAttachments[unit], gltexture->_texture, 0, glFramebufferTextureLayer(GL_FRAMEBUFFER, colorAttachments[unit], gltexture->_texture, 0,
b._subresource); b._subresource);
@ -98,6 +100,8 @@ public:
if (gltexture) { if (gltexture) {
if (gltexture->_target == GL_TEXTURE_2D) { if (gltexture->_target == GL_TEXTURE_2D) {
glFramebufferTexture2D(GL_FRAMEBUFFER, attachement, GL_TEXTURE_2D, gltexture->_texture, 0); glFramebufferTexture2D(GL_FRAMEBUFFER, attachement, GL_TEXTURE_2D, gltexture->_texture, 0);
} else if (gltexture->_target == GL_TEXTURE_2D_MULTISAMPLE) {
glFramebufferTexture2D(GL_FRAMEBUFFER, attachement, GL_TEXTURE_2D_MULTISAMPLE, gltexture->_texture, 0);
} else { } else {
glFramebufferTextureLayer(GL_FRAMEBUFFER, attachement, gltexture->_texture, 0, glFramebufferTextureLayer(GL_FRAMEBUFFER, attachement, gltexture->_texture, 0,
_gpuObject.getDepthStencilBufferSubresource()); _gpuObject.getDepthStencilBufferSubresource());

View file

@ -216,8 +216,10 @@ void GL41FixedAllocationTexture::allocateStorage() const {
const GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat()); const GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat());
const auto numMips = _gpuObject.getNumMips(); const auto numMips = _gpuObject.getNumMips();
const auto numSlices = _gpuObject.getNumSlices(); const auto numSlices = _gpuObject.getNumSlices();
const auto numSamples = _gpuObject.getNumSamples();
// glTextureStorage2D(_id, mips, texelFormat.internalFormat, dimensions.x, dimensions.y); // glTextureStorage2D(_id, mips, texelFormat.internalFormat, dimensions.x, dimensions.y);
if (!_gpuObject.isMultisample()) {
for (GLint level = 0; level < numMips; level++) { for (GLint level = 0; level < numMips; level++) {
Vec3u dimensions = _gpuObject.evalMipDimensions(level); Vec3u dimensions = _gpuObject.evalMipDimensions(level);
for (GLenum target : getFaceTargets(_target)) { for (GLenum target : getFaceTargets(_target)) {
@ -230,6 +232,14 @@ void GL41FixedAllocationTexture::allocateStorage() const {
} }
} }
} }
} else {
const auto dimensions = _gpuObject.getDimensions();
if (!_gpuObject.isArray()) {
glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, numSamples, texelFormat.internalFormat, dimensions.x, dimensions.y, GL_FALSE);
} else {
glTexImage3DMultisample(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, numSamples, texelFormat.internalFormat, dimensions.x, dimensions.y, dimensions.z, GL_FALSE);
}
}
glTexParameteri(_target, GL_TEXTURE_BASE_LEVEL, 0); glTexParameteri(_target, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(_target, GL_TEXTURE_MAX_LEVEL, numMips - 1); glTexParameteri(_target, GL_TEXTURE_MAX_LEVEL, numMips - 1);

View file

@ -62,6 +62,8 @@ public:
if (gltexture) { if (gltexture) {
if (gltexture->_target == GL_TEXTURE_2D) { if (gltexture->_target == GL_TEXTURE_2D) {
glNamedFramebufferTexture(_id, colorAttachments[unit], gltexture->_texture, 0); glNamedFramebufferTexture(_id, colorAttachments[unit], gltexture->_texture, 0);
} else if (gltexture->_target == GL_TEXTURE_2D_MULTISAMPLE) {
glNamedFramebufferTexture(_id, colorAttachments[unit], gltexture->_texture, 0);
} else { } else {
glNamedFramebufferTextureLayer(_id, colorAttachments[unit], gltexture->_texture, 0, b._subresource); glNamedFramebufferTextureLayer(_id, colorAttachments[unit], gltexture->_texture, 0, b._subresource);
} }
@ -93,6 +95,9 @@ public:
if (gltexture) { if (gltexture) {
if (gltexture->_target == GL_TEXTURE_2D) { if (gltexture->_target == GL_TEXTURE_2D) {
glNamedFramebufferTexture(_id, attachement, gltexture->_texture, 0); glNamedFramebufferTexture(_id, attachement, gltexture->_texture, 0);
}
else if (gltexture->_target == GL_TEXTURE_2D_MULTISAMPLE) {
glNamedFramebufferTexture(_id, attachement, gltexture->_texture, 0);
} else { } else {
glNamedFramebufferTextureLayer(_id, attachement, gltexture->_texture, 0, glNamedFramebufferTextureLayer(_id, attachement, gltexture->_texture, 0,
_gpuObject.getDepthStencilBufferSubresource()); _gpuObject.getDepthStencilBufferSubresource());

View file

@ -380,12 +380,23 @@ void GL45FixedAllocationTexture::allocateStorage() const {
const auto dimensions = _gpuObject.getDimensions(); const auto dimensions = _gpuObject.getDimensions();
const auto mips = _gpuObject.getNumMips(); const auto mips = _gpuObject.getNumMips();
const auto numSlices = _gpuObject.getNumSlices(); const auto numSlices = _gpuObject.getNumSlices();
const auto numSamples = _gpuObject.getNumSamples();
if (!_gpuObject.isMultisample()) {
if (!_gpuObject.isArray()) { if (!_gpuObject.isArray()) {
glTextureStorage2D(_id, mips, texelFormat.internalFormat, dimensions.x, dimensions.y); glTextureStorage2D(_id, mips, texelFormat.internalFormat, dimensions.x, dimensions.y);
} else { } else {
glTextureStorage3D(_id, mips, texelFormat.internalFormat, dimensions.x, dimensions.y, numSlices); glTextureStorage3D(_id, mips, texelFormat.internalFormat, dimensions.x, dimensions.y, numSlices);
} }
} else {
if (!_gpuObject.isArray()) {
glTextureStorage2DMultisample(_id, numSamples, texelFormat.internalFormat, dimensions.x, dimensions.y, GL_FALSE);
}
else {
glTextureStorage3DMultisample(_id, numSamples, texelFormat.internalFormat, dimensions.x, dimensions.y, numSlices, GL_FALSE);
}
}
glTextureParameteri(_id, GL_TEXTURE_BASE_LEVEL, 0); glTextureParameteri(_id, GL_TEXTURE_BASE_LEVEL, 0);
glTextureParameteri(_id, GL_TEXTURE_MAX_LEVEL, mips - 1); glTextureParameteri(_id, GL_TEXTURE_MAX_LEVEL, mips - 1);

View file

@ -130,6 +130,8 @@ public:
} }
#endif #endif
} else if (gltexture->_target == GL_TEXTURE_2D_MULTISAMPLE) {
glFramebufferTexture2D(GL_FRAMEBUFFER, colorAttachments[unit], GL_TEXTURE_2D_MULTISAMPLE, gltexture->_texture, 0);
} else { } else {
glFramebufferTextureLayer(GL_FRAMEBUFFER, colorAttachments[unit], gltexture->_texture, 0, glFramebufferTextureLayer(GL_FRAMEBUFFER, colorAttachments[unit], gltexture->_texture, 0,
b._subresource); b._subresource);
@ -162,6 +164,8 @@ public:
if (gltexture) { if (gltexture) {
if (gltexture->_target == GL_TEXTURE_2D) { if (gltexture->_target == GL_TEXTURE_2D) {
glFramebufferTexture2D(GL_FRAMEBUFFER, attachement, GL_TEXTURE_2D, gltexture->_texture, 0); glFramebufferTexture2D(GL_FRAMEBUFFER, attachement, GL_TEXTURE_2D, gltexture->_texture, 0);
} else if (gltexture->_target == GL_TEXTURE_2D_MULTISAMPLE) {
glFramebufferTexture2D(GL_FRAMEBUFFER, attachement, GL_TEXTURE_2D_MULTISAMPLE, gltexture->_texture, 0);
} else { } else {
glFramebufferTextureLayer(GL_FRAMEBUFFER, attachement, gltexture->_texture, 0, glFramebufferTextureLayer(GL_FRAMEBUFFER, attachement, gltexture->_texture, 0,
_gpuObject.getDepthStencilBufferSubresource()); _gpuObject.getDepthStencilBufferSubresource());

View file

@ -272,8 +272,10 @@ void GLESFixedAllocationTexture::allocateStorage() const {
const GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat()); const GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat());
const auto numMips = _gpuObject.getNumMips(); const auto numMips = _gpuObject.getNumMips();
const auto numSlices = _gpuObject.getNumSlices(); const auto numSlices = _gpuObject.getNumSlices();
const auto numSamples = _gpuObject.getNumSamples();
// glTextureStorage2D(_id, mips, texelFormat.internalFormat, dimensions.x, dimensions.y); // glTextureStorage2D(_id, mips, texelFormat.internalFormat, dimensions.x, dimensions.y);
if (!_gpuObject.isMultisample()) {
for (GLint level = 0; level < numMips; level++) { for (GLint level = 0; level < numMips; level++) {
Vec3u dimensions = _gpuObject.evalMipDimensions(level); Vec3u dimensions = _gpuObject.evalMipDimensions(level);
for (GLenum target : getFaceTargets(_target)) { for (GLenum target : getFaceTargets(_target)) {
@ -295,6 +297,16 @@ void GLESFixedAllocationTexture::allocateStorage() const {
} }
} }
} }
} else {
const auto dimensions = _gpuObject.getDimensions();
if (!_gpuObject.isArray()) {
glTexStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, numSamples,
texelFormat.internalFormat, dimensions.x, dimensions.y,
GL_FALSE);
} else {
// NOT SUPPORTED (yet)
}
}
glTexParameteri(_target, GL_TEXTURE_BASE_LEVEL, 0); glTexParameteri(_target, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(_target, GL_TEXTURE_MAX_LEVEL, numMips - 1); glTexParameteri(_target, GL_TEXTURE_MAX_LEVEL, numMips - 1);

View file

@ -176,10 +176,18 @@ TexturePointer Texture::createRenderBuffer(const Element& texelFormat, uint16 wi
return create(TextureUsageType::RENDERBUFFER, TEX_2D, texelFormat, width, height, 1, 1, 0, numMips, sampler); return create(TextureUsageType::RENDERBUFFER, TEX_2D, texelFormat, width, height, 1, 1, 0, numMips, sampler);
} }
TexturePointer Texture::createRenderBufferMultisample(const Element& texelFormat, uint16 width, uint16 height, uint16 numSamples, const Sampler& sampler) {
return create(TextureUsageType::RENDERBUFFER, TEX_2D, texelFormat, width, height, 1, numSamples, 0, gpu::Texture::SINGLE_MIP, sampler);
}
TexturePointer Texture::createRenderBufferArray(const Element& texelFormat, uint16 width, uint16 height, uint16 numSlices, uint16 numMips, const Sampler& sampler) { TexturePointer Texture::createRenderBufferArray(const Element& texelFormat, uint16 width, uint16 height, uint16 numSlices, uint16 numMips, const Sampler& sampler) {
return create(TextureUsageType::RENDERBUFFER, TEX_2D, texelFormat, width, height, 1, 1, numSlices, numMips, sampler); return create(TextureUsageType::RENDERBUFFER, TEX_2D, texelFormat, width, height, 1, 1, numSlices, numMips, sampler);
} }
TexturePointer Texture::createRenderBufferMultisampleArray(const Element& texelFormat, uint16 width, uint16 height, uint16 numSlices, uint16 numSamples, const Sampler& sampler) {
return create(TextureUsageType::RENDERBUFFER, TEX_2D, texelFormat, width, height, 1, numSamples, numSlices, gpu::Texture::SINGLE_MIP, sampler);
}
TexturePointer Texture::create1D(const Element& texelFormat, uint16 width, uint16 numMips, const Sampler& sampler) { TexturePointer Texture::create1D(const Element& texelFormat, uint16 width, uint16 numMips, const Sampler& sampler) {
return create(TextureUsageType::RESOURCE, TEX_1D, texelFormat, width, 1, 1, 1, 0, numMips, sampler); return create(TextureUsageType::RESOURCE, TEX_1D, texelFormat, width, 1, 1, 1, 0, numMips, sampler);
} }

View file

@ -387,7 +387,9 @@ public:
static TexturePointer create3D(const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numMips = SINGLE_MIP, const Sampler& sampler = Sampler()); static TexturePointer create3D(const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numMips = SINGLE_MIP, const Sampler& sampler = Sampler());
static TexturePointer createCube(const Element& texelFormat, uint16 width, uint16 numMips = 1, const Sampler& sampler = Sampler()); static TexturePointer createCube(const Element& texelFormat, uint16 width, uint16 numMips = 1, const Sampler& sampler = Sampler());
static TexturePointer createRenderBuffer(const Element& texelFormat, uint16 width, uint16 height, uint16 numMips = SINGLE_MIP, const Sampler& sampler = Sampler()); static TexturePointer createRenderBuffer(const Element& texelFormat, uint16 width, uint16 height, uint16 numMips = SINGLE_MIP, const Sampler& sampler = Sampler());
static TexturePointer createRenderBufferMultisample(const Element& texelFormat, uint16 width, uint16 height, uint16 numSamples, const Sampler& sampler = Sampler());
static TexturePointer createRenderBufferArray(const Element& texelFormat, uint16 width, uint16 height, uint16 numSlices, uint16 numMips = SINGLE_MIP, const Sampler& sampler = Sampler()); static TexturePointer createRenderBufferArray(const Element& texelFormat, uint16 width, uint16 height, uint16 numSlices, uint16 numMips = SINGLE_MIP, const Sampler& sampler = Sampler());
static TexturePointer createRenderBufferMultisampleArray(const Element& texelFormat, uint16 width, uint16 height, uint16 numSlices, uint16 numSamples, const Sampler& sampler = Sampler());
static TexturePointer createStrict(const Element& texelFormat, uint16 width, uint16 height, uint16 numMips = SINGLE_MIP, const Sampler& sampler = Sampler()); static TexturePointer createStrict(const Element& texelFormat, uint16 width, uint16 height, uint16 numMips = SINGLE_MIP, const Sampler& sampler = Sampler());
static TexturePointer createExternal(const ExternalRecycler& recycler, const Sampler& sampler = Sampler()); static TexturePointer createExternal(const ExternalRecycler& recycler, const Sampler& sampler = Sampler());
@ -435,6 +437,7 @@ public:
uint16 getNumSamples() const { return _numSamples; } uint16 getNumSamples() const { return _numSamples; }
// NumSamples can only have certain values based on the hw // NumSamples can only have certain values based on the hw
static uint16 evalNumSamplesUsed(uint16 numSamplesTried); static uint16 evalNumSamplesUsed(uint16 numSamplesTried);
bool isMultisample() const { return _numSamples > 1; }
// max mip is in the range [ 0 if no sub mips, log2(max(width, height, depth))] // max mip is in the range [ 0 if no sub mips, log2(max(width, height, depth))]
// It is defined at creation time (immutable) // It is defined at creation time (immutable)

View file

@ -105,7 +105,7 @@ void Material::setMetallic(float metallic) {
void Material::setScattering(float scattering) { void Material::setScattering(float scattering) {
scattering = glm::clamp(scattering, 0.0f, 1.0f); scattering = glm::clamp(scattering, 0.0f, 1.0f);
_key.setMetallic(scattering > 0.0f); _key.setScattering(scattering > 0.0f);
_scattering = scattering; _scattering = scattering;
} }

View file

@ -417,9 +417,13 @@ MaterialCache& MaterialCache::instance() {
} }
NetworkMaterialResourcePointer MaterialCache::getMaterial(const QUrl& url) { NetworkMaterialResourcePointer MaterialCache::getMaterial(const QUrl& url) {
return ResourceCache::getResource(url, QUrl(), nullptr).staticCast<NetworkMaterialResource>(); return ResourceCache::getResource(url).staticCast<NetworkMaterialResource>();
} }
QSharedPointer<Resource> MaterialCache::createResource(const QUrl& url, const QSharedPointer<Resource>& fallback, const void* extra) { QSharedPointer<Resource> MaterialCache::createResource(const QUrl& url) {
return QSharedPointer<Resource>(new NetworkMaterialResource(url), &Resource::deleter); return QSharedPointer<Resource>(new NetworkMaterialResource(url), &Resource::deleter);
} }
QSharedPointer<Resource> MaterialCache::createResourceCopy(const QSharedPointer<Resource>& resource) {
return QSharedPointer<Resource>(new NetworkMaterialResource(*resource.staticCast<NetworkMaterialResource>().data()), &Resource::deleter);
}

View file

@ -53,7 +53,8 @@ public:
NetworkMaterialResourcePointer getMaterial(const QUrl& url); NetworkMaterialResourcePointer getMaterial(const QUrl& url);
protected: protected:
virtual QSharedPointer<Resource> createResource(const QUrl& url, const QSharedPointer<Resource>& fallback, const void* extra) override; virtual QSharedPointer<Resource> createResource(const QUrl& url) override;
QSharedPointer<Resource> createResourceCopy(const QSharedPointer<Resource>& resource) override;
}; };
#endif #endif

View file

@ -40,6 +40,50 @@ public:
bool combineParts; bool combineParts;
}; };
// From: https://stackoverflow.com/questions/41145012/how-to-hash-qvariant
class QVariantHasher {
public:
QVariantHasher() : buff(&bb), ds(&buff) {
bb.reserve(1000);
buff.open(QIODevice::WriteOnly);
}
uint hash(const QVariant& v) {
buff.seek(0);
ds << v;
return qHashBits(bb.constData(), buff.pos());
}
private:
QByteArray bb;
QBuffer buff;
QDataStream ds;
};
namespace std {
template <>
struct hash<QVariantHash> {
size_t operator()(const QVariantHash& a) const {
QVariantHasher hasher;
return hasher.hash(a);
}
};
template <>
struct hash<QUrl> {
size_t operator()(const QUrl& a) const {
return qHash(a);
}
};
template <>
struct hash<GeometryExtra> {
size_t operator()(const GeometryExtra& a) const {
size_t result = 0;
hash_combine(result, a.mapping, a.textureBaseUrl, a.combineParts);
return result;
}
};
}
QUrl resolveTextureBaseUrl(const QUrl& url, const QUrl& textureBaseUrl) { QUrl resolveTextureBaseUrl(const QUrl& url, const QUrl& textureBaseUrl) {
return textureBaseUrl.isValid() ? textureBaseUrl : url; return textureBaseUrl.isValid() ? textureBaseUrl : url;
} }
@ -110,7 +154,7 @@ void GeometryMappingResource::downloadFinished(const QByteArray& data) {
GeometryExtra extra { _mapping, _textureBaseUrl, false }; GeometryExtra extra { _mapping, _textureBaseUrl, false };
// Get the raw GeometryResource // Get the raw GeometryResource
_geometryResource = modelCache->getResource(url, QUrl(), &extra).staticCast<GeometryResource>(); _geometryResource = modelCache->getResource(url, QUrl(), &extra, std::hash<GeometryExtra>()(extra)).staticCast<GeometryResource>();
// Avoid caching nested resources - their references will be held by the parent // Avoid caching nested resources - their references will be held by the parent
_geometryResource->_isCacheable = false; _geometryResource->_isCacheable = false;
@ -253,13 +297,19 @@ void GeometryReader::run() {
class GeometryDefinitionResource : public GeometryResource { class GeometryDefinitionResource : public GeometryResource {
Q_OBJECT Q_OBJECT
public: public:
GeometryDefinitionResource(const ModelLoader& modelLoader, const QUrl& url, const QVariantHash& mapping, const QUrl& textureBaseUrl, bool combineParts) : GeometryDefinitionResource(const ModelLoader& modelLoader, const QUrl& url) : GeometryResource(url), _modelLoader(modelLoader) {}
GeometryResource(url, resolveTextureBaseUrl(url, textureBaseUrl)), _modelLoader(modelLoader), _mapping(mapping), _combineParts(combineParts) {} GeometryDefinitionResource(const GeometryDefinitionResource& other) :
GeometryResource(other),
_modelLoader(other._modelLoader),
_mapping(other._mapping),
_combineParts(other._combineParts) {}
QString getType() const override { return "GeometryDefinition"; } QString getType() const override { return "GeometryDefinition"; }
virtual void downloadFinished(const QByteArray& data) override; virtual void downloadFinished(const QByteArray& data) override;
void setExtra(void* extra) override;
protected: protected:
Q_INVOKABLE void setGeometryDefinition(HFMModel::Pointer hfmModel, QVariantHash mapping); Q_INVOKABLE void setGeometryDefinition(HFMModel::Pointer hfmModel, QVariantHash mapping);
@ -269,6 +319,13 @@ private:
bool _combineParts; bool _combineParts;
}; };
void GeometryDefinitionResource::setExtra(void* extra) {
const GeometryExtra* geometryExtra = static_cast<const GeometryExtra*>(extra);
_mapping = geometryExtra ? geometryExtra->mapping : QVariantHash();
_textureBaseUrl = resolveTextureBaseUrl(_url, geometryExtra ? geometryExtra->textureBaseUrl : QUrl());
_combineParts = geometryExtra ? geometryExtra->combineParts : true;
}
void GeometryDefinitionResource::downloadFinished(const QByteArray& data) { void GeometryDefinitionResource::downloadFinished(const QByteArray& data) {
if (_url != _effectiveBaseURL) { if (_url != _effectiveBaseURL) {
_url = _effectiveBaseURL; _url = _effectiveBaseURL;
@ -323,27 +380,26 @@ ModelCache::ModelCache() {
modelFormatRegistry->addFormat(GLTFSerializer()); modelFormatRegistry->addFormat(GLTFSerializer());
} }
QSharedPointer<Resource> ModelCache::createResource(const QUrl& url, const QSharedPointer<Resource>& fallback, QSharedPointer<Resource> ModelCache::createResource(const QUrl& url) {
const void* extra) {
Resource* resource = nullptr; Resource* resource = nullptr;
if (url.path().toLower().endsWith(".fst")) { if (url.path().toLower().endsWith(".fst")) {
resource = new GeometryMappingResource(url); resource = new GeometryMappingResource(url);
} else { } else {
const GeometryExtra* geometryExtra = static_cast<const GeometryExtra*>(extra); resource = new GeometryDefinitionResource(_modelLoader, url);
auto mapping = geometryExtra ? geometryExtra->mapping : QVariantHash();
auto textureBaseUrl = geometryExtra ? geometryExtra->textureBaseUrl : QUrl();
bool combineParts = geometryExtra ? geometryExtra->combineParts : true;
resource = new GeometryDefinitionResource(_modelLoader, url, mapping, textureBaseUrl, combineParts);
} }
return QSharedPointer<Resource>(resource, &Resource::deleter); return QSharedPointer<Resource>(resource, &Resource::deleter);
} }
QSharedPointer<Resource> ModelCache::createResourceCopy(const QSharedPointer<Resource>& resource) {
return QSharedPointer<Resource>(new GeometryDefinitionResource(*resource.staticCast<GeometryDefinitionResource>().data()), &Resource::deleter);
}
GeometryResource::Pointer ModelCache::getGeometryResource(const QUrl& url, GeometryResource::Pointer ModelCache::getGeometryResource(const QUrl& url,
const QVariantHash& mapping, const QUrl& textureBaseUrl) { const QVariantHash& mapping, const QUrl& textureBaseUrl) {
bool combineParts = true; bool combineParts = true;
GeometryExtra geometryExtra = { mapping, textureBaseUrl, combineParts }; GeometryExtra geometryExtra = { mapping, textureBaseUrl, combineParts };
GeometryResource::Pointer resource = getResource(url, QUrl(), &geometryExtra).staticCast<GeometryResource>(); GeometryResource::Pointer resource = getResource(url, QUrl(), &geometryExtra, std::hash<GeometryExtra>()(geometryExtra)).staticCast<GeometryResource>();
if (resource) { if (resource) {
if (resource->isLoaded() && resource->shouldSetTextures()) { if (resource->isLoaded() && resource->shouldSetTextures()) {
resource->setTextures(); resource->setTextures();
@ -356,7 +412,7 @@ GeometryResource::Pointer ModelCache::getCollisionGeometryResource(const QUrl& u
const QVariantHash& mapping, const QUrl& textureBaseUrl) { const QVariantHash& mapping, const QUrl& textureBaseUrl) {
bool combineParts = false; bool combineParts = false;
GeometryExtra geometryExtra = { mapping, textureBaseUrl, combineParts }; GeometryExtra geometryExtra = { mapping, textureBaseUrl, combineParts };
GeometryResource::Pointer resource = getResource(url, QUrl(), &geometryExtra).staticCast<GeometryResource>(); GeometryResource::Pointer resource = getResource(url, QUrl(), &geometryExtra, std::hash<GeometryExtra>()(geometryExtra)).staticCast<GeometryResource>();
if (resource) { if (resource) {
if (resource->isLoaded() && resource->shouldSetTextures()) { if (resource->isLoaded() && resource->shouldSetTextures()) {
resource->setTextures(); resource->setTextures();

View file

@ -82,8 +82,12 @@ class GeometryResource : public Resource, public Geometry {
public: public:
using Pointer = QSharedPointer<GeometryResource>; using Pointer = QSharedPointer<GeometryResource>;
GeometryResource(const QUrl& url, const QUrl& textureBaseUrl = QUrl()) : GeometryResource(const QUrl& url) : Resource(url) {}
Resource(url), _textureBaseUrl(textureBaseUrl) {} GeometryResource(const GeometryResource& other) :
Resource(other),
Geometry(other),
_textureBaseUrl(other._textureBaseUrl),
_isCacheable(other._isCacheable) {}
virtual bool areTexturesLoaded() const override { return isLoaded() && Geometry::areTexturesLoaded(); } virtual bool areTexturesLoaded() const override { return isLoaded() && Geometry::areTexturesLoaded(); }
@ -153,8 +157,8 @@ public:
protected: protected:
friend class GeometryMappingResource; friend class GeometryMappingResource;
virtual QSharedPointer<Resource> createResource(const QUrl& url, const QSharedPointer<Resource>& fallback, virtual QSharedPointer<Resource> createResource(const QUrl& url) override;
const void* extra) override; QSharedPointer<Resource> createResourceCopy(const QSharedPointer<Resource>& resource) override;
private: private:
ModelCache(); ModelCache();

View file

@ -21,11 +21,13 @@ ShaderCache& ShaderCache::instance() {
} }
NetworkShaderPointer ShaderCache::getShader(const QUrl& url) { NetworkShaderPointer ShaderCache::getShader(const QUrl& url) {
return ResourceCache::getResource(url, QUrl(), nullptr).staticCast<NetworkShader>(); return ResourceCache::getResource(url).staticCast<NetworkShader>();
} }
QSharedPointer<Resource> ShaderCache::createResource(const QUrl& url, const QSharedPointer<Resource>& fallback, QSharedPointer<Resource> ShaderCache::createResource(const QUrl& url) {
const void* extra) {
return QSharedPointer<Resource>(new NetworkShader(url), &Resource::deleter); return QSharedPointer<Resource>(new NetworkShader(url), &Resource::deleter);
} }
QSharedPointer<Resource> ShaderCache::createResourceCopy(const QSharedPointer<Resource>& resource) {
return QSharedPointer<Resource>(new NetworkShader(*resource.staticCast<NetworkShader>().data()), &Resource::deleter);
}

View file

@ -14,6 +14,7 @@
class NetworkShader : public Resource { class NetworkShader : public Resource {
public: public:
NetworkShader(const QUrl& url); NetworkShader(const QUrl& url);
NetworkShader(const NetworkShader& other) : Resource(other), _source(other._source) {}
QString getType() const override { return "NetworkShader"; } QString getType() const override { return "NetworkShader"; }
@ -31,8 +32,8 @@ public:
NetworkShaderPointer getShader(const QUrl& url); NetworkShaderPointer getShader(const QUrl& url);
protected: protected:
virtual QSharedPointer<Resource> createResource(const QUrl& url, const QSharedPointer<Resource>& fallback, virtual QSharedPointer<Resource> createResource(const QUrl& url) override;
const void* extra) override; QSharedPointer<Resource> createResourceCopy(const QSharedPointer<Resource>& resource) override;
}; };
#endif #endif

View file

@ -194,10 +194,28 @@ public:
int maxNumPixels; int maxNumPixels;
}; };
namespace std {
template <>
struct hash<QByteArray> {
size_t operator()(const QByteArray& a) const {
return qHash(a);
}
};
template <>
struct hash<TextureExtra> {
size_t operator()(const TextureExtra& a) const {
size_t result = 0;
hash_combine(result, (int)a.type, a.content, a.maxNumPixels);
return result;
}
};
}
ScriptableResource* TextureCache::prefetch(const QUrl& url, int type, int maxNumPixels) { ScriptableResource* TextureCache::prefetch(const QUrl& url, int type, int maxNumPixels) {
auto byteArray = QByteArray(); auto byteArray = QByteArray();
TextureExtra extra = { (image::TextureUsage::Type)type, byteArray, maxNumPixels }; TextureExtra extra = { (image::TextureUsage::Type)type, byteArray, maxNumPixels };
return ResourceCache::prefetch(url, &extra); return ResourceCache::prefetch(url, &extra, std::hash<TextureExtra>()(extra));
} }
NetworkTexturePointer TextureCache::getTexture(const QUrl& url, image::TextureUsage::Type type, const QByteArray& content, int maxNumPixels) { NetworkTexturePointer TextureCache::getTexture(const QUrl& url, image::TextureUsage::Type type, const QByteArray& content, int maxNumPixels) {
@ -211,7 +229,7 @@ NetworkTexturePointer TextureCache::getTexture(const QUrl& url, image::TextureUs
modifiedUrl.setQuery(query.toString()); modifiedUrl.setQuery(query.toString());
} }
TextureExtra extra = { type, content, maxNumPixels }; TextureExtra extra = { type, content, maxNumPixels };
return ResourceCache::getResource(modifiedUrl, QUrl(), &extra).staticCast<NetworkTexture>(); return ResourceCache::getResource(modifiedUrl, QUrl(), &extra, std::hash<TextureExtra>()(extra)).staticCast<NetworkTexture>();
} }
gpu::TexturePointer TextureCache::getTextureByHash(const std::string& hash) { gpu::TexturePointer TextureCache::getTextureByHash(const std::string& hash) {
@ -305,42 +323,52 @@ gpu::TexturePointer TextureCache::getImageTexture(const QString& path, image::Te
return gpu::TexturePointer(loader(std::move(image), path.toStdString(), shouldCompress, target, false)); return gpu::TexturePointer(loader(std::move(image), path.toStdString(), shouldCompress, target, false));
} }
QSharedPointer<Resource> TextureCache::createResource(const QUrl& url, const QSharedPointer<Resource>& fallback, QSharedPointer<Resource> TextureCache::createResource(const QUrl& url) {
const void* extra) { return QSharedPointer<Resource>(new NetworkTexture(url), &Resource::deleter);
const TextureExtra* textureExtra = static_cast<const TextureExtra*>(extra); }
auto type = textureExtra ? textureExtra->type : image::TextureUsage::DEFAULT_TEXTURE;
auto content = textureExtra ? textureExtra->content : QByteArray(); QSharedPointer<Resource> TextureCache::createResourceCopy(const QSharedPointer<Resource>& resource) {
auto maxNumPixels = textureExtra ? textureExtra->maxNumPixels : ABSOLUTE_MAX_TEXTURE_NUM_PIXELS; return QSharedPointer<Resource>(new NetworkTexture(*resource.staticCast<NetworkTexture>().data()), &Resource::deleter);
NetworkTexture* texture = new NetworkTexture(url, type, content, maxNumPixels);
return QSharedPointer<Resource>(texture, &Resource::deleter);
} }
int networkTexturePointerMetaTypeId = qRegisterMetaType<QWeakPointer<NetworkTexture>>(); int networkTexturePointerMetaTypeId = qRegisterMetaType<QWeakPointer<NetworkTexture>>();
NetworkTexture::NetworkTexture(const QUrl& url) : NetworkTexture::NetworkTexture(const QUrl& url, bool resourceTexture) :
Resource(url), Resource(url),
_type(),
_maxNumPixels(100) _maxNumPixels(100)
{ {
if (resourceTexture) {
_textureSource = std::make_shared<gpu::TextureSource>(url); _textureSource = std::make_shared<gpu::TextureSource>(url);
_lowestRequestedMipLevel = 0;
_loaded = true; _loaded = true;
} }
}
NetworkTexture::NetworkTexture(const NetworkTexture& other) :
Resource(other),
_type(other._type),
_currentlyLoadingResourceType(other._currentlyLoadingResourceType),
_originalWidth(other._originalWidth),
_originalHeight(other._originalHeight),
_width(other._width),
_height(other._height),
_maxNumPixels(other._maxNumPixels)
{
}
static bool isLocalUrl(const QUrl& url) { static bool isLocalUrl(const QUrl& url) {
auto scheme = url.scheme(); auto scheme = url.scheme();
return (scheme == HIFI_URL_SCHEME_FILE || scheme == URL_SCHEME_QRC || scheme == RESOURCE_SCHEME); return (scheme == HIFI_URL_SCHEME_FILE || scheme == URL_SCHEME_QRC || scheme == RESOURCE_SCHEME);
} }
NetworkTexture::NetworkTexture(const QUrl& url, image::TextureUsage::Type type, const QByteArray& content, int maxNumPixels) : void NetworkTexture::setExtra(void* extra) {
Resource(url), const TextureExtra* textureExtra = static_cast<const TextureExtra*>(extra);
_type(type), _type = textureExtra ? textureExtra->type : image::TextureUsage::DEFAULT_TEXTURE;
_maxNumPixels(maxNumPixels) _maxNumPixels = textureExtra ? textureExtra->maxNumPixels : ABSOLUTE_MAX_TEXTURE_NUM_PIXELS;
{
_textureSource = std::make_shared<gpu::TextureSource>(url, (int)type); _textureSource = std::make_shared<gpu::TextureSource>(_url, (int)_type);
_lowestRequestedMipLevel = 0; _lowestRequestedMipLevel = 0;
auto fileNameLowercase = url.fileName().toLower(); auto fileNameLowercase = _url.fileName().toLower();
if (fileNameLowercase.endsWith(TEXTURE_META_EXTENSION)) { if (fileNameLowercase.endsWith(TEXTURE_META_EXTENSION)) {
_currentlyLoadingResourceType = ResourceType::META; _currentlyLoadingResourceType = ResourceType::META;
} else if (fileNameLowercase.endsWith(".ktx")) { } else if (fileNameLowercase.endsWith(".ktx")) {
@ -351,17 +379,18 @@ NetworkTexture::NetworkTexture(const QUrl& url, image::TextureUsage::Type type,
_shouldFailOnRedirect = _currentlyLoadingResourceType != ResourceType::KTX; _shouldFailOnRedirect = _currentlyLoadingResourceType != ResourceType::KTX;
if (type == image::TextureUsage::CUBE_TEXTURE) { if (_type == image::TextureUsage::CUBE_TEXTURE) {
setLoadPriority(this, SKYBOX_LOAD_PRIORITY); setLoadPriority(this, SKYBOX_LOAD_PRIORITY);
} else if (_currentlyLoadingResourceType == ResourceType::KTX) { } else if (_currentlyLoadingResourceType == ResourceType::KTX) {
setLoadPriority(this, HIGH_MIPS_LOAD_PRIORITY); setLoadPriority(this, HIGH_MIPS_LOAD_PRIORITY);
} }
if (!url.isValid()) { if (!_url.isValid()) {
_loaded = true; _loaded = true;
} }
// if we have content, load it after we have our self pointer // if we have content, load it after we have our self pointer
auto content = textureExtra ? textureExtra->content : QByteArray();
if (!content.isEmpty()) { if (!content.isEmpty()) {
_startedLoading = true; _startedLoading = true;
QMetaObject::invokeMethod(this, "downloadFinished", Qt::QueuedConnection, Q_ARG(const QByteArray&, content)); QMetaObject::invokeMethod(this, "downloadFinished", Qt::QueuedConnection, Q_ARG(const QByteArray&, content));
@ -396,7 +425,7 @@ gpu::TexturePointer NetworkTexture::getFallbackTexture() const {
class ImageReader : public QRunnable { class ImageReader : public QRunnable {
public: public:
ImageReader(const QWeakPointer<Resource>& resource, const QUrl& url, ImageReader(const QWeakPointer<Resource>& resource, const QUrl& url,
const QByteArray& data, int maxNumPixels); const QByteArray& data, size_t extraHash, int maxNumPixels);
void run() override final; void run() override final;
void read(); void read();
@ -406,6 +435,7 @@ private:
QWeakPointer<Resource> _resource; QWeakPointer<Resource> _resource;
QUrl _url; QUrl _url;
QByteArray _content; QByteArray _content;
size_t _extraHash;
int _maxNumPixels; int _maxNumPixels;
}; };
@ -1039,7 +1069,7 @@ void NetworkTexture::loadTextureContent(const QByteArray& content) {
return; return;
} }
QThreadPool::globalInstance()->start(new ImageReader(_self, _url, content, _maxNumPixels)); QThreadPool::globalInstance()->start(new ImageReader(_self, _url, content, _extraHash, _maxNumPixels));
} }
void NetworkTexture::refresh() { void NetworkTexture::refresh() {
@ -1064,10 +1094,11 @@ void NetworkTexture::refresh() {
Resource::refresh(); Resource::refresh();
} }
ImageReader::ImageReader(const QWeakPointer<Resource>& resource, const QUrl& url, const QByteArray& data, int maxNumPixels) : ImageReader::ImageReader(const QWeakPointer<Resource>& resource, const QUrl& url, const QByteArray& data, size_t extraHash, int maxNumPixels) :
_resource(resource), _resource(resource),
_url(url), _url(url),
_content(data), _content(data),
_extraHash(extraHash),
_maxNumPixels(maxNumPixels) _maxNumPixels(maxNumPixels)
{ {
DependencyManager::get<StatTracker>()->incrementStat("PendingProcessing"); DependencyManager::get<StatTracker>()->incrementStat("PendingProcessing");
@ -1123,11 +1154,12 @@ void ImageReader::read() {
} }
auto networkTexture = resource.staticCast<NetworkTexture>(); auto networkTexture = resource.staticCast<NetworkTexture>();
// Hash the source image to for KTX caching // Hash the source image and extraHash for KTX caching
std::string hash; std::string hash;
{ {
QCryptographicHash hasher(QCryptographicHash::Md5); QCryptographicHash hasher(QCryptographicHash::Md5);
hasher.addData(_content); hasher.addData(_content);
hasher.addData(std::to_string(_extraHash).c_str());
hash = hasher.result().toHex().toStdString(); hash = hasher.result().toHex().toStdString();
} }
@ -1216,11 +1248,11 @@ void ImageReader::read() {
Q_ARG(int, texture->getHeight())); Q_ARG(int, texture->getHeight()));
} }
NetworkTexturePointer TextureCache::getResourceTexture(QUrl resourceTextureUrl) { NetworkTexturePointer TextureCache::getResourceTexture(const QUrl& resourceTextureUrl) {
gpu::TexturePointer texture; gpu::TexturePointer texture;
if (resourceTextureUrl == SPECTATOR_CAMERA_FRAME_URL) { if (resourceTextureUrl == SPECTATOR_CAMERA_FRAME_URL) {
if (!_spectatorCameraNetworkTexture) { if (!_spectatorCameraNetworkTexture) {
_spectatorCameraNetworkTexture.reset(new NetworkTexture(resourceTextureUrl)); _spectatorCameraNetworkTexture.reset(new NetworkTexture(resourceTextureUrl, true));
} }
if (!_spectatorCameraFramebuffer) { if (!_spectatorCameraFramebuffer) {
getSpectatorCameraFramebuffer(); // initialize frame buffer getSpectatorCameraFramebuffer(); // initialize frame buffer
@ -1231,7 +1263,7 @@ NetworkTexturePointer TextureCache::getResourceTexture(QUrl resourceTextureUrl)
// FIXME: Generalize this, DRY up this code // FIXME: Generalize this, DRY up this code
if (resourceTextureUrl == HMD_PREVIEW_FRAME_URL) { if (resourceTextureUrl == HMD_PREVIEW_FRAME_URL) {
if (!_hmdPreviewNetworkTexture) { if (!_hmdPreviewNetworkTexture) {
_hmdPreviewNetworkTexture.reset(new NetworkTexture(resourceTextureUrl)); _hmdPreviewNetworkTexture.reset(new NetworkTexture(resourceTextureUrl, true));
} }
if (_hmdPreviewFramebuffer) { if (_hmdPreviewFramebuffer) {
texture = _hmdPreviewFramebuffer->getRenderBuffer(0); texture = _hmdPreviewFramebuffer->getRenderBuffer(0);

View file

@ -45,8 +45,8 @@ class NetworkTexture : public Resource, public Texture {
Q_OBJECT Q_OBJECT
public: public:
NetworkTexture(const QUrl& url); NetworkTexture(const QUrl& url, bool resourceTexture = false);
NetworkTexture(const QUrl& url, image::TextureUsage::Type type, const QByteArray& content, int maxNumPixels); NetworkTexture(const NetworkTexture& other);
~NetworkTexture() override; ~NetworkTexture() override;
QString getType() const override { return "NetworkTexture"; } QString getType() const override { return "NetworkTexture"; }
@ -63,6 +63,8 @@ public:
Q_INVOKABLE void setOriginalDescriptor(ktx::KTXDescriptor* descriptor) { _originalKtxDescriptor.reset(descriptor); } Q_INVOKABLE void setOriginalDescriptor(ktx::KTXDescriptor* descriptor) { _originalKtxDescriptor.reset(descriptor); }
void setExtra(void* extra) override;
signals: signals:
void networkTextureCreated(const QWeakPointer<NetworkTexture>& self); void networkTextureCreated(const QWeakPointer<NetworkTexture>& self);
@ -181,7 +183,7 @@ public:
gpu::TexturePointer getTextureByHash(const std::string& hash); gpu::TexturePointer getTextureByHash(const std::string& hash);
gpu::TexturePointer cacheTextureByHash(const std::string& hash, const gpu::TexturePointer& texture); gpu::TexturePointer cacheTextureByHash(const std::string& hash, const gpu::TexturePointer& texture);
NetworkTexturePointer getResourceTexture(QUrl resourceTextureUrl); NetworkTexturePointer getResourceTexture(const QUrl& resourceTextureUrl);
const gpu::FramebufferPointer& getHmdPreviewFramebuffer(int width, int height); const gpu::FramebufferPointer& getHmdPreviewFramebuffer(int width, int height);
const gpu::FramebufferPointer& getSpectatorCameraFramebuffer(); const gpu::FramebufferPointer& getSpectatorCameraFramebuffer();
const gpu::FramebufferPointer& getSpectatorCameraFramebuffer(int width, int height); const gpu::FramebufferPointer& getSpectatorCameraFramebuffer(int width, int height);
@ -201,8 +203,8 @@ protected:
// Overload ResourceCache::prefetch to allow specifying texture type for loads // Overload ResourceCache::prefetch to allow specifying texture type for loads
Q_INVOKABLE ScriptableResource* prefetch(const QUrl& url, int type, int maxNumPixels = ABSOLUTE_MAX_TEXTURE_NUM_PIXELS); Q_INVOKABLE ScriptableResource* prefetch(const QUrl& url, int type, int maxNumPixels = ABSOLUTE_MAX_TEXTURE_NUM_PIXELS);
virtual QSharedPointer<Resource> createResource(const QUrl& url, const QSharedPointer<Resource>& fallback, virtual QSharedPointer<Resource> createResource(const QUrl& url) override;
const void* extra) override; QSharedPointer<Resource> createResourceCopy(const QSharedPointer<Resource>& resource) override;
private: private:
friend class ImageReader; friend class ImageReader;

View file

@ -158,8 +158,8 @@ void ScriptableResourceCache::updateTotalSize(const qint64& deltaSize) {
_resourceCache->updateTotalSize(deltaSize); _resourceCache->updateTotalSize(deltaSize);
} }
ScriptableResource* ScriptableResourceCache::prefetch(const QUrl& url, void* extra) { ScriptableResource* ScriptableResourceCache::prefetch(const QUrl& url, void* extra, size_t extraHash) {
return _resourceCache->prefetch(url, extra); return _resourceCache->prefetch(url, extra, extraHash);
} }
@ -211,20 +211,20 @@ void ScriptableResource::disconnectHelper() {
} }
} }
ScriptableResource* ResourceCache::prefetch(const QUrl& url, void* extra) { ScriptableResource* ResourceCache::prefetch(const QUrl& url, void* extra, size_t extraHash) {
ScriptableResource* result = nullptr; ScriptableResource* result = nullptr;
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
// Must be called in thread to ensure getResource returns a valid pointer // Must be called in thread to ensure getResource returns a valid pointer
BLOCKING_INVOKE_METHOD(this, "prefetch", BLOCKING_INVOKE_METHOD(this, "prefetch",
Q_RETURN_ARG(ScriptableResource*, result), Q_RETURN_ARG(ScriptableResource*, result),
Q_ARG(QUrl, url), Q_ARG(void*, extra)); Q_ARG(QUrl, url), Q_ARG(void*, extra), Q_ARG(size_t, extraHash));
return result; return result;
} }
result = new ScriptableResource(url); result = new ScriptableResource(url);
auto resource = getResource(url, QUrl(), extra); auto resource = getResource(url, QUrl(), extra, extraHash);
result->_resource = resource; result->_resource = resource;
result->setObjectName(url.toString()); result->setObjectName(url.toString());
@ -265,12 +265,12 @@ ResourceCache::~ResourceCache() {
void ResourceCache::clearATPAssets() { void ResourceCache::clearATPAssets() {
{ {
QWriteLocker locker(&_resourcesLock); QWriteLocker locker(&_resourcesLock);
for (auto& url : _resources.keys()) { QList<QUrl> urls = _resources.keys();
for (auto& url : urls) {
// If this is an ATP resource // If this is an ATP resource
if (url.scheme() == URL_SCHEME_ATP) { if (url.scheme() == URL_SCHEME_ATP) {
auto resourcesWithExtraHash = _resources.take(url);
// Remove it from the resource hash for (auto& resource : resourcesWithExtraHash) {
auto resource = _resources.take(url);
if (auto strongRef = resource.lock()) { if (auto strongRef = resource.lock()) {
// Make sure the resource won't reinsert itself // Make sure the resource won't reinsert itself
strongRef->setCache(nullptr); strongRef->setCache(nullptr);
@ -279,6 +279,7 @@ void ResourceCache::clearATPAssets() {
} }
} }
} }
}
{ {
QWriteLocker locker(&_unusedResourcesLock); QWriteLocker locker(&_unusedResourcesLock);
for (auto& resource : _unusedResources.values()) { for (auto& resource : _unusedResources.values()) {
@ -297,19 +298,23 @@ void ResourceCache::refreshAll() {
clearUnusedResources(); clearUnusedResources();
resetUnusedResourceCounter(); resetUnusedResourceCounter();
QHash<QUrl, QWeakPointer<Resource>> resources; QHash<QUrl, QHash<size_t, QWeakPointer<Resource>>> allResources;
{ {
QReadLocker locker(&_resourcesLock); QReadLocker locker(&_resourcesLock);
resources = _resources; allResources = _resources;
} }
// Refresh all remaining resources in use // Refresh all remaining resources in use
foreach (QSharedPointer<Resource> resource, resources) { // FIXME: this will trigger multiple refreshes for the same resource if they have different hashes
for (auto& resourcesWithExtraHash : allResources) {
for (auto& resourceWeak : resourcesWithExtraHash) {
auto resource = resourceWeak.lock();
if (resource) { if (resource) {
resource->refresh(); resource->refresh();
} }
} }
} }
}
QVariantList ResourceCache::getResourceList() { QVariantList ResourceCache::getResourceList() {
QVariantList list; QVariantList list;
@ -338,39 +343,54 @@ void ResourceCache::setRequestLimit(uint32_t limit) {
} }
} }
QSharedPointer<Resource> ResourceCache::getResource(const QUrl& url, const QUrl& fallback, void* extra) { QSharedPointer<Resource> ResourceCache::getResource(const QUrl& url, const QUrl& fallback, void* extra, size_t extraHash) {
QSharedPointer<Resource> resource; QSharedPointer<Resource> resource;
{ {
QReadLocker locker(&_resourcesLock); QReadLocker locker(&_resourcesLock);
resource = _resources.value(url).lock(); auto& resourcesWithExtraHash = _resources[url];
auto resourcesWithExtraHashIter = resourcesWithExtraHash.find(extraHash);
if (resourcesWithExtraHashIter != resourcesWithExtraHash.end()) {
// We've seen this extra info before
resource = resourcesWithExtraHashIter.value().lock();
} else if (resourcesWithExtraHash.size() > 0.0f) {
// We haven't seen this extra info before, but we've already downloaded the resource. We need a new copy of this object (with any old hash).
resource = createResourceCopy(resourcesWithExtraHash.begin().value().lock());
resource->setExtra(extra);
resource->setExtraHash(extraHash);
resource->setSelf(resource);
resource->setCache(this);
resource->moveToThread(qApp->thread());
connect(resource.data(), &Resource::updateSize, this, &ResourceCache::updateTotalSize);
resourcesWithExtraHash.insert(extraHash, resource);
removeUnusedResource(resource);
resource->ensureLoading();
}
} }
if (resource) { if (resource) {
removeUnusedResource(resource); removeUnusedResource(resource);
} }
if (!resource && !url.isValid() && !url.isEmpty() && fallback.isValid()) { if (!resource && (!url.isValid() || url.isEmpty()) && fallback.isValid()) {
resource = getResource(fallback, QUrl()); resource = getResource(fallback, QUrl(), extra, extraHash);
} }
if (!resource) { if (!resource) {
resource = createResource( resource = createResource(url);
url, resource->setExtra(extra);
fallback.isValid() ? getResource(fallback, QUrl()) : QSharedPointer<Resource>(), resource->setExtraHash(extraHash);
extra);
resource->setSelf(resource); resource->setSelf(resource);
resource->setCache(this); resource->setCache(this);
resource->moveToThread(qApp->thread()); resource->moveToThread(qApp->thread());
connect(resource.data(), &Resource::updateSize, this, &ResourceCache::updateTotalSize); connect(resource.data(), &Resource::updateSize, this, &ResourceCache::updateTotalSize);
{ {
QWriteLocker locker(&_resourcesLock); QWriteLocker locker(&_resourcesLock);
_resources.insert(url, resource); _resources[url].insert(extraHash, resource);
} }
removeUnusedResource(resource); removeUnusedResource(resource);
resource->ensureLoading(); resource->ensureLoading();
} }
DependencyManager::get<ResourceRequestObserver>()->update( DependencyManager::get<ResourceRequestObserver>()->update(resource->getURL(), -1, "ResourceCache::getResource");
resource->getURL(), -1, "ResourceCache::getResource");
return resource; return resource;
} }
@ -527,6 +547,27 @@ bool ResourceCache::attemptHighestPriorityRequest() {
static int requestID = 0; static int requestID = 0;
Resource::Resource(const Resource& other) :
QObject(),
_url(other._url),
_effectiveBaseURL(other._effectiveBaseURL),
_activeUrl(other._activeUrl),
_requestByteRange(other._requestByteRange),
_shouldFailOnRedirect(other._shouldFailOnRedirect),
_startedLoading(other._startedLoading),
_failedToLoad(other._failedToLoad),
_loaded(other._loaded),
_loadPriorities(other._loadPriorities),
_bytesReceived(other._bytesReceived),
_bytesTotal(other._bytesTotal),
_bytes(other._bytes),
_requestID(++requestID),
_extraHash(other._extraHash) {
if (!other._loaded) {
_startedLoading = false;
}
}
Resource::Resource(const QUrl& url) : Resource::Resource(const QUrl& url) :
_url(url), _url(url),
_activeUrl(url), _activeUrl(url),
@ -678,7 +719,7 @@ void Resource::setSize(const qint64& bytes) {
void Resource::reinsert() { void Resource::reinsert() {
QWriteLocker locker(&_cache->_resourcesLock); QWriteLocker locker(&_cache->_resourcesLock);
_cache->_resources.insert(_url, _self); _cache->_resources[_url].insert(_extraHash, _self);
} }

View file

@ -231,16 +231,16 @@ protected slots:
// Prefetches a resource to be held by the QScriptEngine. // Prefetches a resource to be held by the QScriptEngine.
// Left as a protected member so subclasses can overload prefetch // Left as a protected member so subclasses can overload prefetch
// and delegate to it (see TextureCache::prefetch(const QUrl&, int). // and delegate to it (see TextureCache::prefetch(const QUrl&, int).
ScriptableResource* prefetch(const QUrl& url, void* extra); ScriptableResource* prefetch(const QUrl& url, void* extra, size_t extraHash);
// FIXME: The return type is not recognized by JavaScript. // FIXME: The return type is not recognized by JavaScript.
/// Loads a resource from the specified URL and returns it. /// Loads a resource from the specified URL and returns it.
/// If the caller is on a different thread than the ResourceCache, /// If the caller is on a different thread than the ResourceCache,
/// returns an empty smart pointer and loads its asynchronously. /// returns an empty smart pointer and loads its asynchronously.
/// \param fallback a fallback URL to load if the desired one is unavailable /// \param fallback a fallback URL to load if the desired one is unavailable
/// \param extra extra data to pass to the creator, if appropriate // FIXME: std::numeric_limits<size_t>::max() could be a valid extraHash
QSharedPointer<Resource> getResource(const QUrl& url, const QUrl& fallback = QUrl(), QSharedPointer<Resource> getResource(const QUrl& url, const QUrl& fallback = QUrl()) { return getResource(url, fallback, nullptr, std::numeric_limits<size_t>::max()); }
void* extra = NULL); QSharedPointer<Resource> getResource(const QUrl& url, const QUrl& fallback, void* extra, size_t extraHash);
private slots: private slots:
void clearATPAssets(); void clearATPAssets();
@ -251,11 +251,11 @@ protected:
// which should be a QScriptEngine with ScriptableResource registered, so that // which should be a QScriptEngine with ScriptableResource registered, so that
// the QScriptEngine will delete the pointer when it is garbage collected. // the QScriptEngine will delete the pointer when it is garbage collected.
// JSDoc is provided on more general function signature. // JSDoc is provided on more general function signature.
Q_INVOKABLE ScriptableResource* prefetch(const QUrl& url) { return prefetch(url, nullptr); } Q_INVOKABLE ScriptableResource* prefetch(const QUrl& url) { return prefetch(url, nullptr, std::numeric_limits<size_t>::max()); }
/// Creates a new resource. /// Creates a new resource.
virtual QSharedPointer<Resource> createResource(const QUrl& url, const QSharedPointer<Resource>& fallback, virtual QSharedPointer<Resource> createResource(const QUrl& url) = 0;
const void* extra) = 0; virtual QSharedPointer<Resource> createResourceCopy(const QSharedPointer<Resource>& resource) = 0;
void addUnusedResource(const QSharedPointer<Resource>& resource); void addUnusedResource(const QSharedPointer<Resource>& resource);
void removeUnusedResource(const QSharedPointer<Resource>& resource); void removeUnusedResource(const QSharedPointer<Resource>& resource);
@ -278,7 +278,7 @@ private:
void resetResourceCounters(); void resetResourceCounters();
// Resources // Resources
QHash<QUrl, QWeakPointer<Resource>> _resources; QHash<QUrl, QHash<size_t, QWeakPointer<Resource>>> _resources;
QReadWriteLock _resourcesLock { QReadWriteLock::Recursive }; QReadWriteLock _resourcesLock { QReadWriteLock::Recursive };
int _lastLRUKey = 0; int _lastLRUKey = 0;
@ -332,10 +332,10 @@ public:
* Prefetches a resource. * Prefetches a resource.
* @function ResourceCache.prefetch * @function ResourceCache.prefetch
* @param {string} url - URL of the resource to prefetch. * @param {string} url - URL of the resource to prefetch.
* @param {object} [extra=null]
* @returns {ResourceObject} * @returns {ResourceObject}
*/ */
Q_INVOKABLE ScriptableResource* prefetch(const QUrl& url, void* extra = nullptr); Q_INVOKABLE ScriptableResource* prefetch(const QUrl& url) { return prefetch(url, nullptr, std::numeric_limits<size_t>::max()); }
Q_INVOKABLE ScriptableResource* prefetch(const QUrl& url, void* extra, size_t extraHash);
signals: signals:
@ -360,6 +360,7 @@ class Resource : public QObject {
public: public:
Resource(const Resource& other);
Resource(const QUrl& url); Resource(const QUrl& url);
virtual ~Resource(); virtual ~Resource();
@ -415,6 +416,9 @@ public:
unsigned int getDownloadAttempts() { return _attempts; } unsigned int getDownloadAttempts() { return _attempts; }
unsigned int getDownloadAttemptsRemaining() { return _attemptsRemaining; } unsigned int getDownloadAttemptsRemaining() { return _attemptsRemaining; }
virtual void setExtra(void* extra) {};
void setExtraHash(size_t extraHash) { _extraHash = extraHash; }
signals: signals:
/// Fired when the resource begins downloading. /// Fired when the resource begins downloading.
void loading(); void loading();
@ -492,6 +496,8 @@ protected:
int _requestID; int _requestID;
ResourceRequest* _request{ nullptr }; ResourceRequest* _request{ nullptr };
size_t _extraHash;
public slots: public slots:
void handleDownloadProgress(uint64_t bytesReceived, uint64_t bytesTotal); void handleDownloadProgress(uint64_t bytesReceived, uint64_t bytesTotal);
void handleReplyFinished(); void handleReplyFinished();

View file

@ -48,8 +48,11 @@ NetworkClipLoaderPointer ClipCache::getClipLoader(const QUrl& url) {
return getResource(url).staticCast<NetworkClipLoader>(); return getResource(url).staticCast<NetworkClipLoader>();
} }
QSharedPointer<Resource> ClipCache::createResource(const QUrl& url, const QSharedPointer<Resource>& fallback, const void* extra) { QSharedPointer<Resource> ClipCache::createResource(const QUrl& url) {
qCDebug(recordingLog) << "Loading recording at" << url; qCDebug(recordingLog) << "Loading recording at" << url;
return QSharedPointer<Resource>(new NetworkClipLoader(url), &Resource::deleter); return QSharedPointer<Resource>(new NetworkClipLoader(url), &Resource::deleter);
} }
QSharedPointer<Resource> ClipCache::createResourceCopy(const QSharedPointer<Resource>& resource) {
return QSharedPointer<Resource>(new NetworkClipLoader(*resource.staticCast<NetworkClipLoader>().data()), &Resource::deleter);
}

View file

@ -33,6 +33,8 @@ class NetworkClipLoader : public Resource {
Q_OBJECT Q_OBJECT
public: public:
NetworkClipLoader(const QUrl& url); NetworkClipLoader(const QUrl& url);
NetworkClipLoader(const NetworkClipLoader& other) : Resource(other), _clip(other._clip) {}
virtual void downloadFinished(const QByteArray& data) override; virtual void downloadFinished(const QByteArray& data) override;
ClipPointer getClip() { return _clip; } ClipPointer getClip() { return _clip; }
bool completed() { return _failedToLoad || isLoaded(); } bool completed() { return _failedToLoad || isLoaded(); }
@ -54,7 +56,8 @@ public slots:
NetworkClipLoaderPointer getClipLoader(const QUrl& url); NetworkClipLoaderPointer getClipLoader(const QUrl& url);
protected: protected:
virtual QSharedPointer<Resource> createResource(const QUrl& url, const QSharedPointer<Resource>& fallback, const void* extra) override; virtual QSharedPointer<Resource> createResource(const QUrl& url) override;
QSharedPointer<Resource> createResourceCopy(const QSharedPointer<Resource>& resource) override;
private: private:
ClipCache(QObject* parent = nullptr); ClipCache(QObject* parent = nullptr);

View file

@ -579,7 +579,7 @@ void RenderPipelines::updateMultiMaterial(graphics::MultiMaterial& multiMaterial
} else { } else {
forceDefault = true; forceDefault = true;
} }
schemaKey.setScattering(true); schemaKey.setScatteringMap(true);
} }
break; break;
case graphics::MaterialKey::EMISSIVE_MAP_BIT: case graphics::MaterialKey::EMISSIVE_MAP_BIT:

View file

@ -123,7 +123,6 @@ Rectangle {
anchors.right: parent.right anchors.right: parent.right
} }
} }
Item { Item {
height: childrenRect.height height: childrenRect.height
anchors.left: parent.left anchors.left: parent.left
@ -134,18 +133,17 @@ Rectangle {
anchors.left: parent.left anchors.left: parent.left
} }
HifiControls.ComboBox { ComboBox {
anchors.right: parent.right anchors.right: parent.right
currentIndex: 1 currentIndex: 1
model: ListModel { model: [
id: cbItems "RGB",
ListElement { text: "RGB"; color: "Yellow" } "SRGB",
ListElement { text: "SRGB"; color: "Green" } "Reinhard",
ListElement { text: "Reinhard"; color: "Yellow" } "Filmic",
ListElement { text: "Filmic"; color: "White" } ]
}
width: 200 width: 200
onCurrentIndexChanged: { render.mainViewTask.getConfig("ToneMapping")["curve"] = currentIndex } onCurrentIndexChanged: { render.mainViewTask.getConfig("ToneMapping")["curve"] = currentIndex; }
} }
} }
} }
@ -170,7 +168,7 @@ Rectangle {
framebuffer.config.mode = mode; framebuffer.config.mode = mode;
} }
HifiControls.ComboBox { ComboBox {
anchors.right: parent.right anchors.right: parent.right
currentIndex: 0 currentIndex: 0
model: ListModel { model: ListModel {

View file

@ -2328,7 +2328,7 @@ function createTextureProperty(property, elProperty) {
elInput.setAttribute("id", elementID); elInput.setAttribute("id", elementID);
elInput.setAttribute("type", "text"); elInput.setAttribute("type", "text");
let imageLoad = _.debounce(function (url) { let imageLoad = function(url) {
if (url.slice(0, 5).toLowerCase() === "atp:/") { if (url.slice(0, 5).toLowerCase() === "atp:/") {
elImage.src = ""; elImage.src = "";
elImage.style.display = "none"; elImage.style.display = "none";
@ -2348,15 +2348,12 @@ function createTextureProperty(property, elProperty) {
elDiv.classList.remove("no-preview"); elDiv.classList.remove("no-preview");
elDiv.classList.add("no-texture"); elDiv.classList.add("no-texture");
} }
}, IMAGE_DEBOUNCE_TIMEOUT);
elInput.imageLoad = imageLoad;
elInput.oninput = function (event) {
// Add throttle
let url = event.target.value;
imageLoad(url);
updateProperty(property.name, url, property.isParticleProperty)
}; };
elInput.onchange = elInput.oninput; elInput.imageLoad = imageLoad;
elInput.addEventListener('change', createEmitTextPropertyUpdateFunction(property));
elInput.addEventListener('change', function(ev) {
imageLoad(ev.target.value);
});
elProperty.appendChild(elInput); elProperty.appendChild(elInput);
elProperty.appendChild(elDiv); elProperty.appendChild(elDiv);