added GPU spport with vulkano
This commit is contained in:
parent
74f80bcf9b
commit
663dbad1ba
3 changed files with 262 additions and 10 deletions
32
src/main.rs
32
src/main.rs
|
|
@ -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
37
src/mandelbrot_shader.cs
Normal 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
203
src/vulkan.rs
Normal 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue