MayaFlux 0.4.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
TextureContainer.cpp
Go to the documentation of this file.
2
9
10namespace MayaFlux::Kakshya {
11
13using Portal::Graphics::TextureLoom;
14
15namespace {
16
18
19 enum class StorageKind : uint8_t { U8,
20 U16,
21 F32 };
22
23 StorageKind storage_kind_for(ImageFormat format)
24 {
25 switch (format) {
26 case ImageFormat::R8:
27 case ImageFormat::RG8:
28 case ImageFormat::RGB8:
29 case ImageFormat::RGBA8:
30 case ImageFormat::RGBA8_SRGB:
31 case ImageFormat::BGRA8:
32 case ImageFormat::BGRA8_SRGB:
33 case ImageFormat::DEPTH24:
34 case ImageFormat::DEPTH24_STENCIL8:
35 return StorageKind::U8;
36
37 case ImageFormat::R16:
38 case ImageFormat::RG16:
39 case ImageFormat::RGBA16:
40 case ImageFormat::R16F:
41 case ImageFormat::RG16F:
42 case ImageFormat::RGBA16F:
43 case ImageFormat::DEPTH16:
44 return StorageKind::U16;
45
46 case ImageFormat::R32F:
47 case ImageFormat::RG32F:
48 case ImageFormat::RGBA32F:
49 case ImageFormat::DEPTH32F:
50 return StorageKind::F32;
51
52 default:
53 return StorageKind::U8;
54 }
55 }
56
57 bool is_float_format(ImageFormat format)
58 {
59 switch (format) {
60 case ImageFormat::R16F:
61 case ImageFormat::RG16F:
62 case ImageFormat::RGBA16F:
63 case ImageFormat::R32F:
64 case ImageFormat::RG32F:
65 case ImageFormat::RGBA32F:
66 case ImageFormat::DEPTH32F:
67 return true;
68 default:
69 return false;
70 }
71 }
72
73 DataVariant make_empty_storage(ImageFormat format, size_t element_count)
74 {
75 switch (storage_kind_for(format)) {
76 case StorageKind::U8:
77 return std::vector<uint8_t>(element_count, 0U);
78 case StorageKind::U16:
79 return std::vector<uint16_t>(element_count, 0U);
80 case StorageKind::F32:
81 return std::vector<float>(element_count, 0.0F);
82 }
83 return std::vector<uint8_t>(element_count, 0U);
84 }
85
86 // Raw byte pointer + byte size from a DataVariant that is known to be
87 // one of the three image alternatives.
88 std::pair<const uint8_t*, size_t> variant_bytes(const DataVariant& v)
89 {
90 return std::visit(
91 [](const auto& vec) -> std::pair<const uint8_t*, size_t> {
92 using T = typename std::decay_t<decltype(vec)>::value_type;
93 if constexpr (std::is_same_v<T, uint8_t>
94 || std::is_same_v<T, uint16_t>
95 || std::is_same_v<T, float>) {
96 return {
97 reinterpret_cast<const uint8_t*>(vec.data()),
98 vec.size() * sizeof(T)
99 };
100 } else {
101 return { nullptr, 0 };
102 }
103 },
104 v);
105 }
106
107 std::pair<uint8_t*, size_t> variant_bytes_mut(DataVariant& v)
108 {
109 return std::visit(
110 [](auto& vec) -> std::pair<uint8_t*, size_t> {
111 using T = typename std::decay_t<decltype(vec)>::value_type;
112 if constexpr (std::is_same_v<T, uint8_t>
113 || std::is_same_v<T, uint16_t>
114 || std::is_same_v<T, float>) {
115 return {
116 reinterpret_cast<uint8_t*>(vec.data()),
117 vec.size() * sizeof(T)
118 };
119 } else {
120 return { nullptr, 0 };
121 }
122 },
123 v);
124 }
125
126 double read_normalized_at(const DataVariant& v, ImageFormat format, size_t elem_index)
127 {
128 return std::visit(
129 [format, elem_index](const auto& vec) -> double {
130 using T = typename std::decay_t<decltype(vec)>::value_type;
131 if (elem_index >= vec.size())
132 return 0.0;
133 if constexpr (std::is_same_v<T, uint8_t>) {
134 return static_cast<double>(vec[elem_index]) / 255.0;
135 } else if constexpr (std::is_same_v<T, uint16_t>) {
136 return is_float_format(format)
137 ? static_cast<double>(vec[elem_index])
138 : static_cast<double>(vec[elem_index]) / 65535.0;
139 } else if constexpr (std::is_same_v<T, float>) {
140 return static_cast<double>(vec[elem_index]);
141 } else {
142 return 0.0;
143 }
144 },
145 v);
146 }
147
148 void write_normalized_at(DataVariant& v, ImageFormat format, size_t elem_index, double value)
149 {
150 std::visit(
151 [format, elem_index, value](auto& vec) {
152 using T = typename std::decay_t<decltype(vec)>::value_type;
153 if (elem_index >= vec.size())
154 return;
155 if constexpr (std::is_same_v<T, uint8_t>) {
156 vec[elem_index] = static_cast<uint8_t>(
157 std::clamp(value * 255.0, 0.0, 255.0));
158 } else if constexpr (std::is_same_v<T, uint16_t>) {
159 if (is_float_format(format)) {
160 vec[elem_index] = static_cast<uint16_t>(value);
161 } else {
162 vec[elem_index] = static_cast<uint16_t>(
163 std::clamp(value * 65535.0, 0.0, 65535.0));
164 }
165 } else if constexpr (std::is_same_v<T, float>) {
166 vec[elem_index] = static_cast<float>(value);
167 }
168 },
169 v);
170 }
171
172} // namespace
173
174//=============================================================================
175// Construction
176//=============================================================================
177
178TextureContainer::TextureContainer(uint32_t width, uint32_t height, ImageFormat format, uint32_t layers)
179 : m_width(width)
180 , m_height(height)
181 , m_format(format)
182 , m_channels(TextureLoom::get_channel_count(format))
183 , m_bpp(TextureLoom::get_bytes_per_pixel(format))
184{
185 const size_t element_count = static_cast<size_t>(m_width) * m_height * m_channels;
186
187 for (uint32_t i = 0; i < std::max(layers, 1U); ++i) {
188 m_data.emplace_back(make_empty_storage(m_format, element_count));
189 m_processed_data.emplace_back(make_empty_storage(m_format, element_count));
190 }
191
192 setup_dimensions();
193
194 m_ready_for_processing.store(true);
195
196 MF_INFO(Journal::Component::Kakshya, Journal::Context::ContainerProcessing,
197 "TextureContainer created: {}x{} layers={} fmt={} bpp={}",
198 m_width, m_height, m_data.size(), static_cast<int>(m_format), m_bpp);
199}
200
201TextureContainer::TextureContainer(const std::shared_ptr<Core::VKImage>& image, ImageFormat format)
202 : TextureContainer(image->get_width(), image->get_height(), format)
203{
204 from_image(image, 0);
205}
206
207//=============================================================================
208// Setup
209//=============================================================================
210
211void TextureContainer::setup_dimensions()
212{
213 const uint64_t h = m_height;
214 const uint64_t w = m_width;
215 const uint64_t c = m_channels;
216 const auto n = static_cast<uint64_t>(m_data.size());
217
218 m_structure = ContainerDataStructure::image_interleaved();
219
220 if (n > 1) {
221 m_structure.dimensions = DataDimension::create_dimensions(
222 DataModality::IMAGE_COLOR, { n, h, w, c }, MemoryLayout::ROW_MAJOR);
223 } else {
224 m_structure.dimensions = DataDimension::create_dimensions(
225 DataModality::IMAGE_COLOR, { h, w, c }, MemoryLayout::ROW_MAJOR);
226 }
227}
228
229//=============================================================================
230// GPU bridge
231//=============================================================================
232
233void TextureContainer::from_image(const std::shared_ptr<Core::VKImage>& image, uint32_t layer)
234{
235 if (!image || !image->is_initialized()) {
236 MF_ERROR(Journal::Component::Kakshya, Journal::Context::ContainerProcessing,
237 "TextureContainer::from_image called with uninitialised image");
238 return;
239 }
240
241 if (layer >= m_data.size()) {
242 MF_ERROR(Journal::Component::Kakshya, Journal::Context::ContainerProcessing,
243 "TextureContainer::from_image layer {} out of range ({})", layer, m_data.size());
244 return;
245 }
246
247 const size_t sz = byte_size();
248
249 const size_t element_count = static_cast<size_t>(m_width) * m_height * m_channels;
250 m_data[layer] = make_empty_storage(m_format, element_count);
251
252 auto [ptr, bytes] = variant_bytes_mut(m_data[layer]);
253 if (!ptr || bytes != sz) {
254 MF_ERROR(Journal::Component::Kakshya, Journal::Context::ContainerProcessing,
255 "TextureContainer::from_image variant size mismatch ({} vs {})", bytes, sz);
256 return;
257 }
258
259 TextureLoom::instance().download_data(image, ptr, sz);
260
261 {
262 std::unique_lock lock(m_data_mutex);
263 m_processed_data[layer] = m_data[layer];
264 }
265
266 update_processing_state(ProcessingState::READY);
267
268 MF_DEBUG(Journal::Component::Kakshya, Journal::Context::ContainerProcessing,
269 "TextureContainer: downloaded {} bytes from VKImage", sz);
270}
271
272std::shared_ptr<Core::VKImage> TextureContainer::to_image(uint32_t layer) const
273{
274 std::shared_lock lock(m_data_mutex);
275
276 if (layer >= m_data.size()) {
277 MF_ERROR(Journal::Component::Kakshya, Journal::Context::ContainerProcessing,
278 "TextureContainer::to_image layer {} out of range ({})", layer, m_data.size());
279 return nullptr;
280 }
281
282 auto [ptr, bytes] = variant_bytes(m_data[layer]);
283 if (!ptr || bytes == 0) {
284 MF_ERROR(Journal::Component::Kakshya, Journal::Context::ContainerProcessing,
285 "TextureContainer::to_image called on empty/invalid buffer");
286 return nullptr;
287 }
288
289 auto img = TextureLoom::instance().create_2d(m_width, m_height, m_format, ptr);
290 if (!img) {
291 MF_ERROR(Journal::Component::Kakshya, Journal::Context::ContainerProcessing,
292 "TextureContainer::to_image: TextureLoom failed to create VKImage");
293 }
294 return img;
295}
296
297//=============================================================================
298// Pixel access
299//=============================================================================
300
301std::span<const uint8_t> TextureContainer::pixel_bytes(uint32_t layer) const
302{
303 if (layer >= m_data.size())
304 return {};
305 auto [ptr, bytes] = variant_bytes(m_data[layer]);
306 return ptr ? std::span<const uint8_t>(ptr, bytes) : std::span<const uint8_t> {};
307}
308
309std::span<uint8_t> TextureContainer::pixel_bytes(uint32_t layer)
310{
311 if (layer >= m_data.size())
312 return {};
313 auto [ptr, bytes] = variant_bytes_mut(m_data[layer]);
314 return ptr ? std::span<uint8_t>(ptr, bytes) : std::span<uint8_t> {};
315}
316
317std::span<const uint8_t> TextureContainer::as_uint8(uint32_t layer) const
318{
319 if (layer >= m_data.size())
320 return {};
321 const auto* v = std::get_if<std::vector<uint8_t>>(&m_data[layer]);
322 return v ? std::span<const uint8_t>(v->data(), v->size()) : std::span<const uint8_t> {};
323}
324
325std::span<const uint16_t> TextureContainer::as_uint16(uint32_t layer) const
326{
327 if (layer >= m_data.size())
328 return {};
329 const auto* v = std::get_if<std::vector<uint16_t>>(&m_data[layer]);
330 return v ? std::span<const uint16_t>(v->data(), v->size()) : std::span<const uint16_t> {};
331}
332
333std::span<const float> TextureContainer::as_float(uint32_t layer) const
334{
335 if (layer >= m_data.size())
336 return {};
337 const auto* v = std::get_if<std::vector<float>>(&m_data[layer]);
338 return v ? std::span<const float>(v->data(), v->size()) : std::span<const float> {};
339}
340
341void TextureContainer::set_pixels(std::span<const uint8_t> data, uint32_t layer)
342{
343 if (layer >= m_data.size()) {
344 MF_ERROR(Journal::Component::Kakshya, Journal::Context::ContainerProcessing,
345 "TextureContainer::set_pixels(u8) layer {} out of range", layer);
346 return;
347 }
348 auto* buf = std::get_if<std::vector<uint8_t>>(&m_data[layer]);
349 if (!buf) {
350 MF_ERROR(Journal::Component::Kakshya, Journal::Context::ContainerProcessing,
351 "TextureContainer::set_pixels(u8) called on non-uint8 format {}",
352 static_cast<int>(m_format));
353 return;
354 }
355 if (data.size() != buf->size()) {
356 MF_ERROR(Journal::Component::Kakshya, Journal::Context::ContainerProcessing,
357 "TextureContainer::set_pixels(u8) size mismatch: got {} expected {}",
358 data.size(), buf->size());
359 return;
360 }
361 std::unique_lock lock(m_data_mutex);
362 std::ranges::copy(data, buf->begin());
363 m_processed_data[layer] = m_data[layer];
364}
365
366void TextureContainer::set_pixels(std::span<const uint16_t> data, uint32_t layer)
367{
368 if (layer >= m_data.size()) {
369 MF_ERROR(Journal::Component::Kakshya, Journal::Context::ContainerProcessing,
370 "TextureContainer::set_pixels(u16) layer {} out of range", layer);
371 return;
372 }
373 auto* buf = std::get_if<std::vector<uint16_t>>(&m_data[layer]);
374 if (!buf) {
375 MF_ERROR(Journal::Component::Kakshya, Journal::Context::ContainerProcessing,
376 "TextureContainer::set_pixels(u16) called on non-uint16 format {}",
377 static_cast<int>(m_format));
378 return;
379 }
380 if (data.size() != buf->size()) {
381 MF_ERROR(Journal::Component::Kakshya, Journal::Context::ContainerProcessing,
382 "TextureContainer::set_pixels(u16) size mismatch: got {} expected {}",
383 data.size(), buf->size());
384 return;
385 }
386 std::unique_lock lock(m_data_mutex);
387 std::ranges::copy(data, buf->begin());
388 m_processed_data[layer] = m_data[layer];
389}
390
391void TextureContainer::set_pixels(std::span<const float> data, uint32_t layer)
392{
393 if (layer >= m_data.size()) {
394 MF_ERROR(Journal::Component::Kakshya, Journal::Context::ContainerProcessing,
395 "TextureContainer::set_pixels(f32) layer {} out of range", layer);
396 return;
397 }
398 auto* buf = std::get_if<std::vector<float>>(&m_data[layer]);
399 if (!buf) {
400 MF_ERROR(Journal::Component::Kakshya, Journal::Context::ContainerProcessing,
401 "TextureContainer::set_pixels(f32) called on non-float format {}",
402 static_cast<int>(m_format));
403 return;
404 }
405 if (data.size() != buf->size()) {
406 MF_ERROR(Journal::Component::Kakshya, Journal::Context::ContainerProcessing,
407 "TextureContainer::set_pixels(f32) size mismatch: got {} expected {}",
408 data.size(), buf->size());
409 return;
410 }
411 std::unique_lock lock(m_data_mutex);
412 std::ranges::copy(data, buf->begin());
413 m_processed_data[layer] = m_data[layer];
414}
415
416//=============================================================================
417// NDDimensionalContainer
418//=============================================================================
419
420std::vector<DataDimension> TextureContainer::get_dimensions() const
421{
422 return m_structure.dimensions;
423}
424
425uint64_t TextureContainer::get_total_elements() const
426{
427 return m_structure.get_total_elements();
428}
429
430MemoryLayout TextureContainer::get_memory_layout() const
431{
432 return m_structure.memory_layout;
433}
434
435void TextureContainer::set_memory_layout(MemoryLayout layout)
436{
437 m_structure.memory_layout = layout;
438}
439
440uint64_t TextureContainer::get_frame_size() const
441{
442 return static_cast<uint64_t>(m_width) * m_channels;
443}
444
445uint64_t TextureContainer::get_num_frames() const
446{
447 return m_height;
448}
449
450std::span<const double> TextureContainer::get_frame(uint64_t frame_index) const
451{
452 if (frame_index >= m_height)
453 return {};
454
455 std::shared_lock lock(m_data_mutex);
456 if (m_data.empty())
457 return {};
458
459 const size_t row_elems = static_cast<size_t>(m_width) * m_channels;
460 const size_t offset = static_cast<size_t>(frame_index) * row_elems;
461
462 m_frame_cache.resize(row_elems);
463 for (size_t i = 0; i < row_elems; ++i)
464 m_frame_cache[i] = read_normalized_at(m_data[0], m_format, offset + i);
465
466 return { m_frame_cache.data(), m_frame_cache.size() };
467}
468
469void TextureContainer::get_frames(
470 std::span<double> output, uint64_t start_frame, uint64_t num_frames) const
471{
472 std::shared_lock lock(m_data_mutex);
473 if (m_data.empty())
474 return;
475
476 const size_t row_elems = static_cast<size_t>(m_width) * m_channels;
477 size_t out_idx = 0;
478
479 for (uint64_t r = start_frame; r < start_frame + num_frames && r < m_height; ++r) {
480 const size_t offset = static_cast<size_t>(r) * row_elems;
481 for (size_t i = 0; i < row_elems && out_idx < output.size(); ++i, ++out_idx)
482 output[out_idx] = read_normalized_at(m_data[0], m_format, offset + i);
483 }
484}
485
486std::vector<DataVariant> TextureContainer::get_region_data(const Region& region) const
487{
488 std::shared_lock lock(m_data_mutex);
489 if (m_data.empty())
490 return {};
491
492 return std::visit(
493 [&](const auto& vec) -> std::vector<DataVariant> {
494 using T = typename std::decay_t<decltype(vec)>::value_type;
495 if constexpr (std::is_same_v<T, uint8_t>
496 || std::is_same_v<T, uint16_t>
497 || std::is_same_v<T, float>) {
498 auto extracted = extract_region_data<T>(
499 std::span<const T>(vec.data(), vec.size()),
500 region,
501 m_structure.dimensions);
502 return { DataVariant(std::move(extracted)) };
503 } else {
504 return {};
505 }
506 },
507 m_data[0]);
508}
509
510std::vector<DataVariant> TextureContainer::get_segments_data(
511 const std::vector<RegionSegment>& /*segments*/) const
512{
513 std::shared_lock lock(m_data_mutex);
514 return m_processed_data;
515}
516
517void TextureContainer::set_region_data(
518 const Region& region, const std::vector<DataVariant>& data)
519{
520 if (data.empty())
521 return;
522 if (region.start_coordinates.size() < 2 || region.end_coordinates.size() < 2)
523 return;
524
525 const uint64_t y0 = region.start_coordinates[0];
526 const uint64_t x0 = region.start_coordinates[1];
527 const uint64_t y1 = std::min(region.end_coordinates[0], static_cast<uint64_t>(m_height - 1));
528 const uint64_t x1 = std::min(region.end_coordinates[1], static_cast<uint64_t>(m_width - 1));
529
530 std::unique_lock lock(m_data_mutex);
531
532 std::visit(
533 [&](auto& dst_vec) {
534 using T = typename std::decay_t<decltype(dst_vec)>::value_type;
535 if constexpr (std::is_same_v<T, uint8_t>
536 || std::is_same_v<T, uint16_t>
537 || std::is_same_v<T, float>) {
538 const auto* src = std::get_if<std::vector<T>>(&data[0]);
539 if (!src || src->empty()) {
540 MF_ERROR(Journal::Component::Kakshya, Journal::Context::ContainerProcessing,
541 "TextureContainer::set_region_data source variant does not match "
542 "container element type");
543 return;
544 }
545
546 size_t src_idx = 0;
547 for (uint64_t y = y0; y <= y1 && src_idx < src->size(); ++y) {
548 for (uint64_t x = x0; x <= x1 && src_idx < src->size(); ++x) {
549 const size_t dst_idx = (y * m_width + x) * m_channels;
550 for (uint32_t c = 0; c < m_channels && src_idx < src->size(); ++c, ++src_idx) {
551 if (dst_idx + c < dst_vec.size())
552 dst_vec[dst_idx + c] = (*src)[src_idx];
553 }
554 }
555 }
556 }
557 },
558 m_data[0]);
559
560 m_processed_data[0] = m_data[0];
561}
562
563double TextureContainer::get_value_at(const std::vector<uint64_t>& coordinates) const
564{
565 if (coordinates.size() < 3 || m_data.empty())
566 return 0.0;
567
568 const size_t elem_idx = (coordinates[0] * m_width + coordinates[1]) * m_channels
569 + coordinates[2];
570
571 std::shared_lock lock(m_data_mutex);
572 return read_normalized_at(m_data[0], m_format, elem_idx);
573}
574
575void TextureContainer::set_value_at(const std::vector<uint64_t>& coordinates, double value)
576{
577 if (coordinates.size() < 3 || m_data.empty())
578 return;
579
580 const size_t elem_idx = (coordinates[0] * m_width + coordinates[1]) * m_channels
581 + coordinates[2];
582
583 std::unique_lock lock(m_data_mutex);
584 write_normalized_at(m_data[0], m_format, elem_idx, value);
585}
586
587uint64_t TextureContainer::coordinates_to_linear_index(const std::vector<uint64_t>& coords) const
588{
589 return coordinates_to_linear(coords, m_structure.dimensions);
590}
591
592std::vector<uint64_t> TextureContainer::linear_index_to_coordinates(uint64_t index) const
593{
594 return linear_to_coordinates(index, m_structure.dimensions);
595}
596
597void TextureContainer::clear()
598{
599 std::unique_lock lock(m_data_mutex);
600 const size_t element_count = static_cast<size_t>(m_width) * m_height * m_channels;
601
602 for (size_t i = 0; i < m_data.size(); ++i) {
603 m_data[i] = make_empty_storage(m_format, element_count);
604 m_processed_data[i] = make_empty_storage(m_format, element_count);
605 }
606
607 update_processing_state(ProcessingState::IDLE);
608}
609
610//=============================================================================
611// SignalSourceContainer
612//=============================================================================
613
614ProcessingState TextureContainer::get_processing_state() const
615{
616 return m_processing_state.load();
617}
618
619void TextureContainer::update_processing_state(ProcessingState state)
620{
621 ProcessingState prev = m_processing_state.exchange(state);
622 if (prev == state)
623 return;
624
625 std::lock_guard lock(m_state_mutex);
626 if (m_state_cb)
627 m_state_cb(shared_from_this(), state);
628}
629
630void TextureContainer::register_state_change_callback(
631 std::function<void(const std::shared_ptr<SignalSourceContainer>&, ProcessingState)> cb)
632{
633 std::lock_guard lock(m_state_mutex);
634 m_state_cb = std::move(cb);
635}
636
637void TextureContainer::unregister_state_change_callback()
638{
639 std::lock_guard lock(m_state_mutex);
640 m_state_cb = nullptr;
641}
642
643bool TextureContainer::is_ready_for_processing() const
644{
645 return m_ready_for_processing.load(std::memory_order_acquire);
646}
647
648void TextureContainer::mark_ready_for_processing(bool ready)
649{
650 m_ready_for_processing.store(ready, std::memory_order_release);
651}
652
653std::vector<DataVariant>& TextureContainer::get_processed_data()
654{
655 return m_processed_data;
656}
657
658const std::vector<DataVariant>& TextureContainer::get_processed_data() const
659{
660 return m_processed_data;
661}
662
663const std::vector<DataVariant>& TextureContainer::get_data()
664{
665 return m_data;
666}
667
668DataAccess TextureContainer::channel_data(size_t channel_index)
669{
670 (void)channel_index;
671
672 if (m_data.empty()) {
673 static DataVariant empty = std::vector<uint8_t> {};
674 static std::vector<DataDimension> empty_dims;
675 return { empty, empty_dims, DataModality::IMAGE_COLOR };
676 }
677
678 return { m_data[0], m_structure.dimensions, DataModality::IMAGE_COLOR };
679}
680
681std::vector<DataAccess> TextureContainer::all_channel_data()
682{
683 std::vector<DataAccess> result;
684 result.reserve(m_channels);
685 for (size_t c = 0; c < m_channels; ++c)
686 result.push_back(channel_data(c));
687 return result;
688}
689
690void TextureContainer::add_region_group(const RegionGroup& group)
691{
692 std::lock_guard lock(m_state_mutex);
693 m_region_groups[group.name] = group;
694}
695
696const RegionGroup& TextureContainer::get_region_group(const std::string& name) const
697{
698 static const RegionGroup empty;
699 std::shared_lock lock(m_data_mutex);
700 auto it = m_region_groups.find(name);
701 return it != m_region_groups.end() ? it->second : empty;
702}
703
704std::unordered_map<std::string, RegionGroup> TextureContainer::get_all_region_groups() const
705{
706 std::shared_lock lock(m_data_mutex);
707 return m_region_groups;
708}
709
710void TextureContainer::remove_region_group(const std::string& name)
711{
712 std::lock_guard lock(m_state_mutex);
713 m_region_groups.erase(name);
714}
715
716void TextureContainer::lock() { m_data_mutex.lock(); }
717void TextureContainer::unlock() { m_data_mutex.unlock(); }
718bool TextureContainer::try_lock() { return m_data_mutex.try_lock(); }
719
720const void* TextureContainer::get_raw_data() const
721{
722 if (m_data.empty()) {
723 return nullptr;
724 }
725
726 auto [ptr, bytes] = variant_bytes(m_data[0]);
727 return (ptr && bytes > 0) ? static_cast<const void*>(ptr) : nullptr;
728}
729
730bool TextureContainer::has_data() const
731{
732 if (m_data.empty()) {
733 return false;
734 }
735
736 auto [ptr, bytes] = variant_bytes(m_data[0]);
737 return ptr && bytes > 0;
738}
739
740} // namespace MayaFlux::Kakshya
#define MF_INFO(comp, ctx,...)
#define MF_ERROR(comp, ctx,...)
#define MF_DEBUG(comp, ctx,...)
uint32_t h
Definition InkPress.cpp:25
IO::ImageData image
uint32_t width
Type-erased accessor for NDData with semantic view construction.
TextureContainer(uint32_t width, uint32_t height, Portal::Graphics::ImageFormat format, uint32_t layers=1)
Construct an empty container with declared dimensions.
std::string format(format_string< std::remove_cvref_t< Args >... > fmt_str, Args &&... args)
Definition Format.hpp:30
ProcessingState
Represents the current processing lifecycle state of a container.
uint64_t coordinates_to_linear(const std::vector< uint64_t > &coords, const std::vector< DataDimension > &dimensions)
Convert N-dimensional coordinates to a linear index for interleaved data.
Definition CoordUtils.cpp:6
std::variant< std::vector< double >, std::vector< float >, std::vector< uint8_t >, std::vector< uint16_t >, std::vector< uint32_t >, std::vector< std::complex< float > >, std::vector< std::complex< double > >, std::vector< glm::vec2 >, std::vector< glm::vec3 >, std::vector< glm::vec4 >, std::vector< glm::mat4 > > DataVariant
Multi-type data storage for different precision needs.
Definition NDData.hpp:76
std::vector< uint64_t > linear_to_coordinates(uint64_t index, const std::vector< DataDimension > &dimensions)
Convert a linear index to N-dimensional coordinates for interleaved data.
MemoryLayout
Memory layout for multi-dimensional data.
Definition NDData.hpp:39
ImageFormat
User-friendly image format enum.
std::string name
Descriptive name of the group.
Organizes related signal regions into a categorized collection.
std::vector< uint64_t > end_coordinates
Ending frame index (inclusive)
Definition Region.hpp:72
std::vector< uint64_t > start_coordinates
Starting frame index (inclusive)
Definition Region.hpp:69
Represents a point or span in N-dimensional space.
Definition Region.hpp:67