Theme

alex/beta-minecraft-rust

Minecraft Beta 1.7.3 Rewrite

BRANCH main
main
SNAPSHOT Draw terrain GPU-driven from packed per-section buffers with GPU occlusion culling, add a render distance slider (2-128) with a quad/disk shape, cut chunk bookkeeping at high render distances, and fix a light underflow at y=0
DOWNLOAD
Download file

root / crates / client / src / gpu / instance.rs

use std::ffi::{CStr, c_char};
use std::ptr::{self, null, null_mut};
use std::sync::Arc;

use raw_window_handle::{RawDisplayHandle, RawWindowHandle};
use volcanic::loader::{InstanceCommands, Loader};
use volcanic::*;

use super::validation::{LAYER, VAR, Validation};
use super::{Error, Result, check, create, zeroed};

pub struct Instance {
    pub(crate) handle: VkInstance,
    pub(crate) commands: InstanceCommands,
    /// Whether `VK_KHR_get_surface_capabilities2` is enabled, which present timing needs.
    pub(crate) surface_capabilities2: bool,
    messenger: VkDebugUtilsMessengerEXT,
    _loader: Loader,
}

// SAFETY: Vulkan instance handles and their loaded commands may be used from any thread.
unsafe impl Send for Instance {}
unsafe impl Sync for Instance {}

impl Instance {
    pub fn new(display: RawDisplayHandle, validation: Option<Validation>) -> Result<Arc<Self>> {
        let loader = unsafe { Loader::new()? };
        let platform_extension = match display {
            RawDisplayHandle::Wayland(_) => VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME,
            RawDisplayHandle::Xlib(_) => VK_KHR_XLIB_SURFACE_EXTENSION_NAME,
            RawDisplayHandle::Xcb(_) => VK_KHR_XCB_SURFACE_EXTENSION_NAME,
            _ => return Err(Error::UnsupportedWindowSystem),
        };

        let validation = match validation {
            Some(_) if !layer_available(&loader)? => {
                tracing::warn!("{VAR} is set but {LAYER:?} is not installed");
                None
            }
            validation => validation,
        };
        if let Some(validation) = validation {
            tracing::info!("Vulkan validation enabled: {VAR}={validation}");
        }

        let mut extensions: Vec<*const c_char> = vec![
            VK_KHR_SURFACE_EXTENSION_NAME.as_ptr(),
            platform_extension.as_ptr(),
        ];
        let surface_capabilities2 =
            extension_available(&loader, VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME)?;
        if surface_capabilities2 {
            extensions.push(VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME.as_ptr());
        }
        let layers: Vec<*const c_char> = if validation.is_some() {
            extensions.push(VK_EXT_DEBUG_UTILS_EXTENSION_NAME.as_ptr());
            extensions.push(VK_EXT_LAYER_SETTINGS_EXTENSION_NAME.as_ptr());
            vec![LAYER.as_ptr()]
        } else {
            Vec::new()
        };

        let application = VkApplicationInfo {
            sType: VkStructureType::ApplicationInfo,
            pApplicationName: c"Minecraft".as_ptr(),
            apiVersion: VK_API_VERSION_1_4,
            ..zeroed()
        };
        let report_flags = validation.map(Validation::report_flags).unwrap_or_default();
        let layer_settings = validation.map(|v| v.layer_settings(&report_flags));
        let messenger_info = validation.map(Validation::messenger_info);
        let settings_info = layer_settings
            .as_ref()
            .map(|settings| VkLayerSettingsCreateInfoEXT {
                sType: VkStructureType::LayerSettingsCreateInfoExt,
                pNext: messenger_info
                    .as_ref()
                    .map_or(null(), |info| ptr::from_ref(info).cast()),
                settingCount: settings.len() as u32,
                pSettings: settings.as_ptr(),
            });
        let info = VkInstanceCreateInfo {
            sType: VkStructureType::InstanceCreateInfo,
            pNext: settings_info
                .as_ref()
                .map_or(null(), |info| ptr::from_ref(info).cast()),
            pApplicationInfo: &application,
            enabledLayerCount: layers.len() as u32,
            ppEnabledLayerNames: layers.as_ptr(),
            enabledExtensionCount: extensions.len() as u32,
            ppEnabledExtensionNames: extensions.as_ptr(),
            ..zeroed()
        };
        let handle = create(|handle| unsafe { loader.vkCreateInstance(&info, null(), handle) })?;
        let commands = unsafe { loader.load_instance(handle)? };
        let messenger = match &messenger_info {
            Some(messenger_info) => create(|messenger| unsafe {
                commands.vkCreateDebugUtilsMessengerEXT(handle, messenger_info, null(), messenger)
            })?,
            None => VK_NULL_HANDLE,
        };
        Ok(Arc::new(Self {
            handle,
            commands,
            surface_capabilities2,
            messenger,
            _loader: loader,
        }))
    }
}

impl Drop for Instance {
    fn drop(&mut self) {
        let commands = &self.commands;
        unsafe {
            if self.messenger != VK_NULL_HANDLE {
                commands.vkDestroyDebugUtilsMessengerEXT(self.handle, self.messenger, null());
            }
            commands.vkDestroyInstance(self.handle, null());
        }
    }
}

fn layer_available(loader: &Loader) -> Result<bool> {
    let layers = super::enumerate(|count, items| unsafe {
        loader.vkEnumerateInstanceLayerProperties(count, items)
    })?;
    Ok(layers
        .iter()
        .any(|layer| unsafe { CStr::from_ptr(layer.layerName.as_ptr()) } == LAYER))
}

fn extension_available(loader: &Loader, name: &CStr) -> Result<bool> {
    let extensions = super::enumerate(|count, items| unsafe {
        loader.vkEnumerateInstanceExtensionProperties(null(), count, items)
    })?;
    Ok(extensions
        .iter()
        .any(|extension| unsafe { CStr::from_ptr(extension.extensionName.as_ptr()) } == name))
}

pub struct Surface {
    pub(crate) handle: VkSurfaceKHR,
    pub(crate) instance: Arc<Instance>,
}

impl Surface {
    /// # Safety
    /// The window and display handles must stay valid for the surface's lifetime.
    pub unsafe fn new(
        instance: &Arc<Instance>,
        display: RawDisplayHandle,
        window: RawWindowHandle,
    ) -> Result<Self> {
        let (commands, mut handle) = (&instance.commands, VK_NULL_HANDLE);
        let result = match (display, window) {
            (RawDisplayHandle::Wayland(display), RawWindowHandle::Wayland(window)) => {
                let info = VkWaylandSurfaceCreateInfoKHR {
                    sType: VkStructureType::WaylandSurfaceCreateInfoKhr,
                    display: display.display.as_ptr(),
                    surface: window.surface.as_ptr(),
                    ..zeroed()
                };
                unsafe {
                    commands.vkCreateWaylandSurfaceKHR(instance.handle, &info, null(), &mut handle)
                }
            }
            (RawDisplayHandle::Xlib(display), RawWindowHandle::Xlib(window)) => {
                let info = VkXlibSurfaceCreateInfoKHR {
                    sType: VkStructureType::XlibSurfaceCreateInfoKhr,
                    dpy: display.display.map_or(null_mut(), |d| d.as_ptr()),
                    window: window.window,
                    ..zeroed()
                };
                unsafe {
                    commands.vkCreateXlibSurfaceKHR(instance.handle, &info, null(), &mut handle)
                }
            }
            (RawDisplayHandle::Xcb(display), RawWindowHandle::Xcb(window)) => {
                let info = VkXcbSurfaceCreateInfoKHR {
                    sType: VkStructureType::XcbSurfaceCreateInfoKhr,
                    connection: display.connection.map_or(null_mut(), |c| c.as_ptr()),
                    window: window.window.get(),
                    ..zeroed()
                };
                unsafe {
                    commands.vkCreateXcbSurfaceKHR(instance.handle, &info, null(), &mut handle)
                }
            }
            _ => return Err(Error::UnsupportedWindowSystem),
        };
        check(result)?;
        Ok(Self {
            handle,
            instance: instance.clone(),
        })
    }
}

impl Drop for Surface {
    fn drop(&mut self) {
        let commands = &self.instance.commands;
        unsafe { commands.vkDestroySurfaceKHR(self.instance.handle, self.handle, null()) };
    }
}