mirror of
https://github.com/overte-org/overte.git
synced 2025-08-10 07:53:08 +02:00
time-box Picks and solve in round-robin sequence
This commit is contained in:
parent
7a19f48b9c
commit
9fbe7ec194
6 changed files with 107 additions and 60 deletions
|
@ -174,4 +174,12 @@ void PickScriptingInterface::registerMetaTypes(QScriptEngine* engine) {
|
||||||
engine->globalObject().setProperty("PickType", pickTypes);
|
engine->globalObject().setProperty("PickType", pickTypes);
|
||||||
|
|
||||||
qScriptRegisterMetaType(engine, pickTypesToScriptValue, pickTypesFromScriptValue);
|
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);
|
||||||
|
}
|
||||||
|
|
|
@ -185,6 +185,14 @@ public:
|
||||||
*/
|
*/
|
||||||
Q_INVOKABLE bool isMouse(unsigned int uid);
|
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:
|
public slots:
|
||||||
static constexpr unsigned int PICK_NOTHING() { return 0; }
|
static constexpr unsigned int PICK_NOTHING() { return 0; }
|
||||||
static constexpr unsigned int PICK_ENTITIES() { return PickFilter::getBitMask(PickFilter::FlagBit::PICK_ENTITIES); }
|
static constexpr unsigned int PICK_ENTITIES() { return PickFilter::getBitMask(PickFilter::FlagBit::PICK_ENTITIES); }
|
||||||
|
@ -202,4 +210,4 @@ public slots:
|
||||||
static constexpr unsigned int INTERSECTED_HUD() { return IntersectionType::HUD; }
|
static constexpr unsigned int INTERSECTED_HUD() { return IntersectionType::HUD; }
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_PickScriptingInterface_h
|
#endif // hifi_PickScriptingInterface_h
|
||||||
|
|
|
@ -2984,4 +2984,4 @@ std::unordered_map<std::string, graphics::MultiMaterial> EntityItem::getMaterial
|
||||||
toReturn = _materials;
|
toReturn = _materials;
|
||||||
}
|
}
|
||||||
return toReturn;
|
return toReturn;
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,7 @@ template<typename T>
|
||||||
class PickCacheOptimizer {
|
class PickCacheOptimizer {
|
||||||
|
|
||||||
public:
|
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:
|
protected:
|
||||||
typedef std::unordered_map<T, std::unordered_map<PickCacheKey, PickResultPointer>> PickCache;
|
typedef std::unordered_map<T, std::unordered_map<PickCacheKey, PickResultPointer>> PickCache;
|
||||||
|
@ -67,66 +67,85 @@ void PickCacheOptimizer<T>::cacheResult(const bool intersects, const PickResultP
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
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;
|
PickCache results;
|
||||||
for (const auto& pickPair : picks) {
|
const uint32_t INVALID_PICK_ID = 0;
|
||||||
std::shared_ptr<Pick<T>> pick = std::static_pointer_cast<Pick<T>>(pickPair.second);
|
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();
|
T mathematicalPick = pick->getMathematicalPick();
|
||||||
PickResultPointer res = pick->getDefaultResult(mathematicalPick.toVariantMap());
|
PickResultPointer res = pick->getDefaultResult(mathematicalPick.toVariantMap());
|
||||||
|
|
||||||
if (!pick->isEnabled() || pick->getFilter().doesPickNothing() || pick->getMaxDistance() < 0.0f || !mathematicalPick) {
|
if (!pick->isEnabled() || pick->getFilter().doesPickNothing() || pick->getMaxDistance() < 0.0f || !mathematicalPick) {
|
||||||
pick->setPickResult(res);
|
pick->setPickResult(res);
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pick->getFilter().doesPickEntities()) {
|
|
||||||
PickCacheKey entityKey = { pick->getFilter().getEntityFlags(), pick->getIncludeItems(), pick->getIgnoreItems() };
|
|
||||||
if (!checkAndCompareCachedResults(mathematicalPick, results, res, entityKey)) {
|
|
||||||
PickResultPointer entityRes = pick->getEntityIntersection(mathematicalPick);
|
|
||||||
if (entityRes) {
|
|
||||||
cacheResult(entityRes->doesIntersect(), entityRes, entityKey, res, mathematicalPick, results, pick);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pick->getFilter().doesPickOverlays()) {
|
|
||||||
PickCacheKey overlayKey = { pick->getFilter().getOverlayFlags(), pick->getIncludeItems(), pick->getIgnoreItems() };
|
|
||||||
if (!checkAndCompareCachedResults(mathematicalPick, results, res, overlayKey)) {
|
|
||||||
PickResultPointer overlayRes = pick->getOverlayIntersection(mathematicalPick);
|
|
||||||
if (overlayRes) {
|
|
||||||
cacheResult(overlayRes->doesIntersect(), overlayRes, overlayKey, res, mathematicalPick, results, pick);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pick->getFilter().doesPickAvatars()) {
|
|
||||||
PickCacheKey avatarKey = { pick->getFilter().getAvatarFlags(), pick->getIncludeItems(), pick->getIgnoreItems() };
|
|
||||||
if (!checkAndCompareCachedResults(mathematicalPick, results, res, avatarKey)) {
|
|
||||||
PickResultPointer avatarRes = pick->getAvatarIntersection(mathematicalPick);
|
|
||||||
if (avatarRes) {
|
|
||||||
cacheResult(avatarRes->doesIntersect(), avatarRes, avatarKey, res, mathematicalPick, results, pick);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Can't intersect with HUD in desktop mode
|
|
||||||
if (pick->getFilter().doesPickHUD() && shouldPickHUD) {
|
|
||||||
PickCacheKey hudKey = { pick->getFilter().getHUDFlags(), QVector<QUuid>(), QVector<QUuid>() };
|
|
||||||
if (!checkAndCompareCachedResults(mathematicalPick, results, res, hudKey)) {
|
|
||||||
PickResultPointer hudRes = pick->getHUDIntersection(mathematicalPick);
|
|
||||||
if (hudRes) {
|
|
||||||
cacheResult(true, hudRes, hudKey, res, mathematicalPick, results, pick);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pick->getMaxDistance() == 0.0f || (pick->getMaxDistance() > 0.0f && res->checkOrFilterAgainstMaxDistance(pick->getMaxDistance()))) {
|
|
||||||
pick->setPickResult(res);
|
|
||||||
} else {
|
} else {
|
||||||
pick->setPickResult(pick->getDefaultResult(mathematicalPick.toVariantMap()));
|
if (pick->getFilter().doesPickEntities()) {
|
||||||
|
PickCacheKey entityKey = { pick->getFilter().getEntityFlags(), pick->getIncludeItems(), pick->getIgnoreItems() };
|
||||||
|
if (!checkAndCompareCachedResults(mathematicalPick, results, res, entityKey)) {
|
||||||
|
PickResultPointer entityRes = pick->getEntityIntersection(mathematicalPick);
|
||||||
|
if (entityRes) {
|
||||||
|
cacheResult(entityRes->doesIntersect(), entityRes, entityKey, res, mathematicalPick, results, pick);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pick->getFilter().doesPickOverlays()) {
|
||||||
|
PickCacheKey overlayKey = { pick->getFilter().getOverlayFlags(), pick->getIncludeItems(), pick->getIgnoreItems() };
|
||||||
|
if (!checkAndCompareCachedResults(mathematicalPick, results, res, overlayKey)) {
|
||||||
|
PickResultPointer overlayRes = pick->getOverlayIntersection(mathematicalPick);
|
||||||
|
if (overlayRes) {
|
||||||
|
cacheResult(overlayRes->doesIntersect(), overlayRes, overlayKey, res, mathematicalPick, results, pick);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pick->getFilter().doesPickAvatars()) {
|
||||||
|
PickCacheKey avatarKey = { pick->getFilter().getAvatarFlags(), pick->getIncludeItems(), pick->getIgnoreItems() };
|
||||||
|
if (!checkAndCompareCachedResults(mathematicalPick, results, res, avatarKey)) {
|
||||||
|
PickResultPointer avatarRes = pick->getAvatarIntersection(mathematicalPick);
|
||||||
|
if (avatarRes) {
|
||||||
|
cacheResult(avatarRes->doesIntersect(), avatarRes, avatarKey, res, mathematicalPick, results, pick);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Can't intersect with HUD in desktop mode
|
||||||
|
if (pick->getFilter().doesPickHUD() && shouldPickHUD) {
|
||||||
|
PickCacheKey hudKey = { pick->getFilter().getHUDFlags(), QVector<QUuid>(), QVector<QUuid>() };
|
||||||
|
if (!checkAndCompareCachedResults(mathematicalPick, results, res, hudKey)) {
|
||||||
|
PickResultPointer hudRes = pick->getHUDIntersection(mathematicalPick);
|
||||||
|
if (hudRes) {
|
||||||
|
cacheResult(true, hudRes, hudKey, res, mathematicalPick, results, pick);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pick->getMaxDistance() == 0.0f || (pick->getMaxDistance() > 0.0f && res->checkOrFilterAgainstMaxDistance(pick->getMaxDistance()))) {
|
||||||
|
pick->setPickResult(res);
|
||||||
|
} else {
|
||||||
|
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
|
#endif // hifi_PickCacheOptimizer_h
|
||||||
|
|
|
@ -89,14 +89,17 @@ void PickManager::setIncludeItems(unsigned int uid, const QVector<QUuid>& includ
|
||||||
}
|
}
|
||||||
|
|
||||||
void PickManager::update() {
|
void PickManager::update() {
|
||||||
|
uint64_t expiry = usecTimestampNow() + _perFrameTimeBudget;
|
||||||
std::unordered_map<PickQuery::PickType, std::unordered_map<unsigned int, std::shared_ptr<PickQuery>>> cachedPicks;
|
std::unordered_map<PickQuery::PickType, std::unordered_map<unsigned int, std::shared_ptr<PickQuery>>> cachedPicks;
|
||||||
withReadLock([&] {
|
withReadLock([&] {
|
||||||
cachedPicks = _picks;
|
cachedPicks = _picks;
|
||||||
});
|
});
|
||||||
|
|
||||||
bool shouldPickHUD = _shouldPickHUDOperator();
|
bool shouldPickHUD = _shouldPickHUDOperator();
|
||||||
_rayPickCacheOptimizer.update(cachedPicks[PickQuery::Ray], shouldPickHUD);
|
// we pass the same expiry to both updates
|
||||||
_stylusPickCacheOptimizer.update(cachedPicks[PickQuery::Stylus], false);
|
// 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) {
|
bool PickManager::isLeftHand(unsigned int uid) {
|
||||||
|
@ -121,4 +124,4 @@ bool PickManager::isMouse(unsigned int uid) {
|
||||||
return pick->isMouse();
|
return pick->isMouse();
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,8 @@
|
||||||
#include "Pick.h"
|
#include "Pick.h"
|
||||||
#include "PickCacheOptimizer.h"
|
#include "PickCacheOptimizer.h"
|
||||||
|
|
||||||
|
#include <NumericalConstants.h>
|
||||||
|
|
||||||
class PickManager : public Dependency, protected ReadWriteLockable {
|
class PickManager : public Dependency, protected ReadWriteLockable {
|
||||||
SINGLETON_DEPENDENCY
|
SINGLETON_DEPENDENCY
|
||||||
|
|
||||||
|
@ -48,17 +50,24 @@ public:
|
||||||
|
|
||||||
static const unsigned int INVALID_PICK_ID { 0 };
|
static const unsigned int INVALID_PICK_ID { 0 };
|
||||||
|
|
||||||
|
unsigned int getPerFrameTimeBudget() const { return _perFrameTimeBudget; }
|
||||||
|
void setPerFrameTimeBudget(unsigned int numUsecs) { _perFrameTimeBudget = numUsecs; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::function<bool()> _shouldPickHUDOperator;
|
std::function<bool()> _shouldPickHUDOperator;
|
||||||
std::function<glm::vec2(const glm::vec3&)> _calculatePos2DFromHUDOperator;
|
std::function<glm::vec2(const glm::vec3&)> _calculatePos2DFromHUDOperator;
|
||||||
|
|
||||||
std::shared_ptr<PickQuery> findPick(unsigned int uid) const;
|
std::shared_ptr<PickQuery> findPick(unsigned int uid) const;
|
||||||
std::unordered_map<PickQuery::PickType, std::unordered_map<unsigned int, std::shared_ptr<PickQuery>>> _picks;
|
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;
|
std::unordered_map<unsigned int, PickQuery::PickType> _typeMap;
|
||||||
unsigned int _nextPickID { INVALID_PICK_ID + 1 };
|
unsigned int _nextPickID { INVALID_PICK_ID + 1 };
|
||||||
|
|
||||||
PickCacheOptimizer<PickRay> _rayPickCacheOptimizer;
|
PickCacheOptimizer<PickRay> _rayPickCacheOptimizer;
|
||||||
PickCacheOptimizer<StylusTip> _stylusPickCacheOptimizer;
|
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
|
#endif // hifi_PickManager_h
|
||||||
|
|
Loading…
Reference in a new issue