MayaFlux 0.5.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches

◆ wire_field_operators()

void MayaFlux::Buffers::NetworkGeometryBuffer::wire_field_operators ( )
private

Find the first GpuFieldOperator in the network's operator chain and wire its compute stages: a VertexFieldProcessor when it carries bindings, plus whichever spatial-hash / claim / density / population stages its SpatialFieldConfig calls for.

No-op unless the network is one of the field-compatible types (ParticleNetwork, PointCloudNetwork); mesh and instance buffers never reach the scan. Called at the end of setup_processors, so a scene that puts a GpuFieldOperator in the chain needs no hand-wired VertexFieldProcessor and must not add one itself. Implemented in NetworkGeometryFieldWiring.cpp to keep the compute-processor headers out of the base translation unit.

Definition at line 43 of file NetworkGeometryFieldWiring.cpp.

44{
45 if (!is_field_compatible_network(m_network.get())) {
46 return;
47 }
48
49 auto op_chain = m_network ? m_network->get_operator_chain() : nullptr;
50 if (!op_chain) {
51 return;
52 }
53
54 std::shared_ptr<Nodes::Network::GpuFieldOperator> field_op;
55 for (const auto& op : op_chain->operators()) {
56 field_op = std::dynamic_pointer_cast<Nodes::Network::GpuFieldOperator>(op);
57 if (field_op) {
58 break;
59 }
60 }
61 if (!field_op) {
62 return;
63 }
64
65 auto self = std::dynamic_pointer_cast<NetworkGeometryBuffer>(shared_from_this());
67 if (!self || !chain) {
69 "NetworkGeometryBuffer: no processing chain to wire field stages onto");
70 return;
71 }
72
73 if (field_op->binding_count() > 0) {
74 chain->add_postprocessor(std::make_shared<VertexFieldProcessor>(field_op), self);
75 }
76
77 const auto& pconfig = field_op->get_field_config();
78 if (!pconfig.absorb_radius.has_value() && !pconfig.density_color) {
79 return;
80 }
81
82 if (pconfig.transfer_on_claim && !pconfig.absorb_radius.has_value()) {
84 "NetworkGeometryBuffer: transfer_on_claim requires absorb_radius (there is no "
85 "claim to reinterpret without it); transfer disabled");
86 }
87
88 auto* physics = dynamic_cast<Nodes::Network::PhysicsOperator*>(get_network()->get_operator());
89
90 float cell_size = 0.0F;
91 if (pconfig.cell_size.has_value()) {
92 cell_size = *pconfig.cell_size;
93 } else if (physics) {
94 cell_size = physics->get_interaction_radius();
95 } else {
97 "NetworkGeometryBuffer: SpatialFieldConfig has no cell_size and the network's "
98 "primary operator is not a PhysicsOperator, so one cannot be derived");
99 return;
100 }
101
102 auto hash_config = SpatialHashConfig::from_network(self, cell_size);
103 if (!hash_config.has_value()) {
105 "NetworkGeometryBuffer: SpatialHashConfig::from_network failed");
106 return;
107 }
108
109 const uint32_t live_count = hash_config->particle_count;
110 bool reserve_enabled = pconfig.reserve_fraction > 0.0F;
111
112 if (reserve_enabled && !pconfig.absorb_radius.has_value()) {
114 "NetworkGeometryBuffer: reserve_fraction requires absorb_radius (destroy-on-"
115 "absorption has nothing to destroy without claims); reserve capacity disabled");
116 reserve_enabled = false;
117 }
118
119 uint32_t total_count = live_count;
120 if (reserve_enabled) {
121 const auto reserve_count = static_cast<uint32_t>(
122 std::ceil(static_cast<float>(live_count) * pconfig.reserve_fraction));
123 total_count = live_count + reserve_count;
124
125 const size_t needed_bytes = static_cast<size_t>(total_count) * hash_config->stride_words * 4;
126 self->resize(needed_bytes, true);
127
128 hash_config->particle_count = total_count;
129 }
130
131 if (total_count != live_count) {
132 hash_config->declare_fields(self);
133
134 std::vector<uint32_t> cluster_ids(total_count, 0U);
135 if (auto* graphics_op
136 = dynamic_cast<Nodes::Network::GraphicsOperator*>(get_network()->get_operator())) {
137 auto live_ids = graphics_op->build_cluster_ids();
138 std::copy_n(live_ids.begin(),
139 std::min<size_t>(live_ids.size(), live_count), cluster_ids.begin());
140 }
141 std::shared_ptr<VKBuffer> cluster_staging;
142 upload_back_buffer(self->write_state_slot("hash_cluster_id"), cluster_ids.data(),
143 cluster_ids.size() * sizeof(uint32_t), cluster_staging);
144 } else {
145 self->ensure_cluster_ids();
146 hash_config->declare_fields(self);
147 }
148
149 std::optional<PopulationConfig> pop_config;
150 if (reserve_enabled) {
151 pop_config = PopulationConfig { .hash = *hash_config, .live_count = live_count };
152 pop_config->declare_fields(self);
153 chain->add_processor(std::make_shared<PopulationInitProcessor>(*pop_config), self);
154 }
155
156 chain->add_processor(std::make_shared<HashClearProcessor>(*hash_config), self);
157 chain->add_processor(std::make_shared<HashCountProcessor>(*hash_config, reserve_enabled), self);
158 chain->add_processor(std::make_shared<HashScanProcessor>(*hash_config), self);
159 chain->add_processor(std::make_shared<HashScatterProcessor>(*hash_config, reserve_enabled), self);
160
161 if (pconfig.density_color) {
162 chain->add_processor(
163 std::make_shared<HashDensityColorProcessor>(*hash_config, field_op), self);
164 }
165
166 if (!pconfig.absorb_radius.has_value()) {
167 return;
168 }
169
170 MutationConfig claim_config { .hash = *hash_config, .absorb_radius = *pconfig.absorb_radius };
171 claim_config.declare_fields(self);
172
173 const bool transfer_on_claim = pconfig.transfer_on_claim;
174
175 chain->add_processor(std::make_shared<ClaimInitProcessor>(claim_config), self);
176 chain->add_processor(
177 std::make_shared<ClaimProcessor>(claim_config, field_op, reserve_enabled), self);
178 chain->add_processor(std::make_shared<ClaimFlattenProcessor>(claim_config), self);
179 chain->add_processor(
180 std::make_shared<ClaimAccumulateProcessor>(
181 claim_config, physics, reserve_enabled ? live_count : 0U, field_op, transfer_on_claim),
182 self);
183
184 if (pconfig.cosmetic_swallow) {
185 chain->add_processor(
186 std::make_shared<ClaimSwallowProcessor>(claim_config, field_op, transfer_on_claim), self);
187 }
188
189 if (transfer_on_claim) {
190 chain->add_processor(std::make_shared<ClaimTransferProcessor>(claim_config), self);
191 }
192
193 if (pop_config.has_value()) {
194 chain->add_processor(
195 std::make_shared<PopulationSpawnProcessor>(*pop_config, field_op), self);
196 }
197}
#define MF_ERROR(comp, ctx,...)
std::shared_ptr< Nodes::Network::NodeNetwork > m_network
std::shared_ptr< Nodes::Network::NodeNetwork > get_network() const
Get the network driving this buffer.
std::shared_ptr< Buffers::BufferProcessingChain > get_processing_chain() override
Access the buffer's processing chain.
Definition VKBuffer.cpp:263
void upload_back_buffer(const VKBufferResources::GenerationSlot &slot, const void *data, size_t size, std::shared_ptr< VKBuffer > &staging)
Upload host memory into a raw back_buffers slot.
@ Init
Engine/subsystem initialization.
@ Buffers
Buffers, Managers, processors and processing chains.
Tendency< A, C > chain(const Tendency< A, B > &first, const Tendency< B, C > &second)
Sequential composition: evaluate first, feed result into second.
Definition Tendency.hpp:82
static std::optional< SpatialHashConfig > from_network(const std::shared_ptr< NetworkGeometryBuffer > &buffer, float cell_size)
Build hash grid parameters from a NetworkGeometryBuffer's network and its primary operator.

References MayaFlux::Journal::Buffers, MayaFlux::Buffers::SpatialHashConfig::declare_fields(), MayaFlux::Buffers::SpatialHashConfig::from_network(), MayaFlux::Nodes::Network::PhysicsOperator::get_interaction_radius(), get_network(), MayaFlux::Buffers::VKBuffer::get_processing_chain(), MayaFlux::Buffers::MutationConfig::hash, MayaFlux::Buffers::PopulationConfig::hash, MayaFlux::Journal::Init, m_network, MF_ERROR, and MayaFlux::Buffers::upload_back_buffer().

Referenced by setup_processors().

+ Here is the call graph for this function:
+ Here is the caller graph for this function: