19using Portal::Graphics::TextureLoom;
29 , m_channels(TextureLoom::get_channel_count(format))
30 , m_bpp(TextureLoom::get_bytes_per_pixel(format))
32 m_chain = std::make_shared<DataProcessingChain>();
33 const size_t element_count =
static_cast<size_t>(m_width) * m_height * m_channels;
35 for (uint32_t i = 0; i < std::max(layers, 1U); ++i) {
39 m_normalised_cache.resize(m_data.size());
41 m_normalised_dirty = std::vector<std::atomic<bool>>(m_data.size());
42 for (
auto& flag : m_normalised_dirty)
43 flag.
store(true,
std::memory_order_relaxed);
45 m_slot_locks.resize(m_data.size());
48 m_ready_for_processing.store(
true);
50 MF_INFO(Journal::Component::Kakshya, Journal::Context::ContainerProcessing,
51 "TextureContainer created: {}x{} layers={} fmt={} bpp={}",
52 m_width, m_height, m_data.size(),
static_cast<int>(m_format), m_bpp);
55TextureContainer::TextureContainer(
const std::shared_ptr<Core::VKImage>&
image, ImageFormat format)
56 : TextureContainer(
image->get_width(),
image->get_height(), format)
65void TextureContainer::setup_dimensions()
67 const uint64_t
h = m_height;
68 const uint64_t w = m_width;
69 const uint64_t c = m_channels;
70 const auto n =
static_cast<uint64_t
>(m_data.size());
72 m_structure = ContainerDataStructure::image_interleaved();
75 m_structure.dimensions = DataDimension::create_dimensions(
76 DataModality::IMAGE_COLOR_ARRAY, { n,
h, w, c }, MemoryLayout::ROW_MAJOR);
78 m_structure.dimensions = DataDimension::create_dimensions(
79 DataModality::IMAGE_COLOR, {
h, w, c }, MemoryLayout::ROW_MAJOR);
87void TextureContainer::from_image(
const std::shared_ptr<Core::VKImage>&
image, uint32_t layer)
90 MF_ERROR(Journal::Component::Kakshya, Journal::Context::ContainerProcessing,
91 "TextureContainer::from_image called with uninitialised image");
95 if (layer >= m_data.size()) {
96 MF_ERROR(Journal::Component::Kakshya, Journal::Context::ContainerProcessing,
97 "TextureContainer::from_image layer {} out of range ({})", layer, m_data.size());
101 const size_t sz = byte_size();
102 const size_t element_count =
static_cast<size_t>(m_width) * m_height * m_channels;
108 if (!
ptr || bytes != sz) {
109 MF_ERROR(Journal::Component::Kakshya, Journal::Context::ContainerProcessing,
110 "TextureContainer::from_image variant size mismatch ({} vs {})", bytes, sz);
113 TextureLoom::instance().download_data(
image,
ptr, sz,
nullptr);
116 m_normalised_dirty[layer].store(
true, std::memory_order_release);
117 update_processing_state(ProcessingState::READY);
120void TextureContainer::from_image(
121 const std::shared_ptr<Core::VKImage>&
image,
122 const std::shared_ptr<Buffers::VKBuffer>& staging,
126 MF_ERROR(Journal::Component::Kakshya, Journal::Context::ContainerProcessing,
127 "TextureContainer::from_image(staging) called with uninitialised image");
131 if (layer >= m_data.size()) {
132 MF_ERROR(Journal::Component::Kakshya, Journal::Context::ContainerProcessing,
133 "TextureContainer::from_image(staging) layer {} out of range ({})", layer, m_data.size());
137 const size_t sz = byte_size();
138 const size_t element_count =
static_cast<size_t>(m_width) * m_height * m_channels;
144 if (!
ptr || bytes != sz) {
145 MF_ERROR(Journal::Component::Kakshya, Journal::Context::ContainerProcessing,
146 "TextureContainer::from_image(staging) variant size mismatch ({} vs {})", bytes, sz);
149 TextureLoom::instance().download_data(
image,
ptr, sz, staging);
152 m_normalised_dirty[layer].store(
true, std::memory_order_release);
153 update_processing_state(ProcessingState::READY);
156void TextureContainer::from_image_array(
const std::shared_ptr<Core::VKImage>&
image)
159 MF_ERROR(Journal::Component::Kakshya, Journal::Context::ContainerProcessing,
160 "TextureContainer::from_image_array called with uninitialised image");
164 const auto n =
static_cast<uint32_t
>(m_data.size());
165 if (
image->get_array_layers() < n) {
166 MF_ERROR(Journal::Component::Kakshya, Journal::Context::ContainerProcessing,
167 "TextureContainer::from_image_array image has {} layers, container expects {}",
168 image->get_array_layers(), n);
172 const size_t layer_bytes = byte_size();
173 std::vector<uint8_t> combined(layer_bytes * n);
174 TextureLoom::instance().download_data(
image, combined.data(), combined.size(),
nullptr);
176 const size_t element_count =
static_cast<size_t>(m_width) * m_height * m_channels;
177 for (uint32_t i = 0; i < n; ++i) {
181 if (
ptr && bytes == layer_bytes)
182 std::memcpy(
ptr, combined.data() + i * layer_bytes, layer_bytes);
184 m_normalised_dirty[i].store(
true, std::memory_order_release);
187 update_processing_state(ProcessingState::READY);
190void TextureContainer::from_image_array(
191 const std::shared_ptr<Core::VKImage>&
image,
192 const std::shared_ptr<Buffers::VKBuffer>& staging)
195 MF_ERROR(Journal::Component::Kakshya, Journal::Context::ContainerProcessing,
196 "TextureContainer::from_image_array(staging) called with uninitialised image");
200 const auto n =
static_cast<uint32_t
>(m_data.size());
201 if (
image->get_array_layers() < n) {
202 MF_ERROR(Journal::Component::Kakshya, Journal::Context::ContainerProcessing,
203 "TextureContainer::from_image_array(staging) image has {} layers, container expects {}",
204 image->get_array_layers(), n);
208 const size_t layer_bytes = byte_size();
209 std::vector<uint8_t> combined(layer_bytes * n);
210 TextureLoom::instance().download_data(
image, combined.data(), combined.size(), staging);
212 const size_t element_count =
static_cast<size_t>(m_width) * m_height * m_channels;
213 for (uint32_t i = 0; i < n; ++i) {
217 if (
ptr && bytes == layer_bytes)
218 std::memcpy(
ptr, combined.data() + i * layer_bytes, layer_bytes);
220 m_normalised_dirty[i].store(
true, std::memory_order_release);
223 update_processing_state(ProcessingState::READY);
226std::shared_ptr<Core::VKImage> TextureContainer::to_image(uint32_t layer)
const
228 if (layer >= m_data.size()) {
229 MF_ERROR(Journal::Component::Kakshya, Journal::Context::ContainerProcessing,
230 "TextureContainer::to_image layer {} out of range ({})", layer, m_data.size());
234 std::shared_ptr<Core::VKImage> img;
235 seqlock_read_void(m_slot_locks[layer], 8, [&] {
237 if (!
ptr || bytes == 0)
239 img = TextureLoom::instance().create_2d(m_width, m_height, m_format,
ptr);
243 MF_ERROR(Journal::Component::Kakshya, Journal::Context::ContainerProcessing,
244 "TextureContainer::to_image: TextureLoom failed to create VKImage");
249std::shared_ptr<Core::VKImage> TextureContainer::to_image(
250 uint32_t layer,
const std::shared_ptr<Buffers::VKBuffer>& staging)
const
252 if (layer >= m_data.size()) {
253 MF_ERROR(Journal::Component::Kakshya, Journal::Context::ContainerProcessing,
254 "TextureContainer::to_image(staging) layer {} out of range ({})", layer, m_data.size());
258 std::shared_ptr<Core::VKImage> img;
259 seqlock_read_void(m_slot_locks[layer], 8, [&] {
261 if (!
ptr || bytes == 0) {
262 MF_ERROR(Journal::Component::Kakshya, Journal::Context::ContainerProcessing,
263 "TextureContainer::to_image(staging) called on empty/invalid buffer");
266 auto& loom = TextureLoom::instance();
267 img = loom.create_2d(m_width, m_height, m_format,
nullptr);
269 MF_ERROR(Journal::Component::Kakshya, Journal::Context::ContainerProcessing,
270 "TextureContainer::to_image(staging): VKImage allocation failed");
273 loom.upload_data(img,
ptr, bytes, staging);
278std::shared_ptr<Core::VKImage> TextureContainer::to_image_array()
const
280 const auto n =
static_cast<uint32_t
>(m_data.size());
285 std::shared_ptr<Core::VKImage> img;
286 seqlock_read_void(m_slot_locks[0], 8, [&] {
288 if (!
ptr || bytes == 0)
290 img = TextureLoom::instance().create_2d(m_width, m_height, m_format,
ptr);
295 const size_t layer_bytes = byte_size();
296 std::vector<uint8_t> combined(layer_bytes * n);
297 for (uint32_t i = 0; i < n; ++i) {
298 bool ok = seqlock_read_void(m_slot_locks[i], 8, [&] {
300 if (!
ptr || bytes != layer_bytes) {
301 MF_ERROR(Journal::Component::Kakshya, Journal::Context::ContainerProcessing,
302 "TextureContainer::to_image_array layer {} has unexpected byte count ({} vs {})",
303 i, bytes, layer_bytes);
306 std::memcpy(combined.data() + i * layer_bytes,
ptr, layer_bytes);
312 return TextureLoom::instance().create_2d_array(m_width, m_height, n, m_format, combined.data());
315std::shared_ptr<Core::VKImage> TextureContainer::to_image_array(
316 const std::shared_ptr<Buffers::VKBuffer>& staging)
const
318 const auto n =
static_cast<uint32_t
>(m_data.size());
323 std::shared_ptr<Core::VKImage> img;
324 seqlock_read_void(m_slot_locks[0], 8, [&] {
326 if (!
ptr || bytes == 0)
328 auto& loom = TextureLoom::instance();
329 img = loom.create_2d(m_width, m_height, m_format,
nullptr);
331 loom.upload_data(img,
ptr, bytes, staging);
336 const size_t layer_bytes = byte_size();
337 std::vector<uint8_t> combined(layer_bytes * n);
338 for (uint32_t i = 0; i < n; ++i) {
339 bool ok = seqlock_read_void(m_slot_locks[i], 8, [&] {
341 if (!
ptr || bytes != layer_bytes) {
342 MF_ERROR(Journal::Component::Kakshya, Journal::Context::ContainerProcessing,
343 "TextureContainer::to_image_array(staging) layer {} size mismatch", i);
346 std::memcpy(combined.data() + i * layer_bytes,
ptr, layer_bytes);
353 auto& loom = TextureLoom::instance();
354 auto img = loom.create_2d_array(m_width, m_height, n, m_format,
nullptr);
357 loom.upload_data(img, combined.data(), combined.size(), staging);
365std::span<const uint8_t> TextureContainer::pixel_bytes(uint32_t layer)
const
367 if (layer >= m_data.size())
370 return ptr ? std::span<const uint8_t>(
ptr, bytes) : std::span<const uint8_t> {};
373std::span<uint8_t> TextureContainer::pixel_bytes(uint32_t layer)
375 if (layer >= m_data.size())
378 return ptr ? std::span<uint8_t>(
ptr, bytes) : std::span<uint8_t> {};
381std::span<const uint8_t> TextureContainer::as_uint8(uint32_t layer)
const
383 if (layer >= m_data.size())
385 const auto* v = std::get_if<std::vector<uint8_t>>(&m_data[layer]);
386 return v ? std::span<const uint8_t>(v->data(), v->size()) : std::span<const uint8_t> {};
389std::span<const uint16_t> TextureContainer::as_uint16(uint32_t layer)
const
391 if (layer >= m_data.size())
393 const auto* v = std::get_if<std::vector<uint16_t>>(&m_data[layer]);
394 return v ? std::span<const uint16_t>(v->data(), v->size()) : std::span<const uint16_t> {};
397std::span<const float> TextureContainer::as_float(uint32_t layer)
const
399 if (layer >= m_data.size())
401 const auto* v = std::get_if<std::vector<float>>(&m_data[layer]);
402 return v ? std::span<const float>(v->data(), v->size()) : std::span<const float> {};
405void TextureContainer::set_pixels(std::span<const uint8_t> data, uint32_t layer)
407 if (layer >= m_data.size()) {
408 MF_ERROR(Journal::Component::Kakshya, Journal::Context::ContainerProcessing,
409 "TextureContainer::set_pixels(u8) layer {} out of range", layer);
412 auto* buf = std::get_if<std::vector<uint8_t>>(&m_data[layer]);
414 MF_ERROR(Journal::Component::Kakshya, Journal::Context::ContainerProcessing,
415 "TextureContainer::set_pixels(u8) called on non-uint8 format {}",
416 static_cast<int>(m_format));
419 if (data.size() != buf->size()) {
420 MF_ERROR(Journal::Component::Kakshya, Journal::Context::ContainerProcessing,
421 "TextureContainer::set_pixels(u8) size mismatch: got {} expected {}",
422 data.size(), buf->size());
427 std::ranges::copy(data, buf->begin());
428 m_normalised_dirty[layer].store(
true, std::memory_order_release);
431void TextureContainer::set_pixels(std::span<const uint16_t> data, uint32_t layer)
433 if (layer >= m_data.size()) {
434 MF_ERROR(Journal::Component::Kakshya, Journal::Context::ContainerProcessing,
435 "TextureContainer::set_pixels(u16) layer {} out of range", layer);
438 auto* buf = std::get_if<std::vector<uint16_t>>(&m_data[layer]);
440 MF_ERROR(Journal::Component::Kakshya, Journal::Context::ContainerProcessing,
441 "TextureContainer::set_pixels(u16) called on non-uint16 format {}",
442 static_cast<int>(m_format));
445 if (data.size() != buf->size()) {
446 MF_ERROR(Journal::Component::Kakshya, Journal::Context::ContainerProcessing,
447 "TextureContainer::set_pixels(u16) size mismatch: got {} expected {}",
448 data.size(), buf->size());
453 std::ranges::copy(data, buf->begin());
454 m_normalised_dirty[layer].store(
true, std::memory_order_release);
457void TextureContainer::set_pixels(std::span<const float> data, uint32_t layer)
459 if (layer >= m_data.size()) {
460 MF_ERROR(Journal::Component::Kakshya, Journal::Context::ContainerProcessing,
461 "TextureContainer::set_pixels(f32) layer {} out of range", layer);
464 auto* buf = std::get_if<std::vector<float>>(&m_data[layer]);
466 MF_ERROR(Journal::Component::Kakshya, Journal::Context::ContainerProcessing,
467 "TextureContainer::set_pixels(f32) called on non-float format {}",
468 static_cast<int>(m_format));
471 if (data.size() != buf->size()) {
472 MF_ERROR(Journal::Component::Kakshya, Journal::Context::ContainerProcessing,
473 "TextureContainer::set_pixels(f32) size mismatch: got {} expected {}",
474 data.size(), buf->size());
479 std::ranges::copy(data, buf->begin());
480 m_normalised_dirty[layer].store(
true, std::memory_order_release);
483std::span<const float> TextureContainer::as_normalised_float(uint32_t layer)
const
485 if (layer >= m_data.size())
488 if (!m_normalised_dirty[layer].load(std::memory_order_acquire))
489 return { m_normalised_cache[layer] };
491 std::span<const float> result;
492 seqlock_read_void(m_slot_locks[layer], 8, [&] {
493 result = Kakshya::as_normalised_float(m_data[layer], m_normalised_cache[layer]);
497 m_normalised_dirty[layer].
store(
false, std::memory_order_release);
506std::vector<DataDimension> TextureContainer::get_dimensions()
const
508 return m_structure.dimensions;
511uint64_t TextureContainer::get_total_elements()
const
513 return m_structure.get_total_elements();
518 return m_structure.memory_layout;
523 m_structure.memory_layout = layout;
526uint64_t TextureContainer::get_frame_size()
const
528 return static_cast<uint64_t
>(m_width) * m_channels;
531uint64_t TextureContainer::get_num_frames()
const
536std::vector<DataVariant> TextureContainer::get_region_data(
const Region& region)
const
545 if (layer >= m_data.size())
548 std::optional<std::vector<DataVariant>> result;
549 seqlock_read_void(m_slot_locks[layer], 8, [&] {
551 [&](
const auto& vec) -> std::vector<DataVariant> {
552 using T =
typename std::decay_t<
decltype(vec)>::value_type;
553 if constexpr (std::is_same_v<T, uint8_t>
554 || std::is_same_v<T, uint16_t>
555 || std::is_same_v<T, float>) {
556 auto extracted = extract_region_data<T>(
557 std::span<const T>(vec.data(), vec.size()),
559 m_structure.dimensions);
567 return result.value_or(std::vector<DataVariant> {});
570std::vector<DataVariant> TextureContainer::get_segments_data(
571 const std::vector<RegionSegment>& )
const
573 std::vector<DataVariant> out;
574 out.reserve(m_data.size());
575 for (
size_t i = 0; i < m_data.size(); ++i) {
576 seqlock_read_void(m_slot_locks[i], 8, [&] {
577 out.push_back(m_data[i]);
583void TextureContainer::set_region_data(
584 const Region& region,
const std::vector<DataVariant>& data)
595 if (layer >= m_data.size())
598 const size_t coord_offset = (m_data.size() > 1) ? 1 : 0;
601 const uint64_t y1 = std::min(region.
end_coordinates[coord_offset],
static_cast<uint64_t
>(m_height - 1));
602 const uint64_t x1 = std::min(region.
end_coordinates[coord_offset + 1],
static_cast<uint64_t
>(m_width - 1));
607 using T =
typename std::decay_t<
decltype(dst_vec)>::value_type;
608 if constexpr (std::is_same_v<T, uint8_t>
609 || std::is_same_v<T, uint16_t>
610 || std::is_same_v<T, float>) {
611 const auto* src = std::get_if<std::vector<T>>(&data[0]);
612 if (!src || src->empty()) {
613 MF_ERROR(Journal::Component::Kakshya, Journal::Context::ContainerProcessing,
614 "TextureContainer::set_region_data source variant does not match "
615 "container element type");
619 for (uint64_t y = y0; y <= y1 && src_idx < src->size(); ++y) {
620 for (uint64_t x = x0; x <= x1 && src_idx < src->size(); ++x) {
621 const size_t dst_idx = (y * m_width + x) * m_channels;
622 for (uint32_t c = 0; c < m_channels && src_idx < src->size(); ++c, ++src_idx) {
623 if (dst_idx + c < dst_vec.size())
624 dst_vec[dst_idx + c] = (*src)[src_idx];
633std::type_index TextureContainer::value_element_type()
const
636 if (element_size == 1) {
637 return typeid(uint8_t);
639 if (element_size == 2) {
640 return typeid(uint16_t);
642 if (element_size == 4) {
643 return typeid(float);
646 return typeid(uint8_t);
649uint64_t TextureContainer::coordinates_to_linear_index(
const std::vector<uint64_t>& coords)
const
654std::vector<uint64_t> TextureContainer::linear_index_to_coordinates(uint64_t
index)
const
659void TextureContainer::clear()
661 const size_t element_count =
static_cast<size_t>(m_width) * m_height * m_channels;
663 for (
size_t i = 0; i < m_data.size(); ++i) {
668 update_processing_state(ProcessingState::IDLE);
677 return m_processing_state.load();
686 seqlock_read_void(m_cb_lock, 8, [&] {
688 m_state_cb(shared_from_this(), state);
692void TextureContainer::register_state_change_callback(
693 std::function<
void(
const std::shared_ptr<SignalSourceContainer>&,
ProcessingState)> cb)
696 m_state_cb = std::move(cb);
699void TextureContainer::unregister_state_change_callback()
702 m_state_cb =
nullptr;
705bool TextureContainer::is_ready_for_processing()
const
707 return m_ready_for_processing.load(std::memory_order_acquire);
710void TextureContainer::mark_ready_for_processing(
bool ready)
712 m_ready_for_processing.store(ready, std::memory_order_release);
715std::vector<DataVariant>& TextureContainer::get_processed_data()
717 return m_processed_data;
720const std::vector<DataVariant>& TextureContainer::get_processed_data()
const
722 return m_processed_data;
725const std::vector<DataVariant>& TextureContainer::get_data()
730DataAccess TextureContainer::channel_data(
size_t channel_index)
734 if (m_data.empty()) {
735 static DataVariant empty = std::vector<uint8_t> {};
736 static std::vector<DataDimension> empty_dims;
737 return { empty, empty_dims, DataModality::IMAGE_COLOR };
740 return { m_data[0], m_structure.dimensions, DataModality::IMAGE_COLOR };
743std::vector<DataAccess> TextureContainer::all_channel_data()
745 std::vector<DataAccess> result;
746 result.reserve(m_channels);
747 for (
size_t c = 0; c < m_channels; ++c)
748 result.push_back(channel_data(c));
755 m_region_groups[group.
name] = group;
761 std::optional<RegionGroup> result;
762 seqlock_read_void(m_region_lock, 8, [&] {
763 auto it = m_region_groups.find(
name);
764 result = (it != m_region_groups.end()) ? it->second : empty;
766 return result.value_or(empty);
769std::unordered_map<std::string, RegionGroup> TextureContainer::get_all_region_groups()
const
771 std::optional<std::unordered_map<std::string, RegionGroup>> result;
772 seqlock_read_void(m_region_lock, 8, [&] {
773 result = m_region_groups;
775 return result.value_or(std::unordered_map<std::string, RegionGroup> {});
778void TextureContainer::remove_region_group(
const std::string&
name)
781 m_region_groups.erase(
name);
784const void* TextureContainer::get_raw_data()
const
786 if (m_data.empty()) {
791 return (
ptr && bytes > 0) ?
static_cast<const void*
>(
ptr) :
nullptr;
794bool TextureContainer::has_data()
const
796 if (m_data.empty()) {
801 return ptr && bytes > 0;
804std::shared_ptr<DataProcessingChain> TextureContainer::get_processing_chain()
807 m_chain = std::make_shared<DataProcessingChain>();
812void TextureContainer::get_frames_impl(
815 uint64_t start_frame,
817 const std::type_info&
type)
const
824 if (frame_index >= m_data.size()) {
825 return { std::span<const uint8_t> {} };
828 const size_t layer_elems =
static_cast<size_t>(m_width) * m_height * m_channels;
830 seqlock_read_void(m_slot_locks[frame_index], 8, [&] {
833 using T =
typename std::decay_t<
decltype(vec)>::value_type;
834 if constexpr (std::is_same_v<T, uint8_t> || std::is_same_v<T, uint16_t> || std::is_same_v<T, float>) {
835 const size_t nn = std::min(layer_elems, vec.size());
838 return { std::span<const uint8_t> {} };
841 m_data[frame_index]);
846void TextureContainer::get_frames_typed(
849 uint64_t start_frame,
851 const std::type_info&
type)
const
853 if (
type ==
typeid(uint8_t)) {
854 get_frames_typed_as<uint8_t>(std::span<uint8_t>(
static_cast<uint8_t*
>(
output),
count), start_frame, num_frames);
857 if (
type ==
typeid(uint16_t)) {
858 get_frames_typed_as<uint16_t>(std::span<uint16_t>(
static_cast<uint16_t*
>(
output),
count), start_frame, num_frames);
861 if (
type ==
typeid(
float)) {
862 get_frames_typed_as<float>(std::span<float>(
static_cast<float*
>(
output),
count), start_frame, num_frames);
866 error<std::runtime_error>(
867 Journal::Component::Kakshya,
868 Journal::Context::Runtime,
869 std::source_location::current(),
870 "TextureContainer supports only uint8_t, uint16_t, and float for typed frame extraction");
874auto TextureContainer::get_frame_typed_as(uint64_t frame_index)
const -> std::span<const T>
876 if (frame_index >= m_data.size())
879 std::span<const T> result;
880 seqlock_read_void(m_slot_locks[frame_index], 8, [&] {
881 const auto* vec = std::get_if<std::vector<T>>(&m_data[frame_index]);
882 if (!vec || vec->empty())
884 const size_t layer_elems =
static_cast<size_t>(m_width) * m_height * m_channels;
885 const size_t n = std::min(layer_elems, vec->size());
886 result = std::span<const T>(vec->data(), n);
893void TextureContainer::get_frames_typed_as(std::span<T>
output, uint64_t start_frame, uint64_t num_frames)
const
895 const size_t layer_elems =
static_cast<size_t>(m_width) * m_height * m_channels;
898 for (uint64_t layer = start_frame;
899 layer < start_frame + num_frames && layer < m_data.size() && out_idx <
output.size();
901 seqlock_read_void(m_slot_locks[layer], 8, [&] {
902 const auto* vec = std::get_if<std::vector<T>>(&m_data[layer]);
903 if (!vec || vec->empty())
905 const size_t copy_n = std::min(layer_elems, std::min(vec->size(),
output.size() - out_idx));
906 std::copy_n(vec->begin(),
static_cast<std::ptrdiff_t
>(copy_n),
output.begin() +
static_cast<std::ptrdiff_t
>(out_idx));
911 if (out_idx <
output.size()) {
912 std::fill(
output.begin() +
static_cast<std::ptrdiff_t
>(out_idx),
output.end(), T {});
916void TextureContainer::get_value_impl(
917 const std::vector<uint64_t>& coords,
919 const std::type_info&
type)
const
921 if (coords.empty() || m_data.empty())
924 const size_t layer = (m_data.size() > 1) ?
static_cast<size_t>(coords[0]) : 0;
925 if (layer >= m_data.size() || coords.size() < (m_data.size() > 1 ? 4U : 3U))
928 const size_t co = (m_data.size() > 1) ? 1 : 0;
929 const size_t idx = (coords[co] * m_width + coords[co + 1]) * m_channels + coords[co + 2];
931 seqlock_read_void(m_slot_locks[layer], 8, [&] {
932 std::visit([&](
const auto& vec) {
933 using T =
typename std::decay_t<
decltype(vec)>::value_type;
934 if (
type !=
typeid(T) || idx >= vec.size())
936 *
static_cast<T*
>(out) = vec[idx];
942void TextureContainer::set_value_impl(
943 const std::vector<uint64_t>& coords,
945 const std::type_info&
type)
947 if (coords.empty() || m_data.empty())
950 const size_t layer = (m_data.size() > 1) ?
static_cast<size_t>(coords[0]) : 0;
951 if (layer >= m_data.size() || coords.size() < (m_data.size() > 1 ? 4U : 3U))
954 const size_t co = (m_data.size() > 1) ? 1 : 0;
955 const size_t idx = (coords[co] * m_width + coords[co + 1]) * m_channels + coords[co + 2];
958 std::visit([&](
auto& vec) {
959 using T =
typename std::decay_t<
decltype(vec)>::value_type;
960 if (
type !=
typeid(T) || idx >= vec.size())
962 vec[idx] = *
static_cast<const T*
>(in);
#define MF_INFO(comp, ctx,...)
#define MF_ERROR(comp, ctx,...)
vk::PhysicalDeviceType type
std::shared_ptr< Core::VKImage > output
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.
RAII guard that brackets a Seqlock write region.
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.
typename detail::span_const_from_vector_variant< DataVariant >::type DataSpanVariant
std::pair< const uint8_t *, size_t > variant_bytes(const DataVariant &v)
Get a pointer to the raw bytes of a DataVariant and its size.
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.
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.
size_t storage_element_size(ImageFormat format)
MemoryLayout
Memory layout for multi-dimensional data.
DataVariant make_empty_storage(ImageFormat format, size_t element_count)
std::pair< uint8_t *, size_t > variant_bytes_mutable(DataVariant &v)
Get a mutable pointer to the raw bytes of a DataVariant and its size.
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.
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.