Cogs.Core
BridgeRasterSource.cpp
1#include "BridgeRasterSource.h"
2#include "RasterConversion.h"
3#include "RasterSourceSubscription.h"
4
5#include "../ClipmapTerrainTypes.h"
6
7#include "Rendering/ITextures.h"
8#include "Rendering/IContext.h"
9
10#include "Foundation/Logging/Logger.h"
11
12#include <memory>
13
14namespace
15{
16 Cogs::Logging::Log logger = Cogs::Logging::getLogger("BridgeRasterSource");
17}
18
19namespace Cogs
20{
22 {
23 RequestClosure(size_t generation, size_t tileGeneration) : generation(generation), tileGeneration(tileGeneration) {}
24
25 uint64_t providerId = 0;
26 BridgeRasterSource * source = nullptr;
27 size_t generation = 0;
28 size_t id = 0;
29 size_t rasterLevel = 0;
30 size_t tileGeneration = 0;
31 };
32
33 void loadTileData(BridgeRasterSource * source,
34 RasterTile * tile,
35 const size_t requestGeneration,
36 const size_t rasterLevel,
37 const size_t /*id*/,
38 const uint8_t * data,
39 int width,
40 int height,
41 TextureFormat format,
42 bool isHeight = false,
43 float minZ = 0,
44 float maxZ = 0)
45 {
46#ifdef _WIN32
47 source->device->initializeThread();
48#endif
49
50 const size_t cacheLimit = source->cacheSize;
51
52 while (source->textureBufferSize > cacheLimit) {
53 WriteLock sourceLock(*source);
54
55 source->evictLruTile();
56 }
57
58 size_t textureSize;
59 TextureHandle textureHandle;
60
61 const bool supportQuery = (source->getFlags() & RasterSourceFlags::Queryable) != 0;
62 bool compressCache = (source->getFlags() & RasterSourceFlags::CompressCache) != 0;
63 const bool isCompressible = Cogs::isCompressible(format);
64
65 if (compressCache && !isCompressible) {
66 if (!source->suppressCompressionError) {
67 source->suppressCompressionError = true;
68 LOG_ERROR(logger, "Compression only supported for R8G8B8A8_UNORM or R8G8B8A8_UNORM_SRGB format.");
69 }
70
71 compressCache = false;
72 } else if (compressCache && (width % 4 != 0 || height % 4 != 0)) {
73 if (!source->suppressDimensionError) {
74 LOG_ERROR(logger, "Tile dimensions must be a multiple of 4 to support block compression.");
75 source->suppressDimensionError = true;
76 }
77
78 compressCache = false;
79 }
80
81 if (compressCache) {
82 textureSize = Cogs::getTextureSize(width, height, format) / 4;
83
84 std::vector<uint8_t> compressed(textureSize);
85 auto compressedFormat = compressTileData(compressed, data, format, width, height);
86
87 textureHandle = source->loadTexture(compressed.data(), width, height, compressedFormat, textureSize);
88 } else {
89 textureSize = Cogs::getTextureSize(width, height, format);
90 textureHandle = source->loadTexture(data, width, height, format, textureSize);
91 }
92
93 source->device->waitForCommandSync();
94
95 TileLoadResponse response{};
96 response.rasterLevel = static_cast<int>(rasterLevel);
97 response.tile = tile;
98
99 std::vector<uint8_t> buffer;
100
101 if (supportQuery) {
102 source->allocateTileStorage(textureSize, buffer);
103 buffer.assign(data, data + textureSize);
104 }
105
106 {
107 WriteLock lock(*source);
108
109 source->loadTileData(tile, requestGeneration, buffer, textureHandle, format, textureSize);
110
111 if (isHeight) {
112 source->isHeight = true;
113
114 tile->data->minZ = minZ;
115 tile->data->maxZ = maxZ;
116
117 source->minZ = std::min(source->minZ, minZ);
118 source->maxZ = std::max(source->maxZ, maxZ);
119 }
120 }
121
122 std::lock_guard<std::mutex> lock(source->subscriberMutex);
123
124 for (auto handler : source->subscribers) {
125 handler->postResponse(response);
126 }
127 }
128
129 void loadCallbackInternal(void * userData, int /*level*/, int /*x*/, int /*y*/, TileData * dataPtr)
130 {
131 assert(userData && "User data must point to valid closure.");
132 assert(dataPtr && "Tile data must point to valid structure.");
133
134 std::unique_ptr<RequestClosure> closure(reinterpret_cast<RequestClosure *>(userData));
135 BridgeRasterSource * source = closure->source;
136
137 // Enter processing state for this request. Ensures the tile data etc. is not deleted while the lock
138 // is in scope.
139 std::lock_guard<RasterSource::Processing> processingLock(source->processing);
140 --source->closures;
141
142 assert(source->closures >= 0 && "Too many closures received.");
143
144 if (closure->generation < source->generation) {
145 // Drop responses to requests made before source was invalidated.
146 return;
147 }
148
149 RasterTile * tile = nullptr;
150 {
151 ReadLock lock(*source);
152
153 tile = source->getTile(closure->id);
154 }
155
156 if (tile->data && closure->tileGeneration < tile->data->generation) {
157 // Drop responses to requests that have been preempted by later requests.
158 return;
159 }
160
161 auto tileData = reinterpret_cast<const TileData *>(dataPtr);
162 auto data = reinterpret_cast<const uint8_t *>(tileData->imageData);
163 auto format = tileData->format;
164
165 if (data) {
166 if (tileData->shouldConvert() || tileData->shouldFlip()) {
167 std::vector<uint8_t> convertedData;
168 auto targetFormat = convertBitmapData(data, tileData, convertedData);
169
170 if (targetFormat == TextureFormat::Unknown) {
171 LOG_ERROR(logger, "Texture format %hu not convertible.", tileData->format);
172 return;
173 }
174
175 loadTileData(closure->source, tile, closure->tileGeneration, closure->rasterLevel, closure->id, convertedData.data(), tileData->width, tileData->height, targetFormat, tileData->isHeight(), tileData->minZ, tileData->maxZ);
176 } else {
177 loadTileData(closure->source, tile, closure->tileGeneration, closure->rasterLevel, closure->id, data, tileData->width, tileData->height, format, tileData->isHeight(), tileData->minZ, tileData->maxZ);
178 }
179 } else {
180 WriteLock lock(*source);
181
182 source->loadEmptyTileData(tile);
183 }
184 }
185
186#define CHECKED(c, p) p
187
188 void loadCallback(void * userData, int level, int x, int y, TileData * dataPtr)
189 {
190 RequestClosure * closure = reinterpret_cast<RequestClosure *>(userData);
191 BridgeRasterSource * source = closure->source;
192
193 assert(closure && "Invalid closure in tile load callback.");
194 assert(source && "Invalid raster source in tile load closure.");
195
196 // loadCallbackInternal will take ownership of the closure and destroy it.
197 CHECKED(source->context, loadCallbackInternal(userData, level, x, y, dataPtr));
198 }
199}
200
201Cogs::BridgeRasterSource::BridgeRasterSource(CogsContext * context, RasterSourceParameters * parameters) :
202 context(context),
203 cacheSize(parameters->cacheSize),
204 providerId(parameters->id),
205 flags(parameters->flags),
206 requestCallback(parameters->tileRequestCallback),
207 allocationCallback(parameters->allocationCallback),
208 deallocationCallback(parameters->deallocationCallback)
209{
210 const bool supportQuery = (flags & RasterSourceFlags::Queryable) != 0;
211 const bool compressCache = (flags & RasterSourceFlags::CompressCache) != 0;
212
213 if (supportQuery && compressCache) {
214 LOG_ERROR(logger, "Raster source cannot support queries on compressed data.");
215 }
216
217 format = static_cast<TextureFormat>(parameters->format);
218 name = std::string(parameters->name);
219
220 GeodeticExtent geodeticExtent(parameters->minX, parameters->minY, parameters->maxX, parameters->maxY);
221
222 tileLongitudePosts = parameters->tileWidth;
223 tileLatitudePosts = parameters->tileHeight;
224
225 const size_t numLevels = parameters->numLevels;
226
227 double deltaLongitude = parameters->deltaX;
228 double deltaLatitude = parameters->deltaY;
229
230 const double extentX = geodeticExtent.getEast() - geodeticExtent.getWest();
231 const double extentY = geodeticExtent.getNorth() - geodeticExtent.getSouth();
232
233 double tilePostDeltaLongitude = deltaLongitude / (double)tileLongitudePosts;
234 double tilePostDeltaLatitude = deltaLatitude / (double)tileLatitudePosts;
235
236 for (size_t i = 0; i < numLevels; ++i) {
237 const int longitudePosts = static_cast<int>(std::ceil(extentX / deltaLongitude)) * tileLongitudePosts;
238 const int latitudePosts = static_cast<int>(std::ceil(extentY / deltaLatitude)) * tileLatitudePosts;
239
240 assert(longitudePosts > 0 && "Number of longitude posts must be a positive integer.");
241 assert(latitudePosts > 0 && "Number of latitude posts must be a positive integer.");
242
243 levels.emplace_back(this,
244 static_cast<int>(i),
245 geodeticExtent,
246 parameters->noData,
247 longitudePosts,
248 latitudePosts,
249 tileLongitudePosts,
250 tileLatitudePosts,
251 tilePostDeltaLongitude,
252 tilePostDeltaLatitude);
253
254 deltaLongitude /= 2.0;
255 deltaLatitude /= 2.0;
256
257 tilePostDeltaLongitude /= 2.0;
258 tilePostDeltaLatitude /= 2.0;
259 }
260}
261
262Cogs::BridgeRasterSource::~BridgeRasterSource()
263{
264 assert(!processing.getCount() && "Request processing should be completed before destruction.");
265}
266
267void Cogs::BridgeRasterSource::requestTile(const TileLoadRequest & request)
268{
269 const RasterTileIdentifier & identifier = request.tile->identifier;
270
271 if (request.tile->isResident()) {
272 return;
273 }
274
275 if (request.tile->isOOB()) {
276 loadInvalidTile(request.tile);
277
278 return;
279 }
280
281 auto closure = std::make_unique<RequestClosure>(generation, request.tile->generation);
282 closure->providerId = this->providerId;
283 closure->source = this;
284 closure->rasterLevel = request.tile->identifier.level;
285 closure->id = identifier.getHashCode();
286
287 ++closures;
288
289 if (!requestCallback(closure.get(), loadCallback, identifier.level, identifier.x, identifier.y)) {
290 // The request will not be processed, in most cases due to an identical request already being processed.
291 --closures;
292 } else {
293 closure.release();
294 }
295}
296
297void Cogs::BridgeRasterSource::allocateTileStorage(const size_t textureSize, std::vector<uint8_t> & buffer)
298{
299 if (allocationCallback) {
300 allocationCallback(nullptr, static_cast<unsigned int>(textureSize));
301 }
302
303 buffer.resize(textureSize);
304}
305
306void Cogs::BridgeRasterSource::deallocateTileStorage(std::vector<uint8_t> & buffer)
307{
308 if (buffer.size()) {
309 if (deallocationCallback) {
310 deallocationCallback(nullptr, static_cast<unsigned int>(buffer.size()));
311 }
312
313 buffer.clear();
314 buffer.shrink_to_fit();
315 }
316}
317
318namespace
319{
320 static std::vector<uint8_t> emptyBuffer;
321}
322
323void Cogs::BridgeRasterSource::loadInvalidTile(RasterTile * tile)
324{
325 if (invalidTileHandle == TextureHandle::InvalidHandle) {
326 uint8_t bytes[] = {
327 110, 110, 110, 0,
328 127, 127, 127, 0,
329 127, 127, 127, 0,
330 110, 110, 110, 0,
331 };
332
333 invalidTileHandle = device->getTextures()->loadTexture(bytes, 2, 2, TextureFormat::R8G8B8A8_UNORM);
334 }
335
336 loadTileData(tile, tile->generation, emptyBuffer, invalidTileHandle, TextureFormat::R8G8B8A8_UNORM, 0);
337}
338
339void Cogs::BridgeRasterSource::loadEmptyTileData(RasterTile * tile)
340{
341 loadTileData(tile, tile->generation, emptyBuffer, TextureHandle::NoHandle, TextureFormat::R8G8B8A8_UNORM, 0);
342}
Log implementation class.
Definition: LogManager.h:139
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Definition: LogManager.h:180
Contains all Cogs related functionality.
Definition: FieldSetter.h:23
static const Handle_t NoHandle
Represents a handle to nothing.
Definition: Common.h:77
static const Handle_t InvalidHandle
Represents an invalid handle.
Definition: Common.h:80