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"
29 Matrix projectionMatrix;
30 Vector2 oneOverTextureSize;
37 Vector2 destinationOffset;
44void Cogs::ClipmapUpdater::initialize(IGraphicsDevice * device, NormalUpdater * normalUpdater)
46 auto positionFormat = initializeGeometry(device);
47 initializeStateObjects(device);
48 initializeShaders(device, positionFormat);
49 initializeSamplerStates(device);
51 this->normalUpdater = normalUpdater;
53 this->upsampler.initialize(device);
56void Cogs::ClipmapUpdater::applyNewTiles(RenderContext & context,
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)
64 const Extent & nextExtent = level->nextExtent;
66 const auto & rasterExtent = level->rasterLevel->getIndexExtent();
68 ClipmapUpdate rasterUpdate(level, rasterExtent);
69 ClipmapUpdate entireLevel(level, nextExtent);
71 entireLevel = ClipmapUpdate::intersectUpdates(rasterUpdate, entireLevel);
73 static std::vector<ClipmapUpdate> intersections;
74 intersections.clear();
76 for (
size_t i = 0; i < numTiles; ++i) {
77 ClipmapUpdate thisTile(level, tiles[i]->extent);
79 ClipmapUpdate intersection = ClipmapUpdate::intersectUpdates(entireLevel, thisTile);
81 if (intersection.getWidth() > 0 && intersection.getHeight() > 0) {
82 intersections.push_back(intersection);
84 if (level->normalLevel) {
85 normalUpdates.push_back(intersection);
88 updatedTiles.push_back(tiles[i]);
89 updatedLevels.push_back(level);
93 if (!intersections.size()) {
return; }
95 updateRasterLevel(context, intersections.data(), intersections.size());
98size_t Cogs::ClipmapUpdater::updateRasterLevel(RenderContext & renderContext, ClipmapUpdate * clipmapUpdates,
const size_t numUpdates)
100 static std::vector<ClipmapUpdate> updates;
101 static std::vector<ClipmapUpdate> clearUpdates;
102 static std::vector<RasterTileRegion> clearTileRegions;
103 static std::vector<RasterTileRegion> tileRegions;
105 static std::vector<const RasterTileRegion * > residentTileRegions;
106 static std::vector<TextureHandle> residentTileTextures;
107 static std::vector<const RasterTileRegion * > missingTileRegions;
109 static std::vector<RasterTile * > needsUpsample;
110 static std::vector<RasterTileRegion * > needsUpsampleRegion;
112 RasterTile clearTile{};
114 clearTileRegions.clear();
117 residentTileRegions.clear();
118 residentTileTextures.clear();
119 missingTileRegions.clear();
121 needsUpsample.clear();
122 needsUpsampleRegion.clear();
124 for (
size_t i = 0; i < numUpdates; ++i) {
126 clearUpdates.clear();
128 auto & currentUpdate = clipmapUpdates[i];
129 auto rasterLevel = currentUpdate.getLevel()->rasterLevel;
131 ClipmapUpdate rasterUpdate(currentUpdate.getLevel(), rasterLevel->getIndexExtent());
133 auto intersected = ClipmapUpdate::intersectUpdates(rasterUpdate, currentUpdate);
135 if (intersected.getWidth() < currentUpdate.getWidth() || intersected.getHeight() < currentUpdate.getHeight()) {
137 ClipmapUpdate::splitUpdateToAvoidWrapping(currentUpdate, clearUpdates);
139 for (
auto & clearUpdate : clearUpdates) {
140 clearTileRegions.push_back(RasterTileRegion{ &clearTile, clearUpdate.getExtent() });
144 ClipmapUpdate::splitUpdateToAvoidWrapping(intersected, updates);
146 for (
auto & update : updates) {
154 clipmapUpdates[i].getLevel()->rasterLevel->getTilesInExtent(extent, tileRegions);
158 for (
auto & clearRegion : clearTileRegions) {
159 residentTileRegions.push_back(&clearRegion);
163 auto source = clipmapUpdates[0].getLevel()->rasterLevel->getSource();
166 ReadLock lock(*source);
168 for (
auto & region : tileRegions) {
171 const bool hasResidentTile = source->tryGetTextureHandle(region.tile, tileTexture);
173 if (hasResidentTile) {
175 needsUpsample.push_back(region.tile);
176 needsUpsampleRegion.push_back(®ion);
178 residentTileRegions.push_back(®ion);
179 residentTileTextures.push_back(tileTexture);
182 missingTileRegions.push_back(®ion);
187 for (
size_t i = 0; i < needsUpsample.size(); ++i) {
188 const auto tileId = needsUpsample[i]->identifier;
190 if (tileId.level == 0)
continue;
192 const RasterTileIdentifier parentId = { tileId.level - 1, tileId.x / 2, tileId.y / 2 };
194 WriteLock lock(*source);
196 auto parentTile = source->getTile(parentId);
197 auto newTextureHandle = upsampler.upsampleTile(renderContext, source, parentTile, needsUpsample[i]);
200 residentTileRegions.push_back(needsUpsampleRegion[i]);
201 residentTileTextures.push_back(newTextureHandle);
205 renderTilesToLevelTexture(renderContext, clipmapUpdates[0].getLevel(), (
const RasterTileRegion **)residentTileRegions.data(), residentTileTextures.data(), residentTileRegions.size());
207 upsampler.upsampleLevelRegions(renderContext, clipmapUpdates[0].getLevel(), missingTileRegions.data(), missingTileRegions.size());
209 return missingTileRegions.size();
212void Cogs::ClipmapUpdater::renderTilesToLevelTexture(RenderContext & renderContext,
213 ClipmapLevel * level,
214 const RasterTileRegion ** regions,
215 const TextureHandle * tileTextures,
216 const size_t numRegions)
218 if (!numRegions)
return;
220 IContext * context = renderContext.context;
222 const RenderTexture * levelTexture = &level->renderTexture;
223 const TextureOrigin originInTextures = level->origin;
224 const Extent & nextExtent = level->nextExtent;
226 const float w =
static_cast<float>(levelTexture->width);
227 const float h =
static_cast<float>(levelTexture->height);
229 context->setEffect(updateEffectHandle);
232 context->setViewport(0, 0, w, h);
234 const uint32_t strides[] = { 2 *
sizeof(float) };
235 context->setVertexBuffers(&vertexBufferHandle, 1, strides,
nullptr);
236 context->setInputLayout(updateLayoutHandle);
238 context->setConstantBuffer(levelBufferBinding, levelBufferHandle);
239 context->setConstantBuffer(regionBufferBinding, regionBufferHandle);
241 glm::mat4 projection;
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);
246 projection = glm::ortho<float>(0, w, 0, h, -1.0f, 1.0f);
250 MappedBuffer<LevelTextureParameters> levelParameters(context, levelBufferHandle,
MapMode::WriteDiscard);
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();
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];
268 const RasterTileRegion * region = regions[i];
270 const int clipmapWidth = nextExtent.getWidth();
271 const int clipmapHeight = nextExtent.getHeight();
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;
276 const int width = region->extent.getWidth();
277 const int height = region->extent.getHeight();
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));
287 if (regionParameters->clear) {
288 const float noData = level->rasterLevel->getNoData();
289 regionParameters->clearValue = glm::vec4(std::isnan(noData) ? 0.0f : noData);
298void Cogs::ClipmapUpdater::applyIfNotLoaded(RenderContext & renderContext,
const WorldOptions & worldOptions, ClipmapLevel * level,
const RasterTileIdentifier & tileId,
const size_t maxLevel)
300 auto source = level->rasterLevel->getSource();
304 ReadLock lock(*source);
306 tile = source->getTile(tileId);
308 if (!tile || tile->isResident())
return;
311 std::vector<const RasterTile *> updatedTiles;
312 std::vector<ClipmapLevel *> updatedLevels;
313 std::vector<ClipmapUpdate> normalUpdates;
315 applyNewTiles(renderContext, level, (
const RasterTile **)&tile, 1, updatedTiles, updatedLevels, normalUpdates);
317 for (
auto & normalUpdate : normalUpdates) {
318 normalUpdater->updateNormalLevel(renderContext, worldOptions, normalUpdate, *normalUpdate.getLevel()->normalLevel);
321 for (
size_t i = 0; i < updatedTiles.size(); ++i) {
322 auto finerLevel = updatedLevels[i]->finerLevel;
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);
333void Cogs::ClipmapUpdater::preloadTiles(ClipmapLevel & clipmapLevel,
const Extent & extent)
336 std::vector<RasterTileRegion> regions;
337 clipmapLevel.rasterLevel->getTilesInExtent(extent, regions);
339 auto source = clipmapLevel.rasterLevel->getSource();
341 WriteLock lock(*source);
343 for (
auto & region : regions) {
344 if (!region.tile->isRequested()) {
345 requestTileLoad(source, clipmapLevel, region.tile);
346 source->refTile(region.tile);
351void Cogs::ClipmapUpdater::requestTileLoad(RasterSource * rasterSource, ClipmapLevel & , RasterTile * tile)
353 TileLoadRequest request = { tile };
355 tile->setRequested();
357 rasterSource->requestTile(request);
360size_t Cogs::ClipmapUpdater::applyNewData(RenderContext & renderContext,
const WorldOptions & worldOptions, RasterSourceSubscription & subscription, std::vector<ClipmapLevel> & clipmapLevels,
const size_t maxLevel)
362 std::vector<TileLoadResponse> responses;
363 const auto maxTiles = renderContext.maxAppliedTilesPerFrame;
365 subscription.getResponses(responses, maxTiles);
367 if (!responses.size())
return 0;
369 std::sort(responses.begin(), responses.end(),
370 [](
const TileLoadResponse & a,
const TileLoadResponse & b)
372 return a.rasterLevel < b.rasterLevel;
375 std::vector<const RasterTile *> tiles;
378 std::vector<const RasterTile *> updatedTiles;
379 std::vector<ClipmapLevel *> updatedLevels;
380 std::vector<ClipmapUpdate> normalUpdates;
382 auto currentLevel = responses.back().rasterLevel;
384 for (
auto & response : responses) {
385 const RasterTile * tile = response.tile;
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);
394 currentLevel = response.rasterLevel;
398 tiles.push_back(tile);
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);
409 for (
auto & normalUpdate : normalUpdates) {
410 normalUpdater->updateNormalLevel(renderContext, worldOptions, normalUpdate, *normalUpdate.getLevel()->normalLevel);
413 for (
size_t i = 0; i < updatedTiles.size(); ++i) {
414 const auto tile = updatedTiles[i];
415 auto finerLevel = updatedLevels[i]->finerLevel;
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);
425 return responses.size();
428void Cogs::ClipmapUpdater::requestTileResidency(ClipmapLevel & level)
430 static std::vector<RasterTileRegion> tileRegions;
433 const Extent & nextExtent = level.nextExtent;
435 level.rasterLevel->getTilesInExtent(nextExtent, tileRegions);
437 auto source = level.rasterLevel->getSource();
439 level.isHeight = source->isHeight;
441 for (
auto & region : tileRegions) {
442 auto tile = region.tile;
444 WriteLock lock(*source);
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);
454 if (level.isHeight && level.minZ == 0 && level.maxZ == 0) {
455 level.minZ = source->minZ;
456 level.maxZ = source->maxZ;
460void Cogs::ClipmapUpdater::initializeStateObjects(IGraphicsDevice * device)
462 IRenderTargets * renderTargets = device->getRenderTargets();
465 rasterizerStateHandle = renderTargets->loadRasterizerState(rasterizerState);
467 DepthStencilState depthStencilState = {
473 depthStencilStateHandle = renderTargets->loadDepthStencilState(depthStencilState);
476void Cogs::ClipmapUpdater::initializeShaders(IGraphicsDevice * device, VertexFormatHandle positionFormat)
478 auto effects = device->getEffects();
479 auto buffers = device->getBuffers();
481 LOG_DEBUG(logger,
"Loading clipmap update effect.");
483 updateEffectHandle = Terrain::EffectLoader::loadEffect(effects,
"ClipmapUpdateVS",
"ClipmapUpdatePS");
486 LOG_ERROR(logger,
"Error loading clipmap update effect.");
491 updateLayoutHandle = buffers->loadInputLayout(&positionFormat, 1, updateEffectHandle);
494 levelBufferBinding = effects->getConstantBufferBinding(updateEffectHandle,
"LevelTextureParameters");
497 regionBufferBinding = effects->getConstantBufferBinding(updateEffectHandle,
"RegionParameters");
499 textureBinding = effects->getTextureBinding(updateEffectHandle,
"imagery", 0);
500 samplerStateBinding = effects->getSamplerStateBinding(updateEffectHandle,
"imagerySampler", 0);
503void Cogs::ClipmapUpdater::initializeSamplerStates(IGraphicsDevice * device)
505 SamplerState nearestClampState = {
510 SamplerState::ComparisonFunction::Never,
512 { 0.0f, 0.0f, 0.0f, 0.0f },
515 nearestClampStateHandle = device->getTextures()->loadSamplerState(nearestClampState);
521 VertexFormatHandle positionFormat = device->getBuffers()->createVertexFormat(&positionElement, 1);
522 vertexBufferHandle = device->getBuffers()->loadVertexBuffer(quadVertices, 6, positionFormat);
523 return positionFormat;
526void Cogs::ClipmapUpdater::setupRenderingState(RenderContext & renderContext)
528 renderContext.context->setDepthStencilState(depthStencilStateHandle);
529 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.
@ 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.