MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
Chimera.hpp
Go to the documentation of this file.
1#pragma once
2
5
6namespace MayaFlux::Buffers {
7class TextureArrayBuffer;
8}
9
10namespace MayaFlux::Core {
11class VKImage;
12}
13
14namespace MayaFlux::Kakshya {
15class DynamicVideoStream;
16class TextureCollection;
17}
18
19namespace MayaFlux::Vruta {
20class Routine;
21}
22
23namespace MayaFlux::Kriya {
24
25class BufferPipeline;
26class BufferOperation;
27
28/**
29 * @class Chimera
30 * @brief Assembles the layers of a TextureArrayBuffer from whatever feeds them, so a
31 * fragment shader can build one picture out of many.
32 *
33 * A layer holds a still image, a GPU image copied every frame, or a moment of a ring
34 * or a texture collection that is still being recorded. Moments are read at a lag
35 * behind the write head, with
36 * a lag that varies in time, or played freely from where the layer entered, forward,
37 * held or backward, optionally smoothed between frames. A layer can be cut to
38 * another moment, or given a new source, while running. Layers are numbered in
39 * declaration order, enter after their own delay, and refresh every frame or every
40 * few. Copies share the same layers, and the set stops when the last copy goes.
41 *
42 * A lagged layer stays at least two frames behind the frame being written. A layer
43 * whose position falls outside what the ring still holds keeps its last frame and
44 * reports once. stop() holds every layer on its last frame.
45 *
46 * Built from a buffer with ChimeraBuilder or create_chimera. The layers are fed by
47 * the cycles of the builder's pipeline, one per frame, after whatever the pipeline
48 * records. What the layers mean on screen is the buffer's fragment shader. Call the
49 * controls from the frame clock.
50 */
51class MAYAFLUX_API Chimera {
52public:
53 Chimera() = default;
54
55 /** @brief Number of layers. */
56 [[nodiscard]] size_t layer_count() const;
57
58 /**
59 * @brief Set the weight of one layer in the buffer's push constants.
60 * @param layer Layer index.
61 * @param weight What the shader does with it; zero or less hides the layer in
62 * the shipped shader.
63 */
64 void level(size_t layer, double weight);
65
66 /**
67 * @brief Set the params of one layer in the buffer's per-layer data.
68 *
69 * Placement, scale, a mask or anything else the shader reads from
70 * layerData[2 * layer]. Enables the buffer's layer data.
71 */
72 void params(size_t layer, const glm::vec4& values);
73
74 /**
75 * @brief Change the speed of a freely playing layer.
76 *
77 * Applies from the current position, so it never jumps. Zero holds, a negative
78 * ratio plays backward. A lagged layer ignores it.
79 *
80 * @param layer Layer index.
81 * @param ratio Multiple of the ring's frame rate.
82 */
83 void speed(size_t layer, double ratio);
84
85 /**
86 * @brief Jump a ring or collection layer to a moment this many seconds behind
87 * the live head.
88 *
89 * A lagged layer keeps following its lag from there, so a lag that varies carries
90 * on from the new moment. Layers fed by an image ignore it.
91 *
92 * @param layer Layer index.
93 * @param seconds Distance behind the write head, at least two frames.
94 */
95 void cut(size_t layer, double seconds);
96
97 /** @brief Replace the layer's content with a still image, delivered on the next frame. */
98 void set(size_t layer, Kakshya::ImageData image);
99
100 /** @brief Replace the layer's content with a GPU image copied every frame. */
101 void set(size_t layer, std::shared_ptr<Core::VKImage> image);
102
103 /** @brief Replace the layer's content with a ring, read as the layer's lag or speed say. */
104 void set(size_t layer, std::shared_ptr<Kakshya::DynamicVideoStream> ring);
105
106 /**
107 * @brief Replace the layer's content with a texture collection, read as the
108 * layer's lag or speed say.
109 * @param frame_rate Appends per second, which turns seconds into layers; zero
110 * takes one append per frame.
111 */
112 void set(size_t layer, std::shared_ptr<Kakshya::TextureCollection> collection, double frame_rate = 0.0);
113
114 /** @brief Stop feeding. Every layer keeps its last frame. */
115 void stop();
116
117private:
118 friend class ChimeraBuilder;
119
120 struct Layer {
121 std::shared_ptr<Kakshya::DynamicVideoStream> ring;
122 std::shared_ptr<Kakshya::TextureCollection> collection;
123 double collection_rate {};
124 std::shared_ptr<Core::VKImage> image;
125 std::shared_ptr<const Kakshya::ImageData> picture;
126 std::shared_ptr<const Kinesis::TimeMap> lag;
127 std::shared_ptr<double> ratio { std::make_shared<double>(1.0) };
128 std::optional<Portal::Graphics::FitMode> fit;
129 std::optional<glm::vec4> params;
130 std::vector<uint8_t> scratch;
131 double delay {};
132 double lag_offset {};
133 double position {};
134 float level { 1.0F };
135 uint32_t interval { 1 };
136 bool smooth {};
137 bool entered {};
138 bool missed {};
139 bool dirty {};
140 bool piped {};
141 };
142
143 struct State {
144 std::shared_ptr<Buffers::TextureArrayBuffer> buffer;
145 std::shared_ptr<BufferPipeline> pipeline;
146 std::vector<Layer> layers;
147 double frame_rate { 60.0 };
148 uint64_t ticks {};
149
150 void halt();
151 };
152
153 static void tick(State& state, uint64_t frame);
154 static void feed(State& state, Layer& layer, uint32_t index, double elapsed);
155 static void collect(State& state, Layer& layer, uint32_t index, double elapsed);
156 static void place(const State& state, Layer& layer, uint64_t head, double rate, double nearest, double elapsed);
157 static void stamp(State& state, uint32_t index, const glm::vec4& timing);
158
159 std::shared_ptr<State> m_state;
160 std::shared_ptr<void> m_guard;
161};
162
163/**
164 * @class ChimeraBuilder
165 * @brief Declares the layers of a Chimera, in order, and starts feeding them.
166 *
167 * layer() opens a new layer and the calls after it describe that layer. A call made
168 * before any layer() describes the first.
169 *
170 * @code{.cpp}
171 * auto chimera = MayaFlux::create_chimera(array)
172 * .layer().from("res/eye.png").params({ 0.3F, 0.4F, 0.2F, 0.F })
173 * .layer().from("res/mouth.png").params({ 0.5F, 0.7F, 0.3F, 0.F })
174 * .layer().from(ring).lag(0.0)
175 * .layer().from(ring).lag(0.5).smooth().level(0.6)
176 * .start();
177 * @endcode
178 *
179 * The builder holds one BufferPipeline, the one it was given or one it was made
180 * with, so what is recorded and what is assembled read as one chain:
181 *
182 * @code{.cpp}
183 * auto chimera = MayaFlux::create_chimera(array)
184 * .record(BufferOperation::capture_to_stream(io, camera, 240))
185 * .layer().from_pipeline().lag(0.0)
186 * .layer().from_pipeline().lag(0.5)
187 * .start();
188 * @endcode
189 */
190class MAYAFLUX_API ChimeraBuilder {
191public:
192 /**
193 * @param buffer Array buffer whose layers are fed.
194 * @param pipeline The pipeline that records, and whose cycles feed the layers.
195 */
197 std::shared_ptr<Buffers::TextureArrayBuffer> buffer,
198 std::shared_ptr<BufferPipeline> pipeline);
199
200 /** @brief The pipeline the builder records into. */
201 [[nodiscard]] std::shared_ptr<BufferPipeline> get_pipeline() const { return m_pipeline; }
202
203 /** @brief The array buffer whose layers the builder feeds, for its mode, weights or push constants. */
204 [[nodiscard]] std::shared_ptr<Buffers::TextureArrayBuffer> get_buffer() const { return m_buffer; }
205
206 /**
207 * @brief Replace the builder's pipeline with one made elsewhere.
208 *
209 * The pipeline must not be running. start() adds the feeding to the end of its
210 * operations and runs it at the frame rate, one cycle per frame, and stop() ends
211 * it. Operations the builder recorded earlier stay in the pipeline it had.
212 */
213 ChimeraBuilder& use_pipeline(std::shared_ptr<BufferPipeline> pipeline);
214
215 /** @brief Add an operation to the builder's pipeline, ahead of the feeding. */
216 ChimeraBuilder& record(BufferOperation&& operation);
217
218 /**
219 * @brief Call an action on the running Chimera every this many seconds.
220 *
221 * Runs in the pipeline after the layers are fed, on the frame clock, first one
222 * interval after start(). The action may call any control, such as cut, set,
223 * speed or level. It does not belong to a layer.
224 *
225 * @param seconds Interval, at least one frame.
226 * @param action Called with the running Chimera.
227 */
228 ChimeraBuilder& every(double seconds, std::function<void(Chimera&)> action);
229
230 /**
231 * @brief Draw the buffer this way once start() runs.
232 *
233 * Rendering is set up after every layer's params and the layer data are in place,
234 * so a shader that reads layerData is bound correctly. Leave it out for a buffer
235 * that already renders.
236 */
238
239 /**
240 * @brief Create the per-layer buffer the shader reads as layerData.
241 *
242 * For a shader that reads the timing a Chimera writes and declares the buffer.
243 * Layers given params enable it without this call.
244 */
245 ChimeraBuilder& layer_data();
246
247 /** @brief Open the next layer. */
248 ChimeraBuilder& layer();
249
250 /** @brief Feed the layer from the stream the pipeline records, read when start() runs. */
251 ChimeraBuilder& from_pipeline();
252
253 /** @brief Feed the layer from a ring that is being recorded. */
254 ChimeraBuilder& from(std::shared_ptr<Kakshya::DynamicVideoStream> ring);
255
256 /**
257 * @brief Feed the layer from a texture collection that is being appended to.
258 *
259 * Read like a ring, by lag or speed, entirely on the GPU, up to its newest
260 * append. smooth() does not apply.
261 *
262 * @param frame_rate Appends per second, which turns seconds into layers; zero
263 * takes one append per frame.
264 */
265 ChimeraBuilder& from(std::shared_ptr<Kakshya::TextureCollection> collection, double frame_rate = 0.0);
266
267 /** @brief Feed the layer by copying a GPU image every frame. */
268 ChimeraBuilder& from(std::shared_ptr<Core::VKImage> image);
269
270 /** @brief Fill the layer once with a still image. */
272
273 /** @brief Fill the layer once with an image file, loaded now as RGBA. */
274 ChimeraBuilder& from(const std::string& path);
275
276 /** @brief Seconds after start() before the layer begins to be fed. */
277 ChimeraBuilder& enters_after(double seconds);
278
279 /** @brief Read the ring this many seconds behind its write head, at least two frames. */
280 ChimeraBuilder& lag(double seconds);
281
282 /** @brief Read the ring behind its write head by a lag that varies with time, in seconds since the layer entered. */
283 ChimeraBuilder& lag(Kinesis::TimeMap seconds);
284
285 /** @brief Play a layer without a lag at this multiple of the ring's frame rate. Negative plays backward. */
286 ChimeraBuilder& speed(double ratio);
287
288 /** @brief Blend the two frames around a ring position, so slow or varying time does not step. Not for collections. */
289 ChimeraBuilder& smooth(bool enable = true);
290
291 /** @brief Refresh a ring or GPU image layer every this many frames. */
292 ChimeraBuilder& every_n_frames(uint32_t frames);
293
294 /** @brief Fit frames of another extent into this layer in this way, instead of the buffer's. */
296
297 /** @brief Params for the layer in the buffer's per-layer data, such as placement. Enables it. */
298 ChimeraBuilder& params(const glm::vec4& values);
299
300 /** @brief Weight passed to the shader for the layer. */
301 ChimeraBuilder& level(double weight);
302
303 /**
304 * @brief Start feeding every declared layer on the frame clock.
305 * @return The running set, or an empty one if the buffer is null, no layer was
306 * declared, or more layers were declared than the buffer holds.
307 */
308 [[nodiscard]] Chimera start();
309
310private:
311 struct Action {
312 double seconds {};
313 std::function<void(Chimera&)> run;
314 };
315
317
318 std::shared_ptr<Buffers::TextureArrayBuffer> m_buffer;
319 std::shared_ptr<BufferPipeline> m_pipeline;
320 std::optional<Portal::Graphics::RenderConfig> m_render;
321 std::vector<Chimera::Layer> m_layers;
322 std::vector<Action> m_actions;
323};
324
325} // namespace MayaFlux::Kriya
IO::ImageData image
Definition Decoder.cpp:47
std::optional< glm::vec3 > position
Definition Decoder.cpp:93
glm::vec2 current
float rate
std::shared_ptr< NetworkGeometryBuffer > buffer
uint32_t mode
double weight
std::vector< std::byte > values
Definition VDBWriter.cpp:26
uint32_t index
Definition VKDevice.cpp:122
Fundamental unit of operation in buffer processing pipelines.
std::shared_ptr< BufferPipeline > get_pipeline() const
The pipeline the builder records into.
Definition Chimera.hpp:201
std::optional< Portal::Graphics::RenderConfig > m_render
Definition Chimera.hpp:320
std::vector< Action > m_actions
Definition Chimera.hpp:322
std::shared_ptr< Buffers::TextureArrayBuffer > get_buffer() const
The array buffer whose layers the builder feeds, for its mode, weights or push constants.
Definition Chimera.hpp:204
std::shared_ptr< Buffers::TextureArrayBuffer > m_buffer
Definition Chimera.hpp:318
std::shared_ptr< BufferPipeline > m_pipeline
Definition Chimera.hpp:319
std::vector< Chimera::Layer > m_layers
Definition Chimera.hpp:321
Declares the layers of a Chimera, in order, and starts feeding them.
Definition Chimera.hpp:190
std::shared_ptr< void > m_guard
Definition Chimera.hpp:160
std::shared_ptr< State > m_state
Definition Chimera.hpp:159
Assembles the layers of a TextureArrayBuffer from whatever feeds them, so a fragment shader can build...
Definition Chimera.hpp:51
FitMode
How a source extent is mapped onto a destination extent.
Raw image data loaded from file or read back from a GPU image.
Definition ImageData.hpp:55
Typed, composable, stateless callable from domain D to range R.
Definition Tendency.hpp:22
std::function< void(Chimera &)> run
Definition Chimera.hpp:313
std::shared_ptr< Kakshya::DynamicVideoStream > ring
Definition Chimera.hpp:121
std::optional< glm::vec4 > params
Definition Chimera.hpp:129
std::shared_ptr< Core::VKImage > image
Definition Chimera.hpp:124
std::optional< Portal::Graphics::FitMode > fit
Definition Chimera.hpp:128
std::vector< uint8_t > scratch
Definition Chimera.hpp:130
std::shared_ptr< const Kakshya::ImageData > picture
Definition Chimera.hpp:125
std::shared_ptr< Kakshya::TextureCollection > collection
Definition Chimera.hpp:122
std::shared_ptr< const Kinesis::TimeMap > lag
Definition Chimera.hpp:126
std::vector< Layer > layers
Definition Chimera.hpp:146
std::shared_ptr< BufferPipeline > pipeline
Definition Chimera.hpp:145
std::shared_ptr< Buffers::TextureArrayBuffer > buffer
Definition Chimera.hpp:144
Unified rendering configuration for graphics buffers.