makepad_render/
cx_metal.rs

1//use cocoa::base::{id};
2//use cocoa::appkit::{NSView};
3//use cocoa::foundation::{NSAutoreleasePool, NSUInteger, NSRange};
4//use core_graphics::geometry::CGSize;
5//use core_graphics::color::CGColor;
6use makepad_objc_sys::{msg_send};
7use makepad_objc_sys::runtime::YES;
8//use metal::*;
9use crate::cx_apple::*;
10use crate::cx_cocoa::*;
11use crate::cx::*;
12
13impl Cx {
14    
15    pub fn render_view(
16        &mut self,
17        pass_id: usize,
18        view_id: usize,
19        scroll: Vec2,
20        clip: (Vec2, Vec2),
21        zbias: &mut f32,
22        zbias_step: f32,
23        encoder: id,
24        metal_cx: &MetalCx,
25    ) {
26        // tad ugly otherwise the borrow checker locks 'self' and we can't recur
27        let draw_calls_len = self.views[view_id].draw_calls_len;
28        //self.views[view_id].set_clipping_uniforms();
29        self.views[view_id].uniform_view_transform(&Mat4::identity());
30        self.views[view_id].parent_scroll = scroll;
31        let local_scroll = self.views[view_id].get_local_scroll();
32        let clip = self.views[view_id].intersect_clip(clip);
33        
34        for draw_call_id in 0..draw_calls_len {
35            let sub_view_id = self.views[view_id].draw_calls[draw_call_id].sub_view_id;
36            if sub_view_id != 0 {
37                self.render_view(
38                    pass_id,
39                    sub_view_id,
40                    Vec2 {x: local_scroll.x + scroll.x, y: local_scroll.y + scroll.y},
41                    clip,
42                    zbias,
43                    zbias_step,
44                    encoder,
45                    metal_cx,
46                );
47            }
48            else {
49                let cxview = &mut self.views[view_id];
50                //view.platform.uni_vw.update_with_f32_data(device, &view.uniforms);
51                let draw_call = &mut cxview.draw_calls[draw_call_id];
52                let sh = &self.shaders[draw_call.shader_id];
53                let shp = sh.platform.as_ref().unwrap();
54                
55                if draw_call.instance_dirty {
56                    draw_call.instance_dirty = false;
57                    // update the instance buffer data
58                    self.platform.bytes_written += draw_call.instance.len() * 4;
59                    draw_call.platform.inst_vbuf.update_with_f32_data(metal_cx, &draw_call.instance);
60                }
61                
62                // update the zbias uniform if we have it.
63                draw_call.set_zbias(*zbias);
64                draw_call.set_local_scroll(scroll, local_scroll);
65                draw_call.set_clip(clip);
66                *zbias += zbias_step;
67                
68                if draw_call.uniforms_dirty {
69                    draw_call.uniforms_dirty = false;
70                }
71                
72                // lets verify our instance_offset is not disaligned
73                let instances = (draw_call.instance.len() / sh.mapping.instance_slots) as u64;
74                if instances == 0 {
75                    continue;
76                }
77                let pipeline_state = shp.pipeline_state;
78                unsafe {let () = msg_send![encoder, setRenderPipelineState: pipeline_state];}
79                
80                if let Some(buf) = shp.geom_vbuf.multi_buffer_read().buffer {
81                    unsafe {msg_send![
82                        encoder,
83                        setVertexBuffer: buf
84                        offset: 0
85                        atIndex: 0
86                    ]}
87                }
88                else {println!("Drawing error: geom_vbuf None")}
89                
90                if let Some(buf) = draw_call.platform.inst_vbuf.multi_buffer_read().buffer {
91                    unsafe {msg_send![
92                        encoder,
93                        setVertexBuffer: buf
94                        offset: 0
95                        atIndex: 1
96                    ]}
97                }
98                else {println!("Drawing error: inst_vbuf None")}
99                
100                let pass_uniforms = self.passes[pass_id].pass_uniforms.as_slice();
101                let view_uniforms = cxview.view_uniforms.as_slice();
102                let draw_uniforms = draw_call.draw_uniforms.as_slice();
103                
104                unsafe {
105                    let () = msg_send![encoder, setVertexBytes: pass_uniforms.as_ptr() as *const std::ffi::c_void length: (pass_uniforms.len() * 4) as u64 atIndex: 2u64];
106                    let () = msg_send![encoder, setVertexBytes: view_uniforms.as_ptr() as *const std::ffi::c_void length: (view_uniforms.len() * 4) as u64 atIndex: 3u64];
107                    let () = msg_send![encoder, setVertexBytes: draw_uniforms.as_ptr() as *const std::ffi::c_void length: (draw_uniforms.len() * 4) as u64 atIndex: 4u64];
108                    let () = msg_send![encoder, setVertexBytes: draw_call.uniforms.as_ptr() as *const std::ffi::c_void length: (draw_call.uniforms.len() * 4) as u64 atIndex: 5u64];
109                    let () = msg_send![encoder, setFragmentBytes: pass_uniforms.as_ptr() as *const std::ffi::c_void length: (pass_uniforms.len() * 4) as u64 atIndex: 0u64];
110                    let () = msg_send![encoder, setFragmentBytes: view_uniforms.as_ptr() as *const std::ffi::c_void length: (view_uniforms.len() * 4) as u64 atIndex: 1u64];
111                    let () = msg_send![encoder, setFragmentBytes: draw_uniforms.as_ptr() as *const std::ffi::c_void length: (draw_uniforms.len() * 4) as u64 atIndex: 2u64];
112                    let () = msg_send![encoder, setFragmentBytes: draw_call.uniforms.as_ptr() as *const std::ffi::c_void length: (draw_call.uniforms.len() * 4) as u64 atIndex: 3u64];
113                }
114                //encoder.set_vertex_bytes(2, (pass_uniforms.len() * 4) as u64, pass_uniforms.as_ptr() as *const std::ffi::c_void);
115                //encoder.set_vertex_bytes(3, (view_uniforms.len() * 4) as u64, view_uniforms.as_ptr() as *const std::ffi::c_void);
116                //encoder.set_vertex_bytes(4, (draw_uniforms.len() * 4) as u64, draw_uniforms.as_ptr() as *const std::ffi::c_void);
117                //encoder.set_vertex_bytes(5, (draw_call.uniforms.len() * 4) as u64, draw_call.uniforms.as_ptr() as *const std::ffi::c_void);
118                //encoder.set_fragment_bytes(0, (pass_uniforms.len() * 4) as u64, pass_uniforms.as_ptr() as *const std::ffi::c_void);
119                //encoder.set_fragment_bytes(1, (view_uniforms.len() * 4) as u64, view_uniforms.as_ptr() as *const std::ffi::c_void);
120                //encoder.set_fragment_bytes(2, (draw_uniforms.len() * 4) as u64, draw_uniforms.as_ptr() as *const std::ffi::c_void);
121                //encoder.set_fragment_bytes(3, (draw_call.uniforms.len() * 4) as u64, draw_call.uniforms.as_ptr() as *const std::ffi::c_void);
122                // lets set our textures
123                for (i, texture_id) in draw_call.textures_2d.iter().enumerate() {
124                    let cxtexture = &mut self.textures[*texture_id as usize];
125                    if cxtexture.update_image {
126                        metal_cx.update_platform_texture_image2d(cxtexture);
127                    }
128                    if let Some(mtl_texture) = cxtexture.platform.mtl_texture {
129                        let () = unsafe {msg_send![
130                            encoder,
131                            setFragmentTexture: mtl_texture
132                            atIndex: i as u64
133                        ]};
134                        let () = unsafe {msg_send![
135                            encoder,
136                            setVertexTexture: mtl_texture
137                            atIndex: i as u64
138                        ]};
139                    }
140                }
141                self.platform.draw_calls_done += 1;
142                if let Some(buf) = shp.geom_ibuf.multi_buffer_read().buffer {
143                    
144                    let () = unsafe {msg_send![
145                        encoder,
146                        drawIndexedPrimitives: MTLPrimitiveType::Triangle
147                        indexCount: sh.shader_gen.geometry_indices.len() as u64
148                        indexType: MTLIndexType::UInt32
149                        indexBuffer: buf
150                        indexBufferOffset: 0
151                        instanceCount: instances
152                    ]};
153                }
154                else {println!("Drawing error: geom_ibuf None")}
155            }
156        }
157    }
158    
159    pub fn setup_render_pass_descriptor(&mut self, render_pass_descriptor: id, pass_id: usize, inherit_dpi_factor: f32, first_texture: Option<id>, metal_cx: &MetalCx) {
160        let pass_size = self.passes[pass_id].pass_size;
161        
162        self.passes[pass_id].set_ortho_matrix(Vec2::default(), pass_size);
163        self.passes[pass_id].uniform_camera_view(&Mat4::identity());
164        self.passes[pass_id].paint_dirty = false;
165        let dpi_factor = if let Some(override_dpi_factor) = self.passes[pass_id].override_dpi_factor {
166            override_dpi_factor
167        }
168        else {
169            inherit_dpi_factor
170        };
171        self.passes[pass_id].set_dpi_factor(dpi_factor);
172        
173        for (index, color_texture) in self.passes[pass_id].color_textures.iter().enumerate() {
174            let color_attachments: id = unsafe {msg_send![render_pass_descriptor, colorAttachments]};
175            let color_attachment: id = unsafe {msg_send![color_attachments, objectAtIndexedSubscript: 0]};
176            // let color_attachment = render_pass_descriptor.color_attachments().object_at(0).unwrap();
177            
178            let is_initial;
179            if index == 0 && first_texture.is_some() {
180                let () = unsafe {msg_send![
181                    color_attachment,
182                    setTexture: first_texture.unwrap()
183                ]};
184                is_initial = true;
185            }
186            else {
187                let cxtexture = &mut self.textures[color_texture.texture_id];
188                is_initial = metal_cx.update_platform_render_target(cxtexture, dpi_factor, pass_size, false);
189                
190                if let Some(mtl_texture) = cxtexture.platform.mtl_texture {
191                    let () = unsafe {msg_send![
192                        color_attachment,
193                        setTexture: mtl_texture
194                    ]};
195                }
196                else {
197                    println!("draw_pass_to_texture invalid render target");
198                }
199                
200            }
201            unsafe {msg_send![color_attachment, setStoreAction: MTLStoreAction::Store]}
202            
203            match color_texture.clear_color {
204                ClearColor::InitWith(color) => {
205                    if is_initial {
206                        unsafe {
207                            let () = msg_send![color_attachment, setLoadAction: MTLLoadAction::Clear];
208                            let () = msg_send![color_attachment, setClearColor: MTLClearColor {
209                                red: color.r as f64,
210                                green: color.g as f64,
211                                blue: color.b as f64,
212                                alpha: color.a as f64
213                            }];
214                        }
215                    }
216                    else {
217                        unsafe {let () = msg_send![color_attachment, setLoadAction: MTLLoadAction::Load];}
218                    }
219                },
220                ClearColor::ClearWith(color) => {
221                    unsafe {
222                        let () = msg_send![color_attachment, setLoadAction: MTLLoadAction::Clear];
223                        let () = msg_send![color_attachment, setClearColor: MTLClearColor {
224                            red: color.r as f64,
225                            green: color.g as f64,
226                            blue: color.b as f64,
227                            alpha: color.a as f64
228                        }];
229                    }
230                }
231            }
232        }
233        // attach depth texture
234        if let Some(depth_texture_id) = self.passes[pass_id].depth_texture {
235            let cxtexture = &mut self.textures[depth_texture_id];
236            let is_initial = metal_cx.update_platform_render_target(cxtexture, dpi_factor, pass_size, true);
237            
238            let depth_attachment: id = unsafe {msg_send![render_pass_descriptor, depthAttachment]};
239            
240            if let Some(mtl_texture) = cxtexture.platform.mtl_texture {
241                unsafe {msg_send![depth_attachment, setTexture: mtl_texture]}
242            }
243            else {
244                println!("draw_pass_to_texture invalid render target");
245            }
246            let () = unsafe {msg_send![depth_attachment, setStoreAction: MTLStoreAction::Store]};
247            
248            match self.passes[pass_id].clear_depth {
249                ClearDepth::InitWith(depth) => {
250                    if is_initial {
251                        let () = unsafe {msg_send![depth_attachment, setLoadAction: MTLLoadAction::Clear]};
252                        let () = unsafe {msg_send![depth_attachment, setClearDepth: depth as f64]};
253                    }
254                    else {
255                        let () = unsafe {msg_send![depth_attachment, setLoadAction: MTLLoadAction::Load]};
256                    }
257                },
258                ClearDepth::ClearWith(depth) => {
259                    let () = unsafe {msg_send![depth_attachment, setLoadAction: MTLLoadAction::Clear]};
260                    let () = unsafe {msg_send![depth_attachment, setClearDepth: depth as f64]};
261                }
262            }
263            // create depth state
264            if self.passes[pass_id].platform.mtl_depth_state.is_none() {
265                
266                let desc: id = unsafe {msg_send![class!(MTLDepthStencilDescriptor), new]};
267                let () = unsafe {msg_send![desc, setDepthCompareFunction: MTLCompareFunction::LessEqual]};
268                let () = unsafe {msg_send![desc, setDepthWriteEnabled: true]};
269                let depth_stencil_state: id = unsafe {msg_send![metal_cx.device, newDepthStencilStateWithDescriptor: desc]};
270                self.passes[pass_id].platform.mtl_depth_state = Some(depth_stencil_state);
271            }
272        }
273    }
274    
275    pub fn draw_pass_to_layer(
276        &mut self,
277        pass_id: usize,
278        dpi_factor: f32,
279        layer: id,
280        metal_cx: &mut MetalCx,
281    ) {
282        self.platform.bytes_written = 0;
283        self.platform.draw_calls_done = 0;
284        let view_id = self.passes[pass_id].main_view_id.unwrap();
285        
286        let pool: id = unsafe {msg_send![class!(NSAutoreleasePool), new]};
287        
288        //let command_buffer = command_queue.new_command_buffer();
289        let drawable: id = unsafe {msg_send![layer, nextDrawable]};
290        if drawable != nil {
291            let render_pass_descriptor: id = unsafe {msg_send![class!(MTLRenderPassDescriptorInternal), renderPassDescriptor]};
292
293            let texture: id = unsafe {msg_send![drawable, texture]};
294
295            self.setup_render_pass_descriptor(render_pass_descriptor, pass_id, dpi_factor, Some(texture), metal_cx);
296            
297            let command_buffer: id = unsafe {msg_send![metal_cx.command_queue, commandBuffer]};
298            let encoder: id = unsafe {msg_send![command_buffer, renderCommandEncoderWithDescriptor: render_pass_descriptor]};
299            
300            unsafe {msg_send![encoder, textureBarrier]}
301            
302            if let Some(depth_state) = self.passes[pass_id].platform.mtl_depth_state {
303                let () = unsafe {msg_send![encoder, setDepthStencilState: depth_state]};
304            }
305            let mut zbias = 0.0;
306            let zbias_step = self.passes[pass_id].zbias_step;
307            
308            self.render_view(
309                pass_id,
310                view_id,
311                Vec2::default(),
312                (Vec2 {x: -50000., y: -50000.}, Vec2 {x: 50000., y: 50000.}),
313                &mut zbias,
314                zbias_step,
315                encoder,
316                &metal_cx,
317            );
318            
319            let () = unsafe {msg_send![encoder, endEncoding]};
320            let () = unsafe {msg_send![command_buffer, presentDrawable: drawable]};
321            let () = unsafe {msg_send![command_buffer, commit]};
322            //command_buffer.wait_until_scheduled();
323        }
324        let () = unsafe {msg_send![pool, release]};
325    }
326    
327    pub fn draw_pass_to_texture(
328        &mut self,
329        pass_id: usize,
330        dpi_factor: f32,
331        metal_cx: &MetalCx,
332    ) {
333        let view_id = self.passes[pass_id].main_view_id.unwrap();
334        
335        let pool: id = unsafe {msg_send![class!(NSAutoreleasePool), new]};
336        let render_pass_descriptor: id = unsafe {msg_send![class!(MTLRenderPassDescriptorInternal), renderPassDescriptor]};
337
338        self.setup_render_pass_descriptor(render_pass_descriptor, pass_id, dpi_factor, None, metal_cx);
339        
340        let command_buffer: id = unsafe {msg_send![metal_cx.command_queue, commandBuffer]};
341        let encoder: id = unsafe {msg_send![command_buffer, renderCommandEncoderWithDescriptor: render_pass_descriptor]};
342        
343        if let Some(depth_state) = self.passes[pass_id].platform.mtl_depth_state {
344            let () = unsafe {msg_send![encoder, setDepthStencilState: depth_state]};
345        }
346        
347        let mut zbias = 0.0;
348        let zbias_step = self.passes[pass_id].zbias_step;
349        self.render_view(
350            pass_id,
351            view_id,
352            Vec2::default(),
353            (Vec2 {x: -50000., y: -50000.}, Vec2 {x: 50000., y: 50000.}),
354            &mut zbias,
355            zbias_step,
356            encoder,
357            &metal_cx,
358        );
359        let () = unsafe {msg_send![encoder, textureBarrier]};
360        let () = unsafe {msg_send![encoder, endEncoding]};
361        let () = unsafe {msg_send![command_buffer, commit]};
362        //command_buffer.wait_until_scheduled();
363        let () = unsafe {msg_send![pool, release]};
364    }
365}
366
367pub struct MetalCx {
368    pub device: id,
369    pub command_queue: id
370}
371
372impl MetalCx {
373    
374    pub fn new() -> MetalCx {
375        let devices = get_all_metal_devices();
376        for device in devices {
377            let is_low_power: BOOL = unsafe {msg_send![device, isLowPower]};
378            let command_queue: id = unsafe {msg_send![device, newCommandQueue]};
379            if is_low_power == YES {
380                return MetalCx {
381                    command_queue: command_queue,
382                    device: device
383                }
384            }
385        }
386        let device = get_default_metal_device().expect("Cannot get default metal device");
387        MetalCx {
388            command_queue: unsafe {msg_send![device, newCommandQueue]},
389            device: device
390        }
391    }
392    
393    pub fn update_platform_render_target(&self, cxtexture: &mut CxTexture, dpi_factor: f32, size: Vec2, is_depth: bool) -> bool {
394        
395        let width = if let Some(width) = cxtexture.desc.width {width as u64} else {(size.x * dpi_factor) as u64};
396        let height = if let Some(height) = cxtexture.desc.height {height as u64} else {(size.y * dpi_factor) as u64};
397        
398        if cxtexture.platform.width == width && cxtexture.platform.height == height && cxtexture.platform.alloc_desc == cxtexture.desc {
399            return false
400        }
401        cxtexture.platform.mtl_texture = None;
402        
403        let mdesc: id = unsafe {msg_send![class!(MTLTextureDescriptor), new]};
404        if !is_depth {
405            match cxtexture.desc.format {
406                TextureFormat::Default | TextureFormat::RenderBGRA => {
407                    unsafe {
408                        let () = msg_send![mdesc, setPixelFormat: MTLPixelFormat::BGRA8Unorm];
409                        let () = msg_send![mdesc, setTextureType: MTLTextureType::D2];
410                        let () = msg_send![mdesc, setStorageMode: MTLStorageMode::Private];
411                        let () = msg_send![mdesc, setUsage: MTLTextureUsage::RenderTarget];
412                    }
413                },
414                _ => {
415                    println!("update_platform_render_target unsupported texture format");
416                    return false;
417                }
418            }
419        }
420        else {
421            match cxtexture.desc.format {
422                TextureFormat::Default | TextureFormat::Depth32Stencil8 => {
423                    unsafe {
424                        let () = msg_send![mdesc, setPixelFormat: MTLPixelFormat::Depth32Float_Stencil8];
425                        let () = msg_send![mdesc, setTextureType: MTLTextureType::D2];
426                        let () = msg_send![mdesc, setStorageMode: MTLStorageMode::Private];
427                        let () = msg_send![mdesc, setUsage: MTLTextureUsage::RenderTarget];
428                    }
429                },
430                _ => {
431                    println!("update_platform_render_targete unsupported texture format");
432                    return false;
433                }
434            }
435        }
436        let () = unsafe {msg_send![mdesc, setWidth: width as u64]};
437        let () = unsafe {msg_send![mdesc, setHeight: height as u64]};
438        let () = unsafe {msg_send![mdesc, setDepth: 1u64]};
439        
440        let tex: id = unsafe {msg_send![self.device, newTextureWithDescriptor: mdesc]};
441
442        cxtexture.platform.width = width;
443        cxtexture.platform.height = height;
444        cxtexture.platform.alloc_desc = cxtexture.desc.clone();
445        cxtexture.platform.mtl_texture = Some(tex);
446        return true
447    }
448    
449    pub fn update_platform_texture_image2d(&self, cxtexture: &mut CxTexture) {
450        
451        if cxtexture.desc.width.is_none() || cxtexture.desc.height.is_none() {
452            println!("update_platform_texture_image2d without width/height");
453            return;
454        }
455        
456        let width = cxtexture.desc.width.unwrap();
457        let height = cxtexture.desc.height.unwrap();
458        
459        // allocate new texture if descriptor change
460        if cxtexture.platform.alloc_desc != cxtexture.desc {
461            cxtexture.platform.mtl_texture = None;
462            
463            let mdesc: id = unsafe {msg_send![class!(MTLTextureDescriptor), new]};
464            unsafe {
465                let () = msg_send![mdesc, setTextureType: MTLTextureType::D2];
466                let () = msg_send![mdesc, setStorageMode: MTLStorageMode::Managed];
467                let () = msg_send![mdesc, setUsage: MTLTextureUsage::RenderTarget];
468                let () = msg_send![mdesc, setWidth: width as u64];
469                let () = msg_send![mdesc, setHeight: height as u64];
470            }
471            
472            match cxtexture.desc.format {
473                TextureFormat::Default | TextureFormat::ImageBGRA => {
474                    let () = unsafe {msg_send![mdesc, setPixelFormat: MTLPixelFormat::BGRA8Unorm]};
475                    
476                    let tex: id = unsafe {msg_send![self.device, newTextureWithDescriptor: mdesc]};
477
478                    cxtexture.platform.mtl_texture = Some(tex);
479                    
480                    if cxtexture.image_u32.len() != width * height {
481                        println!("update_platform_texture_image2d with wrong buffer_u32 size!");
482                        cxtexture.platform.mtl_texture = None;
483                        return;
484                    }
485                    let region = MTLRegion {
486                        origin: MTLOrigin {x: 0, y: 0, z: 0},
487                        size: MTLSize {width: width as u64, height: height as u64, depth: 1}
488                    };
489                    if let Some(mtl_texture) = cxtexture.platform.mtl_texture {
490                        let () = unsafe {msg_send![
491                            mtl_texture,
492                            replaceRegion: region
493                            mipmapLevel: 0
494                            withBytes: cxtexture.image_u32.as_ptr() as *const std::ffi::c_void
495                            bytesPerRow: (width * std::mem::size_of::<u32>()) as u64
496                        ]};
497                    }
498                },
499                _ => {
500                    println!("update_platform_texture_image2d with unsupported format");
501                    return;
502                }
503            }
504            cxtexture.platform.alloc_desc = cxtexture.desc.clone();
505            cxtexture.platform.width = width as u64;
506            cxtexture.platform.height = height as u64;
507        }
508        
509        cxtexture.update_image = false;
510    }
511}
512
513#[derive(Clone)]
514pub struct MetalWindow {
515    pub window_id: usize,
516    pub first_draw: bool,
517    pub window_geom: WindowGeom,
518    pub cal_size: Vec2,
519    pub ca_layer: id,
520    pub cocoa_window: CocoaWindow,
521}
522
523impl MetalWindow {
524    pub fn new(window_id: usize, metal_cx: &MetalCx, cocoa_app: &mut CocoaApp, inner_size: Vec2, position: Option<Vec2>, title: &str) -> MetalWindow {
525        
526        let ca_layer: id = unsafe {msg_send![class!(CAMetalLayer), new]};
527        
528        let mut cocoa_window = CocoaWindow::new(cocoa_app, window_id);
529        
530        cocoa_window.init(title, inner_size, position);
531        
532        unsafe {
533            let () = msg_send![ca_layer, setDevice: metal_cx.device];
534            let () = msg_send![ca_layer, setPixelFormat: MTLPixelFormat::BGRA8Unorm];
535            let () = msg_send![ca_layer, setPresentsWithTransaction: NO];
536            let () = msg_send![ca_layer, setMaximumDrawableCount: 3];
537            let () = msg_send![ca_layer, setDisplaySyncEnabled: NO];
538            let () = msg_send![ca_layer, setNeedsDisplayOnBoundsChange: YES];
539            let () = msg_send![ca_layer, setAutoresizingMask: (1 << 4) | (1 << 1)];
540            let () = msg_send![ca_layer, setAllowsNextDrawableTimeout: NO];
541            let () = msg_send![ca_layer, setDelegate: cocoa_window.view];
542            let () = msg_send![ca_layer, setBackgroundColor: CGColorCreateGenericRGB(0.0, 0.0, 0.0, 1.0)];
543            
544            let view = cocoa_window.view;
545            let () = msg_send![view, setWantsBestResolutionOpenGLSurface: YES];
546            let () = msg_send![view, setWantsLayer: YES];
547            let () = msg_send![view, setLayerContentsPlacement: 11];
548            let () = msg_send![view, setLayer: ca_layer];
549        }
550        
551        MetalWindow {
552            first_draw: true,
553            window_id,
554            cal_size: Vec2::default(),
555            ca_layer,
556            window_geom: cocoa_window.get_window_geom(),
557            cocoa_window
558        }
559    }
560    
561    pub fn set_vsync_enable(&mut self, enable: bool) {
562        let () = unsafe {msg_send![self.ca_layer, setDisplaySyncEnabled: enable]};
563    }
564    
565    pub fn set_buffer_count(&mut self, _count: u64) {
566        let () = unsafe {msg_send![self.ca_layer, setMaximumDrawableCount: 3]};
567    }
568    
569    pub fn resize_core_animation_layer(&mut self, _metal_cx: &MetalCx) -> bool {
570        let cal_size = Vec2 {
571            x: self.window_geom.inner_size.x * self.window_geom.dpi_factor,
572            y: self.window_geom.inner_size.y * self.window_geom.dpi_factor
573        };
574        if self.cal_size != cal_size {
575            self.cal_size = cal_size;
576            unsafe {
577                let () = msg_send![self.ca_layer, setDrawableSize: CGSize {width: cal_size.x as f64, height: cal_size.y as f64}];
578                let () = msg_send![self.ca_layer, setContentsScale: self.window_geom.dpi_factor as f64];
579            }
580            //self.msam_target = Some(RenderTarget::new(device, self.cal_size.x as u64, self.cal_size.y as u64, 2));
581            true
582        }
583        else {
584            false
585        }
586    }
587    
588}
589
590#[derive(Clone, Default)]
591pub struct CxPlatformView {
592}
593
594#[derive(Default, Clone)]
595pub struct CxPlatformDrawCall {
596    //pub uni_dr: MetalBuffer,
597    pub inst_vbuf: MetalBuffer
598}
599
600#[derive(Default, Clone)]
601pub struct CxPlatformTexture {
602    pub alloc_desc: TextureDesc,
603    pub width: u64,
604    pub height: u64,
605    pub mtl_texture: Option<id>
606}
607
608#[derive(Default, Clone)]
609pub struct CxPlatformPass {
610    pub mtl_depth_state: Option<id>
611}
612
613#[derive(Default, Clone)]
614pub struct MultiMetalBuffer {
615    pub buffer: Option<id>,
616    pub size: usize,
617    pub used: usize
618}
619
620#[derive(Default, Clone)]
621pub struct MetalBuffer {
622    pub last_written: usize,
623    pub multi1: MultiMetalBuffer,
624    pub multi2: MultiMetalBuffer,
625    pub multi3: MultiMetalBuffer,
626    pub multi4: MultiMetalBuffer,
627    pub multi5: MultiMetalBuffer,
628    pub multi6: MultiMetalBuffer,
629    pub multi7: MultiMetalBuffer,
630    pub multi8: MultiMetalBuffer,
631    pub multi9: MultiMetalBuffer,
632    pub multi10: MultiMetalBuffer,
633}
634
635impl MetalBuffer {
636    pub fn multi_buffer_read(&self) -> &MultiMetalBuffer {
637        match self.last_written {
638            0 => &self.multi1,
639            1 => &self.multi2,
640            2 => &self.multi3,
641            3 => &self.multi4,
642            _ => &self.multi5,
643        }
644    }
645    
646    pub fn multi_buffer_write(&mut self) -> &mut MultiMetalBuffer {
647        self.last_written = (self.last_written + 1) % 5;
648        match self.last_written {
649            0 => &mut self.multi1,
650            1 => &mut self.multi2,
651            2 => &mut self.multi3,
652            3 => &mut self.multi4,
653            _ => &mut self.multi5,
654        }
655    }
656    
657    pub fn update_with_f32_data(&mut self, metal_cx: &MetalCx, data: &Vec<f32>) {
658        let elem = self.multi_buffer_write();
659        if elem.size < data.len() {
660            elem.buffer = None;
661        }
662        if let None = elem.buffer {
663            let buffer: id = unsafe {msg_send![
664                metal_cx.device,
665                newBufferWithLength: (data.len() * std::mem::size_of::<f32>()) as u64
666                options: MTLResourceOptions::StorageModeShared
667            ]};
668            if buffer == nil {elem.buffer = None} else {elem.buffer = Some(buffer)}
669            elem.size = data.len()
670        }
671        
672        if let Some(buffer) = elem.buffer {
673            unsafe {
674                let p: *mut std::ffi::c_void = msg_send![buffer, contents];
675                std::ptr::copy(data.as_ptr(), p as *mut f32, data.len());
676                let () = msg_send![
677                    buffer,
678                    didModifyRange: NSRange {
679                        location: 0,
680                        length: (data.len() * std::mem::size_of::<f32>()) as u64
681                    }
682                ];
683            }
684        }
685        elem.used = data.len()
686    }
687    
688    pub fn update_with_u32_data(&mut self, metal_cx: &MetalCx, data: &Vec<u32>) {
689        let elem = self.multi_buffer_write();
690        if elem.size < data.len() {
691            elem.buffer = None;
692        }
693        if let None = elem.buffer {
694            let buffer: id = unsafe {msg_send![
695                metal_cx.device,
696                newBufferWithLength: (data.len() * std::mem::size_of::<u32>()) as u64
697                options: MTLResourceOptions::StorageModeShared
698            ]};
699            if buffer == nil {elem.buffer = None} else {elem.buffer = Some(buffer)}
700            elem.size = data.len()
701        }
702        if let Some(buffer) = elem.buffer {
703            unsafe {
704                let p: *mut std::ffi::c_void = msg_send![buffer, contents];
705                std::ptr::copy(data.as_ptr(), p as *mut u32, data.len());
706                let () = msg_send![
707                    buffer,
708                    didModifyRange: NSRange {
709                        location: 0,
710                        length: (data.len() * std::mem::size_of::<f32>()) as u64
711                    }
712                ];
713            }
714        }
715        elem.used = data.len()
716    }
717}