makepad_render/
cx_metalsl.rs

1
2use crate::cx::*;
3use crate::cx_apple::*;
4
5#[derive(Clone)]
6pub struct CxPlatformShader {
7    pub library: id,
8    pub pipeline_state: id,
9    pub geom_vbuf: MetalBuffer,
10    pub geom_ibuf: MetalBuffer,
11}
12
13impl PartialEq for CxPlatformShader {
14    fn eq(&self, _other: &Self) -> bool {false}
15}
16
17pub enum PackType {
18    Packed,
19    Unpacked
20}
21
22impl Cx {
23    pub fn mtl_compile_all_shaders(&mut self, metal_cx: &MetalCx) {
24        for sh in &mut self.shaders {
25            let mtlsh = Self::mtl_compile_shader(sh, metal_cx);
26            if let Err(err) = mtlsh {
27                panic!("Got metal shader compile error: {}", err.msg);
28            }
29        };
30    }
31    pub fn mtl_type_to_packed_metal(ty: &str) -> String {
32        match ty.as_ref() {
33            "float" => "float".to_string(),
34            "vec2" => "packed_float2".to_string(),
35            "vec3" => "packed_float3".to_string(),
36            "vec4" => "packed_float4".to_string(),
37            "mat2" => "packed_float2x2".to_string(),
38            "mat3" => "packed_float3x3".to_string(),
39            "mat4" => "float4x4".to_string(),
40            ty => ty.to_string()
41        }
42    }
43    
44    pub fn mtl_type_to_metal(ty: &str) -> String {
45        match ty.as_ref() {
46            "float" => "float".to_string(),
47            "vec2" => "float2".to_string(),
48            "vec3" => "float3".to_string(),
49            "vec4" => "float4".to_string(),
50            "mat2" => "float2x2".to_string(),
51            "mat3" => "float3x3".to_string(),
52            "mat4" => "float4x4".to_string(),
53            "texture2d" => "texture2d<float>".to_string(),
54            ty => ty.to_string()
55        }
56    }
57    
58    pub fn mtl_assemble_struct(name: &str, vars: &Vec<ShVar>, pack_type: PackType, field: &str) -> String {
59        let mut out = String::new();
60        out.push_str("struct ");
61        out.push_str(name);
62        out.push_str("{\n");
63        out.push_str(field);
64        for var in vars {
65            out.push_str("  ");
66            match pack_type {
67                PackType::Packed => {
68                    out.push_str(&Self::mtl_type_to_packed_metal(&var.ty));
69                    out.push_str(" ");
70                    out.push_str(&var.name);
71                    out.push_str(";\n");
72                },
73                PackType::Unpacked => {
74                    out.push_str(&Self::mtl_type_to_metal(&var.ty));
75                    out.push_str(" ");
76                    out.push_str(&var.name);
77                    out.push_str(";\n");
78                }
79            }
80            
81        };
82        out.push_str("};\n\n");
83        out
84    }
85    
86    pub fn mtl_assemble_texture_slots(textures: &Vec<ShVar>) -> String {
87        let mut out = String::new();
88        out.push_str("struct ");
89        out.push_str("_Tex{\n");
90        for (i, tex) in textures.iter().enumerate() {
91            out.push_str("texture2d<float> ");
92            out.push_str(&tex.name);
93            out.push_str(&format!(" [[texture({})]];\n", i));
94        };
95        out.push_str("};\n\n");
96        out
97    }
98    
99    pub fn mtl_assemble_shader(sg: &ShaderGen) -> Result<(String, CxShaderMapping), SlErr> {
100        
101        let mut mtl_out = "#include <metal_stdlib>\nusing namespace metal;\n".to_string();
102        
103        // ok now define samplers from our sh.
104        let texture_slots = sg.flat_vars( | v | if let ShVarStore::Texture = *v {true} else {false});
105        let geometries = sg.flat_vars( | v | if let ShVarStore::Geometry = *v {true} else {false});
106        let instances = sg.flat_vars( | v | if let ShVarStore::Instance(_) = *v {true} else {false});
107        let mut varyings = sg.flat_vars( | v | if let ShVarStore::Varying = *v {true} else {false});
108        let locals = sg.flat_vars( | v | if let ShVarStore::Local = *v {true} else {false});
109        let pass_uniforms = sg.flat_vars( | v | if let ShVarStore::PassUniform = *v {true} else {false});
110        let view_uniforms = sg.flat_vars( | v | if let ShVarStore::ViewUniform = *v {true} else {false});
111        let draw_uniforms = sg.flat_vars( | v | if let ShVarStore::DrawUniform = *v {true} else {false});
112        let uniforms = sg.flat_vars( | v | if let ShVarStore::Uniform(_) = *v {true} else {false});
113        
114        // lets count the slots
115        let geometry_slots = sg.compute_slot_total(&geometries);
116        let instance_slots = sg.compute_slot_total(&instances);
117        //let varying_slots = sh.compute_slot_total(&varyings);
118        
119        mtl_out.push_str(&Self::mtl_assemble_struct("_Geom", &geometries, PackType::Packed, ""));
120        mtl_out.push_str(&Self::mtl_assemble_struct("_Inst", &instances, PackType::Packed, ""));
121        mtl_out.push_str(&Self::mtl_assemble_struct("_UniPs", &pass_uniforms, PackType::Unpacked, ""));
122        mtl_out.push_str(&Self::mtl_assemble_struct("_UniVw", &view_uniforms, PackType::Unpacked, ""));
123        mtl_out.push_str(&Self::mtl_assemble_struct("_UniDr", &draw_uniforms, PackType::Unpacked, ""));
124        mtl_out.push_str(&Self::mtl_assemble_struct("_Uni", &uniforms, PackType::Unpacked, ""));
125        mtl_out.push_str(&Self::mtl_assemble_struct("_Loc", &locals, PackType::Unpacked, ""));
126        
127        // we need to figure out which texture slots exist
128        mtl_out.push_str(&Self::mtl_assemble_texture_slots(&texture_slots));
129        
130        // we need to figure out which texture slots exist
131        // mtl_out.push_str(&Self::assemble_constants(&texture_slots));
132        let mut const_cx = SlCx {
133            depth: 0,
134            target: SlTarget::Constant,
135            defargs_fn: "".to_string(),
136            defargs_call: "".to_string(),
137            call_prefix: "_".to_string(),
138            shader_gen: sg,
139            scope: Vec::new(),
140            fn_deps: Vec::new(),
141            fn_done: Vec::new(),
142            auto_vary: Vec::new()
143        };
144        let consts = sg.flat_consts();
145        for cnst in &consts {
146            let const_init = assemble_const_init(cnst, &mut const_cx) ?;
147            mtl_out.push_str("#define ");
148            mtl_out.push_str(" ");
149            mtl_out.push_str(&cnst.name);
150            mtl_out.push_str(" (");
151            mtl_out.push_str(&const_init.sl);
152            mtl_out.push_str(")\n");
153        }
154        
155        let mut vtx_cx = SlCx {
156            depth: 0,
157            target: SlTarget::Vertex,
158            defargs_fn: "_Tex _tex, thread _Loc &_loc, thread _Vary &_vary, thread _Geom &_geom, thread _Inst &_inst, device _UniPs &_uni_ps, device _UniVw &_uni_vw, device _UniDr &_uni_dr, device _Uni &_uni".to_string(),
159            defargs_call: "_tex, _loc, _vary, _geom, _inst, _uni_ps, _uni_vw, _uni_dr, _uni".to_string(),
160            call_prefix: "_".to_string(),
161            shader_gen: sg,
162            scope: Vec::new(),
163            fn_deps: vec!["vertex".to_string()],
164            fn_done: Vec::new(),
165            auto_vary: Vec::new()
166        };
167        let vtx_fns = assemble_fn_and_deps(sg, &mut vtx_cx) ?;
168        let mut pix_cx = SlCx {
169            depth: 0,
170            target: SlTarget::Pixel,
171            defargs_fn: "_Tex _tex, thread _Loc &_loc, thread _Vary &_vary, device _UniPs &_uni_ps, device _UniVw &_uni_vw, device _UniDr &_uni_dr, device _Uni &_uni".to_string(),
172            defargs_call: "_tex, _loc, _vary, _uni_ps, _uni_vw, _uni_dr, _uni".to_string(),
173            call_prefix: "_".to_string(),
174            shader_gen: sg,
175            scope: Vec::new(),
176            fn_deps: vec!["pixel".to_string()],
177            fn_done: vtx_cx.fn_done,
178            auto_vary: Vec::new()
179        };
180        
181        let pix_fns = assemble_fn_and_deps(sg, &mut pix_cx) ?;
182        
183        // lets add the auto_vary ones to the varyings struct
184        for auto in &pix_cx.auto_vary {
185            varyings.push(auto.clone());
186        }
187        mtl_out.push_str(&Self::mtl_assemble_struct("_Vary", &varyings, PackType::Unpacked, "  float4 mtl_position [[position]];\n"));
188        
189        mtl_out.push_str("//Vertex shader\n");
190        mtl_out.push_str(&vtx_fns);
191        mtl_out.push_str("//Pixel shader\n");
192        mtl_out.push_str(&pix_fns);
193        
194        // lets define the vertex shader
195        mtl_out.push_str("vertex _Vary _vertex_shader(_Tex _tex, device _Geom *in_geometries [[buffer(0)]], device _Inst *in_instances [[buffer(1)]],\n");
196        mtl_out.push_str("  device _UniPs &_uni_ps [[buffer(2)]], device _UniVw &_uni_vw [[buffer(3)]], device _UniDr &_uni_dr [[buffer(4)]], device _Uni &_uni [[buffer(5)]],\n");
197        mtl_out.push_str("  uint vtx_id [[vertex_id]], uint inst_id [[instance_id]]){\n");
198        mtl_out.push_str("  _Loc _loc;\n");
199        mtl_out.push_str("  _Vary _vary;\n");
200        mtl_out.push_str("  _Geom _geom = in_geometries[vtx_id];\n");
201        mtl_out.push_str("  _Inst _inst = in_instances[inst_id];\n");
202        mtl_out.push_str("  _vary.mtl_position = _vertex(");
203        mtl_out.push_str(&vtx_cx.defargs_call);
204        mtl_out.push_str(");\n\n");
205        
206        for auto in pix_cx.auto_vary {
207            if let ShVarStore::Geometry = auto.store {
208                mtl_out.push_str("       _vary.");
209                mtl_out.push_str(&auto.name);
210                mtl_out.push_str(" = _geom.");
211                mtl_out.push_str(&auto.name);
212                mtl_out.push_str(";\n");
213            }
214            else if let ShVarStore::Instance(_) = auto.store {
215                mtl_out.push_str("       _vary.");
216                mtl_out.push_str(&auto.name);
217                mtl_out.push_str(" = _inst.");
218                mtl_out.push_str(&auto.name);
219                mtl_out.push_str(";\n");
220            }
221        }
222        
223        mtl_out.push_str("       return _vary;\n");
224        mtl_out.push_str("};\n");
225        // then the fragment shader
226        mtl_out.push_str("fragment float4 _fragment_shader(_Vary _vary[[stage_in]],_Tex _tex,\n");
227        mtl_out.push_str("  device _UniPs &_uni_ps [[buffer(0)]], device _UniVw &_uni_vw [[buffer(1)]], device _UniDr &_uni_dr [[buffer(2)]], device _Uni &_uni [[buffer(3)]]){\n");
228        mtl_out.push_str("  _Loc _loc;\n");
229        mtl_out.push_str("  return _pixel(");
230        mtl_out.push_str(&pix_cx.defargs_call);
231        mtl_out.push_str(");\n};\n");
232        
233        if sg.log != 0 {
234            println!("---- Metal shader -----\n{}", mtl_out);
235        }
236        
237        let uniform_props = UniformProps::construct(sg, &uniforms);
238        Ok((mtl_out, CxShaderMapping {
239            rect_instance_props: RectInstanceProps::construct(sg, &instances),
240            instance_props: InstanceProps::construct(sg, &instances),
241            uniform_props,
242            instances,
243            geometries,
244            instance_slots,
245            geometry_slots,
246            draw_uniforms,
247            view_uniforms,
248            pass_uniforms,
249            uniforms,
250            texture_slots,
251        }))
252    }
253    
254    pub fn mtl_compile_shader(sh: &mut CxShader, metal_cx: &MetalCx) -> Result<(), SlErr> {
255        let (mtlsl, mapping) = Self::mtl_assemble_shader(&sh.shader_gen) ?;
256        
257        let options: id = unsafe {msg_send![class!(MTLCompileOptions), new]};
258        let ns_mtlsl: id = str_to_nsstring(&mtlsl);
259        let mut err: id = nil;
260        let library: id = unsafe {msg_send![
261            metal_cx.device,
262            newLibraryWithSource: ns_mtlsl
263            options: options
264            error: &mut err
265        ]};
266
267        if library == nil {
268            let err_str: id = unsafe {msg_send![err, localizedDescription]};
269            return Err(SlErr {msg: nsstring_to_string(err_str)})
270        }
271        
272        sh.mapping = mapping;
273        sh.platform = Some(CxPlatformShader {
274            pipeline_state: unsafe {
275                let vert: id = msg_send![library, newFunctionWithName: str_to_nsstring("_vertex_shader")];
276                let frag: id = msg_send![library, newFunctionWithName: str_to_nsstring("_fragment_shader")];
277                let rpd: id = msg_send![class!(MTLRenderPipelineDescriptor), new];
278                
279                let () = msg_send![rpd, setVertexFunction: vert];
280                let () = msg_send![rpd, setFragmentFunction: frag];
281                
282                let color_attachments: id = msg_send![rpd, colorAttachments];
283                
284                let ca: id = msg_send![color_attachments, objectAtIndexedSubscript: 0u64];
285                let () = msg_send![ca, setPixelFormat: MTLPixelFormat::BGRA8Unorm];
286                let () = msg_send![ca, setBlendingEnabled: YES];
287                let () = msg_send![ca, setSourceRGBBlendFactor: MTLBlendFactor::One];
288                let () = msg_send![ca, setDestinationRGBBlendFactor: MTLBlendFactor::OneMinusSourceAlpha];
289                let () = msg_send![ca, setSourceAlphaBlendFactor: MTLBlendFactor::One];
290                let () = msg_send![ca, setDestinationAlphaBlendFactor: MTLBlendFactor::OneMinusSourceAlpha];
291                let () = msg_send![ca, setRgbBlendOperation: MTLBlendOperation::Add];
292                let () = msg_send![ca, setAlphaBlendOperation: MTLBlendOperation::Add];
293                let () = msg_send![rpd, setDepthAttachmentPixelFormat: MTLPixelFormat::Depth32Float_Stencil8];
294                
295                let mut err: id = nil;
296                let rps: id = msg_send![
297                    metal_cx.device,
298                    newRenderPipelineStateWithDescriptor: rpd 
299                    error: &mut err
300                ];
301                if rps == nil{
302                    panic!("Could not create render pipeline state")
303                }
304                rps//.expect("Could not create render pipeline state")
305            },
306            library: library,
307            geom_ibuf: {
308                let mut geom_ibuf = MetalBuffer {..Default::default()};
309                geom_ibuf.update_with_u32_data(metal_cx, &sh.shader_gen.geometry_indices);
310                geom_ibuf
311            },
312            geom_vbuf: {
313                let mut geom_vbuf = MetalBuffer {..Default::default()};
314                geom_vbuf.update_with_f32_data(metal_cx, &sh.shader_gen.geometry_vertices);
315                geom_vbuf
316            }
317        });
318        return Ok(());
319    }
320}
321
322impl<'a> SlCx<'a> {
323    pub fn map_call(&self, name: &str, args: &Vec<Sl>) -> MapCallResult {
324        match name {
325            "sample2d" => { // transform call to
326                let base = &args[0];
327                let coord = &args[1];
328                return MapCallResult::Rewrite(
329                    format!("{}.sample(sampler(mag_filter::linear,min_filter::linear),{})", base.sl, coord.sl),
330                    "vec4".to_string()
331                )
332            },
333            "color" => {
334                let col = color(&args[0].sl);
335                return MapCallResult::Rewrite(
336                    format!("float4({},{},{},{})", col.r, col.g, col.b, col.a),
337                    "vec4".to_string()
338                );
339            },
340            _ => return MapCallResult::None
341        }
342    }
343    
344    pub fn mat_mul(&self, left: &str, right: &str) -> String {
345        format!("{}*{}", left, right)
346    }
347    
348    pub fn map_type(&self, ty: &str) -> String {
349        Cx::mtl_type_to_metal(ty)
350    }
351    
352    pub fn map_constructor(&self, name: &str, args: &Vec<Sl>) -> String {
353        let mut out = String::new();
354        out.push_str(&self.map_type(name));
355        out.push_str("(");
356        for (i, arg) in args.iter().enumerate() {
357            if i != 0 {
358                out.push_str(", ");
359            }
360            out.push_str(&arg.sl);
361        }
362        out.push_str(")");
363        return out;
364    }
365    
366    pub fn map_var(&mut self, var: &ShVar) -> String {
367        let mty = Cx::mtl_type_to_metal(&var.ty);
368        match var.store {
369            ShVarStore::Uniform(_) => return format!("_uni.{}", var.name),
370            ShVarStore::UniformColor(_) => return format!("_uni_col.{}", var.name),
371            ShVarStore::ViewUniform => return format!("_uni_vw.{}", var.name),
372            ShVarStore::PassUniform => return format!("_uni_ps.{}", var.name),
373            ShVarStore::DrawUniform => return format!("_uni_dr.{}", var.name),
374            ShVarStore::Instance(_) => {
375                if let SlTarget::Pixel = self.target {
376                    if self.auto_vary.iter().find( | v | v.name == var.name).is_none() {
377                        self.auto_vary.push(var.clone());
378                    }
379                    return format!("_vary.{}", var.name);
380                }
381                else {
382                    return format!("{}(_inst.{})", mty, var.name);
383                }
384            },
385            ShVarStore::Geometry => {
386                if let SlTarget::Pixel = self.target {
387                    if self.auto_vary.iter().find( | v | v.name == var.name).is_none() {
388                        self.auto_vary.push(var.clone());
389                    }
390                    return format!("_vary.{}", var.name);
391                }
392                else {
393                    
394                    return format!("{}(_geom.{})", mty, var.name);
395                }
396            },
397            ShVarStore::Texture => return format!("_tex.{}", var.name),
398            ShVarStore::Local => return format!("_loc.{}", var.name),
399            ShVarStore::Varying => return format!("_vary.{}", var.name),
400        }
401    }
402}