From 663dbad1ba58d207be2b09ec450d93cd2c3b9306 Mon Sep 17 00:00:00 2001 From: Clemens-Dautermann Date: Sun, 11 Apr 2021 22:21:30 +0200 Subject: [PATCH] added GPU spport with vulkano --- src/main.rs | 32 ++++-- src/mandelbrot_shader.cs | 37 +++++++ src/vulkan.rs | 203 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 262 insertions(+), 10 deletions(-) create mode 100644 src/mandelbrot_shader.cs create mode 100644 src/vulkan.rs diff --git a/src/main.rs b/src/main.rs index f56d776..31d8eff 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,13 +1,17 @@ +mod vulkan; + extern crate image; extern crate num_complex; +extern crate num_traits; use image::{RgbImage, ImageBuffer, Rgb}; use std::fmt::Error; use num_complex::Complex; use indicatif::ProgressBar; use rayon::prelude::*; +use num_traits::MulAddAssign; -const THRESHHOLD: f64 = 200_i32.pow(2) as f64; +const THRESHHOLD: f64 = 400.0; struct PixelInfo { x: u32, @@ -20,10 +24,9 @@ fn calculate_color(number: Complex, iterations: u32) -> Result, Err let mut cycle = Vec::with_capacity(iterations as usize); for i in 0..iterations { - z = z * z + number; + z.mul_add_assign(z, number); if cycle.contains(&z) { return Ok(Rgb::from([0, 0, 0])); } - if z.norm_sqr() > THRESHHOLD { return Ok(map_num_to_color(i, iterations)); } @@ -135,6 +138,8 @@ fn render_sequence(end_frame: u32, let mut scale = initial_size * (1.0 - scale_factor).powi(start_frame as i32) as f64; for frame in start_frame..end_frame { + print!("\x1B[2J\x1B[1;1H"); + println!("Rendering frame {}...", frame); let fractal: RgbImage = render_around_point(image_size, iterations, scale, @@ -145,18 +150,25 @@ fn render_sequence(end_frame: u32, let filename: String = path.clone() + "frame_" + &*frame.to_string() + ".png"; fractal.save(filename).unwrap(); - println!("Rendered frame {}", frame); } } fn main() { - render_sequence(240, + /* + //special point: + let point = Complex::new(-0.743643887037158704752191506114774, + 0.131825904205311970493132056385139); + + render_sequence(1, 0, - 500, - 200, - 2.0, - Complex::new(-0.761574, -0.0847596), + 512, + 1000, + 1.0, + point, 1.0 / 10.0, - "./out/".parse().unwrap(), + "./test_".parse().unwrap(), ); + */ + + vulkan::run(); } diff --git a/src/mandelbrot_shader.cs b/src/mandelbrot_shader.cs new file mode 100644 index 0000000..572eddf --- /dev/null +++ b/src/mandelbrot_shader.cs @@ -0,0 +1,37 @@ +#version 450 + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +layout(set = 0, binding = 0, rgba8) uniform writeonly image2D img; + +void main() { + vec2 norm_coordinates = (gl_GlobalInvocationID.xy + vec2(0.5)) / vec2(imageSize(img)); + vec2 c = (norm_coordinates - vec2(0.5)) * 2.0 - vec2(5.0, 0.0); + + vec2 z = vec2(0.0, 0.0); + float i; + for (i = 0.0; i < 1.0; i += 0.004) { + z = vec2( + z.x * z.x - z.y * z.y + c.x, + z.y * z.x + z.x * z.y + c.y + ); + + if (length(z) > 4.0) { + break; + } + } + + vec4 to_write; + + if (i <= 0.3){ + to_write = vec4(0.0, 0.0, i, 1.0); + }else if(i <= 0.6){ + to_write = vec4(0.0, i, 1.0, 1.0 ); + }else if(i + 0.004 >= 1.0){ + to_write = vec4(0.0, 0.0, 0.0, 1.0 ); + }else{ + to_write = vec4(i, 1.0, 1.0, 1.0 ); + } + + imageStore(img, ivec2(gl_GlobalInvocationID.xy), to_write); +} \ No newline at end of file diff --git a/src/vulkan.rs b/src/vulkan.rs new file mode 100644 index 0000000..2911273 --- /dev/null +++ b/src/vulkan.rs @@ -0,0 +1,203 @@ +use vulkano::instance::{Instance, PhysicalDevice}; +use vulkano::instance::InstanceExtensions; +use std::io; +use std::io::BufRead; +use vulkano::device::{Device, Features, DeviceExtensions}; +use std::sync::Arc; +use vulkano::image::{StorageImage, ImageDimensions}; +use vulkano::descriptor::descriptor_set::PersistentDescriptorSet; +use vulkano::format::Format; +use vulkano::buffer::BufferUsage; +use vulkano::command_buffer::{AutoCommandBufferBuilder, CommandBuffer}; +use vulkano::sync::GpuFuture; +use vulkano::pipeline::ComputePipeline; +use vulkano::buffer::CpuAccessibleBuffer; +use image::{ImageBuffer, Rgba}; +use vulkano::descriptor::PipelineLayoutAbstract; +use vulkano::image::view::ImageView; +use vulkano::memory::DedicatedAlloc::Image; + +const IMAGE_SIZE: u32 = 1024; + +pub fn run() { + //get a vulcan instance + let instance = Instance::new(None, &InstanceExtensions::none(), None) + .expect("failed to create instance"); + + //get all physical devices + let physical_devices = PhysicalDevice::enumerate(&instance); + + let device: PhysicalDevice; + let number: usize; + + //let the user select a device or just use the only available one + if physical_devices.len() != 1 { + println!("Available GPUs: "); + + for (n, dev) in physical_devices.enumerate() { + println!("{}: {}", n, dev.name()); + } + + println!("Enter number of GPU to use: "); + let input: String = io::stdin().lock().lines().next().unwrap().unwrap(); + number = input.parse::().unwrap(); + } else { + number = 0; + } + + //construct a physical device + device = PhysicalDevice::from_index(&instance, number).unwrap(); + println!("Using GPU \"{}\".", device.name()); + + //get the appropriate queue + let queue_family = device.queue_families() + .find(|&q| q.supports_compute()) + .expect("couldn't find a queue family supporting graphical and compute"); + + //construct the device itself + let (device, mut queues) = { + Device::new(device, &Features::none(), &DeviceExtensions::none(), + [(queue_family, 0.5)].iter().cloned()).expect("failed to create device") + }; + + //extract the queue + let queue = queues.next().unwrap(); + + //build shader + mod cs { + vulkano_shaders::shader! { + ty: "compute", + src: " +#version 450 + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +layout(push_constant) uniform PushConstantData { + float zoom_factor; + float re; + float im; +} pc; + +layout(set = 0, binding = 0, rgba8) uniform writeonly image2D img; + +void main() { + vec2 norm_coordinates = (gl_GlobalInvocationID.xy + vec2(0.5)) / vec2(imageSize(img)); + vec2 c = (norm_coordinates - vec2(0.5)) * pc.zoom_factor - vec2(pc.re, pc.im); + + vec2 z = vec2(0.0, 0.0); + float i; + for (i = 0.0; i < 1.0; i += 0.004) { + z = vec2( + z.x * z.x - z.y * z.y + c.x, + z.y * z.x + z.x * z.y + c.y + ); + + if (length(z) > 4.0) { + break; + } + } + + vec4 to_write; + + if (i <= 0.3){ + to_write = vec4(0.0, 0.0, i, 1.0); + }else if(i <= 0.6){ + to_write = vec4(0.0, i, 1.0, 1.0 ); + }else if(i + 0.004 >= 1.0){ + to_write = vec4(0.0, 0.0, 0.0, 1.0 ); + }else{ + to_write = vec4(i, 1.0, 1.0, 1.0 ); + } + + imageStore(img, ivec2(gl_GlobalInvocationID.xy), to_write); +} +" + } + } + + let shader = cs::Shader::load(device.clone()).expect("failed to create shader module"); + + let compute_pipeline = Arc::new( + ComputePipeline::new(device.clone(), &shader.main_entry_point(), &(), None) + .expect("failed to create compute pipeline"), + ); + + let image = StorageImage::new( + device.clone(), + ImageDimensions::Dim2d { + width: IMAGE_SIZE, + height: IMAGE_SIZE, + array_layers: 1, + }, + Format::R8G8B8A8Unorm, + Some(queue.family()), + ) + .unwrap(); + + let layout = compute_pipeline + .layout() + .descriptor_set_layout(0) + .unwrap(); + + let set = Arc::new( + PersistentDescriptorSet::start( + layout.clone() + ).add_image( + ImageView::new( + image.clone()) + .unwrap()) + .unwrap() + .build() + .unwrap()); + + + let buf = CpuAccessibleBuffer::from_iter( + device.clone(), + BufferUsage::all(), + false, + (0..IMAGE_SIZE * IMAGE_SIZE * 4).map(|_| 0u8), + ) + .expect("failed to create buffer"); + + let push_constants = cs::ty::PushConstantData { + zoom_factor: 1.0, + re: 1.0, + im: 0.0, + }; + + let mut builder = AutoCommandBufferBuilder::new( + device.clone(), queue.family()).unwrap(); + + builder + .dispatch( + [IMAGE_SIZE / 8, IMAGE_SIZE / 8, 1], + compute_pipeline.clone(), + set.clone(), + push_constants, + vec![], + ) + .unwrap() + .copy_image_to_buffer(image.clone(), buf.clone()) + .unwrap(); + + let command_buffer = builder.build().unwrap(); + + let finished = command_buffer.execute(queue.clone()).unwrap(); + finished + .then_signal_fence_and_flush() + .unwrap() + .wait(None) + .unwrap(); + + let buffer_content = buf.read().unwrap(); + + let image = ImageBuffer::, _>::from_raw( + IMAGE_SIZE, + IMAGE_SIZE, + &buffer_content[..]).unwrap(); + + image.save("./pictures/image.png").unwrap(); +} + + +