MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
RelaxationGridBuffer.cpp
Go to the documentation of this file.
4
7
9
11
14
15namespace MayaFlux::Buffers {
16
17namespace {
18
19 /**
20 * @brief Format a stage declares, or the one its ShaderSpec binding carries.
21 * @param stage Stage to resolve.
22 * @param binding Binding whose format a spec stage is read from.
23 * @return The format, or nullopt when a path stage declares none.
24 */
25 std::optional<Kakshya::GpuDataFormat> resolve_state(
26 const RelaxationGridBuffer::GridConfig::Stage& stage,
27 std::string_view binding)
28 {
29 if (stage.state) {
30 return stage.state;
31 }
32
33 const auto* spec = std::get_if<Portal::Graphics::ShaderSpec>(&stage.shader);
34 if (!spec) {
35 return std::nullopt;
36 }
37
38 const auto it = std::ranges::find_if(
39 spec->bindings,
40 [binding](const auto& slot) { return slot.name == binding; });
41
42 if (it == spec->bindings.end()) {
43 return std::nullopt;
44 }
45
46 return it->format;
47 }
48
49 /**
50 * @brief Cell stride in bytes for a GridConfig, after checking its stages.
51 * @param config Grid arrangement.
52 * @return gpu_data_format_bytes of the shared cell format.
53 */
54 size_t resolve_stride(const RelaxationGridBuffer::GridConfig& config)
55 {
56 const auto rule = resolve_state(config.rule, "state_in");
57 const auto emit = resolve_state(config.emit, "cell_state");
58
59 if (!rule || !emit) {
60 error<std::invalid_argument>(
62 std::source_location::current(),
63 "GridConfig: a stage whose shader is a path must declare its state format");
64 }
65
66 if (*rule != *emit) {
67 error<std::invalid_argument>(
69 std::source_location::current(),
70 "GridConfig: rule and emit disagree on the cell format");
71 }
72
74 error<std::invalid_argument>(
76 std::source_location::current(),
77 "GridConfig: a three component cell strides 16 bytes in std430, declare a four component format");
78 }
79
81 }
82
83 /**
84 * @brief Write constants after a stage's fixed prefix in its push constant block.
85 * @param processor Stage processor, attached and not yet dispatched.
86 * @param offset Byte offset of the first constant.
87 * @param constants Words to write, in shader declaration order.
88 *
89 * Grows the block through set_push_constant_size so the pipeline range is
90 * created at the final width on the first cycle.
91 */
92 template <typename Processor>
93 void apply_constants(
94 const std::shared_ptr<Processor>& processor,
95 size_t offset,
96 const std::vector<RelaxationGridBuffer::GridConfig::Constant>& constants)
97 {
98 if (constants.empty()) {
99 return;
100 }
101
102 const size_t end = offset + constants.size() * sizeof(uint32_t);
103 if (processor->get_push_constant_data().size() < end) {
104 processor->set_push_constant_size(end);
105 }
106
107 auto& data = processor->get_push_constant_data();
108 if (data.size() < end) {
109 data.resize(end);
110 }
111
112 size_t at = offset;
113 for (const auto& constant : constants) {
114 std::visit([&](auto value) { std::memcpy(data.data() + at, &value, sizeof(value)); }, constant);
115 at += sizeof(uint32_t);
116 }
117 }
118
119} // namespace
120
123 config.width, config.height, resolve_stride(config),
124 config.rule.shader, config.emit.shader)
125{
128 m_extent = config.extent;
129 m_point_size = config.point_size;
130}
131
133 uint32_t width,
134 uint32_t height,
135 size_t cell_stride_bytes,
136 ShaderSource rule_source,
137 ShaderSource emit_source)
138 : VKBuffer(
139 static_cast<size_t>(width) * height * sizeof(Kakshya::Vertex),
140 Usage::VERTEX,
141 Kakshya::DataModality::VERTEX_POSITIONS_3D)
142 , m_width(width)
143 , m_height(height)
144 , m_cell_stride_bytes(cell_stride_bytes)
145 , m_rule_source(std::move(rule_source))
146 , m_emit_source(std::move(emit_source))
147{
148 auto buffer_service = Registry::BackendRegistry::instance()
150
151 if (!buffer_service) {
152 error<std::runtime_error>(
155 std::source_location::current(),
156 "RelaxationGridBuffer requires a valid BufferService");
157 }
158
159 const size_t state_bytes = get_state_bytes();
160 const auto usage_flags = static_cast<uint32_t>(
161 VkBufferUsageFlags(vk::BufferUsageFlagBits::eStorageBuffer));
162 const auto memory_flags = static_cast<uint32_t>(
163 VkMemoryPropertyFlags(vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent));
164
165 auto& resources = get_buffer_resources();
166 resources.back_buffers.resize(2);
167
168 for (uint32_t i = 0; i < 2; ++i) {
169 void* out_buffer = nullptr;
170 void* out_memory = nullptr;
171 void* out_mapped = nullptr;
172
173 buffer_service->allocate_raw_buffer(
174 state_bytes, usage_flags, memory_flags, true,
175 out_buffer, out_memory, out_mapped);
176
177 resources.back_buffers[i].buffer = static_cast<vk::Buffer>(static_cast<VkBuffer>(out_buffer));
178 resources.back_buffers[i].memory = static_cast<vk::DeviceMemory>(static_cast<VkDeviceMemory>(out_memory));
179 resources.back_buffers[i].mapped_ptr = out_mapped;
180 }
181
183 "RelaxationGridBuffer: {}x{} grid, {} bytes/cell, {} bytes/generation",
184 m_width, m_height, m_cell_stride_bytes, state_bytes);
185}
186
188{
189 auto layout = Kakshya::VertexLayout::for_raw(sizeof(Kakshya::Vertex));
190 layout.vertex_count = get_cell_count();
191 set_vertex_layout(layout);
192
193 m_step_processor = std::visit(
194 [](const auto& src) { return std::make_shared<RelaxationStepProcessor>(src); },
196
197 m_step_processor->set_processing_token(token);
200
202 if (!chain) {
203 chain = std::make_shared<BufferProcessingChain>();
205 }
206 chain->set_preferred_token(token);
207
208 m_emit_processor = std::visit(
209 [](const auto& src) { return std::make_shared<RelaxationEmitProcessor>(src); },
211 m_emit_processor->set_processing_token(token);
212 chain->add_processor(m_emit_processor, shared_from_this());
214 m_emit_processor->set_extent(m_extent);
215 m_emit_processor->set_point_size(m_point_size);
216
218 "RelaxationGridBuffer setup_processors: step + emit attached");
219}
220
222{
223 RenderConfig resolved = config;
225
226 if (resolved.vertex_shader.empty())
227 resolved.vertex_shader = "point.vert.spv";
228 if (resolved.fragment_shader.empty())
229 resolved.fragment_shader = "point.frag.spv";
230
231 ShaderConfig sc { resolved.vertex_shader };
232 apply_render_config(resolved, sc);
233
234 get_processing_chain()->add_final_processor(m_render_processor, shared_from_this());
235
237 "RelaxationGridBuffer: rendering configured");
238}
239
240void RelaxationGridBuffer::seed_state(const void* data, size_t size)
241{
242 if (size != get_state_bytes()) {
244 "seed_state size {} does not match expected {}", size, get_state_bytes());
245 return;
246 }
247
248 auto& resources = get_buffer_resources();
249 auto& front = resources.back_buffers[front_index()];
250
251 if (!front.mapped_ptr) {
253 "seed_state: front generation buffer has no mapped pointer");
254 return;
255 }
256
257 std::memcpy(front.mapped_ptr, data, size);
258}
259
260} // namespace MayaFlux::Buffers
#define MF_INFO(comp, ctx,...)
#define MF_ERROR(comp, ctx,...)
#define MF_DEBUG(comp, ctx,...)
uint64_t offset
Definition Composite.cpp:10
bool size
BufferProcessingChain * chain
const uint32_t * src
glm::vec3 value
uint32_t width
uint32_t height
void setup_rendering(const RenderConfig &config)
Attach a RenderProcessor drawing the emitted vertex output as ordinary untextured point geometry.
std::vector< GridConfig::Constant > m_rule_constants
Rule constants from GridConfig, written at setup_processors.
std::vector< GridConfig::Constant > m_emit_constants
Emit constants from GridConfig, written at setup_processors.
std::shared_ptr< RelaxationEmitProcessor > m_emit_processor
Emit stage, retained for parameter access.
ShaderSource m_emit_source
Either a path to a hand-written emit shader or a generated ShaderSpec.
RelaxationGridBuffer(uint32_t width, uint32_t height, size_t cell_stride_bytes, ShaderSource rule_source, ShaderSource emit_source)
Construct an unregistered double-buffered grid buffer.
void setup_processors(ProcessingToken token) override
Create and attach RelaxationStepProcessor (default processor) and RelaxationEmitProcessor (flat proce...
void seed_state(const void *data, size_t size)
Write initial state directly into the current front generation.
uint32_t get_cell_count() const
Total cell count, width * height.
std::shared_ptr< RelaxationStepProcessor > m_step_processor
Rule stage, retained for parameter access.
size_t get_state_bytes() const
Total byte size of one generation's state buffer.
size_t m_cell_stride_bytes
Size in bytes of one cell's state, fixed at construction.
ShaderSource m_rule_source
Either a path to a hand-written rule shader or a generated ShaderSpec.
std::variant< std::string, Portal::Graphics::ShaderSpec > ShaderSource
Either a shader file path or a generated ShaderSpec, resolved at construction time by setup_processor...
float m_extent
Emit extent applied at setup_processors.
uint32_t front_index() const
Index into m_resources.back_buffers currently holding the most recently completed generation's state.
float m_point_size
Emit point size applied at setup_processors.
GPU-resident, double-buffered SSBO state for synchronous local-rule systems evaluated over a fixed 2D...
void set_vertex_layout(const Kakshya::VertexLayout &layout)
Set vertex layout for this buffer.
Definition VKBuffer.cpp:453
std::shared_ptr< Buffers::BufferProcessingChain > get_processing_chain() override
Access the buffer's processing chain.
Definition VKBuffer.cpp:271
void set_default_processor(const std::shared_ptr< BufferProcessor > &processor) override
Set the buffer's default processor.
Definition VKBuffer.cpp:255
void set_processing_chain(const std::shared_ptr< BufferProcessingChain > &chain, bool force=false) override
Replace the buffer's processing chain.
Definition VKBuffer.cpp:276
void apply_render_config(const RenderConfig &config, const ShaderConfig &shader_config)
Configure the internal m_render_processor from a RenderConfig.
Definition VKBuffer.cpp:344
std::shared_ptr< RenderProcessor > m_render_processor
Definition VKBuffer.hpp:654
const VKBufferResources & get_buffer_resources() const
Get all buffer resources at once (read-only)
Definition VKBuffer.hpp:343
Vulkan-backed buffer wrapper used in processing chains.
Definition VKBuffer.hpp:76
Interface * get_service()
Query for a backend service.
static BackendRegistry & instance()
Get the global registry instance.
ProcessingToken
Bitfield enum defining processing characteristics and backend requirements for buffer operations.
@ BufferManagement
Buffer Management (Buffers::BufferManager, creating buffers)
@ Init
Engine/subsystem initialization.
@ Buffers
Buffers, Managers, processors and processing chains.
size_t gpu_data_format_bytes(GpuDataFormat fmt) noexcept
Byte size of one element of a GpuDataFormat.
@ VEC3_F64
glm::dvec3 (64-bit components)
@ VEC3_F32
glm::vec3 (32-bit components), not a sampled image format
Tendency< D, R > constant(R value)
Tendency returning a fixed value regardless of input.
auto at(const B &anchor, F &&factory, Args &&... args) -> decltype(std::forward< F >(factory)(Kinesis::bounds_of(anchor), std::forward< Args >(args)...))
Call a bounds-taking factory with an anchor's region.
Definition Geometry.hpp:152
BufferUsageHint
Semantic usage hint for buffer allocation and memory properties.
Push constant block for the packed-vertex emit shader.
std::vector< Constant > constants
Initial words after the stage's fixed prefix (GridExtent for a rule, EmitParams for an emit),...
Stage emit
Required. Reads "cell_state", writes packed vertices.
Stage rule
Required. Reads "state_in", writes "state_out".
float point_size
Per-point size written to the vertex scalar field.
Arrangement of a relaxation grid: extent, the update rule, the emit stage that draws it,...
Leading push constant fields every relaxation rule shader receives: grid width then height,...
static VertexLayout for_raw(uint32_t stride=60)
Factory: layout for raw vertex data with common attributes.
Type-neutral vertex carrying the universal 60-byte attribute layout.
Unified rendering configuration for graphics buffers.
Backend buffer management service interface.