Cogs.Core
OctAtlas.cpp
1#include "Renderer/RenderStateUpdater.h"
2#include "../Systems/OctSystem.h"
3#include "OctAtlas.h"
4
5#include "Foundation/Logging/Logger.h"
6
7namespace {
8
10
11
12}
13
14void Cogs::Core::Volumetric::OctAtlas::reset(const DrawContext * renderingContext, const OctData& octData, const uint32_t tileSize, const uint32_t gpuCacheSize, const uint32_t currentTimestamp)
15{
16 auto * device = renderingContext->device;
17
18 auto * iContext = device->getImmediateContext();
19 auto * textures = device->getTextures();
20
21 if (HandleIsValid(textureAtlas0)) textures->releaseTexture(textureAtlas0);
22 textureAtlas0 = Cogs::TextureHandle::NoHandle;
23
24 if (HandleIsValid(textureAtlas1)) textures->releaseTexture(textureAtlas1);
25 textureAtlas1 = Cogs::TextureHandle::NoHandle;
26
27 slots.resize(gpuCacheSize * gpuCacheSize * gpuCacheSize);
28 for (size_t i = 0; i < slots.size(); i++) {
29 slots[i].tileKey = ~uint64_t(0);
30 slots[i].timestampNeeded = currentTimestamp - 1u;
31 slots[i].timestapUpdated = currentTimestamp - 1u;
32 slots[i].flags = Slot::None;
33 }
34
35 slotsLUT.clear();
36
37 slotsLRU.clear();
38 for (size_t i = 1; i < slots.size(); i++) {
39 slotsLRU.push_back(uint32_t(i));
40 }
41
42 // Create new atlas texture
43 TextureDescription desc;
44 auto size = tileSize * gpuCacheSize;
45 desc.target = ResourceDimensions::Texture3D;
46 desc.width = size;
47 desc.height = size;
48 desc.depth = size;
49 desc.layers = 1;
50 desc.faces = 1;
51 desc.levels = 1;
52 desc.samples = 1;
53 desc.flags = TextureFlags::Default;
54 desc.format = TextureFormat::R32G32B32A32_FLOAT;
55 textureAtlas0 = textures->loadTexture(desc, nullptr);
56 switch (octData.source)
57 {
58 case OctSource::Value: break;
59 case OctSource::ValueAge:
60 desc.format = TextureFormat::R32_FLOAT;
61 textureAtlas1 = textures->loadTexture(desc, nullptr);
62 break;
63 }
64
65
66
67 // stager stuff:
68 for (auto & h : toCopy) {
69 textures->releaseTexture(h.texture0);
70 if(HandleIsValid(h.texture1)) textures->releaseTexture(h.texture1);
71 }
72 toCopy.clear();
73 for (auto & h : stagingUnused) {
74 textures->releaseTexture(h.texture0);
75 if (HandleIsValid(h.texture1)) textures->releaseTexture(h.texture1);
76 }
77 stagingUnused.clear();
78
79 // Create new staging textures
80 while (stagingUnused.size() < 10) {
81 TextureDescription stagingDesc;
82 stagingDesc.target = ResourceDimensions::Texture3D;
83 stagingDesc.width = tileSize;
84 stagingDesc.height = tileSize;
85 stagingDesc.depth = tileSize;
86 stagingDesc.layers = 1;
87 stagingDesc.faces = 1;
88 stagingDesc.levels = 1;
89 stagingDesc.samples = 1;
90 stagingDesc.format = TextureFormat::R32G32B32A32_FLOAT;
91 stagingDesc.flags = TextureFlags::UsageWriteStaging;
92 auto tex0 = textures->loadTexture(stagingDesc, nullptr);
94 switch (octData.source)
95 {
96 case OctSource::Value: break;
97 case OctSource::ValueAge:
98 stagingDesc.format = TextureFormat::R32_FLOAT;
99 tex1 = textures->loadTexture(stagingDesc, nullptr);
100 break;
101 }
102 stagingUnused.push_back(Item{ tex0, tex1, {}, {} });
103 }
104 assert(!stagingUnused.empty() && "Must have at least one staging texture");
105
106 // Stage zero-texture
107 toCopy.push_back(stagingUnused.back());
108 stagingUnused.pop_back();
109 uint32_t rowPitch, depthPitch;
110 if (auto* ptr = reinterpret_cast<char*>(iContext->map(toCopy.back().texture0, MapMode::Write, &rowPitch, &depthPitch)); ptr != nullptr) {
111 for (size_t k = 0; k < tileSize; k++) {
112 for (size_t j = 0; j < tileSize; j++) {
113 auto * p = reinterpret_cast<glm::vec4*>(ptr + k*depthPitch + j*rowPitch);
114 for (size_t i = 0; i < tileSize; i++) {
115 auto ii = (i >> 3) & 1;
116 auto jj = (j >> 3) & 1;
117 auto kk = (k >> 3) & 1;
118 *p++ = glm::vec4(1, 1, 1, (ii ^ jj ^ kk) ? 1.f : 0.f);
119 }
120 }
121 }
122 iContext->unmap(toCopy.back().texture0);
123 }
124 if (HandleIsValid(toCopy.back().texture1)) {
125 if (auto* ptr = reinterpret_cast<char*>(iContext->map(toCopy.back().texture0, MapMode::Write, &rowPitch, &depthPitch)); ptr != nullptr) {
126 for (size_t k = 0; k < tileSize; k++) {
127 for (size_t j = 0; j < tileSize; j++) {
128 auto * p = reinterpret_cast<float*>(ptr + k*depthPitch + j*rowPitch);
129 for (size_t i = 0; i < tileSize; i++) {
130 auto ii = (i >> 3) & 1;
131 auto jj = (j >> 3) & 1;
132 auto kk = (k >> 3) & 1;
133 *p++ = (ii ^ jj ^ kk) ? 1.f : 0.f;
134 }
135 }
136 }
137 iContext->unmap(toCopy.back().texture1);
138 }
139 }
140 toCopy.back().slot = 0;
141 toCopy.back().delay = 1;
142}
143
144
145unsigned Cogs::Core::Volumetric::OctAtlas::checkTile(const uint64_t tileKey, const uint32_t nodeTimestamp, const uint32_t currentTimestamp)
146{
147 auto it = slotsLUT.find(tileKey);
148 if (it != slotsLUT.end()) {
149 auto & slot = slots[it->second];
150 slot.timestampNeeded = currentTimestamp;
151
152 auto nodeAge = currentTimestamp - nodeTimestamp;
153 auto cacheAge = currentTimestamp - slot.timestapUpdated;
154
155 if ((slot.flags & Slot::InStaging) != 0) {
156 // Don't re-issue requests while a tile is in staging.
157 return 0;
158 }
159 else if (nodeAge < cacheAge || (slot.flags & Slot::Invalid) != 0) {
160 //LOG_DEBUG(logger, "expired tile %llx", tileKey);
161 slot.flags = (Slot::Flags)(slot.flags | Slot::Invalid);
162 return cacheAge;
163 }
164 else {
165 // Tile should be good and ready to be used.
166 return 0;
167 }
168 }
169 else {
170 //LOG_DEBUG(logger, "Unknown tile %llx", tileKey);
171 return std::numeric_limits<unsigned>::max(); // Never seen tile, max out-of-date.
172 }
173}
174
175void Cogs::Core::Volumetric::OctAtlas::enforceOrder(const uint32_t currentTimestamp)
176{
177 slotsLRU.sort([&](const auto& a, const auto& b) -> bool
178 {
179 return (currentTimestamp - slots[a].timestampNeeded) > (currentTimestamp - slots[b].timestampNeeded);
180 });
181}
182
183
184glm::uvec4 Cogs::Core::Volumetric::OctAtlas::slotPosition(const OctComponent& octComp, const uint64_t tileKey) const
185{
186 unsigned slot = 0;
187 auto it = slotsLUT.find(tileKey);
188 if (it != slotsLUT.end()) {
189 //if ((slots[it->second].flags & Slot::InStaging) == 0) { // Replace InStaging with NoData to remove flickering.
190 if ((slots[it->second].flags & Slot::NoData) == 0) {
191 slot = it->second;
192 assert(slot != 0);
193 }
194 }
195 return glm::uvec4(slot % octComp.gpuCacheSize,
196 (slot / octComp.gpuCacheSize) % octComp.gpuCacheSize,
197 (slot / octComp.gpuCacheSize) / octComp.gpuCacheSize,
198 slot);
199}
200
201
202
203void Cogs::Core::Volumetric::OctAtlas::handleStaging(const DrawContext * renderingContext, OctComponent& octComp, OctData& octData, const uint32_t currentTimestamp, unsigned delay)
204{
205 delay = std::max(1u, delay);
206
207 auto * device = renderingContext->device;
208 auto * iContext = device->getImmediateContext();
209
210 // FIXME: check layout hash.
211
212 for (auto & item : toCopy) {
213 item.delay--;
214
215 if (item.delay == 0) {
216 unsigned i = octComp.tileSize*(item.slot % octComp.gpuCacheSize);
217 unsigned j = octComp.tileSize*((item.slot / octComp.gpuCacheSize) % octComp.gpuCacheSize);
218 unsigned k = octComp.tileSize*((item.slot / octComp.gpuCacheSize) / octComp.gpuCacheSize);
219 iContext->copyTexture(octData.atlas.textureAtlas0, 0, i, j, k, item.texture0, 0);
220 if (HandleIsValid(octData.atlas.textureAtlas1)) {
221 iContext->copyTexture(octData.atlas.textureAtlas1, 0, i, j, k, item.texture1, 0);
222 }
223 //LOG_DEBUG(logger, "Copied tile %llx into slot %x, flags=%x", slots[item.slot].tileKey, item.slot, slots[item.slot].flags);
224 slots[item.slot].flags = OctAtlas::Slot::None;
225 }
226
227 }
228
229 enforceOrder(octData.currentTimestamp);
230
231
232 if (!stagingUnused.empty()) {
233 while (!octData.tileResponses.empty() && !stagingUnused.empty()) {
234 uint32_t slot = 0;
235
236 auto res = std::move(octData.tileResponses.back());
237 octData.tileResponses.pop_back();
238
239 auto it = slotsLUT.find(res->tileKey);
240 if (it != slotsLUT.end()) {
241 slot = it->second; // updating existing tile
242 //LOG_DEBUG(logger, "Updating existing tile %llx, slot %d", res->tileKey, slot);
243 }
244 else if (slots[slotsLRU.front()].timestampNeeded != currentTimestamp) {
245 slot = slotsLRU.front();
246 slotsLRU.pop_front();
247 slotsLRU.push_back(slot);
248
249 octData.atlas.slotsLUT.erase(slots[slot].tileKey);
250 octData.atlas.slotsLUT[res->tileKey] = slot;
251 slots[slot].flags = OctAtlas::Slot::NoData;
252 //LOG_DEBUG(logger, "Evicting tile %llx, slot %d", slots[slot].tileKey, slot);
253 }
254
255 if (slot != 0) {
256 assert(res->data0.size() == octComp.tileSize * octComp.tileSize * octComp.tileSize);
257
258 slots[slot].tileKey = res->tileKey;
259 slots[slot].clientData = res->clientData;
260 slots[slot].timestapUpdated = res->timestamp;
261 slots[slot].flags = (OctAtlas::Slot::Flags)(slots[slot].flags | OctAtlas::Slot::InStaging);
262
263 toCopy.push_back(stagingUnused.back());
264 stagingUnused.pop_back();
265 auto & stage = toCopy.back();
266 stage.slot = slot;
267 stage.delay = delay;
268
269 uint32_t rowPitch, depthPitch;
270 if (auto* ptr = reinterpret_cast<char*>(iContext->map(stage.texture0, MapMode::Write, &rowPitch, &depthPitch)); ptr != nullptr) {
271 for (size_t k = 0; k < octComp.tileSize; k++) {
272 for (size_t j = 0; j < octComp.tileSize; j++) {
273 std::memcpy(ptr + k*depthPitch + j*rowPitch,
274 res->data0.data() + ((k*octComp.tileSize + j)*octComp.tileSize),
275 sizeof(glm::vec4)*octComp.tileSize);
276
277 }
278 }
279 iContext->unmap(stage.texture0);
280 }
281 if (HandleIsValid(stage.texture1)) {
282 assert(res->data1.size() == size_t(octComp.tileSize) * size_t(octComp.tileSize) * size_t(octComp.tileSize));
283 if (auto* ptr = reinterpret_cast<char*>(iContext->map(stage.texture1, MapMode::Write, &rowPitch, &depthPitch)); ptr != nullptr) {
284 for (size_t k = 0; k < octComp.tileSize; k++) {
285 for (size_t j = 0; j < octComp.tileSize; j++) {
286 std::memcpy(ptr + k*depthPitch + j*rowPitch,
287 res->data1.data() + ((k*octComp.tileSize + j)*octComp.tileSize),
288 sizeof(float)*octComp.tileSize);
289 }
290 }
291 iContext->unmap(stage.texture0);
292 }
293 }
294 //LOG_DEBUG(logger, "Inserted %llx into slot %x", slots[slot].tileKey, slot);
295 }
296 else {
297 LOG_DEBUG(logger, "Trashing");
298 }
299
300 octData.tileResponsesStash.push_back(std::move(res));
301 }
302 }
303 else {
304 //LOG_DEBUG(logger, "No staging buffers available");
305 }
306
307 // recycle copied.
308 std::vector<Item> unfinishedCopies;
309 unfinishedCopies.reserve(toCopy.size());
310 for (auto & item : toCopy) {
311 if (item.delay == 0) {
312 stagingUnused.push_back(item);
313 }
314 else {
315 unfinishedCopies.push_back(item);
316 }
317 }
318 toCopy.clear();
319 toCopy.swap(unfinishedCopies);
320}
321
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
@ NoData
Set when data is for a fresh tile when we don't even have outdated data.
Definition: OctAtlas.h:27
@ InStaging
Set while the tile is in the staging queue. It has updated data, but it isn't available yet.
Definition: OctAtlas.h:25
static const Handle_t NoHandle
Represents a handle to nothing.
Definition: Common.h:77
@ Default
Default usage, the texture can be loaded once and bound and sampled in shaders.
Definition: Flags.h:116