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