MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
RelaxationStepProcessor.hpp
Go to the documentation of this file.
1#pragma once
2
4
5namespace MayaFlux::Buffers {
6
7class RelaxationGridBuffer;
8
9/**
10 * @class RelaxationStepProcessor
11 * @brief ComputeProcessor driving one generation step of a
12 * RelaxationGridBuffer per dispatch.
13 *
14 * Bindings: "state_in" at (0,0) and "state_out" at (0,1), the grid's raw
15 * double-buffered state handles, written through
16 * ShaderFoundry::update_descriptor_buffer in processing_function before the
17 * parent binds the set.
18 *
19 * Per cycle the step predicate is evaluated once. If it declines, nothing
20 * changes: no descriptor write, no dispatch, no generation swap, no
21 * snapshot. Otherwise the parent records and submits the dispatch, and when
22 * a dispatch was recorded the front generation is swapped once and a
23 * pending snapshot request is served from the newly-front buffer.
24 *
25 * Layout creation follows the normal ShaderProcessor path through
26 * m_config.bindings. Only the per-generation descriptor write bypasses
27 * ShaderProcessor::bind_buffer and m_bound_buffers, since the state buffers
28 * are raw handle pairs with no VKBuffer wrapper.
29 *
30 * ComputeProcessor fires on_before_execute and on_after_execute twice per
31 * cycle, once inside execute_shader at record time and once from
32 * processing_function, so neither hook owns generation state here.
33 */
34class MAYAFLUX_API RelaxationStepProcessor : public ComputeProcessor {
35public:
36 /** @brief Callable deciding whether the current cycle advances a generation. */
37 using StepPredicate = std::function<bool()>;
38
39 /**
40 * @struct GridExtent
41 * @brief Leading push constant fields every relaxation rule shader
42 * receives: grid width then height, at offset 0.
43 *
44 * Matches the convention AsmGenerator::emit_stencil_body reads for
45 * generated Stencil kernels, and which hand-written rule shaders are
46 * expected to declare as their first two push constant fields. Rule
47 * specific parameters follow at offset 8.
48 */
49 struct GridExtent {
50 uint32_t width;
51 uint32_t height;
52 };
53
54 /**
55 * @brief Construct a step processor for the given rule shader file.
56 * @param shader_path Path to the compute shader implementing the rule.
57 * @param workgroup_x Local workgroup size along X, forwarded to ComputeProcessor.
58 */
59 explicit RelaxationStepProcessor(const std::string& shader_path, uint32_t workgroup_x = 16);
60
61 /**
62 * @brief Construct a step processor from a generated ShaderSpec.
63 * @param spec ShaderSpec implementing the rule, produced by a factory
64 * such as a Stencil-based generated rule (e.g. Jacobi diffusion)
65 * or by ShaderSpec::Assemble with MF_KERNEL for branchy rules.
66 *
67 * Forwards to ComputeProcessor(spec) directly; workgroup sizing comes
68 * from spec.workgroup_size rather than a separate parameter.
69 */
71
72 /**
73 * @brief Set the predicate deciding whether this cycle advances a
74 * generation.
75 * @param predicate Callable returning true to step, false to skip.
76 * Pass nullptr to always step (default).
77 *
78 * Evaluated once per cycle at the top of processing_function, before
79 * any state changes. Returning false skips the cycle entirely.
80 */
81 void set_step_predicate(StepPredicate predicate) { m_step_predicate = std::move(predicate); }
82
83protected:
84 /**
85 * @brief Accept only a RelaxationGridBuffer.
86 * @param cmd_id Command buffer, unused.
87 * @param buffer Buffer being processed.
88 * @return False for any other buffer, which skips execution.
89 *
90 * Fires twice per cycle. Holds no state.
91 */
92 bool on_before_execute(Portal::Graphics::CommandBufferID cmd_id, const std::shared_ptr<VKBuffer>& buffer) override;
93
94 /**
95 * @brief Record that this cycle reached a dispatch.
96 * @param cmd_id Command buffer, unused.
97 * @param buffer Buffer being processed, unused.
98 * @param index Iteration index, unused.
99 * @return Always true.
100 *
101 * ComputeProcessor calls this only when a dispatch is about to be
102 * recorded. processing_function reads the flag to decide whether the
103 * generation swaps.
104 */
105 bool on_iteration(Portal::Graphics::CommandBufferID cmd_id, const std::shared_ptr<VKBuffer>& buffer, uint32_t index) override;
106
107 /**
108 * @brief Serve a pending snapshot when a deferred dispatch completes.
109 * @param buffer Buffer the completed dispatch processed, unused.
110 */
111 void on_dispatch_complete(const std::shared_ptr<VKBuffer>& buffer) override;
112
113 /**
114 * @brief Write the state_in / state_out descriptor bindings for the
115 * current front/back assignment.
116 *
117 * Called once after the descriptor set is created, and again after any
118 * rebuild of it.
119 */
120 void on_descriptors_created() override;
121
122 /**
123 * @brief Cover the grid with a 2D dispatch and write GridExtent.
124 * @param buffer The attached buffer, expected to be a RelaxationGridBuffer.
125 *
126 * The local size is fixed at 16x16x1 and the group counts are
127 * ceil(width / 16) by ceil(height / 16). The workgroup_x constructor
128 * argument and ShaderSpec::workgroup_size are not consulted once
129 * attached to a grid.
130 */
131 void on_attach(const std::shared_ptr<Buffer>& buffer) override;
132
133 /**
134 * @brief Run one cycle: evaluate the predicate, write the state
135 * descriptors, run the parent, then swap and serve a snapshot if
136 * a dispatch was recorded.
137 *
138 * The descriptor write must precede the parent's bind_descriptor_sets.
139 * A write from on_before_execute lands on a set already bound into the
140 * open command buffer, which the driver has consumed by then, freezing
141 * the bindings at whatever on_descriptors_created wrote.
142 */
143 void processing_function(const std::shared_ptr<Buffer>& buffer) override;
144
145private:
146 /**
147 * @brief Issue direct ShaderFoundry descriptor writes for state_in and
148 * state_out against the grid's current front/back raw buffers.
149 * @param grid The RelaxationGridBuffer whose state buffers are being bound.
150 */
151 void write_state_descriptors(const std::shared_ptr<RelaxationGridBuffer>& grid);
152
153 /**
154 * @brief Size the push constant block to at least GridExtent and write
155 * the attached grid's dimensions into its leading 8 bytes.
156 *
157 * Called from on_attach. A ShaderSpec-constructed processor keeps the
158 * size spec.push_constant_bytes gave it. Bytes past sizeof(GridExtent)
159 * are rule constants, written by RelaxationGridBuffer from
160 * GridConfig::Stage::constants, or per cycle by ShaderProcessor::feed at
161 * an explicit offset. set_push_constant_data_raw is unsuitable: it
162 * writes from offset 0 and resizes the block to the size passed.
163 */
164 void write_grid_extent_constants();
165
166 /**
167 * @brief Copy the front state buffer to the host and signal
168 * RelaxationGridBuffer::snapshot_source(), if a request is pending.
169 *
170 * back_buffers are HostVisible | HostCoherent, so download_back_buffer
171 * reads mapped_ptr directly with no staging buffer or fenced transfer.
172 * The dispatch has completed by this point: submit_and_wait has returned
173 * under synchronous submission, and on_dispatch_complete fires under
174 * deferred submission.
175 */
176 void serve_snapshot();
177
178 bool m_dispatched { false }; ///< True when the current cycle recorded a dispatch.
179
180 /**
181 * @brief Fallback staging buffer for download_back_buffer, unused while
182 * back_buffers stays HostVisible | HostCoherent.
183 *
184 * Passed through on every snapshot regardless, since download_back_buffer
185 * takes the direct mapped_ptr branch for this buffer's actual memory type
186 * and never allocates or touches this member. Kept so a future back_buffers
187 * memory type change degrades to the fenced path automatically, with the
188 * staging buffer already lazily sized on first use of that path.
189 */
190 std::shared_ptr<VKBuffer> m_snapshot_staging;
191
192 StepPredicate m_step_predicate; ///< Optional gate deciding whether a given cycle advances a generation
193 std::shared_ptr<RelaxationGridBuffer> m_grid; ///< The attached RelaxationGridBuffer, cached for descriptor writes and snapshot reads.
194};
195
196} // namespace MayaFlux::Buffers
std::shared_ptr< NetworkGeometryBuffer > buffer
uint32_t index
Definition VKDevice.cpp:122
Specialized ShaderProcessor for Compute Pipelines.
std::function< bool()> StepPredicate
Callable deciding whether the current cycle advances a generation.
StepPredicate m_step_predicate
Optional gate deciding whether a given cycle advances a generation.
std::shared_ptr< RelaxationGridBuffer > m_grid
The attached RelaxationGridBuffer, cached for descriptor writes and snapshot reads.
void set_step_predicate(StepPredicate predicate)
Set the predicate deciding whether this cycle advances a generation.
std::shared_ptr< VKBuffer > m_snapshot_staging
Fallback staging buffer for download_back_buffer, unused while back_buffers stays HostVisible | HostC...
ComputeProcessor driving one generation step of a RelaxationGridBuffer per dispatch.
Leading push constant fields every relaxation rule shader receives: grid width then height,...
Complete declarative description of a generated compute shader.