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 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 let geometry_slots = sg.compute_slot_total(&geometries);
116 let instance_slots = sg.compute_slot_total(&instances);
117 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 mtl_out.push_str(&Self::mtl_assemble_texture_slots(&texture_slots));
129
130 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 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 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 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},
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" => { 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}