Overte C++ Documentation
PickCacheOptimizer.h
1 //
2 // Created by Sam Gondelman 10/16/2017
3 // Copyright 2017 High Fidelity, Inc.
4 //
5 // Distributed under the Apache License, Version 2.0.
6 // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
7 //
8 #ifndef hifi_PickCacheOptimizer_h
9 #define hifi_PickCacheOptimizer_h
10 
11 #include <unordered_map>
12 
13 #include "Pick.h"
14 
15 typedef struct PickCacheKey {
16  PickFilter::Flags mask;
17  QVector<QUuid> include;
18  QVector<QUuid> ignore;
19 
20  bool operator==(const PickCacheKey& other) const {
21  return (mask == other.mask && include == other.include && ignore == other.ignore);
22  }
23 } PickCacheKey;
24 
25 template <>
26 struct std::hash<PickCacheKey> {
27  size_t operator()(const PickCacheKey& k) const {
28  return ((std::hash<PickFilter::Flags>()(k.mask) ^ (qHash(k.include) << 1)) >> 1) ^ (qHash(k.ignore) << 1);
29  }
30 };
31 
32 // T is a mathematical representation of a Pick (a MathPick)
33 // For example: RayPicks use T = PickRay
34 template<typename T>
35 class PickCacheOptimizer {
36 
37 public:
38  QVector3D update(std::unordered_map<uint32_t, std::shared_ptr<PickQuery>>& picks, uint32_t& nextToUpdate, uint64_t expiry, bool shouldPickHUD);
39 
40 protected:
41  typedef std::unordered_map<T, std::unordered_map<PickCacheKey, PickResultPointer>> PickCache;
42 
43  // Returns true if this pick exists in the cache, and if it does, update res if the cached result is closer
44  bool checkAndCompareCachedResults(T& pick, PickCache& cache, PickResultPointer& res, const PickCacheKey& key);
45  void cacheResult(const bool intersects, const PickResultPointer& resTemp, const PickCacheKey& key, PickResultPointer& res, T& mathPick, PickCache& cache, const std::shared_ptr<Pick<T>> pick);
46 };
47 
48 template<typename T>
49 bool PickCacheOptimizer<T>::checkAndCompareCachedResults(T& pick, PickCache& cache, PickResultPointer& res, const PickCacheKey& key) {
50  if (cache.find(pick) != cache.end() && cache[pick].find(key) != cache[pick].end()) {
51  res = res->compareAndProcessNewResult(cache[pick][key]);
52  return true;
53  }
54  return false;
55 }
56 
57 template<typename T>
58 void PickCacheOptimizer<T>::cacheResult(const bool needToCompareResults, const PickResultPointer& resTemp, const PickCacheKey& key, PickResultPointer& res, T& mathPick, PickCache& cache, const std::shared_ptr<Pick<T>> pick) {
59  if (needToCompareResults) {
60  cache[mathPick][key] = resTemp;
61  res = res->compareAndProcessNewResult(resTemp);
62  } else {
63  cache[mathPick][key] = pick->getDefaultResult(mathPick.toVariantMap());
64  }
65 }
66 
67 template<typename T>
68 QVector3D PickCacheOptimizer<T>::update(std::unordered_map<uint32_t, std::shared_ptr<PickQuery>>& picks,
69  uint32_t& nextToUpdate, uint64_t expiry, bool shouldPickHUD) {
70  QVector3D numIntersectionsComputed;
71  PickCache results;
72  const uint32_t INVALID_PICK_ID = 0;
73  auto itr = picks.begin();
74  if (nextToUpdate != INVALID_PICK_ID) {
75  itr = picks.find(nextToUpdate);
76  if (itr == picks.end()) {
77  itr = picks.begin();
78  }
79  }
80  uint32_t numUpdates = 0;
81  while(numUpdates < picks.size()) {
82  std::shared_ptr<Pick<T>> pick = std::static_pointer_cast<Pick<T>>(itr->second);
83  T mathematicalPick = pick->getMathematicalPick();
84  PickResultPointer res = pick->getDefaultResult(mathematicalPick.toVariantMap());
85 
86  if (!pick->isEnabled() || pick->getMaxDistance() < 0.0f || !mathematicalPick) {
87  pick->setPickResult(res);
88  } else {
89  if (pick->getFilter().doesPickDomainEntities() || pick->getFilter().doesPickAvatarEntities() || pick->getFilter().doesPickLocalEntities()) {
90  PickCacheKey entityKey = { pick->getFilter().getEntityFlags(), pick->getIncludeItems(), pick->getIgnoreItems() };
91  if (!checkAndCompareCachedResults(mathematicalPick, results, res, entityKey)) {
92  PickResultPointer entityRes = pick->getEntityIntersection(mathematicalPick);
93  numIntersectionsComputed[0]++;
94  if (entityRes) {
95  cacheResult(entityRes->doesIntersect(), entityRes, entityKey, res, mathematicalPick, results, pick);
96  }
97  }
98  }
99 
100  if (pick->getFilter().doesPickAvatars()) {
101  PickCacheKey avatarKey = { pick->getFilter().getAvatarFlags(), pick->getIncludeItems(), pick->getIgnoreItems() };
102  if (!checkAndCompareCachedResults(mathematicalPick, results, res, avatarKey)) {
103  PickResultPointer avatarRes = pick->getAvatarIntersection(mathematicalPick);
104  numIntersectionsComputed[1]++;
105  if (avatarRes) {
106  cacheResult(avatarRes->doesIntersect(), avatarRes, avatarKey, res, mathematicalPick, results, pick);
107  }
108  }
109  }
110 
111  // Can't intersect with HUD in desktop mode
112  if (pick->getFilter().doesPickHUD() && shouldPickHUD) {
113  PickCacheKey hudKey = { pick->getFilter().getHUDFlags(), QVector<QUuid>(), QVector<QUuid>() };
114  if (!checkAndCompareCachedResults(mathematicalPick, results, res, hudKey)) {
115  PickResultPointer hudRes = pick->getHUDIntersection(mathematicalPick);
116  numIntersectionsComputed[2]++;
117  if (hudRes) {
118  cacheResult(true, hudRes, hudKey, res, mathematicalPick, results, pick);
119  }
120  }
121  }
122 
123  if (pick->getMaxDistance() == 0.0f || (pick->getMaxDistance() > 0.0f && res->checkOrFilterAgainstMaxDistance(pick->getMaxDistance()))) {
124  pick->setPickResult(res);
125  } else {
126  pick->setPickResult(pick->getDefaultResult(mathematicalPick.toVariantMap()));
127  }
128  }
129 
130  ++itr;
131  if (itr == picks.end()) {
132  itr = picks.begin();
133  }
134  nextToUpdate = itr->first;
135  ++numUpdates;
136  if (usecTimestampNow() > expiry) {
137  break;
138  }
139  }
140  return numIntersectionsComputed;
141 }
142 
143 #endif // hifi_PickCacheOptimizer_h