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 return &activeTiles[id];
61}
62
63bool Cogs::RasterSource::tryGetTextureHandle(const RasterTile * tile, TextureHandle & textureHandle)
64{
65 if (tile->data) {
66 updateTileUsage(tile);
67
68 textureHandle = tile->data->textureHandle;
69
70 return true;
71 }
72
73 return false;
74}
75
76void Cogs::RasterSource::updateTileUsage(const RasterTile * tile)
77{
78 // We do not want to track the invalid tiles in the LRU.
79 if (tile->isOOB()) return;
80
81 // Referenced tiles are excluded from LRU tracking.
82 if (tile->usage != 0) return;
83
84 auto code = tile->identifier.getHashCode();
85
86 // Update tile usage status to avoid cache eviction
87 auto lruIt = lruMap.find(code);
88 if (lruIt != lruMap.end()) {
89 lruList.erase(lruIt->second);
90 }
91
92 // We move the current tile to the front of the list.
93 lruList.push_front(code);
94 lruMap[code] = lruList.begin();
95}
96
97void Cogs::RasterSource::refTile(RasterTile * tile)
98{
99 tile->usage++;
100
101 // Stop tracking the LRU state of the tile as long as it is referenced, avoiding the
102 // tile being evicted during query operations.
103 auto code = tile->identifier.getHashCode();
104 auto lruIt = lruMap.find(code);
105 if (lruIt != lruMap.end()) {
106 lruList.erase(lruIt->second);
107 lruMap.erase(code);
108 }
109}
110
111void Cogs::RasterSource::unrefTile(RasterTile * tile)
112{
113 assert(tile->usage && "Count should not be zero or less when dereferencing a tile.");
114
115 if (--tile->usage == 0) {
116 updateTileUsage(tile);
117 }
118}
119
120void Cogs::RasterSource::removeTile(RasterTile * tile)
121{
122 deallocateTileStorage(tile->data->data);
123
124 residentTiles.erase(tile->identifier.getHashCode());
125
126 tile->data = nullptr;
127}
128
129void Cogs::RasterSource::removeTileUsageData(size_t code)
130{
131 lruList.pop_back();
132 lruMap.erase(code);
133}
134
135void Cogs::RasterSource::evictLruTile()
136{
137 if (!lruList.size()) return;
138
139 size_t code = lruList.back();
140
141 assert(lruMap[code] == (--lruList.end()));
142
143 removeTileUsageData(code);
144
145 auto tile = getTile(code);
146
147 releaseTileData(*tile->data);
148
149 removeTile(tile);
150}
151
152void Cogs::RasterSource::loadTileData(RasterTile * tile, size_t requestGeneration, std::vector<uint8_t> & data, TextureHandle textureHandle, TextureFormat format, size_t size)
153{
154 if (tile->data && tile->data->generation < requestGeneration) {
155 releaseTileData(*tile->data);
156
157 if (!tile->isInvalidated()) {
158 auto & id = tile->identifier;
159
160 LOG_DEBUG(logger, "Releasing overwritten tile [%d, %d, %d].", id.level, id.x, id.y);
161 }
162 }
163
164 tile->data = tile->data ? tile->data : &residentTiles[tile->identifier.getHashCode()];
165
166 if (tile->data->generation >= requestGeneration) {
167 // While processing this response, newer data has already been loaded. Discard
168 // any texture data and buffer data that will not be used.
169 deallocateTileStorage(data);
170 if (HandleIsValid(textureHandle) && textureHandle != invalidTileHandle) {
171 releaseTexture(textureHandle, size);
172 }
173 return;
174 }
175
176 tile->data->textureHandle = textureHandle;
177 tile->data->data = std::move(data);
178 tile->data->format = format;
179 tile->data->size = size;
180 tile->data->generation.store(tile->generation);
181
182 tile->setValid();
183
184 updateTileUsage(tile);
185
186 return;
187}
188
189void Cogs::RasterSource::invalidateTile(const RasterTileIdentifier & id, InvalidationFlags::EInvalidationFlags flags)
190{
191 auto tile = getTile(id);
192
193 if (!tile) {
194 LOG_WARNING(logger, "Unable to invalidate tile with invalid identifier.");
195
196 return;
197 }
198
199 tile->invalidate();
200 tile->unsetRequested();
201
202 if (flags & InvalidationFlags::InvalidateParents) {
203 auto level = id.level;
204
205 while (level-- > 0) {
206 auto parentId = RasterTileIdentifier{ level, id.x / 2, id.y / 2 };
207
208 auto parentTile = getTile(parentId);
209
210 if (!parentTile) {
211 LOG_WARNING(logger, "Unable to invalidate missing parent tile.");
212 continue;
213 }
214
215 parentTile->invalidate();
216 parentTile->unsetRequested();
217 }
218 }
219}
220
221Cogs::RasterTileData * Cogs::RasterSource::getTileData(RasterTile * tile)
222{
223 return tile->data;
224}
225
230{
231 ++generation;
232}
233
238{
239 assert(!hasProcessingRequests() && "Cannot purge cache with requests still in processing.");
240
241 purgeResidentCache();
242
243 // Clear LRU queue.
244 lruList.clear();
245 lruMap.clear();
246
247 activeTiles.clear();
248}
249
254{
255 // For each resident tile
256 // Release texture resource from GPU.
257 // Release memory buffer.
258
259 for (auto & tile : residentTiles) {
260 auto & tileData = tile.second;
261
262 releaseTileData(tileData);
263 }
264
265 residentTiles.clear();
266}
267
268Cogs::TextureHandle Cogs::RasterSource::loadTexture(const unsigned char * data, const int width, const int height, const TextureFormat format, const size_t size)
269{
270 textureBufferSize += size;
271 return device->getTextures()->loadTexture(data, width, height, format);
272}
273
274void Cogs::RasterSource::releaseTexture(TextureHandle texture, const size_t size)
275{
276 device->getTextures()->releaseTexture(texture);
277 textureBufferSize -= size;
278}
279
281{
282 if (HandleIsValid(tileData.textureHandle) && tileData.textureHandle != invalidTileHandle) {
283 releaseTexture(tileData.textureHandle, tileData.size);
284 }
285
286 if (tileData.data.size()) {
287 deallocateTileStorage(tileData.data);
288 }
289}
290
292{
293 for (auto & tileData : oldTileData) {
294 releaseTileData(tileData);
295 }
296
297 oldTileData.clear();
298}
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