organize PrioritySortUtil differently

This commit is contained in:
Andrew Meadows 2017-11-10 10:09:41 -08:00
parent 99b4283cbc
commit 55cc945c78
2 changed files with 94 additions and 72 deletions

View file

@ -19,18 +19,18 @@
#include <QThreadPool> #include <QThreadPool>
#include <shared/QtHelpers.h> #include <shared/QtHelpers.h>
#include <ColorUtils.h>
#include <AbstractScriptingServicesInterface.h> #include <AbstractScriptingServicesInterface.h>
#include <AbstractViewStateInterface.h> #include <AbstractViewStateInterface.h>
#include <AddressManager.h>
#include <ColorUtils.h>
#include <Model.h> #include <Model.h>
#include <NetworkAccessManager.h> #include <NetworkAccessManager.h>
#include <PerfStat.h> #include <PerfStat.h>
#include <PrioritySortUtil.h>
#include <Rig.h>
#include <SceneScriptingInterface.h> #include <SceneScriptingInterface.h>
#include <ScriptEngine.h> #include <ScriptEngine.h>
#include <AddressManager.h>
#include <Rig.h>
#include <EntitySimulation.h> #include <EntitySimulation.h>
#include <AddressManager.h>
#include <ZoneRenderer.h> #include <ZoneRenderer.h>
#include "EntitiesRendererLogging.h" #include "EntitiesRendererLogging.h"
@ -38,22 +38,6 @@
#include "RenderableWebEntityItem.h" #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 <PrioritySortUtil.h>
size_t std::hash<EntityItemID>::operator()(const EntityItemID& id) const { return qHash(id); } size_t std::hash<EntityItemID>::operator()(const EntityItemID& id) const { return qHash(id); }
std::function<bool()> EntityTreeRenderer::_entitiesShouldFadeFunction; std::function<bool()> EntityTreeRenderer::_entitiesShouldFadeFunction;
@ -353,17 +337,30 @@ void EntityTreeRenderer::updateChangedEntities(const render::ScenePointer& scene
} else { } else {
// we expect the cost to updating all renderables to exceed available time budget // 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 // 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(); uint64_t sortStart = usecTimestampNow();
using SortableRenderer = PrioritySortUtil::Sortable<EntityRendererPointer>; using SortableRenderer = PrioritySortUtil::Sortable<EntityRendererPointer>;
std::priority_queue< SortableRenderer > sortedRenderables; std::priority_queue< SortableRenderer > sortedRenderables;
{ {
PROFILE_RANGE_EX(simulation_physics, "SortRenderables", 0xffff00ff, (uint64_t)_renderablesToUpdate.size()); PROFILE_RANGE_EX(simulation_physics, "SortRenderables", 0xffff00ff, (uint64_t)_renderablesToUpdate.size());
PrioritySortUtil::PriorityCalculator<EntityRendererPointer> priorityCalculator(view); PrioritySortUtil::Prioritizer<EntityRendererPointer> prioritizer(view);
std::unordered_map<EntityItemID, EntityRendererPointer>::iterator itr = _renderablesToUpdate.begin(); std::unordered_map<EntityItemID, EntityRendererPointer>::iterator itr = _renderablesToUpdate.begin();
while (itr != _renderablesToUpdate.end()) { while (itr != _renderablesToUpdate.end()) {
float priority = priorityCalculator.computePriority(itr->second); float priority = prioritizer.computePriority(PrioritizableRenderer(itr->second));
SortableRenderer entry(itr->second, priority); //SortableRenderer entry(itr->second, priority);
sortedRenderables.push(entry); sortedRenderables.push(SortableRenderer(itr->second, priority));
++itr; ++itr;
} }
} }
@ -380,10 +377,11 @@ void EntityTreeRenderer::updateChangedEntities(const render::ScenePointer& scene
} }
uint64_t expiry = updateStart + timeBudget; uint64_t expiry = updateStart + timeBudget;
// process the sorted renderables
std::unordered_map<EntityItemID, EntityRendererPointer>::iterator itr; std::unordered_map<EntityItemID, EntityRendererPointer>::iterator itr;
size_t numSorted = sortedRenderables.size(); size_t numSorted = sortedRenderables.size();
while (!sortedRenderables.empty() && usecTimestampNow() < expiry) { while (!sortedRenderables.empty() && usecTimestampNow() < expiry) {
const EntityRendererPointer& renderer = sortedRenderables.top().getObject(); const EntityRendererPointer& renderer = sortedRenderables.top().getThing();
renderer->updateInScene(scene, transaction); renderer->updateInScene(scene, transaction);
_renderablesToUpdate.erase(renderer->getEntity()->getID()); _renderablesToUpdate.erase(renderer->getEntity()->getID());
sortedRenderables.pop(); sortedRenderables.pop();

View file

@ -14,59 +14,82 @@
#include <glm/glm.hpp> #include <glm/glm.hpp>
#include "ViewFrustum.h" #include "ViewFrustum.h"
namespace PrioritySortUtil { /* PrioritySortUtil is a helper for sorting 3D things relative to a ViewFrustum. To use:
// 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<typename T>::getObjectPosition(const T&);
// float PrioritySortUtil<typename T>::getObjectRadius(const T&);
// uint64_t PrioritySortUtil<typename T>::getObjectAge(const T&);
//
// (2) Below the implementation in (1):
//
// #include <PrioritySortUtil.h>
//
// (3) Create a PriorityCalculator instance:
//
// PrioritySortUtil::PriorityCalculator<T> calculator(viewFrustum);
//
// (4) Loop over your objects and insert the into a priority_queue:
//
// std::priority_queue< PrioritySortUtil::Sortable<T> > sortedObjects;
// for (T obj in objects) {
// float priority = calculator.computePriority(obj);
// PrioritySortUtil::Sortable<T> entry(obj, priority);
// sortedObjects.push(entry);
// }
template <typename T> (1) Derive a class from pure-virtual PrioritySortUtil::Prioritizable
class Sortable { that wraps the Thing you want to prioritize and sort:
public:
Sortable(const T& object, float sortPriority) : _object(object), _priority(sortPriority) {} class PrioritizableThing : public PrioritySortUtil::Prioritizable {
const T& getObject() const { return _object; } public:
void setPriority(float priority) { _priority = priority; } PrioritizableThing(const Thing& thing) : _thing(thing) {}
bool operator<(const Sortable& other) const { return _priority < other._priority; } glm::vec3 getPosition() const override { return _thing.getPosition(); }
private: float getRadius() const const override { return _thing.getBoundingRadius(); }
T _object; uint64_t getTimestamp() const override { return _thing.getLastUpdated(); }
float _priority; 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<Thing> > sortedThings;
for (thing in things) {
float priority = prioritizer.computePriority(PrioritySortUtil::PrioritizableThing(thing));
sortedThings.push(PrioritySortUtil::Sortable<Thing> 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_ANGULAR_COEF { 1.0f };
constexpr float DEFAULT_CENTER_COEF { 0.5f }; constexpr float DEFAULT_CENTER_COEF { 0.5f };
constexpr float DEFAULT_AGE_COEF { 0.25f / (float)(USECS_PER_SECOND) }; constexpr float DEFAULT_AGE_COEF { 0.25f / (float)(USECS_PER_SECOND) };
template <typename T> template <typename T>
class PriorityCalculator { class Sortable {
public: 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 <typename T>
class Prioritizer {
public:
Prioritizer() = delete;
Prioritizer(const ViewFrustum& view) : _view(view) {
cacheView(); 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) : _view(view), _angularWeight(angularWeight), _centerWeight(centerWeight), _ageWeight(ageWeight)
{ {
cacheView(); cacheView();
@ -80,21 +103,21 @@ namespace PrioritySortUtil {
_ageWeight = ageWeight; _ageWeight = ageWeight;
} }
float computePriority(T& object) const { float computePriority(const Prioritizable& prioritizableThing) const {
// priority = weighted linear combination of multiple values: // priority = weighted linear combination of multiple values:
// (a) angular size // (a) angular size
// (b) proximity to center of view // (b) proximity to center of view
// (c) time since last update // (c) time since last update
// where the relative "weights" are tuned to scale the contributing values into units of "priority". // 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; glm::vec3 offset = position - _viewPosition;
float distance = glm::length(offset) + 0.001f; // add 1mm to avoid divide by zero 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) float priority = _angularWeight * (radius / distance)
+ _centerWeight * (glm::dot(offset, _viewForward) / distance) + _centerWeight * (glm::dot(offset, _viewForward) / distance)
+ _ageWeight * (float)(usecTimestampNow() - PrioritySortUtil::getObjectAge(object)); + _ageWeight * (float)(usecTimestampNow() - prioritizableThing.getTimestamp());
// decrement priority of things outside keyhole // decrement priority of things outside keyhole
if (distance - radius > _viewRadius) { if (distance - radius > _viewRadius) {
@ -108,7 +131,7 @@ namespace PrioritySortUtil {
private: private:
void cacheView() { void cacheView() {
// assuming we'll prioritize many objects: cache these values // assuming we'll prioritize many things: cache these values
_viewPosition = _view.getPosition(); _viewPosition = _view.getPosition();
_viewForward = _view.getDirection(); _viewForward = _view.getDirection();
_viewRadius = _view.getCenterRadius(); _viewRadius = _view.getCenterRadius();
@ -122,6 +145,7 @@ namespace PrioritySortUtil {
float _centerWeight { DEFAULT_CENTER_COEF }; float _centerWeight { DEFAULT_CENTER_COEF };
float _ageWeight { DEFAULT_AGE_COEF }; float _ageWeight { DEFAULT_AGE_COEF };
}; };
} } // namespace PrioritySortUtil
#endif // hifi_PrioritySortUtil_h #endif // hifi_PrioritySortUtil_h