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 <shared/QtHelpers.h>
#include <ColorUtils.h>
#include <AbstractScriptingServicesInterface.h>
#include <AbstractViewStateInterface.h>
#include <AddressManager.h>
#include <ColorUtils.h>
#include <Model.h>
#include <NetworkAccessManager.h>
#include <PerfStat.h>
#include <PrioritySortUtil.h>
#include <Rig.h>
#include <SceneScriptingInterface.h>
#include <ScriptEngine.h>
#include <AddressManager.h>
#include <Rig.h>
#include <EntitySimulation.h>
#include <AddressManager.h>
#include <ZoneRenderer.h>
#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 <PrioritySortUtil.h>
size_t std::hash<EntityItemID>::operator()(const EntityItemID& id) const { return qHash(id); }
std::function<bool()> 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<EntityRendererPointer>;
std::priority_queue< SortableRenderer > sortedRenderables;
{
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();
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<EntityItemID, EntityRendererPointer>::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();

View file

@ -14,59 +14,82 @@
#include <glm/glm.hpp>
#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<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);
// }
/* PrioritySortUtil is a helper for sorting 3D things relative to a ViewFrustum. To use:
template <typename T>
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<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_CENTER_COEF { 0.5f };
constexpr float DEFAULT_AGE_COEF { 0.25f / (float)(USECS_PER_SECOND) };
template <typename T>
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 <typename T>
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