MayaFlux 0.5.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
VertexFieldProcessor.cpp
Go to the documentation of this file.
2
4
7
8namespace MayaFlux::Buffers {
9
10namespace {
11
12 /**
13 * @brief Extract a spec from the operator or fail construction.
14 *
15 * The shader is compiled by ShaderConfig's ShaderSpec constructor, which
16 * runs in the member initialiser list, so an operator that yields nothing
17 * cannot produce a usable processor. Failing here beats constructing one
18 * that logs on every cycle.
19 */
20 [[nodiscard]] Portal::Graphics::ShaderSpec require_spec(
21 const std::shared_ptr<Nodes::Network::GpuFieldOperator>& op)
22 {
23 if (!op) {
24 error<std::invalid_argument>(
27 std::source_location::current(),
28 "VertexFieldProcessor: null field operator");
29 }
30
31 auto spec = op->build_spec();
32 if (!spec.has_value()) {
33 error<std::invalid_argument>(
36 std::source_location::current(),
37 "VertexFieldProcessor: operator produced no spec. Bind at least "
38 "one field, and supply a layout carrying a position attribute, "
39 "before constructing the processor.");
40 }
41
42 return *spec;
43 }
44
45} // namespace
46
48 std::shared_ptr<Nodes::Network::GpuFieldOperator> field_operator)
49 : ComputeProcessor(require_spec(field_operator))
50 , m_operator(std::move(field_operator))
51 , m_built_revision(m_operator->revision())
52 , m_needs_cluster_id(m_operator->needs_cluster_id())
53{
55
56 add_binding("vertices",
57 ShaderBinding(0, m_operator->get_vertex_binding(),
59
61 add_binding("cluster_id",
62 ShaderBinding(0, m_operator->get_vertex_binding() + 1,
64 }
65
66 m_params.stride_words = m_operator->get_layout().stride_bytes / 4;
67
69}
70
71void VertexFieldProcessor::set_vertex_range(uint32_t first_vertex, uint32_t vertex_count)
72{
73 m_explicit_first = first_vertex;
74 m_explicit_count = vertex_count;
75 m_range_set = true;
76}
77
84
85void VertexFieldProcessor::on_attach(const std::shared_ptr<Buffer>& buffer)
86{
87 auto vk_buf = std::dynamic_pointer_cast<VKBuffer>(buffer);
88 if (!vk_buf) {
90 "VertexFieldProcessor requires a VKBuffer");
91 return;
92 }
93
94 bind_buffer("vertices", vk_buf);
95
96 m_network_buffer = std::dynamic_pointer_cast<NetworkGeometryBuffer>(buffer);
98
100
101 m_epoch = std::chrono::steady_clock::now();
102
104 "VertexFieldProcessor attached: {} bytes, stride {} words, {} bound fields",
105 vk_buf->get_size_bytes(),
107 m_operator->binding_count());
108}
109
111{
112 return std::chrono::duration<float>(
113 std::chrono::steady_clock::now() - m_epoch)
114 .count();
115}
116
118 const std::shared_ptr<VKBuffer>& buffer) const
119{
120 const uint32_t stride_bytes = m_params.stride_words * 4;
121 if (stride_bytes == 0)
122 return 0;
123
124 return static_cast<uint32_t>(buffer->get_size_bytes() / stride_bytes);
125}
126
128{
129 if (m_operator->revision() == m_built_revision)
130 return true;
131
132 auto spec = m_operator->build_spec();
133 if (!spec.has_value()) {
135 "VertexFieldProcessor: operator no longer yields a spec after rebind. "
136 "Dispatch suppressed; the previous pipeline is not reused because it "
137 "no longer describes the bindings.");
138 m_built_revision = m_operator->revision();
139 return false;
140 }
141
142 set_config(ShaderConfig(*spec));
143
144 add_binding("vertices",
145 ShaderBinding(0, m_operator->get_vertex_binding(),
147
148 m_needs_cluster_id = m_operator->needs_cluster_id();
149 if (m_needs_cluster_id) {
150 add_binding("cluster_id",
151 ShaderBinding(0, m_operator->get_vertex_binding() + 1,
153 }
154
155 m_params.stride_words = m_operator->get_layout().stride_bytes / 4;
156 m_built_revision = m_operator->revision();
157
159 "VertexFieldProcessor: rebuilt for revision {} ({} bound fields)",
160 m_built_revision, m_operator->binding_count());
161
162 return ensure_cluster_binding();
163}
164
167 const std::shared_ptr<VKBuffer>& buffer)
168{
169 if (!buffer)
170 return false;
171
172 if (!sync_revision())
173 return false;
174
175 const uint32_t capacity = buffer_capacity(buffer);
176 if (capacity == 0)
177 return false;
178
179 if (m_range_set) {
180 if (m_explicit_first >= capacity) {
182 "VertexFieldProcessor: range starts at {} but the buffer holds {} "
183 "records. Dispatch suppressed.",
184 m_explicit_first, capacity);
185 return false;
186 }
187
190 } else {
192 m_params.vertex_count = capacity;
193 }
194
195 if (m_params.vertex_count == 0)
196 return false;
197
199
201 "VertexFieldProcessor dispatch: [{}, {}) of {} records, stride {} words, t={:.3f}",
204 capacity,
206 m_params.time);
207
209 return true;
210}
211
213 const std::shared_ptr<VKBuffer>& /*buffer*/)
214{
215 const auto& dispatch = get_dispatch_config();
216 const uint32_t local_x = std::max(1U, dispatch.workgroup_x);
217 const uint32_t groups = (m_params.vertex_count + local_x - 1) / local_x;
218
219 return { std::max(1U, groups), 1U, 1U };
220}
221
223{
224 if (!m_needs_cluster_id) {
225 return true;
226 }
227
228 if (!m_network_buffer) {
230 "VertexFieldProcessor: operator has a cluster-scoped binding but the "
231 "attached buffer is not a NetworkGeometryBuffer, so hash_cluster_id "
232 "has nowhere to live. Dispatch suppressed.");
233 return false;
234 }
235
236 if (!m_network_buffer->ensure_cluster_ids()) {
238 "VertexFieldProcessor: operator has a cluster-scoped binding but "
239 "hash_cluster_id could not be derived (no primary GraphicsOperator, "
240 "or it reports zero points). Dispatch suppressed.");
241 return false;
242 }
243
244 return true;
245}
246
248{
250 return;
251 }
252
253 auto& foundry = Portal::Graphics::get_shader_foundry();
254 const auto handle = m_network_buffer->read_state_handle("hash_cluster_id");
255 const auto bytes = m_network_buffer->get_state_bytes("hash_cluster_id");
256
257 foundry.update_descriptor_buffer(
258 m_descriptor_set_ids[0], m_operator->get_vertex_binding() + 1,
259 vk::DescriptorType::eStorageBuffer, handle, 0, bytes);
260}
261
262} // namespace MayaFlux::Buffers
#define MF_INFO(comp, ctx,...)
#define MF_ERROR(comp, ctx,...)
#define MF_RT_ERROR(comp, ctx,...)
#define MF_RT_TRACE(comp, ctx,...)
virtual void on_attach(const std::shared_ptr< Buffer > &)
Called when this processor is attached to a buffer.
const ShaderDispatchConfig & get_dispatch_config() const
Get current dispatch configuration.
Specialized ShaderProcessor for Compute Pipelines.
void set_push_constant_data(const T &data)
Update push constant data (type-safe)
void add_binding(const std::string &descriptor_name, const ShaderBinding &binding)
Add descriptor binding configuration.
void set_config(const ShaderConfig &config)
Update entire configuration.
void bind_buffer(const std::string &descriptor_name, const std::shared_ptr< VKBuffer > &buffer)
Bind a VKBuffer to a named shader descriptor.
std::vector< Portal::Graphics::DescriptorSetID > m_descriptor_set_ids
bool ensure_cluster_binding()
Ensure the backing hash_cluster_id state field exists, when m_needs_cluster_id is true.
bool on_before_execute(Portal::Graphics::CommandBufferID cmd_id, const std::shared_ptr< VKBuffer > &buffer) override
Rebuild on a stale revision, resolve the range, write push constants.
std::chrono::steady_clock::time_point m_epoch
std::shared_ptr< NetworkGeometryBuffer > m_network_buffer
std::array< uint32_t, 3 > calculate_dispatch_size(const std::shared_ptr< VKBuffer > &buffer) override
One thread per record in the resolved range.
void clear_vertex_range()
Drop an explicit range and cover the whole buffer again.
uint32_t buffer_capacity(const std::shared_ptr< VKBuffer > &buffer) const
Records the buffer can hold at the operator's stride.
std::shared_ptr< Nodes::Network::GpuFieldOperator > m_operator
void set_vertex_range(uint32_t first_vertex, uint32_t vertex_count)
Restrict the dispatch to a slice of the buffer.
bool sync_revision()
Recompile and rebuild the pipeline when the operator has changed.
void on_descriptors_created() override
Write the cluster_id descriptor when the operator needs one.
void on_attach(const std::shared_ptr< Buffer > &buffer) override
Called when this processor is attached to a buffer.
VertexFieldProcessor(std::shared_ptr< Nodes::Network::GpuFieldOperator > field_operator)
float elapsed() const
Seconds since construction, the value bound to the time parameter.
@ GRAPHICS_BACKEND
Standard graphics processing backend configuration.
@ BufferProcessing
Buffer processing (Buffers::BufferManager, processing chains)
@ Buffers
Buffers, Managers, processors and processing chains.
@ STORAGE
Large arrays or buffers the shader may write (SSBO)
MAYAFLUX_API ShaderFoundry & get_shader_foundry()
Get the global shader compiler instance.
Describes how a VKBuffer binds to a shader descriptor.