You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
MineClone/src/render/init.rs

251 lines
8.0 KiB

use std::ffi::{CStr, CString};
use ash::{
Device, Entry, Instance,
vk::{self},
};
use raw_window_handle::{HasDisplayHandle as _, HasWindowHandle as _};
use sdl2::{Sdl, video::Window};
use crate::{APP_NAME, Game, RenderAvailable, RenderCtx, Unstarted};
impl Game<Unstarted> {
pub fn init_render(self, sdl_context: Sdl, window: Window) -> Game<RenderAvailable> {
let (width, height) = window.size();
let entry = Entry::linked();
let instance = create_instance(&window, &entry);
let surface = make_surface(&entry, &instance, &window);
let pdev = find_pdev_dgpu(&instance);
let queue_idx = find_rcs_queue_inx(&instance, &pdev).expect("No RCS queue found");
let dev = make_dev(
&instance,
&pdev,
queue_idx,
vec![c"VK_KHR_swapchain", c"VK_EXT_extended_dynamic_state3"],
);
let queue = unsafe { dev.get_device_queue(queue_idx, 0) };
let cmd_pool = make_command_pool(&dev, queue_idx);
let cmd_buf = alloc_cmd_buf(&dev, cmd_pool);
let host_vis_idx =
find_host_visible(&instance, &pdev).expect("No host visible memory found");
let host_invis_idx =
find_host_invisible(&instance, &pdev).expect("No host invisible memory found");
Game {
state: RenderAvailable {
sdl_context,
window: crate::Window {
width,
height,
_sdl: window,
},
ctx: RenderCtx {
entry,
instance,
pdev,
dev,
surface,
queue,
queue_idx,
cmd_buf,
host_vis_idx,
host_invis_idx,
},
},
}
}
}
fn create_instance(window: &Window, entry: &Entry) -> Instance {
let n_app_name = CString::new(APP_NAME).unwrap();
let n_engine_name = CString::new(format!("{} Engine", APP_NAME)).unwrap();
let appinfo = vk::ApplicationInfo::default()
.application_name(n_app_name.as_c_str())
.application_version(0)
.engine_name(n_engine_name.as_c_str())
.engine_version(0)
.api_version(vk::make_api_version(0, 1, 3, 0));
let default_exts = window
.vulkan_instance_extensions()
.expect("Failed to get list of required extensions from SDL2");
let mut exts = vec![];
exts.extend(default_exts);
let exts: Vec<*const i8> = exts
.iter()
.map(|s| s.as_ptr() as *const i8)
.collect::<Vec<_>>();
let exts: &[*const i8] = exts.as_slice();
let insinfo = vk::InstanceCreateInfo::default()
.application_info(&appinfo)
.enabled_extension_names(exts);
let instance =
unsafe { entry.create_instance(&insinfo, None) }.expect("Failed to create instance");
println!("Created instance");
instance
}
fn make_surface(entry: &Entry, instance: &Instance, window: &Window) -> vk::SurfaceKHR {
let surface = unsafe {
ash_window::create_surface(
&entry,
&instance,
window.display_handle().unwrap().as_raw(),
window.window_handle().unwrap().as_raw(),
None,
)
}
.expect("Failed to create surface");
println!("Created surface");
surface
}
fn find_pdev_dgpu(instance: &Instance) -> vk::PhysicalDevice {
let mut pdevs =
unsafe { instance.enumerate_physical_devices() }.expect("Failed to enumerate devices");
for (i, d) in pdevs.iter().enumerate() {
let props = unsafe { instance.get_physical_device_properties(*d) };
if props.device_type == vk::PhysicalDeviceType::DISCRETE_GPU {
// TODO this assumes that the first discrete GPU will have an RCS queue family
// This is not guaranteed by the spec (example, a compute-only GPU)
// Fix.
println!(
"Found discrete GPU: {}",
unsafe { std::str::from_utf8(std::mem::transmute(props.device_name.as_slice())) }
.unwrap()
);
return pdevs.remove(i);
}
}
println!("No discrete GPU found");
assert!(pdevs.len() > 0, "No GPU found");
return pdevs.remove(0);
}
// Using Intel GPU hardware terminology 🥴
fn find_rcs_queue_inx(instance: &Instance, pdev: &vk::PhysicalDevice) -> Option<u32> {
let qfams = unsafe { instance.get_physical_device_queue_family_properties(*pdev) };
for (inx, qfam) in qfams.iter().enumerate() {
if qfam
.queue_flags
.contains(vk::QueueFlags::GRAPHICS | vk::QueueFlags::COMPUTE)
{
println!("Found RCS queue at index {}", inx);
return Some(inx as u32);
}
}
println!("No RCS queue found");
return None;
}
fn make_dev(
instance: &Instance,
pdev: &vk::PhysicalDevice,
rcs_queue_inx: u32,
exts: Vec<&CStr>,
) -> Device {
unsafe {
let dev_queue = vec![
vk::DeviceQueueCreateInfo::default()
.queue_family_index(rcs_queue_inx)
.queue_priorities(&[1.0]),
];
let exts = exts.iter().map(|&s| s.as_ptr()).collect::<Vec<_>>();
let exts = exts.as_slice();
// Enable all features
let features = instance.get_physical_device_features(*pdev);
let dev_info = vk::DeviceCreateInfo::default()
.queue_create_infos(&dev_queue)
.enabled_extension_names(exts)
.enabled_features(&features);
let dev = instance
.create_device(*pdev, &dev_info, None)
.expect("Failed to create device");
println!("Created device");
dev
}
}
fn make_command_pool(dev: &Device, rcs_queue_inx: u32) -> vk::CommandPool {
unsafe {
let pool_info = vk::CommandPoolCreateInfo::default()
.queue_family_index(rcs_queue_inx)
.flags(vk::CommandPoolCreateFlags::RESET_COMMAND_BUFFER);
let pool = dev
.create_command_pool(&pool_info, None)
.expect("Failed to create command pool");
println!("Created command pool");
pool
}
}
fn alloc_cmd_buf(dev: &Device, pool: vk::CommandPool) -> vk::CommandBuffer {
unsafe {
let alloc_info = vk::CommandBufferAllocateInfo::default()
.command_pool(pool)
.level(vk::CommandBufferLevel::PRIMARY)
.command_buffer_count(1);
let cmd_buf = dev
.allocate_command_buffers(&alloc_info)
.expect("Failed to allocate command buffer")
.remove(0);
println!("Allocated command buffer");
cmd_buf
}
}
fn find_host_visible(instance: &Instance, pdev: &vk::PhysicalDevice) -> Option<u32> {
let mem_props = unsafe { instance.get_physical_device_memory_properties(*pdev) };
for (i, mem_type) in mem_props.memory_types.iter().enumerate() {
if mem_type.property_flags.contains(
vk::MemoryPropertyFlags::DEVICE_LOCAL
| vk::MemoryPropertyFlags::HOST_VISIBLE
| vk::MemoryPropertyFlags::HOST_COHERENT,
) {
println!("Found host visible memory at index {}", i);
return Some(i as u32);
}
}
println!("No host visible memory found");
return None;
}
fn find_host_invisible(instance: &Instance, pdev: &vk::PhysicalDevice) -> Option<u32> {
let mem_props = unsafe { instance.get_physical_device_memory_properties(*pdev) };
for (i, mem_type) in mem_props.memory_types.iter().enumerate() {
if mem_type
.property_flags
.contains(vk::MemoryPropertyFlags::DEVICE_LOCAL)
&& !mem_type.property_flags.contains(
vk::MemoryPropertyFlags::HOST_VISIBLE | vk::MemoryPropertyFlags::HOST_COHERENT,
)
{
println!("Found host invisible memory at index {}", i);
return Some(i as u32);
}
}
println!("No host invisible memory found");
return None;
}