byte-engine-ghi 0.1.0

Graphics hardware interface layer used by Byte-Engine.
use objc2_metal::MTLDevice;

use super::*;

pub struct Instance {
	devices: Vec<Retained<ProtocolObject<dyn mtl::MTLDevice>>>,
	settings: crate::device::Features,
}

unsafe impl Send for Instance {}

impl Instance {
	pub fn new(settings: crate::device::Features) -> Result<Instance, &'static str> {
		let devices = mtl::MTLCopyAllDevices().to_vec();

		if devices.is_empty() {
			return Err("No Metal devices available. The most likely cause is that the system does not support Metal.");
		}

		Ok(Instance { devices, settings })
	}

	pub fn create_device(
		&mut self,
		settings: crate::device::Features,
		queues: &mut [(
			graphics_hardware_interface::QueueSelection,
			&mut Option<graphics_hardware_interface::QueueHandle>,
		)],
	) -> Result<super::Device, &'static str> {
		let device = if let Some(preferred_name) = settings.gpu {
			let selected = self.devices.iter().find(|device| device.name().to_string() == preferred_name);

			match selected {
				Some(device) => device.clone(),
				None => {
					return Err(
						"Requested Metal device not found. The most likely cause is that the device name does not match any available GPU.",
					);
				}
			}
		} else {
			mtl::MTLCreateSystemDefaultDevice()
				.or_else(|| self.devices.first().cloned())
				.ok_or("Metal device creation failed. The most likely cause is that no compatible Metal device is available.")?
		};

		let merged_settings = crate::device::Features {
			validation: settings.validation || self.settings.validation,
			gpu_validation: settings.gpu_validation || self.settings.gpu_validation,
			api_dump: settings.api_dump || self.settings.api_dump,
			ray_tracing: settings.ray_tracing || self.settings.ray_tracing,
			debug_labels: settings.debug_labels || self.settings.debug_labels,
			debug_log_function: settings.debug_log_function.or(self.settings.debug_log_function),
			gpu: settings.gpu.or(self.settings.gpu),
			sparse: settings.sparse || self.settings.sparse,
			geometry_shader: settings.geometry_shader || self.settings.geometry_shader,
			mesh_shading: settings.mesh_shading || self.settings.mesh_shading,
		};

		super::Device::new(merged_settings, device, queues)
	}
}