15 buffer->declare_state(
"mutation_spawn_cursor", 1,
sizeof(uint32_t),
false);
28 ShaderSpec build_population_init_spec()
30 ShaderSpec::Assemble assemble;
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";
51 body +=
" if (i == 0u) {\n";
52 body +=
" spawn_cursor[0] = live_count;\n";
55 assemble.
kernel(KernelSource { .body = std::move(body) });
57 return assemble.build();
66 FieldBinding { .
name =
"spawn_cursor", .binding = 2, .field =
"mutation_spawn_cursor" } },
67 build_population_init_spec())
69 .live_count = config.live_count,
70 .total_count = config.hash.particle_count,
71 .stride_words = config.hash.stride_words,
83 const std::shared_ptr<VKBuffer>& buffer)
103 ShaderSpec build_population_spawn_spec()
105 ShaderSpec::Assemble assemble;
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";
134 body +=
" uint neighbor_count = 0u;\n";
136 .on_hit =
"neighbor_count = neighbor_count + 1u;",
139 body +=
" if (float(neighbor_count) < spawn_density_threshold) { return; }\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";
147 body +=
" alive[slot] = 1u;\n";
148 body +=
" cluster_id[slot] = cluster_id[i];\n";
150 assemble.kernel(KernelSource { .body = std::move(body) });
152 return assemble.build();
159 std::shared_ptr<Nodes::Network::GpuFieldOperator> particle_op)
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" },
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())
171 .spawn_density_threshold = particle_op->get_field_config().spawn_density_threshold,
173 , m_particle_op(std::move(particle_op))
174 , m_built_revision(m_particle_op->revision())
185 const std::shared_ptr<VKBuffer>& buffer)
188 m_params.spawn_density_threshold = m_particle_op->get_field_config().spawn_density_threshold;
189 set_push_constant_data(m_params);
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 ¶ms)
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
uint64_t m_built_revision
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.
One shader binding and where it draws from.
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.