2024-02-21 08:30:15 +00:00
|
|
|
use glam::f32::Vec2;
|
2024-03-13 16:37:28 +00:00
|
|
|
use rand::random;
|
2022-10-27 18:59:47 +00:00
|
|
|
use std::sync::Arc;
|
2022-03-06 19:30:49 +00:00
|
|
|
use vulkano::{
|
2023-04-02 13:07:16 +00:00
|
|
|
buffer::{Buffer, BufferCreateInfo, BufferUsage, Subbuffer},
|
2022-10-05 09:09:26 +00:00
|
|
|
command_buffer::{
|
2024-10-19 12:13:15 +00:00
|
|
|
allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder, CommandBufferUsage,
|
2022-10-05 09:09:26 +00:00
|
|
|
},
|
|
|
|
descriptor_set::{
|
2023-11-12 16:17:37 +00:00
|
|
|
allocator::StandardDescriptorSetAllocator, DescriptorSet, WriteDescriptorSet,
|
2022-10-05 09:09:26 +00:00
|
|
|
},
|
2022-03-06 19:30:49 +00:00
|
|
|
device::Queue,
|
2023-07-03 20:37:29 +00:00
|
|
|
image::view::ImageView,
|
2023-07-19 09:11:17 +00:00
|
|
|
memory::allocator::{AllocationCreateInfo, MemoryTypeFilter, StandardMemoryAllocator},
|
2023-04-22 18:46:22 +00:00
|
|
|
pipeline::{
|
|
|
|
compute::ComputePipelineCreateInfo, layout::PipelineDescriptorSetLayoutCreateInfo,
|
|
|
|
ComputePipeline, Pipeline, PipelineBindPoint, PipelineLayout,
|
2023-06-25 18:08:27 +00:00
|
|
|
PipelineShaderStageCreateInfo,
|
2023-04-22 18:46:22 +00:00
|
|
|
},
|
2022-03-06 19:30:49 +00:00
|
|
|
sync::GpuFuture,
|
|
|
|
};
|
2021-09-13 15:14:54 +00:00
|
|
|
|
|
|
|
pub struct FractalComputePipeline {
|
2022-06-24 15:27:33 +00:00
|
|
|
queue: Arc<Queue>,
|
2021-09-13 15:14:54 +00:00
|
|
|
pipeline: Arc<ComputePipeline>,
|
2022-10-26 14:25:01 +00:00
|
|
|
memory_allocator: Arc<StandardMemoryAllocator>,
|
2022-10-27 18:59:47 +00:00
|
|
|
command_buffer_allocator: Arc<StandardCommandBufferAllocator>,
|
|
|
|
descriptor_set_allocator: Arc<StandardDescriptorSetAllocator>,
|
2023-01-12 12:56:10 +00:00
|
|
|
palette: Subbuffer<[[f32; 4]]>,
|
2022-03-06 19:30:49 +00:00
|
|
|
palette_size: i32,
|
2021-09-13 15:14:54 +00:00
|
|
|
end_color: [f32; 4],
|
|
|
|
}
|
|
|
|
|
|
|
|
impl FractalComputePipeline {
|
2022-10-05 09:09:26 +00:00
|
|
|
pub fn new(
|
|
|
|
queue: Arc<Queue>,
|
2022-10-26 14:25:01 +00:00
|
|
|
memory_allocator: Arc<StandardMemoryAllocator>,
|
2022-10-27 18:59:47 +00:00
|
|
|
command_buffer_allocator: Arc<StandardCommandBufferAllocator>,
|
|
|
|
descriptor_set_allocator: Arc<StandardDescriptorSetAllocator>,
|
2022-10-05 09:09:26 +00:00
|
|
|
) -> FractalComputePipeline {
|
2023-03-05 18:56:35 +00:00
|
|
|
// Initial colors.
|
2021-09-13 15:14:54 +00:00
|
|
|
let colors = vec![
|
|
|
|
[1.0, 0.0, 0.0, 1.0],
|
|
|
|
[1.0, 1.0, 0.0, 1.0],
|
|
|
|
[0.0, 1.0, 0.0, 1.0],
|
|
|
|
[0.0, 1.0, 1.0, 1.0],
|
|
|
|
[0.0, 0.0, 1.0, 1.0],
|
|
|
|
[1.0, 0.0, 1.0, 1.0],
|
|
|
|
];
|
2022-03-06 19:30:49 +00:00
|
|
|
let palette_size = colors.len() as i32;
|
2023-01-12 12:56:10 +00:00
|
|
|
let palette = Buffer::from_iter(
|
2023-09-03 11:09:07 +00:00
|
|
|
memory_allocator.clone(),
|
2023-04-02 13:07:16 +00:00
|
|
|
BufferCreateInfo {
|
|
|
|
usage: BufferUsage::STORAGE_BUFFER,
|
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
AllocationCreateInfo {
|
2023-07-19 09:11:17 +00:00
|
|
|
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
|
|
|
|
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
2023-01-12 12:56:10 +00:00
|
|
|
..Default::default()
|
|
|
|
},
|
2022-03-06 19:30:49 +00:00
|
|
|
colors,
|
2021-09-13 15:14:54 +00:00
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
let end_color = [0.0; 4];
|
|
|
|
|
|
|
|
let pipeline = {
|
2023-04-22 18:46:22 +00:00
|
|
|
let device = queue.device();
|
|
|
|
let cs = cs::load(device.clone())
|
2023-04-18 18:53:08 +00:00
|
|
|
.unwrap()
|
|
|
|
.entry_point("main")
|
|
|
|
.unwrap();
|
2023-06-25 18:08:27 +00:00
|
|
|
let stage = PipelineShaderStageCreateInfo::new(cs);
|
2023-04-22 18:46:22 +00:00
|
|
|
let layout = PipelineLayout::new(
|
|
|
|
device.clone(),
|
|
|
|
PipelineDescriptorSetLayoutCreateInfo::from_stages([&stage])
|
|
|
|
.into_pipeline_layout_create_info(device.clone())
|
|
|
|
.unwrap(),
|
|
|
|
)
|
|
|
|
.unwrap();
|
2021-11-02 20:33:58 +00:00
|
|
|
ComputePipeline::new(
|
2023-04-22 18:46:22 +00:00
|
|
|
device.clone(),
|
2021-11-02 20:33:58 +00:00
|
|
|
None,
|
2023-04-22 18:46:22 +00:00
|
|
|
ComputePipelineCreateInfo::stage_layout(stage, layout),
|
2021-09-13 15:14:54 +00:00
|
|
|
)
|
2021-11-02 20:33:58 +00:00
|
|
|
.unwrap()
|
2021-09-13 15:14:54 +00:00
|
|
|
};
|
2022-10-05 09:09:26 +00:00
|
|
|
|
2021-09-13 15:14:54 +00:00
|
|
|
FractalComputePipeline {
|
2022-06-24 15:27:33 +00:00
|
|
|
queue,
|
2021-09-13 15:14:54 +00:00
|
|
|
pipeline,
|
2022-10-26 14:25:01 +00:00
|
|
|
memory_allocator,
|
2022-10-05 09:09:26 +00:00
|
|
|
command_buffer_allocator,
|
|
|
|
descriptor_set_allocator,
|
2021-09-13 15:14:54 +00:00
|
|
|
palette,
|
2022-03-06 19:30:49 +00:00
|
|
|
palette_size,
|
2021-09-13 15:14:54 +00:00
|
|
|
end_color,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-05 18:56:35 +00:00
|
|
|
/// Randomizes our color palette.
|
2021-09-13 15:14:54 +00:00
|
|
|
pub fn randomize_palette(&mut self) {
|
|
|
|
let mut colors = vec![];
|
2022-03-06 19:30:49 +00:00
|
|
|
for _ in 0..self.palette_size {
|
2024-03-13 16:37:28 +00:00
|
|
|
let r = random::<f32>();
|
|
|
|
let g = random::<f32>();
|
|
|
|
let b = random::<f32>();
|
|
|
|
let a = random::<f32>();
|
2021-09-13 15:14:54 +00:00
|
|
|
colors.push([r, g, b, a]);
|
|
|
|
}
|
2023-01-12 12:56:10 +00:00
|
|
|
self.palette = Buffer::from_iter(
|
2023-09-03 11:09:07 +00:00
|
|
|
self.memory_allocator.clone(),
|
2023-04-02 13:07:16 +00:00
|
|
|
BufferCreateInfo {
|
|
|
|
usage: BufferUsage::STORAGE_BUFFER,
|
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
AllocationCreateInfo {
|
2023-07-19 09:11:17 +00:00
|
|
|
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
|
|
|
|
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
2023-01-12 12:56:10 +00:00
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
colors,
|
2021-09-13 15:14:54 +00:00
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn compute(
|
2022-10-05 09:09:26 +00:00
|
|
|
&self,
|
2023-07-06 08:52:11 +00:00
|
|
|
image_view: Arc<ImageView>,
|
2024-02-21 08:30:15 +00:00
|
|
|
c: Vec2,
|
|
|
|
scale: Vec2,
|
|
|
|
translation: Vec2,
|
2021-09-13 15:14:54 +00:00
|
|
|
max_iters: u32,
|
|
|
|
is_julia: bool,
|
|
|
|
) -> Box<dyn GpuFuture> {
|
2023-03-05 18:56:35 +00:00
|
|
|
// Resize image if needed.
|
2023-07-06 08:52:11 +00:00
|
|
|
let image_extent = image_view.image().extent();
|
2024-10-10 10:16:14 +00:00
|
|
|
let layout = &self.pipeline.layout().set_layouts()[0];
|
|
|
|
let descriptor_set = DescriptorSet::new(
|
2023-11-12 14:10:22 +00:00
|
|
|
self.descriptor_set_allocator.clone(),
|
2024-10-10 10:16:14 +00:00
|
|
|
layout.clone(),
|
2021-12-18 10:32:38 +00:00
|
|
|
[
|
2023-07-06 08:52:11 +00:00
|
|
|
WriteDescriptorSet::image_view(0, image_view),
|
2021-12-18 10:32:38 +00:00
|
|
|
WriteDescriptorSet::buffer(1, self.palette.clone()),
|
|
|
|
],
|
2023-06-03 12:56:27 +00:00
|
|
|
[],
|
2021-12-18 10:32:38 +00:00
|
|
|
)
|
|
|
|
.unwrap();
|
2024-10-19 12:13:15 +00:00
|
|
|
let mut builder = AutoCommandBufferBuilder::primary(
|
2023-11-14 16:57:43 +00:00
|
|
|
self.command_buffer_allocator.clone(),
|
2022-09-10 06:00:08 +00:00
|
|
|
self.queue.queue_family_index(),
|
2024-10-18 18:00:21 +00:00
|
|
|
CommandBufferUsage::OneTimeSubmit,
|
2021-09-13 15:14:54 +00:00
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
|
2023-03-05 18:56:35 +00:00
|
|
|
let push_constants = cs::PushConstants {
|
|
|
|
end_color: self.end_color,
|
2021-09-13 15:14:54 +00:00
|
|
|
c: c.into(),
|
|
|
|
scale: scale.into(),
|
|
|
|
translation: translation.into(),
|
2022-03-06 19:30:49 +00:00
|
|
|
palette_size: self.palette_size,
|
2021-09-13 15:14:54 +00:00
|
|
|
max_iters: max_iters as i32,
|
|
|
|
is_julia: is_julia as u32,
|
|
|
|
};
|
2023-12-25 03:01:16 +00:00
|
|
|
|
2021-09-13 15:14:54 +00:00
|
|
|
builder
|
|
|
|
.bind_pipeline_compute(self.pipeline.clone())
|
2023-08-04 18:55:16 +00:00
|
|
|
.unwrap()
|
2024-10-10 10:16:14 +00:00
|
|
|
.bind_descriptor_sets(
|
|
|
|
PipelineBindPoint::Compute,
|
|
|
|
self.pipeline.layout().clone(),
|
|
|
|
0,
|
|
|
|
descriptor_set,
|
|
|
|
)
|
2023-08-04 18:55:16 +00:00
|
|
|
.unwrap()
|
2024-10-10 10:16:14 +00:00
|
|
|
.push_constants(self.pipeline.layout().clone(), 0, push_constants)
|
2021-09-13 15:14:54 +00:00
|
|
|
.unwrap();
|
2024-10-23 10:07:00 +00:00
|
|
|
unsafe { builder.dispatch([image_extent[0] / 8, image_extent[1] / 8, 1]) }.unwrap();
|
2023-12-25 03:01:16 +00:00
|
|
|
|
2024-10-19 12:13:15 +00:00
|
|
|
let command_buffer = builder.build().unwrap();
|
2022-06-24 15:27:33 +00:00
|
|
|
let finished = command_buffer.execute(self.queue.clone()).unwrap();
|
2021-09-13 15:14:54 +00:00
|
|
|
finished.then_signal_fence_and_flush().unwrap().boxed()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
mod cs {
|
|
|
|
vulkano_shaders::shader! {
|
|
|
|
ty: "compute",
|
2023-03-05 18:56:35 +00:00
|
|
|
src: r"
|
|
|
|
#version 450
|
2021-09-13 15:14:54 +00:00
|
|
|
|
2023-03-05 18:56:35 +00:00
|
|
|
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
|
2021-09-13 15:14:54 +00:00
|
|
|
|
2023-03-05 18:56:35 +00:00
|
|
|
// Image to which we'll write our fractal
|
|
|
|
layout(set = 0, binding = 0, rgba8) uniform writeonly image2D img;
|
2021-09-13 15:14:54 +00:00
|
|
|
|
2023-03-05 18:56:35 +00:00
|
|
|
// Our palette as a dynamic buffer
|
|
|
|
layout(set = 0, binding = 1) buffer Palette {
|
|
|
|
vec4 data[];
|
|
|
|
} palette;
|
2021-09-13 15:14:54 +00:00
|
|
|
|
2023-03-05 18:56:35 +00:00
|
|
|
// Our variable inputs as push constants
|
|
|
|
layout(push_constant) uniform PushConstants {
|
|
|
|
vec4 end_color;
|
|
|
|
vec2 c;
|
|
|
|
vec2 scale;
|
|
|
|
vec2 translation;
|
|
|
|
int palette_size;
|
|
|
|
int max_iters;
|
|
|
|
bool is_julia;
|
|
|
|
} push_constants;
|
2021-09-13 15:14:54 +00:00
|
|
|
|
2023-03-05 18:56:35 +00:00
|
|
|
// Gets smooth color between current color (determined by iterations) and the next
|
|
|
|
// color in the palette by linearly interpolating the colors based on:
|
|
|
|
// https://linas.org/art-gallery/escape/smooth.html
|
|
|
|
vec4 get_color(
|
|
|
|
int palette_size,
|
|
|
|
vec4 end_color,
|
|
|
|
int i,
|
|
|
|
int max_iters,
|
|
|
|
float len_z
|
|
|
|
) {
|
|
|
|
if (i < max_iters) {
|
|
|
|
float iters_float = float(i) + 1.0 - log(log(len_z)) / log(2.0f);
|
|
|
|
float iters_floor = floor(iters_float);
|
|
|
|
float remainder = iters_float - iters_floor;
|
|
|
|
vec4 color_start = palette.data[int(iters_floor) % push_constants.palette_size];
|
|
|
|
vec4 color_end = palette.data[(int(iters_floor) + 1) % push_constants.palette_size];
|
|
|
|
return mix(color_start, color_end, remainder);
|
|
|
|
}
|
|
|
|
return end_color;
|
|
|
|
}
|
2021-09-13 15:14:54 +00:00
|
|
|
|
2023-03-05 18:56:35 +00:00
|
|
|
void main() {
|
|
|
|
// Scale image pixels to range
|
|
|
|
vec2 dims = vec2(imageSize(img));
|
|
|
|
float ar = dims.x / dims.y;
|
|
|
|
float x_over_width = (gl_GlobalInvocationID.x / dims.x);
|
|
|
|
float y_over_height = (gl_GlobalInvocationID.y / dims.y);
|
|
|
|
float x0 = ar * (push_constants.translation.x + (x_over_width - 0.5) * push_constants.scale.x);
|
|
|
|
float y0 = push_constants.translation.y + (y_over_height - 0.5) * push_constants.scale.y;
|
2021-09-13 15:14:54 +00:00
|
|
|
|
2023-03-05 18:56:35 +00:00
|
|
|
// Julia is like mandelbrot, but instead changing the constant `c` will change the
|
|
|
|
// shape you'll see. Thus we want to bind the c to mouse position.
|
|
|
|
// With mandelbrot, c = scaled xy position of the image. Z starts from zero.
|
|
|
|
// With julia, c = any value between the interesting range (-2.0 - 2.0),
|
|
|
|
// Z = scaled xy position of the image.
|
|
|
|
vec2 c;
|
|
|
|
vec2 z;
|
|
|
|
if (push_constants.is_julia) {
|
|
|
|
c = push_constants.c;
|
|
|
|
z = vec2(x0, y0);
|
|
|
|
} else {
|
|
|
|
c = vec2(x0, y0);
|
|
|
|
z = vec2(0.0, 0.0);
|
|
|
|
}
|
2021-09-13 15:14:54 +00:00
|
|
|
|
2023-03-05 18:56:35 +00:00
|
|
|
// Escape time algorithm:
|
|
|
|
// https://en.wikipedia.org/wiki/Plotting_algorithms_for_the_Mandelbrot_set
|
|
|
|
// It's an iterative algorithm where the bailout point (number of iterations) will
|
|
|
|
// determine the color we choose from the palette.
|
|
|
|
int i;
|
|
|
|
float len_z;
|
|
|
|
for (i = 0; i < push_constants.max_iters; i += 1) {
|
|
|
|
z = vec2(
|
|
|
|
z.x * z.x - z.y * z.y + c.x,
|
|
|
|
z.y * z.x + z.x * z.y + c.y
|
|
|
|
);
|
2021-09-13 15:14:54 +00:00
|
|
|
|
2023-03-05 18:56:35 +00:00
|
|
|
len_z = length(z);
|
|
|
|
// Using 8.0 for bailout limit give a little nicer colors with smooth colors
|
|
|
|
// 2.0 is enough to 'determine' an escape will happen.
|
|
|
|
if (len_z > 8.0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2021-09-13 15:14:54 +00:00
|
|
|
|
2023-03-05 18:56:35 +00:00
|
|
|
vec4 write_color = get_color(
|
|
|
|
push_constants.palette_size,
|
|
|
|
push_constants.end_color,
|
|
|
|
i,
|
|
|
|
push_constants.max_iters,
|
|
|
|
len_z
|
|
|
|
);
|
|
|
|
imageStore(img, ivec2(gl_GlobalInvocationID.xy), write_color);
|
|
|
|
}
|
|
|
|
",
|
2021-09-13 15:14:54 +00:00
|
|
|
}
|
|
|
|
}
|