mev 0.1.1

Metal Et Vulkan abstraction
Documentation
use std::{
    hash::{Hash, Hasher},
    ops::Mul,
};

use foreign_types::ForeignType;
use metal::MTLTextureType;

use crate::{
    generic::{
        ArgumentKind, Automatic, ComponentSwizzle, Extent1, Extent2, Extent3, ImageExtent,
        OutOfMemory, PixelFormat, Sampled, Storage, Swizzle, ViewDesc,
    },
    ImageUsage,
};

use super::{
    arguments::ArgumentsField,
    from::{MetalInto, TryIntoMetal, TryMetalInto},
    Device,
};

#[derive(Clone, Debug)]
pub struct Image {
    texture: metal::Texture,
}

impl PartialEq for Image {
    fn eq(&self, other: &Self) -> bool {
        self.texture.as_ptr() == other.texture.as_ptr()
    }
}

impl Eq for Image {}

impl Hash for Image {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.texture.as_ptr().hash(state);
    }
}

unsafe impl Send for Image {}
unsafe impl Sync for Image {}

impl Image {
    pub(super) fn new(texture: metal::Texture) -> Self {
        Image { texture }
    }

    pub(super) fn metal(&self) -> &metal::TextureRef {
        &self.texture
    }
}

impl crate::traits::Resource for Image {}

#[hidden_trait::expose]
impl crate::traits::Image for Image {
    fn format(&self) -> PixelFormat {
        self.texture.pixel_format().expect_metal_into()
    }

    fn extent(&self) -> ImageExtent {
        match self.texture.texture_type() {
            MTLTextureType::D1 | MTLTextureType::D1Array => {
                let width = self.texture.width();
                ImageExtent::D1(Extent1::new(width as u32))
            }
            MTLTextureType::D2 | MTLTextureType::D2Array => {
                let width = self.texture.width();
                let height = self.texture.height();
                ImageExtent::D2(Extent2::new(width as u32, height as u32))
            }
            MTLTextureType::D2Multisample => unimplemented!(),
            MTLTextureType::D2MultisampleArray => unimplemented!(),
            MTLTextureType::Cube => unimplemented!(),
            MTLTextureType::CubeArray => unimplemented!(),
            MTLTextureType::D3 => {
                let width = self.texture.width();
                let height = self.texture.height();
                let depth = self.texture.depth();
                ImageExtent::D3(Extent3::new(width as u32, height as u32, depth as u32))
            }
        }
    }

    fn layers(&self) -> u32 {
        self.texture.array_length() as u32
    }

    fn levels(&self) -> u32 {
        self.texture.mipmap_level_count() as u32
    }

    fn usage(&self) -> ImageUsage {
        self.texture.usage().metal_into()
    }

    fn view(&self, _device: &Device, desc: ViewDesc) -> Image {
        use foreign_types::{ForeignType, ForeignTypeRef};
        use objc::*;

        let pixel_format = desc.format.expect_into_metal();
        let root_texture = self.texture.parent_texture().unwrap_or(&self.texture);

        if desc.swizzle == Swizzle::IDENTITY {
            if desc.base_layer == 0 && desc.base_level == 0 {
                let texture = root_texture.new_texture_view(desc.format.expect_into_metal());
                Image { texture }
            } else {
                let base_layer = self.texture.parent_relative_slice() as u32 + desc.base_layer;
                let base_level = self.texture.mipmap_level_count() as u32 + desc.base_level;

                let texture = root_texture.new_texture_view_from_slice(
                    pixel_format,
                    self.texture.texture_type(),
                    metal::NSRange::new(base_level.into(), desc.levels.into()),
                    metal::NSRange::new(base_layer.into(), desc.layers.into()),
                );
                Image { texture }
            }
        } else {
            let base_layer = self.texture.parent_relative_slice() as u32 + desc.base_layer;
            let base_level = self.texture.parent_relative_level() as u32 + desc.base_level;
            let swizzle: MTLTextureSwizzleChannels =
                unsafe { msg_send![self.texture.as_ptr(), swizzle] };

            let new_swizzle = swizzle * desc.swizzle;

            let texture = unsafe {
                msg_send![root_texture.as_ptr(), newTextureViewWithPixelFormat:pixel_format
                                                textureType:self.texture.texture_type()
                                                levels:metal::NSRange::new(base_level.into(), desc.levels.into())
                                                slices:metal::NSRange::new(base_layer.into(), desc.layers.into())
                                                swizzle:new_swizzle

                ]
            };

            Image { texture }
        }
    }

    fn detached(&self) -> bool {
        use foreign_types::ForeignType;
        use metal::NSUInteger;
        use objc::*;

        let count: NSUInteger = unsafe { msg_send![(self.texture.as_ptr()), retainCount] };
        count == 1
    }
}

#[allow(dead_code)]
#[derive(Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
enum MTLTextureSwizzle {
    Zero = 0,
    One = 1,
    Red = 2,
    Green = 3,
    Blue = 4,
    Alpha = 5,
}

#[derive(Clone, Copy, PartialEq, Eq)]
#[repr(C)]
struct MTLTextureSwizzleChannels {
    r: MTLTextureSwizzle,
    g: MTLTextureSwizzle,
    b: MTLTextureSwizzle,
    a: MTLTextureSwizzle,
}

impl Mul<Swizzle> for MTLTextureSwizzleChannels {
    type Output = Self;

    fn mul(self, rhs: Swizzle) -> Self {
        use ComponentSwizzle::*;

        let mul = |rhs: ComponentSwizzle, i: MTLTextureSwizzle| -> MTLTextureSwizzle {
            match rhs {
                Identity => i,
                Zero => MTLTextureSwizzle::Zero,
                One => MTLTextureSwizzle::One,
                R => self.r,
                G => self.g,
                B => self.b,
                A => self.a,
            }
        };

        let r = mul(rhs.r, self.r);
        let g = mul(rhs.g, self.g);
        let b = mul(rhs.b, self.b);
        let a = mul(rhs.a, self.a);

        MTLTextureSwizzleChannels { r, g, b, a }
    }
}

impl ArgumentsField<Automatic> for Image {
    const KIND: ArgumentKind = ArgumentKind::SampledImage;
    const SIZE: usize = 1;

    #[inline(always)]
    fn bind_vertex(&self, slot: u32, encoder: &metal::RenderCommandEncoderRef) {
        encoder.set_vertex_texture(slot.into(), Some(&self.texture));
    }

    #[inline(always)]
    fn bind_fragment(&self, slot: u32, encoder: &metal::RenderCommandEncoderRef) {
        encoder.set_fragment_texture(slot.into(), Some(&self.texture));
    }

    #[inline(always)]
    fn bind_compute(&self, slot: u32, encoder: &metal::ComputeCommandEncoderRef) {
        encoder.set_texture(slot.into(), Some(&self.texture));
    }
}

impl ArgumentsField<Sampled> for Image {
    const KIND: ArgumentKind = ArgumentKind::SampledImage;
    const SIZE: usize = 1;

    #[inline(always)]
    fn bind_vertex(&self, slot: u32, encoder: &metal::RenderCommandEncoderRef) {
        encoder.set_vertex_texture(slot.into(), Some(&self.texture));
    }

    #[inline(always)]
    fn bind_fragment(&self, slot: u32, encoder: &metal::RenderCommandEncoderRef) {
        encoder.set_fragment_texture(slot.into(), Some(&self.texture));
    }

    #[inline(always)]
    fn bind_compute(&self, slot: u32, encoder: &metal::ComputeCommandEncoderRef) {
        encoder.set_texture(slot.into(), Some(&self.texture));
    }
}

impl ArgumentsField<Storage> for Image {
    const KIND: ArgumentKind = ArgumentKind::StorageImage;
    const SIZE: usize = 1;

    #[inline(always)]
    fn bind_vertex(&self, slot: u32, encoder: &metal::RenderCommandEncoderRef) {
        encoder.set_vertex_texture(slot.into(), Some(&self.texture));
    }

    #[inline(always)]
    fn bind_fragment(&self, slot: u32, encoder: &metal::RenderCommandEncoderRef) {
        encoder.set_fragment_texture(slot.into(), Some(&self.texture));
    }

    #[inline(always)]
    fn bind_compute(&self, slot: u32, encoder: &metal::ComputeCommandEncoderRef) {
        encoder.set_texture(slot.into(), Some(&self.texture));
    }
}