<commit_before>// Copyright 2016 The Gfx-rs Developers.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::os::raw::c_void;
use std::sync::Arc;
use std::slice;
use std::mem;
use std::str;

use cocoa::base::{selector, class};
use cocoa::foundation::{NSUInteger};

use gfx_core as core;
use gfx_core::{factory, handle};
use gfx_core::handle::Producer;
use gfx_core::handle::Manager;

use metal::*;

use command::CommandBuffer;

use {Resources, Share, Texture, Buffer, Shader, Program, Pipeline};
use native;
use mirror;

#[derive(Copy, Clone)]
pub struct RawMapping {
    pointer: *mut c_void,
}

impl core::mapping::Raw for RawMapping {
    unsafe fn set<T>(&self, index: usize, val: T) {
        *(self.pointer as *mut T).offset(index as isize) = val;
    }

    unsafe fn to_slice<T>(&self, len: usize) -> &[T] {
        slice::from_raw_parts(self.pointer as *const T, len)
    }

    unsafe fn to_mut_slice<T>(&self, len: usize) -> &mut [T] {
        slice::from_raw_parts_mut(self.pointer as *mut T, len)
    }
}

pub struct Factory {
    drawable: *mut CAMetalDrawable,
    device: MTLDevice,
    queue: MTLCommandQueue,
    share: Arc<Share>,
    frame_handles: handle::Manager<Resources>,
}

impl Factory {
    pub fn new(device: MTLDevice, share: Arc<Share>, drawable: *mut CAMetalDrawable) -> Factory {
        Factory {
            drawable: drawable,
            device: device,
            queue: device.new_command_queue(),
            share: share,
            frame_handles: handle::Manager::new(),
        }
    }

    pub fn create_command_buffer(&self) -> CommandBuffer {
        CommandBuffer::new(self.queue, self.drawable)
    }

    fn create_buffer_internal(&self, info: factory::BufferInfo, raw_data: Option<*const c_void>)
            -> Result<handle::RawBuffer<Resources>, factory::BufferError> {
        use map::{map_buffer_usage};

        let usage = map_buffer_usage(info.usage);

        if info.bind.contains(factory::RENDER_TARGET) | info.bind.contains(factory::DEPTH_STENCIL) {
            return Err(factory::BufferError::UnsupportedBind(info.bind))
        }

        let mut raw_buf = if let Some(data) = raw_data {
            self.device.new_buffer_with_data(unsafe { mem::transmute(data) }, info.size as u64, usage)
        } else {
            self.device.new_buffer(info.size as u64, usage)
        };

        let buf = Buffer(native::Buffer(raw_buf), info.usage);
        Ok(self.share.handles.borrow_mut().make_buffer(buf, info))
    }

    pub fn make_depth_stencil(&self, info: &core::pso::DepthStencilInfo) -> MTLDepthStencilState {
        use map::{map_function, map_stencil_op};

        let desc = MTLDepthStencilDescriptor::alloc().init();
        desc.set_depth_write_enabled(info.depth.is_some());
        desc.set_depth_compare_function(match info.depth {
            Some(ref depth) => map_function(depth.fun),
            None => MTLCompareFunction::Never
        });

        if let Some(stencil) = info.front {
            let front = MTLStencilDescriptor::alloc().init();
            front.set_stencil_compare_function(map_function(stencil.fun));
            front.set_stencil_failure_operation(map_stencil_op(stencil.op_fail));
            front.set_depth_failure_operation(map_stencil_op(stencil.op_depth_fail));
            front.set_depth_stencil_pass_operation(map_stencil_op(stencil.op_pass));

            // TODO: wrong type?
            front.set_read_mask(stencil.mask_read as u32);
            front.set_write_mask(stencil.mask_write as u32);

            desc.set_front_face_stencil(front);
        };

        if let Some(stencil) = info.back {
            let back = MTLStencilDescriptor::alloc().init();
            back.set_stencil_compare_function(map_function(stencil.fun));
            back.set_stencil_failure_operation(map_stencil_op(stencil.op_fail));
            back.set_depth_failure_operation(map_stencil_op(stencil.op_depth_fail));
            back.set_depth_stencil_pass_operation(map_stencil_op(stencil.op_pass));

            // TODO: wrong type?
            back.set_read_mask(stencil.mask_read as u32);
            back.set_write_mask(stencil.mask_write as u32);

            desc.set_back_face_stencil(back);
        };

        self.device.new_depth_stencil_state(desc)
    }
}


impl core::Factory<Resources> for Factory {
    type Mapper = RawMapping;

    fn get_capabilities(&self) -> &core::Capabilities {
        &self.share.capabilities
    }

    fn create_buffer_raw(&mut self, info: factory::BufferInfo) -> Result<handle::RawBuffer<Resources>, factory::BufferError> {
        self.create_buffer_internal(info, None)
    }

    fn create_buffer_const_raw(&mut self, data: &[u8], stride: usize, role: factory::BufferRole, bind: factory::Bind)
                                -> Result<handle::RawBuffer<Resources>, factory::BufferError> {
        let info = factory::BufferInfo {
            role: role,
            usage: factory::Usage::Const,
            bind: bind,
            size: data.len(),
            stride: stride,
        };
        self.create_buffer_internal(info, Some(data.as_ptr() as *const c_void))
    }

    fn create_shader(&mut self, stage: core::shade::Stage, code: &[u8])
                     -> Result<handle::Shader<Resources>, core::shade::CreateShaderError> {
        use gfx_core::shade::{CreateShaderError, Stage};

        let lib = match stage {
            Stage::Vertex | Stage::Pixel => {
                let src = str::from_utf8(code).unwrap();
                match self.device.new_library_with_source(src, MTLCompileOptions::nil()) {
                    Ok(lib) => lib,
                    Err(err) => return Err(CreateShaderError::CompilationFailed(err.into()))
                }
            },
            _ => return Err(CreateShaderError::StageNotSupported(stage))
        };

        let shader = Shader {
            func: lib.get_function(match stage {
                Stage::Vertex => "vert",
                Stage::Pixel => "frag",
                _ => return Err(CreateShaderError::StageNotSupported(stage))
            })
        };

        Ok(self.share.handles.borrow_mut().make_shader(shader))
    }

    fn create_program(&mut self, shader_set: &core::ShaderSet<Resources>)
                      -> Result<handle::Program<Resources>, core::shade::CreateProgramError> {
        use gfx_core::shade::{ProgramInfo, Stage};

        let (prog, info) = match shader_set {
            &core::ShaderSet::Simple(ref vs, ref ps) => {
                let mut info = ProgramInfo {
                    vertex_attributes: Vec::new(),
                    globals: Vec::new(),
                    constant_buffers: Vec::new(),
                    textures: Vec::new(),
                    unordereds: Vec::new(),
                    samplers: Vec::new(),
                    outputs: Vec::new(),
                    output_depth: false,
                    knows_outputs: false,
                };

                let fh = &mut self.frame_handles;
                let (vs, ps) = (vs.reference(fh).func, ps.reference(fh).func);

                let mut reflection = MTLRenderPipelineReflection::nil();

                // since Metal doesn't allow for fetching shader reflection
                // without creating a PSO, we're creating a "fake" PSO to get
                // the reflection, and destroying it afterwards.
                //
                // Tracking: https://forums.developer.apple.com/thread/46535
                let pso_descriptor = MTLRenderPipelineDescriptor::alloc().init();
                pso_descriptor.set_vertex_function(vs);
                if !ps.is_null() {
                    pso_descriptor.set_fragment_function(ps);
                }
                pso_descriptor.color_attachments().object_at(0).set_pixel_format(MTLPixelFormat::BGRA8Unorm);

                // when we use `[[ stage_in ]]` we have to have a vertex
                // descriptor attached to the PSO descriptor
                //
                // dummy values are used for the attributes but the buffer
                // slot `30` is occupied by the dummy buffer
                //
                // TODO: prevent collision between dummy buffers and real
                //       values
                let vertex_desc = MTLVertexDescriptor::new();

                let buf = vertex_desc.layouts().object_at(30);
                buf.set_stride(16);
                buf.set_step_function(MTLVertexStepFunction::Constant);
                buf.set_step_rate(0);

                for i in 0..16 {
                    let attribute = vertex_desc.attributes().object_at(i as usize);
                    attribute.set_format(MTLVertexFormat::Char4);
                    attribute.set_offset(0);
                    attribute.set_buffer_index(30);
                }

                pso_descriptor.set_vertex_descriptor(vertex_desc);

                let pso = self.device.new_render_pipeline_state_with_reflection(pso_descriptor, &mut reflection).unwrap();

                // fill the `ProgramInfo` struct with goodies
                mirror::populate_vertex_attributes(&mut info, vs.vertex_attributes());

                mirror::populate_info(&mut info, Stage::Vertex, reflection.vertex_arguments());
                mirror::populate_info(&mut info, Stage::Pixel,  reflection.fragment_arguments());

                println!("{:?},\n{:?},\n{:?},\n{:?},\n{:?},\n", info.vertex_attributes, info.constant_buffers, info.textures, info.samplers, info.outputs);

                // destroy PSO & reflection object after we're done with
                // parsing reflection
                unsafe {
                    //pso.release();
                    //reflection.release();
                }

                // FIXME: retain functions?
                let program = Program {
                    vs: vs,
                    ps: ps
                };

                (program, info)
            },

            // Metal only supports vertex + fragment and has some features from
            // geometry shaders in vertex (layered rendering)
            //
            // Tracking: https://forums.developer.apple.com/message/9495
            _ => { return Err("Metal only supports vertex + fragment shader programs".into()); }
        };

        Ok(self.share.handles.borrow_mut().make_program(prog, info))
    }

    fn create_pipeline_state_raw(&mut self, program: &handle::Program<Resources>, desc: &core::pso::Descriptor)
                                 -> Result<handle::RawPipelineState<Resources>, core::pso::CreationError> {
        use map::{map_depth_surface, map_vertex_format, map_topology,
                  map_winding, map_cull, map_fill};

        let vertex_desc = MTLVertexDescriptor::new();

        println!("att: {:?}", desc.attributes);
        // TODO: implement instancing
        //
        // TODO: find a better way to set the buffer's stride, step func and
        //       step rate
        let buf = vertex_desc.layouts().object_at(0);
        buf.set_stride(desc.attributes[0].unwrap().0.stride as u64);
        buf.set_step_function(MTLVertexStepFunction::PerVertex);
        buf.set_step_rate(1);

        for (attr, attr_desc) in program.get_info().vertex_attributes.iter().zip(desc.attributes.iter()) {
            let (elem, irate) = match attr_desc {
                &Some((ref el, ir)) => (el, ir),
                &None => continue,
            };

            if elem.offset & 1 != 0 {
                error!("Vertex attribute {} must be aligned to 2 bytes, has offset {}",
                    attr.name, elem.offset);
                return Err(core::pso::CreationError);
            }

            // TODO: handle case when requested vertex format is invalid
            let attribute = vertex_desc.attributes().object_at(attr.slot as usize);
            attribute.set_format(map_vertex_format(elem.format).unwrap());
            attribute.set_offset(elem.offset as u64);
            attribute.set_buffer_index(0);
        }

        let prog = self.frame_handles.ref_program(program);

        let pso_descriptor = MTLRenderPipelineDescriptor::alloc().init();
        println!("{:?}", prog.ps);
        pso_descriptor.set_vertex_function(prog.vs);
        if !prog.ps.is_null() {
            pso_descriptor.set_fragment_function(prog.ps);
        }
        pso_descriptor.set_vertex_descriptor(vertex_desc);
        pso_descriptor.set_input_primitive_topology(map_topology(desc.primitive));
        if !prog.ps.is_null() {
            pso_descriptor.color_attachments().object_at(0).set_pixel_format(MTLPixelFormat::BGRA8Unorm);
        } else {
            pso_descriptor.color_attachments().object_at(0).set_pixel_format(MTLPixelFormat::Invalid);
        }

        if let Some(depth_desc) = desc.depth_stencil {
            // TODO: depthstencil
            println!("{:?}", depth_desc);
            // pso_descriptor.set_depth_attachment_pixel_format(MTLPixelFormat::Depth32Float);
            pso_descriptor.set_depth_attachment_pixel_format(map_depth_surface(depth_desc.0).unwrap());
        }

        let pso = self.device.new_render_pipeline_state(pso_descriptor).unwrap();

        let pso = Pipeline {
            pipeline: pso,
            depth_stencil: desc.depth_stencil.map(|desc| self.make_depth_stencil(&desc.1)),
            winding: map_winding(desc.rasterizer.front_face),
            cull: map_cull(desc.rasterizer.cull_face),
            fill: map_fill(desc.rasterizer.method)
        };

        Ok(self.share.handles.borrow_mut().make_pso(pso, program))
    }
<commit_msg>[metal] fixed PSO creation<commit_after>// Copyright 2016 The Gfx-rs Developers.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::os::raw::c_void;
use std::sync::Arc;
use std::slice;
use std::mem;
use std::str;

use cocoa::base::{selector, class};
use cocoa::foundation::{NSUInteger};

use gfx_core as core;
use gfx_core::{factory, handle};
use gfx_core::handle::Producer;
use gfx_core::handle::Manager;

use metal::*;

use command::CommandBuffer;

use {Resources, Share, Texture, Buffer, Shader, Program, Pipeline};
use native;
use mirror;

#[derive(Copy, Clone)]
pub struct RawMapping {
    pointer: *mut c_void,
}

impl core::mapping::Raw for RawMapping {
    unsafe fn set<T>(&self, index: usize, val: T) {
        *(self.pointer as *mut T).offset(index as isize) = val;
    }

    unsafe fn to_slice<T>(&self, len: usize) -> &[T] {
        slice::from_raw_parts(self.pointer as *const T, len)
    }

    unsafe fn to_mut_slice<T>(&self, len: usize) -> &mut [T] {
        slice::from_raw_parts_mut(self.pointer as *mut T, len)
    }
}

pub struct Factory {
    drawable: *mut CAMetalDrawable,
    device: MTLDevice,
    queue: MTLCommandQueue,
    share: Arc<Share>,
    frame_handles: handle::Manager<Resources>,
}

impl Factory {
    pub fn new(device: MTLDevice, share: Arc<Share>, drawable: *mut CAMetalDrawable) -> Factory {
        Factory {
            drawable: drawable,
            device: device,
            queue: device.new_command_queue(),
            share: share,
            frame_handles: handle::Manager::new(),
        }
    }

    pub fn create_command_buffer(&self) -> CommandBuffer {
        CommandBuffer::new(self.queue, self.drawable)
    }

    fn create_buffer_internal(&self, info: factory::BufferInfo, raw_data: Option<*const c_void>)
            -> Result<handle::RawBuffer<Resources>, factory::BufferError> {
        use map::{map_buffer_usage};

        let usage = map_buffer_usage(info.usage);

        if info.bind.contains(factory::RENDER_TARGET) | info.bind.contains(factory::DEPTH_STENCIL) {
            return Err(factory::BufferError::UnsupportedBind(info.bind))
        }

        let mut raw_buf = if let Some(data) = raw_data {
            self.device.new_buffer_with_data(unsafe { mem::transmute(data) }, info.size as u64, usage)
        } else {
            self.device.new_buffer(info.size as u64, usage)
        };

        let buf = Buffer(native::Buffer(raw_buf), info.usage);
        Ok(self.share.handles.borrow_mut().make_buffer(buf, info))
    }

    pub fn make_depth_stencil(&self, info: &core::pso::DepthStencilInfo) -> MTLDepthStencilState {
        use map::{map_function, map_stencil_op};

        let desc = MTLDepthStencilDescriptor::alloc().init();
        desc.set_depth_write_enabled(info.depth.is_some());
        desc.set_depth_compare_function(match info.depth {
            Some(ref depth) => map_function(depth.fun),
            None => MTLCompareFunction::Never
        });

        if let Some(stencil) = info.front {
            let front = MTLStencilDescriptor::alloc().init();
            front.set_stencil_compare_function(map_function(stencil.fun));
            front.set_stencil_failure_operation(map_stencil_op(stencil.op_fail));
            front.set_depth_failure_operation(map_stencil_op(stencil.op_depth_fail));
            front.set_depth_stencil_pass_operation(map_stencil_op(stencil.op_pass));

            // TODO: wrong type?
            front.set_read_mask(stencil.mask_read as u32);
            front.set_write_mask(stencil.mask_write as u32);

            desc.set_front_face_stencil(front);
        };

        if let Some(stencil) = info.back {
            let back = MTLStencilDescriptor::alloc().init();
            back.set_stencil_compare_function(map_function(stencil.fun));
            back.set_stencil_failure_operation(map_stencil_op(stencil.op_fail));
            back.set_depth_failure_operation(map_stencil_op(stencil.op_depth_fail));
            back.set_depth_stencil_pass_operation(map_stencil_op(stencil.op_pass));

            // TODO: wrong type?
            back.set_read_mask(stencil.mask_read as u32);
            back.set_write_mask(stencil.mask_write as u32);

            desc.set_back_face_stencil(back);
        };

        self.device.new_depth_stencil_state(desc)
    }
}


impl core::Factory<Resources> for Factory {
    type Mapper = RawMapping;

    fn get_capabilities(&self) -> &core::Capabilities {
        &self.share.capabilities
    }

    fn create_buffer_raw(&mut self, info: factory::BufferInfo) -> Result<handle::RawBuffer<Resources>, factory::BufferError> {
        self.create_buffer_internal(info, None)
    }

    fn create_buffer_const_raw(&mut self, data: &[u8], stride: usize, role: factory::BufferRole, bind: factory::Bind)
                                -> Result<handle::RawBuffer<Resources>, factory::BufferError> {
        let info = factory::BufferInfo {
            role: role,
            usage: factory::Usage::Const,
            bind: bind,
            size: data.len(),
            stride: stride,
        };
        self.create_buffer_internal(info, Some(data.as_ptr() as *const c_void))
    }

    fn create_shader(&mut self, stage: core::shade::Stage, code: &[u8])
                     -> Result<handle::Shader<Resources>, core::shade::CreateShaderError> {
        use gfx_core::shade::{CreateShaderError, Stage};

        let lib = match stage {
            Stage::Vertex | Stage::Pixel => {
                let src = str::from_utf8(code).unwrap();
                match self.device.new_library_with_source(src, MTLCompileOptions::nil()) {
                    Ok(lib) => lib,
                    Err(err) => return Err(CreateShaderError::CompilationFailed(err.into()))
                }
            },
            _ => return Err(CreateShaderError::StageNotSupported(stage))
        };

        let shader = Shader {
            func: lib.get_function(match stage {
                Stage::Vertex => "vert",
                Stage::Pixel => "frag",
                _ => return Err(CreateShaderError::StageNotSupported(stage))
            })
        };

        Ok(self.share.handles.borrow_mut().make_shader(shader))
    }

    fn create_program(&mut self, shader_set: &core::ShaderSet<Resources>)
                      -> Result<handle::Program<Resources>, core::shade::CreateProgramError> {
        use gfx_core::shade::{ProgramInfo, Stage};

        let (prog, info) = match shader_set {
            &core::ShaderSet::Simple(ref vs, ref ps) => {
                let mut info = ProgramInfo {
                    vertex_attributes: Vec::new(),
                    globals: Vec::new(),
                    constant_buffers: Vec::new(),
                    textures: Vec::new(),
                    unordereds: Vec::new(),
                    samplers: Vec::new(),
                    outputs: Vec::new(),
                    output_depth: false,
                    knows_outputs: false,
                };

                let fh = &mut self.frame_handles;
                let (vs, ps) = (vs.reference(fh).func, ps.reference(fh).func);

                let mut reflection = MTLRenderPipelineReflection::nil();

                // since Metal doesn't allow for fetching shader reflection
                // without creating a PSO, we're creating a "fake" PSO to get
                // the reflection, and destroying it afterwards.
                //
                // Tracking: https://forums.developer.apple.com/thread/46535
                let pso_descriptor = MTLRenderPipelineDescriptor::alloc().init();
                pso_descriptor.set_vertex_function(vs);
                if !ps.is_null() {
                    pso_descriptor.set_fragment_function(ps);
                }
                pso_descriptor.color_attachments().object_at(0).set_pixel_format(MTLPixelFormat::BGRA8Unorm);

                // when we use `[[ stage_in ]]` we have to have a vertex
                // descriptor attached to the PSO descriptor
                //
                // dummy values are used for the attributes but the buffer
                // slot `30` is occupied by the dummy buffer
                //
                // TODO: prevent collision between dummy buffers and real
                //       values
                let vertex_desc = MTLVertexDescriptor::new();

                let buf = vertex_desc.layouts().object_at(30);
                buf.set_stride(16);
                buf.set_step_function(MTLVertexStepFunction::Constant);
                buf.set_step_rate(0);

                for i in 0..16 {
                    let attribute = vertex_desc.attributes().object_at(i as usize);
                    attribute.set_format(MTLVertexFormat::Char4);
                    attribute.set_offset(0);
                    attribute.set_buffer_index(30);
                }

                pso_descriptor.set_vertex_descriptor(vertex_desc);

                let pso = self.device.new_render_pipeline_state_with_reflection(pso_descriptor, &mut reflection).unwrap();

                // fill the `ProgramInfo` struct with goodies
                mirror::populate_vertex_attributes(&mut info, vs.vertex_attributes());

                mirror::populate_info(&mut info, Stage::Vertex, reflection.vertex_arguments());
                mirror::populate_info(&mut info, Stage::Pixel,  reflection.fragment_arguments());

                println!("{:?},\n{:?},\n{:?},\n{:?},\n{:?},\n", info.vertex_attributes, info.constant_buffers, info.textures, info.samplers, info.outputs);

                // destroy PSO & reflection object after we're done with
                // parsing reflection
                unsafe {
                    //pso.release();
                    //reflection.release();
                }

                // FIXME: retain functions?
                let program = Program {
                    vs: vs,
                    ps: ps
                };

                (program, info)
            },

            // Metal only supports vertex + fragment and has some features from
            // geometry shaders in vertex (layered rendering)
            //
            // Tracking: https://forums.developer.apple.com/message/9495
            _ => { return Err("Metal only supports vertex + fragment shader programs".into()); }
        };

        Ok(self.share.handles.borrow_mut().make_program(prog, info))
    }

    fn create_pipeline_state_raw(&mut self, program: &handle::Program<Resources>, desc: &core::pso::Descriptor)
                                 -> Result<handle::RawPipelineState<Resources>, core::pso::CreationError> {
        use map::{map_depth_surface, map_vertex_format, map_topology,
                  map_winding, map_cull, map_fill};

        let vertex_desc = MTLVertexDescriptor::new();

        println!("att: {:?}", desc.attributes);
        // TODO: find a better way to set the buffer's stride, step func and
        //       step rate
        let buf = vertex_desc.layouts().object_at(0);
        let vat = desc.vertex_buffers[desc.attributes[0].unwrap().0 as usize].unwrap();
        buf.set_stride(vat.stride as u64);
        buf.set_step_function(if vat.rate > 0 {MTLVertexStepFunction::PerInstance} else {MTLVertexStepFunction::PerVertex});
        buf.set_step_rate(vat.rate as u64);

        for (attr, attr_desc) in program.get_info().vertex_attributes.iter().zip(desc.attributes.iter()) {
            let elem = match attr_desc {
                &Some((_, el)) => el,
                &None => continue,
            };

            if elem.offset & 1 != 0 {
                error!("Vertex attribute {} must be aligned to 2 bytes, has offset {}",
                    attr.name, elem.offset);
                return Err(core::pso::CreationError);
            }

            // TODO: handle case when requested vertex format is invalid
            let attribute = vertex_desc.attributes().object_at(attr.slot as usize);
            attribute.set_format(map_vertex_format(elem.format).unwrap());
            attribute.set_offset(elem.offset as u64);
            attribute.set_buffer_index(0);
        }

        let prog = self.frame_handles.ref_program(program);

        let pso_descriptor = MTLRenderPipelineDescriptor::alloc().init();
        println!("{:?}", prog.ps);
        pso_descriptor.set_vertex_function(prog.vs);
        if !prog.ps.is_null() {
            pso_descriptor.set_fragment_function(prog.ps);
        }
        pso_descriptor.set_vertex_descriptor(vertex_desc);
        pso_descriptor.set_input_primitive_topology(map_topology(desc.primitive));
        if !prog.ps.is_null() {
            pso_descriptor.color_attachments().object_at(0).set_pixel_format(MTLPixelFormat::BGRA8Unorm);
        } else {
            pso_descriptor.color_attachments().object_at(0).set_pixel_format(MTLPixelFormat::Invalid);
        }

        if let Some(depth_desc) = desc.depth_stencil {
            // TODO: depthstencil
            println!("{:?}", depth_desc);
            // pso_descriptor.set_depth_attachment_pixel_format(MTLPixelFormat::Depth32Float);
            pso_descriptor.set_depth_attachment_pixel_format(map_depth_surface((depth_desc.0).0).unwrap());
        }

        let pso = self.device.new_render_pipeline_state(pso_descriptor).unwrap();

        let pso = Pipeline {
            pipeline: pso,
            depth_stencil: desc.depth_stencil.map(|desc| self.make_depth_stencil(&desc.1)),
            winding: map_winding(desc.rasterizer.front_face),
            cull: map_cull(desc.rasterizer.cull_face),
            fill: map_fill(desc.rasterizer.method)
        };

        Ok(self.share.handles.borrow_mut().make_pso(pso, program))
    }

{'fraction_non_alphanumeric': 0.11691753232987068, 'fraction_numerical': 0.004840647304077451, 'mean_word_length': 2.2535923669387286, 'pattern_counts': {'":': 0, '<': 47, '<?xml version=': 0, '>': 98, 'https://': 4, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6119348', 'n_tokens_mistral': 8250, 'n_tokens_neox': 7933, 'n_words': 1615}