29 case SF::B8G8R8A8_SRGB:
30 return IF::BGRA8_SRGB;
31 case SF::B8G8R8A8_UNORM:
33 case SF::R8G8B8A8_SRGB:
34 return IF::RGBA8_SRGB;
35 case SF::R8G8B8A8_UNORM:
37 case SF::R16G16B16A16_SFLOAT:
38 case SF::A2B10G10R10_UNORM:
40 case SF::R32G32B32A32_SFLOAT:
43 return IF::BGRA8_SRGB;
47 Portal::Graphics::ImageKey sampled_image_key(
56 uint32_t frame_capacity)
58 , m_frame_capacity(frame_capacity)
61 error<std::invalid_argument>(
64 std::source_location::current(),
65 "WindowContainer requires a valid window");
73 "WindowContainer created for window '{}' ({}x{} frames={})",
89 const auto& state =
m_window->get_state();
90 const uint32_t w = state.current_width;
91 const uint32_t
h = state.current_height;
92 const uint32_t c =
m_window->get_create_info().container_format.color_channels;
93 const size_t sz =
static_cast<size_t>(w) *
h * c;
100 static_cast<uint64_t
>(
h),
101 static_cast<uint64_t
>(w),
102 static_cast<uint64_t
>(c) },
107 slot = std::vector<uint8_t>(sz, 0U);
112 flag.store(
true, std::memory_order_relaxed);
120 if (frame_index >=
m_data.size())
123 auto* v = std::get_if<std::vector<uint8_t>>(&
m_data[frame_index]);
124 return (v && !v->empty()) ? v->data() :
nullptr;
130 std::memory_order_release);
160 std::vector<DataVariant> result;
169 const std::span<const uint8_t> src_span {
src->data(),
src->size() };
173 for (
const auto& r : group.regions) {
177 result.emplace_back(extract_nd_region<uint8_t>(src_span, r, dims));
178 }
catch (
const std::exception& e) {
180 "WindowContainer::get_region_data extraction failed : {}", e.what());
202 const std::shared_ptr<Buffers::VKBuffer>& staging)
const
217 if (frame_index >=
m_data.size()) {
219 "WindowContainer::to_image({}) : out of range (capacity={})",
225 const uint32_t head =
m_write_head.load(std::memory_order_acquire);
228 if (!ring_full && frame_index >= head) {
230 "WindowContainer::image_at({}) : frame index is ahead of write head ({}), data may be stale or uninitialized",
247 uint32_t frame_index,
const std::shared_ptr<Buffers::VKBuffer>& staging)
const
249 if (frame_index >=
m_data.size()) {
251 "WindowContainer::image_at(staging, {}) : out of range (capacity={})",
257 const uint32_t head =
m_write_head.load(std::memory_order_acquire);
259 if (!ring_full && frame_index >= head) {
261 "WindowContainer::image_at(staging, {}) : frame index is ahead of write head ({}), data may be stale or uninitialized",
281 "WindowContainer::region_to_image — region must have at least 2 coordinates (SPATIAL_Y, SPATIAL_X)");
288 "WindowContainer::region_to_image : inverted region");
296 std::vector<uint8_t> cropped;
298 cropped = extract_region_data<uint8_t>(
299 std::span<const uint8_t> {
pixels->pixels.data(),
pixels->pixels.size() },
302 }
catch (
const std::exception& e) {
304 "WindowContainer::region_to_image : crop failed for '{}': {}",
305 m_window->get_create_info().title, e.what());
311 const auto key = std::pair { rw, rh };
319 std::optional<uint32_t> frame_index)
const
323 const auto index = frame_index.value_or(0);
325 if (
index >= data.size())
328 const auto* source = std::get_if<std::vector<uint8_t>>(&data[
index]);
341 if (!snapshot || snapshot->pixels.empty()) {
343 "WindowContainer : no usable pixel data for '{}'",
353 "WindowContainer::set_region_data — write path not yet implemented");
358 std::optional<std::vector<DataVariant>> result;
362 return result.value_or(std::vector<DataVariant> {});
367 std::optional<std::vector<DataVariant>> result;
371 return result.value_or(std::vector<DataVariant> {});
403 return (v && !v->empty()) ? v->data() :
nullptr;
412 result = std::visit([](
const auto& v) {
return !v.empty(); },
m_processed_data[0]);
420 "WindowContainer::load_region — no-op. Register regions via add_region_group()");
426 "WindowContainer::unload_region — no-op. Remove regions via remove_region_group()");
450 std::span<const float> result;
481 std::optional<RegionGroup> result;
486 return result.value_or(empty);
491 std::optional<std::unordered_map<std::string, RegionGroup>> result;
495 return result.value_or(std::unordered_map<std::string, RegionGroup> {});
516 if (old == new_state)
550 auto readback = std::make_shared<WindowAccessProcessor>();
551 readback->on_attach(shared_from_this());
642 "WindowContainer::channel_data — not meaningful for interleaved image data; returning full surface");
663 return { get_frame_typed(frame_index) };
669 uint64_t start_frame,
671 const std::type_info&
type)
const
673 if (
type !=
typeid(uint8_t)) {
674 error<std::runtime_error>(
677 std::source_location::current(),
678 "WindowContainer only supports uint8_t");
686 std::span<const uint8_t> result;
687 seqlock_read_void(m_data_lock, 8, [&] {
688 const uint64_t
h = m_structure.get_height();
689 if (frame_index >=
h || m_processed_data.empty())
692 const auto*
pixels = std::get_if<std::vector<uint8_t>>(&m_processed_data[0]);
696 const uint64_t row_elems = m_structure.get_width() * m_structure.get_channel_count();
697 const uint64_t
offset = frame_index * row_elems;
701 result = std::span<const uint8_t>(
pixels->data() +
offset, row_elems);
712 std::ranges::fill(output, uint8_t { 0 });
718 std::ranges::fill(output, uint8_t { 0 });
723 const uint64_t frames_to_copy = std::min(num_frames,
h - start_frame);
724 const uint64_t elems_to_copy = std::min(frames_to_copy * row_elems,
static_cast<uint64_t
>(output.size()));
725 const uint64_t src_offset = start_frame * row_elems;
727 std::copy_n(
pixels->begin() +
static_cast<std::ptrdiff_t
>(src_offset),
728 static_cast<std::ptrdiff_t
>(elems_to_copy),
731 if (elems_to_copy < output.size())
732 std::fill(output.begin() +
static_cast<std::ptrdiff_t
>(elems_to_copy), output.end(), uint8_t { 0 });
736void WindowContainer::get_value_impl(
737 const std::vector<uint64_t>&
coords,
739 const std::type_info&
type)
const
741 if (
type !=
typeid(uint8_t) ||
coords.size() < 3)
744 const uint64_t w = m_structure.get_width();
745 const uint64_t c = m_structure.get_channel_count();
748 seqlock_read_void(m_data_lock, 8, [&] {
749 if (m_processed_data.empty())
751 const auto*
pixels = std::get_if<std::vector<uint8_t>>(&m_processed_data[0]);
754 *
static_cast<uint8_t*
>(
out) = (*
pixels)[idx];
#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
BufferProcessingChain * chain
std::weak_ptr< Core::Window > window
std::vector< float > * out
std::vector< glm::ivec3 > coords
vk::PhysicalDeviceType type
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::optional< PixelSnapshot > snapshot_pixels(std::optional< uint32_t > frame_index=std::nullopt) const
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.
ImageFormat get_image_format() const
Portal ImageFormat corresponding to the live swapchain surface format.
bool is_region_loaded(const Region ®ion) const override
Always returns true.
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 container-owned 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
Refresh the cached VKImage for m_data[frame_index].
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
std::atomic< uint32_t > m_write_head
uint64_t get_total_elements() const override
Get the total number of elements in the container.
Memory::Seqlock m_region_lock
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 ®ion) const
Crop a region from the last readback and refresh its cached 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 ®ion) override
No-op.
Memory::Seqlock m_data_lock
Memory::Seqlock m_image_cache_lock
Portal::Graphics::ImageCacheEntry m_surface_image
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
std::atomic< bool > m_ready_for_processing
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
ContainerDataStructure m_structure
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.
std::vector< Portal::Graphics::ImageCacheEntry > m_frame_images
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 ®ion) 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 >
uint32_t m_frame_capacity
void set_region_data(const Region ®ion, 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 ®ion) 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::map< std::pair< uint32_t, uint32_t >, Portal::Graphics::ImageCacheEntry > m_region_images
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.
Memory::Seqlock m_cb_lock
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.
Scope guard for a serialized Seqlock write.
std::shared_ptr< Core::VKImage > refresh_cached_image(ImageCacheEntry &entry, const ImageKey &key, std::span< const uint8_t > pixels, const std::shared_ptr< Buffers::VKBuffer > &staging={})
Refresh a sampled image in an owner-held cache entry.
static TextureLoom & instance()
@ 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.
ImageFormat
User-friendly image format enum.
typename detail::span_const_from_vector_variant< DataVariant >::type DataSpanVariant
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.
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.
@ VIDEO_COLOR
4D video (time + 2D + color)
@ IMAGE_COLOR
2D RGB/RGBA image
bool regions_intersect(const Region &r1, const Region &r2) noexcept
Test whether two N-dimensional regions overlap on every shared axis.
MemoryLayout
Memory layout for multi-dimensional data.
@ ROW_MAJOR
C/C++ style (last dimension varies fastest)
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.
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...
std::shared_ptr< T > store(std::shared_ptr< T > obj)
Transfer ownership of an existing object to the persistent store for process lifetime.
SurfaceFormat
Default pixel format for window surfaces (Vulkan-compatible)
std::vector< DataDimension > dimensions
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.
MemoryLayout memory_layout
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.
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)
std::vector< uint64_t > start_coordinates
Starting frame index (inclusive)
Represents a point or span in N-dimensional space.