├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── assets ├── cornell-box.mtl ├── cornell-box.obj ├── crate.jpg ├── crate.mtl ├── crate.obj ├── wolf.mtl └── wolf.obj ├── build.rs ├── rustfmt.toml ├── shaders ├── bbox.comp ├── count_pairs.comp ├── quad.frag ├── quad.vert ├── raycasting.comp.tera ├── tracer.comp.tera └── write_pairs.comp └── src ├── args.rs ├── control ├── camera.rs ├── event_manager.rs ├── keyboard.rs ├── mod.rs └── mouse.rs ├── fps_counter.rs ├── gl_types.rs ├── grid ├── bbox.rs ├── mod.rs ├── pair_counter.rs └── pair_writer.rs ├── main.rs ├── render ├── drawer.rs ├── mod.rs ├── offline.rs ├── realtime.rs └── vulkan_ctx.rs ├── scene.rs └── tracers ├── mod.rs ├── raycasting.rs └── tracer.rs /.gitignore: -------------------------------------------------------------------------------- 1 | Cargo.lock 2 | target/ 3 | **/*.rs.bk 4 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "vulkano-raytracing" 3 | version = "0.1.1" 4 | authors = ["Vlad Alex "] 5 | build = "build.rs" 6 | 7 | [[bin]] 8 | name = "tracer" 9 | path = "src/main.rs" 10 | 11 | [dependencies] 12 | vulkano = "0.7.1" 13 | vulkano-shader-derive = "0.7" 14 | vulkano-win = "0.7" 15 | vulkano_text = "0.5" 16 | winit = "0.7.0" 17 | 18 | time = "0.1" 19 | image = "0.18" 20 | tobj = "0.1" 21 | cgmath = "0.16" 22 | clap = "~2.27.0" 23 | regex = "0.2" 24 | lazy_static = "~1.0.0" 25 | 26 | [build-dependencies] 27 | tera = "0.11" 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Vlad Alex 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [WIP] vulkano-raytracing 2 | 3 | Real-time interactive GPU ray-tracer, created using awesome [vulkano](https://vulkano.rs/). 4 | It is currently in very early stage of development. 5 | 6 | ## Build 7 | 8 | To build this project you need: 9 | - Rust nightly toolchain (just use [rustup](https://www.rustup.rs/) if you 10 | don't have Rust yet) 11 | - Vulkan drivers for your GPU 12 | - (Optional) [LunarG SDK](https://www.lunarg.com/vulkan-sdk/) for 13 | validation and debug 14 | 15 | Then comes the moment you understand you love Rust so much. 16 | ```bash 17 | cargo build 18 | ``` 19 | And you are done! 20 | 21 | 22 | ## Usage 23 | 24 | [clap](https://clap.rs/) provides user with excelent help message: 25 | ```bash 26 | USAGE: 27 | tracer [FLAGS] [OPTIONS] 28 | 29 | FLAGS: 30 | --benchmark Turn on benchmarking 31 | -h, --help Prints help information 32 | -V, --version Prints version information 33 | 34 | OPTIONS: 35 | -r, --resolution 36 | Sets the resolution of the image [default: 640 480] 37 | -p, --position 38 | Sets the position of camera [default: 0.0 0.0 5.0] 39 | --fov 40 | Sets the field of view [default: 40.0 40.0] 41 | --sensitivity 42 | Sets the sensitivity of the controls (camera movement) [default: 1.0] 43 | --fps-update-interval 44 | Sets the interval (in milliseconds) of FPS update. 45 | Displayed FPS is the average in the last interval [default: 100] 46 | --log-level 47 | Sets the log messages amount [default: perf] 48 | [values: none, error, warning, perf, info, debug] 49 | 50 | ARGS: 51 | Sets the path to file with model to render 52 | ``` 53 | Basicly, you just want to run 54 | ```bash 55 | cargo run -- /path/to/model.obj 56 | ``` 57 | There are some models in assets folder that you can use to get the idea. 58 | But it should work with any Wavefront model you want. (You want to 59 | run release build with good GPU to render big models, though). 60 | 61 | Then the window will open and display the passed model. You can control the camera using 62 | keyboard and mouse. Use WASD or arrow keys to move around, Shift and Ctrl to 63 | move up and down, and mouse to rotate the camera. You can see current FPS and 64 | camera position+rotation in the left-top corner. 65 | 66 | ## Development 67 | 68 | It is my own pet-project, that I develop just for fun. But help is highly 69 | appriciated, especially if you are a Vulkan/Vulkano/raytracing expert, 'cause I'm not. 70 | Feel free to contact me, create an issue or pull request. 71 | But make sure to run `cargo fmt` before creating pull request. 72 | 73 | ## Roadmap 74 | 75 | This is probably a subject to change. 76 | 77 | **v0.1** (aka baby-tracer) 78 | - [x] Single triangle rendering 79 | - [x] User-controllable pinhole camera model 80 | - [x] Wavefront model support 81 | 82 | **v0.2** (acceleration time!) 83 | - [ ] Single-level uniform grid 84 | - [ ] Two-level grid 85 | 86 | **v0.3** (stop hurting eyes) 87 | - [ ] Basic shading (shadows, refraction, reflection) 88 | - [ ] Depth of field and motion blur 89 | 90 | **v0.4** (OMG, it's moving) 91 | - [ ] Animated glTF models support 92 | 93 | **v0.5** (adult swim) 94 | - [ ] Unidirectional path-tracing 95 | - [ ] Anti-aliasing 96 | 97 | **v0.x** (*scary future that needs more research*) 98 | - [ ] Better path-tracing algorithm 99 | - [ ] Better acceleration structure 100 | - [ ] Denoising 101 | - [ ] Some mindblowing hacks 102 | - [ ] Load-balancing 103 | - [ ] Ambient occlusion 104 | ...and more 105 | 106 | ## Links 107 | 108 | - According to this awesome [video](https://www.youtube.com/watch?v=JSr6wvkvgM0) 109 | by Matt Swoboda, there are such ray-per-pixel (rpp) requirements per feature. 110 | (30FPS 720p picture, scene with ~10'000 triangles) 111 | - Camera rays: 1rpp 112 | - Reflection / refraction: 1rpp per bounce 113 | - Accurate hard shadows: 1rpp per light 114 | - Soft shadows: 10-200 rpp per light 115 | - Path-tracing: 500-1000 rpp 116 | - Ambient occlusion: looks good at 100-500 rpp 117 | - [Great paper](https://www.kalojanov.com/data/gpu_grid_construction.pdf) with 118 | uniform grid construction algorithm by Javor Kalojanov and Philipp Slusallek. 119 | - [Another great paper](https://www.kalojanov.com/data/two_level_grids.pdf) by 120 | Javor Kalojanov togeather with Markus Billeter and Philipp Slusallek, that 121 | describes two-level grid structure and uses unifor grid algorithm for 122 | top-level cells. Will probably be used for v0.2 123 | 124 | -------------------------------------------------------------------------------- /assets/cornell-box.mtl: -------------------------------------------------------------------------------- 1 | # Blender MTL File: 'cornell-box-cycles.blend' 2 | # Material Count: 3 3 | 4 | newmtl Light 5 | Ns 00.000000 6 | Ka 1.000000 1.000000 1.000000 7 | Kd 1.000000 1.000000 1.000000 8 | Ks 0.000000 0.000000 0.000000 9 | Ke 0.000000 0.000000 0.000000 10 | Ni 1.000000 11 | d 1.000000 12 | illum 2 13 | 14 | newmtl DarkGreen 15 | Ns 96.078431 16 | Ka 1.000000 1.000000 1.000000 17 | Kd 0.000000 0.320000 0.000000 18 | Ks 0.500000 0.500000 0.500000 19 | Ke 0.000000 0.000000 0.000000 20 | Ni 1.000000 21 | d 1.000000 22 | illum 2 23 | 24 | newmtl Khaki 25 | Ns 96.078431 26 | Ka 1.000000 1.000000 1.000000 27 | Kd 0.800000 0.659341 0.439560 28 | Ks 0.500000 0.500000 0.500000 29 | Ke 0.000000 0.000000 0.000000 30 | Ni 1.000000 31 | d 1.000000 32 | illum 2 33 | 34 | newmtl BloodyRed 35 | Ns 96.078431 36 | Ka 1.000000 1.000000 1.000000 37 | Kd 0.445000 0.000000 0.000000 38 | Ks 0.500000 0.500000 0.500000 39 | Ke 0.000000 0.000000 0.000000 40 | Ni 1.000000 41 | d 1.000000 42 | illum 2 43 | -------------------------------------------------------------------------------- /assets/cornell-box.obj: -------------------------------------------------------------------------------- 1 | # Blender v2.79 (sub 0) OBJ File: 'cornell-box-cycles.blend' 2 | # www.blender.org 3 | mtllib cornell-box.mtl 4 | o area_light 5 | v -0.884011 5.319334 -2.517968 6 | v -0.884011 5.318497 -3.567968 7 | v 0.415989 5.318497 -3.567968 8 | v 0.415989 5.319334 -2.517968 9 | vn 0.0000 -1.0000 0.0008 10 | usemtl Light 11 | s off 12 | f 2//1 4//1 1//1 13 | f 2//1 3//1 4//1 14 | o back_wall 15 | v -2.950011 -0.162686 -5.835598 16 | v 2.545989 -0.162686 -5.835598 17 | v 2.545989 5.325313 -5.839967 18 | v -3.014011 5.325313 -5.839967 19 | vn 0.0000 0.0008 1.0000 20 | usemtl Khaki 21 | s off 22 | f 5//2 7//2 8//2 23 | f 5//2 6//2 7//2 24 | o ceiling 25 | v -3.014011 5.329765 -0.247969 26 | v -3.014011 5.325313 -5.839968 27 | v 2.545989 5.325313 -5.839968 28 | v 2.545989 5.329765 -0.247969 29 | vn 0.0000 -1.0000 0.0008 30 | usemtl Khaki 31 | s off 32 | f 10//3 12//3 9//3 33 | f 10//3 11//3 12//3 34 | o floor 35 | v -2.982011 -0.158233 -0.243599 36 | v 2.545989 -0.158233 -0.243599 37 | v 2.545989 -0.162686 -5.835598 38 | v -2.950011 -0.162686 -5.835598 39 | vn 0.0000 1.0000 -0.0008 40 | usemtl Khaki 41 | s off 42 | f 14//4 16//4 13//4 43 | f 14//4 15//4 16//4 44 | o left_wall 45 | v -2.982011 -0.158233 -0.243599 46 | v -2.950011 -0.162686 -5.835598 47 | v -3.014011 5.325313 -5.839968 48 | v -3.014011 5.329765 -0.247969 49 | vn 1.0000 0.0058 -0.0000 50 | vn 0.9999 0.0117 0.0057 51 | usemtl BloodyRed 52 | s off 53 | f 17//5 19//5 20//5 54 | f 17//6 18//6 19//6 55 | o right_wall 56 | v 2.545989 -0.162686 -5.835598 57 | v 2.545989 -0.158233 -0.243599 58 | v 2.545989 5.329765 -0.247969 59 | v 2.545989 5.325313 -5.839968 60 | vn -1.0000 0.0000 0.0000 61 | usemtl DarkGreen 62 | s off 63 | f 22//7 24//7 21//7 64 | f 22//7 23//7 24//7 65 | o short_box 66 | v 1.245989 1.491249 -0.894913 67 | v 1.725989 1.489975 -2.494912 68 | v 0.145989 1.489601 -2.964912 69 | v -0.354011 1.490859 -1.384912 70 | v -0.354011 -0.159141 -1.383599 71 | v -0.354011 1.490859 -1.384912 72 | v 0.145989 1.489601 -2.964912 73 | v 0.145989 -0.160399 -2.963598 74 | v 1.245989 -0.158750 -0.893599 75 | v 1.245989 1.491249 -0.894913 76 | v -0.354011 1.490859 -1.384912 77 | v -0.354011 -0.159141 -1.383599 78 | v 1.725989 -0.160024 -2.493598 79 | v 1.725989 1.489975 -2.494912 80 | v 1.245989 1.491249 -0.894913 81 | v 1.245989 -0.158750 -0.893599 82 | v 0.145989 -0.160399 -2.963598 83 | v 0.145989 1.489601 -2.964912 84 | v 1.725989 1.489975 -2.494912 85 | v 1.725989 -0.160024 -2.493598 86 | vn 0.0000 1.0000 -0.0008 87 | vn -0.9534 -0.0002 -0.3017 88 | vn -0.2928 0.0008 0.9562 89 | vn 0.9578 0.0002 0.2873 90 | vn 0.2851 -0.0008 -0.9585 91 | usemtl Khaki 92 | s off 93 | f 25//8 27//8 28//8 94 | f 30//9 32//9 29//9 95 | f 34//10 36//10 33//10 96 | f 38//11 40//11 37//11 97 | f 42//12 44//12 41//12 98 | f 25//8 26//8 27//8 99 | f 30//9 31//9 32//9 100 | f 34//10 35//10 36//10 101 | f 38//11 39//11 40//11 102 | f 42//12 43//12 44//12 103 | o tall_box 104 | v -1.684011 3.139799 -2.716226 105 | v -0.104011 3.139409 -3.206226 106 | v -0.594011 3.138135 -4.806226 107 | v -2.174011 3.138533 -4.306226 108 | v -1.684011 -0.160200 -2.713598 109 | v -1.684011 3.139799 -2.716226 110 | v -2.174011 3.138533 -4.306226 111 | v -2.174011 -0.161466 -4.303598 112 | v -2.174011 -0.161466 -4.303598 113 | v -2.174011 3.138533 -4.306226 114 | v -0.594011 3.138135 -4.806226 115 | v -0.594011 -0.161864 -4.803598 116 | v -0.594011 -0.161864 -4.803598 117 | v -0.594011 3.138135 -4.806226 118 | v -0.104011 3.139409 -3.206226 119 | v -0.104011 -0.160590 -3.203598 120 | v -0.104011 -0.160590 -3.203598 121 | v -0.104011 3.139409 -3.206226 122 | v -1.684011 3.139799 -2.716226 123 | v -1.684011 -0.160200 -2.713598 124 | vn -0.0000 1.0000 -0.0008 125 | vn -0.9556 0.0002 0.2945 126 | vn -0.3017 -0.0008 -0.9534 127 | vn 0.9562 -0.0002 -0.2928 128 | vn 0.2962 0.0008 0.9551 129 | usemtl Khaki 130 | s off 131 | f 46//13 48//13 45//13 132 | f 49//14 51//14 52//14 133 | f 54//15 56//15 53//15 134 | f 58//16 60//16 57//16 135 | f 62//17 64//17 61//17 136 | f 46//13 47//13 48//13 137 | f 49//14 50//14 51//14 138 | f 54//15 55//15 56//15 139 | f 58//16 59//16 60//16 140 | f 62//17 63//17 64//17 141 | -------------------------------------------------------------------------------- /assets/crate.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vaffeine/vulkano-raytracing/1d275c782849d2f3c4830d4cf02b7e37755b0fe0/assets/crate.jpg -------------------------------------------------------------------------------- /assets/crate.mtl: -------------------------------------------------------------------------------- 1 | # Blender MTL File: 'None' 2 | # Material Count: 1 3 | 4 | newmtl Material.001 5 | Ns 94.117647 6 | Ka 0.000000 0.000000 0.000000 7 | Kd 0.640000 0.640000 0.640000 8 | Ks 0.500000 0.500000 0.500000 9 | Ke 0.000000 0.000000 0.000000 10 | Ni 1.000000 11 | d 1.000000 12 | illum 2 13 | map_Kd crate.jpg 14 | -------------------------------------------------------------------------------- /assets/crate.obj: -------------------------------------------------------------------------------- 1 | # Blender v2.79 (sub 0) OBJ File: '' 2 | # www.blender.org 3 | mtllib crate.mtl 4 | o Cube_Cube.001 5 | v -1.000000 1.000000 1.000000 6 | v -1.000000 1.000000 -1.000000 7 | v -1.000000 -1.000000 -1.000000 8 | v -1.000000 -1.000000 1.000000 9 | v 1.000000 1.000000 -1.000000 10 | v 1.000000 -1.000000 -1.000000 11 | v 1.000000 1.000000 1.000000 12 | v 1.000000 -1.000000 1.000000 13 | vt 1.000000 0.000000 14 | vt 0.000000 1.000000 15 | vt 0.000000 0.000000 16 | vt 1.000000 0.000000 17 | vt 0.000000 1.000000 18 | vt 0.000000 0.000000 19 | vt 1.000000 0.000000 20 | vt 0.000000 1.000000 21 | vt 0.000000 0.000000 22 | vt 1.000000 0.000000 23 | vt 0.000000 1.000000 24 | vt 0.000000 0.000000 25 | vt 1.000000 0.000000 26 | vt 0.000000 0.000000 27 | vt 0.000000 1.000000 28 | vt 1.000000 1.000000 29 | vt 1.000000 1.000000 30 | vt 1.000000 1.000000 31 | vt 1.000000 1.000000 32 | vt 1.000000 1.000000 33 | vn -1.0000 -0.0000 0.0000 34 | vn 0.0000 0.0000 -1.0000 35 | vn 1.0000 -0.0000 0.0000 36 | vn 0.0000 -0.0000 1.0000 37 | vn 0.0000 -1.0000 0.0000 38 | vn 0.0000 1.0000 0.0000 39 | usemtl Material.001 40 | s off 41 | f 2/1/1 4/2/1 1/3/1 42 | f 5/4/2 3/5/2 2/6/2 43 | f 7/7/3 6/8/3 5/9/3 44 | f 1/10/4 8/11/4 7/12/4 45 | f 3/13/5 8/11/5 4/14/5 46 | f 5/4/6 1/15/6 7/12/6 47 | f 2/1/1 3/16/1 4/2/1 48 | f 5/4/2 6/17/2 3/5/2 49 | f 7/7/3 8/18/3 6/8/3 50 | f 1/10/4 4/19/4 8/11/4 51 | f 3/13/5 6/17/5 8/11/5 52 | f 5/4/6 2/20/6 1/15/6 53 | -------------------------------------------------------------------------------- /assets/wolf.mtl: -------------------------------------------------------------------------------- 1 | # Blender MTL File: 'None' 2 | # Material Count: 1 3 | 4 | newmtl Material.001 5 | Ns 96.078431 6 | Ka 1.000000 1.000000 1.000000 7 | Kd 0.640000 0.640000 0.640000 8 | Ks 0.500000 0.500000 0.500000 9 | Ke 0.000000 0.000000 0.000000 10 | Ni 1.000000 11 | d 1.000000 12 | illum 2 13 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate tera; 3 | 4 | use std::fs::File; 5 | use std::io::Write; 6 | use std::path::PathBuf; 7 | 8 | fn to_extension_str(path: &PathBuf) -> &str { 9 | path.extension() 10 | .expect("can't get extension") 11 | .to_str() 12 | .expect("can't get string from extension") 13 | } 14 | 15 | fn main() { 16 | let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); 17 | let shaders_dir = root_dir.join("shaders"); 18 | assert!(shaders_dir.exists(), "shaders directory doesn't exist"); 19 | 20 | let shaders: Vec<_> = shaders_dir 21 | .read_dir() 22 | .expect("can't read shaders direcotry") 23 | .map(|shader_entry| shader_entry.expect("can'r read shader").path()) 24 | .filter(|shader| { 25 | let shader_extension = to_extension_str(&shader); 26 | ["comp", "frag", "vert", "tera"].contains(&shader_extension) 27 | }) 28 | .collect(); 29 | 30 | for shader in std::iter::once(&shaders_dir).chain(shaders.iter()) { 31 | println!( 32 | "cargo:rerun-if-changed={}", 33 | shader.to_str().expect("can't convert path to str") 34 | ); 35 | } 36 | 37 | let templates_glob = { 38 | let mut p = shaders_dir 39 | .to_str() 40 | .expect("can't get string from path") 41 | .to_string(); 42 | p.push_str("/**/*.tera"); 43 | p 44 | }; 45 | let tera = compile_templates!(&templates_glob); 46 | let rendered_shaders_dir = root_dir.join("target").join("shaders"); 47 | std::fs::create_dir_all(rendered_shaders_dir.clone()).expect("failed to create directory"); 48 | for shader in shaders 49 | .into_iter() 50 | .filter(|shader| to_extension_str(&shader) == "tera") 51 | { 52 | let rendered_shader = tera.render( 53 | shader.strip_prefix(&shaders_dir).unwrap().to_str().unwrap(), 54 | &tera::Context::new(), 55 | ).expect("failed to render shader template"); 56 | let output_path = rendered_shaders_dir.join( 57 | shader 58 | .strip_prefix(&shaders_dir) 59 | .unwrap() 60 | .with_extension(""), 61 | ); 62 | println!("{:?}", output_path); 63 | let mut file = File::create(output_path).expect("can't create file for rendered shader"); 64 | file.write_all(rendered_shader.as_bytes()) 65 | .expect("failed to write rendered shader to file"); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | max_width = 99 2 | -------------------------------------------------------------------------------- /shaders/bbox.comp: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | layout(local_size_x = 256, local_size_y = 1, local_size_z = 1) in; 4 | 5 | layout(set = 0, binding = 0) readonly buffer Positions { 6 | vec3 positions[]; 7 | }; 8 | 9 | layout(set = 0, binding = 1) writeonly buffer BBox { 10 | vec3 minmax[]; 11 | }; 12 | 13 | shared vec3 s_minimum[gl_WorkGroupSize.x]; 14 | shared vec3 s_maximum[gl_WorkGroupSize.x]; 15 | 16 | void main() { 17 | uint tid = gl_LocalInvocationID.x; 18 | uint gid = gl_WorkGroupID.x * gl_WorkGroupSize.x * 2 + tid; 19 | if (gid < positions.length()) { 20 | if (gid + gl_WorkGroupSize.x < positions.length()) { 21 | s_maximum[tid] = max(positions[gid], positions[gid + gl_WorkGroupSize.x]); 22 | s_minimum[tid] = min(positions[gid], positions[gid + gl_WorkGroupSize.x]); 23 | } else { 24 | s_maximum[tid] = positions[gid]; 25 | s_minimum[tid] = positions[gid]; 26 | } 27 | } else { 28 | s_maximum[tid] = vec3(-1.0e10); 29 | s_minimum[tid] = vec3(1.0e10); 30 | } 31 | barrier(); 32 | 33 | for (uint s = gl_WorkGroupSize.x / 2; s > 32; s >>= 1) { 34 | if (tid < s) { 35 | s_maximum[tid] = max(s_maximum[tid], s_maximum[tid + s]); 36 | s_minimum[tid] = min(s_minimum[tid], s_minimum[tid + s]); 37 | } 38 | barrier(); 39 | } 40 | 41 | if (tid < 32) { 42 | s_maximum[tid] = max(s_maximum[tid], s_maximum[tid + 32]); 43 | s_maximum[tid] = max(s_maximum[tid], s_maximum[tid + 16]); 44 | s_maximum[tid] = max(s_maximum[tid], s_maximum[tid + 8]); 45 | s_maximum[tid] = max(s_maximum[tid], s_maximum[tid + 4]); 46 | s_maximum[tid] = max(s_maximum[tid], s_maximum[tid + 2]); 47 | s_maximum[tid] = max(s_maximum[tid], s_maximum[tid + 1]); 48 | 49 | s_minimum[tid] = min(s_minimum[tid], s_minimum[tid + 32]); 50 | s_minimum[tid] = min(s_minimum[tid], s_minimum[tid + 16]); 51 | s_minimum[tid] = min(s_minimum[tid], s_minimum[tid + 8]); 52 | s_minimum[tid] = min(s_minimum[tid], s_minimum[tid + 4]); 53 | s_minimum[tid] = min(s_minimum[tid], s_minimum[tid + 2]); 54 | s_minimum[tid] = min(s_minimum[tid], s_minimum[tid + 1]); 55 | } 56 | 57 | if (tid == 0) { 58 | minmax[gl_WorkGroupID.x] = s_minimum[0]; 59 | minmax[gl_WorkGroupID.x + gl_NumWorkGroups.x] = s_maximum[0]; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /shaders/count_pairs.comp: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | layout(local_size_x = 256, local_size_y = 1, local_size_z = 1) in; 4 | 5 | layout(set = 0, binding = 0) readonly buffer Positions { 6 | vec3 positions[]; 7 | }; 8 | 9 | layout(set = 0, binding = 1) readonly buffer Indices { 10 | uvec3 indices[]; 11 | }; 12 | 13 | layout(set = 1, binding = 0) readonly uniform Params { 14 | vec3 min_cell; 15 | vec3 cell_size; 16 | uvec3 resolution; 17 | }; 18 | 19 | layout(set = 1, binding = 1) writeonly buffer Cells { 20 | uint cells[]; 21 | }; 22 | 23 | layout(set = 1, binding = 2) writeonly buffer MinCells { 24 | uvec3 min_cells[]; 25 | }; 26 | 27 | layout(set = 1, binding = 3) writeonly buffer MaxCells { 28 | uvec3 max_cells[]; 29 | }; 30 | 31 | void main() { 32 | uint gid = gl_GlobalInvocationID.x; 33 | if (gid >= indices.length()) { 34 | return; 35 | } 36 | 37 | uvec3 triangle = indices[gid]; 38 | vec3 a = positions[triangle.x]; 39 | vec3 b = positions[triangle.y]; 40 | vec3 c = positions[triangle.z]; 41 | 42 | vec3 tri_min = min(a, min(b, c)) - min_cell; 43 | vec3 tri_max = max(a, max(b, c)) - min_cell; 44 | 45 | uvec3 min_cell = clamp(uvec3(tri_min / cell_size), uvec3(0), resolution - uvec3(1)); 46 | uvec3 max_cell = clamp(uvec3(tri_max / cell_size), uvec3(0), resolution - uvec3(1)); 47 | min_cells[gid] = min_cell; 48 | max_cells[gid] = max_cell; 49 | 50 | for (uint z = min_cell.z; z <= max_cell.z; ++z) { 51 | for (uint y = min_cell.y; y <= max_cell.y; ++y) { 52 | for (uint x = min_cell.x; x <= max_cell.x; ++x) { 53 | atomicAdd(cells[x + resolution.x * (y + resolution.y * z)], 1); 54 | } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /shaders/quad.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | layout(location = 0) in vec2 tex_coords; 4 | layout(location = 0) out vec4 f_color; 5 | layout(set = 0, binding = 0) uniform sampler2D tex; 6 | 7 | void main() { 8 | f_color = texture(tex, tex_coords); 9 | } 10 | -------------------------------------------------------------------------------- /shaders/quad.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | layout(location = 0) in vec2 position; 4 | layout(location = 0) out vec2 tex_coords; 5 | 6 | void main() { 7 | gl_Position = vec4(position, 0.0, 1.0); 8 | tex_coords = (position + vec2(1.0)) / 2; 9 | } 10 | -------------------------------------------------------------------------------- /shaders/raycasting.comp.tera: -------------------------------------------------------------------------------- 1 | {% extends "tracer.comp.tera" %} 2 | {% block get_color %} 3 | 4 | 5 | vec3 get_color(in Ray ray, in IntersectionResult intersection) { 6 | uvec3 triangle = indices[intersection.triangle_idx]; 7 | 8 | vec3 wuv = vec3(1.0 - intersection.uv.x - intersection.uv.y, intersection.uv.xy); 9 | vec3 norm = point_norm(triangle, wuv); 10 | vec2 st = point_st(triangle, wuv); 11 | 12 | uint model_idx = find_model(intersection.triangle_idx); 13 | uint material_idx = models[model_idx].material_idx; 14 | Material material = materials[material_idx]; 15 | 16 | vec3 diffuse_color = material.diffuse_texture_idx != -1 ? 17 | texture(diffuse_textures[material.diffuse_texture_idx], st).rgb : 18 | vec3(material.diffuse); 19 | float cosine_factor = abs(dot(norm, ray.dir)); 20 | return cosine_factor * diffuse_color; 21 | } 22 | 23 | {% endblock get_color %} 24 | -------------------------------------------------------------------------------- /shaders/tracer.comp.tera: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | layout(local_size_x = 16, local_size_y = 16) in; 4 | layout(set = 0, binding = 0, rgba8) uniform writeonly image2D img; 5 | 6 | struct Camera { 7 | vec3 position; 8 | vec3 view; 9 | vec3 right; 10 | vec3 up; 11 | }; 12 | 13 | struct Grid { 14 | vec3 minimum_cell; 15 | vec3 maximum_cell; 16 | vec3 cell_size; 17 | uvec3 resolution; 18 | }; 19 | 20 | layout (set = 0, binding = 1) uniform Uniform { 21 | Camera camera; 22 | Grid grid; 23 | }; 24 | 25 | layout(set = 0, binding = 2) buffer Statistics { 26 | uint cell_intersections; 27 | uint triangle_tests; 28 | uint triangle_intersections; 29 | }; 30 | 31 | layout(set = 0, binding = 3) readonly buffer Cells { 32 | uint cells[]; 33 | }; 34 | 35 | layout(set = 0, binding = 4) readonly buffer References { 36 | uint references[]; 37 | }; 38 | 39 | layout (std140, set = 1, binding = 0) readonly buffer Positions { 40 | vec3 positions[]; 41 | }; 42 | 43 | layout (std140, set = 1, binding = 1) readonly buffer Indices { 44 | uvec3 indices[]; 45 | }; 46 | 47 | layout (std140, set = 1, binding = 2) readonly buffer Noramls { 48 | vec3 normals[]; 49 | }; 50 | 51 | layout (std140, set = 1, binding = 3) readonly buffer TexCoords { 52 | vec2 texcoords[]; 53 | }; 54 | 55 | struct Model { 56 | uint indices_start; 57 | uint indices_end; 58 | int material_idx; 59 | }; 60 | 61 | layout (std140, set = 1, binding = 4) readonly buffer Models { 62 | Model models[]; 63 | }; 64 | 65 | struct Material { 66 | vec3 ambient; 67 | vec3 diffuse; 68 | vec3 specular; 69 | float shininess; 70 | float dissolve; 71 | float optical_density; 72 | int ambient_texture_idx; 73 | int diffuse_texture_idx; 74 | int specular_texture_idx; 75 | int normal_texture_idx; 76 | int disolve_texture_idx; 77 | }; 78 | 79 | layout (std140, set = 1, binding = 5) readonly buffer Materials { 80 | Material materials[]; 81 | }; 82 | 83 | // I would like to use sampler2DArray, but this extension is supported only by NVidia 84 | // Maximum number of 16, however, is supported by 97% of GPUs 85 | layout(set = 1, binding = 6) uniform sampler2D diffuse_textures[16]; 86 | 87 | struct Ray { 88 | vec3 orig; 89 | vec3 dir; 90 | }; 91 | 92 | struct IntersectionResult { 93 | vec2 uv; 94 | float time; 95 | uint triangle_idx; 96 | bool intersect; 97 | }; 98 | 99 | IntersectionResult void_intersection() { 100 | IntersectionResult result; 101 | result.time = 1e20; 102 | result.intersect = false; 103 | return result; 104 | } 105 | 106 | bool intersect_bbox(in Ray ray, in vec3 inv_dir, in vec3 bbox[2], out float thit) { 107 | uvec3 dir_sign = uvec3(lessThan(ray.dir, vec3(0.0))); 108 | vec3 tmin = (vec3(bbox[dir_sign.x].x, bbox[dir_sign.y].y, 109 | bbox[dir_sign.z].z) - ray.orig) * inv_dir; 110 | vec3 tmax = (vec3(bbox[1 - dir_sign.x].x, bbox[1 - dir_sign.y].y, 111 | bbox[1 - dir_sign.z].z) - ray.orig) * inv_dir; 112 | 113 | if (tmin.x > tmax.y || tmin.y > tmax.x) { 114 | return false; 115 | } 116 | 117 | tmin.x = max(tmin.x, tmin.y); 118 | tmax.x = min(tmax.x, tmax.y); 119 | 120 | if (tmin.x > tmax.z || tmin.z > tmax.x) { 121 | return false; 122 | } 123 | 124 | thit = max(tmin.x, tmin.z); 125 | 126 | return true; 127 | } 128 | 129 | bool intersect_triangle(in Ray ray, in uint triangle_idx, 130 | out IntersectionResult intersection) { 131 | atomicAdd(triangle_tests, 1); 132 | 133 | const float EPSILON = 1e-8; 134 | 135 | uvec3 triangle = indices[triangle_idx]; 136 | vec3 v0 = positions[triangle.x]; 137 | vec3 v1 = positions[triangle.y]; 138 | vec3 v2 = positions[triangle.z]; 139 | 140 | vec3 v0v1 = v1 - v0; 141 | vec3 v0v2 = v2 - v0; 142 | vec3 pvec = cross(ray.dir, v0v2); 143 | float det = dot(v0v1, pvec); 144 | if (abs(det) < EPSILON) { 145 | return false; 146 | } 147 | 148 | float invDet = 1.0 / det; 149 | vec3 tvec = ray.orig - v0; 150 | float u = dot(tvec, pvec) * invDet; 151 | if (u < 0.0 || u > 1.0) { 152 | return false; 153 | } 154 | 155 | vec3 qvec = cross(tvec, v0v1); 156 | float v = dot(ray.dir, qvec) * invDet; 157 | if (v < 0.0 || u + v > 1.0) { 158 | return false; 159 | } 160 | 161 | float t = dot(v0v2, qvec) * invDet; 162 | if (t < 0.0) { 163 | return false; 164 | } 165 | 166 | atomicAdd(triangle_intersections, 1); 167 | intersection = IntersectionResult( 168 | vec2(u, v), 169 | t, 170 | triangle_idx, 171 | true 172 | ); 173 | return true; 174 | } 175 | 176 | void intersect_cell(in Ray ray, in uint cell_idx, 177 | inout IntersectionResult best) { 178 | atomicAdd(cell_intersections, 1); 179 | IntersectionResult result; 180 | for (uint i = cells[cell_idx]; i < cells[cell_idx + 1]; ++i) { 181 | uint triangle_idx = references[i]; 182 | if (intersect_triangle(ray, triangle_idx, result) && 183 | best.time > result.time) { 184 | best = result; 185 | } 186 | } 187 | } 188 | 189 | uint get_axis(in vec3 next_t) { 190 | const uint AXIS_MAP[8] = {2, 1, 2, 1, 2, 2, 0, 0}; 191 | uint k = 192 | (uint(next_t.x < next_t.y) << 2) + 193 | (uint(next_t.x < next_t.z) << 1) + 194 | uint(next_t.y < next_t.z); 195 | return AXIS_MAP[k]; 196 | } 197 | 198 | vec2 point_st(in uvec3 triangle, in vec3 wuv) { 199 | vec2 st0 = texcoords[triangle.x]; 200 | vec2 st1 = texcoords[triangle.y]; 201 | vec2 st2 = texcoords[triangle.z]; 202 | return wuv.x * st0 + wuv.y * st1 + wuv.z * st2; 203 | } 204 | 205 | vec3 point_norm(in uvec3 triangle, in vec3 wuv) { 206 | vec3 norm0 = normals[triangle.x]; 207 | vec3 norm1 = normals[triangle.y]; 208 | vec3 norm2 = normals[triangle.z]; 209 | return wuv.x * norm0 + wuv.y * norm1 + wuv.z * norm2; 210 | } 211 | 212 | uint find_model(in uint triangle_idx) { 213 | for (uint i = 0; i < models.length(); ++i) { 214 | Model model = models[i]; 215 | if (triangle_idx >= model.indices_start && 216 | triangle_idx < model.indices_end) { 217 | return i; 218 | } 219 | } 220 | } 221 | 222 | IntersectionResult intersect_grid(in Ray ray) { 223 | vec3 inv_dir = vec3(1.0) / ray.dir; 224 | IntersectionResult best = void_intersection(); 225 | 226 | float t_hit_bbox; 227 | vec3 bbox[2] = { grid.minimum_cell, grid.maximum_cell }; 228 | if (!intersect_bbox(ray, inv_dir, bbox, t_hit_bbox)) { 229 | return best; 230 | } 231 | 232 | vec3 ray_origin_cell = ray.orig + ray.dir * vec3(t_hit_bbox) - grid.minimum_cell; 233 | uvec3 current_cell = clamp(uvec3(ray_origin_cell / grid.cell_size), 234 | uvec3(0), grid.resolution - uvec3(1)); 235 | 236 | vec3 dir_sign = sign(ray.dir); 237 | vec3 delta_t = dir_sign * grid.cell_size * inv_dir; 238 | ivec3 next_step = ivec3(dir_sign); 239 | vec3 next_t = vec3(t_hit_bbox) + 240 | ((current_cell + step(0.0, ray.dir)) * grid.cell_size - ray_origin_cell) * 241 | inv_dir; 242 | uvec3 exit = mix(grid.resolution, uvec3(-1), lessThan(ray.dir, vec3(0.0))); 243 | 244 | while (true) { 245 | uint cell_idx = current_cell.x + grid.resolution.x * 246 | (current_cell.y + current_cell.z * grid.resolution.y); 247 | intersect_cell(ray, cell_idx, best); 248 | 249 | uint axis = get_axis(next_t); 250 | if (best.time < next_t[axis]) { 251 | break; 252 | } 253 | current_cell[axis] += next_step[axis]; 254 | if (current_cell[axis] == exit[axis]) { 255 | break; 256 | } 257 | next_t[axis] += delta_t[axis]; 258 | } 259 | 260 | return best; 261 | } 262 | 263 | Ray primary_ray(in vec2 uv, in float aspect_ratio) { 264 | vec2 trans = 2.0 * uv - vec2(1.0); 265 | vec3 dir = camera.view + camera.right * trans.x + camera.up * trans.y; 266 | dir *= vec2(aspect_ratio, 1.0).xyx; 267 | 268 | Ray ray; 269 | ray.orig = camera.position; 270 | ray.dir = normalize(dir); 271 | 272 | return ray; 273 | } 274 | 275 | {% block get_color %} 276 | {% endblock get_color %} 277 | 278 | void main() { 279 | vec2 dim = imageSize(img); 280 | vec2 uv = vec2(gl_GlobalInvocationID.xy) / dim; 281 | float aspect_ratio = dim.x / dim.y; 282 | 283 | Ray ray = primary_ray(uv, aspect_ratio); 284 | IntersectionResult best = intersect_grid(ray); 285 | 286 | vec3 color = best.intersect ? get_color(ray, best) : vec3(0.0); 287 | imageStore(img, ivec2(gl_GlobalInvocationID.xy), vec4(color, 1.0)); 288 | } 289 | -------------------------------------------------------------------------------- /shaders/write_pairs.comp: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | layout(local_size_x = 256, local_size_y = 1, local_size_z = 1) in; 4 | 5 | layout(set = 0, binding = 0) readonly uniform Params { 6 | uvec3 resolution; 7 | }; 8 | 9 | layout(set = 0, binding = 1) readonly buffer MinCells { 10 | uvec3 min_cells[]; 11 | }; 12 | 13 | layout(set = 0, binding = 2) readonly buffer MaxCells { 14 | uvec3 max_cells[]; 15 | }; 16 | 17 | layout(set = 0, binding = 3) readonly buffer Cells { 18 | uint cells[]; 19 | }; 20 | 21 | layout(set = 0, binding = 4) buffer CurrentCellIndices { 22 | uint current_cell_idx[]; 23 | }; 24 | 25 | layout(set = 0, binding = 5) writeonly buffer References { 26 | uint references[]; 27 | }; 28 | 29 | void main() { 30 | uint gid = gl_GlobalInvocationID.x; 31 | if (gid >= min_cells.length()) { 32 | return; 33 | } 34 | 35 | uvec3 min_cell = min_cells[gid]; 36 | uvec3 max_cell = max_cells[gid]; 37 | 38 | for (uint z = min_cell.z; z <= max_cell.z; ++z) { 39 | for (uint y = min_cell.y; y <= max_cell.y; ++y) { 40 | for (uint x = min_cell.x; x <= max_cell.x; ++x) { 41 | uint cell_idx = x + resolution.x * (y + resolution.y * z); 42 | uint rel_idx = atomicAdd(current_cell_idx[cell_idx], 1); 43 | uint global_idx = cells[cell_idx] + rel_idx; 44 | references[global_idx] = gid; 45 | } 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/args.rs: -------------------------------------------------------------------------------- 1 | use cgmath; 2 | use clap; 3 | use vulkano; 4 | 5 | use std; 6 | 7 | pub struct Args { 8 | pub model: String, 9 | pub resolution: Vec, 10 | pub position: cgmath::Vector3, 11 | pub fov: [f32; 2], 12 | pub sensitivity: f32, 13 | pub fps_update_interval: i64, 14 | pub log_level: vulkano::instance::debug::MessageTypes, 15 | pub benchmark: bool, 16 | } 17 | 18 | fn is_supported_model_format(val: String) -> Result<(), String> { 19 | const SUPPORTED_FORMATS: &[&str] = &["obj"]; 20 | let extension = val.rsplit('.') 21 | .next() 22 | .ok_or("can't determinate extension of file")?; 23 | if !std::path::Path::new(&val).is_file() { 24 | return Err(String::from("file doesn't exist")); 25 | } 26 | if SUPPORTED_FORMATS.contains(&extension) { 27 | Ok(()) 28 | } else { 29 | Err(format!( 30 | "model format is not supported. Supported formats: {:?}", 31 | SUPPORTED_FORMATS 32 | )) 33 | } 34 | } 35 | 36 | const LOG_LEVELS: &[&str] = &["none", "error", "warning", "perf", "info", "debug"]; 37 | 38 | fn log_level_from_str(val: &str) -> vulkano::instance::debug::MessageTypes { 39 | let idx = LOG_LEVELS 40 | .iter() 41 | .enumerate() 42 | .find(|&(_, v)| v == &val) 43 | .unwrap() 44 | .0; 45 | vulkano::instance::debug::MessageTypes { 46 | error: idx > 0, 47 | warning: idx > 1, 48 | performance_warning: idx > 2, 49 | information: idx > 3, 50 | debug: idx > 4, 51 | } 52 | } 53 | 54 | impl Args { 55 | pub fn get_matches() -> Args { 56 | let matches = clap::App::new("tracer") 57 | .version(crate_version!()) 58 | .author(crate_authors!()) 59 | .about("Interactive raytracer, that renders triangulated models") 60 | .arg( 61 | clap::Arg::with_name("model") 62 | .help("Sets the path to file with model to render") 63 | .required(true) 64 | .index(1) 65 | .validator(is_supported_model_format), 66 | ) 67 | .arg( 68 | clap::Arg::with_name("resolution") 69 | .short("r") 70 | .long("resolution") 71 | .number_of_values(2) 72 | .value_names(&["width", "height"]) 73 | .display_order(1) 74 | .help("Sets the resolution of the image [default: 640 480]"), 75 | ) 76 | .arg( 77 | clap::Arg::with_name("position") 78 | .short("p") 79 | .long("position") 80 | .number_of_values(3) 81 | .value_names(&["x", "y", "z"]) 82 | .display_order(2) 83 | .help("Sets the position of camera [default: 0.0 0.0 5.0]"), 84 | ) 85 | .arg( 86 | clap::Arg::with_name("fov") 87 | .long("fov") 88 | .number_of_values(2) 89 | .value_names(&["horizontal", "vertical"]) 90 | .display_order(3) 91 | .help("Sets the field of view [default: 40.0 40.0]"), 92 | ) 93 | .arg( 94 | clap::Arg::with_name("sensitivity") 95 | .long("sensitivity") 96 | .takes_value(true) 97 | .display_order(4) 98 | .help("Sets the sensitivity of the controls (camera movement) [default: 1.0]"), 99 | ) 100 | .arg( 101 | clap::Arg::with_name("fps-update-interval") 102 | .long("fps-update-interval") 103 | .takes_value(true) 104 | .display_order(5) 105 | .help( 106 | "Sets the interval (in milliseconds) of FPS update. \ 107 | Displayed FPS is the average in the last interval [default: 100]", 108 | ), 109 | ) 110 | .arg( 111 | clap::Arg::with_name("log-level") 112 | .long("log-level") 113 | .takes_value(true) 114 | .possible_values(LOG_LEVELS) 115 | .display_order(6) 116 | .help("Sets the log messages amount [default: perf]"), 117 | ) 118 | .arg( 119 | clap::Arg::with_name("benchmark") 120 | .long("benchmark") 121 | .display_order(7) 122 | .help("Turn on benchmarking"), 123 | ) 124 | .get_matches(); 125 | let model = matches.value_of("model").unwrap().to_string(); 126 | // kbknapp promisses `default_values` method in clap v3. But for now... 127 | let resolution = if matches.is_present("resolution") { 128 | values_t!(matches, "resolution", u32).unwrap_or_else(|e| e.exit()) 129 | } else { 130 | vec![640, 480] 131 | }; 132 | let position = if matches.is_present("position") { 133 | values_t!(matches, "position", f32).unwrap_or_else(|e| e.exit()) 134 | } else { 135 | vec![0.0, 0.0, 5.0] 136 | }; 137 | let fov = if matches.is_present("fov") { 138 | values_t!(matches, "fov", f32).unwrap_or_else(|e| e.exit()) 139 | } else { 140 | vec![40.0, 40.0] 141 | }; 142 | // ...and if I use `default_value` for this one, it will always dispaly it 143 | // in the help message if no model is passed. 144 | // which is not the end of the world but just pisses me off 145 | let sensitivity = if matches.is_present("sensitivity") { 146 | value_t!(matches, "sensitivity", f32).unwrap_or_else(|e| e.exit()) 147 | } else { 148 | 1.0 149 | }; 150 | let fps_update_interval = if matches.is_present("fps-update-interval") { 151 | value_t!(matches, "fps-update-interval", i64).unwrap_or_else(|e| e.exit()) 152 | } else { 153 | 100 154 | }; 155 | let log_level = if matches.is_present("log-level") { 156 | log_level_from_str(matches.value_of("log-level").unwrap()) 157 | } else { 158 | vulkano::instance::debug::MessageTypes::errors_and_warnings() 159 | }; 160 | let benchmark = matches.is_present("benchmark"); 161 | Args { 162 | model, 163 | resolution, 164 | position: cgmath::Vector3::new(position[0], position[1], position[2]), 165 | fov: [fov[0], fov[1]], 166 | sensitivity, 167 | fps_update_interval, 168 | log_level, 169 | benchmark, 170 | } 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /src/control/camera.rs: -------------------------------------------------------------------------------- 1 | extern crate cgmath; 2 | 3 | use cgmath::InnerSpace; 4 | 5 | use super::keyboard::Keyboard; 6 | 7 | use std::f32::consts::PI; 8 | use std::fmt; 9 | 10 | const UP: cgmath::Vector3 = cgmath::Vector3 { 11 | x: 0.0, 12 | y: 1.0, 13 | z: 0.0, 14 | }; 15 | 16 | #[derive(Debug)] 17 | pub struct Camera { 18 | position: cgmath::Vector3, 19 | view_dir: cgmath::Vector3, 20 | fov: [f32; 2], 21 | yaw: f32, 22 | pitch: f32, 23 | } 24 | 25 | impl Camera { 26 | pub fn with_position(position: cgmath::Vector3, fov: [f32; 2]) -> Camera { 27 | const DEFAULT_YAW: f32 = 0.0; 28 | const DEFAULT_PITCH: f32 = 0.0; 29 | Camera { 30 | position: position, 31 | view_dir: view_direction(DEFAULT_YAW, DEFAULT_PITCH), 32 | fov: fov, 33 | yaw: DEFAULT_YAW, 34 | pitch: DEFAULT_PITCH, 35 | } 36 | } 37 | pub fn position(&self) -> [f32; 3] { 38 | self.position.into() 39 | } 40 | pub fn view(&self) -> [f32; 3] { 41 | self.view_dir.into() 42 | } 43 | pub fn axises(&self) -> ([f32; 3], [f32; 3]) { 44 | let horiz_axis = self.view_dir.cross(UP).normalize(); 45 | let vert_axis = horiz_axis.cross(self.view_dir).normalize(); 46 | let right = horiz_axis * scale(self.fov[0]); 47 | let up = vert_axis * scale(-self.fov[1]); 48 | (up.into(), right.into()) 49 | } 50 | pub fn process_keyboard_input(&mut self, keyboard: &Keyboard, delta_seconds: f32) { 51 | const SPEED: f32 = 2.5; 52 | let relative_speed = SPEED * delta_seconds; 53 | if keyboard.forward_pressed { 54 | self.move_forward(relative_speed); 55 | } 56 | if keyboard.back_pressed { 57 | self.move_forward(-relative_speed); 58 | } 59 | if keyboard.right_pressed { 60 | self.strafe(relative_speed); 61 | } 62 | if keyboard.left_pressed { 63 | self.strafe(-relative_speed); 64 | } 65 | if keyboard.top_pressed { 66 | self.move_up(relative_speed); 67 | } 68 | if keyboard.bottom_pressed { 69 | self.move_up(-relative_speed); 70 | } 71 | } 72 | pub fn process_mouse_input(&mut self, delta: (f64, f64)) { 73 | const SPEED: f32 = 0.005; 74 | self.add_yaw(-delta.0 as f32 * SPEED); 75 | self.add_pitch(delta.1 as f32 * SPEED); 76 | } 77 | fn move_forward(&mut self, delta: f32) { 78 | self.position += self.view_dir * delta; 79 | } 80 | fn move_up(&mut self, delta: f32) { 81 | self.position += cgmath::Vector3::new(0.0, delta, 0.0); 82 | } 83 | fn strafe(&mut self, delta: f32) { 84 | self.position += self.view_dir.cross(UP).normalize() * delta; 85 | } 86 | fn add_yaw(&mut self, delta: f32) { 87 | self.yaw = (self.yaw + delta) % (2.0 * PI); 88 | self.view_dir = view_direction(self.yaw, self.pitch); 89 | } 90 | fn add_pitch(&mut self, delta: f32) { 91 | const PADDING: f32 = PI / 4.0; 92 | self.pitch = clamp(self.pitch + delta, -PI / 2.0 + PADDING, PI / 2.0 - PADDING); 93 | self.view_dir = view_direction(self.yaw, self.pitch); 94 | } 95 | } 96 | 97 | impl fmt::Display for Camera { 98 | fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { 99 | write!( 100 | f, 101 | "pos: [{:.2}, {:.2}, {:.2}], yaw: {:.2}, pitch: {:.2}", 102 | self.position.x, self.position.y, self.position.z, self.yaw, self.pitch 103 | ) 104 | } 105 | } 106 | 107 | fn view_direction(yaw: f32, pitch: f32) -> cgmath::Vector3 { 108 | -1.0 * cgmath::Vector3::new( 109 | yaw.sin() * pitch.cos(), 110 | pitch.sin(), 111 | yaw.cos() * pitch.cos(), 112 | ).normalize() 113 | } 114 | 115 | fn scale(fov: f32) -> f32 { 116 | (0.5 * fov * PI / 180.0).tan() 117 | } 118 | 119 | fn clamp(value: f32, min: f32, max: f32) -> f32 { 120 | value.min(max).max(min) 121 | } 122 | -------------------------------------------------------------------------------- /src/control/event_manager.rs: -------------------------------------------------------------------------------- 1 | extern crate winit; 2 | 3 | use super::keyboard::Keyboard; 4 | use super::mouse::Mouse; 5 | 6 | use std::mem; 7 | 8 | pub struct EventManager { 9 | pub keyboard: Keyboard, 10 | pub mouse: Mouse, 11 | done: bool, 12 | recreate_swapchain: bool, 13 | } 14 | 15 | impl EventManager { 16 | pub fn new() -> EventManager { 17 | EventManager { 18 | keyboard: Keyboard::new(), 19 | mouse: Mouse::new(), 20 | done: false, 21 | recreate_swapchain: false, 22 | } 23 | } 24 | 25 | pub fn process_event(&mut self, ev: winit::Event) { 26 | match ev { 27 | winit::Event::WindowEvent { 28 | event: winit::WindowEvent::Closed, 29 | .. 30 | } 31 | | winit::Event::WindowEvent { 32 | event: 33 | winit::WindowEvent::KeyboardInput { 34 | input: 35 | winit::KeyboardInput { 36 | state: winit::ElementState::Released, 37 | scancode: 9, 38 | .. 39 | }, 40 | .. 41 | }, 42 | .. 43 | } => self.done = true, 44 | winit::Event::WindowEvent { 45 | event: winit::WindowEvent::Resized(_, _), 46 | .. 47 | } => self.recreate_swapchain = true, 48 | winit::Event::WindowEvent { 49 | event: winit::WindowEvent::KeyboardInput { input, .. }, 50 | .. 51 | } => { 52 | self.keyboard.handle_keypress(&input); 53 | } 54 | winit::Event::DeviceEvent { 55 | event: winit::DeviceEvent::Motion { axis, value }, 56 | .. 57 | } => { 58 | self.mouse.handle_mousemove(axis, value); 59 | } 60 | _ => (), 61 | } 62 | } 63 | 64 | pub fn recreate_swapchain(&mut self) -> bool { 65 | mem::replace(&mut self.recreate_swapchain, false) 66 | } 67 | 68 | pub fn done(&mut self) -> bool { 69 | mem::replace(&mut self.done, false) 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/control/keyboard.rs: -------------------------------------------------------------------------------- 1 | extern crate winit; 2 | 3 | #[derive(Debug)] 4 | pub struct Keyboard { 5 | pub forward_pressed: bool, 6 | pub left_pressed: bool, 7 | pub right_pressed: bool, 8 | pub back_pressed: bool, 9 | pub top_pressed: bool, 10 | pub bottom_pressed: bool, 11 | } 12 | 13 | impl Keyboard { 14 | pub fn new() -> Keyboard { 15 | Keyboard { 16 | forward_pressed: false, 17 | left_pressed: false, 18 | right_pressed: false, 19 | back_pressed: false, 20 | top_pressed: false, 21 | bottom_pressed: false, 22 | } 23 | } 24 | 25 | pub fn handle_keypress(&mut self, event: &winit::KeyboardInput) { 26 | let pressed = event.state == winit::ElementState::Pressed; 27 | match event.scancode { 28 | 25 | 111 => self.forward_pressed = pressed, 29 | 38 | 113 => self.left_pressed = pressed, 30 | 40 | 114 => self.right_pressed = pressed, 31 | 39 | 116 => self.back_pressed = pressed, 32 | 50 | 62 => self.top_pressed = pressed, 33 | 37 | 105 => self.bottom_pressed = pressed, 34 | _ => (), 35 | }; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/control/mod.rs: -------------------------------------------------------------------------------- 1 | mod camera; 2 | mod keyboard; 3 | mod mouse; 4 | mod event_manager; 5 | 6 | pub use self::camera::Camera; 7 | pub use self::keyboard::Keyboard; 8 | pub use self::mouse::Mouse; 9 | pub use self::event_manager::EventManager; 10 | -------------------------------------------------------------------------------- /src/control/mouse.rs: -------------------------------------------------------------------------------- 1 | extern crate winit; 2 | 3 | use std::mem; 4 | 5 | #[derive(Debug)] 6 | pub struct Mouse { 7 | mouse_delta: (f64, f64), 8 | } 9 | 10 | impl Mouse { 11 | pub fn new() -> Mouse { 12 | Mouse { 13 | mouse_delta: (0.0, 0.0), 14 | } 15 | } 16 | pub fn handle_mousemove(&mut self, axis: winit::AxisId, value: f64) { 17 | match axis { 18 | 0 => self.mouse_delta.0 += value, 19 | 1 => self.mouse_delta.1 += value, 20 | _ => (), 21 | } 22 | } 23 | pub fn fetch_mouse_delta(&mut self) -> (f64, f64) { 24 | mem::replace(&mut self.mouse_delta, (0.0, 0.0)) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/fps_counter.rs: -------------------------------------------------------------------------------- 1 | extern crate time; 2 | 3 | pub use self::time::Duration; 4 | 5 | pub struct FPSCounter { 6 | updated_at: time::PreciseTime, 7 | refresh_rate: time::Duration, 8 | render_time: i64, 9 | frames_rendered: i64, 10 | last_fps: i64, 11 | } 12 | 13 | impl FPSCounter { 14 | pub fn new(refresh_rate: time::Duration) -> FPSCounter { 15 | FPSCounter { 16 | updated_at: time::PreciseTime::now(), 17 | refresh_rate: refresh_rate, 18 | render_time: 0, 19 | frames_rendered: 0, 20 | last_fps: 0, 21 | } 22 | } 23 | pub fn end_frame(&mut self) { 24 | self.frames_rendered += 1; 25 | let elapsed = self.updated_at.to(time::PreciseTime::now()); 26 | if elapsed > self.refresh_rate { 27 | self.updated_at = time::PreciseTime::now(); 28 | self.render_time = elapsed.num_milliseconds() / self.frames_rendered; 29 | self.last_fps = 1000 / self.render_time; 30 | self.frames_rendered = 0; 31 | } 32 | } 33 | pub fn current_fps(&self) -> i64 { 34 | self.last_fps 35 | } 36 | pub fn average_render_time(&self) -> i64 { 37 | self.render_time 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/gl_types.rs: -------------------------------------------------------------------------------- 1 | #[repr(align(16))] 2 | #[derive(Debug, Clone)] 3 | pub struct Vec2 { 4 | pub position: [f32; 2], 5 | } 6 | impl_vertex!(Vec2, position); 7 | 8 | #[repr(align(16))] 9 | #[derive(Debug, Clone)] 10 | pub struct UVec2 { 11 | pub position: [u32; 2], 12 | } 13 | impl_vertex!(UVec2, position); 14 | 15 | #[repr(align(16))] 16 | #[derive(Debug, Clone)] 17 | pub struct Vec3 { 18 | pub position: [f32; 3], 19 | } 20 | impl_vertex!(Vec3, position); 21 | 22 | pub fn vec3_min(x: &Vec3, y: &Vec3) -> Vec3 { 23 | Vec3::from_arr3([ 24 | x.position[0].min(y.position[0]), 25 | x.position[1].min(y.position[1]), 26 | x.position[2].min(y.position[2]), 27 | ]) 28 | } 29 | 30 | pub fn vec3_max(x: &Vec3, y: &Vec3) -> Vec3 { 31 | Vec3::from_arr3([ 32 | x.position[0].max(y.position[0]), 33 | x.position[1].max(y.position[1]), 34 | x.position[2].max(y.position[2]), 35 | ]) 36 | } 37 | 38 | #[repr(align(16))] 39 | #[derive(Debug, Clone)] 40 | pub struct UVec3 { 41 | pub position: [u32; 3], 42 | } 43 | impl_vertex!(UVec3, position); 44 | 45 | pub trait FromArr3 { 46 | fn from_arr3(position: [T; 3]) -> Self; 47 | } 48 | 49 | impl FromArr3 for Vec3 { 50 | fn from_arr3(position: [f32; 3]) -> Self { 51 | Vec3 { position } 52 | } 53 | } 54 | 55 | impl FromArr3 for UVec3 { 56 | fn from_arr3(position: [u32; 3]) -> Self { 57 | UVec3 { position } 58 | } 59 | } 60 | 61 | pub trait FromArr2 { 62 | fn from_arr2(position: [T; 2]) -> Self; 63 | } 64 | 65 | impl FromArr2 for Vec2 { 66 | fn from_arr2(position: [f32; 2]) -> Self { 67 | Vec2 { position } 68 | } 69 | } 70 | 71 | impl FromArr2 for UVec2 { 72 | fn from_arr2(position: [u32; 2]) -> Self { 73 | UVec2 { position } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/grid/bbox.rs: -------------------------------------------------------------------------------- 1 | extern crate vulkano; 2 | 3 | use vulkano::sync::GpuFuture; 4 | 5 | use gl_types::{FromArr3, Vec3, vec3_max, vec3_min}; 6 | 7 | use std::f32; 8 | use std::sync::Arc; 9 | 10 | const WORKGROUP_SIZE: usize = 256; 11 | 12 | mod bbox { 13 | #![allow(dead_code)] 14 | #[derive(VulkanoShader)] 15 | #[ty = "compute"] 16 | #[path = "shaders/bbox.comp"] 17 | struct Dummy; 18 | } 19 | 20 | pub struct BBox { 21 | pub min: Vec3, 22 | pub max: Vec3, 23 | } 24 | 25 | pub struct BBoxFinder { 26 | pipeline: Arc, 27 | output_buffer: Arc>, 28 | descriptor_set: Arc, 29 | work_groups_count: usize, 30 | } 31 | 32 | impl BBoxFinder { 33 | pub fn new( 34 | queue: Arc, 35 | positions: Arc, 36 | triangle_count: usize, 37 | ) -> BBoxFinder { 38 | let device = queue.device(); 39 | 40 | let pipeline = Arc::new({ 41 | let shader = 42 | bbox::Shader::load(device.clone()).expect("failed to create shader module"); 43 | vulkano::pipeline::ComputePipeline::new( 44 | device.clone(), 45 | &shader.main_entry_point(), 46 | &(), 47 | ).expect("failed to create compute pipeline") 48 | }); 49 | 50 | let work_groups_count = triangle_count / (2 * WORKGROUP_SIZE); 51 | let work_groups_count = if triangle_count % (2 * WORKGROUP_SIZE) == 0 { 52 | work_groups_count 53 | } else { 54 | work_groups_count + 1 55 | }; 56 | 57 | let output_buffer = { 58 | let data_iter = (0..2 * work_groups_count).map(|_| Vec3::from_arr3([-1.0; 3])); 59 | vulkano::buffer::CpuAccessibleBuffer::from_iter( 60 | device.clone(), 61 | vulkano::buffer::BufferUsage::all(), 62 | data_iter, 63 | ).expect("failed to create buffer") 64 | }; 65 | 66 | let descriptor_set = Arc::new( 67 | vulkano::descriptor::descriptor_set::PersistentDescriptorSet::start( 68 | pipeline.clone(), 69 | 0, 70 | ).add_buffer(positions) 71 | .unwrap() 72 | .add_buffer(output_buffer.clone()) 73 | .unwrap() 74 | .build() 75 | .unwrap(), 76 | ); 77 | 78 | BBoxFinder { 79 | pipeline, 80 | output_buffer, 81 | descriptor_set, 82 | work_groups_count, 83 | } 84 | } 85 | 86 | pub fn calculate_bbox( 87 | &self, 88 | queue: Arc, 89 | future: Box, 90 | ) -> BBox { 91 | let device = queue.device(); 92 | 93 | let command_buffer = 94 | vulkano::command_buffer::AutoCommandBufferBuilder::primary_one_time_submit( 95 | device.clone(), 96 | queue.family(), 97 | ).unwrap() 98 | .dispatch( 99 | [self.work_groups_count as u32, 1, 1], 100 | self.pipeline.clone(), 101 | self.descriptor_set.clone(), 102 | (), 103 | ) 104 | .unwrap() 105 | .build() 106 | .unwrap(); 107 | 108 | let future = future 109 | .then_execute(queue.clone(), command_buffer) 110 | .unwrap() 111 | .then_signal_fence_and_flush() 112 | .unwrap(); 113 | future.wait(None).unwrap(); 114 | 115 | let output = self.output_buffer 116 | .read() 117 | .expect("failed to lock bbox output buffer"); 118 | let (minimum_buffer, maximum_buffer) = output.split_at(self.work_groups_count); 119 | 120 | let min = minimum_buffer 121 | .into_iter() 122 | .fold(Vec3::from_arr3([f32::MAX; 3]), |min, v| vec3_min(&min, v)); 123 | let max = maximum_buffer 124 | .into_iter() 125 | .fold(Vec3::from_arr3([f32::MIN; 3]), |max, v| vec3_max(&max, v)); 126 | 127 | BBox { min, max } 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/grid/mod.rs: -------------------------------------------------------------------------------- 1 | extern crate vulkano; 2 | 3 | mod bbox; 4 | mod pair_counter; 5 | mod pair_writer; 6 | use self::bbox::{BBox, BBoxFinder}; 7 | use self::pair_counter::PairCounter; 8 | use self::pair_writer::PairWriter; 9 | 10 | use std::sync::Arc; 11 | 12 | pub struct Grid { 13 | pub bbox: BBox, 14 | pub resolution: [u32; 3], 15 | pub cell_size: [f32; 3], 16 | pub cells_buffer: Arc, 17 | pub references_buffer: Arc, 18 | } 19 | 20 | pub struct GridBuilder { 21 | queue: Arc, 22 | bbox_finder: BBoxFinder, 23 | pair_counter: PairCounter, 24 | pair_writer: PairWriter, 25 | triangle_count: usize, 26 | } 27 | 28 | impl GridBuilder { 29 | pub fn new( 30 | queue: Arc, 31 | positions: Arc, 32 | indices: Arc, 33 | triangle_count: usize, 34 | ) -> GridBuilder { 35 | let bbox_finder = BBoxFinder::new(queue.clone(), positions.clone(), triangle_count); 36 | let pair_counter = PairCounter::new( 37 | queue.clone(), 38 | positions.clone(), 39 | indices.clone(), 40 | triangle_count, 41 | ); 42 | let pair_writer = PairWriter::new(queue.clone(), triangle_count); 43 | GridBuilder { 44 | queue, 45 | bbox_finder, 46 | pair_counter, 47 | pair_writer, 48 | triangle_count, 49 | } 50 | } 51 | 52 | pub fn build( 53 | &mut self, 54 | future: Box, 55 | ) -> (Grid, Box) { 56 | let bbox = self.bbox_finder.calculate_bbox(self.queue.clone(), future); 57 | 58 | let dx = bbox.max.position[0] - bbox.min.position[0]; 59 | let dy = bbox.max.position[1] - bbox.min.position[1]; 60 | let dz = bbox.max.position[2] - bbox.min.position[2]; 61 | 62 | let grid_size = [dx, dy, dz]; 63 | let resolution = calc_grid_reolution(&grid_size, self.triangle_count); 64 | let cell_size = [ 65 | dx / resolution[0] as f32, 66 | dy / resolution[1] as f32, 67 | dz / resolution[2] as f32, 68 | ]; 69 | 70 | let count_pairs_result = self.pair_counter.count_pairs( 71 | self.queue.clone(), 72 | bbox.min.position, 73 | cell_size, 74 | resolution, 75 | ); 76 | let (cells_buffer, references_buffer, future) = 77 | self.pair_writer 78 | .write_pairs(self.queue.clone(), count_pairs_result, resolution); 79 | 80 | ( 81 | Grid { 82 | bbox, 83 | resolution, 84 | cell_size, 85 | cells_buffer, 86 | references_buffer, 87 | }, 88 | future, 89 | ) 90 | } 91 | } 92 | 93 | fn calc_grid_reolution(grid_size: &[f32; 3], triangle_count: usize) -> [u32; 3] { 94 | let volume = grid_size[0] * grid_size[1] * grid_size[2]; 95 | let k = (5.0 * triangle_count as f32 / volume).powf(1.0 / 3.0); 96 | let nx = (grid_size[0] * k).floor().max(1.0) as u32; 97 | let ny = (grid_size[1] * k).floor().max(1.0) as u32; 98 | let nz = (grid_size[2] * k).floor().max(1.0) as u32; 99 | [nx, ny, nz] 100 | } 101 | -------------------------------------------------------------------------------- /src/grid/pair_counter.rs: -------------------------------------------------------------------------------- 1 | extern crate vulkano; 2 | 3 | use gl_types::Vec3; 4 | 5 | use vulkano::sync::GpuFuture; 6 | 7 | use std::iter; 8 | use std::sync::Arc; 9 | 10 | const WORKGROUP_SIZE: usize = 256; 11 | 12 | mod count_pairs { 13 | #![allow(dead_code)] 14 | #[derive(VulkanoShader)] 15 | #[ty = "compute"] 16 | #[path = "shaders/count_pairs.comp"] 17 | struct Dummy; 18 | } 19 | 20 | pub struct PairCounter { 21 | pipeline: Arc, 22 | input_ds: Arc, 23 | uniform_buffer_pool: vulkano::buffer::CpuBufferPool, 24 | output_ds_pool: vulkano::descriptor::descriptor_set::FixedSizeDescriptorSetsPool< 25 | Arc< 26 | vulkano::pipeline::ComputePipeline< 27 | vulkano::descriptor::pipeline_layout::PipelineLayout, 28 | >, 29 | >, 30 | >, 31 | triangle_count: usize, 32 | work_groups_count: usize, 33 | } 34 | 35 | pub struct CountPairsResult { 36 | pub pair_count: usize, 37 | pub cells_buffer: Arc, 38 | pub cells_buffer_future: Box, 39 | pub min_cells_buffer: Arc, 40 | pub max_cells_buffer: Arc, 41 | } 42 | 43 | impl PairCounter { 44 | pub fn new( 45 | queue: Arc, 46 | positions: Arc, 47 | indices: Arc, 48 | triangle_count: usize, 49 | ) -> PairCounter { 50 | let device = queue.device(); 51 | 52 | let pipeline = Arc::new({ 53 | let shader = 54 | count_pairs::Shader::load(device.clone()).expect("failed to create shader module"); 55 | vulkano::pipeline::ComputePipeline::new( 56 | device.clone(), 57 | &shader.main_entry_point(), 58 | &(), 59 | ).expect("failed to create compute pipeline") 60 | }); 61 | 62 | let work_groups_count = triangle_count / WORKGROUP_SIZE; 63 | let work_groups_count = if triangle_count % WORKGROUP_SIZE == 0 { 64 | work_groups_count 65 | } else { 66 | work_groups_count + 1 67 | }; 68 | 69 | let input_ds = Arc::new( 70 | vulkano::descriptor::descriptor_set::PersistentDescriptorSet::start( 71 | pipeline.clone(), 72 | 0, 73 | ).add_buffer(positions) 74 | .unwrap() 75 | .add_buffer(indices) 76 | .unwrap() 77 | .build() 78 | .unwrap(), 79 | ); 80 | 81 | let uniform_buffer_pool = vulkano::buffer::CpuBufferPool::uniform_buffer(device.clone()); 82 | let output_ds_pool = vulkano::descriptor::descriptor_set::FixedSizeDescriptorSetsPool::new( 83 | pipeline.clone(), 84 | 1, 85 | ); 86 | 87 | PairCounter { 88 | pipeline, 89 | input_ds, 90 | uniform_buffer_pool, 91 | output_ds_pool, 92 | triangle_count, 93 | work_groups_count, 94 | } 95 | } 96 | 97 | pub fn count_pairs( 98 | &mut self, 99 | queue: Arc, 100 | min_cell: [f32; 3], 101 | cell_size: [f32; 3], 102 | grid_resolution: [u32; 3], 103 | ) -> CountPairsResult { 104 | let device = queue.device(); 105 | 106 | let parameters = self.uniform_buffer_pool 107 | .next(count_pairs::ty::Params { 108 | min_cell, 109 | cell_size, 110 | resolution: grid_resolution, 111 | _dummy0: [0; 4], 112 | _dummy1: [0; 4], 113 | }) 114 | .expect("failed to create parameters buffer"); 115 | 116 | let cell_count = grid_resolution[0] * grid_resolution[1] * grid_resolution[2]; 117 | let ref_buffer = { 118 | // create one more cell so the last one contains total references count 119 | let data_iter = (0..cell_count + 1).map(|_| 0u32); 120 | vulkano::buffer::CpuAccessibleBuffer::from_iter( 121 | device.clone(), 122 | vulkano::buffer::BufferUsage::all(), 123 | data_iter, 124 | ).expect("failed to create cells buffer") 125 | }; 126 | 127 | let min_cells_buffer = vulkano::buffer::DeviceLocalBuffer::<[Vec3]>::array( 128 | queue.device().clone(), 129 | self.triangle_count, 130 | vulkano::buffer::BufferUsage::all(), 131 | iter::once(queue.family()), 132 | ).expect("can't create references buffer"); 133 | 134 | let max_cells_buffer = vulkano::buffer::DeviceLocalBuffer::<[Vec3]>::array( 135 | queue.device().clone(), 136 | self.triangle_count, 137 | vulkano::buffer::BufferUsage::all(), 138 | iter::once(queue.family()), 139 | ).expect("can't create references buffer"); 140 | 141 | let output_ds = self.output_ds_pool 142 | .next() 143 | .add_buffer(parameters) 144 | .unwrap() 145 | .add_buffer(ref_buffer.clone()) 146 | .unwrap() 147 | .add_buffer(min_cells_buffer.clone()) 148 | .unwrap() 149 | .add_buffer(max_cells_buffer.clone()) 150 | .unwrap() 151 | .build() 152 | .unwrap(); 153 | 154 | let command_buffer = 155 | vulkano::command_buffer::AutoCommandBufferBuilder::primary_one_time_submit( 156 | device.clone(), 157 | queue.family(), 158 | ).unwrap() 159 | .dispatch( 160 | [self.work_groups_count as u32, 1, 1], 161 | self.pipeline.clone(), 162 | (self.input_ds.clone(), output_ds), 163 | (), 164 | ) 165 | .unwrap() 166 | .build() 167 | .unwrap(); 168 | 169 | let future = vulkano::sync::now(device.clone()) 170 | .then_execute(queue.clone(), command_buffer) 171 | .unwrap() 172 | .then_signal_fence_and_flush() 173 | .unwrap(); 174 | future.wait(None).unwrap(); 175 | 176 | let mut pair_count = 0; 177 | let (cells_buffer, cells_future) = { 178 | let lock = ref_buffer.read().expect("failed to read cells buffer"); 179 | let data_iter = lock.into_iter().map(|size| { 180 | let prev_count = pair_count; 181 | pair_count += size; 182 | prev_count 183 | }); 184 | vulkano::buffer::ImmutableBuffer::from_iter( 185 | data_iter, 186 | vulkano::buffer::BufferUsage::all(), 187 | queue.clone(), 188 | ).expect("failed to create references buffer") 189 | }; 190 | CountPairsResult { 191 | pair_count: pair_count as usize, 192 | cells_buffer, 193 | cells_buffer_future: Box::new(cells_future), 194 | min_cells_buffer, 195 | max_cells_buffer, 196 | } 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /src/grid/pair_writer.rs: -------------------------------------------------------------------------------- 1 | extern crate vulkano; 2 | 3 | use super::pair_counter::CountPairsResult; 4 | 5 | use vulkano::sync::GpuFuture; 6 | 7 | use std::iter; 8 | use std::sync::Arc; 9 | 10 | const WORKGROUP_SIZE: usize = 256; 11 | 12 | mod write_pairs { 13 | #![allow(dead_code)] 14 | #[derive(VulkanoShader)] 15 | #[ty = "compute"] 16 | #[path = "shaders/write_pairs.comp"] 17 | struct Dummy; 18 | } 19 | 20 | pub struct PairWriter { 21 | pipeline: Arc, 22 | uniform_buffer_pool: vulkano::buffer::CpuBufferPool, 23 | ds_pool: vulkano::descriptor::descriptor_set::FixedSizeDescriptorSetsPool< 24 | Arc< 25 | vulkano::pipeline::ComputePipeline< 26 | vulkano::descriptor::pipeline_layout::PipelineLayout, 27 | >, 28 | >, 29 | >, 30 | work_groups_count: usize, 31 | } 32 | 33 | impl PairWriter { 34 | pub fn new(queue: Arc, triangle_count: usize) -> PairWriter { 35 | let device = queue.device(); 36 | 37 | let pipeline = Arc::new({ 38 | let shader = 39 | write_pairs::Shader::load(device.clone()).expect("failed to create shader module"); 40 | vulkano::pipeline::ComputePipeline::new( 41 | device.clone(), 42 | &shader.main_entry_point(), 43 | &(), 44 | ).expect("failed to create compute pipeline") 45 | }); 46 | 47 | let work_groups_count = triangle_count / WORKGROUP_SIZE; 48 | let work_groups_count = if triangle_count % WORKGROUP_SIZE == 0 { 49 | work_groups_count 50 | } else { 51 | work_groups_count + 1 52 | }; 53 | 54 | let uniform_buffer_pool = vulkano::buffer::CpuBufferPool::uniform_buffer(device.clone()); 55 | let ds_pool = vulkano::descriptor::descriptor_set::FixedSizeDescriptorSetsPool::new( 56 | pipeline.clone(), 57 | 0, 58 | ); 59 | 60 | PairWriter { 61 | pipeline, 62 | uniform_buffer_pool, 63 | ds_pool, 64 | work_groups_count, 65 | } 66 | } 67 | 68 | pub fn write_pairs( 69 | &mut self, 70 | queue: Arc, 71 | count_pairs_result: CountPairsResult, 72 | resolution: [u32; 3], 73 | ) -> ( 74 | Arc, 75 | Arc, 76 | Box, 77 | ) { 78 | let device = queue.device(); 79 | 80 | let params_buffer = self.uniform_buffer_pool 81 | .next(write_pairs::ty::Params { resolution }) 82 | .expect("failed to create params buffer"); 83 | 84 | let cell_count = resolution[0] * resolution[1] * resolution[2]; 85 | let (current_idx_buffer, current_idx_future) = 86 | vulkano::buffer::ImmutableBuffer::from_iter( 87 | (0..cell_count).map(|_| 0u32), 88 | vulkano::buffer::BufferUsage::all(), 89 | queue.clone(), 90 | ).expect("failed to create references buffer"); 91 | 92 | let ref_buffer = vulkano::buffer::DeviceLocalBuffer::<[u32]>::array( 93 | queue.device().clone(), 94 | count_pairs_result.pair_count, 95 | vulkano::buffer::BufferUsage::all(), 96 | iter::once(queue.family()), 97 | ).expect("can't create references buffer"); 98 | 99 | let descriptor_set = self.ds_pool 100 | .next() 101 | .add_buffer(params_buffer) 102 | .unwrap() 103 | .add_buffer(count_pairs_result.min_cells_buffer) 104 | .unwrap() 105 | .add_buffer(count_pairs_result.max_cells_buffer) 106 | .unwrap() 107 | .add_buffer(count_pairs_result.cells_buffer.clone()) 108 | .unwrap() 109 | .add_buffer(current_idx_buffer) 110 | .unwrap() 111 | .add_buffer(ref_buffer.clone()) 112 | .unwrap() 113 | .build() 114 | .unwrap(); 115 | 116 | let command_buffer = 117 | vulkano::command_buffer::AutoCommandBufferBuilder::primary_one_time_submit( 118 | device.clone(), 119 | queue.family(), 120 | ).unwrap() 121 | .dispatch( 122 | [self.work_groups_count as u32, 1, 1], 123 | self.pipeline.clone(), 124 | descriptor_set, 125 | (), 126 | ) 127 | .unwrap() 128 | .build() 129 | .unwrap(); 130 | 131 | let future = count_pairs_result 132 | .cells_buffer_future 133 | .join(current_idx_future) 134 | .then_execute(queue.clone(), command_buffer) 135 | .unwrap() 136 | .then_signal_fence_and_flush() 137 | .unwrap(); 138 | 139 | ( 140 | count_pairs_result.cells_buffer, 141 | ref_buffer, 142 | Box::new(future), 143 | ) 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | #![feature(attr_literals)] 2 | 3 | extern crate cgmath; 4 | extern crate time; 5 | extern crate tobj; 6 | extern crate winit; 7 | 8 | #[macro_use] 9 | extern crate vulkano; 10 | #[macro_use] 11 | extern crate vulkano_shader_derive; 12 | extern crate vulkano_text; 13 | extern crate vulkano_win; 14 | 15 | #[macro_use] 16 | extern crate clap; 17 | #[macro_use] 18 | extern crate lazy_static; 19 | extern crate regex; 20 | 21 | mod args; 22 | mod control; 23 | mod tracers; 24 | mod render; 25 | mod fps_counter; 26 | mod gl_types; 27 | mod grid; 28 | mod scene; 29 | 30 | use args::Args; 31 | use control::EventManager; 32 | use fps_counter::FPSCounter; 33 | use render::RealTimeRender; 34 | use render::OfflineRender; 35 | 36 | fn get_layers<'a>(desired_layers: Vec<&'a str>) -> Vec<&'a str> { 37 | let available_layers: Vec<_> = vulkano::instance::layers_list().unwrap().collect(); 38 | println!("Available layers:"); 39 | for l in &available_layers { 40 | println!("\t{}", l.name()); 41 | } 42 | desired_layers 43 | .into_iter() 44 | .filter(|&l| available_layers.iter().any(|li| li.name() == l)) 45 | .collect() 46 | } 47 | 48 | fn print_message_callback(msg: &vulkano::instance::debug::Message) { 49 | lazy_static! { 50 | // Validation layers spams this error message, although this error is false positive 51 | // https://github.com/vulkano-rs/vulkano/issues/831 52 | static ref FENCE_ERROR_RE: regex::Regex = 53 | regex::Regex::new(r"Fence 0x\w* is in use.").unwrap(); 54 | } 55 | if FENCE_ERROR_RE.is_match(msg.description) { 56 | return; 57 | } 58 | 59 | let ty = if msg.ty.error { 60 | "error" 61 | } else if msg.ty.warning { 62 | "warning" 63 | } else if msg.ty.performance_warning { 64 | "perf" 65 | } else if msg.ty.information { 66 | "info" 67 | } else if msg.ty.debug { 68 | "debug" 69 | } else { 70 | panic!("no-impl"); 71 | }; 72 | println!("{} [{}] : {}", msg.layer_prefix, ty, msg.description); 73 | } 74 | 75 | fn main() { 76 | let args = Args::get_matches(); 77 | let extensions = vulkano::instance::InstanceExtensions { 78 | ext_debug_report: true, 79 | ..vulkano_win::required_extensions() 80 | }; 81 | let layers = get_layers(vec!["VK_LAYER_LUNARG_standard_validation"]); 82 | println!("Using layers: {:?}", layers); 83 | let instance = vulkano::instance::Instance::new(None, &extensions, &layers) 84 | .expect("failed to create instance"); 85 | 86 | let _debug_callback = vulkano::instance::debug::DebugCallback::new( 87 | &instance, 88 | args.log_level, 89 | print_message_callback, 90 | ).ok(); 91 | 92 | let mut camera = control::Camera::with_position(args.position, args.fov); 93 | 94 | if args.benchmark { 95 | let mut render = OfflineRender::new(&args, &instance, [args.resolution[0], args.resolution[1]]); 96 | let statistics = render.render(&camera); 97 | println!("=============== Statistics ==============="); 98 | println!("{}", statistics); 99 | } else { 100 | let mut events_loop = winit::EventsLoop::new(); 101 | let mut event_manager = EventManager::new(); 102 | let mut fps_counter = FPSCounter::new(fps_counter::Duration::milliseconds( 103 | args.fps_update_interval, 104 | )); 105 | 106 | let mut render = RealTimeRender::new(&args, &events_loop, &instance); 107 | let mut previous_frame_end = 108 | Box::new(vulkano::sync::now(render.vulkan_ctx.device.clone())) as Box<_>; 109 | 110 | loop { 111 | previous_frame_end = render.render( 112 | &mut camera, 113 | &fps_counter, 114 | event_manager.recreate_swapchain(), 115 | previous_frame_end, 116 | ); 117 | fps_counter.end_frame(); 118 | 119 | events_loop.poll_events(|event| event_manager.process_event(event)); 120 | camera.process_mouse_input(event_manager.mouse.fetch_mouse_delta()); 121 | camera.process_keyboard_input( 122 | &event_manager.keyboard, 123 | args.sensitivity * fps_counter.average_render_time() as f32 / 1000.0, 124 | ); 125 | if event_manager.done() { 126 | break; 127 | } 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/render/drawer.rs: -------------------------------------------------------------------------------- 1 | extern crate vulkano; 2 | use vulkano::descriptor::descriptor_set; 3 | 4 | extern crate vulkano_text; 5 | extern crate vulkano_win; 6 | use vulkano_text::{DrawTextTrait, UpdateTextCache}; 7 | 8 | use std::boxed::Box; 9 | use std::marker::{Send, Sync}; 10 | use std::mem; 11 | use std::sync::Arc; 12 | 13 | use gl_types::Vec2; 14 | 15 | pub struct Drawer<'a> { 16 | pub dimensions: [u32; 2], 17 | pub swapchain: Arc, 18 | pub recreate_swapchain: bool, 19 | pub texture: Arc>, 20 | images: Vec>, 21 | pipeline: Arc< 22 | vulkano::pipeline::GraphicsPipeline< 23 | vulkano::pipeline::vertex::SingleBufferDefinition, 24 | Box, 25 | Arc, 26 | >, 27 | >, 28 | set: Arc, 29 | renderpass: Arc, 30 | framebuffers: Option< 31 | Vec< 32 | Arc< 33 | vulkano::framebuffer::Framebuffer< 34 | Arc, 35 | ((), Arc), 36 | >, 37 | >, 38 | >, 39 | >, 40 | vertex_buffer: Arc>, 41 | text_drawer: vulkano_text::DrawText<'a>, 42 | } 43 | 44 | impl<'a> Drawer<'a> { 45 | pub fn new( 46 | device: Arc, 47 | window: &vulkano_win::Window, 48 | physical: vulkano::instance::PhysicalDevice, 49 | queue: Arc, 50 | ) -> (Drawer<'a>, Box) { 51 | let vs = vs::Shader::load(device.clone()).expect("failed to create shader module"); 52 | let fs = fs::Shader::load(device.clone()).expect("failed to create shader module"); 53 | 54 | let dimensions = { 55 | let (width, height) = window.window().get_inner_size_pixels().unwrap(); 56 | [width, height] 57 | }; 58 | 59 | let (swapchain, images) = create_swapchain( 60 | device.clone(), 61 | window.clone(), 62 | dimensions, 63 | physical.clone(), 64 | queue.clone(), 65 | ); 66 | 67 | let renderpass = Arc::new( 68 | single_pass_renderpass!(device.clone(), 69 | attachments: { 70 | color: { 71 | load: Clear, 72 | store: Store, 73 | format: swapchain.format(), 74 | samples: 1, 75 | } 76 | }, 77 | pass: { 78 | color: [color], 79 | depth_stencil: {} 80 | } 81 | ).unwrap(), 82 | ); 83 | 84 | let pipeline = Arc::new( 85 | vulkano::pipeline::GraphicsPipeline::start() 86 | .vertex_input_single_buffer::() 87 | .vertex_shader(vs.main_entry_point(), ()) 88 | .triangle_strip() 89 | .viewports_dynamic_scissors_irrelevant(1) 90 | .fragment_shader(fs.main_entry_point(), ()) 91 | .blend_alpha_blending() 92 | .render_pass( 93 | vulkano::framebuffer::Subpass::from( 94 | renderpass.clone() 95 | as Arc, 96 | 0, 97 | ).unwrap(), 98 | ) 99 | .build(device.clone()) 100 | .unwrap(), 101 | ); 102 | 103 | let texture = vulkano::image::StorageImage::new( 104 | device.clone(), 105 | vulkano::image::Dimensions::Dim2d { 106 | width: dimensions[0], 107 | height: dimensions[1], 108 | }, 109 | vulkano::format::R8G8B8A8Unorm, 110 | Some(queue.family()), 111 | ).unwrap(); 112 | 113 | let set = Arc::new( 114 | descriptor_set::PersistentDescriptorSet::start(pipeline.clone(), 0) 115 | .add_sampled_image(texture.clone(), create_sampler(device.clone())) 116 | .unwrap() 117 | .build() 118 | .unwrap(), 119 | ); 120 | 121 | let (vertex_buffer, vertex_future) = vulkano::buffer::ImmutableBuffer::from_iter( 122 | [ 123 | Vec2 { 124 | position: [-1.0, -1.0], 125 | }, 126 | Vec2 { 127 | position: [-1.0, 1.0], 128 | }, 129 | Vec2 { 130 | position: [1.0, -1.0], 131 | }, 132 | Vec2 { 133 | position: [1.0, 1.0], 134 | }, 135 | ].iter() 136 | .cloned(), 137 | vulkano::buffer::BufferUsage::vertex_buffer(), 138 | queue.clone(), 139 | ).expect("failed to create buffer"); 140 | 141 | let text_drawer = 142 | vulkano_text::DrawText::new(device.clone(), queue.clone(), swapchain.clone(), &images); 143 | 144 | ( 145 | Drawer { 146 | pipeline: pipeline, 147 | dimensions: dimensions, 148 | swapchain: swapchain, 149 | recreate_swapchain: false, 150 | images: images, 151 | set: set, 152 | renderpass: renderpass, 153 | framebuffers: None, 154 | texture: texture, 155 | vertex_buffer: vertex_buffer, 156 | text_drawer: text_drawer, 157 | }, 158 | Box::new(vertex_future), 159 | ) 160 | } 161 | 162 | pub fn recreate_swapchain(&mut self, window: &vulkano_win::Window) -> bool { 163 | if !self.recreate_swapchain { 164 | return false; 165 | } 166 | 167 | self.dimensions = { 168 | let (new_width, new_height) = window.window().get_inner_size_pixels().unwrap(); 169 | [new_width, new_height] 170 | }; 171 | 172 | let (new_swapchain, new_images) = 173 | match self.swapchain.recreate_with_dimension(self.dimensions) { 174 | Ok(r) => r, 175 | Err(vulkano::swapchain::SwapchainCreationError::UnsupportedDimensions) => { 176 | return true; 177 | } 178 | Err(err) => panic!("{:?}", err), 179 | }; 180 | 181 | mem::replace(&mut self.swapchain, new_swapchain); 182 | mem::replace(&mut self.images, new_images); 183 | 184 | self.framebuffers = None; 185 | self.recreate_swapchain = false; 186 | false 187 | } 188 | 189 | pub fn recreate_framebuffers(&mut self) { 190 | if self.framebuffers.is_some() { 191 | return; 192 | } 193 | 194 | let new_framebuffers = Some( 195 | self.images 196 | .iter() 197 | .map(|image| { 198 | Arc::new( 199 | vulkano::framebuffer::Framebuffer::start(self.renderpass.clone()) 200 | .add(image.clone()) 201 | .unwrap() 202 | .build() 203 | .unwrap(), 204 | ) 205 | }) 206 | .collect::>(), 207 | ); 208 | 209 | mem::replace(&mut self.framebuffers, new_framebuffers); 210 | } 211 | 212 | pub fn draw( 213 | &mut self, 214 | builder: vulkano::command_buffer::AutoCommandBufferBuilder, 215 | image_num: usize, 216 | ) -> vulkano::command_buffer::AutoCommandBufferBuilder { 217 | builder 218 | .update_text_cache(&mut self.text_drawer) 219 | .begin_render_pass( 220 | self.framebuffers.as_ref().unwrap()[image_num].clone(), 221 | false, 222 | vec![[0.0, 0.0, 1.0, 1.0].into()], 223 | ) 224 | .unwrap() 225 | .draw( 226 | self.pipeline.clone(), 227 | vulkano::command_buffer::DynamicState { 228 | line_width: None, 229 | viewports: Some(vec![ 230 | vulkano::pipeline::viewport::Viewport { 231 | origin: [0.0, 0.0], 232 | dimensions: [self.dimensions[0] as f32, self.dimensions[1] as f32], 233 | depth_range: 0.0..1.0, 234 | }, 235 | ]), 236 | scissors: None, 237 | }, 238 | self.vertex_buffer.clone(), 239 | self.set.clone(), 240 | (), 241 | ) 242 | .unwrap() 243 | .draw_text( 244 | &mut self.text_drawer, 245 | self.dimensions[0], 246 | self.dimensions[1], 247 | ) 248 | .end_render_pass() 249 | .unwrap() 250 | } 251 | 252 | pub fn queue_text(&mut self, x: f32, y: f32, size: f32, text: &str) { 253 | for (idx, line) in text.lines().enumerate() { 254 | self.text_drawer.queue_text( 255 | x, 256 | y + idx as f32 * size + size / 5.0, 257 | size, 258 | [1.0, 1.0, 1.0, 1.0], 259 | line, 260 | ); 261 | } 262 | } 263 | 264 | pub fn acquire_next_image( 265 | &mut self, 266 | ) -> Result< 267 | (usize, vulkano::swapchain::SwapchainAcquireFuture), 268 | vulkano::swapchain::AcquireError, 269 | > { 270 | match vulkano::swapchain::acquire_next_image(self.swapchain.clone(), None) { 271 | Ok(r) => Ok(r), 272 | Err(vulkano::swapchain::AcquireError::OutOfDate) => { 273 | self.recreate_swapchain = true; 274 | Err(vulkano::swapchain::AcquireError::OutOfDate) 275 | } 276 | err => err, 277 | } 278 | } 279 | } 280 | 281 | fn create_sampler(device: Arc) -> Arc { 282 | vulkano::sampler::Sampler::new( 283 | device, 284 | vulkano::sampler::Filter::Nearest, 285 | vulkano::sampler::Filter::Nearest, 286 | vulkano::sampler::MipmapMode::Nearest, 287 | vulkano::sampler::SamplerAddressMode::ClampToEdge, 288 | vulkano::sampler::SamplerAddressMode::ClampToEdge, 289 | vulkano::sampler::SamplerAddressMode::ClampToEdge, 290 | 0.0, 291 | 1.0, 292 | 0.0, 293 | 0.0, 294 | ).unwrap() 295 | } 296 | 297 | fn create_swapchain( 298 | device: Arc, 299 | window: &vulkano_win::Window, 300 | dimensions: [u32; 2], 301 | physical: vulkano::instance::PhysicalDevice, 302 | queue: Arc, 303 | ) -> ( 304 | Arc, 305 | Vec>, 306 | ) { 307 | let caps = window 308 | .surface() 309 | .capabilities(physical) 310 | .expect("failed to get surface capabilities"); 311 | 312 | let usage = caps.supported_usage_flags; 313 | let alpha = caps.supported_composite_alpha.iter().next().unwrap(); 314 | let format = caps.supported_formats[0].0; 315 | 316 | vulkano::swapchain::Swapchain::new( 317 | device, 318 | window.surface().clone(), 319 | caps.min_image_count, 320 | format, 321 | dimensions, 322 | 1, 323 | usage, 324 | &queue, 325 | vulkano::swapchain::SurfaceTransform::Identity, 326 | alpha, 327 | vulkano::swapchain::PresentMode::Fifo, 328 | true, 329 | None, 330 | ).expect("failed to create swapchain") 331 | } 332 | 333 | mod vs { 334 | #[allow(dead_code)] 335 | #[derive(VulkanoShader)] 336 | #[ty = "vertex"] 337 | #[path = "shaders/quad.vert"] 338 | struct Dummy; 339 | } 340 | 341 | mod fs { 342 | #[allow(dead_code)] 343 | #[derive(VulkanoShader)] 344 | #[ty = "fragment"] 345 | #[path = "shaders/quad.frag"] 346 | struct Dummy; 347 | } 348 | -------------------------------------------------------------------------------- /src/render/mod.rs: -------------------------------------------------------------------------------- 1 | mod drawer; 2 | mod offline; 3 | mod realtime; 4 | mod vulkan_ctx; 5 | 6 | pub use self::drawer::Drawer; 7 | pub use self::offline::OfflineRender; 8 | pub use self::realtime::RealTimeRender; 9 | pub use self::vulkan_ctx::VulkanCtx; 10 | -------------------------------------------------------------------------------- /src/render/offline.rs: -------------------------------------------------------------------------------- 1 | extern crate time; 2 | extern crate vulkano; 3 | 4 | use vulkano::sync::GpuFuture; 5 | 6 | use super::vulkan_ctx::VulkanCtx; 7 | 8 | use args::Args; 9 | use control::Camera; 10 | use tracers; 11 | use grid::Grid; 12 | 13 | use std::mem; 14 | use std::path::Path; 15 | use std::sync::Arc; 16 | use std::fmt; 17 | 18 | pub struct OfflineRender<'a> { 19 | vulkan_ctx: VulkanCtx<'a>, 20 | statistics_buffer: Arc>, 21 | texture: Arc>, 22 | dimensions: [u32; 2], 23 | } 24 | 25 | impl<'a> OfflineRender<'a> { 26 | pub fn new( 27 | args: &Args, 28 | instance: &'a Arc, 29 | dimensions: [u32; 2], 30 | ) -> OfflineRender<'a> { 31 | let (vulkan_ctx, _) = 32 | VulkanCtx::new(&instance, Path::new(&args.model), |&q| q.supports_compute()); 33 | let statistics_buffer = 34 | vulkano::buffer::CpuAccessibleBuffer::::from_data( 35 | vulkan_ctx.device.clone(), 36 | vulkano::buffer::BufferUsage::all(), 37 | tracers::ty::Statistics { 38 | triangle_intersections: 0, 39 | triangle_tests: 0, 40 | cell_intersections: 0, 41 | }, 42 | ).unwrap(); 43 | 44 | let texture = vulkano::image::StorageImage::new( 45 | vulkan_ctx.device.clone(), 46 | vulkano::image::Dimensions::Dim2d { 47 | width: dimensions[0], 48 | height: dimensions[1], 49 | }, 50 | vulkano::format::R8G8B8A8Unorm, 51 | Some(vulkan_ctx.queue.family()), 52 | ).unwrap(); 53 | 54 | OfflineRender { 55 | vulkan_ctx, 56 | statistics_buffer, 57 | texture, 58 | dimensions, 59 | } 60 | } 61 | 62 | pub fn render(&mut self, camera: &Camera) -> Statistics { 63 | let grid_start = time::PreciseTime::now(); 64 | let (grid, future) = self.vulkan_ctx 65 | .grid_builder 66 | .build(Box::new(vulkano::sync::now(self.vulkan_ctx.device.clone()))); 67 | mem::drop(future); 68 | let grid_build_time = grid_start.to(time::PreciseTime::now()).num_milliseconds(); 69 | 70 | let cb = { 71 | let mut cbb = 72 | vulkano::command_buffer::AutoCommandBufferBuilder::primary_one_time_submit( 73 | self.vulkan_ctx.device.clone(), 74 | self.vulkan_ctx.queue.family(), 75 | ).unwrap(); 76 | 77 | cbb = self.vulkan_ctx.tracer.render( 78 | cbb, 79 | self.texture.clone(), 80 | self.statistics_buffer.clone(), 81 | &camera, 82 | &grid, 83 | ); 84 | 85 | cbb.build().unwrap() 86 | }; 87 | 88 | let render_start = time::PreciseTime::now(); 89 | let future = vulkano::sync::now(self.vulkan_ctx.device.clone()) 90 | .then_execute(self.vulkan_ctx.queue.clone(), cb) 91 | .unwrap() 92 | .then_signal_fence_and_flush() 93 | .unwrap(); 94 | future.wait(None).unwrap(); 95 | let render_time = render_start.to(time::PreciseTime::now()).num_milliseconds(); 96 | 97 | let render_statistics = *self.statistics_buffer 98 | .read() 99 | .expect("failed to lock buffer for reading"); 100 | 101 | let primary_rays = self.dimensions[0] * self.dimensions[1]; 102 | Statistics { 103 | grid_build_time, 104 | render_time, 105 | triangle_count: self.vulkan_ctx.scene_buffers.triangle_count, 106 | primary_rays, 107 | render_statistics, 108 | grid, 109 | } 110 | } 111 | } 112 | 113 | pub struct Statistics { 114 | grid_build_time: i64, 115 | render_time: i64, 116 | triangle_count: usize, 117 | primary_rays: u32, 118 | render_statistics: tracers::ty::Statistics, 119 | grid: Grid, 120 | } 121 | 122 | impl fmt::Display for Statistics { 123 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 124 | let render_time = self.grid_build_time + self.render_time; 125 | writeln!(f, "\n>>> General")?; 126 | writeln!( 127 | f, 128 | "\ttotal time: {} ms ({} FPS)", 129 | render_time, 130 | 1000 / render_time 131 | )?; 132 | writeln!( 133 | f, 134 | "\ttriangles: {}", 135 | self.triangle_count 136 | )?; 137 | writeln!(f, "\tprimary rays: {}", self.primary_rays)?; 138 | writeln!(f, "\n>>> Triangle")?; 139 | writeln!(f, "\ttests: {}", self.render_statistics.triangle_tests)?; 140 | writeln!(f, "\tintersections: {}", self.render_statistics.triangle_intersections)?; 141 | writeln!( 142 | f, 143 | "\ttests per ray: {}", 144 | self.render_statistics.triangle_tests as f32 / self.primary_rays as f32 145 | )?; 146 | writeln!( 147 | f, 148 | "\ttests per triangle: {}", 149 | self.render_statistics.triangle_tests as f32 / self.triangle_count as f32 150 | )?; 151 | writeln!(f, "\n>>> Grid")?; 152 | writeln!(f, "\tbuild time: {} ms", self.grid_build_time)?; 153 | let grid_size = [ 154 | self.grid.bbox.max.position[0] - self.grid.bbox.min.position[0], 155 | self.grid.bbox.max.position[1] - self.grid.bbox.min.position[1], 156 | self.grid.bbox.max.position[2] - self.grid.bbox.min.position[2], 157 | ]; 158 | writeln!(f, "\tsize: {:?}", grid_size)?; 159 | writeln!(f, "\tresolution: {:?}", self.grid.resolution)?; 160 | let cell_count = self.grid.resolution[0] * self.grid.resolution[1] * self.grid.resolution[2]; 161 | writeln!(f, "\tcell count: {}", cell_count)?; 162 | writeln!(f, "\tcell size: {:?}", self.grid.cell_size)?; 163 | writeln!(f, "\tcell intersections: {}", self.render_statistics.cell_intersections)?; 164 | writeln!( 165 | f, 166 | "\tintersections per ray: {}", 167 | self.render_statistics.cell_intersections as f32 / self.primary_rays as f32 168 | )?; 169 | writeln!( 170 | f, 171 | "\tintersections per cell: {}", 172 | self.render_statistics.cell_intersections as f32 / cell_count as f32 173 | ) 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /src/render/realtime.rs: -------------------------------------------------------------------------------- 1 | extern crate vulkano; 2 | extern crate vulkano_win; 3 | extern crate winit; 4 | 5 | use vulkano::sync::GpuFuture; 6 | use vulkano_win::VkSurfaceBuild; 7 | 8 | use super::vulkan_ctx::VulkanCtx; 9 | use super::drawer::Drawer; 10 | 11 | use args::Args; 12 | use control; 13 | use tracers; 14 | use fps_counter::FPSCounter; 15 | 16 | use std::path::Path; 17 | use std::sync::Arc; 18 | 19 | pub struct RealTimeRender<'a> { 20 | pub vulkan_ctx: VulkanCtx<'a>, 21 | window: vulkano_win::Window, 22 | drawer: Drawer<'a>, 23 | } 24 | 25 | impl<'a> RealTimeRender<'a> { 26 | pub fn new( 27 | args: &Args, 28 | events_loop: &winit::EventsLoop, 29 | instance: &'a Arc, 30 | ) -> RealTimeRender<'a> { 31 | let window = winit::WindowBuilder::new() 32 | .with_min_dimensions(args.resolution[0], args.resolution[1]) 33 | .with_max_dimensions(args.resolution[0], args.resolution[1]) 34 | .build_vk_surface(&events_loop, instance.clone()) 35 | .unwrap(); 36 | window.window().set_cursor(winit::MouseCursor::NoneCursor); 37 | 38 | let (vulkan_ctx, _) = VulkanCtx::new(&instance, Path::new(&args.model), |&q| { 39 | q.supports_graphics() && window.surface().is_supported(q).unwrap_or(false) 40 | }); 41 | 42 | let (drawer, _) = Drawer::new( 43 | vulkan_ctx.device.clone(), 44 | &window, 45 | vulkan_ctx.physical.clone(), 46 | vulkan_ctx.queue.clone(), 47 | ); 48 | 49 | RealTimeRender { 50 | vulkan_ctx, 51 | window, 52 | drawer, 53 | } 54 | } 55 | 56 | pub fn render( 57 | &mut self, 58 | camera: &mut control::Camera, 59 | fps_counter: &FPSCounter, 60 | recreate_swapchain: bool, 61 | mut previous_frame_end: Box, 62 | ) -> Box { 63 | previous_frame_end.cleanup_finished(); 64 | 65 | if self.drawer.recreate_swapchain(&self.window) { 66 | return previous_frame_end; 67 | } 68 | 69 | self.drawer.recreate_framebuffers(); 70 | 71 | let (image_num, aquire_future) = match self.drawer.acquire_next_image() { 72 | Ok(r) => r, 73 | Err(vulkano::swapchain::AcquireError::OutOfDate) => { 74 | return previous_frame_end; 75 | } 76 | Err(err) => panic!("{:?}", err), 77 | }; 78 | 79 | let (grid, grid_future) = self.vulkan_ctx 80 | .grid_builder 81 | .build(Box::new(vulkano::sync::now(self.vulkan_ctx.device.clone()))); 82 | 83 | // FIXME: it is not used here, but is required for tracer.render() 84 | let statistics_buffer = 85 | vulkano::buffer::CpuAccessibleBuffer::::from_data( 86 | self.vulkan_ctx.device.clone(), 87 | vulkano::buffer::BufferUsage::all(), 88 | tracers::ty::Statistics { 89 | triangle_intersections: 0, 90 | triangle_tests: 0, 91 | cell_intersections: 0, 92 | }, 93 | ).unwrap(); 94 | 95 | let cb = { 96 | let mut cbb = 97 | vulkano::command_buffer::AutoCommandBufferBuilder::primary_one_time_submit( 98 | self.vulkan_ctx.device.clone(), 99 | self.vulkan_ctx.queue.family(), 100 | ).unwrap(); 101 | cbb = self.vulkan_ctx.tracer.render( 102 | cbb, 103 | self.drawer.texture.clone(), 104 | statistics_buffer.clone(), 105 | &camera, 106 | &grid, 107 | ); 108 | cbb = self.drawer.draw(cbb, image_num); 109 | cbb.build().unwrap() 110 | }; 111 | 112 | self.drawer.recreate_swapchain = recreate_swapchain; 113 | 114 | let future = previous_frame_end 115 | .join(aquire_future) 116 | .join(grid_future) 117 | .then_execute(self.vulkan_ctx.queue.clone(), cb) 118 | .unwrap() 119 | .then_swapchain_present( 120 | self.vulkan_ctx.queue.clone(), 121 | self.drawer.swapchain.clone(), 122 | image_num, 123 | ) 124 | .then_signal_fence_and_flush() 125 | .unwrap(); 126 | 127 | self.drawer.queue_text( 128 | 10.0, 129 | 20.0, 130 | 20.0, 131 | &format!( 132 | "Using device: {}\nRender time: {} ms ({} FPS)\nCamera: {}", 133 | self.vulkan_ctx.physical.name(), 134 | fps_counter.average_render_time(), 135 | fps_counter.current_fps(), 136 | camera 137 | ), 138 | ); 139 | 140 | Box::new(future) 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /src/render/vulkan_ctx.rs: -------------------------------------------------------------------------------- 1 | extern crate vulkano; 2 | 3 | use grid; 4 | use scene; 5 | 6 | use std::path::Path; 7 | use std::sync::Arc; 8 | use tracers::{RaycastingShader, Tracer}; 9 | 10 | pub struct VulkanCtx<'a> { 11 | pub physical: vulkano::instance::PhysicalDevice<'a>, 12 | pub device: Arc, 13 | pub queue: Arc, 14 | pub scene_buffers: scene::ModelBuffers, 15 | pub grid_builder: grid::GridBuilder, 16 | pub tracer: Tracer, 17 | } 18 | 19 | impl<'a> VulkanCtx<'a> { 20 | pub fn new

( 21 | instance: &'a Arc, 22 | model_path: &Path, 23 | predicate: P, 24 | ) -> (VulkanCtx<'a>, Box) 25 | where 26 | for<'r> P: FnMut(&'r vulkano::instance::QueueFamily) -> bool, 27 | { 28 | let physical = vulkano::instance::PhysicalDevice::enumerate(instance) 29 | .next() 30 | .expect("no device available"); 31 | println!( 32 | "Using device: {} (type: {:?})", 33 | physical.name(), 34 | physical.ty() 35 | ); 36 | let queue = physical 37 | .queue_families() 38 | .find(predicate) 39 | .expect("couldn't find a graphical queue family"); 40 | let device_ext = vulkano::device::DeviceExtensions { 41 | khr_swapchain: true, 42 | ..vulkano::device::DeviceExtensions::none() 43 | }; 44 | let (device, mut queues) = vulkano::device::Device::new( 45 | physical, 46 | physical.supported_features(), 47 | &device_ext, 48 | [(queue, 0.5)].iter().cloned(), 49 | ).expect("failed to create device"); 50 | let queue = queues.next().unwrap(); 51 | 52 | let (scene_buffers, load_future) = 53 | scene::ModelBuffers::from_obj(model_path, device.clone(), queue.clone()) 54 | .expect("failed to load model"); 55 | 56 | let tracer = Tracer::new(device.clone(), &scene_buffers, RaycastingShader {}).unwrap(); 57 | 58 | let grid_builder = grid::GridBuilder::new( 59 | queue.clone(), 60 | scene_buffers.positions.clone(), 61 | scene_buffers.indices.clone(), 62 | scene_buffers.triangle_count, 63 | ); 64 | 65 | ( 66 | VulkanCtx { 67 | physical, 68 | device, 69 | queue, 70 | scene_buffers, 71 | grid_builder, 72 | tracer, 73 | }, 74 | load_future, 75 | ) 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/scene.rs: -------------------------------------------------------------------------------- 1 | extern crate image; 2 | extern crate tobj; 3 | extern crate vulkano; 4 | 5 | use gl_types::{FromArr2, FromArr3, UVec3, Vec2, Vec3}; 6 | 7 | use vulkano::sync::GpuFuture; 8 | 9 | use std::path::Path; 10 | use std::sync::Arc; 11 | 12 | use tracers; 13 | 14 | pub struct ModelBuffers { 15 | pub models: Arc, 16 | pub positions: Arc, 17 | pub indices: Arc, 18 | pub normals: Arc, 19 | pub texcoords: Arc, 20 | pub materials: Arc, 21 | pub textures: Vec>>, 22 | pub triangle_count: usize, 23 | } 24 | 25 | impl ModelBuffers { 26 | pub fn from_obj( 27 | path: &Path, 28 | device: Arc, 29 | queue: Arc, 30 | ) -> Result<(ModelBuffers, Box), tobj::LoadError> { 31 | use tobj; 32 | let (obj_models, obj_materials) = tobj::load_obj(&path)?; 33 | let (models, positions, indices, normals, texcoords) = load_mesh(obj_models); 34 | let (materials, textures, textures_future) = 35 | load_materials(device.clone(), queue.clone(), obj_materials); 36 | 37 | let (buffer_models, models_future) = vulkano::buffer::ImmutableBuffer::from_iter( 38 | models.into_iter(), 39 | vulkano::buffer::BufferUsage { 40 | storage_buffer: true, 41 | ..vulkano::buffer::BufferUsage::none() 42 | }, 43 | queue.clone(), 44 | ).unwrap(); 45 | 46 | let (buffer_positions, positions_future) = 47 | to_buffer_vec3::(queue.clone(), &positions); 48 | let (buffer_indices, indices_future) = 49 | to_buffer_vec3::(queue.clone(), &indices); 50 | let (buffer_normals, normals_future) = 51 | to_buffer_vec3::(queue.clone(), &normals); 52 | let (buffer_texcoords, texcoords_future) = 53 | to_buffer_vec2::(queue.clone(), &texcoords); 54 | let (buffer_materials, materials_future) = vulkano::buffer::ImmutableBuffer::from_iter( 55 | materials.into_iter(), 56 | vulkano::buffer::BufferUsage { 57 | storage_buffer: true, 58 | ..vulkano::buffer::BufferUsage::none() 59 | }, 60 | queue.clone(), 61 | ).unwrap(); 62 | 63 | let future = Box::new( 64 | textures_future 65 | .join(models_future) 66 | .join(positions_future) 67 | .join(indices_future) 68 | .join(normals_future) 69 | .join(texcoords_future) 70 | .join(materials_future), 71 | ) as Box<_>; 72 | 73 | Ok(( 74 | ModelBuffers { 75 | models: buffer_models, 76 | positions: buffer_positions, 77 | indices: buffer_indices, 78 | normals: buffer_normals, 79 | texcoords: buffer_texcoords, 80 | materials: buffer_materials, 81 | textures: textures, 82 | triangle_count: indices.len() / 3, 83 | }, 84 | future, 85 | )) 86 | } 87 | 88 | pub fn build_descriptor_set( 89 | &self, 90 | device: Arc, 91 | pipeline: Arc, 92 | set_id: usize, 93 | ) -> Result< 94 | Arc, 95 | vulkano::descriptor::descriptor_set::PersistentDescriptorSetError, 96 | > { 97 | let sampler = vulkano::sampler::Sampler::simple_repeat_linear(device.clone()); 98 | let ds = vulkano::descriptor::descriptor_set::PersistentDescriptorSet::start( 99 | pipeline.clone(), 100 | set_id, 101 | ).add_buffer(self.positions.clone())? 102 | .add_buffer(self.indices.clone())? 103 | .add_buffer(self.normals.clone())? 104 | .add_buffer(self.texcoords.clone())? 105 | .add_buffer(self.models.clone())? 106 | .add_buffer(self.materials.clone())? 107 | .enter_array()? 108 | .add_sampled_image(self.textures[0].clone(), sampler.clone())? 109 | .add_sampled_image(self.textures[1].clone(), sampler.clone())? 110 | .add_sampled_image(self.textures[2].clone(), sampler.clone())? 111 | .add_sampled_image(self.textures[3].clone(), sampler.clone())? 112 | .add_sampled_image(self.textures[4].clone(), sampler.clone())? 113 | .add_sampled_image(self.textures[5].clone(), sampler.clone())? 114 | .add_sampled_image(self.textures[6].clone(), sampler.clone())? 115 | .add_sampled_image(self.textures[7].clone(), sampler.clone())? 116 | .add_sampled_image(self.textures[8].clone(), sampler.clone())? 117 | .add_sampled_image(self.textures[9].clone(), sampler.clone())? 118 | .add_sampled_image(self.textures[10].clone(), sampler.clone())? 119 | .add_sampled_image(self.textures[11].clone(), sampler.clone())? 120 | .add_sampled_image(self.textures[12].clone(), sampler.clone())? 121 | .add_sampled_image(self.textures[13].clone(), sampler.clone())? 122 | .add_sampled_image(self.textures[14].clone(), sampler.clone())? 123 | .add_sampled_image(self.textures[15].clone(), sampler.clone())? 124 | .leave_array()? 125 | .build() 126 | .expect("failed to build scene descriptor set"); 127 | Ok(Arc::new(ds)) 128 | } 129 | } 130 | 131 | fn load_materials( 132 | device: Arc, 133 | queue: Arc, 134 | obj_materials: Vec, 135 | ) -> ( 136 | Vec, 137 | Vec>>, 138 | Box, 139 | ) { 140 | let mut materials = Vec::new(); 141 | let mut textures = Vec::with_capacity(16); 142 | let (ei, mut future) = empty_image(queue.clone()); 143 | for obj_material in obj_materials { 144 | let (material, texture, f) = load_material( 145 | &obj_material, 146 | textures.len() as i32, 147 | device.clone(), 148 | queue.clone(), 149 | ).unwrap(); 150 | materials.push(material); 151 | future = Box::new(future.join(f)); 152 | match texture { 153 | Some(t) => { 154 | textures.push(t); 155 | } 156 | None => (), 157 | }; 158 | } 159 | for _ in 0..16 - textures.len() { 160 | textures.push(ei.clone()); 161 | } 162 | (materials, textures, future) 163 | } 164 | 165 | fn load_mesh( 166 | obj_models: Vec, 167 | ) -> (Vec, Vec, Vec, Vec, Vec) { 168 | let mut models = Vec::new(); 169 | let mut positions = Vec::new(); 170 | let mut indices = Vec::new(); 171 | let mut normals = Vec::new(); 172 | let mut texcoords = Vec::new(); 173 | 174 | for obj_model in obj_models { 175 | let mut mesh = obj_model.mesh; 176 | 177 | let material_idx = match mesh.material_id { 178 | Some(id) => id as i32, 179 | None => panic!("meshes without material are not supported"), 180 | }; 181 | models.push(tracers::ty::Model { 182 | indices_start: indices.len() as u32 / 3, 183 | indices_end: (indices.len() + mesh.indices.len()) as u32 / 3, 184 | material_idx: material_idx, 185 | _dummy0: [0; 4], 186 | }); 187 | 188 | indices.extend( 189 | mesh.indices 190 | .into_iter() 191 | .map(|i| i + positions.len() as u32 / 3), 192 | ); 193 | positions.append(&mut mesh.positions); 194 | normals.append(&mut mesh.normals); 195 | texcoords.append(&mut mesh.texcoords); 196 | } 197 | (models, positions, indices, normals, texcoords) 198 | } 199 | 200 | fn empty_image( 201 | queue: Arc, 202 | ) -> ( 203 | Arc>, 204 | Box, 205 | ) { 206 | let pixel = vec![255u8; 4]; 207 | let (texture, future) = vulkano::image::immutable::ImmutableImage::from_iter( 208 | pixel.into_iter(), 209 | vulkano::image::Dimensions::Dim2d { 210 | width: 1, 211 | height: 1, 212 | }, 213 | vulkano::format::R8G8B8A8Srgb, 214 | queue, 215 | ).unwrap(); 216 | (texture, Box::new(future)) 217 | } 218 | 219 | fn load_texture( 220 | path: &Path, 221 | queue: Arc, 222 | ) -> image::ImageResult<( 223 | Arc>, 224 | Box, 225 | )> { 226 | let image = image::open(path)?.to_rgba(); 227 | let dimensions = image.dimensions(); 228 | let image_data = image.into_raw().clone(); 229 | 230 | let (texture, future) = vulkano::image::immutable::ImmutableImage::from_iter( 231 | image_data.iter().cloned(), 232 | vulkano::image::Dimensions::Dim2d { 233 | width: dimensions.0, 234 | height: dimensions.1, 235 | }, 236 | vulkano::format::R8G8B8A8Srgb, 237 | queue, 238 | ).unwrap(); 239 | Ok((texture, Box::new(future))) 240 | } 241 | 242 | fn load_material( 243 | material: &tobj::Material, 244 | texture_idx: i32, 245 | device: Arc, 246 | queue: Arc, 247 | ) -> image::ImageResult<( 248 | tracers::ty::Material, 249 | Option>>, 250 | Box, 251 | )> { 252 | let (texture, future, texture_idx) = if material.diffuse_texture != "" { 253 | let (texture, future) = load_texture(&Path::new(&material.diffuse_texture), queue)?; 254 | (Some(texture), future, texture_idx) 255 | } else { 256 | ( 257 | None, 258 | Box::new(vulkano::sync::now(device.clone())) as Box, 259 | -1, 260 | ) 261 | }; 262 | let gpu_material = tracers::ty::Material { 263 | ambient: material.ambient, 264 | diffuse: material.diffuse, 265 | specular: material.specular, 266 | shininess: material.shininess, 267 | dissolve: material.dissolve, 268 | optical_density: material.optical_density, 269 | ambient_texture_idx: -1, 270 | diffuse_texture_idx: texture_idx, 271 | specular_texture_idx: -1, 272 | normal_texture_idx: -1, 273 | disolve_texture_idx: -1, 274 | _dummy0: [0; 4], 275 | _dummy1: [0; 4], 276 | _dummy2: [0; 4], 277 | }; 278 | Ok((gpu_material, texture, future)) 279 | } 280 | 281 | fn to_buffer_vec2<'a, T, V>( 282 | queue: Arc, 283 | vec: &[T], 284 | ) -> ( 285 | Arc, 286 | Box, 287 | ) 288 | where 289 | V: 'static + FromArr2 + Sync + Send, 290 | T: Copy, 291 | { 292 | let (buffer, future) = vulkano::buffer::ImmutableBuffer::from_iter( 293 | vec.chunks(2) 294 | .map(|chunk| V::from_arr2([chunk[0], chunk[1]])), 295 | vulkano::buffer::BufferUsage { 296 | storage_buffer: true, 297 | ..vulkano::buffer::BufferUsage::none() 298 | }, 299 | queue, 300 | ).expect("failed to create indices buffer"); 301 | (buffer, Box::new(future)) 302 | } 303 | 304 | fn to_buffer_vec3<'a, T, V>( 305 | queue: Arc, 306 | vec: &[T], 307 | ) -> ( 308 | Arc, 309 | Box, 310 | ) 311 | where 312 | V: 'static + FromArr3 + Sync + Send, 313 | T: Copy, 314 | { 315 | let (buffer, future) = vulkano::buffer::ImmutableBuffer::from_iter( 316 | vec.chunks(3) 317 | .map(|chunk| V::from_arr3([chunk[0], chunk[1], chunk[2]])), 318 | vulkano::buffer::BufferUsage { 319 | storage_buffer: true, 320 | ..vulkano::buffer::BufferUsage::none() 321 | }, 322 | queue, 323 | ).expect("failed to create indices buffer"); 324 | (buffer, Box::new(future)) 325 | } 326 | -------------------------------------------------------------------------------- /src/tracers/mod.rs: -------------------------------------------------------------------------------- 1 | mod raycasting; 2 | mod tracer; 3 | 4 | pub use self::raycasting::{RaycastingShader, ty}; 5 | pub use self::tracer::{Tracer, TracingShader}; 6 | -------------------------------------------------------------------------------- /src/tracers/raycasting.rs: -------------------------------------------------------------------------------- 1 | extern crate vulkano; 2 | 3 | use control::Camera; 4 | use grid::Grid; 5 | use tracers::TracingShader; 6 | 7 | use std::sync::Arc; 8 | 9 | mod shader { 10 | #![allow(dead_code)] 11 | #[derive(VulkanoShader)] 12 | #[ty = "compute"] 13 | #[path = "target/shaders/raycasting.comp"] 14 | struct Dummy; 15 | } 16 | 17 | pub struct RaycastingShader {} 18 | 19 | impl TracingShader for RaycastingShader { 20 | type Uniform = ty::Uniform; 21 | type Shader = self::shader::Shader; 22 | type Layout = self::shader::Layout; 23 | 24 | fn load_shader(self, device: Arc) -> Self::Shader { 25 | Self::Shader::load(device.clone()).expect("failed to create shader module") 26 | } 27 | fn new_uniform(self, camera: &Camera, grid: &Grid) -> Self::Uniform { 28 | Self::Uniform::new(camera, grid) 29 | } 30 | } 31 | 32 | pub use self::shader::{ty, Layout, Shader}; 33 | 34 | impl ty::Uniform { 35 | pub fn new(camera: &Camera, grid: &Grid) -> ty::Uniform { 36 | ty::Uniform { 37 | camera: ty::Camera::new(&camera), 38 | grid: ty::Grid::new(&grid), 39 | _dummy0: [0; 4], 40 | } 41 | } 42 | } 43 | 44 | impl ty::Camera { 45 | fn new(camera: &Camera) -> ty::Camera { 46 | let (up, right) = camera.axises(); 47 | ty::Camera { 48 | position: camera.position(), 49 | view: camera.view(), 50 | up, 51 | right, 52 | _dummy0: [0; 4], 53 | _dummy1: [0; 4], 54 | _dummy2: [0; 4], 55 | } 56 | } 57 | } 58 | 59 | impl ty::Grid { 60 | fn new(grid: &Grid) -> ty::Grid { 61 | ty::Grid { 62 | minimum_cell: grid.bbox.min.position, 63 | maximum_cell: grid.bbox.max.position, 64 | resolution: grid.resolution, 65 | cell_size: grid.cell_size, 66 | _dummy0: [0; 4], 67 | _dummy1: [0; 4], 68 | _dummy2: [0; 4], 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/tracers/tracer.rs: -------------------------------------------------------------------------------- 1 | extern crate vulkano; 2 | use vulkano::descriptor::descriptor_set; 3 | 4 | use super::raycasting; 5 | 6 | use control::Camera; 7 | use grid::Grid; 8 | use scene; 9 | 10 | use std::sync::Arc; 11 | 12 | pub trait TracingShader { 13 | type Layout; 14 | type Uniform; 15 | type Shader; 16 | 17 | fn load_shader(self, device: Arc) -> Self::Shader; 18 | fn new_uniform(self, camera: &Camera, grid: &Grid) -> Self::Uniform; 19 | } 20 | 21 | pub struct Tracer { 22 | pipeline: Arc, 23 | uniform_buffer_pool: vulkano::buffer::CpuBufferPool, 24 | ds_pool: descriptor_set::FixedSizeDescriptorSetsPool< 25 | Arc< 26 | vulkano::pipeline::ComputePipeline< 27 | vulkano::descriptor::pipeline_layout::PipelineLayout, 28 | >, 29 | >, 30 | >, 31 | model_set: Arc, 32 | } 33 | 34 | impl> 35 | Tracer 36 | { 37 | pub fn new( 38 | device: Arc, 39 | scene_buffers: &scene::ModelBuffers, 40 | traing_shader: TS, 41 | ) -> Result, descriptor_set::PersistentDescriptorSetError> { 42 | let shader = traing_shader.load_shader(device.clone()); 43 | let pipeline = Arc::new( 44 | vulkano::pipeline::ComputePipeline::new( 45 | device.clone(), 46 | &shader.main_entry_point(), 47 | &(), 48 | ).expect("failed to create compute pipeline"), 49 | ); 50 | let uniform_buffer_pool = vulkano::buffer::CpuBufferPool::uniform_buffer(device.clone()); 51 | let ds_pool = descriptor_set::FixedSizeDescriptorSetsPool::new(pipeline.clone(), 0); 52 | let model_set = scene_buffers 53 | .build_descriptor_set(device.clone(), pipeline.clone(), 1) 54 | .expect("failed to build scene descriptor set"); 55 | 56 | Ok(Tracer { 57 | pipeline, 58 | uniform_buffer_pool, 59 | ds_pool, 60 | model_set, 61 | }) 62 | } 63 | 64 | pub fn render( 65 | &mut self, 66 | builder: vulkano::command_buffer::AutoCommandBufferBuilder, 67 | texture: Arc>, 68 | statistics: Arc, 69 | camera: &Camera, 70 | grid: &Grid, 71 | ) -> vulkano::command_buffer::AutoCommandBufferBuilder { 72 | let dimensions = texture.dimensions(); 73 | let uniform_buffer = self.uniform_buffer_pool 74 | .next(TS::Uniform::new(&camera, &grid)) 75 | .expect("failed to create uniform buffer"); 76 | let ds = self.ds_pool 77 | .next() 78 | .add_image(texture) 79 | .unwrap() 80 | .add_buffer(uniform_buffer) 81 | .unwrap() 82 | .add_buffer(statistics) 83 | .unwrap() 84 | .add_buffer(grid.cells_buffer.clone()) 85 | .unwrap() 86 | .add_buffer(grid.references_buffer.clone()) 87 | .unwrap() 88 | .build() 89 | .unwrap(); 90 | builder 91 | .dispatch( 92 | [dimensions.width() / 16, dimensions.height() / 16, 1], 93 | self.pipeline.clone(), 94 | (ds, self.model_set.clone()), 95 | (), 96 | ) 97 | .unwrap() 98 | } 99 | } 100 | --------------------------------------------------------------------------------