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