MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
Evolve.hpp
Go to the documentation of this file.
1#pragma once
2
4
7
9class NodeNetwork;
10class GpuFieldOperator;
11struct SpatialFieldConfig;
12}
13
14namespace MayaFlux {
15
16/**
17 * @concept GridCellData
18 * @brief Cell types a relaxation grid can be seeded with from a host callable.
19 *
20 * Arithmetic scalars, two component vectors and four component vectors whose
21 * size is a multiple of four bytes, so sizeof matches the std430 array stride
22 * the rule and emit shaders read. Three component vectors are excluded:
23 * glm::vec3 is 12 bytes but strides 16 in an SSBO array, so a rule with such a
24 * cell declares a four component format instead.
25 */
26template <typename State>
27concept GridCellData = (ArithmeticData<State> || GlmVec2Type<State> || GlmVec4Type<State>)
28 && (sizeof(State) % sizeof(uint32_t) == 0);
29
30/**
31 * @class Evolve
32 * @brief Sets a system going from a config: state that persists, a rule that
33 * advances it every cycle, and rendering attached.
34 *
35 * Owned by Creator as a member. Each call performs the whole sequence its
36 * arguments imply and returns the real object it built or was handed, so
37 * nothing is left to attach afterwards. The returned object carries its own
38 * processors and advances on its own each cycle. The call sets it going and
39 * does not step it.
40 *
41 * An evolving system is not a type here. It is a config handed to a class,
42 * and the sequence that class needs is what this performs. The name says
43 * nothing about a medium: it covers whatever the config's class advances,
44 * which today is three families, selected by the config they consume:
45 * - RelaxationGridBuffer::GridConfig builds a grid, seeds it, registers it in
46 * the graphics domain and attaches rendering.
47 * - VolumeGridBuffer::FlowConfig runs the incompressible flow stages over a
48 * volume. Required refs left unnamed are declared under default names, and
49 * any named ref the volume does not yet hold is declared.
50 * - Nodes::Network::SpatialFieldConfig drives a network's vertices through a
51 * GpuFieldOperator, reading its vertex layout from the network's own
52 * primary operator.
53 *
54 * All three route to the graphics domain and every call requires
55 * RenderConfig::target_window. There is no default window. A null target logs
56 * an error and returns nullptr, and nothing is registered. Exceptions thrown
57 * by the classes' constructors propagate.
58 *
59 * Nothing here is realtime safe. Call from the thread that constructs the
60 * objects.
61 *
62 * @code
63 * auto life = vega.evolve(
64 * Buffers::RelaxationGridBuffer::GridConfig {
65 * .width = 256,
66 * .height = 256,
67 * .rule = { .state = Kakshya::GpuDataFormat::UINT32, .shader = "relax_conway_rule.comp.spv" },
68 * .emit = { .state = Kakshya::GpuDataFormat::UINT32, .shader = "relax_vertex_emit.comp.spv" },
69 * },
70 * [](uint32_t x, uint32_t y) { return (x ^ y) % 7U == 0U ? 1U : 0U; },
71 * { .target_window = window });
72 *
73 * auto volume = std::make_shared<Buffers::VolumeGridBuffer>(
74 * lattice,
75 * Buffers::VolumeGridBuffer::SurfaceConfig { "density", { 48, 48, 48 }, 0.5F });
76 * auto density = volume->declare_scalar("density");
77 * auto heat = volume->declare_scalar("heat");
78 * vega.evolve(
79 * volume,
80 * { .carried = { { density, 0.99F } },
81 * .buoyancy = Buffers::VolumeGridBuffer::FlowConfig::Buoyancy { .temperature = heat, .density = density } },
82 * { .target_window = window });
83 *
84 * auto particles = vega.ParticleNetwork(50000, glm::vec3(-1.0F), glm::vec3(1.0F), distribution);
85 * vega.evolve(
86 * particles,
87 * { .absorb_radius = 0.05F },
88 * [](const auto& field) { field->bind(Nodes::Network::FieldTarget::POSITION, swirl); },
89 * { .target_window = window });
90 * @endcode
91 */
92class MAYAFLUX_API Evolve {
93public:
94 /** @brief Render target and pipeline overrides, shared by every family. */
96
97 /**
98 * @brief Callable that binds fields on the GpuFieldOperator before it is wired.
99 *
100 * Bindings must exist when the geometry buffer is registered: the field pass
101 * is wired only for an operator that carries at least one binding at that
102 * point. The callable receives the operator with its full bind() surface,
103 * cluster scoping included.
104 */
105 using FieldBinder = std::function<void(const std::shared_ptr<Nodes::Network::GpuFieldOperator>&)>;
106
107 Evolve();
109
110 Evolve(const Evolve&) = delete;
111 Evolve& operator=(const Evolve&) = delete;
112 Evolve(Evolve&&) noexcept;
113 Evolve& operator=(Evolve&&) noexcept;
114
115 /**
116 * @brief Build a relaxation grid, register it and attach rendering.
117 * @param config Grid arrangement.
118 * @param render Render target. target_window is required.
119 * @return The registered grid, or nullptr if the target window is null.
120 *
121 * The grid is constructed from the config, registered in the graphics
122 * domain, then rendering is attached. Its state is left as allocated. Use
123 * the seeded overload, or RelaxationGridBuffer::seed_state, to give it an
124 * initial condition.
125 */
126 auto operator()(
127 const Buffers::RelaxationGridBuffer::GridConfig& config,
128 const RenderConfig& render)
129 -> std::shared_ptr<Buffers::RelaxationGridBuffer>
130 {
131 auto grid = build_grid(config, render, {}, 0);
132 if (grid) {
134 }
135 return grid;
136 }
137
138 /**
139 * @brief Build a relaxation grid seeded from a host callable.
140 * @param config Grid arrangement.
141 * @param seed Callable taking cell coordinates (x, y) and returning the
142 * cell's state. x runs over [0, width) and y over [0, height).
143 * @param render Render target. target_window is required.
144 * @return The registered grid, or nullptr if the target window is null or
145 * the seed's return type does not match the grid's cell stride.
146 *
147 * The seed's return type is the cell type, constrained by GridCellData and
148 * checked against the stride the config resolved. The state is written
149 * before registration, so the first generation steps from it.
150 */
151 template <typename Seed>
152 requires std::invocable<Seed&, uint32_t, uint32_t>
156 Seed&& seed,
157 const RenderConfig& render)
158 -> std::shared_ptr<Buffers::RelaxationGridBuffer>
159 {
160 using State = std::remove_cvref_t<std::invoke_result_t<Seed&, uint32_t, uint32_t>>;
161
162 std::vector<std::byte> cells(static_cast<size_t>(config.width) * config.height * sizeof(State));
163 for (uint32_t y = 0; y < config.height; ++y) {
164 for (uint32_t x = 0; x < config.width; ++x) {
165 const State value = seed(x, y);
166 std::memcpy(
167 cells.data() + (static_cast<size_t>(y) * config.width + x) * sizeof(State),
168 &value,
169 sizeof(State));
170 }
171 }
172
173 auto grid = build_grid(config, render, cells, sizeof(State));
174 if (grid) {
176 }
177 return grid;
178 }
179
180 /**
181 * @brief Set the incompressible flow stages going over a volume and attach rendering.
182 * @param volume Unregistered volume. Built with a SurfaceConfig if it is to be drawn.
183 * @param flow Flow parameters. Required refs left with an empty name
184 * (velocity, pressure, divergence, and scratch when viscosity is
185 * above zero) are declared on the volume under default names. A
186 * named ref the volume does not yet hold is declared, with the slot
187 * count its role needs. A ref issued by another volume is left for
188 * setup_flow to reject.
189 * @param render Render target. target_window is required.
190 * @return The volume, or nullptr if it is null or the target window is null.
191 *
192 * Registers the volume in the graphics domain, which establishes its
193 * chain, then builds the flow stages and attaches rendering. The stages
194 * setup_flow builds are reachable afterwards through the volume's
195 * processing chain by type.
196 */
198 const std::shared_ptr<Buffers::VolumeGridBuffer>& volume,
200 const RenderConfig& render)
201 -> std::shared_ptr<Buffers::VolumeGridBuffer>
202 {
203 if (!build_flow(volume, flow, render)) {
204 return nullptr;
205 }
206 MF_LIVE_EXPOSE_AUTO(volume);
207 return volume;
208 }
209
210 /**
211 * @brief Drive a network's vertices with fields and spatial stages, and attach rendering.
212 * @tparam Network A field-compatible NodeNetwork subclass, such as ParticleNetwork.
213 * @param network Unregistered network.
214 * @param spatial Spatial hash, claim, density and population configuration.
215 * Default constructed means field displacement only.
216 * @param bind Called with the GpuFieldOperator before the geometry buffer is
217 * registered. May be empty when the config alone is wanted.
218 * @param render Render target. target_window is required.
219 * @return The network, or nullptr if it is null, has no primary
220 * GraphicsOperator to read a layout from, or the target window is null.
221 *
222 * A ParticleNetwork with no primary operator and its default operator
223 * enabled receives a PhysicsOperator. The GpuFieldOperator is created with
224 * the primary operator's vertex layout and appended to the network's
225 * operator chain, or reused if the chain already holds one, in which case
226 * spatial is ignored. The network and its NetworkGeometryBuffer are then
227 * registered in the graphics domain and rendering is attached. Registering
228 * the buffer wires the field pass and every spatial stage the config
229 * selects.
230 */
231 template <typename Network>
232 requires std::is_base_of_v<Nodes::Network::NodeNetwork, Network>
234 const std::shared_ptr<Network>& network,
236 const FieldBinder& bind,
237 const RenderConfig& render)
238 -> std::shared_ptr<Network>
239 {
240 if (!build_field(std::static_pointer_cast<Nodes::Network::NodeNetwork>(network), spatial, bind, render)) {
241 return nullptr;
242 }
244 return network;
245 }
246
247private:
248 struct Impl;
249 std::unique_ptr<Impl> m_impl;
250
251 /**
252 * @brief Construct, optionally seed, register and render a relaxation grid.
253 * @param config Grid arrangement.
254 * @param render Render target.
255 * @param seed Packed cell bytes, or empty to leave state as allocated.
256 * @param seed_stride Bytes per cell in @p seed, checked against the grid's stride.
257 * @return The grid, or nullptr on failure.
258 */
259 std::shared_ptr<Buffers::RelaxationGridBuffer> build_grid(
261 const RenderConfig& render,
262 const std::vector<std::byte>& seed,
263 size_t seed_stride);
264
265 /**
266 * @brief Resolve refs, register, build the flow stages and render a volume.
267 * @param volume Volume to run the flow over.
268 * @param flow Flow parameters.
269 * @param render Render target.
270 * @return True when the volume was wired.
271 */
272 bool build_flow(
273 const std::shared_ptr<Buffers::VolumeGridBuffer>& volume,
275 const RenderConfig& render);
276
277 /**
278 * @brief Create the field operator, bind, register the network and its buffer, and render.
279 * @param network Network to drive.
280 * @param spatial Spatial field configuration.
281 * @param bind Field binder, possibly empty.
282 * @param render Render target.
283 * @return True when the network was wired.
284 */
285 bool build_field(
286 const std::shared_ptr<Nodes::Network::NodeNetwork>& network,
288 const FieldBinder& bind,
289 const RenderConfig& render);
290};
291
292} // namespace MayaFlux
Core::GlobalNetworkConfig network
Definition Config.cpp:43
#define MF_LIVE_EXPOSE_AUTO(ptr)
Auto-expose variant that deduces the key prefix from the shared_ptr element type.
glm::vec3 value
auto operator()(const std::shared_ptr< Buffers::VolumeGridBuffer > &volume, const Buffers::VolumeGridBuffer::FlowConfig &flow, const RenderConfig &render) -> std::shared_ptr< Buffers::VolumeGridBuffer >
Set the incompressible flow stages going over a volume and attach rendering.
Definition Evolve.hpp:197
Evolve(const Evolve &)=delete
Evolve(Evolve &&) noexcept
auto operator()(const std::shared_ptr< Network > &network, const Nodes::Network::SpatialFieldConfig &spatial, const FieldBinder &bind, const RenderConfig &render) -> std::shared_ptr< Network >
Drive a network's vertices with fields and spatial stages, and attach rendering.
Definition Evolve.hpp:233
Evolve & operator=(const Evolve &)=delete
auto operator()(const Buffers::RelaxationGridBuffer::GridConfig &config, Seed &&seed, const RenderConfig &render) -> std::shared_ptr< Buffers::RelaxationGridBuffer >
Build a relaxation grid seeded from a host callable.
Definition Evolve.hpp:154
std::function< void(const std::shared_ptr< Nodes::Network::GpuFieldOperator > &)> FieldBinder
Callable that binds fields on the GpuFieldOperator before it is wired.
Definition Evolve.hpp:105
std::unique_ptr< Impl > m_impl
Definition Evolve.hpp:249
Sets a system going from a config: state that persists, a rule that advances it every cycle,...
Definition Evolve.hpp:92
Cell types a relaxation grid can be seeded with from a host callable.
Definition Evolve.hpp:27
Main namespace for the Maya Flux audio engine.
Definition Runtime.cpp:12
Arrangement of a relaxation grid: extent, the update rule, the emit stage that draws it,...
Parameters for the incompressible flow stage arrangement.
Declarative configuration for the GPU-side spatial-relational work a GpuFieldOperator can drive beyon...
Unified rendering configuration for graphics buffers.