Skip to content

Commit df3d073

Browse files
committed
Implement GPU compute-driven Bioluminescent Waves visualizer
1 parent 41bb593 commit df3d073

4 files changed

Lines changed: 431 additions & 19 deletions

File tree

src/engine.rs

Lines changed: 182 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,16 @@ pub struct VulkanEngine<'a> {
238238
ferrofluidsim_compute_pipeline: wgpu::ComputePipeline,
239239
ferrofluidsim_clear_pipeline: wgpu::ComputePipeline,
240240
ferrofluidsim_bind_group: wgpu::BindGroup,
241+
242+
// GPU compute bioluminescent waves simulation
243+
#[allow(dead_code)]
244+
biolum_particles_buffer: wgpu::Buffer,
245+
biolum_compute_pipeline: wgpu::ComputePipeline,
246+
biolum_compute_bind_group: wgpu::BindGroup,
247+
biolum_render_bind_group: wgpu::BindGroup,
248+
#[allow(dead_code)]
249+
biolum_render_pipeline_layout: wgpu::PipelineLayout,
250+
241251
#[allow(dead_code)]
242252
pub start_time: std::time::Instant,
243253

@@ -642,6 +652,33 @@ impl<'a> VulkanEngine<'a> {
642652
immediate_size: 0,
643653
});
644654

655+
let biolum_render_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
656+
label: Some("Biolum Render Layout"),
657+
entries: &[
658+
wgpu::BindGroupLayoutEntry {
659+
binding: 0,
660+
visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
661+
ty: wgpu::BindingType::Buffer {
662+
ty: wgpu::BufferBindingType::Storage { read_only: true },
663+
has_dynamic_offset: false,
664+
min_binding_size: None,
665+
},
666+
count: None,
667+
},
668+
],
669+
});
670+
671+
let biolum_render_pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
672+
label: Some("Biolum Render Pipeline Layout"),
673+
bind_group_layouts: &[
674+
Some(&bind_group_layout),
675+
Some(&smoke_render_layout),
676+
Some(&camera_bind_group_layout),
677+
Some(&biolum_render_bind_group_layout),
678+
],
679+
immediate_size: 0,
680+
});
681+
645682
// Shared shader headers — included at compile time, resolved via simple string replacement.
646683
// This is the single source of truth for AudioUniforms layout and glyph font.
647684
const SHADER_COMMON: &str = include_str!("shaders/_common.wgsl");
@@ -674,6 +711,7 @@ impl<'a> VulkanEngine<'a> {
674711
16 => include_str!("shaders/vis_synthwaveracer.wgsl"),
675712
17 => include_str!("shaders/vis_cuboids.wgsl"),
676713
18 => include_str!("shaders/vis_vumeters.wgsl"),
714+
19 => include_str!("shaders/vis_bioluminescence.wgsl"),
677715
_ => include_str!("shaders/vis_spectrum.wgsl"),
678716
}
679717
};
@@ -744,23 +782,9 @@ impl<'a> VulkanEngine<'a> {
744782
source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(source.as_str())),
745783
});
746784

747-
let (layout, vertex_buffers, primitive, vs_entry) = match vis_def.pipeline_type {
748-
crate::state::PipelineType::FullscreenQuad => (
749-
&render_pipeline_layout,
750-
Vec::new(),
751-
wgpu::PrimitiveState {
752-
topology: wgpu::PrimitiveTopology::TriangleList,
753-
strip_index_format: None,
754-
front_face: wgpu::FrontFace::Ccw,
755-
cull_mode: None,
756-
polygon_mode: wgpu::PolygonMode::Fill,
757-
unclipped_depth: false,
758-
conservative: false,
759-
},
760-
"vs_main"
761-
),
762-
crate::state::PipelineType::Mesh3D { .. } => (
763-
&render_pipeline_layout_3d,
785+
let (layout, vertex_buffers, primitive, vs_entry) = if vis_def.id == 19 {
786+
(
787+
&biolum_render_pipeline_layout,
764788
vec![Vertex::desc()],
765789
wgpu::PrimitiveState {
766790
topology: wgpu::PrimitiveTopology::TriangleList,
@@ -772,7 +796,38 @@ impl<'a> VulkanEngine<'a> {
772796
conservative: false,
773797
},
774798
"vs_main_3d"
775-
),
799+
)
800+
} else {
801+
match vis_def.pipeline_type {
802+
crate::state::PipelineType::FullscreenQuad => (
803+
&render_pipeline_layout,
804+
Vec::new(),
805+
wgpu::PrimitiveState {
806+
topology: wgpu::PrimitiveTopology::TriangleList,
807+
strip_index_format: None,
808+
front_face: wgpu::FrontFace::Ccw,
809+
cull_mode: None,
810+
polygon_mode: wgpu::PolygonMode::Fill,
811+
unclipped_depth: false,
812+
conservative: false,
813+
},
814+
"vs_main"
815+
),
816+
crate::state::PipelineType::Mesh3D { .. } => (
817+
&render_pipeline_layout_3d,
818+
vec![Vertex::desc()],
819+
wgpu::PrimitiveState {
820+
topology: wgpu::PrimitiveTopology::TriangleList,
821+
strip_index_format: None,
822+
front_face: wgpu::FrontFace::Ccw,
823+
cull_mode: None,
824+
polygon_mode: wgpu::PolygonMode::Fill,
825+
unclipped_depth: false,
826+
conservative: false,
827+
},
828+
"vs_main_3d"
829+
),
830+
}
776831
};
777832

778833
let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
@@ -1495,6 +1550,89 @@ impl<'a> VulkanEngine<'a> {
14951550
label: Some("Ferrofluid Clear"), layout: Some(&ferrofluidsim_pipeline_layout), module: &ferrofluidsim_compute_shader, entry_point: Some("clear"), compilation_options: Default::default(), cache: None,
14961551
});
14971552

1553+
// --- Bioluminescent Waves Compute & Render Setup ---
1554+
let biolum_particles_buffer = device.create_buffer(&wgpu::BufferDescriptor {
1555+
label: Some("Bioluminescent Particles"),
1556+
size: (65536 * 32) as u64, // 65,536 particles * 32 bytes/particle
1557+
usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
1558+
mapped_at_creation: false,
1559+
});
1560+
queue.write_buffer(&biolum_particles_buffer, 0, &vec![0u8; 65536 * 32]);
1561+
1562+
let biolum_compute_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
1563+
label: Some("Biolum Compute Layout"),
1564+
entries: &[
1565+
wgpu::BindGroupLayoutEntry {
1566+
binding: 0,
1567+
visibility: wgpu::ShaderStages::COMPUTE,
1568+
ty: wgpu::BindingType::Buffer {
1569+
ty: wgpu::BufferBindingType::Uniform,
1570+
has_dynamic_offset: false,
1571+
min_binding_size: None,
1572+
},
1573+
count: None,
1574+
},
1575+
wgpu::BindGroupLayoutEntry {
1576+
binding: 1,
1577+
visibility: wgpu::ShaderStages::COMPUTE,
1578+
ty: wgpu::BindingType::Buffer {
1579+
ty: wgpu::BufferBindingType::Storage { read_only: false },
1580+
has_dynamic_offset: false,
1581+
min_binding_size: None,
1582+
},
1583+
count: None,
1584+
},
1585+
],
1586+
});
1587+
1588+
let biolum_compute_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
1589+
label: Some("Biolum Compute Bind Group"),
1590+
layout: &biolum_compute_layout,
1591+
entries: &[
1592+
wgpu::BindGroupEntry {
1593+
binding: 0,
1594+
resource: uniform_buffer.as_entire_binding(),
1595+
},
1596+
wgpu::BindGroupEntry {
1597+
binding: 1,
1598+
resource: biolum_particles_buffer.as_entire_binding(),
1599+
},
1600+
],
1601+
});
1602+
1603+
let biolum_compute_source = resolve_shader_includes(include_str!("shaders/biolum_compute.wgsl"));
1604+
let biolum_compute_shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
1605+
label: Some("Biolum Compute Shader"),
1606+
source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(&biolum_compute_source)),
1607+
});
1608+
1609+
let biolum_compute_pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
1610+
label: Some("Biolum Compute Pipeline Layout"),
1611+
bind_group_layouts: &[Some(&biolum_compute_layout)],
1612+
immediate_size: 0,
1613+
});
1614+
1615+
let biolum_compute_pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
1616+
label: Some("Biolum Compute Pipeline"),
1617+
layout: Some(&biolum_compute_pipeline_layout),
1618+
module: &biolum_compute_shader,
1619+
entry_point: Some("main"),
1620+
compilation_options: Default::default(),
1621+
cache: None,
1622+
});
1623+
1624+
let biolum_render_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
1625+
label: Some("Biolum Render Bind Group"),
1626+
layout: &biolum_render_bind_group_layout,
1627+
entries: &[
1628+
wgpu::BindGroupEntry {
1629+
binding: 0,
1630+
resource: biolum_particles_buffer.as_entire_binding(),
1631+
},
1632+
],
1633+
});
1634+
// --- End Bioluminescent Waves Setup ---
1635+
14981636
let mut query_set = None;
14991637
let mut query_resolve_buffer = None;
15001638
let mut query_read_buffer = None;
@@ -1830,6 +1968,13 @@ impl<'a> VulkanEngine<'a> {
18301968
ferrofluidsim_compute_pipeline,
18311969
ferrofluidsim_clear_pipeline,
18321970
ferrofluidsim_bind_group,
1971+
1972+
biolum_particles_buffer,
1973+
biolum_compute_pipeline,
1974+
biolum_compute_bind_group,
1975+
biolum_render_bind_group,
1976+
biolum_render_pipeline_layout,
1977+
18331978
start_time: std::time::Instant::now(),
18341979
fft_compute_pipeline,
18351980
fft_bind_group,
@@ -3937,6 +4082,17 @@ impl<'a> VulkanEngine<'a> {
39374082
compute_pass.dispatch_workgroups(391, 1, 1);
39384083
}
39394084

4085+
if vis_def.id == 19 {
4086+
println!("DEBUG: Bioluminescence Waves Sim Compute Dispatching!");
4087+
let mut compute_pass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {
4088+
label: Some("Bioluminescence Waves Sim Compute"),
4089+
timestamp_writes: None,
4090+
});
4091+
compute_pass.set_pipeline(&self.biolum_compute_pipeline);
4092+
compute_pass.set_bind_group(0, Some(&self.biolum_compute_bind_group), &[]);
4093+
compute_pass.dispatch_workgroups(256, 1, 1);
4094+
}
4095+
39404096
{
39414097
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
39424098
label: Some("Main Render Pass"),
@@ -3945,7 +4101,11 @@ impl<'a> VulkanEngine<'a> {
39454101
resolve_target: None,
39464102
depth_slice: None,
39474103
ops: wgpu::Operations {
3948-
load: wgpu::LoadOp::Clear(wgpu::Color { r: 0.1, g: 0.1, b: 0.1, a: 1.0 }),
4104+
load: wgpu::LoadOp::Clear(if vis_def.id == 19 {
4105+
wgpu::Color { r: 0.001, g: 0.003, b: 0.008, a: 1.0 }
4106+
} else {
4107+
wgpu::Color { r: 0.1, g: 0.1, b: 0.1, a: 1.0 }
4108+
}),
39494109
store: wgpu::StoreOp::Store,
39504110
},
39514111
})],
@@ -4014,6 +4174,9 @@ impl<'a> VulkanEngine<'a> {
40144174
},
40154175
crate::state::PipelineType::Mesh3D { geometry, instances } => {
40164176
render_pass.set_bind_group(2, &self.camera_bind_group, &[]);
4177+
if vis_def.id == 19 {
4178+
render_pass.set_bind_group(3, &self.biolum_render_bind_group, &[]);
4179+
}
40174180
if let Some(mesh) = self.mesh_registry.get(geometry) {
40184181
render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
40194182
render_pass.set_index_buffer(mesh.index_buffer.slice(..), wgpu::IndexFormat::Uint32);

0 commit comments

Comments
 (0)