MayaFlux 0.4.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
TextureContainer.hpp
Go to the documentation of this file.
1#pragma once
2
5
6namespace MayaFlux::Core {
7class VKImage;
8}
9
10namespace MayaFlux::Kakshya {
11
12/**
13 * @class TextureContainer
14 * @brief SignalSourceContainer wrapping GPU texture data as addressable pixel bytes.
15 *
16 * TextureContainer is the Kakshya-layer representation of a 2D texture.
17 * It owns a flat interleaved RGBA/float pixel buffer matching the declared
18 * ImageFormat, described with IMAGE_COLOR or IMAGE_2D dimensions.
19 *
20 * The container is the data carrier for Yantra::Texture workflow pipelines.
21 * GPU materialization is intentionally not automatic: callers drive upload
22 * and download explicitly via from_image() and to_image(), keeping the
23 * container decoupled from any specific VKImage lifetime.
24 *
25 * Dimension convention (IMAGE_COLOR, INTERLEAVED):
26 * dims[0] -> SPATIAL_Y (height)
27 * dims[1] -> SPATIAL_X (width)
28 * dims[2] -> CHANNEL (bytes per pixel for format)
29 *
30 * Processing model:
31 * No default processor is created. The container holds bytes passively.
32 * Workflow operations receive and return TextureContainer instances;
33 * TextureExecutionContext dispatches compute shaders against them.
34 */
35class MAYAFLUX_API TextureContainer : public SignalSourceContainer {
36public:
37 /**
38 * @brief Construct an empty container with declared dimensions.
39 * @param width Texture width in pixels.
40 * @param height Texture height in pixels.
41 * @param format Pixel format; determines bytes per pixel.
42 * @param layers Number of array layers (default 1). set when using with array textures.
43 *
44 * Allocates a zeroed pixel buffer. No GPU resource is created.
45 */
46 TextureContainer(uint32_t width, uint32_t height, Portal::Graphics::ImageFormat format, uint32_t layers = 1);
47
48 /**
49 * @brief Construct from an existing VKImage, downloading its pixel data.
50 * @param image Source GPU texture. Must be initialized.
51 * @param format Pixel format of the image.
52 *
53 * Performs a blocking GPU->CPU download via TextureLoom.
54 * Equivalent to constructing empty then calling from_image().
55 */
57 const std::shared_ptr<Core::VKImage>& image,
59
60 ~TextureContainer() override = default;
61
66
67 [[nodiscard]] uint32_t get_layer_count() const { return static_cast<uint32_t>(m_data.size()); }
68
69 //=========================================================================
70 // GPU bridge
71 //=========================================================================
72
73 /**
74 * @brief Download pixel data from a VKImage into this container.
75 * @param image Source GPU texture. Must be initialized and match dimensions.
76 * @param layer Array layer index for array textures (default 0).
77 *
78 * Blocking. Overwrites the existing pixel buffer. Does not change
79 * declared width, height, or format — caller must ensure consistency.
80 */
81 void from_image(const std::shared_ptr<Core::VKImage>& image, uint32_t layer = 0);
82
83 /**
84 * @brief Upload the pixel buffer to a new VKImage via TextureLoom.
85 * @param layer Array layer index for array textures (default 0).
86 * @return Newly created and uploaded VKImage. Null on failure.
87 *
88 * Creates a fresh 2D texture each call. Does not cache the result.
89 * The returned image is owned by TextureLoom's internal registry.
90 */
91 [[nodiscard]] std::shared_ptr<Core::VKImage> to_image(uint32_t layer = 0) const;
92
93 //=========================================================================
94 // Pixel access
95 //=========================================================================
96
97 /**
98 * @brief Read-only byte-level view over the pixel buffer.
99 *
100 * The returned span covers the raw byte footprint of the layer,
101 * regardless of the underlying variant type. For uint8 formats this
102 * is the natural view; for uint16 and float formats the caller can
103 * reinterpret_cast for typed access, or prefer the as_uint16 /
104 * as_float accessors below.
105 *
106 * @param layer Array layer index for array textures (default 0).
107 */
108 [[nodiscard]] std::span<const uint8_t> pixel_bytes(uint32_t layer = 0) const;
109
110 /**
111 * @brief Read-write byte-level view over the pixel buffer.
112 * @param layer Array layer index for array textures (default 0).
113 */
114 [[nodiscard]] std::span<uint8_t> pixel_bytes(uint32_t layer = 0);
115
116 /**
117 * @brief Typed view over the uint8 variant.
118 * Returns an empty span if the layer's variant is not uint8.
119 */
120 [[nodiscard]] std::span<const uint8_t> as_uint8(uint32_t layer = 0) const;
121
122 /**
123 * @brief Typed view over the uint16 variant.
124 * Returns an empty span if the layer's variant is not uint16.
125 * 16-bit UNORM formats and half-float formats both reside here;
126 * for half-float, the uint16 bits are the IEEE-754 binary16
127 * encoding.
128 */
129 [[nodiscard]] std::span<const uint16_t> as_uint16(uint32_t layer = 0) const;
130
131 /**
132 * @brief Typed view over the float variant.
133 * Returns an empty span if the layer's variant is not float.
134 */
135 [[nodiscard]] std::span<const float> as_float(uint32_t layer = 0) const;
136
137 /**
138 * @brief Replace the layer's pixel buffer with a byte source.
139 * Valid only when the declared format is uint8-backed.
140 * Size must equal width * height * bytes_per_pixel.
141 */
142 void set_pixels(std::span<const uint8_t> data, uint32_t layer = 0);
143
144 /**
145 * @brief Replace the layer's pixel buffer with a uint16 source.
146 * Valid when the declared format is uint16-backed
147 * (R16/RG16/RGBA16, R16F/RG16F/RGBA16F).
148 * Size must equal width * height * channels.
149 */
150 void set_pixels(std::span<const uint16_t> data, uint32_t layer = 0);
151
152 /**
153 * @brief Replace the layer's pixel buffer with a float source.
154 * Valid when the declared format is float-backed
155 * (R32F/RG32F/RGBA32F).
156 * Size must equal width * height * channels.
157 */
158 void set_pixels(std::span<const float> data, uint32_t layer = 0);
159
160 //=========================================================================
161 // Metadata
162 //=========================================================================
163
164 [[nodiscard]] uint32_t get_width() const { return m_width; }
165 [[nodiscard]] uint32_t get_height() const { return m_height; }
166 [[nodiscard]] Portal::Graphics::ImageFormat get_format() const { return m_format; }
167 [[nodiscard]] uint32_t get_channel_count() const { return m_channels; }
168
169 /** @brief Byte count of one complete pixel row. */
170 [[nodiscard]] size_t row_stride() const { return static_cast<size_t>(m_width) * m_bpp; }
171
172 /** @brief Total byte count of the pixel buffer. */
173 [[nodiscard]] size_t byte_size() const { return static_cast<size_t>(m_width) * m_height * m_bpp; }
174
175 //=========================================================================
176 // NDDimensionalContainer
177 //=========================================================================
178
179 [[nodiscard]] std::vector<DataDimension> get_dimensions() const override;
180 [[nodiscard]] uint64_t get_total_elements() const override;
181 [[nodiscard]] MemoryLayout get_memory_layout() const override;
182 void set_memory_layout(MemoryLayout layout) override;
183 [[nodiscard]] uint64_t get_frame_size() const override;
184 [[nodiscard]] uint64_t get_num_frames() const override;
185
186 [[nodiscard]] std::vector<DataVariant> get_region_data(const Region& region) const override;
187 [[nodiscard]] std::vector<DataVariant> get_segments_data(
188 const std::vector<RegionSegment>& segments) const override;
189 [[nodiscard]] std::vector<DataVariant> get_region_group_data(const RegionGroup&) const override { return m_processed_data; }
190
191 [[nodiscard]] double get_value_at(const std::vector<uint64_t>& coordinates) const override;
192 void set_value_at(const std::vector<uint64_t>& coordinates, double value) override;
193 [[nodiscard]] uint64_t coordinates_to_linear_index(const std::vector<uint64_t>& coords) const override;
194 [[nodiscard]] std::vector<uint64_t> linear_index_to_coordinates(uint64_t index) const override;
195 void clear() override;
196
197 //=========================================================================
198 // SignalSourceContainer
199 //=========================================================================
200
201 /**
202 * @brief Returns a normalized double view of one pixel row.
203 *
204 * Maps frame_index to row index (SPATIAL_Y). Each uint8_t sample is
205 * normalized to [0.0, 1.0]. The returned span is backed by m_frame_cache
206 * and remains valid until the next call to get_frame() or clear().
207 *
208 * @param frame_index Row index in [0, height).
209 * @return Span of width * channels doubles, or empty if out of range.
210 */
211 [[nodiscard]] std::span<const double> get_frame(uint64_t frame_index) const override;
212
213 /**
214 * @brief Copy a range of pixel rows into output as normalized doubles.
215 *
216 * Rows [start_frame, start_frame + num_frames) are written sequentially.
217 * Each uint8_t sample is normalized to [0.0, 1.0]. Rows beyond the image
218 * height are skipped silently.
219 *
220 * @param output Destination buffer. Must hold at least
221 * num_frames * width * channels doubles.
222 * @param start_frame First row index.
223 * @param num_frames Number of rows to copy.
224 */
225 void get_frames(std::span<double> output,
226 uint64_t start_frame, uint64_t num_frames) const override;
227
228 /**
229 * @brief Write DataVariant bytes into the pixel buffer at the region bounds.
230 *
231 * The region's SPATIAL_Y / SPATIAL_X coordinates are used as the destination
232 * rectangle. The first DataVariant in @p data must hold std::vector<uint8_t>
233 * with exactly region_width * region_height * channels bytes.
234 *
235 * @param region Destination region (start/end SPATIAL_Y, SPATIAL_X coords).
236 * @param data Source data. First element must be vector<uint8_t>.
237 */
238 void set_region_data(const Region& region,
239 const std::vector<DataVariant>& data) override;
240
241 [[nodiscard]] ProcessingState get_processing_state() const override;
242 void update_processing_state(ProcessingState state) override;
243 void register_state_change_callback(
244 std::function<void(const std::shared_ptr<SignalSourceContainer>&, ProcessingState)> cb) override;
245 void unregister_state_change_callback() override;
246
247 [[nodiscard]] bool is_ready_for_processing() const override;
248 void mark_ready_for_processing(bool ready) override;
249
250 [[nodiscard]] std::vector<DataVariant>& get_processed_data() override;
251 [[nodiscard]] const std::vector<DataVariant>& get_processed_data() const override;
252 [[nodiscard]] const std::vector<DataVariant>& get_data() override;
253
254 /** @brief No-op. TextureContainer has no BufferManager integration. */
255 void mark_buffers_for_processing(bool) override { }
256
257 /** @brief No-op. TextureContainer has no BufferManager integration. */
258 void mark_buffers_for_removal() override { }
259
260 [[nodiscard]] DataAccess channel_data(size_t channel_index) override;
261 [[nodiscard]] std::vector<DataAccess> all_channel_data() override;
262
263 /** @brief No-op. All pixel data is always resident; no streaming regions. */
264 void load_region(const Region&) override { }
265
266 /** @brief No-op. All pixel data is always resident; no streaming regions. */
267 void unload_region(const Region&) override { }
268 [[nodiscard]] bool is_region_loaded(const Region&) const override { return true; }
269
270 void add_region_group(const RegionGroup& group) override;
271 [[nodiscard]] const RegionGroup& get_region_group(const std::string& name) const override;
272 [[nodiscard]] std::unordered_map<std::string, RegionGroup> get_all_region_groups() const override;
273 void remove_region_group(const std::string& name) override;
274
275 void lock() override;
276 void unlock() override;
277 [[nodiscard]] bool try_lock() override;
278
279 [[nodiscard]] const void* get_raw_data() const override;
280 [[nodiscard]] bool has_data() const override;
281
282 [[nodiscard]] ContainerDataStructure& get_structure() override { return m_structure; }
283 [[nodiscard]] const ContainerDataStructure& get_structure() const override { return m_structure; }
284 void set_structure(ContainerDataStructure s) override { m_structure = std::move(s); }
285
286 void reset_processing_token() { m_processing_token.store(-1); }
288 {
289 int expected = -1;
290 return m_processing_token.compare_exchange_strong(expected, ch);
291 }
292 [[nodiscard]] bool has_processing_token(int ch) const
293 {
294 return m_processing_token.load() == ch;
295 }
296
297 /** @brief No-op. TextureContainer holds bytes passively; no processor lifecycle. */
298 void create_default_processor() override { }
299
300 /** @brief No-op. TextureContainer holds bytes passively; no processor lifecycle. */
301 void process_default() override { }
302
303 void set_default_processor(const std::shared_ptr<DataProcessor>& p) override { m_processor = p; }
304 std::shared_ptr<DataProcessor> get_default_processor() const override { return m_processor; }
305 std::shared_ptr<DataProcessingChain> get_processing_chain() override { return m_chain; }
306 void set_processing_chain(const std::shared_ptr<DataProcessingChain>& c) override { m_chain = c; }
307
308 /**
309 * @brief No-op. TextureContainer does not track concurrent dimension readers.
310 * @return Always 0.
311 */
312 uint32_t register_dimension_reader(uint32_t) override { return 0; }
313
314 /** @brief No-op. TextureContainer does not track concurrent dimension readers. */
315 void unregister_dimension_reader(uint32_t) override { }
316
317 /**
318 * @brief No-op. TextureContainer does not track concurrent dimension readers.
319 * @return Always false.
320 */
321 [[nodiscard]] bool has_active_readers() const override { return false; }
322
323 /** @brief No-op. TextureContainer does not track concurrent dimension readers. */
324 void mark_dimension_consumed(uint32_t, uint32_t) override { }
325
326 /**
327 * @brief No-op. TextureContainer does not track concurrent dimension readers.
328 * @return Always true.
329 */
330 [[nodiscard]] bool all_dimensions_consumed() const override { return true; }
331
332private:
333 void setup_dimensions();
334
335 uint32_t m_width {};
336 uint32_t m_height {};
338 uint32_t m_channels {};
339 size_t m_bpp {};
340
341 std::vector<DataVariant> m_data;
342 std::vector<DataVariant> m_processed_data;
343 std::shared_ptr<DataProcessor> m_processor;
344 std::shared_ptr<DataProcessingChain> m_chain;
345
347
348 std::atomic<ProcessingState> m_processing_state { ProcessingState::IDLE };
349 std::atomic<bool> m_ready_for_processing { false };
350 std::atomic<int> m_processing_token { -1 };
351
352 mutable std::shared_mutex m_data_mutex;
353 std::mutex m_state_mutex;
354
355 std::function<void(const std::shared_ptr<SignalSourceContainer>&, ProcessingState)> m_state_cb;
356 std::unordered_map<std::string, RegionGroup> m_region_groups;
357
358 /** @brief Backing storage for per-channel DataVariants returned by channel_data(). */
359 mutable std::vector<DataVariant> m_channel_cache;
360
361 /** @brief Row cache backing the double span returned by get_frame(). */
362 mutable std::vector<double> m_frame_cache;
363};
364
365} // namespace MayaFlux::Kakshya
IO::ImageData image
Definition Decoder.cpp:57
uint32_t width
Definition Decoder.cpp:59
Type-erased accessor for NDData with semantic view construction.
Data-driven interface for managing arbitrary processable signal sources.
void unload_region(const Region &) override
No-op.
void load_region(const Region &) override
No-op.
std::unordered_map< std::string, RegionGroup > m_region_groups
void set_processing_chain(const std::shared_ptr< DataProcessingChain > &c) override
Set the processing chain for this container.
void set_structure(ContainerDataStructure s) override
Set the data structure for this container.
std::vector< DataVariant > m_channel_cache
Backing storage for per-channel DataVariants returned by channel_data().
~TextureContainer() override=default
TextureContainer(const std::shared_ptr< Core::VKImage > &image, Portal::Graphics::ImageFormat format)
Construct from an existing VKImage, downloading its pixel data.
TextureContainer(const TextureContainer &)=delete
ContainerDataStructure & get_structure() override
Get the data structure defining this container's layout.
TextureContainer(uint32_t width, uint32_t height, Portal::Graphics::ImageFormat format, uint32_t layers=1)
Construct an empty container with declared dimensions.
std::shared_ptr< DataProcessingChain > get_processing_chain() override
Get the current processing chain for this container.
bool has_active_readers() const override
No-op.
TextureContainer(TextureContainer &&)=delete
std::function< void(const std::shared_ptr< SignalSourceContainer > &, ProcessingState)> m_state_cb
std::vector< DataVariant > m_processed_data
void unregister_dimension_reader(uint32_t) override
No-op.
bool all_dimensions_consumed() const override
No-op.
const ContainerDataStructure & get_structure() const override
void set_default_processor(const std::shared_ptr< DataProcessor > &p) override
Set the default data processor for this container.
void mark_buffers_for_removal() override
No-op.
size_t row_stride() const
Byte count of one complete pixel row.
std::shared_ptr< DataProcessor > get_default_processor() const override
Get the current default data processor.
std::shared_ptr< DataProcessingChain > m_chain
uint32_t register_dimension_reader(uint32_t) override
No-op.
Portal::Graphics::ImageFormat get_format() const
std::vector< double > m_frame_cache
Row cache backing the double span returned by get_frame().
void create_default_processor() override
No-op.
TextureContainer & operator=(const TextureContainer &)=delete
bool is_region_loaded(const Region &) const override
Check if a region is loaded in memory.
void mark_buffers_for_processing(bool) override
No-op.
std::vector< DataVariant > get_region_group_data(const RegionGroup &) const override
Get data for multiple regions efficiently.
std::shared_ptr< DataProcessor > m_processor
TextureContainer & operator=(TextureContainer &&)=delete
void mark_dimension_consumed(uint32_t, uint32_t) override
No-op.
size_t byte_size() const
Total byte count of the pixel buffer.
SignalSourceContainer wrapping GPU texture data as addressable pixel bytes.
ProcessingState
Represents the current processing lifecycle state of a container.
std::optional< RegionGroup > get_region_group(const std::unordered_map< std::string, RegionGroup > &groups, const std::string &name)
Get a RegionGroup by name from a group map.
void add_region_group(std::unordered_map< std::string, RegionGroup > &groups, const RegionGroup &group)
Add a RegionGroup to a group map.
MemoryLayout
Memory layout for multi-dimensional data.
Definition NDData.hpp:39
void remove_region_group(std::unordered_map< std::string, RegionGroup > &groups, const std::string &name)
Remove a RegionGroup by name from a group map.
ImageFormat
User-friendly image format enum.
Container structure for consistent dimension ordering.
Organizes related signal regions into a categorized collection.
Represents a point or span in N-dimensional space.
Definition Region.hpp:73