1#include "ClipmapUpdater.h"
3#include "ClipmapLevel.h"
4#include "ClipmapUpdate.h"
5#include "ClipmapTerrainTypes.h"
8#include "NormalUpdater.h"
9#include "RenderContext.h"
10#include "Raster/RasterSource.h"
11#include "Raster/RasterSourceSubscription.h"
13#include "Rendering/IRenderTargets.h"
14#include "Rendering/IBuffers.h"
16#include "Foundation/Logging/Logger.h"
30 Matrix projectionMatrix;
31 Vector2 oneOverTextureSize;
38 Vector2 destinationOffset;
45void Cogs::ClipmapUpdater::initialize(IGraphicsDevice * device, NormalUpdater * normalUpdater)
47 auto positionFormat = initializeGeometry(device);
48 initializeStateObjects(device);
49 initializeShaders(device, positionFormat);
50 initializeSamplerStates(device);
52 this->normalUpdater = normalUpdater;
54 this->upsampler.initialize(device);
57void Cogs::ClipmapUpdater::applyNewTiles(RenderContext & context,
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)
65 const Extent & nextExtent = level->nextExtent;
67 const auto & rasterExtent = level->rasterLevel->getIndexExtent();
69 ClipmapUpdate rasterUpdate(level, rasterExtent);
70 ClipmapUpdate entireLevel(level, nextExtent);
72 entireLevel = ClipmapUpdate::intersectUpdates(rasterUpdate, entireLevel);
74 static std::vector<ClipmapUpdate> intersections;
75 intersections.clear();
77 for (
size_t i = 0; i < numTiles; ++i) {
78 ClipmapUpdate thisTile(level, tiles[i]->extent);
80 ClipmapUpdate intersection = ClipmapUpdate::intersectUpdates(entireLevel, thisTile);
82 if (intersection.getWidth() > 0 && intersection.getHeight() > 0) {
83 intersections.push_back(intersection);
85 if (level->normalLevel) {
86 normalUpdates.push_back(intersection);
89 updatedTiles.push_back(tiles[i]);
90 updatedLevels.push_back(level);
94 if (!intersections.size()) {
return; }
96 updateRasterLevel(context, intersections.data(), intersections.size());
99size_t Cogs::ClipmapUpdater::updateRasterLevel(RenderContext & renderContext, ClipmapUpdate * clipmapUpdates,
const size_t numUpdates)
101 static std::vector<ClipmapUpdate> updates;
102 static std::vector<ClipmapUpdate> clearUpdates;
103 static std::vector<RasterTileRegion> clearTileRegions;
104 static std::vector<RasterTileRegion> tileRegions;
106 static std::vector<const RasterTileRegion * > residentTileRegions;
107 static std::vector<TextureHandle> residentTileTextures;
108 static std::vector<const RasterTileRegion * > missingTileRegions;
110 static std::vector<RasterTile * > needsUpsample;
111 static std::vector<RasterTileRegion * > needsUpsampleRegion;
113 RasterTile clearTile{};
115 clearTileRegions.clear();
118 residentTileRegions.clear();
119 residentTileTextures.clear();
120 missingTileRegions.clear();
122 needsUpsample.clear();
123 needsUpsampleRegion.clear();
125 for (
size_t i = 0; i < numUpdates; ++i) {
127 clearUpdates.clear();
129 auto & currentUpdate = clipmapUpdates[i];
130 auto rasterLevel = currentUpdate.getLevel()->rasterLevel;
132 ClipmapUpdate rasterUpdate(currentUpdate.getLevel(), rasterLevel->getIndexExtent());
134 auto intersected = ClipmapUpdate::intersectUpdates(rasterUpdate, currentUpdate);
136 if (intersected.getWidth() < currentUpdate.getWidth() || intersected.getHeight() < currentUpdate.getHeight()) {
138 ClipmapUpdate::splitUpdateToAvoidWrapping(currentUpdate, clearUpdates);
140 for (
auto & clearUpdate : clearUpdates) {
141 clearTileRegions.push_back(RasterTileRegion{ &clearTile, clearUpdate.getExtent() });
145 ClipmapUpdate::splitUpdateToAvoidWrapping(intersected, updates);
147 for (
auto & update : updates) {
155 clipmapUpdates[i].getLevel()->rasterLevel->getTilesInExtent(extent, tileRegions);
159 for (
auto & clearRegion : clearTileRegions) {
160 residentTileRegions.push_back(&clearRegion);
164 auto source = clipmapUpdates[0].getLevel()->rasterLevel->getSource();
167 ReadLock lock(*source);
169 for (
auto & region : tileRegions) {
172 const bool hasResidentTile = source->tryGetTextureHandle(region.tile, tileTexture);
174 if (hasResidentTile) {
176 needsUpsample.push_back(region.tile);
177 needsUpsampleRegion.push_back(®ion);
179 residentTileRegions.push_back(®ion);
180 residentTileTextures.push_back(tileTexture);
183 missingTileRegions.push_back(®ion);
188 for (
size_t i = 0; i < needsUpsample.size(); ++i) {
189 const auto tileId = needsUpsample[i]->identifier;
191 if (tileId.level == 0)
continue;
193 const RasterTileIdentifier parentId = { tileId.level - 1, tileId.x / 2, tileId.y / 2 };
195 WriteLock lock(*source);
197 auto parentTile = source->getTile(parentId);
198 auto newTextureHandle = upsampler.upsampleTile(renderContext, source, parentTile, needsUpsample[i]);
201 residentTileRegions.push_back(needsUpsampleRegion[i]);
202 residentTileTextures.push_back(newTextureHandle);
206 renderTilesToLevelTexture(renderContext, clipmapUpdates[0].getLevel(), (
const RasterTileRegion **)residentTileRegions.data(), residentTileTextures.data(), residentTileRegions.size());
208 upsampler.upsampleLevelRegions(renderContext, clipmapUpdates[0].getLevel(), missingTileRegions.data(), missingTileRegions.size());
210 return missingTileRegions.size();
213void Cogs::ClipmapUpdater::renderTilesToLevelTexture(RenderContext & renderContext,
214 ClipmapLevel * level,
215 const RasterTileRegion ** regions,
216 const TextureHandle * tileTextures,
217 const size_t numRegions)
219 if (!numRegions)
return;
221 IContext * context = renderContext.context;
223 const RenderTexture * levelTexture = &level->renderTexture;
224 const TextureOrigin originInTextures = level->origin;
225 const Extent & nextExtent = level->nextExtent;
227 const float w =
static_cast<float>(levelTexture->width);
228 const float h =
static_cast<float>(levelTexture->height);
230 context->setEffect(updateEffectHandle);
233 context->setViewport(0, 0, w, h);
235 const uint32_t strides[] = { 2 *
sizeof(float) };
236 context->setVertexBuffers(&vertexBufferHandle, 1, strides,
nullptr);
237 context->setInputLayout(updateLayoutHandle);
239 context->setConstantBuffer(levelBufferBinding, levelBufferHandle);
240 context->setConstantBuffer(regionBufferBinding, regionBufferHandle);
242 glm::mat4 projection;
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);
247 projection = glm::ortho<float>(0, w, 0, h, -1.0f, 1.0f);
251 MappedBuffer<LevelTextureParameters> levelParameters(context, levelBufferHandle,
MapMode::WriteDiscard);
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();
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];
269 const RasterTileRegion * region = regions[i];
271 const int clipmapWidth = nextExtent.getWidth();
272 const int clipmapHeight = nextExtent.getHeight();
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;
277 const int width = region->extent.getWidth();
278 const int height = region->extent.getHeight();
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));
288 if (regionParameters->clear) {
289 const float noData = level->rasterLevel->getNoData();
290 regionParameters->clearValue = glm::vec4(std::isnan(noData) ? 0.0f : noData);
299void Cogs::ClipmapUpdater::applyIfNotLoaded(RenderContext & renderContext,
const WorldOptions & worldOptions, ClipmapLevel * level,
const RasterTileIdentifier & tileId,
const size_t maxLevel)
301 auto source = level->rasterLevel->getSource();
305 ReadLock lock(*source);
307 tile = source->getTile(tileId);
309 if (!tile || tile->isResident())
return;
312 std::vector<const RasterTile *> updatedTiles;
313 std::vector<ClipmapLevel *> updatedLevels;
314 std::vector<ClipmapUpdate> normalUpdates;
316 applyNewTiles(renderContext, level, (
const RasterTile **)&tile, 1, updatedTiles, updatedLevels, normalUpdates);
318 for (
auto & normalUpdate : normalUpdates) {
319 normalUpdater->updateNormalLevel(renderContext, worldOptions, normalUpdate, *normalUpdate.getLevel()->normalLevel);
322 for (
size_t i = 0; i < updatedTiles.size(); ++i) {
323 auto finerLevel = updatedLevels[i]->finerLevel;
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);
334void Cogs::ClipmapUpdater::preloadTiles(ClipmapLevel & clipmapLevel,
const Extent & extent)
337 std::vector<RasterTileRegion> regions;
338 clipmapLevel.rasterLevel->getTilesInExtent(extent, regions);
340 auto source = clipmapLevel.rasterLevel->getSource();
342 WriteLock lock(*source);
344 for (
auto & region : regions) {
345 if (!region.tile->isRequested()) {
346 requestTileLoad(source, clipmapLevel, region.tile);
347 source->refTile(region.tile);
352void Cogs::ClipmapUpdater::requestTileLoad(RasterSource * rasterSource, ClipmapLevel & , RasterTile * tile)
354 TileLoadRequest request = { tile };
356 tile->setRequested();
358 rasterSource->requestTile(request);
361size_t Cogs::ClipmapUpdater::applyNewData(RenderContext & renderContext,
const WorldOptions & worldOptions, RasterSourceSubscription & subscription, std::vector<ClipmapLevel> & clipmapLevels,
const size_t maxLevel)
363 std::vector<TileLoadResponse> responses;
364 const auto maxTiles = renderContext.maxAppliedTilesPerFrame;
366 subscription.getResponses(responses, maxTiles);
368 if (!responses.size())
return 0;
370 std::sort(responses.begin(), responses.end(),
371 [](
const TileLoadResponse & a,
const TileLoadResponse & b)
373 return a.rasterLevel < b.rasterLevel;
376 std::vector<const RasterTile *> tiles;
379 std::vector<const RasterTile *> updatedTiles;
380 std::vector<ClipmapLevel *> updatedLevels;
381 std::vector<ClipmapUpdate> normalUpdates;
383 auto currentLevel = responses.back().rasterLevel;
385 for (
auto & response : responses) {
386 const RasterTile * tile = response.tile;
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);
395 currentLevel = response.rasterLevel;
399 tiles.push_back(tile);
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);
410 for (
auto & normalUpdate : normalUpdates) {
411 normalUpdater->updateNormalLevel(renderContext, worldOptions, normalUpdate, *normalUpdate.getLevel()->normalLevel);
414 for (
size_t i = 0; i < updatedTiles.size(); ++i) {
415 const auto tile = updatedTiles[i];
416 auto finerLevel = updatedLevels[i]->finerLevel;
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);
426 return responses.size();
429void Cogs::ClipmapUpdater::requestTileResidency(ClipmapLevel & level)
431 static std::vector<RasterTileRegion> tileRegions;
434 const Extent & nextExtent = level.nextExtent;
436 level.rasterLevel->getTilesInExtent(nextExtent, tileRegions);
438 auto source = level.rasterLevel->getSource();
440 level.isHeight = source->isHeight;
442 for (
auto & region : tileRegions) {
443 auto tile = region.tile;
445 WriteLock lock(*source);
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);
455 if (level.isHeight && level.minZ == 0 && level.maxZ == 0) {
456 level.minZ = source->minZ;
457 level.maxZ = source->maxZ;
461void Cogs::ClipmapUpdater::initializeStateObjects(IGraphicsDevice * device)
463 IRenderTargets * renderTargets = device->getRenderTargets();
466 rasterizerStateHandle = renderTargets->loadRasterizerState(rasterizerState);
468 DepthStencilState depthStencilState = {
474 depthStencilStateHandle = renderTargets->loadDepthStencilState(depthStencilState);
477void Cogs::ClipmapUpdater::initializeShaders(IGraphicsDevice * device, VertexFormatHandle positionFormat)
479 auto effects = device->getEffects();
480 auto buffers = device->getBuffers();
482 LOG_DEBUG(logger,
"Loading clipmap update effect.");
484 updateEffectHandle = Terrain::EffectLoader::loadEffect(effects,
"ClipmapUpdateVS",
"ClipmapUpdatePS");
487 LOG_ERROR(logger,
"Error loading clipmap update effect.");
493 updateLayoutHandle = buffers->loadInputLayout(&positionFormat, 1, updateEffectHandle);
496 levelBufferBinding = effects->getConstantBufferBinding(updateEffectHandle,
"LevelTextureParameters");
499 regionBufferBinding = effects->getConstantBufferBinding(updateEffectHandle,
"RegionParameters");
501 textureBinding = effects->getTextureBinding(updateEffectHandle,
"imagery", 0);
502 samplerStateBinding = effects->getSamplerStateBinding(updateEffectHandle,
"imagerySampler", 0);
505void Cogs::ClipmapUpdater::initializeSamplerStates(IGraphicsDevice * device)
507 SamplerState nearestClampState = {
512 SamplerState::ComparisonFunction::Never,
514 { 0.0f, 0.0f, 0.0f, 0.0f },
517 nearestClampStateHandle = device->getTextures()->loadSamplerState(nearestClampState);
523 VertexFormatHandle positionFormat = device->getBuffers()->createVertexFormat(&positionElement, 1);
524 vertexBufferHandle = device->getBuffers()->loadVertexBuffer(quadVertices, 6, positionFormat);
525 return positionFormat;
528void Cogs::ClipmapUpdater::setupRenderingState(RenderContext & renderContext)
530 renderContext.context->setDepthStencilState(depthStencilStateHandle);
531 renderContext.context->setRasterizerState(rasterizerStateHandle);
Log implementation class.
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
Contains all Cogs related functionality.
@ 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.
@ ConstantBuffer
The buffer can be bound as input to effects as a constant buffer.
@ Always
Always evaluates to true.
static const Handle_t NoHandle
Represents a handle to nothing.
static const Handle_t InvalidHandle
Represents an invalid handle.
@ WriteDiscard
Write access. When unmapping the graphics system will discard the old contents of the resource.
@ Front
Cull front facing primitives.
@ Clamp
Texture coordinates are clamped to the [0, 1] range.
@ MinMagMipPoint
Point sampling for both minification and magnification.
@ Dynamic
Buffer will be loaded and modified with some frequency.