MayaFlux 0.5.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
SpatialTransfer.cpp
Go to the documentation of this file.
1#include "SpatialTransfer.hpp"
2
3#include "FileWriter.hpp"
4#include "SpatialExport.hpp"
5
8
11
13
14#include <chrono>
15
16namespace MayaFlux::IO {
17
19 std::string stream_name,
20 std::shared_ptr<Buffers::NetworkGeometryBuffer> buffer)
21{
23 .stream_name = std::move(stream_name),
24 .capture_frame = [buffer](SpatialCache& cache, const std::string& name) {
26 }
27 };
28}
29
30namespace {
31 std::atomic<uint32_t> g_next_spatial_capture_id { 1 };
32
33 bool run_sources(SpatialCache& cache, const std::vector<SpatialCaptureSource>& sources)
34 {
35 for (const auto& source : sources) {
36 if (!source.capture_frame(cache, source.stream_name)) {
38 "SpatialCapture: source '{}' failed", source.stream_name);
39 return false;
40 }
41 }
42 return true;
43 }
44} // namespace
45
47 std::shared_ptr<SpatialCache> cache;
48 std::vector<SpatialCaptureSource> sources;
49 uint32_t max_frames {};
50 std::atomic<uint32_t> frame { 0 };
51 std::atomic<bool> stop_requested { false };
52 std::atomic<bool> recording { false };
53};
54
56{
57 if (state.max_frames != 0 && state.frame.load(std::memory_order_relaxed) >= state.max_frames) {
58 return false;
59 }
60
61 if (!run_sources(*state.cache, state.sources)) {
63 "SpatialCapture: failed at frame {}", state.frame.load(std::memory_order_relaxed));
64 return false;
65 }
66
67 state.frame.fetch_add(1, std::memory_order_relaxed);
68 return true;
69}
70
72 Vruta::TaskScheduler& scheduler,
73 std::shared_ptr<SpatialCache> cache,
74 std::vector<SpatialCaptureSource> sources)
75 : m_scheduler(scheduler)
76 , m_cache(std::move(cache))
77 , m_sources(std::move(sources))
78{
79}
80
85
87{
88 return m_state && m_state->recording.load(std::memory_order_acquire);
89}
90
92{
93 return m_state ? m_state->frame.load(std::memory_order_relaxed) : 0;
94}
95
96void SpatialCapture::start(uint32_t max_frames, uint64_t frame_interval)
97{
98 if (!m_cache) {
100 "SpatialCapture: cannot start, null cache");
101 return;
102 }
103
104 if (m_sources.empty()) {
106 "SpatialCapture: cannot start, no sources");
107 return;
108 }
109
110 stop();
111
112 m_state = std::make_shared<CaptureState>();
113 m_state->cache = m_cache;
114 m_state->sources = m_sources;
115 m_state->max_frames = max_frames;
116 m_state->recording.store(true, std::memory_order_release);
117
118 m_task_name = "spatial_capture_"
119 + std::to_string(g_next_spatial_capture_id.fetch_add(1, std::memory_order_relaxed));
120
121 auto routine = [](Vruta::TaskScheduler&,
122 std::shared_ptr<CaptureState> state,
123 uint64_t interval) -> Vruta::GraphicsRoutine {
124 auto& p = co_await Kriya::GetGraphicsPromise {};
125 while (!p.should_terminate
126 && !state->stop_requested.load(std::memory_order_acquire)
127 && run_one_frame(*state)) {
128 co_await Kriya::FrameDelay { .frames_to_wait = interval };
129 }
130 state->recording.store(false, std::memory_order_release);
131 };
132
134 std::make_shared<Vruta::GraphicsRoutine>(
135 routine(m_scheduler, m_state, frame_interval)),
136 m_task_name, false);
137
139 "SpatialCapture: recording {} stream(s) every {} frame(s), {}",
140 m_sources.size(), frame_interval,
141 max_frames == 0 ? std::string("unbounded")
142 : std::format("{} frames", max_frames));
143}
144
146{
147 if (!m_state || !m_state->recording.load(std::memory_order_acquire)) {
148 return;
149 }
150
151 m_state->stop_requested.store(true, std::memory_order_release);
153
155 "SpatialCapture: stop requested after {} frames",
156 m_state->frame.load(std::memory_order_relaxed));
157}
158
160 std::shared_ptr<Buffers::RelaxationGridBuffer> grid,
161 std::string stream_name,
162 float extent,
163 ToAttributes to_attributes)
164 : m_grid(std::move(grid))
165 , m_stream_name(std::move(stream_name))
166 , m_to_attributes(std::move(to_attributes))
167{
169}
170
175
176bool RelaxationGridCapture::start(const std::string& filepath)
177{
178 auto cache = std::make_shared<SpatialCache>();
179 if (!cache->open(filepath)) {
181 "RelaxationGridCapture: failed to open '{}': {}", filepath, cache->get_last_error());
182 return false;
183 }
184
185 m_cache = std::move(cache);
187 "RelaxationGridCapture: recording '{}' to '{}'", m_stream_name, filepath);
188 return true;
189}
190
192{
193 if (!m_cache) {
194 return;
195 }
196
197 m_cache->close();
198 m_cache.reset();
199
201 "RelaxationGridCapture: stopped '{}'", m_stream_name);
202}
203
204bool RelaxationGridCapture::write_snapshot(std::span<const uint8_t> bytes)
205{
206 if (!m_cache) {
207 return true;
208 }
209
210 return m_cache->write(m_stream_name,
213 .positions = m_positions,
214 .ids = m_ids,
215 .attributes = m_to_attributes(bytes) });
216}
217
219 const std::string& path_pattern,
220 const std::vector<SpatialCaptureSource>& sources)
221{
222 if (sources.empty()) {
224 "save_spatial_snapshot: no sources");
225 return false;
226 }
227
228 const auto now_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
229 std::chrono::system_clock::now().time_since_epoch())
230 .count();
231 const auto path = resolve_sequence_path(path_pattern, static_cast<uint64_t>(now_ms));
232
233 SpatialCache cache;
234 if (!cache.open(path)) {
236 "save_spatial_snapshot: failed to open '{}': {}", path, cache.get_last_error());
237 return false;
238 }
239
240 const bool ok = run_sources(cache, sources);
241 cache.close();
242 return ok;
243}
244
245} // namespace MayaFlux::IO
#define MF_INFO(comp, ctx,...)
#define MF_ERROR(comp, ctx,...)
std::shared_ptr< NetworkGeometryBuffer > buffer
std::string name
Definition VKDevice.cpp:143
std::function< std::vector< SpatialAttribute >(std::span< const uint8_t >)> ToAttributes
std::shared_ptr< SpatialCache > m_cache
bool start(const std::string &filepath)
Open the archive.
RelaxationGridCapture(std::shared_ptr< Buffers::RelaxationGridBuffer > grid, std::string stream_name, float extent, ToAttributes to_attributes)
std::shared_ptr< Buffers::RelaxationGridBuffer > m_grid
bool write_snapshot(std::span< const uint8_t > bytes)
Write one snapshot as a sample, if currently capturing.
std::string get_last_error() const
bool open(const std::string &filepath)
Open an Alembic archive for writing, Ogawa backend.
void close()
Finalize and close the archive.
Alembic-backed writer for time-sampled spatial entity state: particle systems, point clouds,...
std::shared_ptr< SpatialCache > m_cache
std::vector< SpatialCaptureSource > m_sources
static bool run_one_frame(CaptureState &state)
One tick: write every source's stream.
void start(uint32_t max_frames=0, uint64_t frame_interval=1)
Spawn the capture routine, resetting the frame counter.
std::shared_ptr< CaptureState > m_state
Vruta::TaskScheduler & m_scheduler
void stop()
Request the capture routine stop.
SpatialCapture(Vruta::TaskScheduler &scheduler, std::shared_ptr< SpatialCache > cache, std::vector< SpatialCaptureSource > sources)
A C++20 coroutine-based graphics processing task with frame-accurate timing.
Definition Routine.hpp:496
void add_task(const std::shared_ptr< Routine > &routine, const std::string &name="", bool initialize=false)
Add a routine to the scheduler based on its processing token.
Definition Scheduler.cpp:23
bool cancel_task(const std::shared_ptr< Routine > &routine)
Cancels and removes a task from the scheduler.
Token-based multimodal task scheduling system for unified coroutine processing.
Definition Scheduler.hpp:51
bool relaxation_grid_positions(const std::shared_ptr< Buffers::RelaxationGridBuffer > &grid, float extent, std::vector< glm::vec3 > &positions, std::vector< uint64_t > &ids)
Generate the fixed grid positions and cell ids a RelaxationGridBuffer's cells occupy,...
bool save_spatial_snapshot(const std::string &path_pattern, const std::vector< SpatialCaptureSource > &sources)
Open a fresh SpatialCache, write every source once, then close it.
std::string resolve_sequence_path(std::string_view pattern, uint64_t frame)
Substitute a frame index into a numbered output pattern.
bool write_network_geometry_buffer_sample(SpatialCache &cache, const std::string &stream_name, const std::shared_ptr< Buffers::NetworkGeometryBuffer > &buffer)
Pack a NetworkGeometryBuffer's driving network into one or more SpatialCache streams,...
SpatialCaptureSource make_network_geometry_source(std::string stream_name, std::shared_ptr< Buffers::NetworkGeometryBuffer > buffer)
Build a SpatialCaptureSource that packs a NetworkGeometryBuffer's driving network through write_netwo...
@ FileIO
Filesystem I/O operations.
@ IO
Networking, file handling, streaming.
One named stream a SpatialCapture (or save_spatial_snapshot) writes into a SpatialCache each tick.
std::shared_ptr< SpatialCache > cache
std::vector< SpatialCaptureSource > sources
Portal::Graphics::PrimitiveTopology topology
One sample's worth of data for one named stream.
graphics-domain awaiter for frame-accurate timing delays
Templated awaitable for accessing a coroutine's promise object.