use super::*;
use crate::types::*;
use anyhow::{Context, Result};
use std::collections::HashMap;
pub struct MetalBackend {
devices: HashMap<DeviceHandle, MetalDevice>,
next_device_handle: DeviceHandle,
buffers: HashMap<BufferHandle, MetalBuffer>,
next_buffer_handle: BufferHandle,
shaders: HashMap<ShaderHandle, MetalShader>,
next_shader_handle: ShaderHandle,
pipelines: HashMap<PipelineHandle, MetalPipeline>,
next_pipeline_handle: PipelineHandle,
bind_group_layouts: HashMap<BindGroupLayoutHandle, MetalBindGroupLayout>,
next_bind_group_layout_handle: BindGroupLayoutHandle,
bind_groups: HashMap<BindGroupHandle, MetalBindGroup>,
next_bind_group_handle: BindGroupHandle,
render_targets: HashMap<RenderTargetHandle, MetalRenderTarget>,
next_render_target_handle: RenderTargetHandle,
surfaces: HashMap<SurfaceHandle, MetalSurface>,
next_surface_handle: SurfaceHandle,
}
struct MetalDevice {
adapter_id: u32,
}
struct MetalBuffer {
device_handle: DeviceHandle,
size: u64,
}
struct MetalShader {
device_handle: DeviceHandle,
source: String,
}
struct MetalPipeline {
device_handle: DeviceHandle,
}
struct MetalBindGroupLayout {
device_handle: DeviceHandle,
}
struct MetalBindGroup {
device_handle: DeviceHandle,
}
struct MetalRenderTarget {
device_handle: DeviceHandle,
width: u32,
height: u32,
format: TextureFormat,
has_rendered: bool,
}
struct MetalSurface {
device_handle: DeviceHandle,
width: u32,
height: u32,
}
impl MetalBackend {
pub fn new() -> Result<Self> {
tracing::info!("Initializing Metal backend");
Ok(Self {
devices: HashMap::new(),
next_device_handle: 1,
buffers: HashMap::new(),
next_buffer_handle: 1,
shaders: HashMap::new(),
next_shader_handle: 1,
pipelines: HashMap::new(),
next_pipeline_handle: 1,
bind_group_layouts: HashMap::new(),
next_bind_group_layout_handle: 1,
bind_groups: HashMap::new(),
next_bind_group_handle: 1,
render_targets: HashMap::new(),
next_render_target_handle: 1,
surfaces: HashMap::new(),
next_surface_handle: 1,
})
}
}
impl GpuBackend for MetalBackend {
fn backend_type(&self) -> BackendType {
BackendType::Metal
}
fn enumerate_adapters(&self) -> Vec<AdapterInfo> {
vec![
AdapterInfo {
id: 0,
name: "Metal GPU".to_string(),
vendor: "Apple".to_string(),
backend: BackendType::Metal,
device_type: DeviceType::IntegratedGpu,
}
]
}
fn create_device(&mut self, adapter_id: u32) -> Result<DeviceHandle> {
let handle = self.next_device_handle;
self.next_device_handle += 1;
self.devices.insert(handle, MetalDevice { adapter_id });
Ok(handle)
}
fn destroy_device(&mut self, device: DeviceHandle) {
self.devices.remove(&device);
self.buffers.retain(|_, b| b.device_handle != device);
self.shaders.retain(|_, s| s.device_handle != device);
self.pipelines.retain(|_, p| p.device_handle != device);
self.render_targets.retain(|_, t| t.device_handle != device);
self.surfaces.retain(|_, s| s.device_handle != device);
}
fn is_device_valid(&self, device: DeviceHandle) -> bool {
self.devices.contains_key(&device)
}
fn create_buffer(&mut self, device: DeviceHandle, size: u64, _usage: BufferUsage) -> Result<BufferHandle> {
if !self.devices.contains_key(&device) {
anyhow::bail!("Invalid device handle");
}
let handle = self.next_buffer_handle;
self.next_buffer_handle += 1;
self.buffers.insert(handle, MetalBuffer {
device_handle: device,
size,
});
Ok(handle)
}
fn destroy_buffer(&mut self, buffer: BufferHandle) {
self.buffers.remove(&buffer);
}
fn write_buffer(&mut self, buffer: BufferHandle, _offset: u64, _data: &[u8]) -> Result<()> {
if !self.buffers.contains_key(&buffer) {
anyhow::bail!("Invalid buffer handle");
}
Ok(())
}
fn buffer_size(&self, buffer: BufferHandle) -> u64 {
self.buffers.get(&buffer).map(|b| b.size).unwrap_or(0)
}
fn create_shader(&mut self, device: DeviceHandle, slang_source: &str) -> Result<ShaderHandle> {
self.create_shader_with_paths(device, slang_source, &[])
}
fn create_shader_with_paths(&mut self, device: DeviceHandle, slang_source: &str, _search_paths: &[&str]) -> Result<ShaderHandle> {
if !self.devices.contains_key(&device) {
anyhow::bail!("Invalid device handle");
}
let handle = self.next_shader_handle;
self.next_shader_handle += 1;
self.shaders.insert(handle, MetalShader {
device_handle: device,
source: slang_source.to_string(),
});
Ok(handle)
}
fn destroy_shader(&mut self, shader: ShaderHandle) {
self.shaders.remove(&shader);
}
fn create_bind_group_layout(&mut self, device: DeviceHandle, _entries: &[BindGroupLayoutEntry]) -> Result<BindGroupLayoutHandle> {
if !self.devices.contains_key(&device) {
anyhow::bail!("Invalid device handle");
}
let handle = self.next_bind_group_layout_handle;
self.next_bind_group_layout_handle += 1;
self.bind_group_layouts.insert(handle, MetalBindGroupLayout {
device_handle: device,
});
Ok(handle)
}
fn create_bind_group(&mut self, device: DeviceHandle, _layout: BindGroupLayoutHandle, _entries: &[BindGroupEntry]) -> Result<BindGroupHandle> {
if !self.devices.contains_key(&device) {
anyhow::bail!("Invalid device handle");
}
let handle = self.next_bind_group_handle;
self.next_bind_group_handle += 1;
self.bind_groups.insert(handle, MetalBindGroup {
device_handle: device,
});
Ok(handle)
}
fn destroy_bind_group(&mut self, bind_group: BindGroupHandle) {
self.bind_groups.remove(&bind_group);
}
fn create_pipeline(
&mut self,
device: DeviceHandle,
_vertex_shader: ShaderHandle,
_fragment_shader: ShaderHandle,
_vertex_layout: &VertexBufferLayout,
_topology: PrimitiveTopology,
_target_format: TextureFormat,
) -> Result<PipelineHandle> {
if !self.devices.contains_key(&device) {
anyhow::bail!("Invalid device handle");
}
let handle = self.next_pipeline_handle;
self.next_pipeline_handle += 1;
self.pipelines.insert(handle, MetalPipeline {
device_handle: device,
});
Ok(handle)
}
fn create_pipeline_with_layout(
&mut self,
device: DeviceHandle,
vertex_shader: ShaderHandle,
fragment_shader: ShaderHandle,
vertex_layout: &VertexBufferLayout,
topology: PrimitiveTopology,
target_format: TextureFormat,
_bind_group_layouts: &[BindGroupLayoutHandle],
) -> Result<PipelineHandle> {
self.create_pipeline(device, vertex_shader, fragment_shader, vertex_layout, topology, target_format)
}
fn destroy_pipeline(&mut self, pipeline: PipelineHandle) {
self.pipelines.remove(&pipeline);
}
fn create_render_target(&mut self, device: DeviceHandle, width: u32, height: u32, format: TextureFormat) -> Result<RenderTargetHandle> {
if !self.devices.contains_key(&device) {
anyhow::bail!("Invalid device handle");
}
let handle = self.next_render_target_handle;
self.next_render_target_handle += 1;
self.render_targets.insert(handle, MetalRenderTarget {
device_handle: device,
width,
height,
format,
has_rendered: false,
});
Ok(handle)
}
fn destroy_render_target(&mut self, target: RenderTargetHandle) {
self.render_targets.remove(&target);
}
fn render_to_target(&mut self, device: DeviceHandle, target: RenderTargetHandle, _commands: &[RenderCommand]) -> Result<()> {
if !self.devices.contains_key(&device) {
anyhow::bail!("Invalid device handle");
}
let render_target = self.render_targets.get_mut(&target)
.context("Invalid render target handle")?;
render_target.has_rendered = true;
Ok(())
}
fn read_target_to_cpu(&mut self, target: RenderTargetHandle, output: &mut [u8]) -> Result<()> {
let render_target = self.render_targets.get(&target)
.context("Invalid render target handle")?;
if !render_target.has_rendered {
anyhow::bail!("Cannot read from render target that hasn't been rendered to");
}
for byte in output.iter_mut() {
*byte = 128;
}
Ok(())
}
fn create_surface(
&mut self,
device: DeviceHandle,
_window: &dyn raw_window_handle::HasWindowHandle,
_display: &dyn raw_window_handle::HasDisplayHandle,
) -> Result<SurfaceHandle> {
if !self.devices.contains_key(&device) {
anyhow::bail!("Invalid device handle");
}
let handle = self.next_surface_handle;
self.next_surface_handle += 1;
self.surfaces.insert(handle, MetalSurface {
device_handle: device,
width: 800,
height: 600,
});
tracing::info!("Created Metal surface {}", handle);
Ok(handle)
}
fn destroy_surface(&mut self, surface: SurfaceHandle) {
self.surfaces.remove(&surface);
}
fn surface_acquire(&mut self, surface: SurfaceHandle) -> Result<SwapchainImageHandle> {
if !self.surfaces.contains_key(&surface) {
anyhow::bail!("Invalid surface handle");
}
Ok(1) }
fn surface_render(&mut self, surface: SurfaceHandle, _image: SwapchainImageHandle, _commands: &[RenderCommand]) -> Result<()> {
if !self.surfaces.contains_key(&surface) {
anyhow::bail!("Invalid surface handle");
}
Ok(())
}
fn surface_present(&mut self, surface: SurfaceHandle, _image: SwapchainImageHandle) -> Result<()> {
if !self.surfaces.contains_key(&surface) {
anyhow::bail!("Invalid surface handle");
}
Ok(())
}
fn surface_resize(&mut self, surface: SurfaceHandle, width: u32, height: u32) -> Result<()> {
let surf = self.surfaces.get_mut(&surface)
.context("Invalid surface handle")?;
surf.width = width;
surf.height = height;
Ok(())
}
fn surface_size(&self, surface: SurfaceHandle) -> (u32, u32) {
self.surfaces.get(&surface)
.map(|s| (s.width, s.height))
.unwrap_or((0, 0))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_metal_backend_creation() {
let backend = MetalBackend::new().unwrap();
assert_eq!(backend.backend_type(), BackendType::Metal);
}
#[test]
fn test_metal_adapters() {
let backend = MetalBackend::new().unwrap();
let adapters = backend.enumerate_adapters();
assert!(!adapters.is_empty());
}
}