diff --git a/shaders/skybox.frag b/shaders/skybox.frag index 63315d9..f7c6ea4 100644 --- a/shaders/skybox.frag +++ b/shaders/skybox.frag @@ -1,7 +1,7 @@ #version 450 // vim: ft=c -layout(binding = 1) uniform samplerCube combined_image; +layout(binding = 0) uniform samplerCube combined_image; layout(location = 0) out vec4 outColor; diff --git a/src/game/mod.rs b/src/game/mod.rs new file mode 100644 index 0000000..125e218 --- /dev/null +++ b/src/game/mod.rs @@ -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 { + pub fn main_loop(mut self) -> Game { + 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, + }, + } + } +} diff --git a/src/input/ingame_input.rs b/src/input/ingame_input.rs new file mode 100644 index 0000000..86bfa0e --- /dev/null +++ b/src/input/ingame_input.rs @@ -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 { + 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; + } + } +} diff --git a/src/input/mod.rs b/src/input/mod.rs new file mode 100644 index 0000000..6abb388 --- /dev/null +++ b/src/input/mod.rs @@ -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)) {} +} diff --git a/src/main.rs b/src/main.rs index ae2e881..41cc3d9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,1149 +1,101 @@ #![allow(unused_variables, unused_mut)] +mod game; +mod input; +mod render; + use ash::Device; use ash::Entry; use ash::Instance; -use ash::khr::{surface, swapchain}; +use ash::khr::surface; use ash::vk; -use ash::vk::ImageCreateFlags; -use sdl2::mouse::MouseUtil; - -use std::cmp; -use std::ffi::CStr; -use std::ffi::CString; -use std::thread; -use std::time::Duration; -use std::time::Instant; +use game::Camera; +use render::SwapchainCtx; +use render::loading_world::Component; +use render::loading_world::WorldComponent; +use render::skybox::Skybox; +use sdl2::Sdl; -use raw_window_handle::HasDisplayHandle; -use raw_window_handle::HasWindowHandle; - -use sdl2::event::Event; -use sdl2::event::WindowEvent; -use sdl2::keyboard::Keycode; -use sdl2::video::Window; +use std::collections::HashMap; const APP_NAME: &str = "MineClone"; -const MOV_STEP: f32 = 0.05; -const ROT_MOUSE_SCALE: f32 = 0.001; - -const MAX_WIDTH: u32 = 3440; -const MAX_HEIGHT: u32 = 1440; - -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, +pub trait GameState {} - // LEFT - -0.5, -0.5, 1.0, 1.0, - -1.0, 0.0, 0.0, 0.0, +pub struct Unstarted; - -0.5, 0.5, 2.0, 1.0, - -1.0, 0.0, 0.0, 0.0, +impl GameState for Unstarted {} - -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, -]; - -fn create_instance(window: &Window, entry: &Entry) -> Instance { - unsafe { - 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::>(); - let exts: &[*const i8] = exts.as_slice(); - - let insinfo = vk::InstanceCreateInfo::default() - .application_info(&appinfo) - .enabled_extension_names(exts); - - let instance = entry - .create_instance(&insinfo, None) - .expect("Failed to create instance"); - println!("Created instance"); - instance - } -} - -fn find_pdev_dgpu(instance: &Instance) -> vk::PhysicalDevice { - unsafe { - let mut pdevs = instance - .enumerate_physical_devices() - .expect("Failed to enumerate devices"); - for (i, d) in pdevs.iter().enumerate() { - let props = 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: {}", - 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); - } -} - -fn make_surface(entry: &Entry, instance: &Instance, window: &Window) -> vk::SurfaceKHR { - unsafe { - let surface = 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 - } +pub struct RenderCtx { + entry: Entry, + instance: Instance, + pdev: vk::PhysicalDevice, + dev: Device, + surface: vk::SurfaceKHR, + surface_loader: surface::Instance, + queue: vk::Queue, + queue_idx: u32, + cmd_buf: vk::CommandBuffer, + host_vis_idx: u32, + host_invis_idx: u32, } -// Using Intel GPU hardware terminology 🥴 -fn find_rcs_queue_inx(instance: &Instance, pdev: &vk::PhysicalDevice) -> Option { - unsafe { - let qfams = 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::>(); - 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 { - unsafe { - let mem_props = 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 { - unsafe { - let mem_props = 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; - } -} - -fn mem_alloc(dev: &Device, mem_type_inx: u32, size: u64) -> vk::DeviceMemory { - unsafe { - let mem_info = vk::MemoryAllocateInfo::default() - .allocation_size(size) - .memory_type_index(mem_type_inx); - let mem = dev - .allocate_memory(&mem_info, None) - .expect("Failed to allocate memory"); - println!("Allocated memory"); - mem - } -} - -fn make_depth_img( - dev: &Device, - host_invisible_inx: u32, +pub struct Window { width: u32, height: u32, - mem: vk::DeviceMemory, - qfam: u32, -) -> vk::Image { - unsafe { - let queue_fams = [qfam]; - 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 = dev - .create_image(&img_info, None) - .expect("Failed to create image"); - println!("Created image"); - - dev.bind_image_memory(img, mem, 0) - .expect("Failed to bind image memory"); - - img - } -} - -fn alloc_buf( - dev: &Device, - mem: &vk::DeviceMemory, - size: u64, - usage: vk::BufferUsageFlags, -) -> vk::Buffer { - unsafe { - let buf_info = vk::BufferCreateInfo::default() - .size(size) - .usage(usage) - .sharing_mode(vk::SharingMode::EXCLUSIVE); - let buf = dev - .create_buffer(&buf_info, None) - .expect("Failed to create buffer"); - - dev.bind_buffer_memory(buf, *mem, 0) - .expect("Failed to bind buffer memory"); - println!("Created buffer"); - buf - } } -fn buf_to_ptr(dev: &Device, mem: &vk::DeviceMemory, buf: &vk::Buffer) -> *mut std::ffi::c_void { - unsafe { - let ptr = dev - .map_memory(*mem, 0, vk::WHOLE_SIZE, vk::MemoryMapFlags::empty()) - .expect("Failed to map memory"); - println!("Mapped memory"); - ptr - } -} - -fn setup_desc_layout(dev: &Device) -> 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 { - dev.create_descriptor_set_layout(&layout_info, None) - .expect("Failed to create descriptor set layout") - }; - layout -} - -fn setup_render_pass(dev: &Device) -> 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 { - dev.create_render_pass(&pass_info, None) - .expect("Failed to create render pass") - }; - pass -} - -fn setup_pipe_layout(dev: &Device) -> vk::PipelineLayout { - let layout = setup_desc_layout(&dev); - let push_range: [vk::PushConstantRange; 2] = [ - vk::PushConstantRange::default() - .stage_flags(vk::ShaderStageFlags::VERTEX) - .offset(0) - .size(48), // vec4 camera_orig, camera_rot - 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 { - dev.create_pipeline_layout(&pipe_layout, None) - .expect("Failed to create pipeline layout") - }; - pipe_layout -} - -fn setup_shader_stage( - dev: &Device, - 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] +pub struct RenderAvailable { + sdl_context: Sdl, + window: Window, + ctx: RenderCtx, } -fn setup_pipeline( - dev: &Device, - vert_shader: &vk::ShaderModule, - frag_shader: &vk::ShaderModule, - pass: &vk::RenderPass, - pipe_layout: &vk::PipelineLayout, - cull_mode: vk::CullModeFlags, -) -> vk::Pipeline { - let shader_stages = setup_shader_stage(&dev, *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(cull_mode) - .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, - ); +impl GameState for RenderAvailable {} - let blend_attachments = [blend]; +pub struct LoadingWorld { + sdl_context: Sdl, + window: Window, + ctx: RenderCtx, - 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]); + requested_descriptors: HashMap, - let dynamic = vk::PipelineDynamicStateCreateInfo::default().dynamic_states(&[ - vk::DynamicState::VIEWPORT_WITH_COUNT, - vk::DynamicState::SCISSOR_WITH_COUNT, - ]); + swapchain: SwapchainCtx, + pass: vk::RenderPass, - 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(*pipe_layout) - .dynamic_state(&dynamic) - .render_pass(*pass) - .subpass(0); - - let pipe = unsafe { - dev.create_graphics_pipelines(vk::PipelineCache::null(), &[pipe_info], None) - .expect("Failed to create graphics pipeline") - .remove(0) - }; - println!("Created pipeline"); - pipe + skybox: Skybox, + components: Vec>, } -// TODO dynamic state for resizing -fn setup_swapchain( - pdev: &vk::PhysicalDevice, - dev: &Device, - surface: &vk::SurfaceKHR, - surface_loader: &surface::Instance, - swapchain_loader: &ash::khr::swapchain::Device, - instance: &Instance, - width: u32, - height: u32, -) -> (vk::SwapchainKHR, vk::Extent2D) { - let caps = unsafe { - surface_loader - .get_physical_device_surface_capabilities(*pdev, *surface) - .expect("Failed to get surface capabilities") - }; +impl GameState for LoadingWorld {} - let formats = unsafe { - surface_loader - .get_physical_device_surface_formats(*pdev, *surface) - .expect("Failed to get surface formats") - }; +pub struct InWorld { + sdl_context: Sdl, + window: Window, + ctx: RenderCtx, - 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"); + skybox: (Skybox, vk::DescriptorSet), + components: Vec, - // 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); + sem_avail: vk::Semaphore, + sem_finish: vk::Semaphore, + fence_flight: vk::Fence, - let swap_create = vk::SwapchainCreateInfoKHR::default() - .surface(*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); + swapchain: SwapchainCtx, + pass: vk::RenderPass, - let swapchain = unsafe { - swapchain_loader - .create_swapchain(&swap_create, None) - .expect("Failed to create swapchain") - }; - println!("Created swapchain"); - (swapchain, caps.current_extent) + camera: Camera, } -fn setup_desc_pool(dev: &Device) -> vk::DescriptorPool { - let pool_size_storage = vk::DescriptorPoolSize::default() - .ty(vk::DescriptorType::STORAGE_BUFFER) - .descriptor_count(1); - let pool_size_image = vk::DescriptorPoolSize::default() - .ty(vk::DescriptorType::COMBINED_IMAGE_SAMPLER) - .descriptor_count(1); - let pool_sizes = [pool_size_storage, pool_size_image]; +impl GameState for InWorld {} - let pool_info = vk::DescriptorPoolCreateInfo::default() - .max_sets(1) - .pool_sizes(&pool_sizes); - let pool = unsafe { - dev.create_descriptor_pool(&pool_info, None) - .expect("Failed to create descriptor pool") - }; - pool +pub struct Game { + state: S, } -fn make_swap_images( - dev: &Device, - swapchain: &vk::SwapchainKHR, - swapchain_loader: &swapchain::Device, -) -> Vec { - unsafe { - let images = swapchain_loader - .get_swapchain_images(*swapchain) - .expect("Failed to get swapchain images"); - println!("Fetched swapchain images"); - images - } -} - -fn make_swap_views( - dev: &Device, - swap_images: &Vec, - format: vk::Format, -) -> Vec { - 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 { - dev.create_image_view(&view_info, None) - .expect("Failed to create image view") - }; - views.push(view); - } - views -} - -fn make_depth_view(dev: &Device, 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 { - dev.create_image_view(&view_info, None) - .expect("Failed to create image view") - }; - view -} - -fn make_framebufs( - dev: &Device, - swap_views: &Vec, - depth_view: &vk::ImageView, - pass: &vk::RenderPass, - width: u32, - height: u32, -) -> Vec { - 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 { - dev.create_framebuffer(&framebuf_info, None) - .expect("Failed to create framebuffer") - }; - framebufs.push(framebuf); - } - framebufs -} - -fn make_desc_sets(dev: &Device, desc_pool: &vk::DescriptorPool) -> Vec { - let layout = setup_desc_layout(&dev); - let layouts = [layout]; - let alloc_info = vk::DescriptorSetAllocateInfo::default() - .descriptor_pool(*desc_pool) - .set_layouts(&layouts); - let sets = unsafe { - dev.allocate_descriptor_sets(&alloc_info) - .expect("Failed to allocate descriptor sets") - }; - sets -} - -fn write_desc_sets( - dev: &Device, - desc_sets: &Vec, - mesh_buf: &vk::Buffer, - image_view: vk::ImageView, - skybox_sampler: vk::Sampler, -) { - let buf_info = vk::DescriptorBufferInfo::default() - .buffer(*mesh_buf) - .offset(0) - .range(vk::WHOLE_SIZE); - let buf_infos = [buf_info]; - let buf_desc = vk::WriteDescriptorSet::default() - .dst_set(desc_sets[0]) - .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(image_view) - .sampler(skybox_sampler); - let img_infos = &[img_info]; - let img_desc = vk::WriteDescriptorSet::default() - .dst_set(desc_sets[0]) - .dst_binding(1) - .dst_array_element(0) - .descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER) - .image_info(img_infos); - - unsafe { - dev.update_descriptor_sets(&[buf_desc, img_desc], &[]); - } -} - -fn make_sem(dev: &Device) -> vk::Semaphore { - let sem_info = vk::SemaphoreCreateInfo::default(); - let sem = unsafe { - dev.create_semaphore(&sem_info, None) - .expect("Failed to create semaphore") - }; - sem -} - -fn make_fence(dev: &Device) -> vk::Fence { - let fence_info = vk::FenceCreateInfo::default().flags(vk::FenceCreateFlags::SIGNALED); - let fence = unsafe { - dev.create_fence(&fence_info, None) - .expect("Failed to create fence") - }; - fence -} - -fn wait_for_fence(dev: &Device, fence: &vk::Fence) { - unsafe { - let fence = [*fence]; - dev.wait_for_fences(&fence, true, u64::MAX) - .expect("Failed to wait for fence"); - } -} - -fn reset_fence(dev: &Device, fence: &vk::Fence) { - unsafe { - let fence = [*fence]; - dev.reset_fences(&fence).expect("Failed to reset fence"); - } -} - -fn ack_next_img( - dev: &Device, - swapchain_loader: &swapchain::Device, - swapchain: &vk::SwapchainKHR, - sem_avail: &vk::Semaphore, - fence: &vk::Fence, -) -> u32 { - unsafe { - let (img_inx, _) = swapchain_loader - .acquire_next_image(*swapchain, u64::MAX, *sem_avail, vk::Fence::null()) - .expect("Failed to acquire next image"); - img_inx - } -} - -fn record_commands( - dev: &Device, - cmd_buf: &vk::CommandBuffer, - framebuf: &vk::Framebuffer, - pass: &vk::RenderPass, - width: u32, - height: u32, - pipes: &[(vk::Pipeline, vk::PipelineLayout, &[vk::DescriptorSet])], - camera: &Camera, - skybox_buffer: &vk::Buffer, - skybox_image: &vk::Image, -) { - 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(*framebuf) - .render_area( - vk::Rect2D::default() - .offset(vk::Offset2D::default()) - .extent(vk::Extent2D { width, 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::>(); - dev.cmd_copy_buffer_to_image( - *cmd_buf, - *skybox_buffer, - *skybox_image, - vk::ImageLayout::GENERAL, - ®ions, - ); - dev.cmd_begin_render_pass(*cmd_buf, &render_pass_info, vk::SubpassContents::INLINE); - - let screen_res: Vec = (Vec::::from([width, height])) - .iter() - .map(|f| f.to_ne_bytes()) - .collect::>() - .into_flattened(); - let base_color: Vec = (Vec::::from([0.0, 1.0, 1.0, 1.0])) - .iter() - .map(|f| f.to_ne_bytes()) - .collect::>() - .into_flattened(); - let cam_data: Vec = (Vec::::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::>() - .into_flattened(); - - for (pipe, layout, desc_sets) in pipes { - dev.cmd_bind_pipeline(*cmd_buf, vk::PipelineBindPoint::GRAPHICS, *pipe); - - 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, height })], - ); - - dev.cmd_push_constants( - *cmd_buf, - *layout, - vk::ShaderStageFlags::VERTEX, - 0, - cam_data.as_ref(), - ); - dev.cmd_push_constants( - *cmd_buf, - *layout, - vk::ShaderStageFlags::VERTEX, - 32, - screen_res.as_ref(), - ); - dev.cmd_push_constants( - *cmd_buf, - *layout, - vk::ShaderStageFlags::FRAGMENT, - 48, - base_color.as_ref(), - ); - - dev.cmd_draw(*cmd_buf, 36, 1, 0, 0); - } - - dev.cmd_end_render_pass(*cmd_buf); - } -} - -fn start_cmd_buf(dev: &Device, cmd_buf: &vk::CommandBuffer) { - unsafe { - dev.reset_command_buffer(*cmd_buf, vk::CommandBufferResetFlags::empty()) - .expect("Failed to reset command buffer"); - let begin_info = - vk::CommandBufferBeginInfo::default().flags(vk::CommandBufferUsageFlags::empty()); - dev.begin_command_buffer(*cmd_buf, &begin_info) - .expect("Failed to begin command buffer"); - } -} - -fn end_cmd_buf(dev: &Device, cmd_buf: &vk::CommandBuffer) { - unsafe { - dev.end_command_buffer(*cmd_buf) - .expect("Failed to end command buffer"); - } -} - -fn submit_queue( - dev: &Device, - queue: &vk::Queue, - cmd_buf: &vk::CommandBuffer, - sem_avail: &vk::Semaphore, - sem_finish: &vk::Semaphore, - fence: &vk::Fence, -) { - let cmd_bufs = [*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 { - dev.queue_submit(*queue, &[submit_info], *fence) - .expect("Failed to submit queue"); - } -} - -fn queue_present( - swapchain_loader: &swapchain::Device, - queue: &vk::Queue, - swapchain: &vk::SwapchainKHR, - img_inx: u32, - sem_finish: &vk::Semaphore, -) { - let swapchains = [*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_loader - .queue_present(*queue, &present_info) - .expect("Failed to present queue"); - } -} - -#[derive(Debug)] -struct Vector3 { - x: f32, - y: f32, - z: f32, -} - -#[derive(Debug)] -struct Camera { - origin: Vector3, - rotation: Vector3, -} - -fn handle_cont_key(keycode: Keycode, camera: &mut Camera, mouse: &MouseUtil) { - 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; - } - _ => {} +impl Game { + pub fn new() -> Self { + Self { state: Unstarted } } } @@ -1154,390 +106,18 @@ fn decode_rif(raw: &[u8]) -> &[u8] { fn main() { let sdl_context = sdl2::init().unwrap(); let video_subsystem = sdl_context.video().unwrap(); - let mouse = sdl_context.mouse(); - - let mut width = 1024; - let mut height = 768; let mut window = video_subsystem - .window(APP_NAME, width, height) + .window(APP_NAME, 1024, 768) .vulkan() .resizable() .build() .unwrap(); - // We start in relative move - mouse.set_relative_mouse_mode(true); - - let entry = Entry::linked(); - - let instance = create_instance(&window, &entry); - - let surface = make_surface(&entry, &instance, &window); - - let pdev = find_pdev_dgpu(&instance); - - let rcs_queue_inx = find_rcs_queue_inx(&instance, &pdev).expect("No RCS queue found"); - - let dev = make_dev( - &instance, - &pdev, - rcs_queue_inx, - vec![c"VK_KHR_swapchain", c"VK_EXT_extended_dynamic_state3"], - ); - - let queue = unsafe { dev.get_device_queue(rcs_queue_inx, 0) }; - - let cmd_pool = make_command_pool(&dev, rcs_queue_inx); - - let cmd_buf = alloc_cmd_buf(&dev, cmd_pool); - - let host_visible_inx = - find_host_visible(&instance, &pdev).expect("No host visible memory found"); - - let host_invisible_inx = - find_host_invisible(&instance, &pdev).expect("No host invisible memory found"); - - let mesh_mem = mem_alloc(&dev, host_visible_inx, MESH_SIZE); - // Max viewport size is 3440x1440 - let depth_mem = mem_alloc( - &dev, - host_invisible_inx, - (MAX_WIDTH * MAX_HEIGHT * 8) as u64, - ); - - let mut depth_img = make_depth_img( - &dev, - host_invisible_inx, - width, - height, - depth_mem, - rcs_queue_inx, - ); - - let mesh_buf = alloc_buf( - &dev, - &mesh_mem, - MESH_SIZE, - vk::BufferUsageFlags::TRANSFER_SRC | vk::BufferUsageFlags::STORAGE_BUFFER, - ); - - let mesh_mem = buf_to_ptr(&dev, &mesh_mem, &mesh_buf); - unsafe { - std::ptr::copy(POSITIONS.as_ptr(), mesh_mem as *mut f32, POSITIONS.len()); - } - - let (skybox_image, skybox_image_view, skybox_sampler) = { - let qf_idxs = [rcs_queue_inx]; - let create_info = vk::ImageCreateInfo::default() - .flags(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 { dev.create_image(&create_info, None) }.expect("Failed to create image"); - - let skybox_mem_size: vk::MemoryRequirements = - unsafe { dev.get_image_memory_requirements(image) }; - let skybox_dev_mem = mem_alloc(&dev, host_invisible_inx, skybox_mem_size.size); - unsafe { - dev.bind_image_memory(image, skybox_dev_mem, 0) - .expect("Failed to bind image memory"); - } - 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), - ); - let image_view = unsafe { dev.create_image_view(&create_info, None) } - .expect("Failed to create image view"); - 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); - let sampler = - unsafe { dev.create_sampler(&create_info, None) }.expect("Failed to create sampler"); - - (image, image_view, sampler) - }; - - let skybox_mem = mem_alloc(&dev, host_visible_inx, 3 * 2048 * 2048 * 6); - let skybox_buf = alloc_buf( - &dev, - &skybox_mem, - 3 * 2048 * 2048 * 6, - vk::BufferUsageFlags::TRANSFER_SRC, - ); - let skybox_ptr = buf_to_ptr(&dev, &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 skybox_vert_shader_bin = std::fs::read("shaders/skybox_vert.spv").unwrap(); - let skybox_frag_shader_bin = std::fs::read("shaders/skybox_frag.spv").unwrap(); - - let skybox_vert_shader = unsafe { - dev.create_shader_module( - &vk::ShaderModuleCreateInfo { - code_size: skybox_vert_shader_bin.len(), - p_code: skybox_vert_shader_bin.as_ptr() as *const u32, - ..Default::default() - }, - None, - ) - .expect("Failed to create vertex shader module") - }; - - let skybox_frag_shader = unsafe { - dev.create_shader_module( - &vk::ShaderModuleCreateInfo { - code_size: skybox_frag_shader_bin.len(), - p_code: skybox_frag_shader_bin.as_ptr() as *const u32, - ..Default::default() - }, - None, - ) - .expect("Failed to create fragment shader module") - }; - - let cube_vert_shader_bin = std::fs::read("shaders/cube_vert.spv").unwrap(); - let cube_frag_shader_bin = std::fs::read("shaders/cube_frag.spv").unwrap(); - - let cube_vert_shader = unsafe { - dev.create_shader_module( - &vk::ShaderModuleCreateInfo { - code_size: cube_vert_shader_bin.len(), - p_code: cube_vert_shader_bin.as_ptr() as *const u32, - ..Default::default() - }, - None, - ) - .expect("Failed to create vertex shader module") - }; - - let cube_frag_shader = unsafe { - dev.create_shader_module( - &vk::ShaderModuleCreateInfo { - code_size: cube_frag_shader_bin.len(), - p_code: cube_frag_shader_bin.as_ptr() as *const u32, - ..Default::default() - }, - None, - ) - .expect("Failed to create fragment shader module") - }; - - let pass = setup_render_pass(&dev); - - let pipe_layout = setup_pipe_layout(&dev); - let skybox_pipe = setup_pipeline( - &dev, - &skybox_vert_shader, - &skybox_frag_shader, - &pass, - &pipe_layout, - vk::CullModeFlags::NONE, - ); - let cube_pipe = setup_pipeline( - &dev, - &cube_vert_shader, - &cube_frag_shader, - &pass, - &pipe_layout, - vk::CullModeFlags::FRONT, - ); - - let surface_loader = surface::Instance::new(&entry, &instance); - let swapchain_loader = swapchain::Device::new(&instance, &dev); - - let (mut swapchain, mut extents) = setup_swapchain( - &pdev, - &dev, - &surface, - &surface_loader, - &swapchain_loader, - &instance, - width, - height, - ); - - let mut swap_images = make_swap_images(&dev, &swapchain, &swapchain_loader); - - let mut swap_views = make_swap_views(&dev, &swap_images, vk::Format::B8G8R8A8_UNORM); - - let mut depth_view = make_depth_view(&dev, &depth_img, vk::Format::D32_SFLOAT); - - let mut framebufs = make_framebufs(&dev, &swap_views, &depth_view, &pass, width, height); - - let desc_pool = setup_desc_pool(&dev); - - let desc_sets = make_desc_sets(&dev, &desc_pool); - - write_desc_sets( - &dev, - &desc_sets, - &mesh_buf, - skybox_image_view, - skybox_sampler, - ); - - let sem_avail = make_sem(&dev); - let sem_finish = make_sem(&dev); - let fence_flight = make_fence(&dev); - - let mut event_pump = sdl_context.event_pump().expect("Failed to get event pump"); - let mut camera = Camera { - origin: Vector3 { - x: 0.0, - y: 0.0, - z: 0.0, - }, - rotation: Vector3 { - x: 0.0, - y: 0.0, - z: 0.0, - }, - }; - - let mut running = true; - while running { - let now = Instant::now(); - for event in event_pump.poll_iter() { - match event { - Event::Quit { .. } => running = false, - Event::Window { - win_event: WindowEvent::Resized(w, h), - .. - } => { - width = w as u32; - height = h as u32; - unsafe { - dev.device_wait_idle() - .expect("Failed to wait for device idle"); - swapchain_loader.destroy_swapchain(swapchain, None); - } - (swapchain, _) = setup_swapchain( - &pdev, - &dev, - &surface, - &surface_loader, - &swapchain_loader, - &instance, - width, - height, - ); - swap_images = make_swap_images(&dev, &swapchain, &swapchain_loader); - swap_views = make_swap_views(&dev, &swap_images, vk::Format::B8G8R8A8_UNORM); - depth_img = make_depth_img( - &dev, - host_invisible_inx, - width, - height, - depth_mem, - rcs_queue_inx, - ); - depth_view = make_depth_view(&dev, &depth_img, vk::Format::D32_SFLOAT); - framebufs = - make_framebufs(&dev, &swap_views, &depth_view, &pass, width, height); - } - // We do this key here and not in `handle_cont_key()` because we don't want key - // repeat, just a single press - Event::KeyDown { - keycode: Some(Keycode::ESCAPE), - repeat: false, - .. - } => { - mouse.set_relative_mouse_mode(!mouse.relative_mouse_mode()); - } - Event::MouseMotion { - window_id, - xrel, - yrel, - .. - } if mouse.relative_mouse_mode() => { - // We only wanna do movement if the mouse is in relative mode (ie. its pinned to - // the center of the window and hidden) - camera.rotation.y -= xrel as f32 * ROT_MOUSE_SCALE; - camera.rotation.x -= yrel as f32 * ROT_MOUSE_SCALE; - mouse.warp_mouse_in_window(&window, width as i32 / 2, height as i32 / 2); - } - _ => {} - } - } - - event_pump - .keyboard_state() - .pressed_scancodes() - .filter_map(Keycode::from_scancode) - .for_each(|k| handle_cont_key(k, &mut camera, &mouse)); - - wait_for_fence(&dev, &fence_flight); - reset_fence(&dev, &fence_flight); - - let img_inx = ack_next_img( - &dev, - &swapchain_loader, - &swapchain, - &sem_avail, - &fence_flight, - ); - - start_cmd_buf(&dev, &cmd_buf); - - record_commands( - &dev, - &cmd_buf, - &framebufs[img_inx as usize], - &pass, - width, - height, - &[ - (cube_pipe, pipe_layout, &desc_sets), - (skybox_pipe, pipe_layout, &desc_sets), - ], - &camera, - &skybox_buf, - &skybox_image, - ); - end_cmd_buf(&dev, &cmd_buf); - - submit_queue( - &dev, - &queue, - &cmd_buf, - &sem_avail, - &sem_finish, - &fence_flight, - ); - - queue_present(&swapchain_loader, &queue, &swapchain, img_inx, &sem_finish); - - let elapsed = now.elapsed(); - let delay = (1_000_000u128 / 60).saturating_sub(elapsed.as_micros()); - thread::sleep(Duration::from_micros(delay as u64)); - } + let game = Game::new().init_render(sdl_context, window); + let mut game = game.start_load_world(); + let cube = game.create_cube(); + game.add_component(cube); + let game = game.start_world(); + let game = game.main_loop(); } diff --git a/src/render/cube.rs b/src/render/cube.rs new file mode 100644 index 0000000..937a534 --- /dev/null +++ b/src/render/cube.rs @@ -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 { + 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)> { + let mut constants = Vec::new(); + let cam_data: Vec = (Vec::::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::>() + .into_flattened(); + constants.push((vk::ShaderStageFlags::VERTEX, 0, cam_data)); + + let screen_res: Vec = res + .iter() + .map(|f| f.to_ne_bytes()) + .collect::>() + .into_flattened(); + constants.push((vk::ShaderStageFlags::VERTEX, 32, screen_res)); + + let base_color: Vec = (Vec::::from([0.0, 1.0, 1.0, 1.0])) + .iter() + .map(|f| f.to_ne_bytes()) + .collect::>() + .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 { + 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 +} diff --git a/src/render/in_world.rs b/src/render/in_world.rs new file mode 100644 index 0000000..02ce576 --- /dev/null +++ b/src/render/in_world.rs @@ -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 { + 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::>(); + 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 + } +} diff --git a/src/render/init.rs b/src/render/init.rs new file mode 100644 index 0000000..34efea4 --- /dev/null +++ b/src/render/init.rs @@ -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 { + pub fn init_render(self, sdl_context: Sdl, window: Window) -> Game { + 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::>(); + 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 { + 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::>(); + 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 { + 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 { + 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; +} diff --git a/src/render/loading_world.rs b/src/render/loading_world.rs new file mode 100644 index 0000000..8d04ff6 --- /dev/null +++ b/src/render/loading_world.rs @@ -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; + + 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)>; +} + +pub struct Component { + pub inner: Box, + pub desc_set: DescriptorSet, +} + +impl Game { + pub fn start_load_world(self) -> Game { + 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 { + pub fn add_component(&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 { + 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::>(), + ); + + 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::>(); + + 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::DescriptorPool { + let pool_sizes = requested_descriptors + .into_iter() + .map(|(desc, count)| { + vk::DescriptorPoolSize::default() + .ty(desc) + .descriptor_count(count) + }) + .collect::>(); + + 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 { + 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 +} diff --git a/src/render/mod.rs b/src/render/mod.rs new file mode 100644 index 0000000..838f979 --- /dev/null +++ b/src/render/mod.rs @@ -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, + swap_views: Vec, + depth_mem: vk::DeviceMemory, + depth_image: vk::Image, + depth_view: vk::ImageView, + framebufs: Vec, + 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"); + } + } +} diff --git a/src/render/skybox.rs b/src/render/skybox.rs new file mode 100644 index 0000000..0dddfd9 --- /dev/null +++ b/src/render/skybox.rs @@ -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 { + 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)> { + let mut constants = Vec::new(); + let cam_data: Vec = (Vec::::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::>() + .into_flattened(); + constants.push((vk::ShaderStageFlags::VERTEX, 0, cam_data)); + + let screen_res: Vec = res + .iter() + .map(|f| f.to_ne_bytes()) + .collect::>() + .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 +} diff --git a/src/render/swapchain.rs b/src/render/swapchain.rs new file mode 100644 index 0000000..494450e --- /dev/null +++ b/src/render/swapchain.rs @@ -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 { + 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 { + 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, + depth_view: &vk::ImageView, + pass: &vk::RenderPass, + width: u32, + height: u32, +) -> Vec { + 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 +}