makepad_render/
cx_glsl.rs

1use crate::cx::*;
2
3pub enum GLShaderType {
4    OpenGL,
5    WebGL1
6}
7
8impl Cx {
9    
10    pub fn gl_assemble_uniforms(unis: &Vec<ShVar>) -> String {
11        let mut out = String::new();
12        for uni in unis {
13            out.push_str("uniform ");
14            out.push_str(&uni.ty);
15            out.push_str(" ");
16            out.push_str(&uni.name);
17            out.push_str(";\n")
18        };
19        out
20    }
21    
22    pub fn ceil_div4(base: usize) -> usize {
23        let r = base >> 2;
24        if base & 3 != 0 {
25            return r + 1
26        }
27        r
28    }
29    
30    pub fn gl_assemble_texture_slots(unis: &Vec<ShVar>) -> String {
31        let mut out = String::new();
32        for uni in unis {
33            out.push_str("uniform ");
34            match uni.ty.as_ref() {
35                "texture2d" => out.push_str("sampler2D"),
36                _ => out.push_str("unknown_texture_type")
37            };
38            out.push_str(" ");
39            out.push_str(&uni.name);
40            out.push_str(";\n")
41        };
42        out
43    }
44    
45    pub fn gl_assemble_vartype(i: usize, total: usize, left: usize) -> String {
46        if i == total - 1 {
47            match left {
48                1 => "float",
49                2 => "vec2",
50                3 => "vec3",
51                _ => "vec4"
52            }.to_string()
53        }
54        else {
55            "vec4".to_string()
56        }
57    }
58    
59    
60    pub fn gl_assemble_varblock(thing: &str, base: &str, slots: usize) -> String {
61        // ok lets do a ceil
62        let mut out = String::new();
63        let total = Self::ceil_div4(slots);
64        for i in 0..total {
65            out.push_str(thing);
66            out.push_str(" ");
67            out.push_str(&Self::gl_assemble_vartype(i, total, slots & 3));
68            out.push_str(" ");
69            out.push_str(base);
70            out.push_str(&i.to_string());
71            out.push_str(";\n");
72        }
73        out
74    }
75    
76    pub fn gl_assemble_vardef(var: &ShVar) -> String {
77        // ok lets do a ceil
78        let mut out = String::new();
79        out.push_str(&var.ty);
80        out.push_str(" ");
81        out.push_str(&var.name);
82        out.push_str(";\n");
83        out
84    }
85    
86    pub fn gl_assemble_unpack(base: &str, slot: usize, total_slots: usize, sv: &ShVar) -> String {
87        let mut out = String::new();
88        // ok we have the slot we start at
89        out.push_str("    ");
90        out.push_str(&sv.name);
91        out.push_str("=");
92        let id = (slot)>>2;
93        
94        // just splat directly
95        if sv.ty == "vec2" {
96            match slot & 3 {
97                0 => {
98                    out.push_str(base);
99                    out.push_str(&id.to_string());
100                    out.push_str(".xy;\r\n");
101                    return out
102                }
103                1 => {
104                    out.push_str(base);
105                    out.push_str(&id.to_string());
106                    out.push_str(".yz;\r\n");
107                    return out
108                }
109                2 => {
110                    out.push_str(base);
111                    out.push_str(&id.to_string());
112                    out.push_str(".zw;\r\n");
113                    return out
114                }
115                _ => ()
116            }
117        }
118        if sv.ty == "vec3" {
119            match slot & 3 {
120                0 => {
121                    out.push_str(base);
122                    out.push_str(&id.to_string());
123                    out.push_str(".xyz;\r\n");
124                    return out
125                }
126                1 => {
127                    out.push_str(base);
128                    out.push_str(&id.to_string());
129                    out.push_str(".yzw;\r\n");
130                    return out
131                }
132                _ => ()
133            }
134        }
135        if sv.ty == "vec4" {
136            if slot & 3 == 0 {
137                out.push_str(base);
138                out.push_str(&id.to_string());
139                out.push_str(".xyzw;\r\n");
140                return out
141            }
142        }
143        if sv.ty != "float" {
144            out.push_str(&sv.ty);
145            out.push_str("(");
146        }
147        
148        // splat via loose props
149        let svslots = match sv.ty.as_ref() {
150            "float" => 1,
151            "vec2" => 2,
152            "vec3" => 3,
153            _ => 4
154        };
155        
156        for i in 0..svslots {
157            if i != 0 {
158                out.push_str(", ");
159            }
160            out.push_str(base);
161            
162            let id = (slot + i)>>2;
163            let ext = (slot + i) & 3;
164            
165            out.push_str(&id.to_string());
166            out.push_str(
167                match ext {
168                    0 => {
169                        if (id == total_slots>>2) && total_slots & 3 == 1 {
170                            ""
171                        }
172                        else {
173                            ".x"
174                        }
175                    }
176                    1 => ".y",
177                    2 => ".z",
178                    _ => ".w"
179                }
180            );
181        }
182        if sv.ty != "float" {
183            out.push_str(")");
184        }
185        out.push_str(";\n");
186        out
187    }
188    
189    pub fn gl_assemble_pack_chunk(base: &str, id: usize, chunk: &str, sv: &ShVar) -> String {
190        let mut out = String::new();
191        out.push_str("    ");
192        out.push_str(base);
193        out.push_str(&id.to_string());
194        out.push_str(chunk);
195        out.push_str(&sv.name);
196        out.push_str(";\n");
197        out
198    }
199    
200    pub fn gl_assemble_pack(base: &str, slot: usize, total_slots: usize, sv: &ShVar) -> String {
201        // now we go the other way. we take slot and assign ShaderVar into it
202        let mut out = String::new();
203        let id = (slot)>>2;
204        
205        // just splat directly
206        if sv.ty == "vec2" {
207            match slot & 3 {
208                0 => return Self::gl_assemble_pack_chunk(base, id, ".xy =", &sv),
209                1 => return Self::gl_assemble_pack_chunk(base, id, ".yz =", &sv),
210                2 => return Self::gl_assemble_pack_chunk(base, id, ".zw =", &sv),
211                _ => ()
212            }
213        }
214        if sv.ty == "vec3" {
215            match slot & 3 {
216                0 => return Self::gl_assemble_pack_chunk(base, id, ".xyz =", &sv),
217                1 => return Self::gl_assemble_pack_chunk(base, id, ".yzw =", &sv),
218                _ => ()
219            }
220        }
221        if sv.ty == "vec4" {
222            if slot & 3 == 0 {
223                return Self::gl_assemble_pack_chunk(base, id, ".xyzw =", &sv);
224            }
225        }
226        
227        let svslots = match sv.ty.as_ref() {
228            "float" => 1,
229            "vec2" => 2,
230            "vec3" => 3,
231            _ => 4
232        };
233        
234        for i in 0..svslots {
235            out.push_str("    ");
236            out.push_str(base);
237            let id = (slot + i)>>2;
238            let ext = (slot + i) & 3;
239            out.push_str(&id.to_string());
240            out.push_str(
241                match ext {
242                    0 => {
243                        if (id == total_slots>>2) && total_slots & 3 == 1 { // we are at last slot
244                            ""
245                        }
246                        else {
247                            ".x"
248                        }
249                    }
250                    1 => ".y",
251                    2 => ".z",
252                    _ => ".w"
253                }
254            );
255            out.push_str(" = ");
256            out.push_str(&sv.name);
257            out.push_str(
258                match i {
259                    0 => {
260                        if sv.ty == "float" {
261                            ""
262                        }
263                        else {
264                            ".x"
265                        }
266                    }
267                    1 => ".y",
268                    2 => ".z",
269                    _ => ".w"
270                }
271            );
272            out.push_str(";\r\n");
273        }
274        out
275    }
276    
277    pub fn gl_assemble_shader(sg: &ShaderGen, shtype: GLShaderType) -> Result<(String, String, CxShaderMapping), SlErr> {
278        
279        let mut vtx_out = String::new();
280        let mut pix_out = String::new();
281        match shtype {
282            GLShaderType::OpenGL => {
283                vtx_out.push_str("#version 100\n");
284                pix_out.push_str("#version 100\n");
285                pix_out.push_str("#extension GL_OES_standard_derivatives : enable\n");
286                vtx_out.push_str("precision highp float;\n");
287                pix_out.push_str("precision highp float;\n");
288                vtx_out.push_str("precision highp int;\n");
289                pix_out.push_str("precision highp int;\n");
290            },
291            GLShaderType::WebGL1 => {
292                pix_out.push_str("#extension GL_OES_standard_derivatives : enable\n");
293                vtx_out.push_str("precision highp float;\n");
294                pix_out.push_str("precision highp float;\n");
295                vtx_out.push_str("precision highp int;\n");
296                pix_out.push_str("precision highp int;\n");
297            }
298        }
299
300        vtx_out.push_str("vec4 texture2Dflip(sampler2D sampler, vec2 pos){return texture2D(sampler, vec2(pos.x, 1.0-pos.y));}\n");
301        pix_out.push_str("vec4 texture2Dflip(sampler2D sampler, vec2 pos){return texture2D(sampler, vec2(pos.x, 1.0-pos.y));}\n");
302
303        // ok now define samplers from our sh.
304        let texture_slots = sg.flat_vars(|v| if let ShVarStore::Texture = *v{true} else {false});
305        let geometries = sg.flat_vars(|v| if let ShVarStore::Geometry = *v{true} else {false});
306        let instances = sg.flat_vars(|v| if let ShVarStore::Instance(_) = *v{true} else {false});
307        let mut varyings = sg.flat_vars(|v| if let ShVarStore::Varying = *v{true} else {false});
308        let locals = sg.flat_vars(|v| if let ShVarStore::Local = *v{true} else {false});
309        let pass_uniforms = sg.flat_vars(|v| if let ShVarStore::PassUniform = *v{true} else {false});
310        let view_uniforms = sg.flat_vars(|v| if let ShVarStore::ViewUniform = *v{true} else {false});
311        let draw_uniforms = sg.flat_vars(|v| if let ShVarStore::DrawUniform = *v{true} else {false});
312        let uniforms = sg.flat_vars(|v| if let ShVarStore::Uniform(_) = *v{true} else {false});
313        
314        let mut const_cx = SlCx {
315            depth: 0,
316            target: SlTarget::Constant,
317            defargs_fn: "".to_string(),
318            defargs_call: "".to_string(),
319            call_prefix: "_".to_string(),
320            shader_gen: sg,
321            scope: Vec::new(),
322            fn_deps: Vec::new(),
323            fn_done: Vec::new(),
324            auto_vary: Vec::new()
325        };
326        let consts = sg.flat_consts();
327        let mut consts_out = String::new();
328        for cnst in &consts {
329            let const_init = assemble_const_init(cnst, &mut const_cx) ?;
330            if cnst.ty != const_init.ty {
331                return Err(SlErr {msg: format!("Constant {} init value {} is not the right type {}", cnst.name, const_init.sl, cnst.ty)})
332            }
333            consts_out.push_str("const ");
334            consts_out.push_str(" ");
335            consts_out.push_str(&cnst.ty);
336            consts_out.push_str(" ");
337            consts_out.push_str(&cnst.name);
338            consts_out.push_str(" = ");
339            consts_out.push_str(&const_init.sl);
340            consts_out.push_str(";\n");
341        }
342        
343        let mut vtx_cx = SlCx {
344            depth: 0,
345            target: SlTarget::Vertex,
346            defargs_fn: "".to_string(),
347            defargs_call: "".to_string(),
348            call_prefix: "".to_string(),
349            shader_gen: sg,
350            scope: Vec::new(),
351            fn_deps: vec!["vertex".to_string()],
352            fn_done: Vec::new(),
353            auto_vary: Vec::new()
354        };
355        let vtx_fns = assemble_fn_and_deps(sg, &mut vtx_cx) ?;
356        
357        let mut pix_cx = SlCx {
358            depth: 0,
359            target: SlTarget::Pixel,
360            defargs_fn: "".to_string(),
361            defargs_call: "".to_string(),
362            call_prefix: "".to_string(),
363            shader_gen: sg,
364            scope: Vec::new(),
365            fn_deps: vec!["pixel".to_string()],
366            fn_done: Vec::new(),
367            auto_vary: Vec::new()
368        };
369        let pix_fns = assemble_fn_and_deps(sg, &mut pix_cx) ?;
370        
371        for auto in &pix_cx.auto_vary {
372            varyings.push(auto.clone());
373        }
374        
375        // lets count the slots
376        let geometry_slots = sg.compute_slot_total(&geometries);
377        let instance_slots = sg.compute_slot_total(&instances);
378        let varying_slots = sg.compute_slot_total(&varyings);
379        let mut shared = String::new();
380        shared.push_str("// Consts\n");
381        shared.push_str(&consts_out);
382        shared.push_str("//Pass uniforms\n");
383        shared.push_str(&Self::gl_assemble_uniforms(&pass_uniforms));
384        shared.push_str("//View uniforms\n");
385        shared.push_str(&Self::gl_assemble_uniforms(&view_uniforms));
386        shared.push_str("//Draw uniforms\n");
387        shared.push_str(&Self::gl_assemble_uniforms(&draw_uniforms));
388        shared.push_str("//Uniforms\n");
389        shared.push_str(&Self::gl_assemble_uniforms(&uniforms));
390        shared.push_str("//Texture slots\n");
391        shared.push_str(&Self::gl_assemble_texture_slots(&texture_slots));
392        shared.push_str("// Varyings\n");
393        shared.push_str(&Self::gl_assemble_varblock("varying", "varying", varying_slots));
394        
395        for local in &locals {shared.push_str(&Self::gl_assemble_vardef(&local));}
396        
397        pix_out.push_str(&shared);
398        
399        vtx_out.push_str(&shared);
400        
401        let mut vtx_main = "void main(){\n".to_string();
402        let mut pix_main = "void main(){\n".to_string();
403        
404        vtx_out.push_str("// Geometry attributes\n");
405        vtx_out.push_str(&Self::gl_assemble_varblock("attribute", "geomattr", geometry_slots));
406        let mut slot_id = 0;
407        for geometry in &geometries {
408            vtx_out.push_str(&Self::gl_assemble_vardef(&geometry));
409            vtx_main.push_str(&Self::gl_assemble_unpack("geomattr", slot_id, geometry_slots, &geometry));
410            slot_id += sg.get_type_slots(&geometry.ty);
411        }
412        
413        vtx_out.push_str("// Instance attributes\n");
414        vtx_out.push_str(&Self::gl_assemble_varblock("attribute", "instattr", instance_slots));
415        let mut slot_id = 0;
416        for instance in &instances {
417            vtx_out.push_str(&Self::gl_assemble_vardef(&instance));
418            vtx_main.push_str(&Self::gl_assemble_unpack("instattr", slot_id, instance_slots, &instance));
419            slot_id += sg.get_type_slots(&instance.ty);
420        }
421        
422        
423        vtx_main.push_str("\n    gl_Position = vertex();\n");
424        vtx_main.push_str("\n    // Varying packing\n");
425        
426        pix_main.push_str("\n    // Varying unpacking\n");
427        
428        // alright lets pack/unpack varyings
429        let mut slot_id = 0;
430        for vary in &varyings {
431            // only if we aren't already a geom/instance var
432            if geometries.iter().find( | v | v.name == vary.name).is_none() &&
433            instances.iter().find( | v | v.name == vary.name).is_none() {
434                vtx_out.push_str(&Self::gl_assemble_vardef(&vary));
435            }
436            pix_out.push_str(&Self::gl_assemble_vardef(&vary));
437            // pack it in the vertexshader
438            vtx_main.push_str(&Self::gl_assemble_pack("varying", slot_id, varying_slots, &vary));
439            // unpack it in the pixelshader
440            pix_main.push_str(&Self::gl_assemble_unpack("varying", slot_id, varying_slots, &vary));
441            slot_id += sg.get_type_slots(&vary.ty);
442        }
443        
444        pix_main.push_str("\n    gl_FragColor = pixel();\n");
445        vtx_main.push_str("\n}\n\0");
446        pix_main.push_str("\n}\n\0");
447        
448        vtx_out.push_str("//Vertex shader\n");
449        vtx_out.push_str(&vtx_fns);
450        pix_out.push_str("//Pixel shader\n");
451        pix_out.push_str(&pix_fns);
452        
453        vtx_out.push_str("//Main function\n");
454        vtx_out.push_str(&vtx_main);
455        
456        pix_out.push_str("//Main function\n");
457        pix_out.push_str(&pix_main);
458        
459        if sg.log != 0 {
460            //println!("---------- Pixelshader:  ---------\n{}", pix_out);
461            //println!("---------- Vertexshader:  ---------\n{}", vtx_out);
462        }
463        // we can also flatten our uniform variable set
464        
465        // lets composite our ShAst structure into a set of methods
466        let uniform_props = UniformProps::construct(sg, &uniforms);
467        Ok((vtx_out, pix_out, CxShaderMapping {
468            instance_props: InstanceProps::construct(sg, &instances),
469            rect_instance_props: RectInstanceProps::construct(sg, &instances),
470            uniform_props,
471            instances,
472            geometries,
473            instance_slots,
474            geometry_slots,
475            draw_uniforms,
476            view_uniforms,
477            pass_uniforms,
478            uniforms,
479            texture_slots,
480        }))
481    }
482}
483
484
485impl<'a> SlCx<'a> {
486    pub fn map_call(&self, name: &str, args: &Vec<Sl>) -> MapCallResult {
487        match name {
488            "matrix_comp_mult" => return MapCallResult::Rename("matrixCompMult".to_string()),
489            "less_than" => return MapCallResult::Rename("less_than".to_string()),
490            "less_than_equal" => return MapCallResult::Rename("lessThanEqual".to_string()),
491            "greater_than" => return MapCallResult::Rename("greaterThan".to_string()),
492            "greater_than_equal" => return MapCallResult::Rename("greaterThanEqual".to_string()),
493            "not_equal" => return MapCallResult::Rename("notEqual".to_string()),
494            "sample2d" => {
495                return MapCallResult::Rename("texture2Dflip".to_string())
496            },
497            "fmod" => {
498                return MapCallResult::Rename("mod".to_string())
499            },
500            "dfdx" => {
501                return MapCallResult::Rename("dFdx".to_string())
502            },
503            "dfdy" => {
504                return MapCallResult::Rename("dFdy".to_string())
505            },
506            "color" => {
507                let col = color(&args[0].sl);
508                return MapCallResult::Rewrite(
509                    format!("vec4({},{},{},{})", col.r, col.g, col.b, col.a),
510                    "vec4".to_string()
511                );
512            },
513            _ => return MapCallResult::None
514        }
515    }
516    
517    pub fn mat_mul(&self, left: &str, right: &str) -> String {
518        format!("{}*{}", left, right)
519    }
520    
521    pub fn map_type(&self, ty: &str) -> String {
522        match ty {
523            "texture2d" => return "sampler2D".to_string(),
524            _ => return ty.to_string()
525        }
526    }
527    
528    pub fn map_constructor(&self, name: &str, args: &Vec<Sl>)->String{
529        let mut out = String::new();
530        out.push_str(&self.map_type(name));
531        out.push_str("(");
532        for (i,arg) in args.iter().enumerate(){
533            if i != 0{
534                out.push_str(", ");
535            }
536            out.push_str(&arg.sl);
537        }
538        out.push_str(")");
539        return out;
540    }
541    
542    pub fn map_var(&mut self, var: &ShVar) -> String {
543        match var.store {
544            ShVarStore::Instance(_) => {
545                if let SlTarget::Pixel = self.target {
546                    if self.auto_vary.iter().find( | v | v.name == var.name).is_none() {
547                        self.auto_vary.push(var.clone());
548                    }
549                }
550            },
551            ShVarStore::Geometry => {
552                if let SlTarget::Pixel = self.target {
553                    if self.auto_vary.iter().find( | v | v.name == var.name).is_none() {
554                        self.auto_vary.push(var.clone());
555                    }
556                }
557            },
558            _ => ()
559        }
560        var.name.clone()
561    }
562    
563}