time-box Picks and solve in round-robin sequence

This commit is contained in:
Andrew Meadows 2018-03-01 22:48:55 -08:00
parent 7a19f48b9c
commit 9fbe7ec194
6 changed files with 107 additions and 60 deletions

View file

@ -175,3 +175,11 @@ void PickScriptingInterface::registerMetaTypes(QScriptEngine* engine) {
qScriptRegisterMetaType(engine, pickTypesToScriptValue, pickTypesFromScriptValue);
}
unsigned int PickScriptingInterface::getPerFrameTimeBudget() const {
return DependencyManager::get<PickManager>()->getPerFrameTimeBudget();
}
void PickScriptingInterface::setPerFrameTimeBudget(unsigned int numUsecs) {
DependencyManager::get<PickManager>()->setPerFrameTimeBudget(numUsecs);
}

View file

@ -185,6 +185,14 @@ public:
*/
Q_INVOKABLE bool isMouse(unsigned int uid);
Q_PROPERTY(unsigned int perFrameTimeBudget READ getPerFrameTimeBudget WRITE setPerFrameTimeBudget)
/**jsdoc
* The max number of usec to spend per frame updating Pick results.
* @typedef {number} Picks.perFrameTimeBudget
*/
unsigned int getPerFrameTimeBudget() const;
void setPerFrameTimeBudget(unsigned int numUsecs);
public slots:
static constexpr unsigned int PICK_NOTHING() { return 0; }
static constexpr unsigned int PICK_ENTITIES() { return PickFilter::getBitMask(PickFilter::FlagBit::PICK_ENTITIES); }

View file

@ -37,7 +37,7 @@ template<typename T>
class PickCacheOptimizer {
public:
void update(std::unordered_map<unsigned int, std::shared_ptr<PickQuery>>& picks, bool shouldPickHUD);
void update(std::unordered_map<uint32_t, std::shared_ptr<PickQuery>>& picks, uint32_t& nextToUpdate, uint64_t expiry, bool shouldPickHUD);
protected:
typedef std::unordered_map<T, std::unordered_map<PickCacheKey, PickResultPointer>> PickCache;
@ -67,19 +67,26 @@ void PickCacheOptimizer<T>::cacheResult(const bool intersects, const PickResultP
}
template<typename T>
void PickCacheOptimizer<T>::update(std::unordered_map<unsigned int, std::shared_ptr<PickQuery>>& picks, bool shouldPickHUD) {
void PickCacheOptimizer<T>::update(std::unordered_map<uint32_t, std::shared_ptr<PickQuery>>& picks,
uint32_t& nextToUpdate, uint64_t expiry, bool shouldPickHUD) {
PickCache results;
for (const auto& pickPair : picks) {
std::shared_ptr<Pick<T>> pick = std::static_pointer_cast<Pick<T>>(pickPair.second);
const uint32_t INVALID_PICK_ID = 0;
std::unordered_map<uint32_t, std::shared_ptr<PickQuery>>::iterator itr = picks.begin();
if (nextToUpdate != INVALID_PICK_ID) {
itr = picks.find(nextToUpdate);
if (itr == picks.end()) {
itr = picks.begin();
}
}
uint32_t numUpdates = 0;
while(numUpdates < picks.size()) {
std::shared_ptr<Pick<T>> pick = std::static_pointer_cast<Pick<T>>(itr->second);
T mathematicalPick = pick->getMathematicalPick();
PickResultPointer res = pick->getDefaultResult(mathematicalPick.toVariantMap());
if (!pick->isEnabled() || pick->getFilter().doesPickNothing() || pick->getMaxDistance() < 0.0f || !mathematicalPick) {
pick->setPickResult(res);
continue;
}
} else {
if (pick->getFilter().doesPickEntities()) {
PickCacheKey entityKey = { pick->getFilter().getEntityFlags(), pick->getIncludeItems(), pick->getIgnoreItems() };
if (!checkAndCompareCachedResults(mathematicalPick, results, res, entityKey)) {
@ -127,6 +134,18 @@ void PickCacheOptimizer<T>::update(std::unordered_map<unsigned int, std::shared_
pick->setPickResult(pick->getDefaultResult(mathematicalPick.toVariantMap()));
}
}
++itr;
if (itr == picks.end()) {
itr = picks.begin();
}
nextToUpdate = itr->first;
++numUpdates;
uint64_t now = usecTimestampNow();
if (usecTimestampNow() > expiry) {
break;
}
}
}
#endif // hifi_PickCacheOptimizer_h

View file

@ -89,14 +89,17 @@ void PickManager::setIncludeItems(unsigned int uid, const QVector<QUuid>& includ
}
void PickManager::update() {
uint64_t expiry = usecTimestampNow() + _perFrameTimeBudget;
std::unordered_map<PickQuery::PickType, std::unordered_map<unsigned int, std::shared_ptr<PickQuery>>> cachedPicks;
withReadLock([&] {
cachedPicks = _picks;
});
bool shouldPickHUD = _shouldPickHUDOperator();
_rayPickCacheOptimizer.update(cachedPicks[PickQuery::Ray], shouldPickHUD);
_stylusPickCacheOptimizer.update(cachedPicks[PickQuery::Stylus], false);
// we pass the same expiry to both updates
// even when the rayPicks consume all the budget at least one stylus will be updated
_rayPickCacheOptimizer.update(cachedPicks[PickQuery::Ray], _nextPickToUpdate[PickQuery::Ray], expiry, shouldPickHUD);
_stylusPickCacheOptimizer.update(cachedPicks[PickQuery::Stylus], _nextPickToUpdate[PickQuery::Stylus], expiry, false);
}
bool PickManager::isLeftHand(unsigned int uid) {

View file

@ -14,6 +14,8 @@
#include "Pick.h"
#include "PickCacheOptimizer.h"
#include <NumericalConstants.h>
class PickManager : public Dependency, protected ReadWriteLockable {
SINGLETON_DEPENDENCY
@ -48,17 +50,24 @@ public:
static const unsigned int INVALID_PICK_ID { 0 };
unsigned int getPerFrameTimeBudget() const { return _perFrameTimeBudget; }
void setPerFrameTimeBudget(unsigned int numUsecs) { _perFrameTimeBudget = numUsecs; }
protected:
std::function<bool()> _shouldPickHUDOperator;
std::function<glm::vec2(const glm::vec3&)> _calculatePos2DFromHUDOperator;
std::shared_ptr<PickQuery> findPick(unsigned int uid) const;
std::unordered_map<PickQuery::PickType, std::unordered_map<unsigned int, std::shared_ptr<PickQuery>>> _picks;
unsigned int _nextPickToUpdate[PickQuery::NUM_PICK_TYPES] { 0, 0 };
std::unordered_map<unsigned int, PickQuery::PickType> _typeMap;
unsigned int _nextPickID { INVALID_PICK_ID + 1 };
PickCacheOptimizer<PickRay> _rayPickCacheOptimizer;
PickCacheOptimizer<StylusTip> _stylusPickCacheOptimizer;
static const unsigned int DEFAULT_PER_FRAME_TIME_BUDGET = 2 * USECS_PER_MSEC;
unsigned int _perFrameTimeBudget { DEFAULT_PER_FRAME_TIME_BUDGET };
};
#endif // hifi_PickManager_h