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