From fe61d92a09c5fa4454e33da8073da99e13da7317 Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Fri, 12 Jul 2024 18:09:17 -0700 Subject: [PATCH] model loading priority updates over time, takes into account out of bounds, avatar entities have higher priority, and fsts can specify to wait for wearables to load before rendering --- interface/resources/qml/Stats.qml | 6 +- interface/src/Application.cpp | 33 ++++++++--- interface/src/avatar/MyAvatar.cpp | 2 +- interface/src/avatar/OtherAvatar.cpp | 2 +- .../AccountServicesScriptingInterface.cpp | 4 +- interface/src/ui/Stats.cpp | 32 +++++++---- interface/src/ui/Stats.h | 39 ++++++++++--- libraries/animation/src/AnimNodeLoader.cpp | 2 +- libraries/audio/src/SoundCache.cpp | 2 +- .../src/avatars-renderer/Avatar.cpp | 25 ++++++++- .../src/avatars-renderer/Avatar.h | 8 ++- .../src/RenderableModelEntityItem.cpp | 2 +- .../src/material-networking/TextureCache.cpp | 6 +- .../src/model-networking/ModelCache.cpp | 4 +- .../src/model-networking/ModelCache.h | 2 + libraries/model-serializers/src/FSTReader.cpp | 2 +- libraries/model-serializers/src/FSTReader.h | 1 + libraries/networking/src/ResourceCache.cpp | 55 ++++++++----------- libraries/networking/src/ResourceCache.h | 18 ++---- libraries/render-utils/src/Model.cpp | 2 +- libraries/render-utils/src/Model.h | 4 +- 21 files changed, 156 insertions(+), 95 deletions(-) diff --git a/interface/resources/qml/Stats.qml b/interface/resources/qml/Stats.qml index 0b5eba99d0..9688f2a492 100644 --- a/interface/resources/qml/Stats.qml +++ b/interface/resources/qml/Stats.qml @@ -304,16 +304,16 @@ Item { } ListView { width: geoCol.width - height: root.downloadUrls.length * 15 + height: root.downloadUrls.length * 30 visible: root.expanded && root.downloadUrls.length > 0; model: root.downloadUrls delegate: StatText { visible: root.expanded; - text: modelData.length > 30 + text: (modelData.length > 30 ? modelData.substring(0, 5) + "..." + modelData.substring(modelData.length - 22) - : modelData + : modelData) + "\n\t" + "Priority: " + root.downloadPriorities[index] + ", Progress: " + root.downloadProgresses[index] + "%" } } } diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 764f2bb54d..16831ee091 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2277,12 +2277,12 @@ void Application::initialize(const QCommandLineParser &parser) { auto loadingRequests = ResourceCache::getLoadingRequests(); QJsonArray loadingRequestsStats; - for (const auto& request : loadingRequests) { + for (const auto& requestPair : loadingRequests) { QJsonObject requestStats; - requestStats["filename"] = request->getURL().fileName(); - requestStats["received"] = request->getBytesReceived(); - requestStats["total"] = request->getBytesTotal(); - requestStats["attempts"] = (int)request->getDownloadAttempts(); + requestStats["filename"] = requestPair.first->getURL().fileName(); + requestStats["received"] = requestPair.first->getBytesReceived(); + requestStats["total"] = requestPair.first->getBytesTotal(); + requestStats["attempts"] = (int)requestPair.first->getDownloadAttempts(); loadingRequestsStats.append(requestStats); } @@ -5731,15 +5731,30 @@ void Application::init() { getEntities()->init(); getEntities()->setEntityLoadingPriorityFunction([this](const EntityItem& item) { - auto dims = item.getScaledDimensions(); - auto maxSize = glm::compMax(dims); + if (item.getEntityHostType() == entity::HostType::AVATAR) { + return item.isMyAvatarEntity() ? Avatar::MYAVATAR_ENTITY_LOADING_PRIORITY : Avatar::OTHERAVATAR_ENTITY_LOADING_PRIORITY; + } + const float maxSize = glm::compMax(item.getScaledDimensions()); if (maxSize <= 0.0f) { return 0.0f; } - auto distance = glm::distance(getMyAvatar()->getWorldPosition(), item.getWorldPosition()); - return atan2(maxSize, distance); + const glm::vec3 itemPosition = item.getWorldPosition(); + const float distance = glm::distance(getMyAvatar()->getWorldPosition(), itemPosition); + float result = atan2(maxSize, distance); + + bool isInView = true; + { + QMutexLocker viewLocker(&_viewMutex); + isInView = _viewFrustum.sphereIntersectsKeyhole(itemPosition, maxSize); + } + if (!isInView) { + const float OUT_OF_VIEW_PENALTY = -M_PI_2; + result += OUT_OF_VIEW_PENALTY; + } + + return result; }); ObjectMotionState::setShapeManager(&_shapeManager); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index fc6fae5456..0dcab1e45b 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -259,7 +259,7 @@ MyAvatar::MyAvatar(QThread* thread) : _headData = new MyHead(this); _skeletonModel = std::make_shared(this, nullptr); - _skeletonModel->setLoadingPriority(MYAVATAR_LOADING_PRIORITY); + _skeletonModel->setLoadingPriorityOperator([]() { return MYAVATAR_LOADING_PRIORITY; }); connect(_skeletonModel.get(), &Model::setURLFinished, this, &Avatar::setModelURLFinished); connect(_skeletonModel.get(), &Model::setURLFinished, this, [this](bool success) { if (success) { diff --git a/interface/src/avatar/OtherAvatar.cpp b/interface/src/avatar/OtherAvatar.cpp index aacb6f56a9..2433bdde91 100644 --- a/interface/src/avatar/OtherAvatar.cpp +++ b/interface/src/avatar/OtherAvatar.cpp @@ -44,7 +44,7 @@ OtherAvatar::OtherAvatar(QThread* thread) : Avatar(thread) { // give the pointer to our head to inherited _headData variable from AvatarData _headData = new Head(this); _skeletonModel = std::make_shared(this, nullptr); - _skeletonModel->setLoadingPriority(OTHERAVATAR_LOADING_PRIORITY); + _skeletonModel->setLoadingPriorityOperator([]() { return OTHERAVATAR_LOADING_PRIORITY; }); connect(_skeletonModel.get(), &Model::setURLFinished, this, &Avatar::setModelURLFinished); connect(_skeletonModel.get(), &Model::rigReady, this, &Avatar::rigReady); connect(_skeletonModel.get(), &Model::rigReset, this, &Avatar::rigReset); diff --git a/interface/src/scripting/AccountServicesScriptingInterface.cpp b/interface/src/scripting/AccountServicesScriptingInterface.cpp index 35e9f3b36d..e77fb13b89 100644 --- a/interface/src/scripting/AccountServicesScriptingInterface.cpp +++ b/interface/src/scripting/AccountServicesScriptingInterface.cpp @@ -159,8 +159,8 @@ bool DownloadInfoResultFromScriptValue(const ScriptValue& object, DownloadInfoRe DownloadInfoResult AccountServicesScriptingInterface::getDownloadInfo() { DownloadInfoResult result; - foreach(const auto& resource, ResourceCache::getLoadingRequests()) { - result.downloading.append(resource->getProgress() * 100.0f); + foreach(const auto& resourcePair, ResourceCache::getLoadingRequests()) { + result.downloading.append(resourcePair.first->getProgress() * 100.0f); } result.pending = ResourceCache::getPendingRequestCount(); return result; diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index 0e3a329375..79e4d0e28c 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -289,8 +289,8 @@ void Stats::updateStats(bool force) { STAT_UPDATE(entityPacketsInKbps, octreeServerCount ? totalEntityKbps / octreeServerCount : -1); - auto loadingRequests = ResourceCache::getLoadingRequests(); - STAT_UPDATE(downloads, loadingRequests.size()); + auto loadingRequestPairs = ResourceCache::getLoadingRequests(); + STAT_UPDATE(downloads, loadingRequestPairs.size()); STAT_UPDATE(downloadLimit, (int)ResourceCache::getRequestLimit()) STAT_UPDATE(downloadsPending, (int)ResourceCache::getPendingRequestCount()); STAT_UPDATE(processing, DependencyManager::get()->getStat("Processing").toInt()); @@ -298,29 +298,37 @@ void Stats::updateStats(bool force) { // See if the active download urls have changed bool shouldUpdateUrls = _downloads != _downloadUrls.size(); + bool shouldUpdateProgresses = false; if (!shouldUpdateUrls) { for (int i = 0; i < _downloads; i++) { - if (loadingRequests[i]->getURL().toString() != _downloadUrls[i]) { + if (loadingRequestPairs[i].first->getURL().toString() != _downloadUrls[i]) { shouldUpdateUrls = true; break; + } else if (loadingRequestPairs[i].first->getProgress() != _downloadProgresses[i]) { + shouldUpdateProgresses = true; } } } // If the urls have changed, update the list if (shouldUpdateUrls) { _downloadUrls.clear(); - foreach (const auto& resource, loadingRequests) { - _downloadUrls << resource->getURL().toString(); + _downloadPriorities.clear(); + foreach (const auto& resourcePair, loadingRequestPairs) { + _downloadUrls << resourcePair.first->getURL().toString(); + _downloadPriorities << resourcePair.second; } emit downloadUrlsChanged(); + emit downloadPrioritiesChanged(); + shouldUpdateProgresses = true; + } + + if (shouldUpdateProgresses) { + _downloadProgresses.clear(); + foreach (const auto& resourcePair, loadingRequestPairs) { + _downloadProgresses << (int)(100.0f * resourcePair.first->getProgress()); + } + emit downloadProgressesChanged(); } - // TODO fix to match original behavior - //stringstream downloads; - //downloads << "Downloads: "; - //foreach(Resource* resource, ) { - // downloads << (int)(resource->getProgress() * 100.0f) << "% "; - //} - //downloads << "(" << << " pending)"; } // Fourth column, octree stats diff --git a/interface/src/ui/Stats.h b/interface/src/ui/Stats.h index a3366904bd..b6d5e5ac9c 100644 --- a/interface/src/ui/Stats.h +++ b/interface/src/ui/Stats.h @@ -211,7 +211,10 @@ private: \ * Read-only. * @property {string[]} downloadUrls - The download URLs. * Read-only. - *

Note: Property not available in the API.

+ * @property {number[]} downloadProgresses - The download progresses. + * Read-only. + * @property {number[]} downloadPriorities - The download priorities. + * Read-only. * @property {number} processing - The number of completed downloads being processed. * Read-only. * @property {number} processingPending - The number of completed downloads waiting to be processed. @@ -529,6 +532,8 @@ class Stats : public QQuickItem { STATS_PROPERTY(int, downloadLimit, 0) STATS_PROPERTY(int, downloadsPending, 0) Q_PROPERTY(QStringList downloadUrls READ downloadUrls NOTIFY downloadUrlsChanged) + Q_PROPERTY(QList downloadProgresses READ downloadProgresses NOTIFY downloadProgressesChanged) + Q_PROPERTY(QList downloadPriorities READ downloadPriorities NOTIFY downloadPrioritiesChanged) STATS_PROPERTY(int, processing, 0) STATS_PROPERTY(int, processingPending, 0) STATS_PROPERTY(int, triangles, 0) @@ -622,7 +627,9 @@ public: } } - QStringList downloadUrls () { return _downloadUrls; } + QStringList downloadUrls() { return _downloadUrls; } + QList downloadProgresses() { return _downloadProgresses; } + QList downloadPriorities() { return _downloadPriorities; } public slots: @@ -1091,6 +1098,20 @@ signals: */ void downloadUrlsChanged(); + /*@jsdoc + * Triggered when the value of the downloadProgresses property changes. + * @function Stats.downloadProgressesChanged + * @returns {Signal} + */ + void downloadProgressesChanged(); + + /*@jsdoc + * Triggered when the value of the downloadPriorities property changes. + * @function Stats.downloadPrioritiesChanged + * @returns {Signal} + */ + void downloadPrioritiesChanged(); + /*@jsdoc * Triggered when the value of the processing property changes. * @function Stats.processingChanged @@ -1809,14 +1830,16 @@ signals: */ private: - int _recentMaxPackets{ 0 } ; // recent max incoming voxel packets to process - bool _resetRecentMaxPacketsSoon{ true }; - bool _expanded{ false }; - bool _showTimingDetails{ false }; - bool _showGameUpdateStats{ false }; + int _recentMaxPackets { 0 } ; // recent max incoming voxel packets to process + bool _resetRecentMaxPacketsSoon { true }; + bool _expanded { false }; + bool _showTimingDetails { false }; + bool _showGameUpdateStats { false }; QString _monospaceFont; const AudioIOStats* _audioStats; - QStringList _downloadUrls = QStringList(); + QStringList _downloadUrls { QStringList() }; + QList _downloadProgresses { QList() }; + QList _downloadPriorities { QList() }; }; #endif // hifi_Stats_h diff --git a/libraries/animation/src/AnimNodeLoader.cpp b/libraries/animation/src/AnimNodeLoader.cpp index 9474c0309f..066ccec056 100644 --- a/libraries/animation/src/AnimNodeLoader.cpp +++ b/libraries/animation/src/AnimNodeLoader.cpp @@ -1085,7 +1085,7 @@ AnimNodeLoader::AnimNodeLoader(const QUrl& url) : { _resource = QSharedPointer::create(url); _resource->setSelf(_resource); - _resource->setLoadPriority(this, ANIM_GRAPH_LOAD_PRIORITY); + _resource->setLoadPriorityOperator(this, []() { return ANIM_GRAPH_LOAD_PRIORITY; }); connect(_resource.data(), &Resource::loaded, this, &AnimNodeLoader::onRequestDone); connect(_resource.data(), &Resource::failed, this, &AnimNodeLoader::onRequestError); _resource->ensureLoading(); diff --git a/libraries/audio/src/SoundCache.cpp b/libraries/audio/src/SoundCache.cpp index 2b02a566ac..55a32e7237 100644 --- a/libraries/audio/src/SoundCache.cpp +++ b/libraries/audio/src/SoundCache.cpp @@ -35,7 +35,7 @@ SharedSoundPointer SoundCache::getSound(const QUrl& url) { QSharedPointer SoundCache::createResource(const QUrl& url) { auto resource = QSharedPointer(new Sound(url), &Resource::deleter); - resource->setLoadPriority(this, SOUNDS_LOADING_PRIORITY); + resource->setLoadPriorityOperator(this, []() { return SOUNDS_LOADING_PRIORITY; }); return resource; } diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 25473bda90..be63127995 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -51,6 +51,8 @@ const float DISPLAYNAME_BACKGROUND_ALPHA = 0.4f; const glm::vec3 HAND_TO_PALM_OFFSET(0.0f, 0.12f, 0.08f); const float Avatar::MYAVATAR_LOADING_PRIORITY = (float)M_PI; // Entity priority is computed as atan2(maxDim, distance) which is <= PI / 2 const float Avatar::OTHERAVATAR_LOADING_PRIORITY = MYAVATAR_LOADING_PRIORITY - EPSILON; +const float Avatar::MYAVATAR_ENTITY_LOADING_PRIORITY = MYAVATAR_LOADING_PRIORITY - EPSILON; +const float Avatar::OTHERAVATAR_ENTITY_LOADING_PRIORITY = OTHERAVATAR_LOADING_PRIORITY - EPSILON; namespace render { template <> const ItemKey payloadGetKey(const AvatarSharedPointer& avatar) { @@ -841,8 +843,23 @@ bool Avatar::getEnableMeshVisible() const { } void Avatar::fixupModelsInScene(const render::ScenePointer& scene) { - bool canTryFade{ false }; + if (_needsWearablesLoadedCheck) { + bool wearablesAreLoaded = true; + // Technically, we should be checking for descendant avatar entities that are owned by this avatar. + // But it's sufficient to just check all children entities here. + forEachChild([&](SpatiallyNestablePointer child) { + if (child->getNestableType() == NestableType::Entity) { + auto entity = std::dynamic_pointer_cast(child); + if (entity && !entity->isVisuallyReady()) { + wearablesAreLoaded = false; + } + } + }); + setEnableMeshVisible(_isMeshVisible || wearablesAreLoaded); + _needsWearablesLoadedCheck = !wearablesAreLoaded; + } + bool canTryFade = false; // check to see if when we added our models to the scene they were ready, if they were not ready, then // fix them up in the scene render::Transaction transaction; @@ -1484,6 +1501,12 @@ void Avatar::rigReady() { buildSpine2SplineRatioCache(); setSkeletonData(getSkeletonDefaultData()); sendSkeletonData(); + + const bool prevNeedsWearablesLoadedCheck = _needsWearablesLoadedCheck; + _needsWearablesLoadedCheck = _skeletonModel && _skeletonModel->isLoaded() && _skeletonModel->getGeometry()->shouldWaitForWearables(); + if (prevNeedsWearablesLoadedCheck != _needsWearablesLoadedCheck) { + setEnableMeshVisible(!_needsWearablesLoadedCheck); + } } // rig has been reset. diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index af6b58a187..7690104b75 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -554,6 +554,9 @@ public: uint32_t appendSubMetaItems(render::ItemIDs& subItems); + static const float MYAVATAR_ENTITY_LOADING_PRIORITY; + static const float OTHERAVATAR_ENTITY_LOADING_PRIORITY; + signals: /*@jsdoc * Triggered when the avatar's target scale is changed. The target scale is the desired scale of the avatar without any @@ -742,8 +745,9 @@ protected: void processMaterials(); AABox _renderBound; - bool _isMeshVisible{ true }; - bool _needMeshVisibleSwitch{ true }; + bool _isMeshVisible { true }; + bool _needMeshVisibleSwitch { true }; + bool _needsWearablesLoadedCheck { false }; static const float MYAVATAR_LOADING_PRIORITY; static const float OTHERAVATAR_LOADING_PRIORITY; diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 1b54d1b3b7..9be5758a4f 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -1310,6 +1310,7 @@ void ModelEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPoint scene->enqueueTransaction(transaction); }); entity->setModel(model); + model->setLoadingPriorityOperator([entity]() { return EntityTreeRenderer::getEntityLoadingPriority(*entity); }); withWriteLock([&] { _model = model; }); } @@ -1317,7 +1318,6 @@ void ModelEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPoint if (_parsedModelURL != model->getURL()) { _texturesLoaded = false; _jointMappingCompleted = false; - model->setLoadingPriority(EntityTreeRenderer::getEntityLoadingPriority(*entity)); model->setURL(_parsedModelURL); } diff --git a/libraries/material-networking/src/material-networking/TextureCache.cpp b/libraries/material-networking/src/material-networking/TextureCache.cpp index 496b00ae2c..840fa50a0a 100644 --- a/libraries/material-networking/src/material-networking/TextureCache.cpp +++ b/libraries/material-networking/src/material-networking/TextureCache.cpp @@ -448,9 +448,9 @@ void NetworkTexture::setExtra(void* extra) { _shouldFailOnRedirect = _currentlyLoadingResourceType != ResourceType::KTX; if (_type == image::TextureUsage::SKY_TEXTURE) { - setLoadPriority(this, SKYBOX_LOAD_PRIORITY); + setLoadPriorityOperator(this, []() { return SKYBOX_LOAD_PRIORITY; }); } else if (_currentlyLoadingResourceType == ResourceType::KTX) { - setLoadPriority(this, HIGH_MIPS_LOAD_PRIORITY); + setLoadPriorityOperator(this, []() { return HIGH_MIPS_LOAD_PRIORITY; }); } if (!_url.isValid()) { @@ -704,7 +704,7 @@ void NetworkTexture::startRequestForNextMipLevel() { init(false); float priority = -(float)_originalKtxDescriptor->header.numberOfMipmapLevels + (float)_lowestKnownPopulatedMip; - setLoadPriority(this, priority); + setLoadPriorityOperator(this, [priority]() { return priority; }); _url.setFragment(QString::number(_lowestKnownPopulatedMip - 1)); TextureCache::attemptRequest(self); } diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp index ddfdeb79d1..264c6b9801 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.cpp +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -252,7 +252,6 @@ void GeometryResource::downloadFinished(const QByteArray& data) { } auto animGraphVariant = _mapping.value("animGraphUrl"); - if (animGraphVariant.isValid()) { QUrl fstUrl(animGraphVariant.toString()); if (fstUrl.isValid()) { @@ -264,6 +263,8 @@ void GeometryResource::downloadFinished(const QByteArray& data) { _animGraphOverrideUrl = QUrl(); } + _waitForWearables = _mapping.value(WAIT_FOR_WEARABLES_FIELD).toBool(); + auto modelCache = DependencyManager::get(); GeometryExtra extra { GeometryMappingPair(base, _mapping), _textureBaseURL, false }; @@ -452,6 +453,7 @@ Geometry::Geometry(const Geometry& geometry) { } _animGraphOverrideUrl = geometry._animGraphOverrideUrl; + _waitForWearables = geometry._waitForWearables; _mapping = geometry._mapping; } diff --git a/libraries/model-networking/src/model-networking/ModelCache.h b/libraries/model-networking/src/model-networking/ModelCache.h index 236c6262bf..7902108709 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.h +++ b/libraries/model-networking/src/model-networking/ModelCache.h @@ -58,6 +58,7 @@ public: virtual bool areTexturesLoaded() const; const QUrl& getAnimGraphOverrideUrl() const { return _animGraphOverrideUrl; } + bool shouldWaitForWearables() const { return _waitForWearables; } const QVariantHash& getMapping() const { return _mapping; } protected: @@ -72,6 +73,7 @@ protected: QUrl _animGraphOverrideUrl; QVariantHash _mapping; // parsed contents of FST file. + bool _waitForWearables { false }; private: mutable bool _areTexturesLoaded { false }; diff --git a/libraries/model-serializers/src/FSTReader.cpp b/libraries/model-serializers/src/FSTReader.cpp index 7e84f012a7..9c1ff5a431 100644 --- a/libraries/model-serializers/src/FSTReader.cpp +++ b/libraries/model-serializers/src/FSTReader.cpp @@ -22,7 +22,7 @@ #include -const QStringList SINGLE_VALUE_PROPERTIES{"name", "filename", "texdir", "script", "comment"}; +const QStringList SINGLE_VALUE_PROPERTIES { NAME_FIELD, FILENAME_FIELD, TEXDIR_FIELD, SCRIPT_FIELD, WAIT_FOR_WEARABLES_FIELD, COMMENT_FIELD }; hifi::VariantMultiHash FSTReader::parseMapping(QIODevice* device) { hifi::VariantMultiHash properties; diff --git a/libraries/model-serializers/src/FSTReader.h b/libraries/model-serializers/src/FSTReader.h index e1b7405346..5557df67c6 100644 --- a/libraries/model-serializers/src/FSTReader.h +++ b/libraries/model-serializers/src/FSTReader.h @@ -33,6 +33,7 @@ static const QString BLENDSHAPE_FIELD = "bs"; static const QString SCRIPT_FIELD = "script"; static const QString JOINT_NAME_MAPPING_FIELD = "jointMap"; static const QString MATERIAL_MAPPING_FIELD = "materialMap"; +static const QString WAIT_FOR_WEARABLES_FIELD = "waitForWearables"; static const QString COMMENT_FIELD = "comment"; class FSTReader { diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index 4e36fb4646..7a08a396a4 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -33,7 +33,12 @@ bool ResourceCacheSharedItems::appendRequest(QWeakPointer resource) { Lock lock(_mutex); if ((uint32_t)_loadingRequests.size() < _requestLimit) { - _loadingRequests.append(resource); + float priority = 0.0f; + // This should always be true, but just in case + if (QSharedPointer resourceStrong = resource.lock()) { + priority = resourceStrong->getLoadPriority(); + } + _loadingRequests.append({ resource, priority }); return true; } else { _pendingRequests.append(resource); @@ -70,14 +75,14 @@ uint32_t ResourceCacheSharedItems::getPendingRequestsCount() const { return _pendingRequests.size(); } -QList> ResourceCacheSharedItems::getLoadingRequests() const { - QList> result; +QList, float>> ResourceCacheSharedItems::getLoadingRequests() const { + QList, float>> result; Lock lock(_mutex); - foreach(QWeakPointer resource, _loadingRequests) { - auto locked = resource.lock(); + foreach(auto resourcePair, _loadingRequests) { + auto locked = resourcePair.first.lock(); if (locked) { - result.append(locked); + result.append({ locked, resourcePair.second }); } } @@ -96,7 +101,7 @@ void ResourceCacheSharedItems::removeRequest(QWeakPointer resource) { // QWeakPointer has no operator== implementation for two weak ptrs, so // manually loop in case resource has been freed. for (int i = 0; i < _loadingRequests.size();) { - auto request = _loadingRequests.at(i); + auto request = _loadingRequests.at(i).first; // Clear our resource and any freed resources if (!request || request.toStrongRef().data() == resource.toStrongRef().data()) { _loadingRequests.removeAt(i); @@ -519,7 +524,7 @@ void ResourceCache::updateTotalSize(const qint64& deltaSize) { emit dirty(); } -QList> ResourceCache::getLoadingRequests() { +QList, float>> ResourceCache::getLoadingRequests() { return DependencyManager::get()->getLoadingRequests(); } @@ -571,7 +576,7 @@ Resource::Resource(const Resource& other) : _startedLoading(other._startedLoading), _failedToLoad(other._failedToLoad), _loaded(other._loaded), - _loadPriorities(other._loadPriorities), + _loadPriorityOperators(other._loadPriorityOperators), _bytesReceived(other._bytesReceived), _bytesTotal(other._bytesTotal), _bytes(other._bytes), @@ -605,40 +610,24 @@ void Resource::ensureLoading() { } } -void Resource::setLoadPriority(const QPointer& owner, float priority) { +void Resource::setLoadPriorityOperator(const QPointer& owner, std::function priorityOperator) { if (!_failedToLoad) { - _loadPriorities.insert(owner, priority); - } -} - -void Resource::setLoadPriorities(const QHash, float>& priorities) { - if (_failedToLoad) { - return; - } - for (QHash, float>::const_iterator it = priorities.constBegin(); - it != priorities.constEnd(); it++) { - _loadPriorities.insert(it.key(), it.value()); - } -} - -void Resource::clearLoadPriority(const QPointer& owner) { - if (!_failedToLoad) { - _loadPriorities.remove(owner); + _loadPriorityOperators.insert(owner, priorityOperator); } } float Resource::getLoadPriority() { - if (_loadPriorities.size() == 0) { + if (_loadPriorityOperators.size() == 0) { return 0; } float highestPriority = -FLT_MAX; - for (QHash, float>::iterator it = _loadPriorities.begin(); it != _loadPriorities.end(); ) { - if (it.key().isNull()) { - it = _loadPriorities.erase(it); + for (QHash, std::function>::iterator it = _loadPriorityOperators.begin(); it != _loadPriorityOperators.end();) { + if (it.key().isNull() || !it.value()) { + it = _loadPriorityOperators.erase(it); continue; } - highestPriority = qMax(highestPriority, it.value()); + highestPriority = qMax(highestPriority, it.value()()); it++; } return highestPriority; @@ -742,7 +731,7 @@ void Resource::attemptRequest() { void Resource::finishedLoading(bool success) { if (success) { - _loadPriorities.clear(); + _loadPriorityOperators.clear(); _loaded = true; } else { _failedToLoad = true; diff --git a/libraries/networking/src/ResourceCache.h b/libraries/networking/src/ResourceCache.h index 0eafd1f900..b920e8a7f8 100644 --- a/libraries/networking/src/ResourceCache.h +++ b/libraries/networking/src/ResourceCache.h @@ -73,7 +73,7 @@ public: QList> getPendingRequests() const; QSharedPointer getHighestPendingRequest(); uint32_t getPendingRequestsCount() const; - QList> getLoadingRequests() const; + QList, float>> getLoadingRequests() const; uint32_t getLoadingRequestsCount() const; void clear(); @@ -82,7 +82,7 @@ private: mutable Mutex _mutex; QList> _pendingRequests; - QList> _loadingRequests; + QList, float>> _loadingRequests; const uint32_t DEFAULT_REQUEST_LIMIT = 10; uint32_t _requestLimit { DEFAULT_REQUEST_LIMIT }; }; @@ -216,7 +216,7 @@ public: void setUnusedResourceCacheSize(qint64 unusedResourcesMaxSize); qint64 getUnusedResourceCacheSize() const { return _unusedResourcesMaxSize; } - static QList> getLoadingRequests(); + static QList, float>> getLoadingRequests(); static uint32_t getPendingRequestCount(); static uint32_t getLoadingRequestCount(); @@ -424,13 +424,7 @@ public: void ensureLoading(); /// Sets the load priority for one owner. - virtual void setLoadPriority(const QPointer& owner, float priority); - - /// Sets a set of priorities at once. - virtual void setLoadPriorities(const QHash, float>& priorities); - - /// Clears the load priority for one owner. - virtual void clearLoadPriority(const QPointer& owner); + virtual void setLoadPriorityOperator(const QPointer& owner, std::function priorityOperator); /// Returns the highest load priority across all owners. float getLoadPriority(); @@ -451,7 +445,7 @@ public: qint64 getBytes() const { return _bytes; } /// For loading resources, returns the load progress. - float getProgress() const { return (_bytesTotal <= 0) ? 0.0f : (float)_bytesReceived / _bytesTotal; } + float getProgress() const { return (_bytesTotal <= 0) ? 0.0f : ((float)_bytesReceived / _bytesTotal); } /// Refreshes the resource. virtual void refresh(); @@ -537,7 +531,7 @@ protected: bool _failedToLoad = false; bool _loaded = false; - QHash, float> _loadPriorities; + QHash, std::function> _loadPriorityOperators; QWeakPointer _self; QPointer _cache; diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index b7b645dd8a..69a593ed09 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -1343,7 +1343,7 @@ void Model::setURL(const QUrl& url) { auto resource = DependencyManager::get()->getGeometryResource(url); if (resource) { - resource->setLoadPriority(this, _loadingPriority); + resource->setLoadPriorityOperator(this, _loadingPriorityOperator); _renderWatcher.setResource(resource); } _rig.initFlow(false); diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 63a96f7253..bc9b8fcfff 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -295,7 +295,7 @@ public: // returns 'true' if needs fullUpdate after geometry change virtual bool updateGeometry(); - void setLoadingPriority(float priority) { _loadingPriority = priority; } + void setLoadingPriorityOperator(std::function priorityOperator) { _loadingPriorityOperator = priorityOperator; } size_t getRenderInfoVertexCount() const { return _renderInfoVertexCount; } size_t getRenderInfoTextureSize(); @@ -518,7 +518,7 @@ protected: uint64_t _created; private: - float _loadingPriority { 0.0f }; + std::function _loadingPriorityOperator { []() { return 0.0f; } }; void calculateTextureInfo();