MayaFlux 0.5.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
WindowContainer.cpp
Go to the documentation of this file.
1#include "WindowContainer.hpp"
2
12
14
15namespace MayaFlux::Kakshya {
16
17namespace {
18
19 /**
20 * @brief Map a MayaFlux surface format to the closest Portal ImageFormat.
21 *
22 * Packed A2B10G10R10 has no direct ImageFormat equivalent; it is widened
23 * to RGBA16F so TextureLoom can allocate a sampled image without data loss.
24 */
25 Portal::Graphics::ImageFormat surface_format_to_image_format(
27 {
30 switch (fmt) {
31 case SF::B8G8R8A8_SRGB:
32 return IF::BGRA8_SRGB;
33 case SF::B8G8R8A8_UNORM:
34 return IF::BGRA8;
35 case SF::R8G8B8A8_SRGB:
36 return IF::RGBA8_SRGB;
37 case SF::R8G8B8A8_UNORM:
38 return IF::RGBA8;
39 case SF::R16G16B16A16_SFLOAT:
40 case SF::A2B10G10R10_UNORM:
41 return IF::RGBA16F;
42 case SF::R32G32B32A32_SFLOAT:
43 return IF::RGBA32F;
44 default:
45 return IF::BGRA8_SRGB;
46 }
47 }
48
49} // namespace
50
51WindowContainer::WindowContainer(std::shared_ptr<Core::Window> window,
52 uint32_t frame_capacity)
53 : m_window(std::move(window))
54 , m_frame_capacity(frame_capacity)
55{
56 if (!m_window) {
57 error<std::invalid_argument>(
60 std::source_location::current(),
61 "WindowContainer requires a valid window");
62 }
63
64 m_processing_chain = std::make_shared<DataProcessingChain>();
66
68 "WindowContainer created for window '{}' ({}x{} frames={})",
69 m_window->get_create_info().title,
71}
72
74{
75 return surface_format_to_image_format(query_surface_format(m_window));
76}
77
78// =========================================================================
79// Setup
80// =========================================================================
81
83{
84 const auto& state = m_window->get_state();
85 const uint32_t w = state.current_width;
86 const uint32_t h = state.current_height;
87 const uint32_t c = m_window->get_create_info().container_format.color_channels;
88 const size_t sz = static_cast<size_t>(w) * h * c;
89
91
94 { static_cast<uint64_t>(m_frame_capacity),
95 static_cast<uint64_t>(h),
96 static_cast<uint64_t>(w),
97 static_cast<uint64_t>(c) },
99
100 m_data.resize(m_frame_capacity);
101 for (auto& slot : m_data)
102 slot = std::vector<uint8_t>(sz, 0U);
103
105 m_normalised_dirty = std::vector<std::atomic<bool>>(m_frame_capacity);
106 for (auto& flag : m_normalised_dirty)
107 flag.store(true, std::memory_order_relaxed);
108
109 m_processed_data.resize(1);
110 m_processed_data[0] = std::vector<uint8_t>(sz, 0U);
111}
112
113uint8_t* WindowContainer::mutable_frame_ptr(uint32_t frame_index)
114{
115 if (frame_index >= m_data.size())
116 return nullptr;
117
118 auto* v = std::get_if<std::vector<uint8_t>>(&m_data[frame_index]);
119 return (v && !v->empty()) ? v->data() : nullptr;
120}
121
123{
124 m_write_head.store((m_write_head.load(std::memory_order_relaxed) + 1U) % m_frame_capacity,
125 std::memory_order_release);
126 m_frames_written.fetch_add(1U, std::memory_order_release);
127}
128
129// =========================================================================
130// NDDimensionalContainer
131// =========================================================================
132
133std::vector<DataDimension> WindowContainer::get_dimensions() const
134{
135 return m_structure.dimensions;
136}
137
142
147
152
153std::vector<DataVariant> WindowContainer::get_region_data(const Region& region) const
154{
155 std::vector<DataVariant> result;
156
157 seqlock_read_void(m_data_lock, 8, [&] {
158 if (m_processed_data.empty())
159 return;
160 const auto* src = std::get_if<std::vector<uint8_t>>(&m_processed_data[0]);
161 if (!src || src->empty())
162 return;
163
164 const std::span<const uint8_t> src_span { src->data(), src->size() };
165 const auto& dims = m_structure.dimensions;
166
167 for (const auto& [name, group] : m_region_groups) {
168 for (const auto& r : group.regions) {
169 if (!regions_intersect(r, region))
170 continue;
171 try {
172 result.emplace_back(extract_nd_region<uint8_t>(src_span, r, dims));
173 } catch (const std::exception& e) {
175 "WindowContainer::get_region_data extraction failed : {}", e.what());
176 }
177 }
178 }
179 });
180 return result;
181}
182
183std::shared_ptr<Core::VKImage> WindowContainer::to_image() const
184{
185 std::shared_ptr<Core::VKImage> img;
186 seqlock_read_void(m_data_lock, 8, [&] {
187 if (m_processed_data.empty()) {
188 MF_RT_WARN(Journal::Component::Kakshya, Journal::Context::ContainerProcessing,
189 "WindowContainer::to_image : no readback data available for '{}'",
190 m_window->get_create_info().title);
191 return;
192 }
193
194 const auto* pixels = std::get_if<std::vector<uint8_t>>(&m_processed_data[0]);
195 if (!pixels || pixels->empty()) {
197 "WindowContainer::to_image : processed_data[0] is not uint8_t or is empty for '{}'",
198 m_window->get_create_info().title);
199 return;
200 }
201
202 const auto img_fmt = surface_format_to_image_format(query_surface_format(m_window));
204 m_structure.get_width(), m_structure.get_height(), img_fmt, pixels->data());
205
206 if (!img) {
208 "WindowContainer::to_image : TextureLoom::create_2d failed for '{}'",
209 m_window->get_create_info().title);
210 }
211 });
212 return img;
213}
214
215std::shared_ptr<Core::VKImage> WindowContainer::to_image(
216 const std::shared_ptr<Buffers::VKBuffer>& staging) const
217{
218 std::shared_ptr<Core::VKImage> img;
219 seqlock_read_void(m_data_lock, 8, [&] {
220 if (m_processed_data.empty()) {
221 MF_RT_WARN(Journal::Component::Kakshya, Journal::Context::ContainerProcessing,
222 "WindowContainer::to_image(staging) : no readback data for '{}'",
223 m_window->get_create_info().title);
224 return;
225 }
226 const auto* pixels = std::get_if<std::vector<uint8_t>>(&m_processed_data[0]);
227 if (!pixels || pixels->empty()) {
229 "WindowContainer::to_image(staging) : processed_data[0] is not uint8_t or is empty for '{}'",
230 m_window->get_create_info().title);
231 return;
232 }
233 const auto img_fmt = surface_format_to_image_format(query_surface_format(m_window));
235 img = loom.create_2d(m_structure.get_width(), m_structure.get_height(), img_fmt, nullptr);
236 if (!img) {
238 "WindowContainer::to_image(staging) : VKImage allocation failed for '{}'",
239 m_window->get_create_info().title);
240 return;
241 }
242 loom.upload_data(img, pixels->data(), pixels->size(), staging);
243 });
244 return img;
245}
246
247std::shared_ptr<Core::VKImage> WindowContainer::image_at(uint32_t frame_index) const
248{
249 if (frame_index >= m_data.size()) {
251 "WindowContainer::to_image({}) : out of range (capacity={})",
252 frame_index, m_frame_capacity);
253 return nullptr;
254 }
255
256 const uint64_t written = m_frames_written.load(std::memory_order_acquire);
257 const uint32_t head = m_write_head.load(std::memory_order_acquire);
258 const bool ring_full = written >= m_frame_capacity;
259
260 if (!ring_full && frame_index >= head) {
262 "WindowContainer::image_at({}) : frame index is ahead of write head ({}), data may be stale or uninitialized",
263 frame_index, head);
264 return nullptr;
265 }
266
267 std::shared_ptr<Core::VKImage> img;
268
269 seqlock_read_void(m_data_lock, 8, [&] {
270 const auto* pixels = std::get_if<std::vector<uint8_t>>(&m_data[frame_index]);
271 if (!pixels || pixels->empty()) {
273 "WindowContainer::image_at({}) : slot is empty", frame_index);
274 return;
275 }
276
277 const auto img_fmt = surface_format_to_image_format(query_surface_format(m_window));
279 m_structure.get_width(), m_structure.get_height(), img_fmt, pixels->data());
280
281 if (!img) {
283 "WindowContainer::image_at({}) : TextureLoom::create_2d failed", frame_index);
284 }
285 });
286 return img;
287}
288
289std::shared_ptr<Core::VKImage> WindowContainer::image_at(
290 uint32_t frame_index, const std::shared_ptr<Buffers::VKBuffer>& staging) const
291{
292 if (frame_index >= m_data.size()) {
294 "WindowContainer::image_at(staging, {}) : out of range (capacity={})",
295 frame_index, m_frame_capacity);
296 return nullptr;
297 }
298
299 const uint64_t written = m_frames_written.load(std::memory_order_acquire);
300 const uint32_t head = m_write_head.load(std::memory_order_acquire);
301 const bool ring_full = written >= m_frame_capacity;
302 if (!ring_full && frame_index >= head) {
304 "WindowContainer::image_at(staging, {}) : frame index is ahead of write head ({}), data may be stale or uninitialized",
305 frame_index, head);
306 return nullptr;
307 }
308
309 std::shared_ptr<Core::VKImage> img;
310 seqlock_read_void(m_data_lock, 8, [&] {
311 const auto* pixels = std::get_if<std::vector<uint8_t>>(&m_data[frame_index]);
312 if (!pixels || pixels->empty()) {
314 "WindowContainer::image_at(staging, {}) — slot is empty", frame_index);
315 return;
316 }
317
318 const auto img_fmt = surface_format_to_image_format(query_surface_format(m_window));
320 img = loom.create_2d(m_structure.get_width(), m_structure.get_height(), img_fmt, nullptr);
321
322 if (!img) {
324 "WindowContainer::image_at(staging, {}) : VKImage allocation failed", frame_index);
325 return;
326 }
327 loom.upload_data(img, pixels->data(), pixels->size(), staging);
328 });
329 return img;
330}
331
332std::shared_ptr<Core::VKImage> WindowContainer::region_to_image(const Region& region) const
333{
334 if (region.start_coordinates.size() < 2 || region.end_coordinates.size() < 2) {
336 "WindowContainer::region_to_image — region must have at least 2 coordinates (SPATIAL_Y, SPATIAL_X)");
337 return nullptr;
338 }
339
340 std::shared_ptr<Core::VKImage> img;
341 seqlock_read_void(m_data_lock, 8, [&] {
342 if (m_processed_data.empty()) {
343 MF_RT_WARN(Journal::Component::Kakshya, Journal::Context::ContainerProcessing,
344 "WindowContainer::region_to_image — no readback data for '{}'",
345 m_window->get_create_info().title);
346 return;
347 }
348
349 const auto* src = std::get_if<std::vector<uint8_t>>(&m_processed_data[0]);
350 if (!src || src->empty()) {
352 "WindowContainer::region_to_image — processed_data[0] is not uint8_t or is empty for '{}'",
353 m_window->get_create_info().title);
354 return;
355 }
356
357 std::vector<uint8_t> cropped;
358 try {
359 cropped = extract_region_data<uint8_t>(
360 std::span<const uint8_t> { src->data(), src->size() },
361 region,
363 } catch (const std::exception& e) {
365 "WindowContainer::region_to_image — crop failed for '{}': {}",
366 m_window->get_create_info().title, e.what());
367 return;
368 }
369
370 const auto rh = static_cast<uint32_t>(region.end_coordinates[0] - region.start_coordinates[0] + 1);
371 const auto rw = static_cast<uint32_t>(region.end_coordinates[1] - region.start_coordinates[1] + 1);
372 const auto img_fmt = surface_format_to_image_format(query_surface_format(m_window));
373
374 img = Portal::Graphics::TextureLoom::instance().create_2d(rw, rh, img_fmt, cropped.data());
375 if (!img) {
377 "WindowContainer::region_to_image — TextureLoom::create_2d failed ({}x{}) for '{}'",
378 rw, rh, m_window->get_create_info().title);
379 }
380 });
381
382 return img;
383}
384
385void WindowContainer::set_region_data(const Region& /*region*/, const std::vector<DataVariant>& /*data*/)
386{
388 "WindowContainer::set_region_data — write path not yet implemented");
389}
390
391std::vector<DataVariant> WindowContainer::get_region_group_data(const RegionGroup& /*group*/) const
392{
393 std::optional<std::vector<DataVariant>> result;
394 seqlock_read_void(m_data_lock, 8, [&] {
395 result = m_processed_data;
396 });
397 return result.value_or(std::vector<DataVariant> {});
398}
399
400std::vector<DataVariant> WindowContainer::get_segments_data(const std::vector<RegionSegment>& /*segments*/) const
401{
402 std::optional<std::vector<DataVariant>> result;
403 seqlock_read_void(m_data_lock, 8, [&] {
404 result = m_processed_data;
405 });
406 return result.value_or(std::vector<DataVariant> {});
407}
408
409uint64_t WindowContainer::coordinates_to_linear_index(const std::vector<uint64_t>& coordinates) const
410{
411 return coordinates_to_linear(coordinates, m_structure.dimensions);
412}
413
414std::vector<uint64_t> WindowContainer::linear_index_to_coordinates(uint64_t index) const
415{
417}
418
420{
421 const size_t sz = m_structure.get_total_elements();
422 {
424 m_processed_data.resize(1);
425 m_processed_data[0] = std::vector<uint8_t>(sz, 0U);
426 }
428}
429
431{
432 if (m_processed_data.empty())
433 return nullptr;
434 const auto* v = std::get_if<std::vector<uint8_t>>(&m_processed_data[0]);
435 return (v && !v->empty()) ? v->data() : nullptr;
436}
437
439{
440 bool result = false;
441 seqlock_read_void(m_data_lock, 8, [&] {
442 if (m_processed_data.empty())
443 return;
444 result = std::visit([](const auto& v) { return !v.empty(); }, m_processed_data[0]);
445 });
446 return result;
447}
448
450{
452 "WindowContainer::load_region — no-op. Register regions via add_region_group()");
453}
454
456{
458 "WindowContainer::unload_region — no-op. Remove regions via remove_region_group()");
459}
460
461bool WindowContainer::is_region_loaded(const Region& /*region*/) const
462{
463 return true;
464}
465
467{
469 m_write_head.store(0, std::memory_order_release);
470 m_frames_written.store(0, std::memory_order_release);
472}
473
474std::span<const float> WindowContainer::processed_frame_as_float(uint32_t frame_index) const
475{
476 if (frame_index >= m_processed_data.size())
477 return {};
478
479 if (!m_normalised_dirty[frame_index].load(std::memory_order_acquire))
480 return { m_normalised_cache[frame_index] };
481
482 std::span<const float> result;
483 seqlock_read_void(m_data_lock, 8, [&] {
484 result = Kakshya::as_normalised_float(m_processed_data[frame_index], m_normalised_cache[frame_index]);
485 });
486
487 if (!result.empty())
488 m_normalised_dirty[frame_index].store(false, std::memory_order_release);
489
490 return result;
491}
492
494{
495 if (frame_index >= m_normalised_dirty.size())
496 return;
497 m_normalised_dirty[frame_index].store(true, std::memory_order_release);
498}
499
500// =========================================================================
501// RegionGroup management
502// =========================================================================
503
509
510RegionGroup WindowContainer::get_region_group(const std::string& name) const
511{
512 static const RegionGroup empty;
513 std::optional<RegionGroup> result;
514 seqlock_read_void(m_region_lock, 8, [&] {
515 auto it = m_region_groups.find(name);
516 result = (it != m_region_groups.end()) ? it->second : empty;
517 });
518 return result.value_or(empty);
519}
520
521std::unordered_map<std::string, RegionGroup> WindowContainer::get_all_region_groups() const
522{
523 std::optional<std::unordered_map<std::string, RegionGroup>> result;
524 seqlock_read_void(m_region_lock, 8, [&] {
525 result = m_region_groups;
526 });
527 return result.value_or(std::unordered_map<std::string, RegionGroup> {});
528}
529
530void WindowContainer::remove_region_group(const std::string& name)
531{
533 m_region_groups.erase(name);
534}
535
536// =========================================================================
537// SignalSourceContainer
538// =========================================================================
539
544
546{
547 ProcessingState old = m_processing_state.exchange(new_state);
548 if (old == new_state)
549 return;
550
551 seqlock_read_void(m_cb_lock, 8, [&] {
553 m_state_callback(shared_from_this(), new_state);
554 });
555}
556
558 std::function<void(const std::shared_ptr<SignalSourceContainer>&, ProcessingState)> callback)
559{
561 m_state_callback = std::move(callback);
562}
563
569
571{
572 return m_ready_for_processing.load(std::memory_order_acquire);
573}
574
576{
577 m_ready_for_processing.store(ready, std::memory_order_release);
578}
579
581{
582 auto readback = std::make_shared<WindowAccessProcessor>();
583 readback->on_attach(shared_from_this());
584 m_default_processor = readback;
585}
586
588{
590 m_default_processor->process(shared_from_this());
591}
592
593void WindowContainer::set_default_processor(const std::shared_ptr<DataProcessor>& proc)
594{
596 m_default_processor->on_detach(shared_from_this());
597 m_default_processor = proc;
599 m_default_processor->on_attach(shared_from_this());
600}
601
602std::shared_ptr<DataProcessor> WindowContainer::get_default_processor() const
603{
604 return m_default_processor;
605}
606
607std::shared_ptr<DataProcessingChain> WindowContainer::get_processing_chain()
608{
610 m_processing_chain = std::make_shared<DataProcessingChain>();
611
612 return m_processing_chain;
613}
614
615void WindowContainer::set_processing_chain(const std::shared_ptr<DataProcessingChain>& chain)
616{
617 m_processing_chain = chain;
618}
619
620// =========================================================================
621// Consumer tracking
622// =========================================================================
623
624uint32_t WindowContainer::register_dimension_reader(uint32_t /*slot_index*/)
625{
627 return m_next_reader_id.fetch_add(1, std::memory_order_relaxed);
628}
629
631{
632 if (m_registered_readers.load(std::memory_order_relaxed) > 0)
634}
635
637{
638 return m_registered_readers.load(std::memory_order_acquire) > 0;
639}
640
641void WindowContainer::mark_dimension_consumed(uint32_t /*slot_index*/, uint32_t /*reader_id*/)
642{
643 m_consumed_readers.fetch_add(1, std::memory_order_release);
644}
645
647{
648 return m_consumed_readers.load(std::memory_order_acquire)
649 >= m_registered_readers.load(std::memory_order_acquire);
650}
651
652// =========================================================================
653// Data access
654// =========================================================================
655
656std::vector<DataVariant>& WindowContainer::get_processed_data()
657{
658 return m_processed_data;
659}
660
661const std::vector<DataVariant>& WindowContainer::get_processed_data() const
662{
663 return m_processed_data;
664}
665
666const std::vector<DataVariant>& WindowContainer::get_data()
667{
668 return m_data;
669}
670
672{
674 "WindowContainer::channel_data — not meaningful for interleaved image data; returning full surface");
676}
677
682
684{
686}
687
689{
690 return m_structure.get_height();
691}
692
693auto WindowContainer::get_frame_span_impl(uint64_t frame_index) const -> DataSpanVariant
694{
695 return { get_frame_typed(frame_index) };
696}
697
699 void* output,
700 size_t count,
701 uint64_t start_frame,
702 uint64_t num_frames,
703 const std::type_info& type) const
704{
705 if (type != typeid(uint8_t)) {
706 error<std::runtime_error>(
709 std::source_location::current(),
710 "WindowContainer only supports uint8_t");
711 }
712
713 get_frames_typed(std::span<uint8_t>(static_cast<uint8_t*>(output), count), start_frame, num_frames);
714}
715
716auto WindowContainer::get_frame_typed(uint64_t frame_index) const -> std::span<const uint8_t>
717{
718 std::span<const uint8_t> result;
719 seqlock_read_void(m_data_lock, 8, [&] {
720 const uint64_t h = m_structure.get_height();
721 if (frame_index >= h || m_processed_data.empty())
722 return;
723
724 const auto* pixels = std::get_if<std::vector<uint8_t>>(&m_processed_data[0]);
725 if (!pixels || pixels->empty())
726 return;
727
728 const uint64_t row_elems = m_structure.get_width() * m_structure.get_channel_count();
729 const uint64_t offset = frame_index * row_elems;
730 if (offset + row_elems > pixels->size())
731 return;
732
733 result = std::span<const uint8_t>(pixels->data() + offset, row_elems);
734 });
735
736 return result;
737}
738
739void WindowContainer::get_frames_typed(std::span<uint8_t> output, uint64_t start_frame, uint64_t num_frames) const
740{
741 seqlock_read_void(m_data_lock, 8, [&] {
742 const uint64_t h = m_structure.get_height();
743 if (start_frame >= h || output.empty() || m_processed_data.empty()) {
744 std::ranges::fill(output, uint8_t { 0 });
745 return;
746 }
747
748 const auto* pixels = std::get_if<std::vector<uint8_t>>(&m_processed_data[0]);
749 if (!pixels || pixels->empty()) {
750 std::ranges::fill(output, uint8_t { 0 });
751 return;
752 }
753
754 const uint64_t row_elems = m_structure.get_width() * m_structure.get_channel_count();
755 const uint64_t frames_to_copy = std::min(num_frames, h - start_frame);
756 const uint64_t elems_to_copy = std::min(frames_to_copy * row_elems, static_cast<uint64_t>(output.size()));
757 const uint64_t src_offset = start_frame * row_elems;
758
759 std::copy_n(pixels->begin() + static_cast<std::ptrdiff_t>(src_offset),
760 static_cast<std::ptrdiff_t>(elems_to_copy),
761 output.begin());
762
763 if (elems_to_copy < output.size())
764 std::fill(output.begin() + static_cast<std::ptrdiff_t>(elems_to_copy), output.end(), uint8_t { 0 });
765 });
766}
767
768void WindowContainer::get_value_impl(
769 const std::vector<uint64_t>& coords,
770 void* out,
771 const std::type_info& type) const
772{
773 if (type != typeid(uint8_t) || coords.size() < 3)
774 return;
775
776 const uint64_t w = m_structure.get_width();
777 const uint64_t c = m_structure.get_channel_count();
778 const uint64_t idx = (coords[0] * w + coords[1]) * c + coords[2];
779
780 seqlock_read_void(m_data_lock, 8, [&] {
781 if (m_processed_data.empty())
782 return;
783 const auto* pixels = std::get_if<std::vector<uint8_t>>(&m_processed_data[0]);
784 if (!pixels || idx >= pixels->size())
785 return;
786 *static_cast<uint8_t*>(out) = (*pixels)[idx];
787 });
788}
789
790} // namespace MayaFlux::Kakshya
#define MF_INFO(comp, ctx,...)
#define MF_RT_WARN(comp, ctx,...)
#define MF_RT_ERROR(comp, ctx,...)
#define MF_WARN(comp, ctx,...)
const std::vector< float > * pixels
Definition Decoder.cpp:65
uint32_t h
Definition InkPress.cpp:28
size_t count
std::shared_ptr< Core::VKImage > output
float offset
Type-erased accessor for NDData with semantic view construction.
uint64_t coordinates_to_linear_index(const std::vector< uint64_t > &coordinates) const override
Convert coordinates to linear index based on current memory layout.
ProcessingState get_processing_state() const override
Get the current processing state of the container.
std::atomic< uint32_t > m_registered_readers
std::function< void(const std::shared_ptr< SignalSourceContainer > &, ProcessingState)> m_state_callback
std::atomic< ProcessingState > m_processing_state
void set_processing_chain(const std::shared_ptr< DataProcessingChain > &chain) override
Set the processing chain for this container.
void create_default_processor() override
Create and configure a default processor for this container.
DataAccess channel_data(size_t channel_index) override
Get channel data with semantic interpretation.
bool is_region_loaded(const Region &region) const override
Always returns true.
Portal::Graphics::ImageFormat get_image_format() const
Portal ImageFormat corresponding to the live swapchain surface format.
void invalidate_float_frame_cache(uint32_t frame_index=0)
Invalidate the normalised float cache for a specific processed_data slot.
void set_memory_layout(MemoryLayout layout) override
Set the memory layout for this container.
void unregister_dimension_reader(uint32_t dimension_index) override
Unregister a reader for a specific dimension.
std::shared_ptr< Core::VKImage > to_image() const
Upload the full surface readback to a new VKImage.
WindowContainer(std::shared_ptr< Core::Window > window, uint32_t frame_capacity=60)
Construct from an existing managed window.
std::shared_ptr< Core::VKImage > image_at(uint32_t frame_index) const
Upload m_data[frame_index] to a new VKImage.
void clear() override
Clear all data in the container.
void unregister_state_change_callback() override
Unregister the state change callback, if any.
std::shared_ptr< Core::Window > m_window
uint64_t get_total_elements() const override
Get the total number of elements in the container.
const void * get_raw_data() const override
Get a raw pointer to the underlying data storage.
std::shared_ptr< Core::VKImage > region_to_image(const Region &region) const
Crop a region from the last readback and upload it as a VKImage.
std::vector< DataDimension > get_dimensions() const override
Get the dimensions describing the structure of the data.
std::atomic< uint32_t > m_consumed_readers
const std::vector< DataVariant > & get_data() override
Get a reference to the raw data stored in the container.
void load_region(const Region &region) override
No-op.
std::vector< std::vector< float > > m_normalised_cache
void get_frames_impl(void *output, size_t count, uint64_t start_frame, uint64_t num_frames, const std::type_info &type) const override
Implementation-specific method to retrieve multiple frames.
std::vector< DataVariant > m_processed_data
uint64_t get_num_frames() const override
Get the number of frames in the primary (temporal) dimension.
std::vector< std::atomic< bool > > m_normalised_dirty
std::unordered_map< std::string, RegionGroup > m_region_groups
void mark_dimension_consumed(uint32_t dimension_index, uint32_t reader_id) override
Mark a dimension as consumed for the current processing cycle.
uint8_t * mutable_frame_ptr(uint32_t frame_index)
Mutable pointer into m_data[frame_index] for the processor to write into.
uint32_t register_dimension_reader(uint32_t dimension_index) override
Register a reader for a specific dimension.
std::unordered_map< std::string, RegionGroup > get_all_region_groups() const override
Get all region groups in the container.
void get_frames_typed(std::span< uint8_t > output, uint64_t start_frame, uint64_t num_frames) const
std::vector< DataVariant > m_data
void update_processing_state(ProcessingState new_state) override
Update the processing state of the container.
void mark_ready_for_processing(bool ready) override
Mark the container as ready or not ready for processing.
std::vector< DataVariant > get_region_group_data(const RegionGroup &group) const override
Get data for multiple regions efficiently.
std::shared_ptr< DataProcessingChain > m_processing_chain
std::shared_ptr< DataProcessor > m_default_processor
uint64_t get_frame_size() const override
Get the number of elements that constitute one "frame".
std::atomic< uint64_t > m_frames_written
bool has_data() const override
Check if the container currently holds any data.
void remove_region_group(const std::string &name) override
Remove a region group by name.
void set_default_processor(const std::shared_ptr< DataProcessor > &processor) override
Set the default data processor for this container.
MemoryLayout get_memory_layout() const override
Get the memory layout used by this container.
bool has_active_readers() const override
Check if any dimensions currently have active readers.
std::vector< DataVariant > get_segments_data(const std::vector< RegionSegment > &segments) const override
Get data for multiple region segments efficiently.
std::vector< DataVariant > get_region_data(const Region &region) const override
Extract data for all regions across all region groups that spatially intersect region.
auto get_frame_typed(uint64_t frame_index) const -> std::span< const uint8_t >
void set_region_data(const Region &region, const std::vector< DataVariant > &data) override
Set data for a specific region.
std::shared_ptr< DataProcessor > get_default_processor() const override
Get the current default data processor.
void unload_region(const Region &region) override
No-op.
std::vector< DataAccess > all_channel_data() override
Get all channel data as accessors.
std::span< const float > processed_frame_as_float(uint32_t frame_index=0) const
processed_data[frame_index] as a normalised float span.
bool all_dimensions_consumed() const override
Check if all active dimensions have been consumed in this cycle.
std::vector< DataVariant > & get_processed_data() override
Get a mutable reference to the processed data buffer.
void register_state_change_callback(std::function< void(const std::shared_ptr< SignalSourceContainer > &, ProcessingState)> callback) override
Register a callback to be invoked on processing state changes.
std::shared_ptr< DataProcessingChain > get_processing_chain() override
Get the current processing chain for this container.
RegionGroup get_region_group(const std::string &name) const override
Get a region group by name.
void process_default() override
Process the container's data using the default processor.
void handle_surface_resize()
Reallocate m_data and m_processed_data to match the current window dimensions.
void advance_write_head()
Advance the write head index, wrapping around frame_capacity.
bool is_ready_for_processing() const override
Check if the container is ready for processing.
std::vector< uint64_t > linear_index_to_coordinates(uint64_t linear_index) const override
Convert linear index to coordinates based on current memory layout.
std::atomic< uint32_t > m_next_reader_id
auto get_frame_span_impl(uint64_t frame_index) const -> DataSpanVariant override
Implementation-specific method to retrieve a frame span.
void add_region_group(const RegionGroup &group) override
Add a named group of regions to the container.
RAII guard that brackets a Seqlock write region.
Definition SeqLock.hpp:136
std::shared_ptr< Core::VKImage > create_2d(uint32_t width, uint32_t height, ImageFormat format=ImageFormat::RGBA8, const void *data=nullptr, uint32_t mip_levels=1)
Create a 2D texture.
@ ContainerProcessing
Container operations (Kakshya - file/stream/region processing)
@ Runtime
General runtime operations (default fallback)
@ Kakshya
Containers[Signalsource, Stream, File], Regions, DataProcessors.
ProcessingState
Represents the current processing lifecycle state of a container.
@ IDLE
Container is inactive with no data or not ready for processing.
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:8
typename detail::span_const_from_vector_variant< DataVariant >::type DataSpanVariant
Definition NDData.hpp:592
std::span< const float > as_normalised_float(const DataVariant &variant, std::vector< float > &storage)
Extract a DataVariant holding pixel data as a normalised float span.
Definition DataUtils.cpp:61
@ VIDEO_COLOR
4D video (time + 2D + color)
@ IMAGE_COLOR
2D RGB/RGBA image
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:65
@ ROW_MAJOR
C/C++ style (last dimension varies fastest)
bool regions_intersect(const Region &r1, const Region &r2) noexcept
Test whether two N-dimensional regions overlap on every shared axis.
Core::GraphicsSurfaceInfo::SurfaceFormat query_surface_format(const std::shared_ptr< Core::Window > &window)
Query the actual vk::Format in use by the window's live swapchain, translated back to the MayaFlux su...
ImageFormat
User-friendly image format enum.
std::shared_ptr< T > store(std::shared_ptr< T > obj)
Transfer ownership of an existing object to the persistent store for process lifetime.
Definition Persist.hpp:28
SurfaceFormat
Default pixel format for window surfaces (Vulkan-compatible)
static uint64_t get_height(const std::vector< DataDimension > &dimensions)
Extract height from image/video dimensions.
static uint64_t get_channel_count(const std::vector< DataDimension > &dimensions)
Extract channel count from dimensions.
static size_t get_frame_size(const std::vector< DataDimension > &dimensions)
Extract the size of non time dimensions (channel, spatial, frequency)
static ContainerDataStructure image_interleaved()
Create structure for interleaved image data.
static uint64_t get_total_elements(const std::vector< DataDimension > &dimensions)
Get total elements across all dimensions.
static uint64_t get_width(const std::vector< DataDimension > &dimensions)
Extract width from image/video dimensions.
static std::vector< DataDimension > create_dimensions(DataModality modality, const std::vector< uint64_t > &shape, MemoryLayout layout=MemoryLayout::ROW_MAJOR)
Create dimension descriptors for a data modality.
Definition NDData.cpp:136
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:78
std::vector< uint64_t > start_coordinates
Starting frame index (inclusive)
Definition Region.hpp:75
Represents a point or span in N-dimensional space.
Definition Region.hpp:73