From 55cc945c78fe2a2f9b7a63b34518741457029155 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 10 Nov 2017 10:09:41 -0800 Subject: [PATCH] organize PrioritySortUtil differently --- .../src/EntityTreeRenderer.cpp | 48 ++++--- libraries/shared/src/PrioritySortUtil.h | 118 +++++++++++------- 2 files changed, 94 insertions(+), 72 deletions(-) diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 1efcd863ab..c62eafd9f2 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -19,18 +19,18 @@ #include #include -#include #include #include +#include +#include #include #include #include +#include +#include #include #include -#include -#include #include -#include #include #include "EntitiesRendererLogging.h" @@ -38,22 +38,6 @@ #include "RenderableWebEntityItem.h" -// implement these methods BEFORE including PrioritySortUtil.h -namespace PrioritySortUtil { - glm::vec3 getObjectPosition(const EntityRendererPointer& object) { - return object->getEntity()->getPosition(); - } - - float getObjectRadius(const EntityRendererPointer& object) { - return 0.5f * object->getEntity()->getQueryAACube().getScale(); - } - - uint64_t getObjectAge(const EntityRendererPointer& object) { - return object->getUpdateTime(); - } -} -#include - size_t std::hash::operator()(const EntityItemID& id) const { return qHash(id); } std::function EntityTreeRenderer::_entitiesShouldFadeFunction; @@ -353,17 +337,30 @@ void EntityTreeRenderer::updateChangedEntities(const render::ScenePointer& scene } else { // we expect the cost to updating all renderables to exceed available time budget // so we first sort by priority and update in order until out of time + + // as per the instructions in PriortySortUtil we first derive from PrioritySortUtil::Prioritizable + class PrioritizableRenderer: public PrioritySortUtil::Prioritizable { + public: + PrioritizableRenderer(const EntityRendererPointer& renderer) : _renderer(renderer) { } + glm::vec3 getPosition() const override { return _renderer->getEntity()->getPosition(); } + float getRadius() const override { return 0.5f * _renderer->getEntity()->getQueryAACube().getScale(); } + uint64_t getTimestamp() const override { return _renderer->getUpdateTime(); } + private: + const EntityRendererPointer& _renderer; + }; + + // prioritize and sort the renderables uint64_t sortStart = usecTimestampNow(); using SortableRenderer = PrioritySortUtil::Sortable; std::priority_queue< SortableRenderer > sortedRenderables; { PROFILE_RANGE_EX(simulation_physics, "SortRenderables", 0xffff00ff, (uint64_t)_renderablesToUpdate.size()); - PrioritySortUtil::PriorityCalculator priorityCalculator(view); + PrioritySortUtil::Prioritizer prioritizer(view); std::unordered_map::iterator itr = _renderablesToUpdate.begin(); while (itr != _renderablesToUpdate.end()) { - float priority = priorityCalculator.computePriority(itr->second); - SortableRenderer entry(itr->second, priority); - sortedRenderables.push(entry); + float priority = prioritizer.computePriority(PrioritizableRenderer(itr->second)); + //SortableRenderer entry(itr->second, priority); + sortedRenderables.push(SortableRenderer(itr->second, priority)); ++itr; } } @@ -380,10 +377,11 @@ void EntityTreeRenderer::updateChangedEntities(const render::ScenePointer& scene } uint64_t expiry = updateStart + timeBudget; + // process the sorted renderables std::unordered_map::iterator itr; size_t numSorted = sortedRenderables.size(); while (!sortedRenderables.empty() && usecTimestampNow() < expiry) { - const EntityRendererPointer& renderer = sortedRenderables.top().getObject(); + const EntityRendererPointer& renderer = sortedRenderables.top().getThing(); renderer->updateInScene(scene, transaction); _renderablesToUpdate.erase(renderer->getEntity()->getID()); sortedRenderables.pop(); diff --git a/libraries/shared/src/PrioritySortUtil.h b/libraries/shared/src/PrioritySortUtil.h index 4b32b6d977..d03edc53ac 100644 --- a/libraries/shared/src/PrioritySortUtil.h +++ b/libraries/shared/src/PrioritySortUtil.h @@ -14,59 +14,82 @@ #include #include "ViewFrustum.h" -namespace PrioritySortUtil { - // PrioritySortUtil is a helper template for sorting 3D objects relative to a ViewFrustum. - // To use this utility: - // - // (1) Declare and implement the following methods for your "object" type T: - // - // glm::vec3 PrioritySortUtil::getObjectPosition(const T&); - // float PrioritySortUtil::getObjectRadius(const T&); - // uint64_t PrioritySortUtil::getObjectAge(const T&); - // - // (2) Below the implementation in (1): - // - // #include - // - // (3) Create a PriorityCalculator instance: - // - // PrioritySortUtil::PriorityCalculator calculator(viewFrustum); - // - // (4) Loop over your objects and insert the into a priority_queue: - // - // std::priority_queue< PrioritySortUtil::Sortable > sortedObjects; - // for (T obj in objects) { - // float priority = calculator.computePriority(obj); - // PrioritySortUtil::Sortable entry(obj, priority); - // sortedObjects.push(entry); - // } +/* PrioritySortUtil is a helper for sorting 3D things relative to a ViewFrustum. To use: - template - class Sortable { - public: - Sortable(const T& object, float sortPriority) : _object(object), _priority(sortPriority) {} - const T& getObject() const { return _object; } - void setPriority(float priority) { _priority = priority; } - bool operator<(const Sortable& other) const { return _priority < other._priority; } - private: - T _object; - float _priority; - }; +(1) Derive a class from pure-virtual PrioritySortUtil::Prioritizable + that wraps the Thing you want to prioritize and sort: + + class PrioritizableThing : public PrioritySortUtil::Prioritizable { + public: + PrioritizableThing(const Thing& thing) : _thing(thing) {} + glm::vec3 getPosition() const override { return _thing.getPosition(); } + float getRadius() const const override { return _thing.getBoundingRadius(); } + uint64_t getTimestamp() const override { return _thing.getLastUpdated(); } + private: + // Yes really: the data member is a const reference! + // PrioritizableThing only needs enough scope to compute a priority. + const Thing& _thing; + } + +(2) Loop over your things and insert each into a priority_queue: + + PrioritySortUtil::Prioritizer prioritizer(viewFrustum); + std::priority_queue< PrioritySortUtil::Sortable > sortedThings; + for (thing in things) { + float priority = prioritizer.computePriority(PrioritySortUtil::PrioritizableThing(thing)); + sortedThings.push(PrioritySortUtil::Sortable entry(thing, priority)); + } + +(4) Loop over your priority queue and do timeboxed work: + + uint64_t cutoffTime = usecTimestampNow() + TIME_BUDGET; + while (!sortedThings.empty()) { + const Thing& thing = sortedThings.top(); + // ...do work on thing... + sortedThings.pop(); + if (usecTimestampNow() > cutoffTime) { + break; + } + } + +*/ + +namespace PrioritySortUtil { constexpr float DEFAULT_ANGULAR_COEF { 1.0f }; constexpr float DEFAULT_CENTER_COEF { 0.5f }; constexpr float DEFAULT_AGE_COEF { 0.25f / (float)(USECS_PER_SECOND) }; template - class PriorityCalculator { + class Sortable { public: - PriorityCalculator() = delete; + Sortable(const T& thing, float sortPriority) : _thing(thing), _priority(sortPriority) {} + const T& getThing() const { return _thing; } + void setPriority(float priority) { _priority = priority; } + bool operator<(const Sortable& other) const { return _priority < other._priority; } + private: + T _thing; + float _priority; + }; - PriorityCalculator(const ViewFrustum& view) : _view(view) { + // Prioritizable isn't a template because templates can't have pure-virtual methods. + class Prioritizable { + public: + virtual glm::vec3 getPosition() const = 0; + virtual float getRadius() const = 0; + virtual uint64_t getTimestamp() const = 0; + }; + + template + class Prioritizer { + public: + Prioritizer() = delete; + + Prioritizer(const ViewFrustum& view) : _view(view) { cacheView(); } - PriorityCalculator(const ViewFrustum& view, float angularWeight, float centerWeight, float ageWeight) + Prioritizer(const ViewFrustum& view, float angularWeight, float centerWeight, float ageWeight) : _view(view), _angularWeight(angularWeight), _centerWeight(centerWeight), _ageWeight(ageWeight) { cacheView(); @@ -80,21 +103,21 @@ namespace PrioritySortUtil { _ageWeight = ageWeight; } - float computePriority(T& object) const { + float computePriority(const Prioritizable& prioritizableThing) const { // priority = weighted linear combination of multiple values: // (a) angular size // (b) proximity to center of view // (c) time since last update // where the relative "weights" are tuned to scale the contributing values into units of "priority". - glm::vec3 position = PrioritySortUtil::getObjectPosition(object); + glm::vec3 position = prioritizableThing.getPosition(); glm::vec3 offset = position - _viewPosition; float distance = glm::length(offset) + 0.001f; // add 1mm to avoid divide by zero - float radius = PrioritySortUtil::getObjectRadius(object); + float radius = prioritizableThing.getRadius(); float priority = _angularWeight * (radius / distance) + _centerWeight * (glm::dot(offset, _viewForward) / distance) - + _ageWeight * (float)(usecTimestampNow() - PrioritySortUtil::getObjectAge(object)); + + _ageWeight * (float)(usecTimestampNow() - prioritizableThing.getTimestamp()); // decrement priority of things outside keyhole if (distance - radius > _viewRadius) { @@ -108,7 +131,7 @@ namespace PrioritySortUtil { private: void cacheView() { - // assuming we'll prioritize many objects: cache these values + // assuming we'll prioritize many things: cache these values _viewPosition = _view.getPosition(); _viewForward = _view.getDirection(); _viewRadius = _view.getCenterRadius(); @@ -122,6 +145,7 @@ namespace PrioritySortUtil { float _centerWeight { DEFAULT_CENTER_COEF }; float _ageWeight { DEFAULT_AGE_COEF }; }; -} +} // namespace PrioritySortUtil + #endif // hifi_PrioritySortUtil_h