Cogs.Core
Cache.cpp
1#include "Foundation/Logging/Logger.h"
2
3#include "Context.h"
4#include "Resources/TextureManager.h"
5
6#include "Image360System.h"
7
8namespace {
9 using namespace Cogs::Core;
10 using namespace Cogs::Core::Image360;
11
13
14 size_t cacheMapOffset(size_t level)
15 {
16 // 0: = 0
17 // 1: 6 = 6 * (4^0)
18 // 2: 6 + 6*4 = 6 * (4^0 + 4^1)
19 // 3: 6 + 6*4 + 6*4*4 = 6 * (4^0 + 4^1 + 4^2)
20 // 4: 6 + 6*4 + 6*4*4 + 6*4*4*4 = 6 * (4^0 + 4^1 + 4^2 + 4^3) = 6 * (4^0 + 4^1 + 4^2 + 4^3)
21 //
22 // sum_{i=0}^n 4^i = 1/3 ( 4^(n+1) - 1 )
23 // = 1/3 ( 2^(2n+2) - 1 )
24
25 if (level == 0) return 0;
26
27 size_t n = level - 1;
28 size_t t = (size_t(1) << (2 * n + 2)) - 1;
29 size_t sum = t / 3;
30 assert(3 * sum == t);
31 return 6 * sum;
32 }
33
34 SlotIx tryEvictACacheSlot(Cache& cache, const uint32_t currentFrame)
35 {
36
37 if (cache.lruPointer) {
38 SlotIx slotIx = cache.lru[--cache.lruPointer];
39 Cache::Item& cacheItem = cache.items[slotIx];
40
41 // Note: Since we evict while updating the tree, we must avoid
42 // evicting a tree item that has been visible, is still visible,
43 // but haven't been visited yet due to changes further down in
44 // the tree. Hence
45 if (1 < currentFrame - cacheItem.lastTouched) {
46 LOG_TRACE(logger, "Map=%zu slot=%d rev=%u evicting cache slot, age=%u", cacheItem.mapIx, slotIx, cache.items[slotIx].revision, currentFrame - cacheItem.lastTouched);
47 assert(cache.map[cacheItem.mapIx] == slotIx);
48
49 cache.map[cacheItem.mapIx] = -1;
50 cacheItem.value.state = Cache::Item::State::None;
51 cacheItem.depth.state = Cache::Item::State::None;
52 cacheItem.mapIx = ~0u;
53 return slotIx;
54 }
55 }
56 return -1;
57 }
58
59 SlotIx tryAllocNewCacheSlot(Cache& cache)
60 {
61 size_t o = cache.items.size();
62 if (o < cache.maxItemCount) {
63
64 SlotIx slotIx = SlotIx(o);
65
66 cache.lru.emplace_back(slotIx);
67
68 Cache::Item& cacheItem = cache.items.emplace_back();
69 cacheItem.value.state = Cache::Item::State::None;
70 cacheItem.depth.state = Cache::Item::State::None;
71 cacheItem.mapIx = ~0u;
72 cacheItem.lastTouched = 3;
73 return slotIx;
74 }
75 return -1;
76 }
77
78}
79
80void Cogs::Core::Image360::Cache::init(const Config& config)
81{
82 assert(map.empty());
83 map.resize(cacheMapOffset(config.treeDepth), -1);
84}
85
86SlotIx Cogs::Core::Image360::Cache::isQuadInCache(Context* context,
87 Fetcher& fetcher,
88 const Image360Component& im360Comp,
89 const Config& config,
90 const uint32_t currentFrame,
91 const size_t cacheLevel,
92 const size_t cacheLevelIndex)
93{
94 MapIx mapIx = cacheMapOffset(cacheLevel) + cacheLevelIndex;
95
96 SlotIx slotIx = map[mapIx];
97 if (0 <= slotIx) {
98 Item& cacheItem = items[slotIx];
99 cacheItem.lastTouched = currentFrame;
100 if (cacheItem.value.state == Item::State::Resident && cacheItem.depth.state == Item::State::Resident) {
101 return slotIx;
102 }
103
104 // If state is None, but has a slot in cache, it has been cancelled.
105 if (cacheItem.value.state == Item::State::None) {
106 cacheItem.value.state = Item::State::Loading;
107 fetcher.issueChannelFetch(context,
108 im360Comp,
109 config,
110 mapIx,
111 slotIx,
112 config.valueChannel,
113 cacheItem.revision,
114 cacheLevel,
115 cacheLevelIndex);
116 if (config.valueChannel == config.depthChannel) {
117 cacheItem.depth.state = Item::State::Loading;
118 }
119 }
120 if (config.hasDepth && cacheItem.depth.state == Item::State::None) {
121 cacheItem.depth.state = Item::State::Loading;
122 fetcher.issueChannelFetch(context,
123 im360Comp,
124 config,
125 mapIx,
126 slotIx,
127 config.depthChannel,
128 cacheItem.revision,
129 cacheLevel,
130 cacheLevelIndex);
131 }
132 return -1;
133 }
134
135 // Limit amount of concurrent fetches, check before we try to evict
136 if (!fetcher.canLoadAnyItems(config)) {
137 return -1;
138 }
139
140 // Try issue a fetch
141 slotIx = tryEvictACacheSlot(*this, currentFrame);
142 if (slotIx < 0) {
143 slotIx = tryAllocNewCacheSlot(*this);
144 }
145
146 if (0 <= slotIx) {
147 Item& cacheItem = items[slotIx];
148 cacheItem.lastTouched = currentFrame;
149 cacheItem.revision++;
150 cacheItem.mapIx = mapIx;
151 map[mapIx] = slotIx;
152
153 cacheItem.value.state = Item::State::Loading;
154 fetcher.issueChannelFetch(context,
155 im360Comp,
156 config,
157 mapIx,
158 slotIx,
159 config.valueChannel,
160 cacheItem.revision,
161 cacheLevel,
162 cacheLevelIndex);
163 if (config.hasDepth) {
164 cacheItem.depth.state = Item::State::Loading;
165 if (config.valueChannel != config.depthChannel) {
166 fetcher.issueChannelFetch(context,
167 im360Comp,
168 config,
169 mapIx,
170 slotIx,
171 config.depthChannel,
172 cacheItem.revision,
173 cacheLevel,
174 cacheLevelIndex);
175 }
176 }
177 else {
178 cacheItem.depth.state = Item::State::Resident;
179 }
180 return -1;
181 }
182
183 return -1;
184}
185
186void Cogs::Core::Image360::Cache::updateLeastRecentlyUsedList(const uint32_t currentFrame)
187{
188 assert(items.size() == lru.size());
189
190 std::sort(lru.begin(),
191 lru.end(),
192 [currentFrame, cache=this](const SlotIx a, const SlotIx b) -> bool
193 {
194 return (currentFrame - cache->items[a].lastTouched) < (currentFrame - cache->items[b].lastTouched);
195 });
196 lruPointer = lru.size();
197
198 for (size_t i = 0; i + 1 < lruPointer; i++) {
199 assert(lru[i] != lru[i + 1]);
200 }
201
202#if 0
203 size_t evictable = 0;
204 while (lruPointer) {
205 SlotIx slotIx = lru[--lruPointer];
206 if (0 < currentFrame - items[slotIx].lastTouched) {
207 evictable++;
208 }
209 }
210 if (evictable) {
211 LOG_DEBUG(logger, "Evictable %zu", evictable);
212 }
213 lruPointer = lru.size();
214#endif
215}
A Context instance contains all the services, systems and runtime components needed to use Cogs.
Definition: Context.h:83
Log implementation class.
Definition: LogManager.h:139
Contains the Engine, Renderer, resource managers and other systems needed to run Cogs....
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Definition: LogManager.h:180
size_t maxItemCount
Number of tiles in cache.
Definition: Image360.h:130
size_t lruPointer
Points to one past the least recently and recycable item, decrements and zero means nothing to recycl...
Definition: Image360.h:129
std::vector< SlotIx > lru
Slots sorted s.t. the least recently used items is at end.
Definition: Image360.h:127
uint8_t valueChannel
Data channel to be used as value data. From component.
Definition: Image360.h:49
uint32_t treeDepth
Depth of tile hierarchy. From json.
Definition: Image360.h:45
uint8_t depthChannel
Data channel that contains depth data. From json.
Definition: Image360.h:50
bool hasDepth
If data has depth and component wants depth.
Definition: Image360.h:58