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