Cogs.Core
ClipmapUpdater.cpp
1#include "ClipmapUpdater.h"
2
3#include "ClipmapLevel.h"
4#include "ClipmapUpdate.h"
5#include "ClipmapTerrainTypes.h"
6#include "Effects.h"
7#include "Extent.h"
8#include "NormalUpdater.h"
9#include "RenderContext.h"
10#include "Raster/RasterSource.h"
11#include "Raster/RasterSourceSubscription.h"
12
13#include "Rendering/IRenderTargets.h"
14#include "Rendering/IBuffers.h"
15
16#include "Foundation/Logging/Logger.h"
17
18#include <algorithm>
19#include <cassert>
20
21namespace
22{
23 Cogs::Logging::Log logger = Cogs::Logging::getLogger("ClipmapUpdater");
24}
25
26namespace Cogs
27{
29 {
30 Matrix projectionMatrix;
31 Vector2 oneOverTextureSize;
32 };
33
35 {
36 Vector2 sourceOrigin;
37 Vector2 updateSize;
38 Vector2 destinationOffset;
39 int clear;
40 int pad;
41 Vector4 clearValue;
42 };
43}
44
45void Cogs::ClipmapUpdater::initialize(IGraphicsDevice * device, NormalUpdater * normalUpdater)
46{
47 auto positionFormat = initializeGeometry(device);
48 initializeStateObjects(device);
49 initializeShaders(device, positionFormat);
50 initializeSamplerStates(device);
51
52 this->normalUpdater = normalUpdater;
53
54 this->upsampler.initialize(device);
55}
56
57void Cogs::ClipmapUpdater::applyNewTiles(RenderContext & context,
58 ClipmapLevel * level,
59 const RasterTile ** tiles,
60 const size_t numTiles,
61 std::vector<const RasterTile *> & updatedTiles,
62 std::vector<ClipmapLevel *> & updatedLevels,
63 std::vector<ClipmapUpdate> & normalUpdates)
64{
65 const Extent & nextExtent = level->nextExtent;
66
67 const auto & rasterExtent = level->rasterLevel->getIndexExtent();
68
69 ClipmapUpdate rasterUpdate(level, rasterExtent);
70 ClipmapUpdate entireLevel(level, nextExtent);
71
72 entireLevel = ClipmapUpdate::intersectUpdates(rasterUpdate, entireLevel);
73
74 static std::vector<ClipmapUpdate> intersections;
75 intersections.clear();
76
77 for (size_t i = 0; i < numTiles; ++i) {
78 ClipmapUpdate thisTile(level, tiles[i]->extent);
79
80 ClipmapUpdate intersection = ClipmapUpdate::intersectUpdates(entireLevel, thisTile);
81
82 if (intersection.getWidth() > 0 && intersection.getHeight() > 0) {
83 intersections.push_back(intersection);
84
85 if (level->normalLevel) {
86 normalUpdates.push_back(intersection);
87 }
88
89 updatedTiles.push_back(tiles[i]);
90 updatedLevels.push_back(level);
91 }
92 }
93
94 if (!intersections.size()) { return; }
95
96 updateRasterLevel(context, intersections.data(), intersections.size());
97}
98
99size_t Cogs::ClipmapUpdater::updateRasterLevel(RenderContext & renderContext, ClipmapUpdate * clipmapUpdates, const size_t numUpdates)
100{
101 static std::vector<ClipmapUpdate> updates;
102 static std::vector<ClipmapUpdate> clearUpdates;
103 static std::vector<RasterTileRegion> clearTileRegions;
104 static std::vector<RasterTileRegion> tileRegions;
105
106 static std::vector<const RasterTileRegion * > residentTileRegions;
107 static std::vector<TextureHandle> residentTileTextures;
108 static std::vector<const RasterTileRegion * > missingTileRegions;
109
110 static std::vector<RasterTile * > needsUpsample;
111 static std::vector<RasterTileRegion * > needsUpsampleRegion;
112
113 RasterTile clearTile{};
114
115 clearTileRegions.clear();
116 tileRegions.clear();
117
118 residentTileRegions.clear();
119 residentTileTextures.clear();
120 missingTileRegions.clear();
121
122 needsUpsample.clear();
123 needsUpsampleRegion.clear();
124
125 for (size_t i = 0; i < numUpdates; ++i) {
126 updates.clear();
127 clearUpdates.clear();
128
129 auto & currentUpdate = clipmapUpdates[i];
130 auto rasterLevel = currentUpdate.getLevel()->rasterLevel;
131
132 ClipmapUpdate rasterUpdate(currentUpdate.getLevel(), rasterLevel->getIndexExtent());
133
134 auto intersected = ClipmapUpdate::intersectUpdates(rasterUpdate, currentUpdate);
135
136 if (intersected.getWidth() < currentUpdate.getWidth() || intersected.getHeight() < currentUpdate.getHeight()) {
137 // We need to fill in invalid data.
138 ClipmapUpdate::splitUpdateToAvoidWrapping(currentUpdate, clearUpdates);
139
140 for (auto & clearUpdate : clearUpdates) {
141 clearTileRegions.push_back(RasterTileRegion{ &clearTile, clearUpdate.getExtent() });
142 }
143 }
144
145 ClipmapUpdate::splitUpdateToAvoidWrapping(intersected, updates);
146
147 for (auto & update : updates) {
148 Extent extent = {
149 update.getWest(),
150 update.getSouth(),
151 update.getEast(),
152 update.getNorth()
153 };
154
155 clipmapUpdates[i].getLevel()->rasterLevel->getTilesInExtent(extent, tileRegions);
156 }
157 }
158
159 for (auto & clearRegion : clearTileRegions) {
160 residentTileRegions.push_back(&clearRegion);
161 residentTileTextures.push_back(TextureHandle::NoHandle);
162 }
163
164 auto source = clipmapUpdates[0].getLevel()->rasterLevel->getSource();
165
166 {
167 ReadLock lock(*source);
168
169 for (auto & region : tileRegions) {
170 TextureHandle tileTexture = TextureHandle::InvalidHandle;
171
172 const bool hasResidentTile = source->tryGetTextureHandle(region.tile, tileTexture);
173
174 if (hasResidentTile) {
175 if (tileTexture == TextureHandle::NoHandle) {
176 needsUpsample.push_back(region.tile);
177 needsUpsampleRegion.push_back(&region);
178 } else {
179 residentTileRegions.push_back(&region);
180 residentTileTextures.push_back(tileTexture);
181 }
182 } else {
183 missingTileRegions.push_back(&region);
184 }
185 }
186 }
187
188 for (size_t i = 0; i < needsUpsample.size(); ++i) {
189 const auto tileId = needsUpsample[i]->identifier;
190
191 if (tileId.level == 0) continue;
192
193 const RasterTileIdentifier parentId = { tileId.level - 1, tileId.x / 2, tileId.y / 2 };
194
195 WriteLock lock(*source);
196
197 auto parentTile = source->getTile(parentId);
198 auto newTextureHandle = upsampler.upsampleTile(renderContext, source, parentTile, needsUpsample[i]);
199
200 if (newTextureHandle != TextureHandle::NoHandle) {
201 residentTileRegions.push_back(needsUpsampleRegion[i]);
202 residentTileTextures.push_back(newTextureHandle);
203 }
204 }
205
206 renderTilesToLevelTexture(renderContext, clipmapUpdates[0].getLevel(), (const RasterTileRegion **)residentTileRegions.data(), residentTileTextures.data(), residentTileRegions.size());
207
208 upsampler.upsampleLevelRegions(renderContext, clipmapUpdates[0].getLevel(), missingTileRegions.data(), missingTileRegions.size());
209
210 return missingTileRegions.size();
211}
212
213void Cogs::ClipmapUpdater::renderTilesToLevelTexture(RenderContext & renderContext,
214 ClipmapLevel * level,
215 const RasterTileRegion ** regions,
216 const TextureHandle * tileTextures,
217 const size_t numRegions)
218{
219 if (!numRegions) return;
220
221 IContext * context = renderContext.context;
222
223 const RenderTexture * levelTexture = &level->renderTexture;
224 const TextureOrigin originInTextures = level->origin;
225 const Extent & nextExtent = level->nextExtent;
226
227 const float w = static_cast<float>(levelTexture->width);
228 const float h = static_cast<float>(levelTexture->height);
229
230 context->setEffect(updateEffectHandle);
231
232 context->setRenderTarget(levelTexture->renderTarget, DepthStencilHandle::InvalidHandle);
233 context->setViewport(0, 0, w, h);
234
235 const uint32_t strides[] = { 2 * sizeof(float) };
236 context->setVertexBuffers(&vertexBufferHandle, 1, strides, nullptr);
237 context->setInputLayout(updateLayoutHandle);
238
239 context->setConstantBuffer(levelBufferBinding, levelBufferHandle);
240 context->setConstantBuffer(regionBufferBinding, regionBufferHandle);
241
242 glm::mat4 projection;
243
244 if (level->background) {
245 projection = glm::ortho<float>(0, static_cast<float>(nextExtent.getWidth()), 0, static_cast<float>(nextExtent.getHeight()), -1.0f, 1.0f);
246 } else {
247 projection = glm::ortho<float>(0, w, 0, h, -1.0f, 1.0f);
248 }
249
250 {
251 MappedBuffer<LevelTextureParameters> levelParameters(context, levelBufferHandle, MapMode::WriteDiscard);
252
253 if (levelParameters) {
254 levelParameters->projectionMatrix = projection;
255 levelParameters->oneOverTextureSize[0] = 1.0f / (float)level->rasterLevel->getSource()->getTileWidth();
256 levelParameters->oneOverTextureSize[1] = 1.0f / (float)level->rasterLevel->getSource()->getTileHeight();
257 }
258 }
259
260 TextureHandle currentTextureHandle = TextureHandle::NoHandle;
261
262 for (size_t i = 0; i < numRegions; ++i) {
263 if (tileTextures[i] != currentTextureHandle) {
264 context->setTexture(textureBinding, tileTextures[i]);
265 context->setSamplerState(samplerStateBinding, nearestClampStateHandle);
266 currentTextureHandle = tileTextures[i];
267 }
268
269 const RasterTileRegion * region = regions[i];
270
271 const int clipmapWidth = nextExtent.getWidth();
272 const int clipmapHeight = nextExtent.getHeight();
273
274 const int destWest = (originInTextures.x + (region->tile->extent.west + region->extent.west - nextExtent.west)) % clipmapWidth;
275 const int destSouth = (originInTextures.y + (region->tile->extent.south + region->extent.south - nextExtent.south)) % clipmapHeight;
276
277 const int width = region->extent.getWidth();
278 const int height = region->extent.getHeight();
279
280 {
281 MappedBuffer<RegionParameters> regionParameters(context, regionBufferHandle, MapMode::WriteDiscard);
282
283 if (regionParameters) {
284 regionParameters->sourceOrigin = Vector2(static_cast<float>(region->extent.west), static_cast<float>(region->extent.south));
285 regionParameters->updateSize = Vector2(static_cast<float>(width), static_cast<float>(height));
286 regionParameters->destinationOffset = Vector2(static_cast<float>(destWest), static_cast<float>(destSouth));
287 regionParameters->clear = currentTextureHandle == TextureHandle::NoHandle;
288 if (regionParameters->clear) {
289 const float noData = level->rasterLevel->getNoData();
290 regionParameters->clearValue = glm::vec4(std::isnan(noData) ? 0.0f : noData);
291 }
292 }
293 }
294
295 context->draw(PrimitiveType::TriangleList, 0, 6);
296 }
297}
298
299void Cogs::ClipmapUpdater::applyIfNotLoaded(RenderContext & renderContext, const WorldOptions & worldOptions, ClipmapLevel * level, const RasterTileIdentifier & tileId, const size_t maxLevel)
300{
301 auto source = level->rasterLevel->getSource();
302
303 RasterTile * tile;
304 {
305 ReadLock lock(*source);
306
307 tile = source->getTile(tileId);
308
309 if (!tile || tile->isResident()) return;
310 }
311
312 std::vector<const RasterTile *> updatedTiles;
313 std::vector<ClipmapLevel *> updatedLevels;
314 std::vector<ClipmapUpdate> normalUpdates;
315
316 applyNewTiles(renderContext, level, (const RasterTile **)&tile, 1, updatedTiles, updatedLevels, normalUpdates);
317
318 for (auto & normalUpdate : normalUpdates) {
319 normalUpdater->updateNormalLevel(renderContext, worldOptions, normalUpdate, *normalUpdate.getLevel()->normalLevel);
320 }
321
322 for (size_t i = 0; i < updatedTiles.size(); ++i) {
323 auto finerLevel = updatedLevels[i]->finerLevel;
324
325 if (finerLevel && finerLevel->index <= maxLevel) {
326 applyIfNotLoaded(renderContext, worldOptions, finerLevel, tile->identifier.getSouthwestChild(), maxLevel);
327 applyIfNotLoaded(renderContext, worldOptions, finerLevel, tile->identifier.getSoutheastChild(), maxLevel);
328 applyIfNotLoaded(renderContext, worldOptions, finerLevel, tile->identifier.getNorthwestChild(), maxLevel);
329 applyIfNotLoaded(renderContext, worldOptions, finerLevel, tile->identifier.getNortheastChild(), maxLevel);
330 }
331 }
332}
333
334void Cogs::ClipmapUpdater::preloadTiles(ClipmapLevel & clipmapLevel, const Extent & extent)
335{
336 // Preload the entire world at level 0
337 std::vector<RasterTileRegion> regions;
338 clipmapLevel.rasterLevel->getTilesInExtent(extent, regions);
339
340 auto source = clipmapLevel.rasterLevel->getSource();
341
342 WriteLock lock(*source);
343
344 for (auto & region : regions) {
345 if (!region.tile->isRequested()) {
346 requestTileLoad(source, clipmapLevel, region.tile);
347 source->refTile(region.tile);
348 }
349 }
350}
351
352void Cogs::ClipmapUpdater::requestTileLoad(RasterSource * rasterSource, ClipmapLevel & /*level*/, RasterTile * tile)
353{
354 TileLoadRequest request = { tile };
355
356 tile->setRequested();
357
358 rasterSource->requestTile(request);
359}
360
361size_t Cogs::ClipmapUpdater::applyNewData(RenderContext & renderContext, const WorldOptions & worldOptions, RasterSourceSubscription & subscription, std::vector<ClipmapLevel> & clipmapLevels, const size_t maxLevel)
362{
363 std::vector<TileLoadResponse> responses;
364 const auto maxTiles = renderContext.maxAppliedTilesPerFrame;
365
366 subscription.getResponses(responses, maxTiles);
367
368 if (!responses.size()) return 0;
369
370 std::sort(responses.begin(), responses.end(),
371 [](const TileLoadResponse & a, const TileLoadResponse & b)
372 {
373 return a.rasterLevel < b.rasterLevel;
374 });
375
376 std::vector<const RasterTile *> tiles;
377 tiles.reserve(128);
378
379 std::vector<const RasterTile *> updatedTiles;
380 std::vector<ClipmapLevel *> updatedLevels;
381 std::vector<ClipmapUpdate> normalUpdates;
382
383 auto currentLevel = responses.back().rasterLevel;
384
385 for (auto & response : responses) {
386 const RasterTile * tile = response.tile;
387
388 if (response.rasterLevel != currentLevel) {
389 for (auto & c : clipmapLevels) {
390 if (c.index <= maxLevel && c.rasterLevel->getLevel() == currentLevel) {
391 applyNewTiles(renderContext, &c, tiles.data(), tiles.size(), updatedTiles, updatedLevels, normalUpdates);
392 }
393 }
394
395 currentLevel = response.rasterLevel;
396 tiles.clear();
397 }
398
399 tiles.push_back(tile);
400 }
401
402 if (tiles.size()) {
403 for (auto & c : clipmapLevels) {
404 if (c.index <= maxLevel && c.rasterLevel->getLevel() == tiles[0]->identifier.level) {
405 applyNewTiles(renderContext, &c, tiles.data(), tiles.size(), updatedTiles, updatedLevels, normalUpdates);
406 }
407 }
408 }
409
410 for (auto & normalUpdate : normalUpdates) {
411 normalUpdater->updateNormalLevel(renderContext, worldOptions, normalUpdate, *normalUpdate.getLevel()->normalLevel);
412 }
413
414 for (size_t i = 0; i < updatedTiles.size(); ++i) {
415 const auto tile = updatedTiles[i];
416 auto finerLevel = updatedLevels[i]->finerLevel;
417
418 if (finerLevel && finerLevel->index <= maxLevel) {
419 applyIfNotLoaded(renderContext, worldOptions, finerLevel, tile->identifier.getSouthwestChild(), maxLevel);
420 applyIfNotLoaded(renderContext, worldOptions, finerLevel, tile->identifier.getSoutheastChild(), maxLevel);
421 applyIfNotLoaded(renderContext, worldOptions, finerLevel, tile->identifier.getNorthwestChild(), maxLevel);
422 applyIfNotLoaded(renderContext, worldOptions, finerLevel, tile->identifier.getNortheastChild(), maxLevel);
423 }
424 }
425
426 return responses.size();
427}
428
429void Cogs::ClipmapUpdater::requestTileResidency(ClipmapLevel & level)
430{
431 static std::vector<RasterTileRegion> tileRegions;
432 tileRegions.clear();
433
434 const Extent & nextExtent = level.nextExtent;
435
436 level.rasterLevel->getTilesInExtent(nextExtent, tileRegions);
437
438 auto source = level.rasterLevel->getSource();
439
440 level.isHeight = source->isHeight;
441
442 for (auto & region : tileRegions) {
443 auto tile = region.tile;
444
445 WriteLock lock(*source);
446
447 if (!tile->isResident() && !tile->isRequested()) {
448 requestTileLoad(source, level, tile);
449 } else if (level.isHeight && tile->data) {
450 level.minZ = std::min(level.minZ, tile->data->minZ);
451 level.maxZ = std::max(level.maxZ, tile->data->maxZ);
452 }
453 }
454
455 if (level.isHeight && level.minZ == 0 && level.maxZ == 0) {
456 level.minZ = source->minZ;
457 level.maxZ = source->maxZ;
458 }
459}
460
461void Cogs::ClipmapUpdater::initializeStateObjects(IGraphicsDevice * device)
462{
463 IRenderTargets * renderTargets = device->getRenderTargets();
464
465 RasterizerState rasterizerState = { false, RasterizerState::CullMode::Front, false, true, 0.0f, 0.0f, 0.0f, false, false };
466 rasterizerStateHandle = renderTargets->loadRasterizerState(rasterizerState);
467
468 DepthStencilState depthStencilState = {
469 false,
470 false,
472 };
473
474 depthStencilStateHandle = renderTargets->loadDepthStencilState(depthStencilState);
475}
476
477void Cogs::ClipmapUpdater::initializeShaders(IGraphicsDevice * device, VertexFormatHandle positionFormat)
478{
479 auto effects = device->getEffects();
480 auto buffers = device->getBuffers();
481
482 LOG_DEBUG(logger, "Loading clipmap update effect.");
483
484 updateEffectHandle = Terrain::EffectLoader::loadEffect(effects, "ClipmapUpdateVS", "ClipmapUpdatePS");
485
486 if (!HandleIsValid(updateEffectHandle)) {
487 LOG_ERROR(logger, "Error loading clipmap update effect.");
488
489 return;
490 }
491 assert(effects->checkEffect(updateEffectHandle) == Cogs::ResourceStatus::Ready && "Expects synchronous effect loading");
492
493 updateLayoutHandle = buffers->loadInputLayout(&positionFormat, 1, updateEffectHandle);
494
495 levelBufferHandle = buffers->loadBuffer(nullptr, sizeof(LevelTextureParameters), Usage::Dynamic, AccessMode::Write, BindFlags::ConstantBuffer);
496 levelBufferBinding = effects->getConstantBufferBinding(updateEffectHandle, "LevelTextureParameters");
497
498 regionBufferHandle = buffers->loadBuffer(nullptr, sizeof(RegionParameters), Usage::Dynamic, AccessMode::Write, BindFlags::ConstantBuffer);
499 regionBufferBinding = effects->getConstantBufferBinding(updateEffectHandle, "RegionParameters");
500
501 textureBinding = effects->getTextureBinding(updateEffectHandle, "imagery", 0);
502 samplerStateBinding = effects->getSamplerStateBinding(updateEffectHandle, "imagerySampler", 0);
503}
504
505void Cogs::ClipmapUpdater::initializeSamplerStates(IGraphicsDevice * device)
506{
507 SamplerState nearestClampState = {
512 SamplerState::ComparisonFunction::Never,
513 0,
514 { 0.0f, 0.0f, 0.0f, 0.0f },
515 };
516
517 nearestClampStateHandle = device->getTextures()->loadSamplerState(nearestClampState);
518}
519
520Cogs::VertexFormatHandle Cogs::ClipmapUpdater::initializeGeometry(IGraphicsDevice * device)
521{
522 VertexElement positionElement = { 0, DataFormat::X32Y32_FLOAT, ElementSemantic::Position, 0, InputType::VertexData, 0 };
523 VertexFormatHandle positionFormat = device->getBuffers()->createVertexFormat(&positionElement, 1);
524 vertexBufferHandle = device->getBuffers()->loadVertexBuffer(quadVertices, 6, positionFormat);
525 return positionFormat;
526}
527
528void Cogs::ClipmapUpdater::setupRenderingState(RenderContext & renderContext)
529{
530 renderContext.context->setDepthStencilState(depthStencilStateHandle);
531 renderContext.context->setRasterizerState(rasterizerStateHandle);
532}
Log implementation class.
Definition: LogManager.h:140
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
Contains all Cogs related functionality.
Definition: FieldSetter.h:23
@ Ready
The resource has loaded successfully and is ready for use.
@ VertexData
Per vertex data.
@ TriangleList
List of triangles.
@ Position
Position semantic.
@ Write
The buffer can be mapped and written to by the CPU after creation.
Definition: Flags.h:50
@ ConstantBuffer
The buffer can be bound as input to effects as a constant buffer.
Definition: Flags.h:72
@ Always
Always evaluates to true.
static const Handle_t NoHandle
Represents a handle to nothing.
Definition: Common.h:78
static const Handle_t InvalidHandle
Represents an invalid handle.
Definition: Common.h:81
@ WriteDiscard
Write access. When unmapping the graphics system will discard the old contents of the resource.
Definition: Flags.h:103
@ Front
Cull front facing primitives.
@ Clamp
Texture coordinates are clamped to the [0, 1] range.
Definition: SamplerState.h:17
@ MinMagMipPoint
Point sampling for both minification and magnification.
Definition: SamplerState.h:33
@ Dynamic
Buffer will be loaded and modified with some frequency.
Definition: Flags.h:30