UFO 1.0.0
An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown
Loading...
Searching...
No Matches
data.hpp
1
42#ifndef UFO_CONTAINER_TREE_DATA_HPP
43#define UFO_CONTAINER_TREE_DATA_HPP
44
45// UFO
46#include <ufo/compute/compute.hpp>
47#include <ufo/container/tree/container.hpp>
48#include <ufo/container/tree/index.hpp>
49#include <ufo/numeric/math.hpp>
50#include <ufo/utility/type_traits.hpp>
51
52// STL
53#include <algorithm>
54#include <cstddef>
55#include <cstring>
56
57namespace ufo
58{
59template <class Derived, std::size_t Dim, class... Ts>
61{
62 friend Derived;
63
64 public:
65 static constexpr TreeIndex::offset_type const BF = ipow(std::size_t(2), Dim);
66
69
70 using Index = TreeIndex;
71 using pos_type = Index::pos_type;
72
73 static constexpr std::size_t const NumBuffers = sizeof...(Ts);
74
75 public:
76 ~TreeData() { gpuRelease(); }
77
78 [[nodiscard]] LeafData& leafData() { return leaf_data_; }
79
80 [[nodiscard]] LeafData const& leafData() const { return leaf_data_; }
81
82 [[nodiscard]] InnerData& innerData() { return inner_data_; }
83
84 [[nodiscard]] InnerData const& innerData() const { return inner_data_; }
85
92 [[nodiscard]] bool exists(pos_type block) const
93 {
94 return leaf(block) ? leafExists(block) : innerExists(block);
95 }
96
97 [[nodiscard]] bool leafExists(pos_type block) const
98 {
99 assert(leaf(block));
100 return leaf_data_.capacity() > removeLeafType(block);
101 }
102
103 [[nodiscard]] bool innerExists(pos_type block) const
104 {
105 assert(inner(block));
106 return inner_data_.capacity() > removeInnerType(block);
107 }
108
109 bool gpuInit(WGPUPowerPreference power_preference = WGPUPowerPreference_HighPerformance,
110 WGPUBackendType backend_type = WGPUBackendType_Undefined)
111 {
112 if (nullptr != device_) {
113 return false;
114 }
115
116 instance_ = compute::createInstance();
117 adapter_ = compute::createAdapter(instance_, nullptr, power_preference, backend_type);
118 auto required_limits = requiredLimits(adapter_);
119 device_ = compute::createDevice(adapter_, &required_limits);
120 queue_ = compute::queue(device_);
121
122 return true;
123 }
124
125 bool gpuInit(WGPULimits const& required_limits,
126 WGPUSurface compatible_surface = nullptr,
127 WGPUPowerPreference power_preference = WGPUPowerPreference_HighPerformance,
128 WGPUBackendType backend_type = WGPUBackendType_Undefined)
129 {
130 if (nullptr != device_) {
131 return false;
132 }
133
134 instance_ = compute::createInstance();
135 adapter_ = compute::createAdapter(instance_, compatible_surface, power_preference,
136 backend_type);
137 device_ = compute::createDevice(adapter_, &required_limits);
138 queue_ = compute::queue(device_);
139
140 return true;
141 }
142
143 bool gpuInit(WGPUAdapter adapter)
144 {
145 assert(nullptr != adapter);
146
147 return gpuInit(adapter, requiredLimits(adapter));
148 }
149
150 bool gpuInit(WGPUAdapter adapter, WGPULimits const& required_limits)
151 {
152 if (nullptr != device_) {
153 return false;
154 }
155
156 assert(nullptr != adapter);
157
158 // Increase reference count
159 wgpuAdapterAddRef(adapter);
160
161 adapter_ = adapter;
162 device_ = compute::createDevice(adapter, &required_limits);
163 queue_ = compute::queue(device_);
164
165 return true;
166 }
167
168 bool gpuInit(WGPUDevice device)
169 {
170 if (nullptr != device_) {
171 return false;
172 }
173
174 assert(nullptr != device);
175
176 // Increase reference count
177 wgpuDeviceAddRef(device);
178
179 device_ = device;
180 queue_ = compute::queue(device);
181
182 return true;
183 }
184
185 void gpuRelease()
186 {
187 for (auto& buffers : leaf_buffers_) {
188 for (WGPUBuffer& buf : buffers) {
189 if (nullptr != buf) {
190 wgpuBufferRelease(buf);
191 }
192 }
193 buffers.clear();
194 }
195
196 for (auto& buffers : inner_buffers_) {
197 for (WGPUBuffer& buf : buffers) {
198 if (nullptr != buf) {
199 wgpuBufferRelease(buf);
200 }
201 }
202 buffers.clear();
203 }
204
205 if (nullptr != queue_) {
206 wgpuQueueRelease(queue_);
207 queue_ = nullptr;
208 }
209
210 if (nullptr != device_) {
211 wgpuDeviceRelease(device_);
212 device_ = nullptr;
213 }
214
215 if (nullptr != adapter_) {
216 wgpuAdapterRelease(adapter_);
217 adapter_ = nullptr;
218 }
219
220 if (nullptr != instance_) {
221 wgpuInstanceRelease(instance_);
222 instance_ = nullptr;
223 }
224 }
225
226 [[nodiscard]] WGPUDevice gpuDevice() const { return device_; }
227
228 [[nodiscard]] WGPUQueue gpuQueue() const { return queue_; }
229
230 template <class T>
231 [[nodiscard]] std::size_t gpuNumBuffers() const
232 {
233 return gpuNumLeafBuffers<T>() + gpuNumInnerBuffers<T>();
234 }
235
236 template <class T>
237 [[nodiscard]] std::size_t gpuNumLeafBuffers() const
238 {
239 return leaf_buffers_[index_v<T, Ts...>].size();
240 }
241
242 template <class T>
243 [[nodiscard]] std::size_t gpuNumInnerBuffers() const
244 {
245 return inner_buffers_[index_v<T, Ts...>].size();
246 }
247
248 template <class T>
249 [[nodiscard]] WGPUBuffer gpuLeafBuffer(std::size_t index) const
250 {
251 return leaf_buffers_[index_v<T, Ts...>][index];
252 }
253
254 template <class T>
255 [[nodiscard]] WGPUBuffer gpuInnerBuffer(std::size_t index) const
256 {
257 return inner_buffers_[index_v<T, Ts...>][index];
258 }
259
260 template <class T>
261 [[nodiscard]] std::size_t gpuLeafBufferSize(std::size_t index) const
262 {
263 return wgpuBufferGetSize(gpuLeafBuffer<T>(index));
264 }
265
266 template <class T>
267 [[nodiscard]] std::size_t gpuInnerBufferSize(std::size_t index) const
268 {
269 return wgpuBufferGetSize(gpuInnerBuffer<T>(index));
270 }
271
272 void gpuRead()
273 {
274 gpuReadLeaf();
275 gpuReadInner();
276 }
277
278 void gpuReadLeaf() { (gpuReadLeaf<Ts>(), ...); }
279
280 void gpuReadInner() { (gpuReadInner<Ts>(), ...); }
281
282 template <class T>
283 void gpuRead()
284 {
285 gpuReadLeaf<T>();
286 gpuReadInner<T>();
287 }
288
289 template <class T>
290 void gpuReadLeaf()
291 {
292 // TODO: Implement
293 }
294
295 template <class T>
296 void gpuReadInner()
297 {
298 // TODO: Implement
299 }
300
301 bool gpuWrite()
302 {
303 bool a = gpuWriteLeaf();
304 bool b = gpuWriteInner();
305 return a || b;
306 }
307
308 bool gpuWriteLeaf() { return (gpuWriteLeaf<Ts>() | ...); }
309
310 bool gpuWriteInner() { return (gpuWriteInner<Ts>() | ...); }
311
312 template <class T>
313 bool gpuWrite()
314 {
315 bool a = gpuWriteLeaf<T>();
316 bool b = gpuWriteInner<T>();
317 return a || b;
318 }
319
320 template <class T>
321 bool gpuWriteLeaf()
322 {
323 // TODO: Make gpuWriteLeaf and gpuWriteInner with a single implementation.
324
325 assert(nullptr != device_);
326 assert(nullptr != queue_);
327
328 using Block = typename T::template LeafBlock<Dim, BF>;
329
330 std::size_t const size = leaf_data_.template serializedSize<Block>();
331
332 auto& buffers = leaf_buffers_[index_v<T, Ts...>];
333
334 if (0 == size) {
335 for (auto& buf : buffers) {
336 wgpuBufferRelease(buf);
337 }
338 bool empty = buffers.empty();
339 buffers.clear();
340 return !empty;
341 }
342
343 constexpr std::size_t const bucket_size =
344 LeafData::template serializedBucketSize<Block>();
345
346 std::size_t const buckets_per_buffer = max_buffer_size_ / bucket_size;
347 std::size_t const buffer_size = bucket_size * buckets_per_buffer;
348
349 std::size_t const num_buffers = 1 + (size - 1) / buffer_size;
350
351 buffers.reserve(num_buffers);
352
353 bool updated = false;
354
355 auto it = leaf_data_.template beginBucket<Block>();
356 auto last = leaf_data_.template endBucket<Block>();
357
358 for (std::size_t i{}; num_buffers > i; ++i) {
359 if (buffers.size() <= i) {
360 updated = true;
361
362 auto& buffer = buffers.emplace_back(compute::createBuffer(
363 device_, "", buffer_size, WGPUBufferUsage_Storage | WGPUBufferUsage_CopyDst,
364 true));
365
366 assert(nullptr != buffer);
367
368 void* buf = wgpuBufferGetMappedRange(buffer, 0, buffer_size);
369
370 for (std::size_t i{}; buckets_per_buffer > i && it != last; ++i, ++it) {
371 auto& [data, modified] = *it;
372
373 std::memcpy(buf, data.data(), bucket_size);
374 buf = static_cast<void*>(static_cast<unsigned char*>(buf) + bucket_size);
375 modified = false;
376 }
377
378 wgpuBufferUnmap(buffer);
379 } else {
380 WGPUBuffer& buffer = buffers[i];
381
382 std::size_t offset = 0;
383 for (std::size_t i{}; buckets_per_buffer > i && it != last; ++i, ++it) {
384 auto& [data, modified] = *it;
385
386 if (modified) {
387 wgpuQueueWriteBuffer(queue_, buffer, offset, data.data(), bucket_size);
388 modified = false;
389 }
390 offset += bucket_size;
391 }
392 }
393 }
394
395 // FIXME: Probably do not want to release them but instead reuse later when needed,
396 // like how std::vector works. Need a function similar to `shrink_to_fit`. Also, need
397 // to keep track of which ones are "active" and not.
398 for (std::size_t i = num_buffers; buffers.size() > i; ++i) {
399 updated = true;
400 wgpuBufferRelease(buffers[i]);
401 }
402 buffers.resize(num_buffers);
403
404 return updated;
405 }
406
407 template <class T>
408 bool gpuWriteInner()
409 {
410 // TODO: Make gpuWriteLeaf and gpuWriteInner with a single implementation.
411
412 assert(nullptr != device_);
413 assert(nullptr != queue_);
414
415 using Block = typename T::template InnerBlock<Dim, BF>;
416
417 std::size_t const size = inner_data_.template serializedSize<Block>();
418
419 auto& buffers = inner_buffers_[index_v<T, Ts...>];
420
421 if (0 == size) {
422 for (auto& buf : buffers) {
423 wgpuBufferRelease(buf);
424 }
425 bool empty = buffers.empty();
426 buffers.clear();
427 return !empty;
428 }
429
430 constexpr std::size_t const bucket_size =
431 InnerData::template serializedBucketSize<Block>();
432
433 std::size_t const buckets_per_buffer = max_buffer_size_ / bucket_size;
434 std::size_t const buffer_size = bucket_size * buckets_per_buffer;
435
436 std::size_t const num_buffers = 1 + (size - 1) / buffer_size;
437
438 buffers.reserve(num_buffers);
439
440 bool updated = false;
441
442 auto it = inner_data_.template beginBucket<Block>();
443 auto last = inner_data_.template endBucket<Block>();
444
445 for (std::size_t i{}; num_buffers > i; ++i) {
446 if (buffers.size() <= i) {
447 updated = true;
448
449 auto& buffer = buffers.emplace_back(compute::createBuffer(
450 device_, "", buffer_size, WGPUBufferUsage_Storage | WGPUBufferUsage_CopyDst,
451 true));
452
453 assert(nullptr != buffer);
454
455 void* buf = wgpuBufferGetMappedRange(buffer, 0, buffer_size);
456
457 for (std::size_t i{}; buckets_per_buffer > i && it != last; ++i, ++it) {
458 auto& [data, modified] = *it;
459
460 std::memcpy(buf, data.data(), bucket_size);
461 buf = static_cast<void*>(static_cast<unsigned char*>(buf) + bucket_size);
462 modified = false;
463 }
464
465 wgpuBufferUnmap(buffer);
466 } else {
467 WGPUBuffer& buffer = buffers[i];
468
469 std::size_t offset = 0;
470 for (std::size_t i{}; buckets_per_buffer > i && it != last; ++i, ++it) {
471 auto& [data, modified] = *it;
472
473 if (modified) {
474 wgpuQueueWriteBuffer(queue_, buffer, offset, data.data(), bucket_size);
475 modified = false;
476 }
477 offset += bucket_size;
478 }
479 }
480 }
481
482 // FIXME: Probably do not want to release them but instead reuse later when needed,
483 // like how std::vector works. Need a function similar to `shrink_to_fit`. Also, need
484 // to keep track of which ones are "active" and not.
485 for (std::size_t i = num_buffers; buffers.size() > i; ++i) {
486 updated = true;
487 wgpuBufferRelease(buffers[i]);
488 }
489 buffers.resize(num_buffers);
490
491 return updated;
492 }
493
494 protected:
495 [[nodiscard]] static constexpr bool leaf(pos_type block) noexcept
496 {
497 return Index::TYPE_BIT != (Index::TYPE_BIT & block);
498 }
499
500 [[nodiscard]] static constexpr bool inner(pos_type block) noexcept
501 {
502 return !leaf(block);
503 }
504
505 [[nodiscard]] static constexpr pos_type addLeafType(pos_type block) noexcept
506 {
507 return block;
508 }
509
510 [[nodiscard]] static constexpr pos_type removeLeafType(pos_type block) noexcept
511 {
512 return block;
513 }
514
515 [[nodiscard]] static constexpr pos_type addInnerType(pos_type block) noexcept
516 {
517 return Index::TYPE_BIT | block;
518 }
519
520 [[nodiscard]] static constexpr pos_type removeInnerType(pos_type block) noexcept
521 {
522 return ~Index::TYPE_BIT & block;
523 }
524
525 [[nodiscard]] std::size_t size() const { return leafSize() + innerSize(); }
526
527 [[nodiscard]] std::size_t leafSize() const { return leaf_data_.size(); }
528
529 [[nodiscard]] std::size_t innerSize() const { return inner_data_.size(); }
530
531 void reserve(std::size_t cap)
532 {
533 leafReserve((cap + 1) / 2);
534 innerReserve(cap / 2);
535 }
536
537 void leafReserve(std::size_t cap) { leaf_data_.reserve(cap); }
538
539 void innerReserve(std::size_t cap) { inner_data_.reserve(cap); }
540
541 void clear()
542 {
543 leafClear();
544 innerClear();
545 }
546
547 void leafClear() { leaf_data_.clear(); }
548
549 void innerClear() { inner_data_.clear(); }
550
551 [[nodiscard]] pos_type create(bool leaf) { return leaf ? leafCreate() : innerCreate(); }
552
553 [[nodiscard]] pos_type leafCreate() { return addLeafType(leaf_data_.create()); }
554
555 [[nodiscard]] pos_type innerCreate() { return addInnerType(inner_data_.create()); }
556
557 [[nodiscard]] pos_type createThreadSafe(bool leaf)
558 {
559 return leaf ? leafCreateThreadSafe() : innerCreateThreadSafe();
560 }
561
562 [[nodiscard]] pos_type leafCreateThreadSafe()
563 {
564 return addLeafType(leaf_data_.createThreadSafe());
565 }
566
567 [[nodiscard]] pos_type innerCreateThreadSafe()
568 {
569 return addInnerType(inner_data_.createThreadSafe());
570 }
571
572 void erase(pos_type block) { leaf(block) ? leafErase(block) : innerErase(block); }
573
574 void leafErase(pos_type block) { leaf_data_.eraseBlock(removeLeafType(block)); }
575
576 void innerErase(pos_type block) { inner_data_.eraseBlock(removeInnerType(block)); }
577
578 template <class T>
579 [[nodiscard]] T& leafBlock(pos_type block)
580 {
581 assert(leafExists(block));
582 return leaf_data_.template get<T>(removeLeafType(block));
583 }
584
585 template <class T>
586 [[nodiscard]] T const& leafBlock(pos_type block) const
587 {
588 return leaf_data_.template get<T>(removeLeafType(block));
589 }
590
591 template <class T>
592 [[nodiscard]] T& innerBlock(pos_type block)
593 {
594 assert(innerExists(block));
595 return inner_data_.template get<T>(removeInnerType(block));
596 }
597
598 template <class T>
599 [[nodiscard]] T const& innerBlock(pos_type block) const
600 {
601 return inner_data_.template get<T>(removeInnerType(block));
602 }
603
604 private:
605 [[nodiscard]] WGPULimits requiredLimits(WGPUAdapter adapter)
606 {
607 WGPULimits required = WGPU_LIMITS_INIT;
608 WGPULimits supported = WGPU_LIMITS_INIT;
609
610 wgpuAdapterGetLimits(adapter, &supported);
611
612 // These two limits are different because they are "minimum" limits,
613 // they are the only ones we may forward from the adapter's supported limits.
614 required.minUniformBufferOffsetAlignment = supported.minUniformBufferOffsetAlignment;
615 required.minStorageBufferOffsetAlignment = supported.minStorageBufferOffsetAlignment;
616
617 max_buffer_size_ =
618 std::min(max_buffer_size_, static_cast<std::size_t>(supported.maxBufferSize));
619 max_buffer_size_ =
620 std::min(max_buffer_size_,
621 static_cast<std::size_t>(supported.maxStorageBufferBindingSize));
622
623 required.maxBufferSize = max_buffer_size_;
624 required.maxStorageBufferBindingSize = max_buffer_size_;
625
626 required.maxComputeWorkgroupStorageSize = 16352;
627 required.maxComputeInvocationsPerWorkgroup = 256;
628 required.maxComputeWorkgroupSizeX = 256;
629 required.maxComputeWorkgroupSizeY = 256;
630 required.maxComputeWorkgroupSizeZ = 64;
631 required.maxComputeWorkgroupsPerDimension = 65535;
632
633 required.maxUniformBuffersPerShaderStage = 12;
634 required.maxUniformBufferBindingSize = 65536;
635
636 return required;
637 }
638
639 protected:
640 LeafData leaf_data_;
641 InnerData inner_data_;
642
643 WGPUInstance instance_ = nullptr;
644 WGPUAdapter adapter_ = nullptr;
645 WGPUDevice device_ = nullptr;
646 WGPUQueue queue_ = nullptr;
647 std::array<std::vector<WGPUBuffer>, NumBuffers> leaf_buffers_{};
648 std::array<std::vector<WGPUBuffer>, NumBuffers> inner_buffers_{};
649
650 std::size_t max_buffer_size_ = 1'073'741'824 / 2;
651};
652} // namespace ufo
653
654#endif // UFO_CONTAINER_TREE_DATA_HPP
bool exists(pos_type block) const
Checks if a block exists.
Definition data.hpp:92
constexpr T ipow(T base, int exp) noexcept
Computes integer power of a base.
Definition math.hpp:112
All vision-related classes and functions.
Definition cloud.hpp:49
constexpr T b(Lab< T, Flags > color) noexcept
Returns the un-weighted blue–yellow axis value.
Definition lab.hpp:326
constexpr T a(Lab< T, Flags > color) noexcept
Returns the un-weighted green–red axis value.
Definition lab.hpp:310