MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
TextureArrayBuffer.hpp
Go to the documentation of this file.
1#pragma once
2
3#include "TextureBuffer.hpp"
4
7
8namespace MayaFlux::Core {
9class VKImage;
10}
11
12namespace MayaFlux::Buffers {
13
14/**
15 * @class TextureArrayBuffer
16 * @brief A TextureBuffer whose image is a stack of layers, composed in one draw.
17 *
18 * Each layer is a frame of the same size and format, bound to the fragment shader
19 * as a sampler2DArray named textureArray. What the stack means is the shader's
20 * choice: a blend, a median, a slit-scan, a delay map, a difference, a mosaic.
21 * A layer that is not written keeps its last frame, so sources at different rates
22 * can share one array.
23 *
24 * A layer is written from raw pixels, an ImageData or a VKImage, by index or at a
25 * write head that advances on every push_layer. A frame whose extent differs from
26 * the array is dropped, or fitted into the layer with the FitMode chosen by
27 * set_fit. A fit leaves any area it does not cover with the layer's previous
28 * contents. Raw pixels must already match the layer, and are written through host
29 * storage that is uploaded once per cycle. The first VKImage, or an ImageData that
30 * needs converting, moves the buffer to writing each layer straight into the
31 * image, which keeps layers written either way intact. Every such write completes
32 * before the call returns.
33 *
34 * A fit that does not cover the whole layer clears the layer first, so a smaller
35 * part replacing a larger one leaves nothing of it behind. Each layer may have its
36 * own fit through set_layer_fit.
37 *
38 * The shipped shader, texture_array.frag, takes a push constant block of the layer
39 * count, a mode number whose meaning belongs to the shader, and a weight for each
40 * of the first WEIGHT_SLOTS layers. Choose a shader with
41 * setup_rendering({ .fragment_shader = ... }). Extra textures such as a delay map,
42 * or a second array, go in RenderConfig::additional_textures and bind from binding
43 * 2 by the sampler's name. A shader with its own push constants replaces the block
44 * through set_push_constants().
45 *
46 * Two vec4 per layer reach the shader beyond the push constants once
47 * enable_layer_data is called, or set_layer_params is, before the first frame. A
48 * shader reads them with
49 * `layout(set = 1, binding = 0) readonly buffer LayerData { vec4 layerData[]; };`
50 * where layerData[2 * layer] holds the params, which are the caller's to use for
51 * placement, scale, a mask or anything else, and layerData[2 * layer + 1] holds the
52 * timing, which a Chimera fills for the layers it feeds. A shader that does not
53 * declare the buffer must not enable it.
54 *
55 * Rendering, transform and window targeting are those of TextureBuffer. Reading
56 * the buffer back through a texture read returns every layer and is not meant for
57 * this class. Write from the graphics frame clock or the thread that processes the
58 * buffer.
59 */
60class MAYAFLUX_API TextureArrayBuffer : public TextureBuffer {
61public:
62 /** @brief Layers the shipped push constant block carries weights for. */
63 static constexpr uint32_t WEIGHT_SLOTS = 30;
64
65 /**
66 * @param width Width of every layer in pixels.
67 * @param height Height of every layer in pixels.
68 * @param format Pixel format of every layer.
69 * @param layers Number of layers, at least 1.
70 */
72 uint32_t width,
73 uint32_t height,
75 uint32_t layers);
76
77 /**
78 * @brief Build from a stream spec: width, height and format of every layer,
79 * with ring_frames as the layer count. The frame rate is not used.
80 * @param spec Extent, format and layer count.
81 * @param fit How a frame of another extent is fitted, or nullopt to drop it.
82 */
83 explicit TextureArrayBuffer(
84 const Kakshya::VideoStreamSpec& spec,
85 std::optional<Portal::Graphics::FitMode> fit = Portal::Graphics::FitMode::STRETCH);
86
87 void setup_processors(ProcessingToken token) override;
88
89 void setup_rendering(const RenderConfig& config) override;
90
91 /** @brief Number of layers. */
92 [[nodiscard]] uint32_t get_layer_count() const { return m_layers; }
93
94 /** @brief Bytes in one layer. */
95 [[nodiscard]] size_t get_layer_byte_size() const { return m_layer_bytes; }
96
97 /** @brief The layer the next push_layer writes. */
98 [[nodiscard]] uint32_t get_write_head() const { return m_write_head; }
99
100 /**
101 * @brief Choose what happens to a frame whose extent differs from the layer.
102 * @param fit How the frame is fitted, or nullopt to drop it.
103 * @param filter Filter used when the fit scales the frame.
104 */
105 void set_fit(std::optional<Portal::Graphics::FitMode> fit,
106 Portal::Graphics::FilterMode filter = Portal::Graphics::FilterMode::LINEAR)
107 {
108 m_fit = fit;
109 m_filter = filter;
110 }
111
112 /**
113 * @brief Give one layer its own fit, replacing the one from set_fit for that layer.
114 * @param layer Layer index.
115 * @param fit How a frame of another extent is fitted, or nullopt to drop it.
116 */
117 void set_layer_fit(uint32_t layer, std::optional<Portal::Graphics::FitMode> fit)
118 {
119 m_layer_fit[layer] = fit;
120 }
121
122 /**
123 * @brief Create the per-layer buffer the shader reads as layerData.
124 *
125 * Call it before the first frame, and only for a shader that declares the
126 * buffer. Calling it again does nothing.
127 */
128 void enable_layer_data();
129
130 /** @brief Whether the per-layer buffer exists. */
131 [[nodiscard]] bool has_layer_data() const { return m_layer_data != nullptr; }
132
133 /**
134 * @brief Set the params of one layer, layerData[2 * layer]. Enables layer data.
135 * @param layer Layer index.
136 * @param values Four floats whose meaning belongs to the shader.
137 */
138 void set_layer_params(uint32_t layer, const glm::vec4& values);
139
140 /**
141 * @brief Set the timing of one layer, layerData[2 * layer + 1]. Enables layer data.
142 *
143 * A Chimera fills it for the layers it feeds, as age in seconds, frames behind
144 * the live head, ring position and 1 once the layer has been fed.
145 *
146 * @param layer Layer index.
147 * @param values Four floats.
148 */
149 void set_layer_timing(uint32_t layer, const glm::vec4& values);
150
151 /**
152 * @brief Replace the frame of one layer with raw pixels.
153 * @param layer Layer index.
154 * @param pixels Exactly get_layer_byte_size() bytes.
155 * @return True if the frame was stored.
156 */
157 bool submit_layer(uint32_t layer, std::span<const uint8_t> pixels);
158
159 /**
160 * @brief Replace the frame of one layer with an image.
161 *
162 * An image of the layer's extent and format is stored as raw pixels. Any other
163 * is uploaded and fitted, or dropped when set_fit chose nullopt.
164 *
165 * @return True if the frame was stored.
166 */
167 bool submit_layer(uint32_t layer, const Kakshya::ImageData& image);
168
169 /**
170 * @brief Replace the frame of one layer with a GPU image, with no host read.
171 *
172 * The image is blitted into the layer, converting format and fitting its extent
173 * as set_fit says. It must not be this buffer's own image.
174 *
175 * @return True if the frame was stored.
176 */
177 bool submit_layer(uint32_t layer, const std::shared_ptr<Core::VKImage>& image);
178
179 /**
180 * @brief Write a frame at the write head and advance it, wrapping at the last
181 * layer. The head does not move if the frame was not stored.
182 * @tparam Source Anything submit_layer accepts.
183 * @return True if the frame was stored.
184 */
185 template <typename Source>
186 bool push_layer(const Source& source)
187 {
188 if (!submit_layer(m_write_head, source)) {
189 return false;
190 }
191 m_write_head = (m_write_head + 1) % m_layers;
192 return true;
193 }
194
195 /**
196 * @brief Set the weight of one layer.
197 *
198 * What a weight does is up to the shader. In the shipped shader a weight of
199 * zero or less hides the layer. Layers past WEIGHT_SLOTS have no weight.
200 *
201 * @param layer Layer index.
202 * @param weight Weight passed to the shader.
203 */
204 void set_weight(uint32_t layer, float weight);
205
206 /**
207 * @brief Set the weights of the first layers in order.
208 * @param weights Weights, at most WEIGHT_SLOTS or get_layer_count() are used.
209 */
210 void set_weights(std::span<const float> weights);
211
212 /**
213 * @brief Set the mode number passed to the shader.
214 *
215 * Each shader interprets it; see the comment above the push constant block
216 * of the shader in use.
217 */
218 void set_mode(uint32_t mode);
219
220 /**
221 * @brief Replace the default push constants with the shader's own.
222 *
223 * After this call the layer count, mode and weights are no longer sent. The
224 * data is resent unchanged every frame until it is replaced.
225 *
226 * @param data Bytes of the push constant block.
227 * @param size Size of the block in bytes.
228 */
229 void set_push_constants(const void* data, size_t size);
230
231 /** @brief Replace the default push constants with a block of type T. */
232 template <typename T>
233 void set_push_constants(const T& block)
234 {
235 set_push_constants(&block, sizeof(T));
236 }
237
238private:
239 struct Push {
240 uint32_t layer_count {};
241 uint32_t mode {};
242 std::array<float, WEIGHT_SLOTS> weights {};
243 };
244
245 uint32_t m_layers;
247 uint32_t m_write_head {};
248 bool m_layer_mode {};
249 std::optional<Portal::Graphics::FitMode> m_fit { Portal::Graphics::FitMode::STRETCH };
250 Portal::Graphics::FilterMode m_filter { Portal::Graphics::FilterMode::LINEAR };
251 std::unordered_map<uint32_t, std::optional<Portal::Graphics::FitMode>> m_layer_fit;
252 std::shared_ptr<VKBuffer> m_layer_staging;
253 std::shared_ptr<VKBuffer> m_source_staging;
255 std::vector<uint8_t> m_blank;
256 std::shared_ptr<VKBuffer> m_layer_data;
257 std::vector<glm::vec4> m_layer_values;
259 std::vector<uint8_t> m_custom_push;
260
261 void ensure_array();
262 void enter_layer_mode();
263 void flush_push();
264 void clear_layer(uint32_t layer);
265 void bind_layer_data();
266 void write_layer_value(size_t index, const glm::vec4& values);
267 [[nodiscard]] std::optional<Portal::Graphics::FitMode> fit_for(uint32_t layer) const;
268};
269
270}
IO::ImageData image
Definition Decoder.cpp:47
const std::vector< float > * pixels
Definition Decoder.cpp:48
bool format
bool size
uint32_t mode
double weight
std::vector< std::byte > values
Definition VDBWriter.cpp:26
uint32_t index
Definition VKDevice.cpp:122
uint32_t width
uint32_t height
void set_fit(std::optional< Portal::Graphics::FitMode > fit, Portal::Graphics::FilterMode filter=Portal::Graphics::FilterMode::LINEAR)
Choose what happens to a frame whose extent differs from the layer.
uint32_t get_write_head() const
The layer the next push_layer writes.
std::shared_ptr< VKBuffer > m_source_staging
size_t get_layer_byte_size() const
Bytes in one layer.
void set_push_constants(const T &block)
Replace the default push constants with a block of type T.
bool has_layer_data() const
Whether the per-layer buffer exists.
std::unordered_map< uint32_t, std::optional< Portal::Graphics::FitMode > > m_layer_fit
std::shared_ptr< VKBuffer > m_layer_staging
void set_layer_fit(uint32_t layer, std::optional< Portal::Graphics::FitMode > fit)
Give one layer its own fit, replacing the one from set_fit for that layer.
uint32_t get_layer_count() const
Number of layers.
bool push_layer(const Source &source)
Write a frame at the write head and advance it, wrapping at the last layer.
Portal::Graphics::ImageCacheEntry m_source_cache
A TextureBuffer whose image is a stack of layers, composed in one draw.
A hybrid buffer managing both a textured quad geometry and its pixel data.
ProcessingToken
Bitfield enum defining processing characteristics and backend requirements for buffer operations.
ImageFormat
User-friendly image format enum.
Definition ImageData.hpp:12
FilterMode
Texture filtering mode.
Raw image data loaded from file or read back from a GPU image.
Definition ImageData.hpp:55
Describes a DynamicVideoStream so it can be built by designated initializer.
Image retained by the producing owner.
Unified rendering configuration for graphics buffers.