From 2f15842ca45d29316a35b1fe5ee1e7bf8b5ddb3c Mon Sep 17 00:00:00 2001 From: Avery Date: Thu, 17 Apr 2025 00:47:02 +0200 Subject: [PATCH] Reorganise and split `main.rs` - The state of the game is now tracked using a simple state machine, where the main struct representing the game is `Game` and its state a generic implementing `GameState` on that struct, the following states exist: - `Unstarted`: Nothing has been done yet - `RenderAvailable`: The very basic things for rendering are available (sdl context, entry, instance, device, cmdbuf, surface, etc) - `LoadingWorld`: We are loading into a world, the only thing that can be done here currently is add things to be rendered (Components) of which only the `Cube` is available, with the skybox being automaticall added. - `InWorld`: We have loaded a world and can (theoretically) render things and move about - The render logic now lives in `render/` and is split both by phase, as well as components and the swapchain having their own modules - The input logic now lives in input and constits of one trait that should be implemented for states that should handle input, and is currently capable of handling mouse movenent, key presses and keys that should be held down (ex, movement keys) - The game logic lives in `game/` and is/will be responsible for driving both the input and render modules Note: the current commit is broken, as our surface is lost when returning from `Game::init_render` --- shaders/skybox.frag | 2 +- src/game/mod.rs | 96 +++ src/input/ingame_input.rs | 67 ++ src/input/mod.rs | 19 + src/main.rs | 1556 ++--------------------------------- src/render/cube.rs | 413 ++++++++++ src/render/in_world.rs | 284 +++++++ src/render/init.rs | 250 ++++++ src/render/loading_world.rs | 322 ++++++++ src/render/mod.rs | 210 +++++ src/render/skybox.rs | 318 +++++++ src/render/swapchain.rs | 203 +++++ 12 files changed, 2251 insertions(+), 1489 deletions(-) create mode 100644 src/game/mod.rs create mode 100644 src/input/ingame_input.rs create mode 100644 src/input/mod.rs create mode 100644 src/render/cube.rs create mode 100644 src/render/in_world.rs create mode 100644 src/render/init.rs create mode 100644 src/render/loading_world.rs create mode 100644 src/render/mod.rs create mode 100644 src/render/skybox.rs create mode 100644 src/render/swapchain.rs 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 +}