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 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<f64>, iterations: u32) -> Result<Rgb<u8>, 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();
|
||||
}
|
||||
|
|
|
|||
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