added GPU spport with vulkano

This commit is contained in:
Clemens-Dautermann 2021-04-11 22:21:30 +02:00
parent 74f80bcf9b
commit 663dbad1ba
3 changed files with 262 additions and 10 deletions

View file

@ -1,13 +1,17 @@
mod vulkan;
extern crate image; extern crate image;
extern crate num_complex; extern crate num_complex;
extern crate num_traits;
use image::{RgbImage, ImageBuffer, Rgb}; use image::{RgbImage, ImageBuffer, Rgb};
use std::fmt::Error; use std::fmt::Error;
use num_complex::Complex; use num_complex::Complex;
use indicatif::ProgressBar; use indicatif::ProgressBar;
use rayon::prelude::*; use rayon::prelude::*;
use num_traits::MulAddAssign;
const THRESHHOLD: f64 = 200_i32.pow(2) as f64; const THRESHHOLD: f64 = 400.0;
struct PixelInfo { struct PixelInfo {
x: u32, x: u32,
@ -20,10 +24,9 @@ fn calculate_color(number: Complex<f64>, iterations: u32) -> Result<Rgb<u8>, Err
let mut cycle = Vec::with_capacity(iterations as usize); let mut cycle = Vec::with_capacity(iterations as usize);
for i in 0..iterations { 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 cycle.contains(&z) { return Ok(Rgb::from([0, 0, 0])); }
if z.norm_sqr() > THRESHHOLD { if z.norm_sqr() > THRESHHOLD {
return Ok(map_num_to_color(i, iterations)); 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; let mut scale = initial_size * (1.0 - scale_factor).powi(start_frame as i32) as f64;
for frame in start_frame..end_frame { 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, let fractal: RgbImage = render_around_point(image_size,
iterations, iterations,
scale, scale,
@ -145,18 +150,25 @@ fn render_sequence(end_frame: u32,
let filename: String = path.clone() + "frame_" + &*frame.to_string() + ".png"; let filename: String = path.clone() + "frame_" + &*frame.to_string() + ".png";
fractal.save(filename).unwrap(); fractal.save(filename).unwrap();
println!("Rendered frame {}", frame);
} }
} }
fn main() { fn main() {
render_sequence(240, /*
//special point:
let point = Complex::new(-0.743643887037158704752191506114774,
0.131825904205311970493132056385139);
render_sequence(1,
0, 0,
500, 512,
200, 1000,
2.0, 1.0,
Complex::new(-0.761574, -0.0847596), point,
1.0 / 10.0, 1.0 / 10.0,
"./out/".parse().unwrap(), "./test_".parse().unwrap(),
); );
*/
vulkan::run();
} }

37
src/mandelbrot_shader.cs Normal file
View file

@ -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);
}

203
src/vulkan.rs Normal file
View file

@ -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::<usize>().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::<Rgba<u8>, _>::from_raw(
IMAGE_SIZE,
IMAGE_SIZE,
&buffer_content[..]).unwrap();
image.save("./pictures/image.png").unwrap();
}