Repository: bevyengine/bevy
File: crates/bevy_render/src/renderer/raw_vulkan_init.rs (Rust)

Code before:
use bevy_ecs::resource::Resource;
use bevy_platform::collections::HashSet;
use std::{
    any::{Any, TypeId},
    sync::Arc,
};
use thiserror::Error;
use wgpu::{
    hal::api::Vulkan, Adapter, Device, DeviceDescriptor, Instance, InstanceDescriptor, Queue,
};

/// When the `raw_vulkan_init` feature is enabled, these settings will be used to configure the raw vulkan instance.
#[derive(Resource, Default, Clone)]
pub struct RawVulkanInitSettings {
    pub create_instance_callbacks: Vec<
        Arc<
            dyn Fn(
                    &mut wgpu::hal::vulkan::CreateInstanceCallbackArgs,
                    &mut AdditionalVulkanFeatures,
                ) + Send
                + Sync,
        >,
    >,
    pub create_device_callbacks: Vec<
        Arc<
            dyn Fn(
                    &mut wgpu::hal::vulkan::CreateDeviceCallbackArgs,
                    &wgpu::hal::vulkan::Adapter,
                    &mut AdditionalVulkanFeatures,
                ) + Send
                + Sync,
        >,
    >,
}

pub(crate) fn create_raw_vulkan_instance(
    instance_descriptor: &InstanceDescriptor,
    settings: &RawVulkanInitSettings,
    additional_features: &mut AdditionalVulkanFeatures,
) -> Instance {
    unsafe {
        let instance = wgpu::hal::vulkan::Instance::init_with_callback(
            &wgpu::hal::InstanceDescriptor {
                name: "wgpu",
                flags: instance_descriptor.flags,
                memory_budget_thresholds: instance_descriptor.memory_budget_thresholds,
                backend_options: instance_descriptor.backend_options.clone(),
            },
            Some(Box::new(|mut args| {
                for callback in &settings.create_instance_callbacks {
                    (callback)(&mut args, additional_features);
                }
            })),
        )
        .map(|raw_instance| Instance::from_hal::<Vulkan>(raw_instance))
        .unwrap_or_else(|_| Instance::new(instance_descriptor));
        instance
    }
}

pub(crate) async fn create_raw_device(
    adapter: &Adapter,
    device_descriptor: &DeviceDescriptor<'_>,
    settings: &RawVulkanInitSettings,
    additional_features: &mut AdditionalVulkanFeatures,
) -> Result<(Device, Queue), CreateRawVulkanDeviceError> {
    unsafe {
        let Some(raw_adapter) = adapter.as_hal::<Vulkan>() else {
            return Ok(adapter.request_device(device_descriptor).await?);
        };
        let open_device = raw_adapter.open_with_callback(
            device_descriptor.required_features,
            &device_descriptor.memory_hints,
            Some(Box::new(|mut args| {
                for callback in &settings.create_device_callbacks {
                    (callback)(&mut args, &*raw_adapter, additional_features);
                }
            })),
        )?;

        Ok(adapter.create_device_from_hal::<Vulkan>(open_device, device_descriptor)?)
    }
}

#[derive(Error, Debug)]
pub(crate) enum CreateRawVulkanDeviceError {
    #[error("Could not create a raw Vulkan device because the Vulkan backend is not supported")]
    UnsupportedBackend,
    #[error(transparent)]
    RequestDeviceError(#[from] wgpu::RequestDeviceError),
    #[error(transparent)]
    DeviceError(#[from] wgpu::hal::DeviceError),
}

/// A list of additional Vulkan features that are supported by the current wgpu instance / adapter. This is populated
/// by callbacks defined in [`RawVulkanInitSettings`]
#[derive(Resource, Default, Clone)]
pub struct AdditionalVulkanFeatures(HashSet<TypeId>);

impl AdditionalVulkanFeatures {
    pub fn register<T: Any>(&mut self) {
        self.0.insert(TypeId::of::<T>());
    }

    pub fn has<T: Any>(&self) -> bool {
        self.0.contains(&TypeId::of::<T>())
    }
}


Review comment (suggestion):
I don't know that AdditionalVulkanFeatures makes sense to me. Why not just give access to &mut World in the callbacks, and let users insert a marker ~~component~~* directly? I feel that this is cleaner.

*Resource

Code after:
            &device_descriptor.memory_hints,
            Some(Box::new(|mut args| {
                for callback in &settings.create_device_callbacks {
                    (callback)(&mut args, &raw_adapter, additional_features);
                }
            })),
        )?;

        Ok(adapter.create_device_from_hal::<Vulkan>(open_device, device_descriptor)?)
    }
}

#[derive(Error, Debug)]
pub(crate) enum CreateRawVulkanDeviceError {
    #[error("Could not create a raw Vulkan device because the Vulkan backend is not supported")]
    UnsupportedBackend,
    #[error(transparent)]
    RequestDeviceError(#[from] wgpu::RequestDeviceError),
    #[error(transparent)]
    DeviceError(#[from] wgpu::hal::DeviceError),
}

/// A list of additional Vulkan features that are supported by the current wgpu instance / adapter. This is populated
/// by callbacks defined in [`RawVulkanInitSettings`]
#[derive(Resource, Default, Clone)]
pub struct AdditionalVulkanFeatures(HashSet<TypeId>);

impl AdditionalVulkanFeatures {
    pub fn register<T: Any>(&mut self) {
        self.0.insert(TypeId::of::<T>());
    }

    pub fn has<T: Any>(&self) -> bool {
        self.0.contains(&TypeId::of::<T>())
    }
}