Cogs.Core
RasterSource.cpp
1#include "RasterSource.h"
2
3#include "RasterTile.h"
4
5#include "Rendering/ITextures.h"
6
7#include "Foundation/Logging/Logger.h"
8
9#include <cassert>
10#include <algorithm>
11
12namespace
13{
14 Cogs::Logging::Log logger = Cogs::Logging::getLogger("RasterSource");
15}
16
17Cogs::RasterSource::~RasterSource()
18{
19 for (auto & r : residentTiles) {
20 auto & tileData = r.second;
21
22 if (HandleIsValid(tileData.textureHandle) && tileData.textureHandle != invalidTileHandle) {
23 releaseTexture(tileData.textureHandle, tileData.data.size());
24 }
25 }
26
27 if (HandleIsValid(invalidTileHandle)) {
28 releaseTexture(invalidTileHandle, 0);
29 }
30}
31
32Cogs::RasterTile * Cogs::RasterSource::getTile(const RasterTileIdentifier & identifier)
33{
34 if (static_cast<size_t>(identifier.level) > getLevels().size() - 1) {
35 return nullptr;
36 }
37
38 const size_t hashCode = identifier.getHashCode();
39 auto tileIt = activeTiles.find(hashCode);
40
41 if (tileIt == activeTiles.end()) {
42 auto & level = levels[identifier.level];
43 auto & tile = activeTiles[hashCode];
44
45 tile.identifier = identifier;
46 tile.extent.west = identifier.x * level.getLongitudePostsPerTile();
47 tile.extent.east = std::min(tile.extent.west + level.getLongitudePostsPerTile(), level.getLongitudePosts()) - 1;
48 tile.extent.south = identifier.y * level.getLatitudePostsPerTile();
49 tile.extent.north = std::min(tile.extent.south + level.getLatitudePostsPerTile(), level.getLatitudePosts()) - 1;
50 tile.flags |= level.isTileIndexValid(identifier.x, identifier.y) ? TileFlags::None : TileFlags::OutOfBounds;
51
52 return &tile;
53 } else {
54 return &tileIt->second;
55 }
56}
57
58Cogs::RasterTile * Cogs::RasterSource::getTile(const size_t id)
59{
60 auto i = activeTiles.find(id);
61
62 if (i != activeTiles.end()) {
63 return &i->second;
64 }
65 return nullptr;
66}
67
68bool Cogs::RasterSource::tryGetTextureHandle(const RasterTile * tile, TextureHandle & textureHandle)
69{
70 if (tile->data) {
71 updateTileUsage(tile);
72
73 textureHandle = tile->data->textureHandle;
74
75 return true;
76 }
77
78 return false;
79}
80
81void Cogs::RasterSource::updateTileUsage(const RasterTile * tile)
82{
83 // We do not want to track the invalid tiles in the LRU.
84 if (tile->isOOB()) return;
85
86 // Referenced tiles are excluded from LRU tracking.
87 if (tile->usage != 0) return;
88
89 auto code = tile->identifier.getHashCode();
90
91 // Update tile usage status to avoid cache eviction
92 auto lruIt = lruMap.find(code);
93 if (lruIt != lruMap.end()) {
94 lruList.erase(lruIt->second);
95 }
96
97 // We move the current tile to the front of the list.
98 lruList.push_front(code);
99 lruMap[code] = lruList.begin();
100}
101
102void Cogs::RasterSource::refTile(RasterTile * tile)
103{
104 tile->usage++;
105
106 // Stop tracking the LRU state of the tile as long as it is referenced, avoiding the
107 // tile being evicted during query operations.
108 auto code = tile->identifier.getHashCode();
109 auto lruIt = lruMap.find(code);
110 if (lruIt != lruMap.end()) {
111 lruList.erase(lruIt->second);
112 lruMap.erase(code);
113 }
114}
115
116void Cogs::RasterSource::unrefTile(RasterTile * tile)
117{
118 assert(tile->usage && "Count should not be zero or less when dereferencing a tile.");
119
120 if (--tile->usage == 0) {
121 updateTileUsage(tile);
122 }
123}
124
125void Cogs::RasterSource::removeTile(RasterTile * tile)
126{
127 deallocateTileStorage(tile->data->data);
128
129 residentTiles.erase(tile->identifier.getHashCode());
130
131 tile->data = nullptr;
132}
133
134void Cogs::RasterSource::removeTileUsageData(size_t code)
135{
136 lruList.pop_back();
137 lruMap.erase(code);
138}
139
140void Cogs::RasterSource::evictLruTile()
141{
142 if (!lruList.size()) return;
143
144 size_t code = lruList.back();
145
146 assert(lruMap[code] == (--lruList.end()));
147
148 removeTileUsageData(code);
149
150 auto tile = getTile(code);
151
152 releaseTileData(*tile->data);
153
154 removeTile(tile);
155}
156
157void Cogs::RasterSource::loadTileData(RasterTile * tile, size_t requestGeneration, std::vector<uint8_t> & data, TextureHandle textureHandle, TextureFormat format, size_t size)
158{
159 if (tile->data && tile->data->generation < requestGeneration) {
160 releaseTileData(*tile->data);
161
162 if (!tile->isInvalidated()) {
163 auto & id = tile->identifier;
164
165 LOG_DEBUG(logger, "Releasing overwritten tile [%d, %d, %d].", id.level, id.x, id.y);
166 }
167 }
168
169 tile->data = tile->data ? tile->data : &residentTiles[tile->identifier.getHashCode()];
170
171 if (tile->data->generation >= requestGeneration) {
172 // While processing this response, newer data has already been loaded. Discard
173 // any texture data and buffer data that will not be used.
174 deallocateTileStorage(data);
175 if (HandleIsValid(textureHandle) && textureHandle != invalidTileHandle) {
176 releaseTexture(textureHandle, size);
177 }
178 return;
179 }
180
181 tile->data->textureHandle = textureHandle;
182 tile->data->data = std::move(data);
183 tile->data->format = format;
184 tile->data->size = size;
185 tile->data->generation.store(tile->generation);
186
187 tile->setValid();
188
189 updateTileUsage(tile);
190
191 return;
192}
193
194void Cogs::RasterSource::invalidateTile(const RasterTileIdentifier & id, InvalidationFlags::EInvalidationFlags flags)
195{
196 auto tile = getTile(id);
197
198 if (!tile) {
199 LOG_WARNING(logger, "Unable to invalidate tile with invalid identifier.");
200
201 return;
202 }
203
204 tile->invalidate();
205 tile->unsetRequested();
206
207 if (flags & InvalidationFlags::InvalidateParents) {
208 auto level = id.level;
209
210 while (level-- > 0) {
211 auto parentId = RasterTileIdentifier{ level, id.x / 2, id.y / 2 };
212
213 auto parentTile = getTile(parentId);
214
215 if (!parentTile) {
216 LOG_WARNING(logger, "Unable to invalidate missing parent tile.");
217 continue;
218 }
219
220 parentTile->invalidate();
221 parentTile->unsetRequested();
222 }
223 }
224}
225
226Cogs::RasterTileData * Cogs::RasterSource::getTileData(RasterTile * tile)
227{
228 return tile->data;
229}
230
235{
236 ++generation;
237}
238
243{
244 assert(!hasProcessingRequests() && "Cannot purge cache with requests still in processing.");
245
246 purgeResidentCache();
247
248 // Clear LRU queue.
249 lruList.clear();
250 lruMap.clear();
251
252 activeTiles.clear();
253}
254
259{
260 // For each resident tile
261 // Release texture resource from GPU.
262 // Release memory buffer.
263
264 for (auto & tile : residentTiles) {
265 auto & tileData = tile.second;
266
267 releaseTileData(tileData);
268 }
269
270 residentTiles.clear();
271}
272
273Cogs::TextureHandle Cogs::RasterSource::loadTexture(const unsigned char * data, const int width, const int height, const TextureFormat format, const size_t size)
274{
275 textureBufferSize += size;
276 return device->getTextures()->loadTexture(data, width, height, format);
277}
278
279void Cogs::RasterSource::releaseTexture(TextureHandle texture, const size_t size)
280{
281 device->getTextures()->releaseTexture(texture);
282 textureBufferSize -= size;
283}
284
286{
287 if (HandleIsValid(tileData.textureHandle) && tileData.textureHandle != invalidTileHandle) {
288 releaseTexture(tileData.textureHandle, tileData.size);
289 }
290
291 if (tileData.data.size()) {
292 deallocateTileStorage(tileData.data);
293 }
294}
295
297{
298 for (auto & tileData : oldTileData) {
299 releaseTileData(tileData);
300 }
301
302 oldTileData.clear();
303}
Log implementation class.
Definition: LogManager.h:140
void releaseTileData(RasterTileData &tileData)
Release the given tile data.
void purgeOldData()
Purges orphaned tile data that has been replaced with new contents.
void purgeResidentCache()
Purge tile data resident in memory and on the GPU.
void purgeCache()
Purge all cache, including resident data.
void cancelActiveRequests()
Cancels all active requests.
bool HandleIsValid(const ResourceHandle_t< T > &handle)
Check if the given resource is valid, that is not equal to NoHandle or InvalidHandle.
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Definition: LogManager.h:181