forked from itycodes/MineClone
				
			- The state of the game is now tracked using a simple state
  machine, where the main struct representing the game is `Game`
  and its state a generic implementing `GameState` on that struct,
  the following states exist:
  - `Unstarted`: Nothing has been done yet
  - `RenderAvailable`: The very basic things for rendering are available
    (sdl context, entry, instance, device, cmdbuf, surface, etc)
  - `LoadingWorld`: We are loading into a world, the only thing that
    can be done here currently is add things to be rendered (Components)
	of which only the `Cube` is available, with the skybox being
	automaticall added.
  - `InWorld`: We have loaded a world and can (theoretically) render things
    and move about
- The render logic now lives in `render/` and is split both by phase, as well as
  components and the swapchain having their own modules
- The input logic now lives in input and constits of one trait that should be
  implemented for states that should handle input, and is currently capable of
  handling mouse movenent, key presses and keys that should be held down (ex,
  movement keys)
- The game logic lives in `game/` and is/will be responsible for driving both
  the input and render modules
Note: the current commit is broken, as our surface is lost when returning from
`Game<Unstarted>::init_render`
			
			
				master
			
			
		
							parent
							
								
									8168eebf7b
								
							
						
					
					
						commit
						2f15842ca4
					
				| @ -0,0 +1,96 @@ | |||||||
|  | use std::{ | ||||||
|  |     process, thread, | ||||||
|  |     time::{Duration, Instant}, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | use sdl2::{ | ||||||
|  |     event::{Event, WindowEvent}, | ||||||
|  |     keyboard::Keycode, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | use crate::{Game, InWorld, RenderAvailable, input::InputHandler}; | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, Default)] | ||||||
|  | pub struct Vector3 { | ||||||
|  |     pub x: f32, | ||||||
|  |     pub y: f32, | ||||||
|  |     pub z: f32, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, Default)] | ||||||
|  | pub struct Camera { | ||||||
|  |     pub origin: Vector3, | ||||||
|  |     pub rotation: Vector3, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Game<InWorld> { | ||||||
|  |     pub fn main_loop(mut self) -> Game<RenderAvailable> { | ||||||
|  |         let mut event_pump = self | ||||||
|  |             .state | ||||||
|  |             .sdl_context | ||||||
|  |             .event_pump() | ||||||
|  |             .expect("Failed to get event pump"); | ||||||
|  |         let mouse = self.state.sdl_context.mouse(); | ||||||
|  | 
 | ||||||
|  |         mouse.set_relative_mouse_mode(true); | ||||||
|  | 
 | ||||||
|  |         let mut running = true; | ||||||
|  |         while running { | ||||||
|  |             let now = Instant::now(); | ||||||
|  |             for event in event_pump.poll_iter() { | ||||||
|  |                 match event { | ||||||
|  |                     // Setting running to false just returns to
 | ||||||
|  |                     // RenderAvailable state (and then main menu)
 | ||||||
|  |                     Event::Quit { .. } => process::exit(0), | ||||||
|  |                     Event::Window { | ||||||
|  |                         win_event: WindowEvent::Resized(w, h), | ||||||
|  |                         .. | ||||||
|  |                     } => { | ||||||
|  |                         self.window_resize((w as u32, h as u32)); | ||||||
|  |                     } | ||||||
|  |                     Event::KeyDown { | ||||||
|  |                         keycode: Some(keycode), | ||||||
|  |                         keymod, | ||||||
|  |                         repeat, | ||||||
|  |                         .. | ||||||
|  |                     } => self.handle_keydown(keycode, keymod, repeat), | ||||||
|  | 
 | ||||||
|  |                     Event::KeyUp { | ||||||
|  |                         keycode: Some(keycode), | ||||||
|  |                         keymod, | ||||||
|  |                         repeat, | ||||||
|  |                         .. | ||||||
|  |                     } => self.handle_keyup(keycode, keymod, repeat), | ||||||
|  |                     Event::MouseMotion { | ||||||
|  |                         mousestate, | ||||||
|  |                         x, | ||||||
|  |                         y, | ||||||
|  |                         xrel, | ||||||
|  |                         yrel, | ||||||
|  |                         .. | ||||||
|  |                     } => self.handle_mouse_motion(mousestate, (x, y), (xrel, yrel)), | ||||||
|  |                     _ => {} | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             event_pump | ||||||
|  |                 .keyboard_state() | ||||||
|  |                 .pressed_scancodes() | ||||||
|  |                 .filter_map(Keycode::from_scancode) | ||||||
|  |                 .for_each(|k| self.handle_cont_key(k)); | ||||||
|  | 
 | ||||||
|  |             self.render_frame(); | ||||||
|  | 
 | ||||||
|  |             let elapsed = now.elapsed(); | ||||||
|  |             let delay = (1_000_000u128 / 60).saturating_sub(elapsed.as_micros()); | ||||||
|  |             thread::sleep(Duration::from_micros(delay as u64)); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         Game { | ||||||
|  |             state: RenderAvailable { | ||||||
|  |                 sdl_context: self.state.sdl_context, | ||||||
|  |                 window: self.state.window, | ||||||
|  |                 ctx: self.state.ctx, | ||||||
|  |             }, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,67 @@ | |||||||
|  | use sdl2::{ | ||||||
|  |     keyboard::{Keycode, Mod}, | ||||||
|  |     mouse::MouseState, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | use crate::{Game, InWorld}; | ||||||
|  | 
 | ||||||
|  | use super::InputHandler; | ||||||
|  | 
 | ||||||
|  | const MOV_STEP: f32 = 0.05; | ||||||
|  | const ROT_MOUSE_SCALE: f32 = 0.001; | ||||||
|  | 
 | ||||||
|  | impl InputHandler for Game<InWorld> { | ||||||
|  |     fn handle_cont_key(&mut self, keycode: Keycode) { | ||||||
|  |         let Self { | ||||||
|  |             state: InWorld { camera, .. }, | ||||||
|  |         } = self; | ||||||
|  | 
 | ||||||
|  |         match keycode { | ||||||
|  |             Keycode::W => { | ||||||
|  |                 camera.origin.x -= -camera.rotation.y.sin() * MOV_STEP; | ||||||
|  |                 camera.origin.z -= camera.rotation.y.cos() * MOV_STEP; | ||||||
|  |             } | ||||||
|  |             Keycode::S => { | ||||||
|  |                 camera.origin.x += -camera.rotation.y.sin() * MOV_STEP; | ||||||
|  |                 camera.origin.z += camera.rotation.y.cos() * MOV_STEP; | ||||||
|  |             } | ||||||
|  |             Keycode::A => { | ||||||
|  |                 camera.origin.x += camera.rotation.y.cos() * MOV_STEP; | ||||||
|  |                 camera.origin.z += camera.rotation.y.sin() * MOV_STEP; | ||||||
|  |             } | ||||||
|  |             Keycode::D => { | ||||||
|  |                 camera.origin.z -= camera.rotation.y.sin() * MOV_STEP; | ||||||
|  |                 camera.origin.x -= camera.rotation.y.cos() * MOV_STEP; | ||||||
|  |             } | ||||||
|  |             Keycode::Space => { | ||||||
|  |                 camera.origin.y += MOV_STEP; | ||||||
|  |             } | ||||||
|  |             Keycode::LShift => { | ||||||
|  |                 camera.origin.y -= MOV_STEP; | ||||||
|  |             } | ||||||
|  |             _ => {} | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn handle_keydown(&mut self, keycode: Keycode, modifier: Mod, repeat: bool) { | ||||||
|  |         match keycode { | ||||||
|  |             Keycode::Escape => { | ||||||
|  |                 let mouse = self.state.sdl_context.mouse(); | ||||||
|  |                 mouse.set_relative_mouse_mode(!mouse.relative_mouse_mode()); | ||||||
|  |             } | ||||||
|  |             _ => {} | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn handle_mouse_motion( | ||||||
|  |         &mut self, | ||||||
|  |         state: MouseState, | ||||||
|  |         abs: (i32, i32), | ||||||
|  |         (xrel, yrel): (i32, i32), | ||||||
|  |     ) { | ||||||
|  |         if self.state.sdl_context.mouse().relative_mouse_mode() { | ||||||
|  |             self.state.camera.rotation.y -= xrel as f32 * ROT_MOUSE_SCALE; | ||||||
|  |             self.state.camera.rotation.x -= yrel as f32 * ROT_MOUSE_SCALE; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,19 @@ | |||||||
|  | mod ingame_input; | ||||||
|  | 
 | ||||||
|  | use sdl2::{ | ||||||
|  |     keyboard::{Keycode, Mod}, | ||||||
|  |     mouse::MouseState, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // More methods to be added for other forms of input
 | ||||||
|  | pub trait InputHandler { | ||||||
|  |     // Handle keys which are meant to be held down and repeated every frame, ex movement keys
 | ||||||
|  |     fn handle_cont_key(&mut self, keycode: Keycode) {} | ||||||
|  | 
 | ||||||
|  |     // Handle single keypresses or keys held down after the repear delay
 | ||||||
|  |     fn handle_keydown(&mut self, keycode: Keycode, modifier: Mod, repeat: bool) {} | ||||||
|  |     fn handle_keyup(&mut self, keycode: Keycode, modifier: Mod, repeat: bool) {} | ||||||
|  | 
 | ||||||
|  |     // Handle mouse movements
 | ||||||
|  |     fn handle_mouse_motion(&mut self, state: MouseState, abs: (i32, i32), rel: (i32, i32)) {} | ||||||
|  | } | ||||||
											
												
													File diff suppressed because it is too large
													Load Diff
												
											
										
									
								| @ -0,0 +1,413 @@ | |||||||
|  | use std::collections::HashMap; | ||||||
|  | 
 | ||||||
|  | use ash::vk; | ||||||
|  | 
 | ||||||
|  | use crate::{Game, LoadingWorld, RenderCtx, render::MemType}; | ||||||
|  | 
 | ||||||
|  | use super::loading_world::WorldComponent; | ||||||
|  | 
 | ||||||
|  | pub struct Cube { | ||||||
|  |     mesh_buf: vk::Buffer, | ||||||
|  |     pipeline: vk::Pipeline, | ||||||
|  |     pipe_layout: vk::PipelineLayout, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const MESH_SIZE: u64 = 36 * 2 * 4 * 4; | ||||||
|  | 
 | ||||||
|  | #[rustfmt::skip] | ||||||
|  | const POSITIONS: [f32; 36 * 2 * 4] = [ | ||||||
|  |     // BOTTOM
 | ||||||
|  |     -0.5, 0.5, 1.0, 1.0, | ||||||
|  |     0.0, 1.0, 0.0, 0.0, | ||||||
|  | 
 | ||||||
|  |     0.5, 0.5, 2.0, 1.0, | ||||||
|  |     0.0, 1.0, 0.0, 0.0, | ||||||
|  | 
 | ||||||
|  |     -0.5, 0.5, 2.0, 1.0, | ||||||
|  |     0.0, 1.0, 0.0, 0.0, | ||||||
|  | 
 | ||||||
|  |     0.5, 0.5, 1.0, 1.0, | ||||||
|  |     0.0, 1.0, 0.0, 0.0, | ||||||
|  | 
 | ||||||
|  |     0.5, 0.5, 2.0, 1.0, | ||||||
|  |     0.0, 1.0, 0.0, 0.0, | ||||||
|  | 
 | ||||||
|  |     -0.5, 0.5, 1.0, 1.0, | ||||||
|  |     0.0, 1.0, 0.0, 0.0, | ||||||
|  | 
 | ||||||
|  |     // TOP
 | ||||||
|  |     0.5, -0.5, 2.0, 1.0, | ||||||
|  |     0.0, -1.0, 0.0, 0.0, | ||||||
|  | 
 | ||||||
|  |     -0.5, -0.5, 1.0, 1.0, | ||||||
|  |     0.0, -1.0, 0.0, 0.0, | ||||||
|  | 
 | ||||||
|  |     -0.5, -0.5, 2.0, 1.0, | ||||||
|  |     0.0, -1.0, 0.0, 0.0, | ||||||
|  | 
 | ||||||
|  |     0.5, -0.5, 2.0, 1.0, | ||||||
|  |     0.0, -1.0, 0.0, 0.0, | ||||||
|  | 
 | ||||||
|  |     0.5, -0.5, 1.0, 1.0, | ||||||
|  |     0.0, -1.0, 0.0, 0.0, | ||||||
|  | 
 | ||||||
|  |     -0.5, -0.5, 1.0, 1.0, | ||||||
|  |     0.0, -1.0, 0.0, 0.0, | ||||||
|  | 
 | ||||||
|  |     // FRONT
 | ||||||
|  |     -0.5, -0.5, 1.0, 1.0, | ||||||
|  |     0.0, 0.0, -1.0, 0.0, | ||||||
|  | 
 | ||||||
|  |     0.5, 0.5, 1.0, 1.0, | ||||||
|  |     0.0, 0.0, -1.0, 0.0, | ||||||
|  | 
 | ||||||
|  |     -0.5, 0.5, 1.0, 1.0, | ||||||
|  |     0.0, 0.0, -1.0, 0.0, | ||||||
|  | 
 | ||||||
|  |     0.5, -0.5, 1.0, 1.0, | ||||||
|  |     0.0, 0.0, -1.0, 0.0, | ||||||
|  | 
 | ||||||
|  |     0.5, 0.5, 1.0, 1.0, | ||||||
|  |     0.0, 0.0, -1.0, 0.0, | ||||||
|  | 
 | ||||||
|  |     -0.5, -0.5, 1.0, 1.0, | ||||||
|  |     0.0, 0.0, -1.0, 0.0, | ||||||
|  | 
 | ||||||
|  |     // BACK
 | ||||||
|  |     0.5, 0.5, 2.0, 1.0, | ||||||
|  |     0.0, 0.0, 1.0, 0.0, | ||||||
|  | 
 | ||||||
|  |     -0.5, -0.5, 2.0, 1.0, | ||||||
|  |     0.0, 0.0, 1.0, 0.0, | ||||||
|  | 
 | ||||||
|  |     -0.5, 0.5, 2.0, 1.0, | ||||||
|  |     0.0, 0.0, 1.0, 0.0, | ||||||
|  | 
 | ||||||
|  |     0.5, 0.5, 2.0, 1.0, | ||||||
|  |     0.0, 0.0, 1.0, 0.0, | ||||||
|  | 
 | ||||||
|  |     0.5, -0.5, 2.0, 1.0, | ||||||
|  |     0.0, 0.0, 1.0, 0.0, | ||||||
|  | 
 | ||||||
|  |     -0.5, -0.5, 2.0, 1.0, | ||||||
|  |     0.0, 0.0, 1.0, 0.0, | ||||||
|  | 
 | ||||||
|  |     // LEFT
 | ||||||
|  |     -0.5, -0.5, 1.0, 1.0, | ||||||
|  |     -1.0, 0.0, 0.0, 0.0, | ||||||
|  | 
 | ||||||
|  |     -0.5, 0.5, 2.0, 1.0, | ||||||
|  |     -1.0, 0.0, 0.0, 0.0, | ||||||
|  | 
 | ||||||
|  |     -0.5, -0.5, 2.0, 1.0, | ||||||
|  |     -1.0, 0.0, 0.0, 0.0, | ||||||
|  | 
 | ||||||
|  |     -0.5, 0.5, 1.0, 1.0, | ||||||
|  |     -1.0, 0.0, 0.0, 0.0, | ||||||
|  | 
 | ||||||
|  |     -0.5, 0.5, 2.0, 1.0, | ||||||
|  |     -1.0, 0.0, 0.0, 0.0, | ||||||
|  | 
 | ||||||
|  |     -0.5, -0.5, 1.0, 1.0, | ||||||
|  |     -1.0, 0.0, 0.0, 0.0, | ||||||
|  | 
 | ||||||
|  |     // RIGHT
 | ||||||
|  |     0.5, 0.5, 2.0, 1.0, | ||||||
|  |     1.0, 0.0, 0.0, 0.0, | ||||||
|  | 
 | ||||||
|  |     0.5, -0.5, 1.0, 1.0, | ||||||
|  |     1.0, 0.0, 0.0, 0.0, | ||||||
|  | 
 | ||||||
|  |     0.5, -0.5, 2.0, 1.0, | ||||||
|  |     1.0, 0.0, 0.0, 0.0, | ||||||
|  | 
 | ||||||
|  |     0.5, 0.5, 2.0, 1.0, | ||||||
|  |     1.0, 0.0, 0.0, 0.0, | ||||||
|  | 
 | ||||||
|  |     0.5, 0.5, 1.0, 1.0, | ||||||
|  |     1.0, 0.0, 0.0, 0.0, | ||||||
|  | 
 | ||||||
|  |     0.5, -0.5, 1.0, 1.0, | ||||||
|  |     1.0, 0.0, 0.0, 0.0, | ||||||
|  | ]; | ||||||
|  | 
 | ||||||
|  | impl WorldComponent for Cube { | ||||||
|  |     fn descriptors(&self) -> std::collections::HashMap<vk::DescriptorType, u32> { | ||||||
|  |         let mut map = HashMap::new(); | ||||||
|  |         map.insert(vk::DescriptorType::COMBINED_IMAGE_SAMPLER, 1); | ||||||
|  |         map.insert(vk::DescriptorType::STORAGE_BUFFER, 1); | ||||||
|  |         map | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn desc_layout(&self, ctx: &RenderCtx) -> vk::DescriptorSetLayout { | ||||||
|  |         let storage_binding = vk::DescriptorSetLayoutBinding::default() | ||||||
|  |             .binding(0) | ||||||
|  |             .descriptor_type(vk::DescriptorType::STORAGE_BUFFER) | ||||||
|  |             .descriptor_count(1) | ||||||
|  |             .stage_flags(vk::ShaderStageFlags::VERTEX); | ||||||
|  |         let image_binding = vk::DescriptorSetLayoutBinding::default() | ||||||
|  |             .binding(1) | ||||||
|  |             .descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER) | ||||||
|  |             .descriptor_count(1) | ||||||
|  |             .stage_flags(vk::ShaderStageFlags::FRAGMENT); | ||||||
|  |         let layouts = [storage_binding, image_binding]; | ||||||
|  | 
 | ||||||
|  |         let layout_info = vk::DescriptorSetLayoutCreateInfo::default().bindings(&layouts); | ||||||
|  |         let layout = unsafe { | ||||||
|  |             ctx.dev | ||||||
|  |                 .create_descriptor_set_layout(&layout_info, None) | ||||||
|  |                 .expect("Failed to create descriptor set layout") | ||||||
|  |         }; | ||||||
|  |         layout | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn write_desc_set( | ||||||
|  |         &self, | ||||||
|  |         ctx: &RenderCtx, | ||||||
|  |         desc_set: vk::DescriptorSet, | ||||||
|  |         skybox: (vk::ImageView, vk::Sampler), | ||||||
|  |     ) { | ||||||
|  |         let buf_info = vk::DescriptorBufferInfo::default() | ||||||
|  |             .buffer(self.mesh_buf) | ||||||
|  |             .offset(0) | ||||||
|  |             .range(vk::WHOLE_SIZE); | ||||||
|  |         let buf_infos = [buf_info]; | ||||||
|  |         let buf_desc = vk::WriteDescriptorSet::default() | ||||||
|  |             .dst_set(desc_set) | ||||||
|  |             .dst_binding(0) | ||||||
|  |             .dst_array_element(0) | ||||||
|  |             .descriptor_type(vk::DescriptorType::STORAGE_BUFFER) | ||||||
|  |             .buffer_info(&buf_infos); | ||||||
|  | 
 | ||||||
|  |         let img_info = vk::DescriptorImageInfo::default() | ||||||
|  |             .image_layout(vk::ImageLayout::GENERAL) | ||||||
|  |             .image_view(skybox.0) | ||||||
|  |             .sampler(skybox.1); | ||||||
|  |         let img_infos = &[img_info]; | ||||||
|  |         let img_desc = vk::WriteDescriptorSet::default() | ||||||
|  |             .dst_set(desc_set) | ||||||
|  |             .dst_binding(1) | ||||||
|  |             .dst_array_element(0) | ||||||
|  |             .descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER) | ||||||
|  |             .image_info(img_infos); | ||||||
|  | 
 | ||||||
|  |         unsafe { | ||||||
|  |             ctx.dev.update_descriptor_sets(&[buf_desc, img_desc], &[]); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn pipeline(&self) -> (vk::Pipeline, vk::PipelineLayout) { | ||||||
|  |         (self.pipeline, self.pipe_layout) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn push_constants( | ||||||
|  |         &self, | ||||||
|  |         camera: &crate::game::Camera, | ||||||
|  |         res: [u32; 2], | ||||||
|  |         base_color: [f32; 4], | ||||||
|  |     ) -> Vec<(vk::ShaderStageFlags, u32, Vec<u8>)> { | ||||||
|  |         let mut constants = Vec::new(); | ||||||
|  |         let cam_data: Vec<u8> = (Vec::<f32>::from([ | ||||||
|  |             camera.origin.x, | ||||||
|  |             camera.origin.y, | ||||||
|  |             camera.origin.z, | ||||||
|  |             0.0, | ||||||
|  |             camera.rotation.x, | ||||||
|  |             camera.rotation.y, | ||||||
|  |             camera.rotation.z, | ||||||
|  |         ])) | ||||||
|  |         .iter() | ||||||
|  |         .map(|f| f.to_ne_bytes()) | ||||||
|  |         .collect::<Vec<_>>() | ||||||
|  |         .into_flattened(); | ||||||
|  |         constants.push((vk::ShaderStageFlags::VERTEX, 0, cam_data)); | ||||||
|  | 
 | ||||||
|  |         let screen_res: Vec<u8> = res | ||||||
|  |             .iter() | ||||||
|  |             .map(|f| f.to_ne_bytes()) | ||||||
|  |             .collect::<Vec<_>>() | ||||||
|  |             .into_flattened(); | ||||||
|  |         constants.push((vk::ShaderStageFlags::VERTEX, 32, screen_res)); | ||||||
|  | 
 | ||||||
|  |         let base_color: Vec<u8> = (Vec::<f32>::from([0.0, 1.0, 1.0, 1.0])) | ||||||
|  |             .iter() | ||||||
|  |             .map(|f| f.to_ne_bytes()) | ||||||
|  |             .collect::<Vec<_>>() | ||||||
|  |             .into_flattened(); | ||||||
|  |         constants.push((vk::ShaderStageFlags::FRAGMENT, 48, base_color)); | ||||||
|  | 
 | ||||||
|  |         constants | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl RenderCtx { | ||||||
|  |     fn create_cube(&self, pass: vk::RenderPass) -> Cube { | ||||||
|  |         let mesh_mem = self.mem_alloc(MemType::HostVisibile, MESH_SIZE); | ||||||
|  | 
 | ||||||
|  |         let mesh_buf = self.alloc_buf( | ||||||
|  |             mesh_mem, | ||||||
|  |             MESH_SIZE, | ||||||
|  |             vk::BufferUsageFlags::TRANSFER_SRC | vk::BufferUsageFlags::STORAGE_BUFFER, | ||||||
|  |         ); | ||||||
|  | 
 | ||||||
|  |         let mesh_mem = self.buf_to_ptr(mesh_mem, mesh_buf); | ||||||
|  |         unsafe { | ||||||
|  |             std::ptr::copy(POSITIONS.as_ptr(), mesh_mem as *mut f32, POSITIONS.len()); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         let pipe_layout = setup_pipe_layout(self); | ||||||
|  |         let pipeline = setup_pipeline(self, &pass, pipe_layout); | ||||||
|  | 
 | ||||||
|  |         Cube { | ||||||
|  |             mesh_buf, | ||||||
|  |             pipeline, | ||||||
|  |             pipe_layout, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Game<LoadingWorld> { | ||||||
|  |     pub fn create_cube(&self) -> Cube { | ||||||
|  |         let Self { | ||||||
|  |             state: LoadingWorld { ctx, pass, .. }, | ||||||
|  |         } = self; | ||||||
|  |         ctx.create_cube(*pass) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn load_cube_shaders(ctx: &RenderCtx) -> (vk::ShaderModule, vk::ShaderModule) { | ||||||
|  |     let shader_vert_shader = ctx.shader_mod_from_file( | ||||||
|  |         "shaders/shader_vert.spv", | ||||||
|  |         vk::ShaderModuleCreateFlags::empty(), | ||||||
|  |     ); | ||||||
|  |     let shader_frag_shader = ctx.shader_mod_from_file( | ||||||
|  |         "shaders/shader_rag.spv", | ||||||
|  |         vk::ShaderModuleCreateFlags::empty(), | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     (shader_vert_shader, shader_frag_shader) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn setup_desc_layout(ctx: &RenderCtx) -> vk::DescriptorSetLayout { | ||||||
|  |     let storage_binding = vk::DescriptorSetLayoutBinding::default() | ||||||
|  |         .binding(0) | ||||||
|  |         .descriptor_type(vk::DescriptorType::STORAGE_BUFFER) | ||||||
|  |         .descriptor_count(1) | ||||||
|  |         .stage_flags(vk::ShaderStageFlags::VERTEX); | ||||||
|  |     let image_binding = vk::DescriptorSetLayoutBinding::default() | ||||||
|  |         .binding(1) | ||||||
|  |         .descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER) | ||||||
|  |         .descriptor_count(1) | ||||||
|  |         .stage_flags(vk::ShaderStageFlags::FRAGMENT); | ||||||
|  |     let layouts = [storage_binding, image_binding]; | ||||||
|  | 
 | ||||||
|  |     let layout_info = vk::DescriptorSetLayoutCreateInfo::default().bindings(&layouts); | ||||||
|  |     let layout = unsafe { | ||||||
|  |         ctx.dev | ||||||
|  |             .create_descriptor_set_layout(&layout_info, None) | ||||||
|  |             .expect("Failed to create descriptor set layout") | ||||||
|  |     }; | ||||||
|  |     layout | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn setup_pipe_layout(ctx: &RenderCtx) -> vk::PipelineLayout { | ||||||
|  |     let layout = setup_desc_layout(ctx); | ||||||
|  |     let push_range: [vk::PushConstantRange; 2] = [ | ||||||
|  |         vk::PushConstantRange::default() | ||||||
|  |             .stage_flags(vk::ShaderStageFlags::VERTEX) | ||||||
|  |             .offset(0) | ||||||
|  |             .size(48), // vec4 camera_orig, camera_rot; uvec2 screen_res
 | ||||||
|  |         vk::PushConstantRange::default() | ||||||
|  |             .stage_flags(vk::ShaderStageFlags::FRAGMENT) | ||||||
|  |             .offset(48) | ||||||
|  |             .size(16), // vec4 base_color
 | ||||||
|  |     ]; | ||||||
|  | 
 | ||||||
|  |     let layouts = &[layout]; | ||||||
|  | 
 | ||||||
|  |     let pipe_layout = vk::PipelineLayoutCreateInfo::default() | ||||||
|  |         .set_layouts(layouts.as_ref()) | ||||||
|  |         .push_constant_ranges(&push_range); | ||||||
|  |     let pipe_layout = unsafe { | ||||||
|  |         ctx.dev | ||||||
|  |             .create_pipeline_layout(&pipe_layout, None) | ||||||
|  |             .expect("Failed to create pipeline layout") | ||||||
|  |     }; | ||||||
|  |     pipe_layout | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn setup_pipeline( | ||||||
|  |     ctx: &RenderCtx, | ||||||
|  |     pass: &vk::RenderPass, | ||||||
|  |     layout: vk::PipelineLayout, | ||||||
|  | ) -> vk::Pipeline { | ||||||
|  |     let (vert_shader, frag_shader) = load_cube_shaders(ctx); | ||||||
|  |     let shader_stages = ctx.setup_simple_shader_stage(vert_shader, frag_shader); | ||||||
|  |     let vert_input = vk::PipelineVertexInputStateCreateInfo::default(); | ||||||
|  |     let input_assembly = vk::PipelineInputAssemblyStateCreateInfo::default() | ||||||
|  |         .topology(vk::PrimitiveTopology::TRIANGLE_LIST); | ||||||
|  |     let rasterization = vk::PipelineRasterizationStateCreateInfo::default() | ||||||
|  |         .polygon_mode(vk::PolygonMode::FILL) | ||||||
|  |         .cull_mode(vk::CullModeFlags::BACK) | ||||||
|  |         .front_face(vk::FrontFace::COUNTER_CLOCKWISE) | ||||||
|  |         .line_width(1.0); | ||||||
|  |     let multisample = vk::PipelineMultisampleStateCreateInfo::default() | ||||||
|  |         .rasterization_samples(vk::SampleCountFlags::TYPE_1); | ||||||
|  |     let depth_stencil = vk::PipelineDepthStencilStateCreateInfo::default() | ||||||
|  |         .depth_test_enable(true) | ||||||
|  |         .depth_write_enable(true) | ||||||
|  |         .depth_compare_op(vk::CompareOp::LESS) | ||||||
|  |         .depth_bounds_test_enable(true) | ||||||
|  |         .stencil_test_enable(false) | ||||||
|  |         .min_depth_bounds(0.0) | ||||||
|  |         .max_depth_bounds(1.0); | ||||||
|  | 
 | ||||||
|  |     let blend = vk::PipelineColorBlendAttachmentState::default() | ||||||
|  |         .src_color_blend_factor(vk::BlendFactor::SRC_COLOR) | ||||||
|  |         .dst_color_blend_factor(vk::BlendFactor::SRC_COLOR) | ||||||
|  |         .color_blend_op(vk::BlendOp::ADD) | ||||||
|  |         .src_alpha_blend_factor(vk::BlendFactor::SRC_COLOR) | ||||||
|  |         .dst_alpha_blend_factor(vk::BlendFactor::SRC_COLOR) | ||||||
|  |         .alpha_blend_op(vk::BlendOp::ADD) | ||||||
|  |         .color_write_mask( | ||||||
|  |             vk::ColorComponentFlags::R | ||||||
|  |                 | vk::ColorComponentFlags::G | ||||||
|  |                 | vk::ColorComponentFlags::B | ||||||
|  |                 | vk::ColorComponentFlags::A, | ||||||
|  |         ); | ||||||
|  | 
 | ||||||
|  |     let blend_attachments = [blend]; | ||||||
|  | 
 | ||||||
|  |     let color_blend = vk::PipelineColorBlendStateCreateInfo::default() | ||||||
|  |         .attachments(blend_attachments.as_ref()) | ||||||
|  |         .logic_op_enable(false) | ||||||
|  |         .logic_op(vk::LogicOp::COPY) | ||||||
|  |         .blend_constants([1.0, 1.0, 1.0, 1.0]); | ||||||
|  | 
 | ||||||
|  |     let dynamic = vk::PipelineDynamicStateCreateInfo::default().dynamic_states(&[ | ||||||
|  |         vk::DynamicState::VIEWPORT_WITH_COUNT, | ||||||
|  |         vk::DynamicState::SCISSOR_WITH_COUNT, | ||||||
|  |     ]); | ||||||
|  | 
 | ||||||
|  |     let pipe_info = vk::GraphicsPipelineCreateInfo::default() | ||||||
|  |         .stages(&shader_stages) | ||||||
|  |         .vertex_input_state(&vert_input) | ||||||
|  |         .input_assembly_state(&input_assembly) | ||||||
|  |         .rasterization_state(&rasterization) | ||||||
|  |         .multisample_state(&multisample) | ||||||
|  |         .depth_stencil_state(&depth_stencil) | ||||||
|  |         .color_blend_state(&color_blend) | ||||||
|  |         .layout(layout) | ||||||
|  |         .dynamic_state(&dynamic) | ||||||
|  |         .render_pass(*pass) | ||||||
|  |         .subpass(0); | ||||||
|  | 
 | ||||||
|  |     let pipe = unsafe { | ||||||
|  |         ctx.dev | ||||||
|  |             .create_graphics_pipelines(vk::PipelineCache::null(), &[pipe_info], None) | ||||||
|  |             .expect("Failed to create graphics pipeline") | ||||||
|  |             .remove(0) | ||||||
|  |     }; | ||||||
|  |     println!("Created pipeline"); | ||||||
|  |     pipe | ||||||
|  | } | ||||||
| @ -0,0 +1,284 @@ | |||||||
|  | use ash::vk::{self}; | ||||||
|  | 
 | ||||||
|  | use crate::{Game, InWorld, RenderCtx, Window}; | ||||||
|  | 
 | ||||||
|  | use super::{ | ||||||
|  |     SwapchainCtx, | ||||||
|  |     loading_world::{Component, WorldComponent as _}, | ||||||
|  |     swapchain::{ | ||||||
|  |         make_depth_img, make_depth_view, make_framebufs, make_swap_images, make_swap_views, | ||||||
|  |         setup_swapchain, | ||||||
|  |     }, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | impl Game<InWorld> { | ||||||
|  |     pub fn window_resize(&mut self, size: (u32, u32)) { | ||||||
|  |         let Self { | ||||||
|  |             state: | ||||||
|  |                 InWorld { | ||||||
|  |                     sdl_context, | ||||||
|  |                     window, | ||||||
|  |                     ctx, | ||||||
|  |                     skybox, | ||||||
|  |                     components, | ||||||
|  |                     sem_avail, | ||||||
|  |                     sem_finish, | ||||||
|  |                     fence_flight, | ||||||
|  |                     swapchain: | ||||||
|  |                         SwapchainCtx { | ||||||
|  |                             surface_loader, | ||||||
|  |                             swapchain_loader, | ||||||
|  |                             swap_images, | ||||||
|  |                             swap_views, | ||||||
|  |                             depth_mem, | ||||||
|  |                             depth_image, | ||||||
|  |                             depth_view, | ||||||
|  |                             framebufs, | ||||||
|  |                             swapchain, | ||||||
|  |                         }, | ||||||
|  |                     pass, | ||||||
|  |                     camera, | ||||||
|  |                 }, | ||||||
|  |         } = self; | ||||||
|  | 
 | ||||||
|  |         window.width = size.0; | ||||||
|  |         window.height = size.1; | ||||||
|  | 
 | ||||||
|  |         unsafe { | ||||||
|  |             ctx.dev | ||||||
|  |                 .device_wait_idle() | ||||||
|  |                 .expect("Failed to wait for device idle"); | ||||||
|  |             swapchain_loader.destroy_swapchain(*swapchain, None); | ||||||
|  |         } | ||||||
|  |         (*swapchain, _) = setup_swapchain( | ||||||
|  |             &ctx, | ||||||
|  |             &surface_loader, | ||||||
|  |             &swapchain_loader, | ||||||
|  |             window.width, | ||||||
|  |             window.height, | ||||||
|  |         ); | ||||||
|  |         *swap_images = make_swap_images(*swapchain, swapchain_loader); | ||||||
|  |         *swap_views = make_swap_views(&ctx, &swap_images, vk::Format::B8G8R8A8_UNORM); | ||||||
|  |         *depth_image = make_depth_img(&ctx, window.width, window.height, *depth_mem); | ||||||
|  |         *depth_view = make_depth_view(&ctx, *depth_image, vk::Format::D32_SFLOAT); | ||||||
|  |         *framebufs = make_framebufs( | ||||||
|  |             &ctx, | ||||||
|  |             &swap_views, | ||||||
|  |             &depth_view, | ||||||
|  |             &pass, | ||||||
|  |             window.width, | ||||||
|  |             window.height, | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn render_frame(&self) { | ||||||
|  |         let Self { | ||||||
|  |             state: | ||||||
|  |                 InWorld { | ||||||
|  |                     sdl_context, | ||||||
|  |                     window, | ||||||
|  |                     ctx, | ||||||
|  |                     skybox, | ||||||
|  |                     components, | ||||||
|  |                     sem_avail, | ||||||
|  |                     sem_finish, | ||||||
|  |                     fence_flight, | ||||||
|  |                     swapchain, | ||||||
|  |                     pass, | ||||||
|  |                     camera, | ||||||
|  |                 }, | ||||||
|  |         } = self; | ||||||
|  | 
 | ||||||
|  |         ctx.wait_for_fence(*fence_flight); | ||||||
|  |         ctx.reset_fence(*fence_flight); | ||||||
|  | 
 | ||||||
|  |         let img_idx = ack_next_img(swapchain, sem_avail); | ||||||
|  | 
 | ||||||
|  |         ctx.start_cmd_buf(); | ||||||
|  | 
 | ||||||
|  |         self.record_commands(img_idx as usize); | ||||||
|  | 
 | ||||||
|  |         ctx.end_cmd_buf(); | ||||||
|  | 
 | ||||||
|  |         ctx.submit_queue(*sem_avail, *sem_finish, *fence_flight); | ||||||
|  | 
 | ||||||
|  |         ctx.queue_present(swapchain, img_idx, *sem_finish); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn record_commands(&self, img_idx: usize) { | ||||||
|  |         let Self { | ||||||
|  |             state: | ||||||
|  |                 InWorld { | ||||||
|  |                     sdl_context, | ||||||
|  |                     window: Window { width, height }, | ||||||
|  |                     ctx: RenderCtx { dev, cmd_buf, .. }, | ||||||
|  |                     skybox, | ||||||
|  |                     components, | ||||||
|  |                     swapchain, | ||||||
|  |                     pass, | ||||||
|  |                     camera, | ||||||
|  |                     .. | ||||||
|  |                 }, | ||||||
|  |         } = self; | ||||||
|  | 
 | ||||||
|  |         let clear_vals = [ | ||||||
|  |             vk::ClearValue { | ||||||
|  |                 color: vk::ClearColorValue { | ||||||
|  |                     float32: [0.0, 0.0, 0.0, 1.0], | ||||||
|  |                 }, | ||||||
|  |             }, | ||||||
|  |             vk::ClearValue { | ||||||
|  |                 depth_stencil: vk::ClearDepthStencilValue { | ||||||
|  |                     depth: 1.0, | ||||||
|  |                     stencil: 0, | ||||||
|  |                 }, | ||||||
|  |             }, | ||||||
|  |         ]; | ||||||
|  | 
 | ||||||
|  |         let render_pass_info = vk::RenderPassBeginInfo::default() | ||||||
|  |             .render_pass(*pass) | ||||||
|  |             .framebuffer(swapchain.framebufs[img_idx]) | ||||||
|  |             .render_area( | ||||||
|  |                 vk::Rect2D::default() | ||||||
|  |                     .offset(vk::Offset2D::default()) | ||||||
|  |                     .extent(vk::Extent2D { | ||||||
|  |                         width: *width, | ||||||
|  |                         height: *height, | ||||||
|  |                     }), | ||||||
|  |             ) | ||||||
|  |             .clear_values(&clear_vals); | ||||||
|  | 
 | ||||||
|  |         unsafe { | ||||||
|  |             let regions = (0..6) | ||||||
|  |                 .map(|i| { | ||||||
|  |                     vk::BufferImageCopy::default() | ||||||
|  |                         .buffer_offset(3 * 2048 * 2048 * i) | ||||||
|  |                         .buffer_row_length(0) | ||||||
|  |                         .buffer_image_height(0) | ||||||
|  |                         .image_offset(vk::Offset3D::default()) | ||||||
|  |                         .image_extent(vk::Extent3D::default().width(2048).height(2048).depth(1)) | ||||||
|  |                         .image_subresource( | ||||||
|  |                             vk::ImageSubresourceLayers::default() | ||||||
|  |                                 .aspect_mask(vk::ImageAspectFlags::COLOR) | ||||||
|  |                                 .mip_level(0) | ||||||
|  |                                 .layer_count(1) | ||||||
|  |                                 .base_array_layer(i as u32), | ||||||
|  |                         ) | ||||||
|  |                 }) | ||||||
|  |                 .collect::<Vec<_>>(); | ||||||
|  |             dev.cmd_copy_buffer_to_image( | ||||||
|  |                 *cmd_buf, | ||||||
|  |                 skybox.0.buf, | ||||||
|  |                 skybox.0.image, | ||||||
|  |                 vk::ImageLayout::GENERAL, | ||||||
|  |                 ®ions, | ||||||
|  |             ); | ||||||
|  |             dev.cmd_begin_render_pass(*cmd_buf, &render_pass_info, vk::SubpassContents::INLINE); | ||||||
|  | 
 | ||||||
|  |             let base_color = [0.0, 1.0, 1.0, 1.0]; | ||||||
|  | 
 | ||||||
|  |             for Component { inner, desc_set } in components { | ||||||
|  |                 let (pipe, layout) = inner.pipeline(); | ||||||
|  |                 dev.cmd_bind_pipeline(*cmd_buf, vk::PipelineBindPoint::GRAPHICS, pipe); | ||||||
|  | 
 | ||||||
|  |                 let desc_sets = [*desc_set]; | ||||||
|  | 
 | ||||||
|  |                 dev.cmd_bind_descriptor_sets( | ||||||
|  |                     *cmd_buf, | ||||||
|  |                     vk::PipelineBindPoint::GRAPHICS, | ||||||
|  |                     layout, | ||||||
|  |                     0, | ||||||
|  |                     desc_sets.as_ref(), | ||||||
|  |                     &[], | ||||||
|  |                 ); | ||||||
|  | 
 | ||||||
|  |                 dev.cmd_set_viewport_with_count( | ||||||
|  |                     *cmd_buf, | ||||||
|  |                     &[vk::Viewport::default() | ||||||
|  |                         .width(*width as f32) | ||||||
|  |                         .height(*height as f32)], | ||||||
|  |                 ); | ||||||
|  |                 dev.cmd_set_scissor_with_count( | ||||||
|  |                     *cmd_buf, | ||||||
|  |                     &[vk::Rect2D::default().extent(vk::Extent2D { | ||||||
|  |                         width: *width, | ||||||
|  |                         height: *height, | ||||||
|  |                     })], | ||||||
|  |                 ); | ||||||
|  | 
 | ||||||
|  |                 inner | ||||||
|  |                     .push_constants(camera, [*width, *height], base_color) | ||||||
|  |                     .into_iter() | ||||||
|  |                     .for_each(|(f, o, d)| { | ||||||
|  |                         dev.cmd_push_constants( | ||||||
|  |                             *cmd_buf, | ||||||
|  |                             layout, | ||||||
|  |                             vk::ShaderStageFlags::VERTEX, | ||||||
|  |                             0, | ||||||
|  |                             d.as_ref(), | ||||||
|  |                         ); | ||||||
|  |                     }); | ||||||
|  | 
 | ||||||
|  |                 dev.cmd_draw(*cmd_buf, 36, 1, 0, 0); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             { | ||||||
|  |                 let (pipe, layout) = skybox.0.pipeline(); | ||||||
|  |                 dev.cmd_bind_pipeline(*cmd_buf, vk::PipelineBindPoint::GRAPHICS, pipe); | ||||||
|  | 
 | ||||||
|  |                 let desc_sets = [skybox.1]; | ||||||
|  | 
 | ||||||
|  |                 dev.cmd_bind_descriptor_sets( | ||||||
|  |                     *cmd_buf, | ||||||
|  |                     vk::PipelineBindPoint::GRAPHICS, | ||||||
|  |                     layout, | ||||||
|  |                     0, | ||||||
|  |                     desc_sets.as_ref(), | ||||||
|  |                     &[], | ||||||
|  |                 ); | ||||||
|  | 
 | ||||||
|  |                 dev.cmd_set_viewport_with_count( | ||||||
|  |                     *cmd_buf, | ||||||
|  |                     &[vk::Viewport::default() | ||||||
|  |                         .width(*width as f32) | ||||||
|  |                         .height(*height as f32)], | ||||||
|  |                 ); | ||||||
|  |                 dev.cmd_set_scissor_with_count( | ||||||
|  |                     *cmd_buf, | ||||||
|  |                     &[vk::Rect2D::default().extent(vk::Extent2D { | ||||||
|  |                         width: *width, | ||||||
|  |                         height: *height, | ||||||
|  |                     })], | ||||||
|  |                 ); | ||||||
|  | 
 | ||||||
|  |                 skybox | ||||||
|  |                     .0 | ||||||
|  |                     .push_constants(camera, [*width, *height], base_color) | ||||||
|  |                     .into_iter() | ||||||
|  |                     .for_each(|(f, o, d)| { | ||||||
|  |                         dev.cmd_push_constants( | ||||||
|  |                             *cmd_buf, | ||||||
|  |                             layout, | ||||||
|  |                             vk::ShaderStageFlags::VERTEX, | ||||||
|  |                             0, | ||||||
|  |                             d.as_ref(), | ||||||
|  |                         ); | ||||||
|  |                     }); | ||||||
|  | 
 | ||||||
|  |                 dev.cmd_draw(*cmd_buf, 36, 1, 0, 0); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             dev.cmd_end_render_pass(*cmd_buf); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn ack_next_img(swapchain: &SwapchainCtx, sem_avail: &vk::Semaphore) -> u32 { | ||||||
|  |     unsafe { | ||||||
|  |         let (img_inx, _) = swapchain | ||||||
|  |             .swapchain_loader | ||||||
|  |             .acquire_next_image(swapchain.swapchain, u64::MAX, *sem_avail, vk::Fence::null()) | ||||||
|  |             .expect("Failed to acquire next image"); | ||||||
|  |         img_inx | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,250 @@ | |||||||
|  | use std::ffi::{CStr, CString}; | ||||||
|  | 
 | ||||||
|  | use ash::{ | ||||||
|  |     Device, Entry, Instance, | ||||||
|  |     khr::surface, | ||||||
|  |     vk::{self}, | ||||||
|  | }; | ||||||
|  | use raw_window_handle::{HasDisplayHandle as _, HasWindowHandle as _}; | ||||||
|  | use sdl2::{Sdl, video::Window}; | ||||||
|  | 
 | ||||||
|  | use crate::{APP_NAME, Game, RenderAvailable, RenderCtx, Unstarted}; | ||||||
|  | 
 | ||||||
|  | impl Game<Unstarted> { | ||||||
|  |     pub fn init_render(self, sdl_context: Sdl, window: Window) -> Game<RenderAvailable> { | ||||||
|  |         let (width, height) = window.size(); | ||||||
|  | 
 | ||||||
|  |         let entry = Entry::linked(); | ||||||
|  | 
 | ||||||
|  |         let instance = create_instance(&window, &entry); | ||||||
|  | 
 | ||||||
|  |         let surface = make_surface(&entry, &instance, &window); | ||||||
|  | 
 | ||||||
|  |         let pdev = find_pdev_dgpu(&instance); | ||||||
|  | 
 | ||||||
|  |         let queue_idx = find_rcs_queue_inx(&instance, &pdev).expect("No RCS queue found"); | ||||||
|  | 
 | ||||||
|  |         let dev = make_dev( | ||||||
|  |             &instance, | ||||||
|  |             &pdev, | ||||||
|  |             queue_idx, | ||||||
|  |             vec![c"VK_KHR_swapchain", c"VK_EXT_extended_dynamic_state3"], | ||||||
|  |         ); | ||||||
|  | 
 | ||||||
|  |         let queue = unsafe { dev.get_device_queue(queue_idx, 0) }; | ||||||
|  | 
 | ||||||
|  |         let cmd_pool = make_command_pool(&dev, queue_idx); | ||||||
|  | 
 | ||||||
|  |         let cmd_buf = alloc_cmd_buf(&dev, cmd_pool); | ||||||
|  | 
 | ||||||
|  |         let host_vis_idx = | ||||||
|  |             find_host_visible(&instance, &pdev).expect("No host visible memory found"); | ||||||
|  | 
 | ||||||
|  |         let host_invis_idx = | ||||||
|  |             find_host_invisible(&instance, &pdev).expect("No host invisible memory found"); | ||||||
|  | 
 | ||||||
|  |         let surface_loader = surface::Instance::new(&entry, &instance); | ||||||
|  | 
 | ||||||
|  |         Game { | ||||||
|  |             state: RenderAvailable { | ||||||
|  |                 sdl_context, | ||||||
|  |                 window: crate::Window { width, height }, | ||||||
|  |                 ctx: RenderCtx { | ||||||
|  |                     entry, | ||||||
|  |                     instance, | ||||||
|  |                     pdev, | ||||||
|  |                     dev, | ||||||
|  |                     surface, | ||||||
|  |                     surface_loader, | ||||||
|  |                     queue, | ||||||
|  |                     queue_idx, | ||||||
|  |                     cmd_buf, | ||||||
|  |                     host_vis_idx, | ||||||
|  |                     host_invis_idx, | ||||||
|  |                 }, | ||||||
|  |             }, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn create_instance(window: &Window, entry: &Entry) -> Instance { | ||||||
|  |     let n_app_name = CString::new(APP_NAME).unwrap(); | ||||||
|  |     let n_engine_name = CString::new(format!("{} Engine", APP_NAME)).unwrap(); | ||||||
|  | 
 | ||||||
|  |     let appinfo = vk::ApplicationInfo::default() | ||||||
|  |         .application_name(n_app_name.as_c_str()) | ||||||
|  |         .application_version(0) | ||||||
|  |         .engine_name(n_engine_name.as_c_str()) | ||||||
|  |         .engine_version(0) | ||||||
|  |         .api_version(vk::make_api_version(0, 1, 3, 0)); | ||||||
|  | 
 | ||||||
|  |     let default_exts = window | ||||||
|  |         .vulkan_instance_extensions() | ||||||
|  |         .expect("Failed to get list of required extensions from SDL2"); | ||||||
|  | 
 | ||||||
|  |     let mut exts = vec![]; | ||||||
|  |     exts.extend(default_exts); | ||||||
|  | 
 | ||||||
|  |     let exts: Vec<*const i8> = exts | ||||||
|  |         .iter() | ||||||
|  |         .map(|s| s.as_ptr() as *const i8) | ||||||
|  |         .collect::<Vec<_>>(); | ||||||
|  |     let exts: &[*const i8] = exts.as_slice(); | ||||||
|  | 
 | ||||||
|  |     let insinfo = vk::InstanceCreateInfo::default() | ||||||
|  |         .application_info(&appinfo) | ||||||
|  |         .enabled_extension_names(exts); | ||||||
|  | 
 | ||||||
|  |     let instance = | ||||||
|  |         unsafe { entry.create_instance(&insinfo, None) }.expect("Failed to create instance"); | ||||||
|  |     println!("Created instance"); | ||||||
|  |     instance | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn make_surface(entry: &Entry, instance: &Instance, window: &Window) -> vk::SurfaceKHR { | ||||||
|  |     let surface = unsafe { | ||||||
|  |         ash_window::create_surface( | ||||||
|  |             &entry, | ||||||
|  |             &instance, | ||||||
|  |             window.display_handle().unwrap().as_raw(), | ||||||
|  |             window.window_handle().unwrap().as_raw(), | ||||||
|  |             None, | ||||||
|  |         ) | ||||||
|  |     } | ||||||
|  |     .expect("Failed to create surface"); | ||||||
|  |     println!("Created surface"); | ||||||
|  |     surface | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn find_pdev_dgpu(instance: &Instance) -> vk::PhysicalDevice { | ||||||
|  |     let mut pdevs = | ||||||
|  |         unsafe { instance.enumerate_physical_devices() }.expect("Failed to enumerate devices"); | ||||||
|  |     for (i, d) in pdevs.iter().enumerate() { | ||||||
|  |         let props = unsafe { instance.get_physical_device_properties(*d) }; | ||||||
|  |         if props.device_type == vk::PhysicalDeviceType::DISCRETE_GPU { | ||||||
|  |             // TODO this assumes that the first discrete GPU will have an RCS queue family
 | ||||||
|  |             // This is not guaranteed by the spec (example, a compute-only GPU)
 | ||||||
|  |             // Fix.
 | ||||||
|  |             println!( | ||||||
|  |                 "Found discrete GPU: {}", | ||||||
|  |                 unsafe { std::str::from_utf8(std::mem::transmute(props.device_name.as_slice())) } | ||||||
|  |                     .unwrap() | ||||||
|  |             ); | ||||||
|  |             return pdevs.remove(i); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     println!("No discrete GPU found"); | ||||||
|  |     assert!(pdevs.len() > 0, "No GPU found"); | ||||||
|  |     return pdevs.remove(0); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Using Intel GPU hardware terminology 🥴
 | ||||||
|  | fn find_rcs_queue_inx(instance: &Instance, pdev: &vk::PhysicalDevice) -> Option<u32> { | ||||||
|  |     let qfams = unsafe { instance.get_physical_device_queue_family_properties(*pdev) }; | ||||||
|  |     for (inx, qfam) in qfams.iter().enumerate() { | ||||||
|  |         if qfam | ||||||
|  |             .queue_flags | ||||||
|  |             .contains(vk::QueueFlags::GRAPHICS | vk::QueueFlags::COMPUTE) | ||||||
|  |         { | ||||||
|  |             println!("Found RCS queue at index {}", inx); | ||||||
|  |             return Some(inx as u32); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     println!("No RCS queue found"); | ||||||
|  |     return None; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn make_dev( | ||||||
|  |     instance: &Instance, | ||||||
|  |     pdev: &vk::PhysicalDevice, | ||||||
|  |     rcs_queue_inx: u32, | ||||||
|  |     exts: Vec<&CStr>, | ||||||
|  | ) -> Device { | ||||||
|  |     unsafe { | ||||||
|  |         let dev_queue = vec![ | ||||||
|  |             vk::DeviceQueueCreateInfo::default() | ||||||
|  |                 .queue_family_index(rcs_queue_inx) | ||||||
|  |                 .queue_priorities(&[1.0]), | ||||||
|  |         ]; | ||||||
|  | 
 | ||||||
|  |         let exts = exts.iter().map(|&s| s.as_ptr()).collect::<Vec<_>>(); | ||||||
|  |         let exts = exts.as_slice(); | ||||||
|  | 
 | ||||||
|  |         // Enable all features
 | ||||||
|  |         let features = instance.get_physical_device_features(*pdev); | ||||||
|  | 
 | ||||||
|  |         let dev_info = vk::DeviceCreateInfo::default() | ||||||
|  |             .queue_create_infos(&dev_queue) | ||||||
|  |             .enabled_extension_names(exts) | ||||||
|  |             .enabled_features(&features); | ||||||
|  | 
 | ||||||
|  |         let dev = instance | ||||||
|  |             .create_device(*pdev, &dev_info, None) | ||||||
|  |             .expect("Failed to create device"); | ||||||
|  |         println!("Created device"); | ||||||
|  |         dev | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn make_command_pool(dev: &Device, rcs_queue_inx: u32) -> vk::CommandPool { | ||||||
|  |     unsafe { | ||||||
|  |         let pool_info = vk::CommandPoolCreateInfo::default() | ||||||
|  |             .queue_family_index(rcs_queue_inx) | ||||||
|  |             .flags(vk::CommandPoolCreateFlags::RESET_COMMAND_BUFFER); | ||||||
|  |         let pool = dev | ||||||
|  |             .create_command_pool(&pool_info, None) | ||||||
|  |             .expect("Failed to create command pool"); | ||||||
|  |         println!("Created command pool"); | ||||||
|  |         pool | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn alloc_cmd_buf(dev: &Device, pool: vk::CommandPool) -> vk::CommandBuffer { | ||||||
|  |     unsafe { | ||||||
|  |         let alloc_info = vk::CommandBufferAllocateInfo::default() | ||||||
|  |             .command_pool(pool) | ||||||
|  |             .level(vk::CommandBufferLevel::PRIMARY) | ||||||
|  |             .command_buffer_count(1); | ||||||
|  |         let cmd_buf = dev | ||||||
|  |             .allocate_command_buffers(&alloc_info) | ||||||
|  |             .expect("Failed to allocate command buffer") | ||||||
|  |             .remove(0); | ||||||
|  |         println!("Allocated command buffer"); | ||||||
|  |         cmd_buf | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn find_host_visible(instance: &Instance, pdev: &vk::PhysicalDevice) -> Option<u32> { | ||||||
|  |     let mem_props = unsafe { instance.get_physical_device_memory_properties(*pdev) }; | ||||||
|  | 
 | ||||||
|  |     for (i, mem_type) in mem_props.memory_types.iter().enumerate() { | ||||||
|  |         if mem_type.property_flags.contains( | ||||||
|  |             vk::MemoryPropertyFlags::DEVICE_LOCAL | ||||||
|  |                 | vk::MemoryPropertyFlags::HOST_VISIBLE | ||||||
|  |                 | vk::MemoryPropertyFlags::HOST_COHERENT, | ||||||
|  |         ) { | ||||||
|  |             println!("Found host visible memory at index {}", i); | ||||||
|  |             return Some(i as u32); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     println!("No host visible memory found"); | ||||||
|  |     return None; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn find_host_invisible(instance: &Instance, pdev: &vk::PhysicalDevice) -> Option<u32> { | ||||||
|  |     let mem_props = unsafe { instance.get_physical_device_memory_properties(*pdev) }; | ||||||
|  |     for (i, mem_type) in mem_props.memory_types.iter().enumerate() { | ||||||
|  |         if mem_type | ||||||
|  |             .property_flags | ||||||
|  |             .contains(vk::MemoryPropertyFlags::DEVICE_LOCAL) | ||||||
|  |             && !mem_type.property_flags.contains( | ||||||
|  |                 vk::MemoryPropertyFlags::HOST_VISIBLE | vk::MemoryPropertyFlags::HOST_COHERENT, | ||||||
|  |             ) | ||||||
|  |         { | ||||||
|  |             println!("Found host invisible memory at index {}", i); | ||||||
|  |             return Some(i as u32); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     println!("No host invisible memory found"); | ||||||
|  |     return None; | ||||||
|  | } | ||||||
| @ -0,0 +1,322 @@ | |||||||
|  | use std::{collections::HashMap, iter}; | ||||||
|  | 
 | ||||||
|  | use ash::{ | ||||||
|  |     khr::{surface, swapchain}, | ||||||
|  |     vk::{self, DescriptorSet}, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | use crate::{ | ||||||
|  |     Game, InWorld, LoadingWorld, RenderAvailable, RenderCtx, game::Camera, render::MemType, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | use super::{ | ||||||
|  |     MAX_HEIGHT, MAX_WIDTH, SwapchainCtx, | ||||||
|  |     swapchain::{ | ||||||
|  |         make_depth_img, make_depth_view, make_framebufs, make_swap_images, make_swap_views, | ||||||
|  |         setup_swapchain, | ||||||
|  |     }, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /// Anything can be rendered in world
 | ||||||
|  | pub trait WorldComponent { | ||||||
|  |     fn descriptors(&self) -> HashMap<vk::DescriptorType, u32>; | ||||||
|  | 
 | ||||||
|  |     fn desc_layout(&self, ctx: &RenderCtx) -> vk::DescriptorSetLayout; | ||||||
|  | 
 | ||||||
|  |     /// Skybox is passed in for reflections
 | ||||||
|  |     fn write_desc_set( | ||||||
|  |         &self, | ||||||
|  |         ctx: &RenderCtx, | ||||||
|  |         desc_set: vk::DescriptorSet, | ||||||
|  |         skybox: (vk::ImageView, vk::Sampler), | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     fn pipeline(&self) -> (vk::Pipeline, vk::PipelineLayout); | ||||||
|  | 
 | ||||||
|  |     fn push_constants( | ||||||
|  |         &self, | ||||||
|  |         camera: &Camera, | ||||||
|  |         res: [u32; 2], | ||||||
|  |         base_color: [f32; 4], | ||||||
|  |     ) -> Vec<(vk::ShaderStageFlags, u32, Vec<u8>)>; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub struct Component { | ||||||
|  |     pub inner: Box<dyn WorldComponent>, | ||||||
|  |     pub desc_set: DescriptorSet, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Game<RenderAvailable> { | ||||||
|  |     pub fn start_load_world(self) -> Game<LoadingWorld> { | ||||||
|  |         let Self { | ||||||
|  |             state: | ||||||
|  |                 RenderAvailable { | ||||||
|  |                     sdl_context, | ||||||
|  |                     ctx, | ||||||
|  |                     window, | ||||||
|  |                 }, | ||||||
|  |         } = self; | ||||||
|  | 
 | ||||||
|  |         let depth_mem = ctx.mem_alloc(MemType::HostInvisible, (MAX_WIDTH * MAX_HEIGHT * 8) as u64); | ||||||
|  | 
 | ||||||
|  |         let pass = setup_render_pass(&ctx); | ||||||
|  | 
 | ||||||
|  |         let surface_loader = surface::Instance::new(&ctx.entry, &ctx.instance); | ||||||
|  |         let swapchain_loader = swapchain::Device::new(&ctx.instance, &ctx.dev); | ||||||
|  | 
 | ||||||
|  |         let (swapchain, extents) = setup_swapchain( | ||||||
|  |             &ctx, | ||||||
|  |             &surface_loader, | ||||||
|  |             &swapchain_loader, | ||||||
|  |             window.width, | ||||||
|  |             window.height, | ||||||
|  |         ); | ||||||
|  | 
 | ||||||
|  |         let depth_img = make_depth_img(&ctx, window.width, window.height, depth_mem); | ||||||
|  |         let swap_images = make_swap_images(swapchain, &swapchain_loader); | ||||||
|  |         let swap_views = make_swap_views(&ctx, &swap_images, vk::Format::B8G8R8A8_UNORM); | ||||||
|  |         let depth_view = make_depth_view(&ctx, depth_img, vk::Format::D32_SFLOAT); | ||||||
|  |         let framebufs = make_framebufs( | ||||||
|  |             &ctx, | ||||||
|  |             &swap_views, | ||||||
|  |             &depth_view, | ||||||
|  |             &pass, | ||||||
|  |             window.width, | ||||||
|  |             window.height, | ||||||
|  |         ); | ||||||
|  | 
 | ||||||
|  |         let skybox = ctx.create_skybox(pass); | ||||||
|  | 
 | ||||||
|  |         let requested_descriptors = skybox.descriptors(); | ||||||
|  | 
 | ||||||
|  |         Game { | ||||||
|  |             state: LoadingWorld { | ||||||
|  |                 sdl_context, | ||||||
|  |                 ctx, | ||||||
|  |                 window, | ||||||
|  | 
 | ||||||
|  |                 requested_descriptors, | ||||||
|  | 
 | ||||||
|  |                 swapchain: SwapchainCtx { | ||||||
|  |                     surface_loader, | ||||||
|  |                     swapchain_loader, | ||||||
|  |                     swap_images, | ||||||
|  |                     swap_views, | ||||||
|  |                     depth_mem, | ||||||
|  |                     depth_image: depth_img, | ||||||
|  |                     depth_view, | ||||||
|  |                     framebufs, | ||||||
|  |                     swapchain, | ||||||
|  |                 }, | ||||||
|  |                 pass, | ||||||
|  | 
 | ||||||
|  |                 skybox, | ||||||
|  |                 components: Vec::new(), | ||||||
|  |             }, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Game<LoadingWorld> { | ||||||
|  |     pub fn add_component<C: WorldComponent + 'static>(&mut self, component: C) { | ||||||
|  |         let Self { | ||||||
|  |             state: | ||||||
|  |                 LoadingWorld { | ||||||
|  |                     requested_descriptors, | ||||||
|  |                     components, | ||||||
|  |                     .. | ||||||
|  |                 }, | ||||||
|  |         } = self; | ||||||
|  |         for (desc, n) in component.descriptors() { | ||||||
|  |             match requested_descriptors.remove(&desc) { | ||||||
|  |                 Some(m) => requested_descriptors.insert(desc, n + m), | ||||||
|  |                 None => requested_descriptors.insert(desc, n), | ||||||
|  |             }; | ||||||
|  |         } | ||||||
|  |         components.push(Box::new(component)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn start_world(self) -> Game<InWorld> { | ||||||
|  |         let Self { | ||||||
|  |             state: | ||||||
|  |                 LoadingWorld { | ||||||
|  |                     sdl_context, | ||||||
|  |                     window, | ||||||
|  |                     ctx, | ||||||
|  | 
 | ||||||
|  |                     requested_descriptors, | ||||||
|  | 
 | ||||||
|  |                     swapchain, | ||||||
|  |                     pass, | ||||||
|  | 
 | ||||||
|  |                     skybox, | ||||||
|  |                     components, | ||||||
|  |                 }, | ||||||
|  |         } = self; | ||||||
|  |         let desc_pool = setup_desc_pool(&ctx, requested_descriptors); | ||||||
|  | 
 | ||||||
|  |         let mut desc_sets = make_desc_sets( | ||||||
|  |             &ctx, | ||||||
|  |             &desc_pool, | ||||||
|  |             &components | ||||||
|  |                 .iter() | ||||||
|  |                 .map(|c| c.desc_layout(&ctx)) | ||||||
|  |                 .chain(iter::once(skybox.desc_layout(&ctx))) | ||||||
|  |                 .collect::<Vec<_>>(), | ||||||
|  |         ); | ||||||
|  | 
 | ||||||
|  |         let skybox_descriptor = desc_sets.pop().unwrap(); | ||||||
|  | 
 | ||||||
|  |         let components = components | ||||||
|  |             .into_iter() | ||||||
|  |             .zip(desc_sets.into_iter()) | ||||||
|  |             .map(|(c, d)| Component { | ||||||
|  |                 inner: c, | ||||||
|  |                 desc_set: d, | ||||||
|  |             }) | ||||||
|  |             .collect::<Vec<_>>(); | ||||||
|  | 
 | ||||||
|  |         for comp in &components { | ||||||
|  |             let Component { inner, desc_set } = comp; | ||||||
|  |             inner.write_desc_set(&ctx, *desc_set, (skybox.imageview, skybox.sampler)); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         skybox.write_desc_set(&ctx, skybox_descriptor, (skybox.imageview, skybox.sampler)); | ||||||
|  | 
 | ||||||
|  |         let sem_avail = ctx.make_sem(); | ||||||
|  |         let sem_finish = ctx.make_sem(); | ||||||
|  |         let fence_flight = ctx.make_fence(); | ||||||
|  | 
 | ||||||
|  |         Game { | ||||||
|  |             state: InWorld { | ||||||
|  |                 sdl_context, | ||||||
|  |                 window, | ||||||
|  |                 ctx, | ||||||
|  | 
 | ||||||
|  |                 skybox: (skybox, skybox_descriptor), | ||||||
|  |                 components, | ||||||
|  | 
 | ||||||
|  |                 sem_avail, | ||||||
|  |                 sem_finish, | ||||||
|  |                 fence_flight, | ||||||
|  | 
 | ||||||
|  |                 swapchain, | ||||||
|  |                 pass, | ||||||
|  | 
 | ||||||
|  |                 camera: Camera::default(), | ||||||
|  |             }, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn setup_render_pass(ctx: &RenderCtx) -> vk::RenderPass { | ||||||
|  |     let color_attach = vk::AttachmentDescription::default() | ||||||
|  |         .format(vk::Format::B8G8R8A8_UNORM) | ||||||
|  |         .samples(vk::SampleCountFlags::TYPE_1) | ||||||
|  |         .load_op(vk::AttachmentLoadOp::CLEAR) | ||||||
|  |         .store_op(vk::AttachmentStoreOp::STORE) | ||||||
|  |         .stencil_load_op(vk::AttachmentLoadOp::DONT_CARE) | ||||||
|  |         .stencil_store_op(vk::AttachmentStoreOp::DONT_CARE) | ||||||
|  |         .initial_layout(vk::ImageLayout::UNDEFINED) | ||||||
|  |         .final_layout(vk::ImageLayout::PRESENT_SRC_KHR); | ||||||
|  |     let depth_attach = vk::AttachmentDescription::default() | ||||||
|  |         .format(vk::Format::D32_SFLOAT) | ||||||
|  |         .samples(vk::SampleCountFlags::TYPE_1) | ||||||
|  |         .load_op(vk::AttachmentLoadOp::CLEAR) | ||||||
|  |         .store_op(vk::AttachmentStoreOp::DONT_CARE) | ||||||
|  |         .stencil_load_op(vk::AttachmentLoadOp::DONT_CARE) | ||||||
|  |         .stencil_store_op(vk::AttachmentStoreOp::DONT_CARE) | ||||||
|  |         .initial_layout(vk::ImageLayout::UNDEFINED) | ||||||
|  |         .final_layout(vk::ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL); | ||||||
|  | 
 | ||||||
|  |     let color_ref = vk::AttachmentReference::default() | ||||||
|  |         .attachment(0) | ||||||
|  |         .layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL); | ||||||
|  | 
 | ||||||
|  |     let depth_ref = vk::AttachmentReference::default() | ||||||
|  |         .attachment(1) | ||||||
|  |         .layout(vk::ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL); | ||||||
|  | 
 | ||||||
|  |     let color_refs = [color_ref]; | ||||||
|  |     let depth_refs = [depth_ref]; | ||||||
|  | 
 | ||||||
|  |     let subpass = vk::SubpassDescription::default() | ||||||
|  |         .pipeline_bind_point(vk::PipelineBindPoint::GRAPHICS) | ||||||
|  |         .color_attachments(color_refs.as_ref()) | ||||||
|  |         .depth_stencil_attachment(&depth_ref); | ||||||
|  | 
 | ||||||
|  |     let subpass_dep = vk::SubpassDependency::default() | ||||||
|  |         .src_subpass(vk::SUBPASS_EXTERNAL) | ||||||
|  |         .dst_subpass(0) | ||||||
|  |         .src_stage_mask( | ||||||
|  |             vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT | ||||||
|  |                 | vk::PipelineStageFlags::EARLY_FRAGMENT_TESTS, | ||||||
|  |         ) | ||||||
|  |         .dst_stage_mask( | ||||||
|  |             vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT | ||||||
|  |                 | vk::PipelineStageFlags::EARLY_FRAGMENT_TESTS, | ||||||
|  |         ) | ||||||
|  |         .src_access_mask(vk::AccessFlags::empty()) | ||||||
|  |         .dst_access_mask( | ||||||
|  |             vk::AccessFlags::COLOR_ATTACHMENT_WRITE | ||||||
|  |                 | vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_WRITE, | ||||||
|  |         ); | ||||||
|  | 
 | ||||||
|  |     let attach_desc = [color_attach, depth_attach]; | ||||||
|  | 
 | ||||||
|  |     let subpasses = [subpass]; | ||||||
|  | 
 | ||||||
|  |     let deps = [subpass_dep]; | ||||||
|  | 
 | ||||||
|  |     let pass_info = vk::RenderPassCreateInfo::default() | ||||||
|  |         .attachments(&attach_desc) | ||||||
|  |         .subpasses(subpasses.as_ref()) | ||||||
|  |         .dependencies(deps.as_ref()); | ||||||
|  | 
 | ||||||
|  |     let pass = unsafe { | ||||||
|  |         ctx.dev | ||||||
|  |             .create_render_pass(&pass_info, None) | ||||||
|  |             .expect("Failed to create render pass") | ||||||
|  |     }; | ||||||
|  |     pass | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn setup_desc_pool( | ||||||
|  |     ctx: &RenderCtx, | ||||||
|  |     requested_descriptors: HashMap<vk::DescriptorType, u32>, | ||||||
|  | ) -> vk::DescriptorPool { | ||||||
|  |     let pool_sizes = requested_descriptors | ||||||
|  |         .into_iter() | ||||||
|  |         .map(|(desc, count)| { | ||||||
|  |             vk::DescriptorPoolSize::default() | ||||||
|  |                 .ty(desc) | ||||||
|  |                 .descriptor_count(count) | ||||||
|  |         }) | ||||||
|  |         .collect::<Vec<_>>(); | ||||||
|  | 
 | ||||||
|  |     let pool_info = vk::DescriptorPoolCreateInfo::default() | ||||||
|  |         .max_sets(1) | ||||||
|  |         .pool_sizes(&pool_sizes); | ||||||
|  |     let pool = unsafe { | ||||||
|  |         ctx.dev | ||||||
|  |             .create_descriptor_pool(&pool_info, None) | ||||||
|  |             .expect("Failed to create descriptor pool") | ||||||
|  |     }; | ||||||
|  |     pool | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn make_desc_sets( | ||||||
|  |     ctx: &RenderCtx, | ||||||
|  |     desc_pool: &vk::DescriptorPool, | ||||||
|  |     layouts: &[vk::DescriptorSetLayout], | ||||||
|  | ) -> Vec<vk::DescriptorSet> { | ||||||
|  |     let alloc_info = vk::DescriptorSetAllocateInfo::default() | ||||||
|  |         .descriptor_pool(*desc_pool) | ||||||
|  |         .set_layouts(&layouts); | ||||||
|  |     let sets = unsafe { | ||||||
|  |         ctx.dev | ||||||
|  |             .allocate_descriptor_sets(&alloc_info) | ||||||
|  |             .expect("Failed to allocate descriptor sets") | ||||||
|  |     }; | ||||||
|  |     sets | ||||||
|  | } | ||||||
| @ -0,0 +1,210 @@ | |||||||
|  | use ash::{ | ||||||
|  |     khr::{surface, swapchain as khr_swapchain}, | ||||||
|  |     vk, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | use crate::RenderCtx; | ||||||
|  | 
 | ||||||
|  | mod cube; | ||||||
|  | pub mod in_world; | ||||||
|  | mod init; | ||||||
|  | pub mod loading_world; | ||||||
|  | pub mod skybox; | ||||||
|  | mod swapchain; | ||||||
|  | 
 | ||||||
|  | const MAX_WIDTH: u32 = 3440; | ||||||
|  | const MAX_HEIGHT: u32 = 1440; | ||||||
|  | 
 | ||||||
|  | pub struct SwapchainCtx { | ||||||
|  |     surface_loader: surface::Instance, | ||||||
|  |     swapchain_loader: khr_swapchain::Device, | ||||||
|  |     swap_images: Vec<vk::Image>, | ||||||
|  |     swap_views: Vec<vk::ImageView>, | ||||||
|  |     depth_mem: vk::DeviceMemory, | ||||||
|  |     depth_image: vk::Image, | ||||||
|  |     depth_view: vk::ImageView, | ||||||
|  |     framebufs: Vec<vk::Framebuffer>, | ||||||
|  |     swapchain: vk::SwapchainKHR, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub enum MemType { | ||||||
|  |     HostVisibile, | ||||||
|  |     HostInvisible, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl RenderCtx { | ||||||
|  |     pub fn mem_alloc(&self, typ: MemType, size: u64) -> vk::DeviceMemory { | ||||||
|  |         let mem_info = vk::MemoryAllocateInfo::default() | ||||||
|  |             .allocation_size(size) | ||||||
|  |             .memory_type_index(match typ { | ||||||
|  |                 MemType::HostVisibile => self.host_vis_idx, | ||||||
|  |                 MemType::HostInvisible => self.host_invis_idx, | ||||||
|  |             }); | ||||||
|  |         let mem = unsafe { self.dev.allocate_memory(&mem_info, None) } | ||||||
|  |             .expect("Failed to allocate memory"); | ||||||
|  |         println!("Allocated memory"); | ||||||
|  |         mem | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn alloc_buf( | ||||||
|  |         &self, | ||||||
|  |         mem: vk::DeviceMemory, | ||||||
|  |         size: vk::DeviceSize, | ||||||
|  |         usage: vk::BufferUsageFlags, | ||||||
|  |     ) -> vk::Buffer { | ||||||
|  |         unsafe { | ||||||
|  |             let buf_info = vk::BufferCreateInfo::default() | ||||||
|  |                 .size(size) | ||||||
|  |                 .usage(usage) | ||||||
|  |                 .sharing_mode(vk::SharingMode::EXCLUSIVE); | ||||||
|  |             let buf = self | ||||||
|  |                 .dev | ||||||
|  |                 .create_buffer(&buf_info, None) | ||||||
|  |                 .expect("Failed to create buffer"); | ||||||
|  | 
 | ||||||
|  |             self.dev | ||||||
|  |                 .bind_buffer_memory(buf, mem, 0) | ||||||
|  |                 .expect("Failed to bind buffer memory"); | ||||||
|  |             println!("Created buffer"); | ||||||
|  |             buf | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn buf_to_ptr(&self, mem: vk::DeviceMemory, buf: vk::Buffer) -> *mut std::ffi::c_void { | ||||||
|  |         let ptr = unsafe { | ||||||
|  |             self.dev | ||||||
|  |                 .map_memory(mem, 0, vk::WHOLE_SIZE, vk::MemoryMapFlags::empty()) | ||||||
|  |         } | ||||||
|  |         .expect("Failed to map memory"); | ||||||
|  |         println!("Mapped memory"); | ||||||
|  |         ptr | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn shader_mod_from_file( | ||||||
|  |         &self, | ||||||
|  |         file: &str, | ||||||
|  |         flags: vk::ShaderModuleCreateFlags, | ||||||
|  |     ) -> vk::ShaderModule { | ||||||
|  |         let shader_bin = std::fs::read(file).unwrap(); | ||||||
|  | 
 | ||||||
|  |         unsafe { | ||||||
|  |             self.dev.create_shader_module( | ||||||
|  |                 &vk::ShaderModuleCreateInfo { | ||||||
|  |                     code_size: shader_bin.len(), | ||||||
|  |                     p_code: shader_bin.as_ptr() as *const u32, | ||||||
|  |                     flags, | ||||||
|  |                     ..Default::default() | ||||||
|  |                 }, | ||||||
|  |                 None, | ||||||
|  |             ) | ||||||
|  |         } | ||||||
|  |         .expect(&format!("Failed to create shader module form file {file}")) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn setup_simple_shader_stage( | ||||||
|  |         &self, | ||||||
|  |         vert_shader: vk::ShaderModule, | ||||||
|  |         frag_shader: vk::ShaderModule, | ||||||
|  |     ) -> [vk::PipelineShaderStageCreateInfo; 2] { | ||||||
|  |         let vert_stage = vk::PipelineShaderStageCreateInfo::default() | ||||||
|  |             .stage(vk::ShaderStageFlags::VERTEX) | ||||||
|  |             .module(vert_shader) | ||||||
|  |             .name(c"main"); | ||||||
|  |         let frag_stage = vk::PipelineShaderStageCreateInfo::default() | ||||||
|  |             .stage(vk::ShaderStageFlags::FRAGMENT) | ||||||
|  |             .module(frag_shader) | ||||||
|  |             .name(c"main"); | ||||||
|  |         [vert_stage, frag_stage] | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn make_sem(&self) -> vk::Semaphore { | ||||||
|  |         let sem_info = vk::SemaphoreCreateInfo::default(); | ||||||
|  |         let sem = unsafe { | ||||||
|  |             self.dev | ||||||
|  |                 .create_semaphore(&sem_info, None) | ||||||
|  |                 .expect("Failed to create semaphore") | ||||||
|  |         }; | ||||||
|  |         sem | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn make_fence(&self) -> vk::Fence { | ||||||
|  |         let fence_info = vk::FenceCreateInfo::default().flags(vk::FenceCreateFlags::SIGNALED); | ||||||
|  |         let fence = unsafe { | ||||||
|  |             self.dev | ||||||
|  |                 .create_fence(&fence_info, None) | ||||||
|  |                 .expect("Failed to create fence") | ||||||
|  |         }; | ||||||
|  |         fence | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn wait_for_fence(&self, fence: vk::Fence) { | ||||||
|  |         unsafe { | ||||||
|  |             let fence = [fence]; | ||||||
|  |             self.dev | ||||||
|  |                 .wait_for_fences(&fence, true, u64::MAX) | ||||||
|  |                 .expect("Failed to wait for fence"); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn reset_fence(&self, fence: vk::Fence) { | ||||||
|  |         unsafe { | ||||||
|  |             let fence = [fence]; | ||||||
|  |             self.dev | ||||||
|  |                 .reset_fences(&fence) | ||||||
|  |                 .expect("Failed to reset fence"); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn start_cmd_buf(&self) { | ||||||
|  |         unsafe { | ||||||
|  |             self.dev | ||||||
|  |                 .reset_command_buffer(self.cmd_buf, vk::CommandBufferResetFlags::empty()) | ||||||
|  |                 .expect("Failed to reset command buffer"); | ||||||
|  |             let begin_info = | ||||||
|  |                 vk::CommandBufferBeginInfo::default().flags(vk::CommandBufferUsageFlags::empty()); | ||||||
|  |             self.dev | ||||||
|  |                 .begin_command_buffer(self.cmd_buf, &begin_info) | ||||||
|  |                 .expect("Failed to begin command buffer"); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn end_cmd_buf(&self) { | ||||||
|  |         unsafe { | ||||||
|  |             self.dev | ||||||
|  |                 .end_command_buffer(self.cmd_buf) | ||||||
|  |                 .expect("Failed to end command buffer"); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn submit_queue(&self, sem_avail: vk::Semaphore, sem_finish: vk::Semaphore, fence: vk::Fence) { | ||||||
|  |         let cmd_bufs = [self.cmd_buf]; | ||||||
|  |         let wait_sems = [sem_avail]; | ||||||
|  |         let signal_sems = [sem_finish]; | ||||||
|  |         let submit_info = vk::SubmitInfo::default() | ||||||
|  |             .command_buffers(&cmd_bufs) | ||||||
|  |             .wait_semaphores(&wait_sems) | ||||||
|  |             .wait_dst_stage_mask(&[vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT]) | ||||||
|  |             .signal_semaphores(&signal_sems); | ||||||
|  |         unsafe { | ||||||
|  |             self.dev | ||||||
|  |                 .queue_submit(self.queue, &[submit_info], fence) | ||||||
|  |                 .expect("Failed to submit queue"); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn queue_present(&self, swapchain: &SwapchainCtx, img_inx: u32, sem_finish: vk::Semaphore) { | ||||||
|  |         let swapchains = [swapchain.swapchain]; | ||||||
|  |         let img_inxs = [img_inx]; | ||||||
|  |         let wait_sems = [sem_finish]; | ||||||
|  |         let present_info = vk::PresentInfoKHR::default() | ||||||
|  |             .swapchains(&swapchains) | ||||||
|  |             .image_indices(&img_inxs) | ||||||
|  |             .wait_semaphores(&wait_sems); | ||||||
|  |         unsafe { | ||||||
|  |             swapchain | ||||||
|  |                 .swapchain_loader | ||||||
|  |                 .queue_present(self.queue, &present_info) | ||||||
|  |                 .expect("Failed to present queue"); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,318 @@ | |||||||
|  | use std::collections::HashMap; | ||||||
|  | 
 | ||||||
|  | use ash::vk::{self}; | ||||||
|  | 
 | ||||||
|  | use crate::{RenderCtx, decode_rif, game::Camera}; | ||||||
|  | 
 | ||||||
|  | use super::{MemType, loading_world::WorldComponent}; | ||||||
|  | 
 | ||||||
|  | pub struct Skybox { | ||||||
|  |     pub buf: vk::Buffer, | ||||||
|  |     pub image: vk::Image, | ||||||
|  |     pub imageview: vk::ImageView, | ||||||
|  |     pub sampler: vk::Sampler, | ||||||
|  |     pipeline: vk::Pipeline, | ||||||
|  |     pipe_layout: vk::PipelineLayout, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl WorldComponent for Skybox { | ||||||
|  |     fn descriptors(&self) -> HashMap<vk::DescriptorType, u32> { | ||||||
|  |         let mut map = HashMap::new(); | ||||||
|  |         map.insert(vk::DescriptorType::COMBINED_IMAGE_SAMPLER, 1); | ||||||
|  |         map | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn desc_layout(&self, ctx: &RenderCtx) -> vk::DescriptorSetLayout { | ||||||
|  |         let image_binding = vk::DescriptorSetLayoutBinding::default() | ||||||
|  |             .binding(0) | ||||||
|  |             .descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER) | ||||||
|  |             .descriptor_count(1) | ||||||
|  |             .stage_flags(vk::ShaderStageFlags::FRAGMENT); | ||||||
|  |         let layouts = [image_binding]; | ||||||
|  | 
 | ||||||
|  |         let layout_info = vk::DescriptorSetLayoutCreateInfo::default().bindings(&layouts); | ||||||
|  |         let layout = unsafe { | ||||||
|  |             ctx.dev | ||||||
|  |                 .create_descriptor_set_layout(&layout_info, None) | ||||||
|  |                 .expect("Failed to create descriptor set layout") | ||||||
|  |         }; | ||||||
|  |         layout | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn write_desc_set( | ||||||
|  |         &self, | ||||||
|  |         ctx: &RenderCtx, | ||||||
|  |         desc_set: vk::DescriptorSet, | ||||||
|  |         _: (vk::ImageView, vk::Sampler), | ||||||
|  |     ) { | ||||||
|  |         let img_info = vk::DescriptorImageInfo::default() | ||||||
|  |             .image_layout(vk::ImageLayout::GENERAL) | ||||||
|  |             .image_view(self.imageview) | ||||||
|  |             .sampler(self.sampler); | ||||||
|  |         let img_infos = &[img_info]; | ||||||
|  |         let img_desc = vk::WriteDescriptorSet::default() | ||||||
|  |             .dst_set(desc_set) | ||||||
|  |             .dst_binding(0) | ||||||
|  |             .dst_array_element(0) | ||||||
|  |             .descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER) | ||||||
|  |             .image_info(img_infos); | ||||||
|  | 
 | ||||||
|  |         unsafe { | ||||||
|  |             ctx.dev.update_descriptor_sets(&[img_desc], &[]); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn pipeline(&self) -> (vk::Pipeline, vk::PipelineLayout) { | ||||||
|  |         (self.pipeline, self.pipe_layout) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn push_constants( | ||||||
|  |         &self, | ||||||
|  |         camera: &Camera, | ||||||
|  |         res: [u32; 2], | ||||||
|  |         base_color: [f32; 4], | ||||||
|  |     ) -> Vec<(vk::ShaderStageFlags, u32, Vec<u8>)> { | ||||||
|  |         let mut constants = Vec::new(); | ||||||
|  |         let cam_data: Vec<u8> = (Vec::<f32>::from([ | ||||||
|  |             camera.origin.x, | ||||||
|  |             camera.origin.y, | ||||||
|  |             camera.origin.z, | ||||||
|  |             0.0, | ||||||
|  |             camera.rotation.x, | ||||||
|  |             camera.rotation.y, | ||||||
|  |             camera.rotation.z, | ||||||
|  |         ])) | ||||||
|  |         .iter() | ||||||
|  |         .map(|f| f.to_ne_bytes()) | ||||||
|  |         .collect::<Vec<_>>() | ||||||
|  |         .into_flattened(); | ||||||
|  |         constants.push((vk::ShaderStageFlags::VERTEX, 0, cam_data)); | ||||||
|  | 
 | ||||||
|  |         let screen_res: Vec<u8> = res | ||||||
|  |             .iter() | ||||||
|  |             .map(|f| f.to_ne_bytes()) | ||||||
|  |             .collect::<Vec<_>>() | ||||||
|  |             .into_flattened(); | ||||||
|  |         constants.push((vk::ShaderStageFlags::VERTEX, 32, screen_res)); | ||||||
|  | 
 | ||||||
|  |         constants | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Skybox {} | ||||||
|  | 
 | ||||||
|  | impl RenderCtx { | ||||||
|  |     pub(super) fn create_skybox(&self, pass: vk::RenderPass) -> Skybox { | ||||||
|  |         let skybox_mem = self.mem_alloc(MemType::HostVisibile, 3 * 2048 * 2048 * 6); | ||||||
|  |         let skybox_buf = self.alloc_buf( | ||||||
|  |             skybox_mem, | ||||||
|  |             3 * 2048 * 2048 * 6, | ||||||
|  |             vk::BufferUsageFlags::TRANSFER_SRC, | ||||||
|  |         ); | ||||||
|  |         let skybox_ptr = self.buf_to_ptr(skybox_mem, skybox_buf); | ||||||
|  |         let img = std::fs::read("resources/skybox.rif").unwrap(); | ||||||
|  |         let data = decode_rif(&img); | ||||||
|  |         unsafe { | ||||||
|  |             std::ptr::copy(data.as_ptr(), skybox_ptr as *mut u8, data.len()); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         let image = make_skybox_image(self); | ||||||
|  |         let imageview = make_skybox_image_view(self, image); | ||||||
|  |         let sampler = make_skybox_sampler(self); | ||||||
|  | 
 | ||||||
|  |         let pipe_layout = setup_pipe_layout(self); | ||||||
|  |         let pipeline = setup_pipeline(self, pass, pipe_layout); | ||||||
|  | 
 | ||||||
|  |         Skybox { | ||||||
|  |             buf: skybox_buf, | ||||||
|  |             image, | ||||||
|  |             imageview, | ||||||
|  |             sampler, | ||||||
|  |             pipeline, | ||||||
|  |             pipe_layout, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn make_skybox_image(ctx: &RenderCtx) -> vk::Image { | ||||||
|  |     let qf_idxs = [ctx.queue_idx]; | ||||||
|  |     let create_info = vk::ImageCreateInfo::default() | ||||||
|  |         .flags(vk::ImageCreateFlags::CUBE_COMPATIBLE) | ||||||
|  |         .image_type(vk::ImageType::TYPE_2D) | ||||||
|  |         .format(vk::Format::R8G8B8_UNORM) | ||||||
|  |         .extent(vk::Extent3D::default().width(2048).height(2048).depth(1)) | ||||||
|  |         .mip_levels(1) | ||||||
|  |         .array_layers(6) | ||||||
|  |         .samples(vk::SampleCountFlags::TYPE_1) | ||||||
|  |         .tiling(vk::ImageTiling::OPTIMAL) | ||||||
|  |         .usage(vk::ImageUsageFlags::TRANSFER_DST | vk::ImageUsageFlags::SAMPLED) | ||||||
|  |         .sharing_mode(vk::SharingMode::EXCLUSIVE) | ||||||
|  |         .initial_layout(vk::ImageLayout::UNDEFINED) | ||||||
|  |         .queue_family_indices(&qf_idxs); | ||||||
|  |     let image = | ||||||
|  |         unsafe { ctx.dev.create_image(&create_info, None) }.expect("Failed to create image"); | ||||||
|  | 
 | ||||||
|  |     let skybox_mem_size: vk::MemoryRequirements = | ||||||
|  |         unsafe { ctx.dev.get_image_memory_requirements(image) }; | ||||||
|  |     let skybox_dev_mem = ctx.mem_alloc(MemType::HostInvisible, skybox_mem_size.size); | ||||||
|  |     unsafe { ctx.dev.bind_image_memory(image, skybox_dev_mem, 0) } | ||||||
|  |         .expect("Failed to bind image memory"); | ||||||
|  | 
 | ||||||
|  |     image | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn make_skybox_image_view(ctx: &RenderCtx, image: vk::Image) -> vk::ImageView { | ||||||
|  |     let create_info = vk::ImageViewCreateInfo::default() | ||||||
|  |         .image(image) | ||||||
|  |         .view_type(vk::ImageViewType::CUBE) | ||||||
|  |         .format(vk::Format::R8G8B8_UNORM) | ||||||
|  |         .components(vk::ComponentMapping::default()) | ||||||
|  |         .subresource_range( | ||||||
|  |             vk::ImageSubresourceRange::default() | ||||||
|  |                 .aspect_mask(vk::ImageAspectFlags::COLOR) | ||||||
|  |                 .base_mip_level(0) | ||||||
|  |                 .level_count(1) | ||||||
|  |                 .base_array_layer(0) | ||||||
|  |                 .layer_count(6), | ||||||
|  |         ); | ||||||
|  |     unsafe { ctx.dev.create_image_view(&create_info, None) }.expect("Failed to create image view") | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn make_skybox_sampler(ctx: &RenderCtx) -> vk::Sampler { | ||||||
|  |     let create_info = vk::SamplerCreateInfo::default() | ||||||
|  |         .mag_filter(vk::Filter::LINEAR) | ||||||
|  |         .min_filter(vk::Filter::LINEAR) | ||||||
|  |         .mipmap_mode(vk::SamplerMipmapMode::LINEAR) | ||||||
|  |         .address_mode_u(vk::SamplerAddressMode::CLAMP_TO_EDGE) | ||||||
|  |         .address_mode_v(vk::SamplerAddressMode::CLAMP_TO_EDGE) | ||||||
|  |         .address_mode_w(vk::SamplerAddressMode::CLAMP_TO_EDGE) | ||||||
|  |         .max_anisotropy(1.0) | ||||||
|  |         .max_lod(1.0) | ||||||
|  |         .border_color(vk::BorderColor::FLOAT_OPAQUE_WHITE); | ||||||
|  |     unsafe { ctx.dev.create_sampler(&create_info, None) }.expect("Failed to create sampler") | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn load_skybox_shaders(ctx: &RenderCtx) -> (vk::ShaderModule, vk::ShaderModule) { | ||||||
|  |     let skybox_vert_shader = ctx.shader_mod_from_file( | ||||||
|  |         "shaders/skybox_vert.spv", | ||||||
|  |         vk::ShaderModuleCreateFlags::empty(), | ||||||
|  |     ); | ||||||
|  |     let skybox_frag_shader = ctx.shader_mod_from_file( | ||||||
|  |         "shaders/skybox_rag.spv", | ||||||
|  |         vk::ShaderModuleCreateFlags::empty(), | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     (skybox_vert_shader, skybox_frag_shader) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn setup_desc_layout(ctx: &RenderCtx) -> vk::DescriptorSetLayout { | ||||||
|  |     let image_binding = vk::DescriptorSetLayoutBinding::default() | ||||||
|  |         .binding(0) | ||||||
|  |         .descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER) | ||||||
|  |         .descriptor_count(1) | ||||||
|  |         .stage_flags(vk::ShaderStageFlags::FRAGMENT); | ||||||
|  |     let layouts = [image_binding]; | ||||||
|  | 
 | ||||||
|  |     let layout_info = vk::DescriptorSetLayoutCreateInfo::default().bindings(&layouts); | ||||||
|  |     let layout = unsafe { ctx.dev.create_descriptor_set_layout(&layout_info, None) } | ||||||
|  |         .expect("Failed to create descriptor set layout"); | ||||||
|  |     layout | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn setup_pipe_layout(ctx: &RenderCtx) -> vk::PipelineLayout { | ||||||
|  |     let layout = setup_desc_layout(ctx); | ||||||
|  |     let push_range: [vk::PushConstantRange; 1] = [ | ||||||
|  |         vk::PushConstantRange::default() | ||||||
|  |             .stage_flags(vk::ShaderStageFlags::VERTEX) | ||||||
|  |             .offset(0) | ||||||
|  |             .size(48), // vec4 camera_orig, camera_rot; uvec2 screen_res
 | ||||||
|  |     ]; | ||||||
|  | 
 | ||||||
|  |     let layouts = &[layout]; | ||||||
|  | 
 | ||||||
|  |     let pipe_layout = vk::PipelineLayoutCreateInfo::default() | ||||||
|  |         .set_layouts(layouts.as_ref()) | ||||||
|  |         .push_constant_ranges(&push_range); | ||||||
|  |     let pipe_layout = unsafe { | ||||||
|  |         ctx.dev | ||||||
|  |             .create_pipeline_layout(&pipe_layout, None) | ||||||
|  |             .expect("Failed to create pipeline layout") | ||||||
|  |     }; | ||||||
|  |     pipe_layout | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn setup_pipeline( | ||||||
|  |     ctx: &RenderCtx, | ||||||
|  |     pass: vk::RenderPass, | ||||||
|  |     layout: vk::PipelineLayout, | ||||||
|  | ) -> vk::Pipeline { | ||||||
|  |     let (vert_shader, frag_shader) = load_skybox_shaders(ctx); | ||||||
|  |     let shader_stages = ctx.setup_simple_shader_stage(vert_shader, frag_shader); | ||||||
|  |     let vert_input = vk::PipelineVertexInputStateCreateInfo::default(); | ||||||
|  |     let input_assembly = vk::PipelineInputAssemblyStateCreateInfo::default() | ||||||
|  |         .topology(vk::PrimitiveTopology::TRIANGLE_LIST); | ||||||
|  |     let rasterization = vk::PipelineRasterizationStateCreateInfo::default() | ||||||
|  |         .polygon_mode(vk::PolygonMode::FILL) | ||||||
|  |         .cull_mode(vk::CullModeFlags::NONE) | ||||||
|  |         .front_face(vk::FrontFace::CLOCKWISE) | ||||||
|  |         .line_width(1.0); | ||||||
|  |     let multisample = vk::PipelineMultisampleStateCreateInfo::default() | ||||||
|  |         .rasterization_samples(vk::SampleCountFlags::TYPE_1); | ||||||
|  |     let depth_stencil = vk::PipelineDepthStencilStateCreateInfo::default() | ||||||
|  |         .depth_test_enable(true) | ||||||
|  |         .depth_write_enable(true) | ||||||
|  |         .depth_compare_op(vk::CompareOp::LESS) | ||||||
|  |         .depth_bounds_test_enable(true) | ||||||
|  |         .stencil_test_enable(false) | ||||||
|  |         .min_depth_bounds(0.0) | ||||||
|  |         .max_depth_bounds(1.0); | ||||||
|  | 
 | ||||||
|  |     let blend = vk::PipelineColorBlendAttachmentState::default() | ||||||
|  |         .src_color_blend_factor(vk::BlendFactor::SRC_COLOR) | ||||||
|  |         .dst_color_blend_factor(vk::BlendFactor::SRC_COLOR) | ||||||
|  |         .color_blend_op(vk::BlendOp::ADD) | ||||||
|  |         .src_alpha_blend_factor(vk::BlendFactor::SRC_COLOR) | ||||||
|  |         .dst_alpha_blend_factor(vk::BlendFactor::SRC_COLOR) | ||||||
|  |         .alpha_blend_op(vk::BlendOp::ADD) | ||||||
|  |         .color_write_mask( | ||||||
|  |             vk::ColorComponentFlags::R | ||||||
|  |                 | vk::ColorComponentFlags::G | ||||||
|  |                 | vk::ColorComponentFlags::B | ||||||
|  |                 | vk::ColorComponentFlags::A, | ||||||
|  |         ); | ||||||
|  | 
 | ||||||
|  |     let blend_attachments = [blend]; | ||||||
|  | 
 | ||||||
|  |     let color_blend = vk::PipelineColorBlendStateCreateInfo::default() | ||||||
|  |         .attachments(blend_attachments.as_ref()) | ||||||
|  |         .logic_op_enable(false) | ||||||
|  |         .logic_op(vk::LogicOp::COPY) | ||||||
|  |         .blend_constants([1.0, 1.0, 1.0, 1.0]); | ||||||
|  | 
 | ||||||
|  |     let dynamic = vk::PipelineDynamicStateCreateInfo::default().dynamic_states(&[ | ||||||
|  |         vk::DynamicState::VIEWPORT_WITH_COUNT, | ||||||
|  |         vk::DynamicState::SCISSOR_WITH_COUNT, | ||||||
|  |     ]); | ||||||
|  | 
 | ||||||
|  |     let pipe_info = vk::GraphicsPipelineCreateInfo::default() | ||||||
|  |         .stages(&shader_stages) | ||||||
|  |         .vertex_input_state(&vert_input) | ||||||
|  |         .input_assembly_state(&input_assembly) | ||||||
|  |         .rasterization_state(&rasterization) | ||||||
|  |         .multisample_state(&multisample) | ||||||
|  |         .depth_stencil_state(&depth_stencil) | ||||||
|  |         .color_blend_state(&color_blend) | ||||||
|  |         .layout(layout) | ||||||
|  |         .dynamic_state(&dynamic) | ||||||
|  |         .render_pass(pass) | ||||||
|  |         .subpass(0); | ||||||
|  | 
 | ||||||
|  |     let pipe = unsafe { | ||||||
|  |         ctx.dev | ||||||
|  |             .create_graphics_pipelines(vk::PipelineCache::null(), &[pipe_info], None) | ||||||
|  |             .expect("Failed to create graphics pipeline") | ||||||
|  |             .remove(0) | ||||||
|  |     }; | ||||||
|  |     println!("Created pipeline"); | ||||||
|  |     pipe | ||||||
|  | } | ||||||
| @ -0,0 +1,203 @@ | |||||||
|  | use std::cmp; | ||||||
|  | 
 | ||||||
|  | use ash::{ | ||||||
|  |     khr::{surface, swapchain as khr_swapchain}, | ||||||
|  |     vk, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | use crate::{ | ||||||
|  |     RenderCtx, | ||||||
|  |     render::{MAX_HEIGHT, MAX_WIDTH}, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | pub fn setup_swapchain( | ||||||
|  |     ctx: &RenderCtx, | ||||||
|  |     surface_loader: &surface::Instance, | ||||||
|  |     swapchain_loader: &khr_swapchain::Device, | ||||||
|  |     width: u32, | ||||||
|  |     height: u32, | ||||||
|  | ) -> (vk::SwapchainKHR, vk::Extent2D) { | ||||||
|  |     let caps = unsafe { | ||||||
|  |         surface_loader | ||||||
|  |             .get_physical_device_surface_capabilities(ctx.pdev, ctx.surface) | ||||||
|  |             .expect("Failed to get surface capabilities") | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     let formats = unsafe { | ||||||
|  |         surface_loader | ||||||
|  |             .get_physical_device_surface_formats(ctx.pdev, ctx.surface) | ||||||
|  |             .expect("Failed to get surface formats") | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     let format = (formats) | ||||||
|  |         .iter() | ||||||
|  |         .filter(|f| { | ||||||
|  |             f.format == vk::Format::B8G8R8A8_UNORM | ||||||
|  |                 && f.color_space == vk::ColorSpaceKHR::SRGB_NONLINEAR | ||||||
|  |         }) | ||||||
|  |         .next() | ||||||
|  |         .expect("No suitable format found"); | ||||||
|  | 
 | ||||||
|  |     // When the window is resized fast, then the caps goes out of sync of the target extents
 | ||||||
|  |     // Thus we are using the passed-in (target) extents instead of the cap extents.
 | ||||||
|  |     // assert_eq!(caps.current_extent.width, width);
 | ||||||
|  |     // assert_eq!(caps.current_extent.height, height);
 | ||||||
|  | 
 | ||||||
|  |     let swap_create = vk::SwapchainCreateInfoKHR::default() | ||||||
|  |         .surface(ctx.surface) | ||||||
|  |         .min_image_count(caps.min_image_count) | ||||||
|  |         .image_format(format.format) | ||||||
|  |         .image_color_space(format.color_space) | ||||||
|  |         .image_extent(vk::Extent2D { | ||||||
|  |             width: cmp::min(cmp::max(caps.current_extent.width, width), MAX_WIDTH), | ||||||
|  |             height: cmp::min(cmp::max(caps.current_extent.height, height), MAX_HEIGHT), | ||||||
|  |         }) | ||||||
|  |         .image_array_layers(1) | ||||||
|  |         .image_usage(vk::ImageUsageFlags::COLOR_ATTACHMENT) | ||||||
|  |         .image_sharing_mode(vk::SharingMode::EXCLUSIVE) | ||||||
|  |         .pre_transform(vk::SurfaceTransformFlagsKHR::IDENTITY) | ||||||
|  |         .composite_alpha(vk::CompositeAlphaFlagsKHR::OPAQUE) | ||||||
|  |         .present_mode(vk::PresentModeKHR::MAILBOX) | ||||||
|  |         .clipped(true); | ||||||
|  | 
 | ||||||
|  |     let swapchain = unsafe { | ||||||
|  |         swapchain_loader | ||||||
|  |             .create_swapchain(&swap_create, None) | ||||||
|  |             .expect("Failed to create swapchain") | ||||||
|  |     }; | ||||||
|  |     println!("Created swapchain"); | ||||||
|  |     (swapchain, caps.current_extent) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub fn make_swap_images( | ||||||
|  |     swapchain: vk::SwapchainKHR, | ||||||
|  |     swapchain_loader: &khr_swapchain::Device, | ||||||
|  | ) -> Vec<vk::Image> { | ||||||
|  |     unsafe { | ||||||
|  |         let images = swapchain_loader | ||||||
|  |             .get_swapchain_images(swapchain) | ||||||
|  |             .expect("Failed to get swapchain images"); | ||||||
|  |         println!("Fetched swapchain images"); | ||||||
|  |         images | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub fn make_swap_views( | ||||||
|  |     ctx: &RenderCtx, | ||||||
|  |     swap_images: &[vk::Image], | ||||||
|  |     format: vk::Format, | ||||||
|  | ) -> Vec<vk::ImageView> { | ||||||
|  |     let mut views = Vec::new(); | ||||||
|  |     for img in swap_images.iter() { | ||||||
|  |         let view_info = vk::ImageViewCreateInfo::default() | ||||||
|  |             .image(*img) | ||||||
|  |             .view_type(vk::ImageViewType::TYPE_2D) | ||||||
|  |             .format(format) | ||||||
|  |             .components(vk::ComponentMapping::default()) | ||||||
|  |             .subresource_range( | ||||||
|  |                 vk::ImageSubresourceRange::default() | ||||||
|  |                     .aspect_mask(vk::ImageAspectFlags::COLOR) | ||||||
|  |                     .base_mip_level(0) | ||||||
|  |                     .level_count(1) | ||||||
|  |                     .base_array_layer(0) | ||||||
|  |                     .layer_count(1), | ||||||
|  |             ); | ||||||
|  |         let view = unsafe { | ||||||
|  |             ctx.dev | ||||||
|  |                 .create_image_view(&view_info, None) | ||||||
|  |                 .expect("Failed to create image view") | ||||||
|  |         }; | ||||||
|  |         views.push(view); | ||||||
|  |     } | ||||||
|  |     views | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub fn make_depth_img( | ||||||
|  |     ctx: &RenderCtx, | ||||||
|  |     width: u32, | ||||||
|  |     height: u32, | ||||||
|  |     mem: vk::DeviceMemory, | ||||||
|  | ) -> vk::Image { | ||||||
|  |     let queue_fams = [ctx.queue_idx]; | ||||||
|  |     let img_info = vk::ImageCreateInfo::default() | ||||||
|  |         .flags(vk::ImageCreateFlags::empty()) | ||||||
|  |         .image_type(vk::ImageType::TYPE_2D) | ||||||
|  |         .format(vk::Format::D32_SFLOAT) | ||||||
|  |         .extent(vk::Extent3D { | ||||||
|  |             width, | ||||||
|  |             height, | ||||||
|  |             depth: 1, | ||||||
|  |         }) | ||||||
|  |         .mip_levels(1) | ||||||
|  |         .array_layers(1) | ||||||
|  |         .samples(vk::SampleCountFlags::TYPE_1) | ||||||
|  |         .tiling(vk::ImageTiling::OPTIMAL) | ||||||
|  |         .usage(vk::ImageUsageFlags::DEPTH_STENCIL_ATTACHMENT) | ||||||
|  |         .sharing_mode(vk::SharingMode::EXCLUSIVE) | ||||||
|  |         .queue_family_indices(&queue_fams) | ||||||
|  |         .initial_layout(vk::ImageLayout::UNDEFINED); | ||||||
|  | 
 | ||||||
|  |     let img = unsafe { | ||||||
|  |         ctx.dev | ||||||
|  |             .create_image(&img_info, None) | ||||||
|  |             .expect("Failed to create image") | ||||||
|  |     }; | ||||||
|  |     println!("Created image"); | ||||||
|  | 
 | ||||||
|  |     unsafe { | ||||||
|  |         ctx.dev | ||||||
|  |             .bind_image_memory(img, mem, 0) | ||||||
|  |             .expect("Failed to bind image memory") | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     img | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub fn make_depth_view(ctx: &RenderCtx, depth_img: vk::Image, format: vk::Format) -> vk::ImageView { | ||||||
|  |     let view_info = vk::ImageViewCreateInfo::default() | ||||||
|  |         .image(depth_img) | ||||||
|  |         .view_type(vk::ImageViewType::TYPE_2D) | ||||||
|  |         .format(format) | ||||||
|  |         .components(vk::ComponentMapping::default()) | ||||||
|  |         .subresource_range( | ||||||
|  |             vk::ImageSubresourceRange::default() | ||||||
|  |                 .aspect_mask(vk::ImageAspectFlags::DEPTH) | ||||||
|  |                 .base_mip_level(0) | ||||||
|  |                 .level_count(1) | ||||||
|  |                 .base_array_layer(0) | ||||||
|  |                 .layer_count(1), | ||||||
|  |         ); | ||||||
|  |     let view = unsafe { | ||||||
|  |         ctx.dev | ||||||
|  |             .create_image_view(&view_info, None) | ||||||
|  |             .expect("Failed to create image view") | ||||||
|  |     }; | ||||||
|  |     view | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub fn make_framebufs( | ||||||
|  |     ctx: &RenderCtx, | ||||||
|  |     swap_views: &Vec<vk::ImageView>, | ||||||
|  |     depth_view: &vk::ImageView, | ||||||
|  |     pass: &vk::RenderPass, | ||||||
|  |     width: u32, | ||||||
|  |     height: u32, | ||||||
|  | ) -> Vec<vk::Framebuffer> { | ||||||
|  |     let mut framebufs = Vec::new(); | ||||||
|  |     for view in swap_views.iter() { | ||||||
|  |         let attachments = [*view, *depth_view]; | ||||||
|  |         let framebuf_info = vk::FramebufferCreateInfo::default() | ||||||
|  |             .render_pass(*pass) | ||||||
|  |             .attachments(attachments.as_ref()) | ||||||
|  |             .width(width) | ||||||
|  |             .height(height) | ||||||
|  |             .layers(1); | ||||||
|  |         let framebuf = unsafe { | ||||||
|  |             ctx.dev | ||||||
|  |                 .create_framebuffer(&framebuf_info, None) | ||||||
|  |                 .expect("Failed to create framebuffer") | ||||||
|  |         }; | ||||||
|  |         framebufs.push(framebuf); | ||||||
|  |     } | ||||||
|  |     framebufs | ||||||
|  | } | ||||||
					Loading…
					
					
				
		Reference in new issue