MayaFlux 0.5.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
PopulationProcessor.cpp
Go to the documentation of this file.
2
5
7
9
10namespace MayaFlux::Buffers {
11
12void PopulationConfig::declare_fields(const std::shared_ptr<NetworkGeometryBuffer>& buffer) const
13{
14 buffer->declare_state("mutation_alive", hash.particle_count, sizeof(uint32_t), false);
15 buffer->declare_state("mutation_spawn_cursor", 1, sizeof(uint32_t), false);
16}
17
18//=============================================================================
19// PopulationInitProcessor
20//=============================================================================
21
22namespace {
23
27
28 ShaderSpec build_population_init_spec()
29 {
30 ShaderSpec::Assemble assemble;
31 assemble
32 .ssbo("vertices", BindingDirection::InOut, Kakshya::GpuDataFormat::FLOAT32)
33 .ssbo("alive", BindingDirection::Output, Kakshya::GpuDataFormat::UINT32)
34 .ssbo("spawn_cursor", BindingDirection::Output, Kakshya::GpuDataFormat::UINT32)
35 .pc("live_count", Kakshya::GpuDataFormat::UINT32)
36 .pc("total_count", Kakshya::GpuDataFormat::UINT32)
37 .pc("stride_words", Kakshya::GpuDataFormat::UINT32)
38 .workgroup(256);
39
40 std::string body;
41 body += " if (i >= total_count) { return; }\n";
42 body += " if (i < live_count) {\n";
43 body += " alive[i] = 1u;\n";
44 body += " } else {\n";
45 body += " alive[i] = 0u;\n";
46 body += " uint b = i * stride_words;\n";
47 body += " for (uint w = 0u; w < stride_words; w = w + 1u) {\n";
48 body += " vertices[b + w] = 0.0f;\n";
49 body += " }\n";
50 body += " }\n";
51 body += " if (i == 0u) {\n";
52 body += " spawn_cursor[0] = live_count;\n";
53 body += " }\n";
54
55 assemble.kernel(KernelSource { .body = std::move(body) });
56
57 return assemble.build();
58 }
59
60} // namespace
61
64 { FieldBinding { .name = "vertices", .binding = 0, .field = {} },
65 FieldBinding { .name = "alive", .binding = 1, .field = "mutation_alive" },
66 FieldBinding { .name = "spawn_cursor", .binding = 2, .field = "mutation_spawn_cursor" } },
67 build_population_init_spec())
68 , m_params {
69 .live_count = config.live_count,
70 .total_count = config.hash.particle_count,
71 .stride_words = config.hash.stride_words,
72 }
73{
74}
75
80
83 const std::shared_ptr<VKBuffer>& buffer)
84{
86 return false;
87 }
88
89 if (m_done) {
90 return false;
91 }
92
93 m_done = true;
94 return true;
95}
96
97//=============================================================================
98// PopulationSpawnProcessor
99//=============================================================================
100
101namespace {
102
103 ShaderSpec build_population_spawn_spec()
104 {
105 ShaderSpec::Assemble assemble;
106 assemble
107 .ssbo("vertices", BindingDirection::InOut, Kakshya::GpuDataFormat::FLOAT32)
108 .ssbo("cell_start", BindingDirection::Input, Kakshya::GpuDataFormat::UINT32)
109 .ssbo("cell_count", BindingDirection::Input, Kakshya::GpuDataFormat::UINT32)
110 .ssbo("particle_index", BindingDirection::Input, Kakshya::GpuDataFormat::UINT32)
111 .ssbo("alive", BindingDirection::InOut, Kakshya::GpuDataFormat::UINT32)
112 .ssbo("cluster_id", BindingDirection::InOut, Kakshya::GpuDataFormat::UINT32)
113 .ssbo("spawn_cursor", BindingDirection::InOut, Kakshya::GpuDataFormat::UINT32)
114 .pc("total_count", Kakshya::GpuDataFormat::UINT32)
115 .pc("stride_words", Kakshya::GpuDataFormat::UINT32)
116 .pc("position_offset", Kakshya::GpuDataFormat::UINT32)
117 .pc("grid_min_x", Kakshya::GpuDataFormat::FLOAT32)
118 .pc("grid_min_y", Kakshya::GpuDataFormat::FLOAT32)
119 .pc("grid_min_z", Kakshya::GpuDataFormat::FLOAT32)
120 .pc("cell_size", Kakshya::GpuDataFormat::FLOAT32)
124 .pc("spawn_density_threshold", Kakshya::GpuDataFormat::FLOAT32)
125 .workgroup(256);
126
127 std::string body;
128 body += " if (i >= total_count) { return; }\n";
129 body += " if (alive[i] == 0u) { return; }\n";
130 body += " uint b = i * stride_words;\n";
131 body += " vec3 p = vec3(vertices[b + position_offset], "
132 "vertices[b + position_offset + 1u], vertices[b + position_offset + 2u]);\n";
133 body += "\n";
134 body += " uint neighbor_count = 0u;\n";
136 .on_hit = "neighbor_count = neighbor_count + 1u;",
137 });
138 body += "\n";
139 body += " if (float(neighbor_count) < spawn_density_threshold) { return; }\n";
140 body += "\n";
141 body += " uint slot = atomicAdd(spawn_cursor[0], 1u);\n";
142 body += " if (slot >= total_count) { return; }\n";
143 body += " uint sb = slot * stride_words;\n";
144 body += " for (uint w = 0u; w < stride_words; w = w + 1u) {\n";
145 body += " vertices[sb + w] = vertices[b + w];\n";
146 body += " }\n";
147 body += " alive[slot] = 1u;\n";
148 body += " cluster_id[slot] = cluster_id[i];\n";
149
150 assemble.kernel(KernelSource { .body = std::move(body) });
151
152 return assemble.build();
153 }
154
155} // namespace
156
158 const PopulationConfig& config,
159 std::shared_ptr<Nodes::Network::GpuFieldOperator> particle_op)
161 { FieldBinding { .name = "vertices", .binding = 0, .field = {} },
162 FieldBinding { .name = "cell_start", .binding = 1, .field = "hash_cell_start" },
163 FieldBinding { .name = "cell_count", .binding = 2, .field = "hash_cell_count" },
164 FieldBinding { .name = "particle_index", .binding = 3, .field = "hash_particle_index" },
165 FieldBinding { .name = "alive", .binding = 4, .field = "mutation_alive" },
166 FieldBinding { .name = "cluster_id", .binding = 5, .field = "hash_cluster_id" },
167 FieldBinding { .name = "spawn_cursor", .binding = 6, .field = "mutation_spawn_cursor" } },
168 build_population_spawn_spec())
169 , m_params {
170 .grid = make_grid_push_constants(config.hash),
171 .spawn_density_threshold = particle_op->get_field_config().spawn_density_threshold,
172 }
173 , m_particle_op(std::move(particle_op))
174 , m_built_revision(m_particle_op->revision())
175{
176}
177
182
185 const std::shared_ptr<VKBuffer>& buffer)
186{
187 return guard_and_resync(cmd_id, buffer, m_particle_op->revision(), m_built_revision, [this] {
188 m_params.spawn_density_threshold = m_particle_op->get_field_config().spawn_density_threshold;
189 set_push_constant_data(m_params);
190 });
191}
192
193} // namespace MayaFlux::Buffers
bool on_before_execute(Portal::Graphics::CommandBufferID cmd_id, const std::shared_ptr< VKBuffer > &buffer) override
Reject buffers that are not the validated NetworkGeometryBuffer.
void dispatch_one_thread_per(uint32_t element_count, const T &params)
Configure manual dispatch for one thread per element, and stage the given push constant data.
bool guard_and_resync(Portal::Graphics::CommandBufferID cmd_id, const std::shared_ptr< VKBuffer > &buffer, uint64_t current_revision, uint64_t &built_revision, F &&reload)
Base on_before_execute guard, then a revision-gated tuning reload.
ComputeProcessor operating on named state fields of a NetworkGeometryBuffer, plus optionally the buff...
bool on_before_execute(Portal::Graphics::CommandBufferID cmd_id, const std::shared_ptr< VKBuffer > &buffer) override
True only on the first call; every call after returns false.
void on_buffer_ready() override
Hook for subclass setup, called at the end of on_attach.
PopulationInitProcessor(const PopulationConfig &config)
bool on_before_execute(Portal::Graphics::CommandBufferID cmd_id, const std::shared_ptr< VKBuffer > &buffer) override
Reject buffers that are not the validated NetworkGeometryBuffer.
void on_buffer_ready() override
Hook for subclass setup, called at the end of on_attach.
std::shared_ptr< Nodes::Network::GpuFieldOperator > m_particle_op
PopulationSpawnProcessor(const PopulationConfig &config, std::shared_ptr< Nodes::Network::GpuFieldOperator > particle_op)
void append_neighbour_walk(std::string &body, const NeighbourWalk &walk)
Append the spatial-hash neighbour-gather loop nest to a kernel body.
GridPushConstants make_grid_push_constants(const SpatialHashConfig &c)
Populate a GridPushConstants from grid config.
BindingDirection
Data flow direction for a shader binding slot.
std::string name
Shader binding name, as declared in ShaderConfig.
void declare_fields(const std::shared_ptr< NetworkGeometryBuffer > &buffer) const
Declare the two state fields population dynamics needs.
Parameters for GPU-local population dynamics: destruction and spawn-as-copy over a seed's fixed,...
Parsed representation of a user-supplied kernel lambda.
std::optional< KernelSource > kernel
When set, KernelOp is ignored.
Complete declarative description of a generated compute shader.