32 m_state->buffer->set_weight(
static_cast<uint32_t
>(layer),
static_cast<float>(
weight));
39 m_state->buffer->set_layer_params(
static_cast<uint32_t
>(layer),
values);
46 *
m_state->layers.at(layer).ratio = ratio;
56 auto& target =
m_state->layers.at(layer);
57 if (!target.ring && !target.collection) {
62 const double elapsed = std::max(
63 static_cast<double>(
m_state->ticks) /
m_state->frame_rate - target.delay, 0.0);
64 target.lag_offset = seconds - (*target.lag)(elapsed);
65 }
else if (target.ring) {
66 target.position =
static_cast<double>(target.ring->get_write_head())
67 - seconds * target.ring->get_frame_rate();
68 target.entered =
true;
70 const double rate = target.collection_rate > 0.0 ? target.collection_rate :
m_state->frame_rate;
71 target.position =
static_cast<double>(target.collection->get_write_head()) - seconds *
rate;
72 target.entered =
true;
82 auto& target =
m_state->layers.at(layer);
84 target.collection.reset();
86 target.picture = std::make_shared<const Kakshya::ImageData>(std::move(
image));
96 auto& target =
m_state->layers.at(layer);
98 target.collection.reset();
99 target.picture.reset();
100 target.image = std::move(
image);
103void Chimera::set(
size_t layer, std::shared_ptr<Kakshya::DynamicVideoStream> ring)
109 auto& target =
m_state->layers.at(layer);
110 target.image.reset();
111 target.picture.reset();
112 target.collection.reset();
113 target.ring = std::move(ring);
114 target.entered =
false;
117void Chimera::set(
size_t layer, std::shared_ptr<Kakshya::TextureCollection> collection,
double frame_rate)
123 auto& target =
m_state->layers.at(layer);
124 target.image.reset();
125 target.picture.reset();
127 target.collection = std::move(collection);
128 target.collection_rate = frame_rate;
129 target.entered =
false;
141 if (state.
buffer->has_layer_data()) {
148 state.
ticks = frame + 1;
149 const double now =
static_cast<double>(frame) / state.
frame_rate;
152 for (
auto& layer : state.
layers) {
155 if (now < layer.delay) {
159 const auto age =
static_cast<float>(now - layer.delay);
162 if (layer.dirty && state.
buffer->submit_layer(
current, *layer.picture)) {
164 stamp(state,
current, glm::vec4(age, 0.0F, 0.0F, 1.0F));
169 if (frame % layer.interval != 0) {
175 stamp(state,
current, glm::vec4(age, 0.0F, 0.0F, 1.0F));
177 }
else if (layer.ring) {
179 }
else if (layer.collection) {
187 const double latest =
static_cast<double>(head) - nearest;
191 layer.
position =
static_cast<double>(head) - std::max(behind, nearest);
203 const uint64_t head = collection->get_write_head();
209 place(state, layer, head,
rate, 1.0, elapsed);
211 const auto frame =
static_cast<uint64_t
>(std::max(std::floor(layer.
position), 0.0));
212 const uint64_t ring = collection->get_spec().ring_layers;
213 if (frame >= head || (ring > 0 && frame + ring < head)) {
216 "Chimera layer {} is outside the collection, holding its last frame",
index);
223 const auto slot =
static_cast<uint32_t
>(ring > 0 ? frame % ring : frame);
224 if (state.
buffer->submit_layer(
index, collection->to_image(slot))) {
226 glm::vec4(
static_cast<float>(elapsed),
static_cast<float>(
static_cast<double>(head) - layer.
position),
227 static_cast<float>(layer.
position), 1.0F));
233 const auto& ring = layer.
ring;
234 const uint64_t head = ring->get_write_head();
239 place(state, layer, head, ring->get_frame_rate(), 2.0, elapsed);
241 const double whole = std::max(std::floor(layer.
position), 0.0);
242 const auto frame =
static_cast<uint64_t
>(whole);
243 if (!ring->is_frame_available(frame)) {
246 "Chimera layer {} is outside the ring, holding its last frame",
index);
253 std::span<const uint8_t>
pixels = ring->get_frame_pixels(frame);
258 const double fraction = layer.
position - whole;
259 if (layer.
smooth && fraction > 0.0 && ring->is_frame_available(frame + 1)) {
260 const auto next = ring->get_frame_pixels(frame + 1);
268 const bool same_extent = ring->get_width() == state.
buffer->get_width()
269 && ring->get_height() == state.
buffer->get_height()
270 && ring->get_format() == state.
buffer->get_format();
276 ring->get_width(), ring->get_height(),
278 ring->get_format(),
pixels)) {
284 glm::vec4(
static_cast<float>(elapsed),
static_cast<float>(
static_cast<double>(head) - layer.
position),
285 static_cast<float>(layer.
position), 1.0F));
290 std::shared_ptr<Buffers::TextureArrayBuffer>
buffer,
291 std::shared_ptr<BufferPipeline> pipeline)
293 , m_pipeline(
std::move(pipeline))
307 "ChimeraBuilder::record needs a pipeline");
317 m_actions.push_back({ .seconds = seconds, .run = std::move(action) });
339 target.collection.reset();
340 target.image.reset();
341 target.picture.reset();
363 target.image.reset();
364 target.picture.reset();
365 target.collection.reset();
366 target.piped =
false;
367 target.ring = std::move(ring);
374 target.image.reset();
375 target.picture.reset();
377 target.piped =
false;
378 target.collection = std::move(collection);
379 target.collection_rate = frame_rate;
387 target.collection.reset();
388 target.picture.reset();
389 target.piped =
false;
390 target.image = std::move(
image);
398 target.collection.reset();
399 target.image.reset();
400 target.piped =
false;
401 target.picture = std::make_shared<const Kakshya::ImageData>(std::move(
image));
409 return from(std::move(*loaded));
413 "ChimeraBuilder::from could not load '{}'", path);
425 return lag(Kinesis::constant<double, double>(seconds));
430 current().
lag = std::make_shared<const Kinesis::TimeMap>(std::move(seconds));
476 "ChimeraBuilder::start needs a buffer and at least one layer");
482 "ChimeraBuilder::start declared {} layers, the buffer holds {}",
489 "ChimeraBuilder::start needs a pipeline to feed the layers");
501 "ChimeraBuilder::start found no stream in the pipeline for from_pipeline");
506 auto state = std::make_shared<Chimera::State>();
514 for (
const auto&
layer : state->layers) {
530 const std::weak_ptr<Chimera::State> weak = state;
533 if (
const auto running = weak.lock()) {
534 Chimera::tick(*running, cycle);
540 const auto cycles =
static_cast<uint32_t
>(std::max(std::round(action.seconds * state->frame_rate), 1.0));
544 const auto running = weak.lock();
545 if (!running || cycle == 0) {
550 view.m_state = running;
559 m_pipeline->execute_frame_rate();
561 set.m_guard = std::shared_ptr<void>(
nullptr, [state](
void*) { state->halt(); });
562 set.m_state = std::move(state);
#define MF_ERROR(comp, ctx,...)
#define MF_WARN(comp, ctx,...)
const std::vector< float > * pixels
std::shared_ptr< NetworkGeometryBuffer > buffer
std::vector< std::byte > values
static std::optional< ImageData > load(const std::string &path, int desired_channels=4, std::optional< Kakshya::Region > region=std::nullopt)
Load image from file (static utility)
BufferOperation & every_n_cycles(uint32_t n)
Set cycle interval for periodic execution.
static BufferOperation dispatch_to(OperationFunction handler, Buffers::ProcessingToken token=Buffers::ProcessingToken::AUDIO_BACKEND)
Create a dispatch operation for external processing.
Fundamental unit of operation in buffer processing pipelines.
ChimeraBuilder & layer_data()
Create the per-layer buffer the shader reads as layerData.
ChimeraBuilder & params(const glm::vec4 &values)
Params for the layer in the buffer's per-layer data, such as placement.
ChimeraBuilder & record(BufferOperation &&operation)
Add an operation to the builder's pipeline, ahead of the feeding.
ChimeraBuilder & speed(double ratio)
Play a layer without a lag at this multiple of the ring's frame rate.
std::optional< Portal::Graphics::RenderConfig > m_render
ChimeraBuilder & lag(double seconds)
Read the ring this many seconds behind its write head, at least two frames.
ChimeraBuilder & fit(Portal::Graphics::FitMode mode)
Fit frames of another extent into this layer in this way, instead of the buffer's.
std::vector< Action > m_actions
ChimeraBuilder & level(double weight)
Weight passed to the shader for the layer.
ChimeraBuilder & use_pipeline(std::shared_ptr< BufferPipeline > pipeline)
Replace the builder's pipeline with one made elsewhere.
ChimeraBuilder & render(Portal::Graphics::RenderConfig config)
Draw the buffer this way once start() runs.
Chimera::Layer & current()
ChimeraBuilder & enters_after(double seconds)
Seconds after start() before the layer begins to be fed.
ChimeraBuilder & smooth(bool enable=true)
Blend the two frames around a ring position, so slow or varying time does not step.
ChimeraBuilder & from(std::shared_ptr< Kakshya::DynamicVideoStream > ring)
Feed the layer from a ring that is being recorded.
std::shared_ptr< Buffers::TextureArrayBuffer > m_buffer
ChimeraBuilder & layer()
Open the next layer.
ChimeraBuilder(std::shared_ptr< Buffers::TextureArrayBuffer > buffer, std::shared_ptr< BufferPipeline > pipeline)
ChimeraBuilder & every_n_frames(uint32_t frames)
Refresh a ring or GPU image layer every this many frames.
Chimera start()
Start feeding every declared layer on the frame clock.
ChimeraBuilder & every(double seconds, std::function< void(Chimera &)> action)
Call an action on the running Chimera every this many seconds.
ChimeraBuilder & from_pipeline()
Feed the layer from the stream the pipeline records, read when start() runs.
std::shared_ptr< BufferPipeline > m_pipeline
std::vector< Chimera::Layer > m_layers
Declares the layers of a Chimera, in order, and starts feeding them.
static void tick(State &state, uint64_t frame)
void params(size_t layer, const glm::vec4 &values)
Set the params of one layer in the buffer's per-layer data.
void speed(size_t layer, double ratio)
Change the speed of a freely playing layer.
static void stamp(State &state, uint32_t index, const glm::vec4 &timing)
void level(size_t layer, double weight)
Set the weight of one layer in the buffer's push constants.
void set(size_t layer, Kakshya::ImageData image)
Replace the layer's content with a still image, delivered on the next frame.
size_t layer_count() const
Number of layers.
static void collect(State &state, Layer &layer, uint32_t index, double elapsed)
static void feed(State &state, Layer &layer, uint32_t index, double elapsed)
void cut(size_t layer, double seconds)
Jump a ring or collection layer to a moment this many seconds behind the live head.
std::shared_ptr< State > m_state
static void place(const State &state, Layer &layer, uint64_t head, double rate, double nearest, double elapsed)
Assembles the layers of a TextureArrayBuffer from whatever feeds them, so a fragment shader can build...
static uint32_t get_channel_count(ImageFormat format)
Get the number of color channels for a given format.
@ GRAPHICS_BACKEND
Standard graphics processing backend configuration.
@ Configuration
Configuration and parameter updates.
@ FileIO
Filesystem I/O operations.
@ CoroutineScheduling
Coroutine scheduling and temporal coordination (Vruta::TaskScheduler)
@ Kriya
Automatable tasks and fluent scheduling api for Nodes and Buffers.
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.
bool blend_frames(ImageFormat format, std::span< const uint8_t > a, std::span< const uint8_t > b, uint8_t *out, double weight)
Linearly blend two frames of the same size and format.
FitMode
How a source extent is mapped onto a destination extent.
MAYAFLUX_API uint32_t s_registered_frame_rate
static std::optional< ImageData > from_bytes(uint32_t width, uint32_t height, uint32_t channels, ImageFormat format, std::span< const uint8_t > bytes)
Build an ImageData from raw pixel bytes in format's layout.
Raw image data loaded from file or read back from a GPU image.
Typed, composable, stateless callable from domain D to range R.
std::shared_ptr< Kakshya::DynamicVideoStream > ring
std::optional< glm::vec4 > params
std::optional< Portal::Graphics::FitMode > fit
std::vector< uint8_t > scratch
std::shared_ptr< double > ratio
std::shared_ptr< Kakshya::TextureCollection > collection
std::shared_ptr< const Kinesis::TimeMap > lag
std::vector< Layer > layers
std::shared_ptr< BufferPipeline > pipeline
std::shared_ptr< Buffers::TextureArrayBuffer > buffer
Unified rendering configuration for graphics buffers.