forked from itycodes/MineClone
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.
251 lines
8.0 KiB
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;
|
|
}
|