Theme

alex/volcanic-mem

BRANCH main
main
SNAPSHOT Initial commit
DOWNLOAD
Download file

root / tests / common / mod.rs

#![allow(dead_code)]

use core::ffi::{CStr, c_char, c_void};
use core::ptr::{null, null_mut};
use std::sync::atomic::{AtomicUsize, Ordering};
use volcanic::loader::{DeviceCommands, InstanceCommands, Loader};
use volcanic::*;
use volcanic_mem::{Allocator, AllocatorConfig};

pub static VALIDATION_ERRORS: AtomicUsize = AtomicUsize::new(0);

unsafe extern "system" fn on_message(
    severity: VkDebugUtilsMessageSeverityFlagBitsEXT,
    _: VkDebugUtilsMessageTypeFlagsEXT,
    data: *const VkDebugUtilsMessengerCallbackDataEXT,
    _: *mut c_void,
) -> VkBool32 {
    let message = unsafe { CStr::from_ptr((*data).pMessage) }.to_string_lossy();
    if severity.contains(VkDebugUtilsMessageSeverityFlagBitsEXT::ERROR_EXT) {
        VALIDATION_ERRORS.fetch_add(1, Ordering::Relaxed);
        eprintln!("[validation error] {message}");
    } else {
        eprintln!("[validation warning] {message}");
    }
    0
}

pub struct Context {
    pub loader: Loader,
    pub instance: InstanceCommands,
    pub instance_handle: VkInstance,
    pub messenger: VkDebugUtilsMessengerEXT,
    pub physical_device: VkPhysicalDevice,
    pub device: DeviceCommands,
    pub device_handle: VkDevice,
    pub queue: VkQueue,
    pub queue_family: u32,
    pub command_pool: VkCommandPool,
    pub validation: bool,
}

pub fn check(result: VkResult) {
    assert_eq!(result, VkResult::Success);
}

impl Context {
    pub fn new() -> Self {
        unsafe {
            let loader = Loader::new().expect("Vulkan loader");
            let layer = c"VK_LAYER_KHRONOS_validation";
            let validation = std::env::var_os("NO_VALIDATION").is_none() && {
                let mut count = 0;
                check(loader.vkEnumerateInstanceLayerProperties(&mut count, null_mut()));
                let mut layers = vec![core::mem::zeroed::<VkLayerProperties>(); count as usize];
                check(loader.vkEnumerateInstanceLayerProperties(&mut count, layers.as_mut_ptr()));
                layers
                    .iter()
                    .any(|l| CStr::from_ptr(l.layerName.as_ptr()) == layer)
            };

            let app = VkApplicationInfo {
                sType: VkStructureType::ApplicationInfo,
                pNext: null(),
                pApplicationName: c"volcanic-mem".as_ptr(),
                applicationVersion: 0,
                pEngineName: null(),
                engineVersion: 0,
                apiVersion: VK_API_VERSION_1_4,
            };
            let messenger_info = VkDebugUtilsMessengerCreateInfoEXT {
                sType: VkStructureType::DebugUtilsMessengerCreateInfoExt,
                pNext: null(),
                flags: VkDebugUtilsMessengerCreateFlagsEXT::empty(),
                messageSeverity: VkDebugUtilsMessageSeverityFlagsEXT::WARNING_EXT
                    | VkDebugUtilsMessageSeverityFlagsEXT::ERROR_EXT,
                messageType: VkDebugUtilsMessageTypeFlagsEXT::GENERAL_EXT
                    | VkDebugUtilsMessageTypeFlagsEXT::VALIDATION_EXT
                    | VkDebugUtilsMessageTypeFlagsEXT::PERFORMANCE_EXT,
                pfnUserCallback: Some(on_message),
                pUserData: null_mut(),
            };
            let layers = [layer.as_ptr()];
            let extensions = [c"VK_EXT_debug_utils".as_ptr()];
            let info = VkInstanceCreateInfo {
                sType: VkStructureType::InstanceCreateInfo,
                pNext: if validation {
                    (&raw const messenger_info).cast()
                } else {
                    null()
                },
                flags: VkInstanceCreateFlags::empty(),
                pApplicationInfo: &app,
                enabledLayerCount: validation as u32,
                ppEnabledLayerNames: layers.as_ptr(),
                enabledExtensionCount: validation as u32,
                ppEnabledExtensionNames: extensions.as_ptr(),
            };
            let mut instance_handle = null_mut();
            check(loader.vkCreateInstance(&info, null(), &mut instance_handle));
            let instance = loader.load_instance(instance_handle).unwrap();
            let mut messenger = 0;
            if validation {
                check(instance.vkCreateDebugUtilsMessengerEXT(
                    instance_handle,
                    &messenger_info,
                    null(),
                    &mut messenger,
                ));
            }

            let mut count = 0;
            check(instance.vkEnumeratePhysicalDevices(instance_handle, &mut count, null_mut()));
            let mut devices = vec![null_mut(); count as usize];
            check(instance.vkEnumeratePhysicalDevices(
                instance_handle,
                &mut count,
                devices.as_mut_ptr(),
            ));
            let physical_device = *devices
                .iter()
                .find(|&&pd| has_extension(&instance, pd, c"VK_EXT_descriptor_heap"))
                .expect("a device with VK_EXT_descriptor_heap");

            let mut count = 0;
            instance.vkGetPhysicalDeviceQueueFamilyProperties(
                physical_device,
                &mut count,
                null_mut(),
            );
            let mut families = vec![core::mem::zeroed::<VkQueueFamilyProperties>(); count as usize];
            instance.vkGetPhysicalDeviceQueueFamilyProperties(
                physical_device,
                &mut count,
                families.as_mut_ptr(),
            );
            let queue_family = families
                .iter()
                .position(|f| {
                    f.queueFlags
                        .contains(VkQueueFlags::GRAPHICS | VkQueueFlags::COMPUTE)
                })
                .unwrap() as u32;

            let mut heap_features: VkPhysicalDeviceDescriptorHeapFeaturesEXT = core::mem::zeroed();
            heap_features.sType = VkStructureType::PhysicalDeviceDescriptorHeapFeaturesExt;
            heap_features.descriptorHeap = VK_TRUE;
            let mut features14: VkPhysicalDeviceVulkan14Features = core::mem::zeroed();
            features14.sType = VkStructureType::PhysicalDeviceVulkan14Features;
            features14.pNext = (&raw mut heap_features).cast();
            features14.maintenance5 = VK_TRUE;
            let mut features12: VkPhysicalDeviceVulkan12Features = core::mem::zeroed();
            features12.sType = VkStructureType::PhysicalDeviceVulkan12Features;
            features12.pNext = (&raw mut features14).cast();
            features12.bufferDeviceAddress = VK_TRUE;

            let priority = 1.0f32;
            let queue_info = VkDeviceQueueCreateInfo {
                sType: VkStructureType::DeviceQueueCreateInfo,
                pNext: null(),
                flags: VkDeviceQueueCreateFlags::empty(),
                queueFamilyIndex: queue_family,
                queueCount: 1,
                pQueuePriorities: &priority,
            };
            let mut device_extensions: Vec<*const c_char> =
                vec![c"VK_EXT_descriptor_heap".as_ptr()];
            if has_extension(&instance, physical_device, c"VK_EXT_memory_budget") {
                device_extensions.push(c"VK_EXT_memory_budget".as_ptr());
            }
            let device_info = VkDeviceCreateInfo {
                sType: VkStructureType::DeviceCreateInfo,
                pNext: (&raw const features12).cast(),
                flags: VkDeviceCreateFlags::empty(),
                queueCreateInfoCount: 1,
                pQueueCreateInfos: &queue_info,
                enabledLayerCount: 0,
                ppEnabledLayerNames: null(),
                enabledExtensionCount: device_extensions.len() as u32,
                ppEnabledExtensionNames: device_extensions.as_ptr(),
                pEnabledFeatures: null(),
            };
            let mut device_handle = null_mut();
            check(instance.vkCreateDevice(
                physical_device,
                &device_info,
                null(),
                &mut device_handle,
            ));
            let device = instance.load_device(device_handle).unwrap();

            let mut queue = null_mut();
            device.vkGetDeviceQueue(device_handle, queue_family, 0, &mut queue);
            let pool_info = VkCommandPoolCreateInfo {
                sType: VkStructureType::CommandPoolCreateInfo,
                pNext: null(),
                flags: VkCommandPoolCreateFlags::RESET_COMMAND_BUFFER,
                queueFamilyIndex: queue_family,
            };
            let mut command_pool = 0;
            check(device.vkCreateCommandPool(device_handle, &pool_info, null(), &mut command_pool));

            Self {
                loader,
                instance,
                instance_handle,
                messenger,
                physical_device,
                device,
                device_handle,
                queue,
                queue_family,
                command_pool,
                validation,
            }
        }
    }

    pub fn allocator(&self) -> Allocator {
        let config = AllocatorConfig {
            memory_budget: unsafe {
                has_extension(
                    &self.instance,
                    self.physical_device,
                    c"VK_EXT_memory_budget",
                )
            },
            ..Default::default()
        };
        unsafe {
            Allocator::new(
                &self.instance,
                self.physical_device,
                &self.device,
                self.device_handle,
                &config,
            )
            .unwrap()
        }
    }

    /// Records commands into a one-shot command buffer, submits them and waits.
    pub fn submit(&self, record: impl FnOnce(VkCommandBuffer)) {
        unsafe {
            let info = VkCommandBufferAllocateInfo {
                sType: VkStructureType::CommandBufferAllocateInfo,
                pNext: null(),
                commandPool: self.command_pool,
                level: VkCommandBufferLevel::Primary,
                commandBufferCount: 1,
            };
            let mut cmd = null_mut();
            check(
                self.device
                    .vkAllocateCommandBuffers(self.device_handle, &info, &mut cmd),
            );
            let begin = VkCommandBufferBeginInfo {
                sType: VkStructureType::CommandBufferBeginInfo,
                pNext: null(),
                flags: VkCommandBufferUsageFlags::ONE_TIME_SUBMIT,
                pInheritanceInfo: null(),
            };
            check(self.device.vkBeginCommandBuffer(cmd, &begin));
            record(cmd);
            check(self.device.vkEndCommandBuffer(cmd));
            let submit = VkSubmitInfo {
                sType: VkStructureType::SubmitInfo,
                pNext: null(),
                waitSemaphoreCount: 0,
                pWaitSemaphores: null(),
                pWaitDstStageMask: null(),
                commandBufferCount: 1,
                pCommandBuffers: &cmd,
                signalSemaphoreCount: 0,
                pSignalSemaphores: null(),
            };
            check(self.device.vkQueueSubmit(self.queue, 1, &submit, 0));
            check(self.device.vkQueueWaitIdle(self.queue));
            self.device
                .vkFreeCommandBuffers(self.device_handle, self.command_pool, 1, &cmd);
        }
    }
}

impl Drop for Context {
    fn drop(&mut self) {
        unsafe {
            self.device
                .vkDestroyCommandPool(self.device_handle, self.command_pool, null());
            self.device.vkDestroyDevice(self.device_handle, null());
            if self.messenger != 0 {
                self.instance.vkDestroyDebugUtilsMessengerEXT(
                    self.instance_handle,
                    self.messenger,
                    null(),
                );
            }
            self.instance
                .vkDestroyInstance(self.instance_handle, null());
        }
    }
}

unsafe fn has_extension(
    instance: &InstanceCommands,
    physical_device: VkPhysicalDevice,
    name: &CStr,
) -> bool {
    unsafe {
        let mut count = 0;
        check(instance.vkEnumerateDeviceExtensionProperties(
            physical_device,
            null(),
            &mut count,
            null_mut(),
        ));
        let mut extensions = vec![core::mem::zeroed::<VkExtensionProperties>(); count as usize];
        check(instance.vkEnumerateDeviceExtensionProperties(
            physical_device,
            null(),
            &mut count,
            extensions.as_mut_ptr(),
        ));
        extensions
            .iter()
            .any(|e| CStr::from_ptr(e.extensionName.as_ptr()) == name)
    }
}