MayaFlux 0.5.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
DiffuseProcessor.hpp
Go to the documentation of this file.
1#pragma once
2
4
5namespace MayaFlux::Buffers {
6
7/**
8 * @class DiffuseProcessor
9 * @brief VolumeFieldProcessor spreading a field into its neighbourhood by
10 * implicit Jacobi iteration.
11 *
12 * Solves the backward Euler form rather than stepping the heat equation
13 * forward, so the result is stable at any coefficient. The explicit form
14 * diverges once the coefficient exceeds a sixth of the squared cell
15 * spacing, which at a sixty-fourth-of-a-unit lattice is reached by
16 * ordinary viscosities.
17 *
18 * Applied to velocity this is viscosity. Applied to a scalar it blurs
19 * density or temperature, which softens the filaments first-order
20 * advection leaves behind.
21 *
22 * Requires a scratch field alongside the target. Jacobi needs the
23 * pre-diffusion values on every pass, and the two target slots are both
24 * consumed by the ping-pong, so the first pass copies what it reads into
25 * the scratch and later passes read it from there. The scratch is bound
26 * once, unqualified, and must have the same stride as the target. It may
27 * be single-buffered and is never swapped.
28 *
29 * Shares PressureProcessor's parity arrangement: both target slots bound
30 * simultaneously, the pass's read and write selected by a push constant
31 * word, since a descriptor set bound into an open command buffer cannot
32 * be rewritten mid-recording.
33 */
34class MAYAFLUX_API DiffuseProcessor : public VolumeFieldProcessor {
35public:
36 /**
37 * @brief Construct a diffusion stage.
38 * @param target_field Name of the field diffused. Must be double-buffered.
39 * @param scratch_field Name of a field of matching stride holding the
40 * pre-diffusion values across passes. Not swapped, may be
41 * single-buffered, and its contents are overwritten every cycle.
42 * @param shader_path Path to the compute shader.
43 * @param iterations Jacobi passes per cycle. Values below 1 clamp to 1.
44 */
46 std::string target_field,
47 std::string scratch_field,
48 const std::string& shader_path,
49 uint32_t iterations = 20);
50
51 /**
52 * @brief Construct a diffusion stage from a generated ShaderSpec.
53 * @param target_field Name of the field diffused.
54 * @param scratch_field Name of the scratch field.
55 * @param spec ShaderSpec implementing one Jacobi pass.
56 * @param iterations Jacobi passes per cycle.
57 */
59 std::string target_field,
60 std::string scratch_field,
62 uint32_t iterations = 20);
63
64 /**
65 * @brief Set the diffusion rate.
66 * @param rate Viscosity for velocity, or a blur rate for a scalar.
67 * Zero leaves the field unchanged. Takes effect next cycle.
68 */
69 void set_rate(float rate);
70
71 /**
72 * @brief Set the integration step.
73 * @param dt Seconds per step. Takes effect next cycle.
74 */
75 void set_time_step(float dt);
76
77 /** @brief Name of the field this stage diffuses. */
78 [[nodiscard]] const std::string& get_target_field() const { return m_target_field; }
79
80 /** @brief Name of the field holding pre-diffusion values across passes. */
81 [[nodiscard]] const std::string& get_scratch_field() const { return m_scratch_field; }
82
83protected:
84 /**
85 * @brief Write the coefficient into the shared prefix.
86 */
87 void on_volume_ready() override;
88
89 /**
90 * @brief Write this pass's parity and first-pass flag.
91 * @param cmd_id Command buffer being recorded into.
92 * @param buffer Buffer under processing.
93 * @param index Zero-based iteration index.
94 * @return Always true. Every pass dispatches.
95 */
96 bool on_iteration(
98 const std::shared_ptr<VKBuffer>& buffer,
99 uint32_t index) override;
100
101 /**
102 * @brief Barrier both target slots and the scratch between passes.
103 * @param cmd_id Command buffer being recorded into.
104 * @param buffer Buffer under processing.
105 * @param index Zero-based index of the pass just recorded.
106 */
107 void on_iteration_barrier(
109 const std::shared_ptr<VKBuffer>& buffer,
110 uint32_t index) override;
111
112 /**
113 * @brief Swap only when the pass count leaves the result in the write slot.
114 * @return True when the iteration count is odd.
115 */
116 [[nodiscard]] bool wants_swap() const override;
117
118private:
119 /**
120 * @brief Write the product of rate and time step into word seven.
121 */
122 void write_coefficient();
123
124 /**
125 * @brief Build the binding table for the two field names.
126 * @param target_field Field diffused, bound in both slots.
127 * @param scratch_field Field holding pre-diffusion values.
128 * @return Table binding the scratch and both target slots.
129 */
130 static std::vector<FieldBinding> make_bindings(
131 const std::string& target_field, const std::string& scratch_field);
132
133 std::string m_target_field;
134 std::string m_scratch_field;
135
136 float m_rate { 0.0F };
137 float m_time_step { 1.0F / 60.0F };
138};
139
140} // namespace MayaFlux::Buffers
float rate
const std::string & get_target_field() const
Name of the field this stage diffuses.
const std::string & get_scratch_field() const
Name of the field holding pre-diffusion values across passes.
VolumeFieldProcessor spreading a field into its neighbourhood by implicit Jacobi iteration.
ComputeProcessor operating on named fields of a VolumeGridBuffer.
Complete declarative description of a generated compute shader.