Cogs.Core
ClipmapUpsampler.cpp
1#include "ClipmapUpsampler.h"
2#include "ClipmapLevel.h"
3#include "Effects.h"
4#include "RenderContext.h"
5#include "Raster/RasterSource.h"
6
7#include "Rendering/IGraphicsDevice.h"
8#include "Rendering/IEffects.h"
9#include "Rendering/IContext.h"
10#include "Rendering/IBuffers.h"
11#include "Rendering/IRenderTargets.h"
12
13#include <cassert>
14
15#include "Foundation/Logging/Logger.h"
16
17namespace
18{
19 Cogs::Logging::Log logger = Cogs::Logging::getLogger("ClipmapUpsampler");
20
21 struct LevelParameters
22 {
23 glm::mat4 projectionMatrix;
24 glm::vec2 oneOverTextureSize;
25 glm::vec2 coarseRatio;
26 };
27
28 struct UpsampleParameters
29 {
30 glm::vec2 sourceOrigin;
31 glm::vec2 updateSize;
32 glm::vec2 destinationOffset;
33
34 float noData;
35 int enableNoData;
36 };
37
38 struct UpsampleTileParameters
39 {
40 glm::mat4 projectionMatrix;
41 glm::vec2 sourceOrigin;
42 };
43}
44
45void Cogs::ClipmapUpsampler::initialize(IGraphicsDevice * device)
46{
47 IEffects * effects = device->getEffects();
48
49 VertexElement positionElement = { 0, DataFormat::X32Y32_FLOAT, ElementSemantic::Position, 0, InputType::VertexData, 0 };
50 VertexFormatHandle positionFormat = device->getBuffers()->createVertexFormat(&positionElement, 1);
51 vertexBufferHandle = device->getBuffers()->loadVertexBuffer(quadVertices, 6, positionFormat);
52
53 LOG_DEBUG(logger, "Loading clipmap upsample effect.");
54
55 upsampleEffectHandle = Terrain::EffectLoader::loadEffect(effects, "ClipmapUpsampleVS", "ClipmapUpsamplePS");
56
57 if (!HandleIsValid(upsampleEffectHandle)) {
58 LOG_ERROR(logger, "Error loading clipmap upsample effect.");
59
60 return;
61 }
62 assert(effects->checkEffect(upsampleEffectHandle) == Cogs::ResourceStatus::Ready && "Expects synchronous effect loading");
63
64 coarseImageryBinding = effects->getTextureBinding(upsampleEffectHandle, "coarseImagery", 0);
65 coarseImagerySamplerBinding = effects->getSamplerStateBinding(upsampleEffectHandle, "coarseImagerySampler", 0);
66
67 levelParameterBinding = effects->getConstantBufferBinding(upsampleEffectHandle, "LevelParameters");
68 levelParameterBufferHandle = device->getBuffers()->loadBuffer(nullptr, sizeof(LevelParameters), Usage::Dynamic, AccessMode::Write, BindFlags::ConstantBuffer);
69
70 upsampleParameterBinding = effects->getConstantBufferBinding(upsampleEffectHandle, "UpsampleParameters");
71 upsampleParameterBufferHandle = device->getBuffers()->loadBuffer(nullptr, sizeof(UpsampleParameters), Usage::Dynamic, AccessMode::Write, BindFlags::ConstantBuffer);
72
73 upsampleLayoutHandle = device->getBuffers()->loadInputLayout(&positionFormat, 1, upsampleEffectHandle);
74
75 LOG_DEBUG(logger, "Loading clipmap upsample tile effect.");
76
77 upsampleTileEffectHandle = Terrain::EffectLoader::loadEffect(effects, "ClipmapUpsampleTileVS", "ClipmapUpsampleTilePS");
78 assert(effects->checkEffect(upsampleTileEffectHandle) == Cogs::ResourceStatus::Ready && "Expects synchronous effect loading");
79
80 upsampleTileLayoutHandle = device->getBuffers()->loadInputLayout(&positionFormat, 1, upsampleTileEffectHandle);
81
82 upsampleTileParameterBinding = effects->getConstantBufferBinding(upsampleTileEffectHandle, "UpsampleTileParameters");
83 upsampleTileParameterBufferHandle = device->getBuffers()->loadBuffer(nullptr, sizeof(UpsampleTileParameters), Usage::Dynamic, AccessMode::Write, BindFlags::ConstantBuffer);
84
85 SamplerState linearRepeatState = {
90 SamplerState::ComparisonFunction::Never,
91 0,
92 { 0.0f, 0.0f, 0.0f, 0.0f },
93 };
94
95 linearRepeatStateHandle = device->getTextures()->loadSamplerState(linearRepeatState);
96
97 SamplerState pointRepeatState = {
102 SamplerState::ComparisonFunction::Never,
103 0,
104 { 0.0f, 0.0f, 0.0f, 0.0f },
105 };
106
107 pointRepeatStateHandle = device->getTextures()->loadSamplerState(pointRepeatState);
108
109 SamplerState linearClampState = {
114 SamplerState::ComparisonFunction::Never,
115 0,
116 { 0.0f, 0.0f, 0.0f, 0.0f },
117 };
118
119 linearClampStateHandle = device->getTextures()->loadSamplerState(linearClampState);
120}
121
122void Cogs::ClipmapUpsampler::upsampleLevelRegions(RenderContext & renderContext,
123 ClipmapLevel * level,
124 const RasterTileRegion ** regions,
125 const size_t numRegions)
126{
127 IContext * context = renderContext.context;
128
129 ClipmapLevel * coarserLevel = level->coarserLevel;
130
131 if (coarserLevel == nullptr || !numRegions) {
132 return;
133 }
134
135 const RenderTexture * levelTexture = &level->renderTexture;
136 const RenderTexture * coarserLevelTexture = &coarserLevel->renderTexture;
137
138 if (coarserLevelTexture == levelTexture) return;
139
140 const TextureOrigin & originInTextures = level->origin;
141 const TextureOrigin & coarserOriginInTextures = coarserLevel->origin;
142
143 const Extent & nextExtent = level->nextExtent;
144 const Extent & coarserNextExtent = coarserLevel->nextExtent;
145
146 const float noData = level->rasterLevel->getNoData();
147 const bool enableNoData = !std::isnan(noData);
148
149 const double ratioX = level->rasterLevel->getPostDeltaLongitude() / coarserLevel->rasterLevel->getPostDeltaLongitude();
150 const double ratioY = level->rasterLevel->getPostDeltaLatitude() / coarserLevel->rasterLevel->getPostDeltaLatitude();
151
152 context->setRenderTarget(levelTexture->renderTarget, DepthStencilHandle::InvalidHandle);
153
154 context->setEffect(upsampleEffectHandle);
155
156 const float width = static_cast<float>(levelTexture->width);
157 const float height = static_cast<float>(levelTexture->height);
158
159 Matrix projection = glm::ortho<float>(0, width, 0, height, -1.0f, 1.0f);
160
161 context->setViewport(0, 0, width, height);
162
163 const uint32_t strides[] = { 2 * sizeof(float) };
164 context->setVertexBuffers(&vertexBufferHandle, 1, strides, nullptr);
165 context->setInputLayout(upsampleLayoutHandle);
166
167 context->setConstantBuffer(levelParameterBinding, levelParameterBufferHandle);
168 context->setConstantBuffer(upsampleParameterBinding, upsampleParameterBufferHandle);
169
170 context->setTexture(coarseImageryBinding, coarserLevelTexture->handle);
171 context->setSamplerState(coarseImagerySamplerBinding, enableNoData ? pointRepeatStateHandle : linearRepeatStateHandle);
172
173 const int fineClipmapWidth = nextExtent.getWidth();
174 const int fineClipmapHeight = nextExtent.getHeight();
175
176 const int coarseClipmapWidth = coarserNextExtent.getWidth();
177 const int coarseClipmapHeight = coarserNextExtent.getHeight();
178
179 const glm::vec2 oneOverTextureSize = { 1.0f / coarserLevelTexture->width, 1.0f / coarserLevelTexture->height };
180 const glm::vec2 coarseRatio = { static_cast<float>(ratioX), static_cast<float>(ratioY) };
181
182 {
183 MappedBuffer<LevelParameters> levelParameters(context, levelParameterBufferHandle, MapMode::WriteDiscard);
184
185 if (levelParameters) {
186 levelParameters->projectionMatrix = projection;
187 levelParameters->oneOverTextureSize = oneOverTextureSize;
188 levelParameters->coarseRatio = coarseRatio;
189 }
190 }
191
192 for (size_t i = 0; i < numRegions; ++i) {
193 const RasterTileRegion * region = regions[i];
194
195 const int destWest = (originInTextures[0] + (region->tile->extent.west + region->extent.west - nextExtent.west)) % fineClipmapWidth;
196 const int destSouth = (originInTextures[1] + (region->tile->extent.south + region->extent.south - nextExtent.south)) % fineClipmapHeight;
197
198 const double sourceWest = (int)(coarserOriginInTextures.x + ((region->tile->extent.west + region->extent.west) * ratioX - coarserNextExtent.west)) % coarseClipmapWidth;
199 const double sourceSouth = (int)(coarserOriginInTextures.y + ((region->tile->extent.south + region->extent.south) * ratioY - coarserNextExtent.south)) % coarseClipmapHeight;
200
201 const int regionWidth = region->extent.getWidth();
202 const int regionHeight = region->extent.getHeight();
203
204 const glm::vec2 sourceOrigin = { static_cast<float>(sourceWest), static_cast<float>(sourceSouth) };
205 const glm::vec2 updateSize = { static_cast<float>(regionWidth), static_cast<float>(regionHeight) };
206 const glm::vec2 destinationOffset = { static_cast<float>(destWest), static_cast<float>(destSouth) };
207
208 {
209 MappedBuffer<UpsampleParameters> upsampleParameters(context, upsampleParameterBufferHandle, MapMode::WriteDiscard);
210
211 if (upsampleParameters) {
212 upsampleParameters->sourceOrigin = sourceOrigin;
213 upsampleParameters->updateSize = updateSize;
214 upsampleParameters->destinationOffset = destinationOffset;
215
216 upsampleParameters->enableNoData = enableNoData;
217 upsampleParameters->noData = noData;
218 }
219 }
220
221 context->draw(PrimitiveType::TriangleList, 0, 6);
222 }
223
224 // Reset the binding since the level may be used as render target on the next draw call (using
225 // the same effect so bindings may not be reset).
226 context->setTexture(coarseImageryBinding, TextureHandle::NoHandle);
227 context->setSamplerState(coarseImagerySamplerBinding, SamplerStateHandle::NoHandle);
228}
229
230namespace
231{
232 float linear(const float a, const float b, float amount)
233 {
234 return a * (1.0f - amount) + b * amount;
235 }
236}
237
238Cogs::TextureHandle Cogs::ClipmapUpsampler::upsampleTile(RenderContext & renderContext, RasterSource * source, RasterTile * parentTile, RasterTile * tile)
239{
240 IContext * context = renderContext.context;
241 IRenderTargets * renderTargets = renderContext.device->getRenderTargets();
242
243 const auto tileId = tile->identifier;
244
245 auto tileData = source->getTileData(tile);
246 const auto parentTileData = source->getTileData(parentTile);
247
248 if (!tileData || !parentTileData) {
250 }
251
252 const size_t width = static_cast<size_t>(tile->getWidth());
253 const size_t height = static_cast<size_t>(tile->getHeight());
254 const auto bpp = Cogs::getBlockSize(parentTileData->format);
255 const auto textureSize = Cogs::getTextureSize(static_cast<int>(width), static_cast<int>(height), parentTileData->format);
256
257 bool useMemCopyUpsample = true;
258
259 if (useMemCopyUpsample) {
260 if (!parentTileData->data.size() || tileData->textureHandle != TextureHandle::NoHandle) {
262 }
263
264 source->allocateTileStorage(textureSize, tileData->data);
265
266 auto bytes = tileData->data.data();
267
268 if (parentTileData->format == TextureFormat::R32_FLOAT) {
269 const float * parentData = reinterpret_cast<float *>(parentTileData->data.data());
270
271 for (size_t y = 0; y < height; ++y) {
272 for (size_t x = 0; x < width; ++x) {
273 size_t xx = x / 2 + ((tileId.x % 2) ? width / 2 : 0);
274 size_t yy = y / 2 + ((tileId.y % 2) ? height / 2 : 0);
275
276 float v1 = parentData[yy * width + xx];
277 float v2 = parentData[yy * width + xx + ((xx < width - 1) ? 1 : 0)];
278
279 float v3 = parentData[(yy + ((yy < height - 1) ? 1 : 0)) * width + xx];
280 float v4 = parentData[(yy + ((yy < height - 1) ? 1 : 0)) * width + xx + ((xx < width - 1) ? 1 : 0)];
281
282 ((float *)bytes)[y * width + x] = linear(
283 linear(v1, v2, (x % 2) ? 0.5f : 0.0f),
284 linear(v3, v4, (x % 2) ? 0.5f : 0.0f),
285 (y % 2) ? 0.5f : 0.0f);
286 }
287 }
288 } else {
289 const auto parentBytes = parentTileData->data.data();
290
291 for (size_t y = 0; y < height; ++y) {
292 for (size_t x = 0; x < width; ++x) {
293 size_t xx = x / 2 + ((tileId.x % 2) ? width / 2 : 0);
294 size_t yy = y / 2 + ((tileId.y % 2) ? height / 2 : 0);
295
296 std::memcpy(bytes + (y * width + x) * bpp, parentBytes + (yy * width + xx) * bpp, bpp);
297 }
298 }
299 }
300
301 auto tileTexture = source->loadTexture(bytes, static_cast<int>(width), static_cast<int>(height), parentTileData->format, textureSize);
302
303 tileData->size = textureSize;
304 tileData->format = parentTileData->format;
305 tileData->textureHandle = tileTexture;
306
307 return tileTexture;
308 } else {
309 auto tileTexture = source->loadTexture(nullptr, static_cast<int>(width), static_cast<int>(height), parentTileData->format, textureSize);
310
311 auto renderTarget = renderTargets->createRenderTarget(&tileTexture, 1);
312 auto depthTarget = renderTargets->createDepthStencilTarget(renderTarget);
313
314 context->setRenderTarget(renderTarget, depthTarget);
315
316 float color[] = { 0, 1, 0, 1, };
317 context->clearRenderTarget(color);
318
319 context->setEffect(upsampleTileEffectHandle);
320
321 Matrix projection = glm::ortho(0.0f, 1.0f, 0.0f, 1.0f, -1.0f, 1.0f);
322 const float sourceOrigin[] = { (tileId.x % 2) ? 0.5f : 0.0f, (tileId.y % 2) ? 0.5f : 0.0f };
323 {
324 MappedBuffer<UpsampleTileParameters> upsampleTileParameters(context, upsampleTileParameterBufferHandle, MapMode::WriteDiscard);
325 if (upsampleTileParameters) {
326 upsampleTileParameters->projectionMatrix = projection;
327 upsampleTileParameters->sourceOrigin.x = sourceOrigin[0];
328 upsampleTileParameters->sourceOrigin.y = sourceOrigin[1];
329 }
330 }
331 context->setConstantBuffer(upsampleTileParameterBinding, upsampleTileParameterBufferHandle);
332
333 context->setViewport(0, 0, static_cast<float>(width), static_cast<float>(height));
334
335 const uint32_t strides[] = { 2 * sizeof(float) };
336 context->setVertexBuffers(&vertexBufferHandle, 1, strides, nullptr);
337 context->setInputLayout(upsampleTileLayoutHandle);
338
339 context->setTexture("coarseImagery", 0, parentTileData->textureHandle);
340 context->setSamplerState("TextureSampler", 0, linearClampStateHandle);
341
342 context->draw(PrimitiveType::TriangleList, 0, 6);
343
344 tileData->size = textureSize;
345 tileData->format = parentTileData->format;
346 tileData->textureHandle = tileTexture;
347 tileData->data.clear();
348
349 renderTargets->releaseDepthStencilTarget(depthTarget);
350 renderTargets->releaseRenderTarget(renderTarget);
351
352 return tileTexture;
353 }
354}
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
@ 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
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
@ Clamp
Texture coordinates are clamped to the [0, 1] range.
Definition: SamplerState.h:17
@ Wrap
Texture coordinates automatically wrap around to [0, 1] range.
Definition: SamplerState.h:19
@ MinMagMipPoint
Point sampling for both minification and magnification.
Definition: SamplerState.h:33
@ MinMagMipLinear
Linear sampling for both minification and magnification.
Definition: SamplerState.h:35
@ Dynamic
Buffer will be loaded and modified with some frequency.
Definition: Flags.h:30