MayaFlux 0.5.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
VolumeGridBuffer.cpp
Go to the documentation of this file.
2
6
9
12
13#include "AdvectProcessor.hpp"
14#include "BuoyancyProcessor.hpp"
15#include "DiffuseProcessor.hpp"
17#include "PressureProcessor.hpp"
20#include "WallProcessor.hpp"
21
22namespace MayaFlux::Buffers {
23
25 Kinesis::Lattice3D lattice,
26 std::initializer_list<FieldDecl> fields,
27 std::optional<SurfaceConfig> surface)
28 : VolumeGridBuffer(lattice, std::vector<FieldDecl>(fields), std::move(surface))
29{
30}
31
33 Kinesis::Lattice3D lattice,
34 std::vector<FieldDecl> fields,
35 std::optional<SurfaceConfig> surface)
36 : VKBuffer(surface_storage_bytes(surface), Usage::VERTEX, Kakshya::DataModality::VERTICES_3D)
37 , m_lattice(lattice)
38 , m_surface(std::move(surface))
39{
41 allocate_fields(fields);
42}
43
45 Kinesis::Lattice3D lattice,
46 std::optional<SurfaceConfig> surface)
47 : VKBuffer(surface_storage_bytes(surface), Usage::VERTEX, Kakshya::DataModality::VERTICES_3D)
48 , m_lattice(lattice)
49 , m_surface(std::move(surface))
50{
52}
53
58
60 const std::string& name, size_t stride_bytes, bool double_buffered)
61{
62 if (name.empty()) {
64 "VolumeGridBuffer: field declared with empty name, skipped");
65 return false;
66 }
67
68 if (stride_bytes == 0) {
70 "VolumeGridBuffer: field '{}' declares zero stride, skipped", name);
71 return false;
72 }
73
74 if (m_fields.contains(name)) {
76 "VolumeGridBuffer: duplicate field '{}', later declaration discarded", name);
77 return false;
78 }
79
80 auto buffer_service = Registry::BackendRegistry::instance()
82
83 if (!buffer_service || !buffer_service->allocate_raw_buffer) {
84 error<std::runtime_error>(
87 std::source_location::current(),
88 "VolumeGridBuffer requires a valid buffer service");
89 }
90
91 const auto usage_flags = static_cast<uint32_t>(
92 static_cast<VkBufferUsageFlags>(
93 vk::BufferUsageFlagBits::eStorageBuffer
94 | vk::BufferUsageFlagBits::eTransferSrc
95 | vk::BufferUsageFlagBits::eTransferDst));
96
97 const auto memory_flags = static_cast<uint32_t>(
98 static_cast<VkMemoryPropertyFlags>(vk::MemoryPropertyFlagBits::eDeviceLocal));
99
100 auto& resources = get_buffer_resources();
101 const size_t field_bytes = static_cast<size_t>(get_cell_count()) * stride_bytes;
102 const uint32_t slot_count = double_buffered ? 2 : 1;
103
104 Field field {
105 .stride_bytes = stride_bytes,
106 .slot_a = static_cast<uint32_t>(resources.back_buffers.size()),
107 .slot_b = 0,
108 .read_is_a = true,
109 };
110
111 for (uint32_t i = 0; i < slot_count; ++i) {
112 void* out_buffer = nullptr;
113 void* out_memory = nullptr;
114 void* out_mapped = nullptr;
115
116 buffer_service->allocate_raw_buffer(
117 field_bytes, usage_flags, memory_flags, false,
118 out_buffer, out_memory, out_mapped);
119
121 slot.buffer = static_cast<vk::Buffer>(static_cast<VkBuffer>(out_buffer));
122 slot.memory = static_cast<vk::DeviceMemory>(static_cast<VkDeviceMemory>(out_memory));
123 slot.mapped_ptr = out_mapped;
124
125 resources.back_buffers.push_back(slot);
126 }
127
128 field.slot_b = double_buffered ? field.slot_a + 1 : field.slot_a;
129
130 m_fields.emplace(name, field);
131 m_field_order.push_back(name);
132
134 "VolumeGridBuffer: field '{}', stride {}, {} slot(s), {} bytes",
135 name, stride_bytes, slot_count, field_bytes * slot_count);
136
137 return true;
138}
139
140void VolumeGridBuffer::allocate_fields(const std::vector<FieldDecl>& decls)
141{
142 for (const auto& decl : decls) {
143 allocate_field(decl.name, decl.stride_bytes, decl.double_buffered);
144 }
145
146 if (m_fields.empty()) {
147 error<std::runtime_error>(
150 std::source_location::current(),
151 "VolumeGridBuffer constructed with no valid fields");
152 }
153
155 "VolumeGridBuffer: {}x{}x{} lattice, {} fields, {} slots",
157 m_fields.size(), get_buffer_resources().back_buffers.size());
158}
159
161{
162 if (!allocate_field(name, sizeof(float), true)) {
163 return {};
164 }
165
166 return ScalarRef {
167 .name = std::move(name),
168 .owner = std::dynamic_pointer_cast<VolumeGridBuffer>(shared_from_this()),
169 };
170}
171
173{
174 if (!allocate_field(name, sizeof(float), false)) {
175 return {};
176 }
177
178 return ScalarRef {
179 .name = std::move(name),
180 .owner = std::dynamic_pointer_cast<VolumeGridBuffer>(shared_from_this()),
181 };
182}
183
185{
186 if (!allocate_field(name, sizeof(glm::vec4), true)) {
187 return {};
188 }
189
190 return VectorRef {
191 .name = std::move(name),
192 .owner = std::dynamic_pointer_cast<VolumeGridBuffer>(shared_from_this()),
193 };
194}
195
197{
198 auto chain = get_processing_chain();
199 if (!chain) {
200 chain = std::make_shared<BufferProcessingChain>();
202 }
203 chain->set_preferred_token(token);
204
205 if (!m_surface) {
207 "VolumeGridBuffer: chain established, no extraction configured");
208 return;
209 }
210
211 auto self = std::dynamic_pointer_cast<VolumeGridBuffer>(shared_from_this());
212
213 m_surface_processor = std::make_shared<VolumeSurfaceProcessor>(
214 self, m_surface->field_name,
215 m_lattice.resampled(m_surface->resolution),
216 m_surface->threshold);
217
220
221 m_counter_buf = std::make_shared<VKBuffer>(
222 sizeof(uint32_t), Usage::HOST_STORAGE, Kakshya::DataModality::UNKNOWN);
223 svc->initialize_buffer(m_counter_buf);
224
225 m_mesh_processor = std::make_shared<SDFMeshProcessor>(
228 m_surface->resolution.x, m_surface->resolution.y, m_surface->resolution.z, 0.0F);
229
230 const uint32_t max_vertices = m_surface_processor->worst_case_vertices();
232 layout.vertex_count = max_vertices;
233 set_vertex_layout(layout);
234
235 m_surface_processor->set_processing_token(token);
236 m_mesh_processor->set_processing_token(token);
237
239 chain->add_processor(m_mesh_processor, self);
240
242 "VolumeGridBuffer: surfacing '{}' at {}x{}x{}, {} max vertices",
243 m_surface->field_name, m_surface->resolution.x, m_surface->resolution.y,
244 m_surface->resolution.z, max_vertices);
245
247 "VolumeGridBuffer: chain established, no stages attached");
248}
249
251{
252 if (!m_surface) {
254 "setup_rendering: no SurfaceConfig was supplied at construction");
255 return;
256 }
257
258 RenderConfig resolved = config;
260
261 if (resolved.vertex_shader.empty())
262 resolved.vertex_shader = "triangle.vert.spv";
263 if (resolved.fragment_shader.empty())
264 resolved.fragment_shader = "triangle.frag.spv";
265
266 apply_render_config(resolved, ShaderConfig { resolved.vertex_shader });
267
268 get_processing_chain()->add_final_processor(m_render_processor, shared_from_this());
269
270 m_render_processor->set_vertex_range(0, 0);
272}
273
275{
276 FlowStages stages;
277
278 auto self = std::dynamic_pointer_cast<VolumeGridBuffer>(shared_from_this());
279 auto chain = get_processing_chain();
280
281 if (!chain) {
283 "setup_flow: no processing chain, call after registration");
284 return stages;
285 }
286
287 const auto require = [this](const std::string& name, const char* role) {
288 if (name.empty()) {
290 "setup_flow: no field supplied for '{}'", role);
291 return false;
292 }
293 if (!has_field(name)) {
295 "setup_flow: '{}' names no field on this volume, supplied as '{}'",
296 name, role);
297 return false;
298 }
299 return true;
300 };
301
302 if (!require(config.velocity, "velocity")
303 || !require(config.divergence, "divergence")
304 || !require(config.pressure, "pressure")) {
305 return stages;
306 }
307
308 if (config.viscosity > 0.0F && !require(config.scratch, "scratch")) {
309 return stages;
310 }
311
312 stages.self_advect = std::make_shared<AdvectProcessor>(
313 config.velocity, config.velocity, "volume_advect_vector.comp.spv");
314 stages.self_advect->set_time_step(config.time_step);
315 chain->add_processor(stages.self_advect, self);
316
317 if (config.buoyancy) {
318 const auto& b = *config.buoyancy;
319
320 if (!require(b.temperature, "buoyancy.temperature")
321 || !require(b.density, "buoyancy.density")) {
322 return stages;
323 }
324
325 stages.buoyancy = std::make_shared<BuoyancyProcessor>(
326 b.temperature, b.density, config.velocity,
327 b.direction, "volume_buoyancy.comp.spv");
328 stages.buoyancy->set_time_step(config.time_step);
329 stages.buoyancy->set_temperature_gain(b.temperature_gain);
330 stages.buoyancy->set_density_gain(b.density_gain);
331 stages.buoyancy->set_ambient(b.ambient);
332 chain->add_processor(stages.buoyancy, self);
333 }
334
335 if (config.viscosity > 0.0F) {
336 stages.diffuse = std::make_shared<DiffuseProcessor>(
337 config.velocity, config.scratch, "volume_diffuse_vector.comp.spv");
338 stages.diffuse->set_rate(config.viscosity);
339 stages.diffuse->set_time_step(config.time_step);
340 chain->add_processor(stages.diffuse, self);
341 }
342
343 if (config.walls) {
344 stages.wall_advected = std::make_shared<WallProcessor>(
345 config.velocity, "volume_wall.comp.spv");
346 chain->add_processor(stages.wall_advected, self);
347 }
348
349 stages.divergence = std::make_shared<DivergenceProcessor>(
350 config.velocity, config.divergence, "volume_divergence.comp.spv");
351 chain->add_processor(stages.divergence, self);
352
353 stages.pressure = std::make_shared<PressureProcessor>(
354 config.divergence, config.pressure, "volume_pressure_jacobi.comp.spv");
355 stages.pressure->set_iteration_count(config.jacobi_iterations);
356 chain->add_processor(stages.pressure, self);
357
358 stages.solenoidal = std::make_shared<SolenoidalProcessor>(
359 config.pressure, config.velocity, "volume_solenoidal.comp.spv");
360 chain->add_processor(stages.solenoidal, self);
361
362 if (config.walls) {
363 stages.wall_projected = std::make_shared<WallProcessor>(
364 config.velocity, "volume_wall.comp.spv");
365 chain->add_processor(stages.wall_projected, self);
366 }
367
368 for (const auto& carried : config.carried) {
369 if (!require(carried.field, "carried")) {
370 continue;
371 }
372
373 auto advect = std::make_shared<AdvectProcessor>(
374 config.velocity, carried.field, "volume_advect_scalar.comp.spv");
375 advect->set_time_step(config.time_step);
376 advect->set_dissipation(carried.dissipation);
377 chain->add_processor(advect, self);
378
379 stages.carriers.push_back(std::move(advect));
380 }
381
383 "VolumeGridBuffer::setup_flow: {} carried, buoyancy {}, viscosity {}, walls {}",
384 stages.carriers.size(), config.buoyancy ? "on" : "off",
385 config.viscosity, config.walls ? "on" : "off");
386
387 return stages;
388}
389
391 const std::string& name, const char* context) const
392{
393 auto it = m_fields.find(name);
394 if (it == m_fields.end()) {
396 "VolumeGridBuffer::{}: no field named '{}'", context, name);
397 return nullptr;
398 }
399 return &it->second;
400}
401
402bool VolumeGridBuffer::has_field(const std::string& name) const
403{
404 return m_fields.contains(name);
405}
406
407size_t VolumeGridBuffer::get_field_bytes(const std::string& name) const
408{
409 auto it = m_fields.find(name);
410 if (it == m_fields.end()) {
411 return 0;
412 }
413 return static_cast<size_t>(get_cell_count()) * it->second.stride_bytes;
414}
415
416std::vector<std::string> VolumeGridBuffer::get_field_names() const
417{
418 return m_field_order;
419}
420
421vk::Buffer VolumeGridBuffer::read_handle(const std::string& name) const
422{
423 const auto* field = find_field(name, "read_handle");
424 if (!field) {
425 return nullptr;
426 }
427
428 const uint32_t slot = field->read_is_a ? field->slot_a : field->slot_b;
429 return get_buffer_resources().back_buffers[slot].buffer;
430}
431
432vk::Buffer VolumeGridBuffer::write_handle(const std::string& name) const
433{
434 const auto* field = find_field(name, "write_handle");
435 if (!field) {
436 return nullptr;
437 }
438
439 const uint32_t slot = field->read_is_a ? field->slot_b : field->slot_a;
440 return get_buffer_resources().back_buffers[slot].buffer;
441}
442
443void VolumeGridBuffer::swap_field(const std::string& name)
444{
445 auto it = m_fields.find(name);
446 if (it == m_fields.end()) {
448 "VolumeGridBuffer::swap_field: no field named '{}'", name);
449 return;
450 }
451
452 if (it->second.slot_a == it->second.slot_b) {
453 return;
454 }
455
456 it->second.read_is_a = !it->second.read_is_a;
457}
458
459void VolumeGridBuffer::seed(const std::string& name, const Kinesis::SpatialField& field)
460{
461 const auto* decl = find_field(name, "seed");
462 if (!decl) {
463 return;
464 }
465
466 if (decl->stride_bytes != sizeof(float)) {
468 "VolumeGridBuffer::seed: field '{}' has stride {}, SpatialField requires {}",
469 name, decl->stride_bytes, sizeof(float));
470 return;
471 }
472
473 const glm::uvec3 res = m_lattice.resolution;
474 std::vector<float> values(m_lattice.cell_count());
475
476 size_t i = 0;
477 for (uint32_t z = 0; z < res.z; ++z) {
478 for (uint32_t y = 0; y < res.y; ++y) {
479 for (uint32_t x = 0; x < res.x; ++x) {
480 values[i++] = field(m_lattice.cell_center({ x, y, z }));
481 }
482 }
483 }
484
485 seed_raw(name, values.data(), values.size() * sizeof(float));
486}
487
488void VolumeGridBuffer::seed(const std::string& name, const Kinesis::VectorField& field)
489{
490 const auto* decl = find_field(name, "seed");
491 if (!decl) {
492 return;
493 }
494
495 if (decl->stride_bytes != sizeof(glm::vec4)) {
497 "VolumeGridBuffer::seed: field '{}' has stride {}, VectorField requires {}",
498 name, decl->stride_bytes, sizeof(glm::vec4));
499 return;
500 }
501
502 const glm::uvec3 res = m_lattice.resolution;
503 std::vector<glm::vec4> values(m_lattice.cell_count());
504
505 size_t i = 0;
506 for (uint32_t z = 0; z < res.z; ++z) {
507 for (uint32_t y = 0; y < res.y; ++y) {
508 for (uint32_t x = 0; x < res.x; ++x) {
509 values[i++] = glm::vec4(field(m_lattice.cell_center({ x, y, z })), 0.0F);
510 }
511 }
512 }
513
514 seed_raw(name, values.data(), values.size() * sizeof(glm::vec4));
515}
516
517void VolumeGridBuffer::seed_raw(const std::string& name, const void* data, size_t size)
518{
519 const auto* field = find_field(name, "seed_raw");
520 if (!field) {
521 return;
522 }
523
524 const size_t expected = static_cast<size_t>(get_cell_count()) * field->stride_bytes;
525 if (size != expected) {
527 "VolumeGridBuffer::seed_raw: size {} does not match expected {} for '{}'",
528 size, expected, name);
529 return;
530 }
531
533
534 const uint32_t slot = field->read_is_a ? field->slot_a : field->slot_b;
536 get_buffer_resources().back_buffers[slot], data, size, m_transfer_staging);
537}
538
539void VolumeGridBuffer::accumulate(const std::string& name, const Kinesis::SpatialField& field)
540{
541 const auto* decl = find_field(name, "accumulate");
542 if (!decl) {
543 return;
544 }
545
546 if (decl->stride_bytes != sizeof(float)) {
548 "VolumeGridBuffer::accumulate: field '{}' has stride {}, SpatialField requires {}",
549 name, decl->stride_bytes, sizeof(float));
550 return;
551 }
552
553 const glm::uvec3 res = m_lattice.resolution;
554 std::vector<float> values(m_lattice.cell_count());
555
556 read_field(name, values.data(), values.size() * sizeof(float));
557
558 size_t i = 0;
559 for (uint32_t z = 0; z < res.z; ++z) {
560 for (uint32_t y = 0; y < res.y; ++y) {
561 for (uint32_t x = 0; x < res.x; ++x) {
562 values[i++] += field(m_lattice.cell_center({ x, y, z }));
563 }
564 }
565 }
566
567 seed_raw(name, values.data(), values.size() * sizeof(float));
568}
569
570void VolumeGridBuffer::accumulate(const std::string& name, const Kinesis::VectorField& field)
571{
572 const auto* decl = find_field(name, "accumulate");
573 if (!decl) {
574 return;
575 }
576
577 if (decl->stride_bytes != sizeof(glm::vec4)) {
579 "VolumeGridBuffer::accumulate: field '{}' has stride {}, VectorField requires {}",
580 name, decl->stride_bytes, sizeof(glm::vec4));
581 return;
582 }
583
584 const glm::uvec3 res = m_lattice.resolution;
585 std::vector<glm::vec4> values(m_lattice.cell_count());
586
587 read_field(name, values.data(), values.size() * sizeof(glm::vec4));
588
589 size_t i = 0;
590 for (uint32_t z = 0; z < res.z; ++z) {
591 for (uint32_t y = 0; y < res.y; ++y) {
592 for (uint32_t x = 0; x < res.x; ++x) {
593 const glm::vec3 v = field(m_lattice.cell_center({ x, y, z }));
594 values[i].x += v.x;
595 values[i].y += v.y;
596 values[i].z += v.z;
597 ++i;
598 }
599 }
600 }
601
602 seed_raw(name, values.data(), values.size() * sizeof(glm::vec4));
603}
604
605void VolumeGridBuffer::read_field(const std::string& name, void* data, size_t size)
606{
607 const auto* field = find_field(name, "read_field");
608 if (!field) {
609 return;
610 }
611
612 const size_t expected = static_cast<size_t>(get_cell_count()) * field->stride_bytes;
613 if (size != expected) {
615 "VolumeGridBuffer::read_field: size {} does not match expected {} for '{}'",
616 size, expected, name);
617 return;
618 }
619
620 const uint32_t slot = field->read_is_a ? field->slot_a : field->slot_b;
622 download_back_buffer(get_buffer_resources().back_buffers[slot], data, size, m_transfer_staging);
623}
624
625size_t VolumeGridBuffer::surface_storage_bytes(const std::optional<SurfaceConfig>& surface)
626{
627 if (!surface) {
628 return 1;
629 }
630
631 const glm::uvec3 res = glm::max(surface->resolution, glm::uvec3(1U));
632 const uint64_t voxels = static_cast<uint64_t>(res.x) * res.y * res.z;
633 return static_cast<size_t>(voxels * 15U * sizeof(Kakshya::MeshVertex));
634}
635
636} // namespace MayaFlux::Buffers
#define MF_INFO(comp, ctx,...)
#define MF_ERROR(comp, ctx,...)
#define MF_DEBUG(comp, ctx,...)
size_t b
void set_vertex_layout(const Kakshya::VertexLayout &layout)
Set vertex layout for this buffer.
Definition VKBuffer.cpp:387
std::shared_ptr< Buffers::BufferProcessingChain > get_processing_chain() override
Access the buffer's processing chain.
Definition VKBuffer.cpp:263
void set_default_processor(const std::shared_ptr< BufferProcessor > &processor) override
Set the buffer's default processor.
Definition VKBuffer.cpp:247
void set_processing_chain(const std::shared_ptr< BufferProcessingChain > &chain, bool force=false) override
Replace the buffer's processing chain.
Definition VKBuffer.cpp:268
void set_needs_depth_attachment(bool needs)
Mark this buffer as requiring depth testing when rendered.
Definition VKBuffer.hpp:567
void apply_render_config(const RenderConfig &config, const ShaderConfig &shader_config)
Configure the internal m_render_processor from a RenderConfig.
Definition VKBuffer.cpp:336
std::shared_ptr< RenderProcessor > m_render_processor
Definition VKBuffer.hpp:620
const VKBufferResources & get_buffer_resources() const
Get all buffer resources at once (read-only)
Definition VKBuffer.hpp:341
void force_internal_usage(bool internal) override
Set whether this buffer is for internal engine usage.
Definition VKBuffer.hpp:518
Vulkan-backed buffer wrapper used in processing chains.
Definition VKBuffer.hpp:76
const Field * find_field(const std::string &name, const char *context) const
Resolve a field by name.
vk::Buffer write_handle(const std::string &name) const
Handle a stage should write the named field to.
std::unordered_map< std::string, Field > m_fields
TransferHandle m_pending_transfer
In-flight seed upload, resolved before the next transfer.
uint32_t get_cell_count() const
Total cell count.
std::optional< SurfaceConfig > m_surface
std::vector< std::string > get_field_names() const
Names of every declared field, in declaration order.
FlowStages setup_flow(const FlowConfig &config)
Build and append the incompressible flow stages.
static size_t surface_storage_bytes(const std::optional< SurfaceConfig > &surface)
Bytes of vertex storage the extraction resolution requires.
void seed(const std::string &name, const Kinesis::SpatialField &field)
Write initial values into a scalar field from a Kinesis field.
std::shared_ptr< VKBuffer > m_transfer_staging
Reused across seed and read calls.
void accumulate(const std::string &name, const Kinesis::SpatialField &field)
Add sampled values into a scalar field.
std::shared_ptr< VolumeSurfaceProcessor > m_surface_processor
std::vector< std::string > m_field_order
vk::Buffer read_handle(const std::string &name) const
Handle a stage should read the named field from.
void setup_processors(ProcessingToken token) override
Establish the processing chain without attaching any stage.
void read_field(const std::string &name, void *data, size_t size)
Copy the current read slot of a field to host memory.
VectorRef declare_vector(std::string name)
Declare a double-buffered vector field.
ScalarRef declare_scalar(std::string name)
Declare a double-buffered scalar field.
bool allocate_field(const std::string &name, size_t stride_bytes, bool double_buffered)
Allocate one field's slots and register it.
bool has_field(const std::string &name) const
Whether a field of this name was declared.
void setup_rendering(const RenderConfig &config)
Attach a RenderProcessor drawing the extracted surface.
VolumeGridBuffer(Kinesis::Lattice3D lattice, std::initializer_list< FieldDecl > fields, std::optional< SurfaceConfig > surface=std::nullopt)
Construct an unregistered multi-field volume.
ScalarRef declare_scratch(std::string name)
Declare a single-slot scalar field.
void seed_raw(const std::string &name, const void *data, size_t size)
Write initial values into a field from raw host memory.
std::shared_ptr< VKBuffer > m_counter_buf
Atomic vertex counter for the extraction stage.
size_t get_field_bytes(const std::string &name) const
Byte size of one slot of the named field, or 0 if undeclared.
void allocate_fields(const std::vector< FieldDecl > &decls)
Populate m_fields from declarations.
std::shared_ptr< SDFMeshProcessor > m_mesh_processor
void swap_field(const std::string &name)
Exchange read and write slots for the named field.
GPU-resident state for multi-field simulations evaluated over a fixed 3D topology: incompressible flu...
Interface * get_service()
Query for a backend service.
static BackendRegistry & instance()
Get the global registry instance.
void resolve_transfer(TransferHandle &handle)
Wait for a transfer and release its resources.
TransferHandle upload_back_buffer_async(const VKBufferResources::GenerationSlot &slot, const void *data, size_t size, std::shared_ptr< VKBuffer > &staging)
Upload to a back_buffers slot without waiting for completion.
ProcessingToken
Bitfield enum defining processing characteristics and backend requirements for buffer operations.
void download_back_buffer(const VKBufferResources::GenerationSlot &slot, void *data, size_t size, std::shared_ptr< VKBuffer > &staging)
Download a raw back_buffers slot to host memory.
@ BufferManagement
Buffer Management (Buffers::BufferManager, creating buffers)
@ Init
Engine/subsystem initialization.
@ Buffers
Buffers, Managers, processors and processing chains.
@ UNKNOWN
Unknown or undefined modality.
BufferUsageHint
Semantic usage hint for buffer allocation and memory properties.
A scalar field's name, issued by the volume that owns it.
std::vector< GenerationSlot > back_buffers
Definition VKBuffer.hpp:48
A vector field's name, issued by the volume that owns it.
One field's declaration within a volume.
float time_step
Applied to every stage that integrates.
bool walls
Free-slip on the six faces, twice per cycle.
VectorRef scratch
Required only when viscosity is above zero.
float viscosity
Zero omits the diffusion stage entirely.
ScalarRef divergence
Required. Single-slot is correct.
Parameters for the incompressible flow stage arrangement.
std::shared_ptr< AdvectProcessor > self_advect
std::shared_ptr< PressureProcessor > pressure
std::shared_ptr< WallProcessor > wall_projected
std::vector< std::shared_ptr< AdvectProcessor > > carriers
Parallel to config.carried.
std::shared_ptr< DiffuseProcessor > diffuse
std::shared_ptr< DivergenceProcessor > divergence
std::shared_ptr< SolenoidalProcessor > solenoidal
std::shared_ptr< BuoyancyProcessor > buoyancy
Every stage setup_flow built, in no particular order.
Vertex type for indexed triangle mesh primitives (TRIANGLE_LIST topology)
static VertexLayout for_meshes(uint32_t stride=60)
Factory: layout for MeshVertex (position, color, weight, uv, normal, tangent)
Lattice3D resampled(const glm::uvec3 &res) const noexcept
A lattice over the same bounds at a different resolution.
Definition Lattice.hpp:103
AABB3D bounds
Continuous extent subdivided.
Definition Lattice.hpp:27
size_t cell_count() const noexcept
Total cell count.
Definition Lattice.hpp:36
glm::uvec3 resolution
Cell count per axis. Zero on any axis is invalid.
Definition Lattice.hpp:26
glm::vec3 cell_center(const glm::uvec3 &c) const noexcept
World position of a cell's centre.
Definition Lattice.hpp:70
A regular subdivision of an AABB3D into a cell count per axis.
Definition Lattice.hpp:25
Typed, composable, stateless callable from domain D to range R.
Definition Tendency.hpp:22
Unified rendering configuration for graphics buffers.
Backend buffer management service interface.