MayaFlux 0.5.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
VolumeFieldProcessor.hpp
Go to the documentation of this file.
1#pragma once
2
4
5namespace MayaFlux::Buffers {
6
7class VolumeGridBuffer;
8
9/**
10 * @class VolumeFieldProcessor
11 * @brief ComputeProcessor operating on named fields of a VolumeGridBuffer.
12 *
13 * Holds everything the lattice stages share: validating field names
14 * against the attached volume, sizing the dispatch from the lattice,
15 * writing the common leading push constant words, rewriting field
16 * descriptors every cycle, and swapping slots after the dispatch.
17 * Subclasses supply only a binding table and whatever parameter words
18 * follow the shared prefix.
19 *
20 * The parameter block opens with a fixed prefix: three lattice extents, a
21 * subclass word, three cell sizes, a second subclass word. The layout is
22 * private to the base. Subclasses reach it through write_lattice_word3,
23 * write_lattice_word7, and write_param_tail, which rejects any offset
24 * overlapping the prefix.
25 *
26 * The binding table drives both descriptor writes and validation. A field
27 * appearing with both READ and WRITE access is required to be
28 * double-buffered, since the stage would otherwise read a neighbourhood
29 * of the storage it is writing. A field appearing only as WRITE carries
30 * no such requirement.
31 *
32 * Descriptors are written directly through
33 * ShaderFoundry::update_descriptor_buffer rather than bind_buffer, since
34 * VolumeGridBuffer's field storage consists of raw handle pairs with no
35 * VKBuffer wrapper. The write happens in processing_function, before
36 * execute_shader binds the set into the command buffer, because a
37 * descriptor set already bound into an open command buffer cannot be
38 * rewritten.
39 *
40 * Slot swaps happen in processing_function after the parent call rather
41 * than in on_after_execute, which fires more than once per cycle. A swap
42 * is not idempotent.
43 */
44class MAYAFLUX_API VolumeFieldProcessor : public ComputeProcessor {
45public:
46 /**
47 * @enum FieldAccess
48 * @brief Which slot of a field a binding resolves to.
49 */
50 enum class FieldAccess {
51 READ, ///< Resolves to VolumeGridBuffer::read_handle.
52 WRITE ///< Resolves to VolumeGridBuffer::write_handle.
53 };
54
55 /**
56 * @struct FieldBinding
57 * @brief One shader binding and the field slot it draws from.
58 */
59 struct FieldBinding {
60 std::string name; ///< Shader binding name, as declared in ShaderConfig.
61 uint32_t binding; ///< Binding index within set 0.
62 std::string field; ///< Field name on the attached volume.
63 FieldAccess access; ///< Which slot of that field.
64 };
65
66 /** @brief The attached volume, or null if attachment failed validation. */
67 [[nodiscard]] const std::shared_ptr<VolumeGridBuffer>& get_volume() const { return m_volume; }
68
69protected:
70 /**
71 * @brief Construct from a shader path.
72 * @param bindings Binding table. Entries are registered into
73 * m_config.bindings in order.
74 * @param shader_path Path to the compute shader.
75 */
76 VolumeFieldProcessor(std::vector<FieldBinding> bindings, const std::string& shader_path);
77
78 /**
79 * @brief Construct from a generated ShaderSpec.
80 * @param bindings Binding table.
81 * @param spec ShaderSpec implementing the stage.
82 */
83 VolumeFieldProcessor(std::vector<FieldBinding> bindings, const Portal::Graphics::ShaderSpec& spec);
84
85 /**
86 * @brief Cache and validate the volume, size the dispatch, write the
87 * shared parameter prefix, then call on_volume_ready.
88 * @param buffer The attached buffer, expected to be a VolumeGridBuffer.
89 *
90 * On any validation failure the cached volume is reset, which makes
91 * on_before_execute reject every subsequent cycle.
92 */
93 void on_attach(const std::shared_ptr<Buffer>& buffer) override;
94
95 /**
96 * @brief Write every binding in the table for the current slot assignment.
97 */
98 void on_descriptors_created() override;
99
100 /**
101 * @brief Reject buffers that are not the validated volume.
102 * @param cmd_id Command buffer this cycle's dispatch will be recorded into.
103 * @param buffer The attached buffer, received as VKBuffer.
104 * @return True if a volume survived validation and matches the argument.
105 */
106 bool on_before_execute(Portal::Graphics::CommandBufferID cmd_id, const std::shared_ptr<VKBuffer>& buffer) override;
107
108 /**
109 * @brief Rewrite field descriptors, run the shader, then swap.
110 * @param buffer Buffer under processing.
111 *
112 * Read slots change whenever an upstream stage swaps, so the bindings
113 * are rewritten every cycle rather than once.
114 */
115 void processing_function(const std::shared_ptr<Buffer>& buffer) override;
116
117 /**
118 * @brief Hook for subclass parameters, called at the end of on_attach.
119 *
120 * The volume is non-null and the shared prefix is already written when
121 * this runs. Subclasses writing parameters past the prefix override
122 * this rather than on_attach.
123 */
124 virtual void on_volume_ready() { }
125
126 /**
127 * @brief Whether this cycle's dispatch should be followed by a swap.
128 * @return True by default.
129 *
130 * PressureProcessor overrides this: after an even number of Jacobi
131 * passes the result is already in the read slot.
132 */
133 [[nodiscard]] virtual bool wants_swap() const { return true; }
134
135 /**
136 * @brief Register a field to swap after each dispatch.
137 * @param field Field name. Order of registration is order of swap.
138 */
139 void add_swap_field(std::string field);
140
141 /**
142 * @brief Write the lattice dimensions and cell size into the staged
143 * push constant block, preserving word three.
144 */
145 void write_lattice_params();
146
147 /**
148 * @brief Write word three of the shared prefix.
149 * @param value Subclass-defined word, typically a mode or parity flag.
150 */
151 void write_lattice_word3(uint32_t value);
152
153 /**
154 * @brief Write word seven of the parameter prefix, at byte offset 28.
155 * @param value Subclass-defined float, typically a rate or step.
156 */
157 void write_lattice_word7(float value);
158
159 /**
160 * @brief Write subclass parameters past the shared prefix.
161 * @param offset Byte offset from the start of the push constant block.
162 * Values below sizeof(LatticeParams) are rejected.
163 * @param data Source bytes.
164 * @param size Byte count.
165 */
166 void write_param_tail(size_t offset, const void* data, size_t size);
167
168 /**
169 * @brief Raise the push constant block to at least this size.
170 * @param size Byte count the subclass's full parameter struct occupies.
171 *
172 * Called from the subclass constructor. The base raises the block to
173 * sizeof(LatticeParams) independently.
174 */
175 void reserve_param_size(size_t size);
176
177private:
178 /**
179 * @brief Register the binding table into m_config.bindings.
180 */
181 void register_bindings();
182
183 /**
184 * @brief Check every named field exists and that read-write fields
185 * carry two slots.
186 * @return True if the cached volume satisfies the binding table.
187 */
188 bool validate_fields();
189
190 /**
191 * @brief Size the manual dispatch to cover the lattice.
192 */
193 void size_dispatch();
194
195 /**
196 * @brief Issue descriptor writes for every entry in the binding table.
197 */
198 void write_field_descriptors();
199
200 std::vector<FieldBinding> m_bindings;
201 std::vector<std::string> m_swap_fields;
202
203 std::shared_ptr<VolumeGridBuffer> m_volume; ///< The attached volume, null until on_attach validates it.
204};
205
206} // namespace MayaFlux::Buffers
float value
float offset
Specialized ShaderProcessor for Compute Pipelines.
std::shared_ptr< VolumeGridBuffer > m_volume
The attached volume, null until on_attach validates it.
virtual void on_volume_ready()
Hook for subclass parameters, called at the end of on_attach.
FieldAccess
Which slot of a field a binding resolves to.
const std::shared_ptr< VolumeGridBuffer > & get_volume() const
The attached volume, or null if attachment failed validation.
virtual bool wants_swap() const
Whether this cycle's dispatch should be followed by a swap.
ComputeProcessor operating on named fields of a VolumeGridBuffer.
std::string field
Field name on the attached volume.
std::string name
Shader binding name, as declared in ShaderConfig.
One shader binding and the field slot it draws from.
Complete declarative description of a generated compute shader.