makepad_render/
cx_opengl.rs

1use crate::cx::*;
2use crate::cx_xlib::*;
3use makepad_glx_sys as glx_sys;
4use makepad_x11_sys as X11_sys;
5use std::ffi::{CStr, CString};
6use std::os::raw::{c_ulong, c_void};
7use std::ptr;
8use std::mem;
9
10impl Cx {
11    
12    pub fn render_view(
13        &mut self,
14        pass_id: usize,
15        view_id: usize,
16        scroll: Vec2,
17        clip: (Vec2, Vec2),
18        full_repaint: bool,
19        view_rect: &Rect,
20        opengl_cx: &OpenglCx,
21        zbias: &mut f32,
22        zbias_step: f32
23    ) { 
24        
25        // tad ugly otherwise the borrow checker locks 'self' and we can't recur
26        let draw_calls_len = self.views[view_id].draw_calls_len;
27        if !full_repaint && !view_rect.intersects(self.views[view_id].get_scrolled_rect()) {
28            return
29        }
30        self.views[view_id].uniform_view_transform(&Mat4::identity());
31        self.views[view_id].parent_scroll = scroll;
32        let local_scroll = self.views[view_id].get_local_scroll();
33        let clip = self.views[view_id].intersect_clip(clip);
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                    full_repaint,
43                    view_rect,
44                    opengl_cx,
45                    zbias,
46                    zbias_step
47                );
48            }
49            else {
50                let cxview = &mut self.views[view_id];
51                //view.platform.uni_vw.update_with_f32_data(device, &view.uniforms);
52                let draw_call = &mut cxview.draw_calls[draw_call_id];
53                let sh = &self.shaders[draw_call.shader_id];
54                let shp = sh.platform.as_ref().unwrap();
55                
56                if draw_call.instance_dirty {
57                    draw_call.instance_dirty = false;
58                    draw_call.platform.inst_vbuf.update_with_f32_data(opengl_cx, &draw_call.instance);
59                }
60                
61                draw_call.platform.check_vao(draw_call.shader_id, &shp);
62                
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                unsafe {
73                    gl::UseProgram(shp.program);
74                    gl::BindVertexArray(draw_call.platform.vao.unwrap());
75                    let instances = draw_call.instance.len() / sh.mapping.instance_slots;
76                    let indices = sh.shader_gen.geometry_indices.len();
77                    
78                    let pass_uniforms = self.passes[pass_id].pass_uniforms.as_slice();
79                    let view_uniforms = cxview.view_uniforms.as_slice();
80                    let draw_uniforms = draw_call.draw_uniforms.as_slice();
81                    
82                    opengl_cx.set_uniform_buffer(&shp.pass_uniforms, pass_uniforms);
83                    opengl_cx.set_uniform_buffer(&shp.view_uniforms, view_uniforms);
84                    opengl_cx.set_uniform_buffer(&shp.draw_uniforms, draw_uniforms);
85                    opengl_cx.set_uniform_buffer(&shp.uniforms, &draw_call.uniforms);
86                    
87                    // lets set our textures
88                    for (i, texture_id) in draw_call.textures_2d.iter().enumerate() {
89                        let cxtexture = &mut self.textures[*texture_id as usize];
90                        if cxtexture.update_image {
91                            opengl_cx.update_platform_texture_image2d(cxtexture);
92                        }
93                        // get the loc
94                        gl::ActiveTexture(gl::TEXTURE0 + i as u32);
95                        if let Some(texture) = cxtexture.platform.gl_texture {
96                            gl::BindTexture(gl::TEXTURE_2D, texture);
97                        }
98                        else {
99                            gl::BindTexture(gl::TEXTURE_2D, 0);
100                        }
101                    }
102                    
103                    gl::DrawElementsInstanced(
104                        gl::TRIANGLES,
105                        indices as i32,
106                        gl::UNSIGNED_INT,
107                        ptr::null(),
108                        instances as i32
109                    );
110                }
111            }
112        } 
113    }
114    
115    pub fn calc_dirty_bounds(&mut self, pass_id: usize, view_id: usize, view_bounds: &mut ViewBounds) {
116        let draw_calls_len = self.views[view_id].draw_calls_len;
117        for draw_call_id in 0..draw_calls_len {
118            let sub_view_id = self.views[view_id].draw_calls[draw_call_id].sub_view_id;
119            if sub_view_id != 0 {
120                self.calc_dirty_bounds(pass_id, sub_view_id, view_bounds)
121            }
122            else {
123                let cxview = &mut self.views[view_id];
124                let draw_call = &mut cxview.draw_calls[draw_call_id];
125                //let sh = &self.shaders[draw_call.shader_id];
126                //let shp = sh.platform.as_ref().unwrap();
127                
128                if draw_call.instance_dirty || draw_call.uniforms_dirty {
129                    view_bounds.add_rect(&cxview.get_inverse_scrolled_rect());
130                }
131            }
132        }
133    }
134    
135    pub fn set_default_depth_and_blend_mode() {
136        unsafe {
137            gl::Enable(gl::DEPTH_TEST);
138            gl::DepthFunc(gl::LEQUAL);
139            gl::BlendEquationSeparate(gl::FUNC_ADD, gl::FUNC_ADD);
140            gl::BlendFuncSeparate(gl::ONE, gl::ONE_MINUS_SRC_ALPHA, gl::ONE, gl::ONE_MINUS_SRC_ALPHA);
141            gl::Enable(gl::BLEND);
142        }
143    }
144    
145    pub fn draw_pass_to_window(
146        &mut self,
147        pass_id: usize,
148        dpi_factor: f32,
149        opengl_window: &mut OpenglWindow,
150        opengl_cx: &OpenglCx,
151        _force_full_repaint: bool,
152    ) -> bool {
153        let view_id = self.passes[pass_id].main_view_id.unwrap();
154        
155        let mut view_bounds = ViewBounds::new();
156        let mut init_repaint = false;
157        self.calc_dirty_bounds(pass_id, view_id, &mut view_bounds);
158
159        let full_repaint =  true;/*force_full_repaint || view_bounds.max_x - view_bounds.min_x > opengl_window.window_geom.inner_size.x - 100.
160         && view_bounds.max_y - view_bounds.min_y > opengl_window.window_geom.inner_size.y - 100. ||
161         opengl_window.opening_repaint_count < 10;*/
162        if opengl_window.opening_repaint_count < 10 { // for some reason the first repaint doesn't arrive on the window
163            opengl_window.opening_repaint_count += 1;
164            init_repaint = true;
165        }
166        let window;
167        let view_rect;
168        if full_repaint {
169            opengl_window.xlib_window.hide_child_windows();
170            
171            window = opengl_window.xlib_window.window.unwrap();
172            
173            let pass_size = self.passes[pass_id].pass_size;
174            self.passes[pass_id].set_ortho_matrix(Vec2::default(), pass_size);
175            
176            let pix_width = opengl_window.window_geom.inner_size.x * opengl_window.window_geom.dpi_factor;
177            let pix_height = opengl_window.window_geom.inner_size.y * opengl_window.window_geom.dpi_factor;
178            
179            unsafe {
180                glx_sys::glXMakeCurrent(opengl_cx.display, window, opengl_cx.context);
181                gl::Viewport(0, 0, pix_width as i32, pix_height as i32);
182            }
183            view_rect = Rect::default();
184        }
185        else {
186            if view_bounds.max_x == std::f32::NEG_INFINITY
187                || view_bounds.max_y == std::f32::NEG_INFINITY
188                || view_bounds.min_x == std::f32::INFINITY
189                || view_bounds.min_x == std::f32::INFINITY
190                || view_bounds.min_x == view_bounds.max_x
191                || view_bounds.min_y == view_bounds.max_y {
192                return false
193            }
194            /*
195            unsafe {
196                glx_sys::glXMakeCurrent(xlib_app.display, opengl_window.xlib_window.window.unwrap(), opengl_cx.context);
197                gl::Viewport(
198                    0,
199                    0,
200                    (opengl_window.window_geom.inner_size.x * opengl_window.window_geom.dpi_factor) as i32,
201                    (opengl_window.window_geom.inner_size.y * opengl_window.window_geom.dpi_factor) as i32
202                );
203                gl::ClearColor(0.0, 1.0, 0.0, 0.0);
204                gl::Clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT);
205                glx_sys::glXSwapBuffers(xlib_app.display, opengl_window.xlib_window.window.unwrap());
206            }*/
207            
208            let pix_width = (view_bounds.max_x - view_bounds.min_x) * opengl_window.window_geom.dpi_factor;
209            let pix_height = (view_bounds.max_y - view_bounds.min_y) * opengl_window.window_geom.dpi_factor;
210            
211            window = opengl_window.xlib_window.alloc_child_window(
212                (view_bounds.min_x * opengl_window.window_geom.dpi_factor) as i32,
213                (view_bounds.min_y * opengl_window.window_geom.dpi_factor) as i32,
214                pix_width as u32,
215                pix_height as u32
216            ).unwrap();
217            
218            //let pass_size = self.passes[pass_id].pass_size;
219            self.passes[pass_id].set_ortho_matrix(
220                Vec2 {x: view_bounds.min_x, y: view_bounds.min_y},
221                Vec2 {x: pix_width / opengl_window.window_geom.dpi_factor, y: pix_height / opengl_window.window_geom.dpi_factor}
222            );
223            
224            unsafe {
225                glx_sys::glXMakeCurrent(opengl_cx.display, window, opengl_cx.context);
226                gl::Viewport(0, 0, pix_width as i32, pix_height as i32);
227            }
228            view_rect = Rect {x: view_bounds.min_x, y: view_bounds.min_y, w: view_bounds.max_x - view_bounds.min_x, h: view_bounds.max_y - view_bounds.min_y}
229        }
230        
231        self.passes[pass_id].uniform_camera_view(&Mat4::identity());
232        self.passes[pass_id].set_dpi_factor(dpi_factor);
233        // set up the
234        let clear_color = if self.passes[pass_id].color_textures.len() == 0 {
235            Color::default()
236        }
237        else {
238            match self.passes[pass_id].color_textures[0].clear_color {
239                ClearColor::InitWith(color) => color,
240                ClearColor::ClearWith(color) => color
241            }
242        };
243        let clear_depth = match self.passes[pass_id].clear_depth {
244            ClearDepth::InitWith(depth) => depth,
245            ClearDepth::ClearWith(depth) => depth
246        };
247        
248        unsafe {
249            gl::BindFramebuffer(gl::FRAMEBUFFER, 0);
250            gl::ClearDepth(clear_depth);
251            gl::ClearColor(clear_color.r, clear_color.g, clear_color.b, clear_color.a);
252            gl::Clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT);
253        }
254        Self::set_default_depth_and_blend_mode();
255        
256        let mut zbias = 0.0;
257        let zbias_step = self.passes[pass_id].zbias_step;
258        
259        self.render_view(
260            pass_id,
261            view_id,
262            Vec2::default(),
263            (Vec2 {x: -50000., y: -50000.}, Vec2 {x: 50000., y: 50000.}),
264            full_repaint,
265            &view_rect,
266            &opengl_cx,
267            &mut zbias,
268            zbias_step
269        );
270        
271        unsafe {
272            glx_sys::glXSwapBuffers(opengl_cx.display, window);
273        }
274        return init_repaint;
275    }
276    
277    pub fn draw_pass_to_texture(
278        &mut self,
279        pass_id: usize,
280        inherit_dpi_factor: f32,
281        opengl_cx: &OpenglCx,
282    ) {
283        
284        let pass_size = self.passes[pass_id].pass_size;
285        self.passes[pass_id].set_ortho_matrix(Vec2::default(), pass_size);
286        self.passes[pass_id].uniform_camera_view(&Mat4::identity());
287        self.passes[pass_id].paint_dirty = false;
288        
289        let dpi_factor = if let Some(override_dpi_factor) = self.passes[pass_id].override_dpi_factor {
290            override_dpi_factor
291        }
292        else {
293            inherit_dpi_factor
294        };
295        self.passes[pass_id].set_dpi_factor(dpi_factor);
296        
297        let mut clear_color = Color::default();
298        let mut clear_depth = 1.0;
299        let mut clear_flags = 0;
300        
301        // make a framebuffer
302        if self.passes[pass_id].platform.gl_framebuffer.is_none() {
303            unsafe {
304                let mut gl_framebuffer = std::mem::MaybeUninit::uninit();
305                gl::GenFramebuffers(1, gl_framebuffer.as_mut_ptr());
306                self.passes[pass_id].platform.gl_framebuffer = Some(gl_framebuffer.assume_init());
307            }
308        }
309        
310        // bind the framebuffer
311        unsafe {
312            gl::BindFramebuffer(gl::FRAMEBUFFER, self.passes[pass_id].platform.gl_framebuffer.unwrap());
313        }
314        
315        for (index, color_texture) in self.passes[pass_id].color_textures.iter().enumerate() {
316            match color_texture.clear_color {
317                ClearColor::InitWith(color) => {
318                    if opengl_cx.update_platform_render_target(&mut self.textures[color_texture.texture_id], dpi_factor, pass_size, false) {
319                        clear_color = color;
320                        clear_flags = gl::COLOR_BUFFER_BIT;
321                    }
322                },
323                ClearColor::ClearWith(color) => {
324                    opengl_cx.update_platform_render_target(&mut self.textures[color_texture.texture_id], dpi_factor, pass_size, false);
325                    clear_color = color;
326                    clear_flags = gl::COLOR_BUFFER_BIT;
327                }
328            }
329            if let Some(gl_texture) = self.textures[color_texture.texture_id].platform.gl_texture {
330                unsafe {
331                    gl::FramebufferTexture2D(gl::FRAMEBUFFER, gl::COLOR_ATTACHMENT0 + index as u32, gl::TEXTURE_2D, gl_texture, 0);
332                }
333            }
334        }
335        
336        // attach/clear depth buffers, if any
337        if let Some(depth_texture_id) = self.passes[pass_id].depth_texture {
338            match self.passes[pass_id].clear_depth {
339                ClearDepth::InitWith(depth_clear) => {
340                    if opengl_cx.update_platform_render_target(&mut self.textures[depth_texture_id], dpi_factor, pass_size, true) {
341                        clear_depth = depth_clear;
342                        clear_flags = gl::DEPTH_BUFFER_BIT;
343                    }
344                },
345                ClearDepth::ClearWith(depth_clear) => {
346                    opengl_cx.update_platform_render_target(&mut self.textures[depth_texture_id], dpi_factor, pass_size, true);
347                    clear_depth = depth_clear;
348                    clear_flags = gl::COLOR_BUFFER_BIT;
349                }
350            }
351            if let Some(gl_texture) = self.textures[depth_texture_id].platform.gl_texture {
352                unsafe {
353                    gl::FramebufferTexture2D(gl::FRAMEBUFFER, gl::DEPTH_STENCIL_ATTACHMENT, gl::TEXTURE_2D, gl_texture, 0);
354                }
355            }
356        }
357        unsafe {
358            gl::Viewport(0, 0, (pass_size.x * dpi_factor) as i32, (pass_size.y * dpi_factor) as i32);
359        }
360        if clear_flags != 0 {
361            unsafe {
362                gl::ClearDepth(clear_depth);
363                gl::ClearColor(clear_color.r, clear_color.g, clear_color.b, clear_color.a);
364                gl::Clear(clear_flags);
365            }
366        }
367        
368        Self::set_default_depth_and_blend_mode();
369        
370        let mut zbias = 0.0;
371        let zbias_step = self.passes[pass_id].zbias_step;
372        let view_id = self.passes[pass_id].main_view_id.unwrap();
373        
374        self.render_view(
375            pass_id,
376            view_id,
377            Vec2::default(),
378            (Vec2 {x: -50000., y: -50000.}, Vec2 {x: 50000., y: 50000.}),
379            true,
380            &Rect::default(),
381            &opengl_cx,
382            &mut zbias,
383            zbias_step
384        );
385        
386    }
387    
388    //let view_id = self.passes[pass_id].main_view_id.unwrap();
389    //let _pass_size = self.passes[pass_id].pass_size;
390    
391    /*
392        for (index, color_texture) in self.passes[pass_id].color_textures.iter().enumerate() {
393
394            let cxtexture = &mut self.textures[color_texture.texture_id];
395
396            metal_cx.update_platform_render_target(cxtexture, dpi_factor, pass_size, false);
397            let color_attachment = render_pass_descriptor.color_attachments().object_at(index).unwrap();
398            if let Some(mtltex) = &cxtexture.platform.mtltexture {
399                color_attachment.set_texture(Some(&mtltex));
400            }
401            else {
402                println!("draw_pass_to_texture invalid render target");
403            }
404            color_attachment.set_store_action(MTLStoreAction::Store);
405            if let Some(color) = color_texture.clear_color {
406                color_attachment.set_load_action(MTLLoadAction::Clear);
407                color_attachment.set_clear_color(MTLClearColor::new(color.r as f64, color.g as f64, color.b as f64, color.a as f64));
408            }
409            else {
410                color_attachment.set_load_action(MTLLoadAction::Load);
411            }
412        }
413        */
414    //self.render_view(pass_id, view_id, true, &Rect::zero(), &opengl_cx);
415    // commit
416    //}
417    
418    pub fn opengl_compile_all_shaders(&mut self, opengl_cx: &OpenglCx) {
419        unsafe {
420            glx_sys::glXMakeCurrent(opengl_cx.display, opengl_cx.hidden_window, opengl_cx.context);
421        }
422        for sh in &mut self.shaders {
423            let openglsh = Self::opengl_compile_shader(sh, opengl_cx);
424            if let Err(err) = openglsh {
425                panic!("Got opengl shader compile error:: {}", err.msg);
426            }
427        };
428    }
429    
430    pub fn opengl_has_shader_error(compile: bool, shader: usize, source: &str) -> Option<String> {
431        //None
432        unsafe {
433            
434            let mut success = i32::from(gl::FALSE);
435            
436            if compile {
437                gl::GetShaderiv(shader as u32, gl::COMPILE_STATUS, &mut success);
438            }
439            else {
440                gl::GetProgramiv(shader as u32, gl::LINK_STATUS, &mut success);
441            };
442            
443            if success != i32::from(gl::TRUE) {
444                let mut length = 0;
445                if compile {
446                    gl::GetShaderiv(shader as u32, gl::INFO_LOG_LENGTH, &mut length);
447                } else {
448                    gl::GetProgramiv(shader as u32, gl::INFO_LOG_LENGTH, &mut length);
449                }
450                let mut log = Vec::with_capacity(length as usize);
451                if compile {
452                    gl::GetShaderInfoLog(shader as u32, length, ptr::null_mut(), log.as_mut_ptr());
453                } else {
454                    gl::GetProgramInfoLog(shader as u32, length, ptr::null_mut(), log.as_mut_ptr());
455                }
456                log.set_len(length as usize);
457                let mut r = "".to_string();
458                r.push_str(CStr::from_ptr(log.as_ptr()).to_str().unwrap());
459                r.push_str("\n");
460                let split = source.split("\n");
461                for (line, chunk) in split.enumerate() {
462                    r.push_str(&(line + 1).to_string());
463                    r.push_str(":");
464                    r.push_str(chunk);
465                    r.push_str("\n");
466                }
467                Some(r)
468            }
469            else {
470                None
471            }
472        }
473    }
474    
475    pub fn opengl_get_attributes(program: u32, prefix: &str, slots: usize) -> Vec<OpenglAttribute> {
476        let mut attribs = Vec::new();
477        
478        let stride = (slots * mem::size_of::<f32>()) as i32;
479        let num_attr = Self::ceil_div4(slots);
480        for i in 0..num_attr {
481            let mut name = prefix.to_string();
482            name.push_str(&i.to_string());
483            name.push_str("\0");
484            
485            let mut size = ((slots - i * 4)) as i32;
486            if size > 4 {
487                size = 4;
488            }
489            unsafe {
490                attribs.push(
491                    OpenglAttribute {
492                        loc: gl::GetAttribLocation(program, name.as_ptr() as *const _) as u32,
493                        offset: (i * 4 * mem::size_of::<f32>()) as usize,
494                        size: size,
495                        stride: stride
496                    }
497                )
498            }
499        }
500        attribs
501    }
502    
503    pub fn opengl_get_uniforms(program: u32, sg: &ShaderGen, unis: &Vec<ShVar>) -> Vec<OpenglUniform> {
504        let mut gl_uni = Vec::new();
505        
506        for uni in unis {
507            let mut name0 = "".to_string();
508            name0.push_str(&uni.name);
509            name0.push_str("\0");
510            unsafe {
511                gl_uni.push(OpenglUniform {
512                    loc: gl::GetUniformLocation(program, name0.as_ptr() as *const _),
513                    name: uni.name.clone(),
514                    size: sg.get_type_slots(&uni.ty)
515                })
516            }
517        }
518        gl_uni
519    }
520    
521    pub fn opengl_get_texture_slots(program: u32, texture_slots: &Vec<ShVar>) -> Vec<OpenglUniform> {
522        let mut gl_texture_slots = Vec::new();
523        
524        for slot in texture_slots {
525            let mut name0 = "".to_string();
526            name0.push_str(&slot.name);
527            name0.push_str("\0");
528            unsafe {
529                gl_texture_slots.push(OpenglUniform {
530                    loc: gl::GetUniformLocation(program, name0.as_ptr() as *const _),
531                    name: slot.name.clone(),
532                    size: 0
533                    //,sampler:sam.sampler.clone()
534                })
535            }
536        }
537        gl_texture_slots
538    }
539    
540    pub fn opengl_compile_shader(sh: &mut CxShader, opengl_cx: &OpenglCx) -> Result<(), SlErr> {
541        
542        let (vertex, fragment, mapping) = Self::gl_assemble_shader(&sh.shader_gen, GLShaderType::OpenGL) ?;
543        // now we have a pixel and a vertex shader
544        // so lets now pass it to GL
545        unsafe {
546            let vs = gl::CreateShader(gl::VERTEX_SHADER);
547            gl::ShaderSource(vs, 1, [vertex.as_ptr() as *const _].as_ptr(), ptr::null());
548            gl::CompileShader(vs);
549            if let Some(error) = Self::opengl_has_shader_error(true, vs as usize, &vertex) {
550                return Err(SlErr {
551                    msg: format!("ERROR::SHADER::VERTEX::COMPILATION_FAILED\n{}", error)
552                })
553            }
554            
555            let fs = gl::CreateShader(gl::FRAGMENT_SHADER);
556            gl::ShaderSource(fs, 1, [fragment.as_ptr() as *const _].as_ptr(), ptr::null());
557            gl::CompileShader(fs);
558            if let Some(error) = Self::opengl_has_shader_error(true, fs as usize, &fragment) {
559                return Err(SlErr {
560                    msg: format!("ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n{}", error)
561                })
562            }
563            
564            let program = gl::CreateProgram();
565            gl::AttachShader(program, vs);
566            gl::AttachShader(program, fs);
567            gl::LinkProgram(program);
568            if let Some(error) = Self::opengl_has_shader_error(false, program as usize, "") {
569                return Err(SlErr {
570                    msg: format!("ERROR::SHADER::LINK::COMPILATION_FAILED\n{}", error)
571                })
572            }
573            gl::DeleteShader(vs);
574            gl::DeleteShader(fs);
575            
576            let geom_attribs = Self::opengl_get_attributes(program, "geomattr", mapping.geometry_slots);
577            let inst_attribs = Self::opengl_get_attributes(program, "instattr", mapping.instance_slots);
578            
579            // lets fetch the uniform positions for our uniforms
580            sh.platform = Some(CxPlatformShader {
581                program: program, 
582                geom_ibuf: {
583                    let mut buf = OpenglBuffer::default();
584                    buf.update_with_u32_data(opengl_cx, &sh.shader_gen.geometry_indices);
585                    buf
586                },
587                geom_vbuf: {
588                    let mut buf = OpenglBuffer::default();
589                    buf.update_with_f32_data(opengl_cx, &sh.shader_gen.geometry_vertices);
590                    buf
591                },
592                geom_attribs,
593                inst_attribs,
594                pass_uniforms: Self::opengl_get_uniforms(program, &sh.shader_gen, &mapping.pass_uniforms),
595                view_uniforms: Self::opengl_get_uniforms(program, &sh.shader_gen, &mapping.view_uniforms),
596                draw_uniforms: Self::opengl_get_uniforms(program, &sh.shader_gen, &mapping.draw_uniforms),
597                uniforms: Self::opengl_get_uniforms(program, &sh.shader_gen, &mapping.uniforms),
598            });
599            sh.mapping = mapping;
600            return Ok(());
601        }
602    }
603}
604
605#[derive(Clone, PartialEq)]
606pub struct ViewBounds {
607    pub min_x: f32,
608    pub min_y: f32,
609    pub max_x: f32,
610    pub max_y: f32
611}
612
613impl ViewBounds {
614    fn new() -> ViewBounds {
615        ViewBounds {
616            min_x: std::f32::INFINITY,
617            min_y: std::f32::INFINITY,
618            max_x: std::f32::NEG_INFINITY,
619            max_y: std::f32::NEG_INFINITY,
620        }
621    }
622    
623    fn add_rect(&mut self, rect: &Rect) {
624        if rect.x < self.min_x {
625            self.min_x = rect.x;
626        }
627        if rect.x + rect.w > self.max_x {
628            self.max_x = rect.x + rect.w;
629        }
630        if rect.y < self.min_y {
631            self.min_y = rect.y;
632        }
633        if rect.y + rect.h > self.max_y {
634            self.max_y = rect.y + rect.h;
635        }
636    }
637}
638pub struct OpenglCx {
639    pub display: *mut glx_sys::Display,
640    pub context: glx_sys::GLXContext,
641    pub visual_info: glx_sys::XVisualInfo,
642    pub hidden_window: glx_sys::Window,
643}
644
645impl OpenglCx {
646    pub fn new(display: *mut X11_sys::Display) -> OpenglCx {
647        unsafe {
648            let display = display as *mut glx_sys::Display;
649
650            // Query GLX version.
651            let mut major = 0;
652            let mut minor = 0;
653            assert!(
654                glx_sys::glXQueryVersion(display, &mut major, &mut minor) >= 0,
655                "can't query GLX version"
656            );
657
658            // Check that GLX version number is 1.4 or higher.
659            assert!(
660                major > 1 || major == 1 && minor >= 4,
661                "GLX version must be 1.4 or higher, got {}.{}",
662                major,
663                minor,
664            );
665            
666            let screen = glx_sys::XDefaultScreen(display);
667
668            // Query extensions string
669            let supported_extensions = glx_sys::glXQueryExtensionsString(display, screen);
670            assert!(
671                !supported_extensions.is_null(),
672                "can't query GLX extensions string"
673            );
674            let supported_extensions = CStr::from_ptr(supported_extensions).to_str().unwrap();
675
676            // Check that required extensions are supported.
677            let required_extensions = &["GLX_ARB_get_proc_address", "GLX_ARB_create_context"];
678            for required_extension in required_extensions {
679                assert!(
680                    supported_extensions.contains(required_extension),
681                    "extension {} is required, but not supported",
682                    required_extension,
683                );
684            }
685
686            // Load GLX function pointers.
687            #[allow(non_snake_case)]
688            let glXCreateContextAttribsARB = mem::transmute::<
689                _,
690                glx_sys::PFNGLXCREATECONTEXTATTRIBSARBPROC,
691            >(glx_sys::glXGetProcAddressARB(
692                CString::new("glXCreateContextAttribsARB")
693                    .unwrap()
694                    .to_bytes_with_nul()
695                    .as_ptr(),
696            ))
697            .expect("can't load glXCreateContextAttribsARB function pointer");
698
699            // Load GL function pointers.
700            gl::load_with(|symbol| {
701                glx_sys::glXGetProcAddressARB(
702                    CString::new(symbol).unwrap().to_bytes_with_nul().as_ptr(),
703                )
704                .map_or(ptr::null(), |ptr| ptr as *const c_void)
705            });
706
707            // Choose framebuffer configuration.
708            let config_attribs = &[
709                glx_sys::GLX_DOUBLEBUFFER as i32,
710                glx_sys::True as i32,
711                glx_sys::GLX_RED_SIZE as i32,
712                8,
713                glx_sys::GLX_GREEN_SIZE as i32,
714                8,
715                glx_sys::GLX_BLUE_SIZE as i32,
716                8,
717                glx_sys::GLX_ALPHA_SIZE as i32,
718                8,
719                glx_sys::None as i32,
720            ];
721            let mut config_count = 0;
722            let configs = glx_sys::glXChooseFBConfig(
723                display,
724                glx_sys::XDefaultScreen(display),
725                config_attribs.as_ptr(),
726                &mut config_count,
727            );
728            if configs.is_null() {
729                panic!("can't choose framebuffer configuration");
730            }
731            let config = *configs;
732            glx_sys::XFree(configs as *mut c_void);
733
734            // Create GLX context.
735            let context_attribs = &[
736                glx_sys::GLX_CONTEXT_MAJOR_VERSION_ARB as i32,
737                3,
738                glx_sys::GLX_CONTEXT_MINOR_VERSION_ARB as i32,
739                0,
740                glx_sys::GLX_CONTEXT_PROFILE_MASK_ARB as i32,
741                glx_sys::GLX_CONTEXT_ES_PROFILE_BIT_EXT as i32,
742                glx_sys::None as i32
743            ];
744            let context = glXCreateContextAttribsARB(
745                display,
746                config,
747                ptr::null_mut(),
748                glx_sys::True as i32,
749                context_attribs.as_ptr(),
750            );
751
752            // Get visual from framebuffer configuration.
753            let visual_info_ptr = glx_sys::glXGetVisualFromFBConfig(display, config);
754            assert!(
755                !visual_info_ptr.is_null(),
756                "can't get visual from framebuffer configuration"
757            );
758            let visual_info = *visual_info_ptr;
759            glx_sys::XFree(visual_info_ptr as *mut c_void);
760
761            let root_window = glx_sys::XRootWindow(display, screen);
762
763            // Create hidden window compatible with visual
764            //
765            // We need a hidden window because we sometimes want to create OpenGL resources, such as
766            // shaders, when Makepad does not have any windows open. In cases such as these, we need
767            // *some* window to make the OpenGL context current on.
768            let mut attributes = mem::zeroed::<glx_sys::XSetWindowAttributes>();
769
770            // We need a color map that is compatible with our visual. Otherwise, the call to
771            // XCreateWindow below will fail. 
772            attributes.colormap = glx_sys::XCreateColormap(
773                display,
774                root_window,
775                visual_info.visual,
776                glx_sys::AllocNone as i32
777            );
778            let hidden_window = glx_sys::XCreateWindow(
779                display,
780                root_window,
781                0,
782                0,
783                16,
784                16,
785                0,
786                visual_info.depth,
787                glx_sys::InputOutput as u32,
788                visual_info.visual,
789                glx_sys::CWColormap as c_ulong,
790                &mut attributes,
791            );
792
793            // To make sure the window stays hidden, we simply never call XMapWindow on it.
794
795            OpenglCx {
796                display,
797                context,
798                visual_info,
799                hidden_window,
800            }
801        }
802    }
803    
804    pub fn set_uniform_buffer(&self, locs: &Vec<OpenglUniform>, uni: &[f32]) {
805        
806        let mut o = 0;
807        for loc in locs {
808            if o + loc.size > uni.len() {
809                return
810            }
811            if (o & 3) != 0 && (o & 3) + loc.size > 4 { // goes over the boundary
812                o += 4 - (o & 3); // make jump to new slot
813            }
814            if loc.loc >= 0 {
815                unsafe {
816                    
817                    match loc.size {
818                        1 => {
819                            gl::Uniform1f(loc.loc as i32, uni[o]);
820                        },
821                        2 => gl::Uniform2f(loc.loc as i32, uni[o], uni[o + 1]),
822                        3 => gl::Uniform3f(loc.loc as i32, uni[o], uni[o + 1], uni[o + 2]),
823                        4 => {
824                            gl::Uniform4f(loc.loc as i32, uni[o], uni[o + 1], uni[o + 2], uni[o + 3]);
825                        },
826                        16 => {
827                            gl::UniformMatrix4fv(loc.loc as i32, 1, 0, uni.as_ptr().offset((o) as isize));
828                        },
829                        _ => ()
830                    }
831                }
832            };
833            o = o + loc.size;
834        }
835        
836    }
837    
838    pub fn update_platform_texture_image2d(&self, cxtexture: &mut CxTexture) {
839        
840        if cxtexture.desc.width.is_none() || cxtexture.desc.height.is_none() {
841            println!("update_platform_texture_image2d without width/height");
842            return;
843        }
844        
845        let width = cxtexture.desc.width.unwrap();
846        let height = cxtexture.desc.height.unwrap();
847        
848        // allocate new texture if descriptor change
849        if cxtexture.platform.alloc_desc != cxtexture.desc {
850            
851            cxtexture.platform.alloc_desc = cxtexture.desc.clone();
852            cxtexture.platform.width = width as u64;
853            cxtexture.platform.height = height as u64;
854            
855            let gl_texture = match cxtexture.platform.gl_texture {
856                None => {
857                    unsafe {
858                        let mut gl_texture = std::mem::MaybeUninit::uninit();
859                        gl::GenTextures(1, gl_texture.as_mut_ptr());
860                        let gl_texture = gl_texture.assume_init();
861                        cxtexture.platform.gl_texture = Some(gl_texture);
862                        gl_texture
863                    }
864                }
865                Some(gl_texture_old) => {
866                    gl_texture_old
867                }
868            };
869            unsafe {
870                gl::BindTexture(gl::TEXTURE_2D, gl_texture);
871                gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR as i32);
872                gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as i32);
873                gl::TexImage2D(gl::TEXTURE_2D, 0, gl::RGBA as i32, width as i32, height as i32, 0, gl::RGBA, gl::UNSIGNED_BYTE, cxtexture.image_u32.as_ptr() as *const _);
874                gl::BindTexture(gl::TEXTURE_2D, 0);
875            }
876        }
877        
878        cxtexture.update_image = false;
879    }
880    
881    pub fn update_platform_render_target(&self, cxtexture: &mut CxTexture, dpi_factor: f32, size: Vec2, is_depth: bool) -> bool {
882        let width = if let Some(width) = cxtexture.desc.width {width as u64} else {(size.x * dpi_factor) as u64};
883        let height = if let Some(height) = cxtexture.desc.height {height as u64} else {(size.y * dpi_factor) as u64};
884        
885        if cxtexture.platform.width == width && cxtexture.platform.height == height && cxtexture.platform.alloc_desc == cxtexture.desc {
886            return false
887        }
888        
889        unsafe {
890            if let Some(gl_texture) = cxtexture.platform.gl_texture {
891                gl::DeleteTextures(1, &gl_texture);
892            }
893            
894            let mut gl_texture = std::mem::MaybeUninit::uninit();
895            gl::GenTextures(1, gl_texture.as_mut_ptr());
896            let gl_texture = gl_texture.assume_init();
897            gl::BindTexture(gl::TEXTURE_2D, gl_texture);
898            gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR as i32);
899            gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as i32);
900            
901            cxtexture.platform.alloc_desc = cxtexture.desc.clone();
902            cxtexture.platform.width = width;
903            cxtexture.platform.height = height;
904            cxtexture.platform.gl_texture = Some(gl_texture);
905            
906            if !is_depth {
907                match cxtexture.desc.format {
908                    TextureFormat::Default | TextureFormat::RenderBGRA => {
909                        gl::TexImage2D(gl::TEXTURE_2D, 0, gl::RGBA as i32, width as i32, height as i32, 0, gl::RGBA, gl::UNSIGNED_BYTE, ptr::null());
910                    },
911                    _ => {
912                        println!("update_platform_render_target unsupported texture format");
913                        return false;
914                    }
915                }
916            }
917            else {
918                match cxtexture.desc.format {
919                    TextureFormat::Default | TextureFormat::Depth32Stencil8 => {
920                        println!("Depth stencil texture!");
921                    },
922                    _ => {
923                        println!("update_platform_render_targete unsupported texture format");
924                        return false;
925                    }
926                }
927            }
928            
929        }
930        return true;
931    }
932}
933
934#[derive(Clone)]
935pub struct CxPlatformShader {
936    pub program: u32,
937    pub geom_vbuf: OpenglBuffer,
938    pub geom_ibuf: OpenglBuffer,
939    pub geom_attribs: Vec<OpenglAttribute>,
940    pub inst_attribs: Vec<OpenglAttribute>,
941    pub pass_uniforms: Vec<OpenglUniform>,
942    pub view_uniforms: Vec<OpenglUniform>,
943    pub draw_uniforms: Vec<OpenglUniform>,
944    pub uniforms: Vec<OpenglUniform>
945}
946
947
948#[derive(Clone)]
949pub struct OpenglWindow {
950    pub first_draw: bool,
951    pub window_id: usize,
952    pub window_geom: WindowGeom,
953    pub opening_repaint_count: u32,
954    pub cal_size: Vec2,
955    pub xlib_window: XlibWindow,
956}
957
958impl OpenglWindow {
959    pub fn new(window_id: usize, opengl_cx: &OpenglCx, xlib_app: &mut XlibApp, inner_size: Vec2, position: Option<Vec2>, title: &str) -> OpenglWindow {
960        
961        let mut xlib_window = XlibWindow::new(xlib_app, window_id);
962        
963        let visual_info = unsafe { mem::transmute(opengl_cx.visual_info) };
964        xlib_window.init(title, inner_size, position, visual_info);
965        
966        OpenglWindow {
967            first_draw: true,
968            window_id,
969            opening_repaint_count: 0,
970            cal_size: Vec2::default(),
971            window_geom: xlib_window.get_window_geom(),
972            xlib_window
973        }
974    }
975    
976    pub fn resize_framebuffer(&mut self, _opengl_cx: &OpenglCx) -> bool {
977        let cal_size = Vec2 {
978            x: self.window_geom.inner_size.x * self.window_geom.dpi_factor,
979            y: self.window_geom.inner_size.y * self.window_geom.dpi_factor
980        };
981        if self.cal_size != cal_size {
982            self.cal_size = cal_size;
983            // resize the framebuffer
984            true
985        }
986        else {
987            false
988        }
989    }
990    
991}
992
993#[derive(Default, Clone)]
994pub struct OpenglAttribute {
995    pub loc: u32,
996    pub size: i32,
997    pub offset: usize,
998    pub stride: i32
999}
1000
1001#[derive(Default, Clone)]
1002pub struct OpenglUniform {
1003    pub loc: i32,
1004    pub name: String,
1005    pub size: usize
1006}
1007/*
1008#[derive(Default, Clone)]
1009pub struct OpenglTextureSlot {
1010    pub loc: isize,
1011    pub name: String
1012}
1013*/
1014#[derive(Clone, Default)]
1015pub struct CxPlatformView {
1016}
1017
1018#[derive(Default, Clone)]
1019pub struct CxPlatformDrawCall {
1020    pub inst_vbuf: OpenglBuffer,
1021    pub vao_shader_id: Option<usize>,
1022    pub vao: Option<u32>
1023}
1024
1025impl CxPlatformDrawCall {
1026    
1027    pub fn check_vao(&mut self, shader_id: usize, shp: &CxPlatformShader) {
1028        if self.vao_shader_id.is_none() || self.vao_shader_id.unwrap() != shader_id {
1029            self.free_vao();
1030            // create the VAO
1031            unsafe {
1032                let mut vao = std::mem::MaybeUninit::uninit();
1033                gl::GenVertexArrays(1, vao.as_mut_ptr());
1034                let vao = vao.assume_init();
1035                gl::BindVertexArray(vao);
1036                
1037                // bind the vertex and indexbuffers
1038                gl::BindBuffer(gl::ARRAY_BUFFER, shp.geom_vbuf.gl_buffer.unwrap());
1039                for attr in &shp.geom_attribs {
1040                    gl::VertexAttribPointer(attr.loc, attr.size, gl::FLOAT, 0, attr.stride, attr.offset as *const () as *const _);
1041                    gl::EnableVertexAttribArray(attr.loc);
1042                }
1043                
1044                gl::BindBuffer(gl::ARRAY_BUFFER, self.inst_vbuf.gl_buffer.unwrap());
1045                
1046                for attr in &shp.inst_attribs {
1047                    gl::VertexAttribPointer(attr.loc, attr.size, gl::FLOAT, 0, attr.stride, attr.offset as *const () as *const _);
1048                    gl::EnableVertexAttribArray(attr.loc);
1049                    gl::VertexAttribDivisor(attr.loc, 1 as gl::types::GLuint);
1050                }
1051                
1052                // bind the indexbuffer
1053                gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, shp.geom_ibuf.gl_buffer.unwrap());
1054                gl::BindVertexArray(0);
1055                
1056                self.vao_shader_id = Some(shader_id);
1057                self.vao = Some(vao);
1058            }
1059        }
1060    }
1061    
1062    fn free_vao(&mut self) {
1063        unsafe {
1064            if let Some(mut vao) = self.vao {
1065                gl::DeleteVertexArrays(1, &mut vao);
1066                self.vao = None;
1067            }
1068        }
1069    }
1070}
1071
1072#[derive(Default, Clone)]
1073pub struct CxPlatformTexture {
1074    pub alloc_desc: TextureDesc,
1075    pub width: u64,
1076    pub height: u64,
1077    pub gl_texture: Option<u32>,
1078}
1079
1080#[derive(Default, Clone)]
1081pub struct CxPlatformPass {
1082    pub gl_framebuffer: Option<u32>
1083}
1084
1085#[derive(Default, Clone)]
1086pub struct OpenglBuffer {
1087    pub gl_buffer: Option<u32>
1088}
1089
1090impl OpenglBuffer {
1091    
1092    pub fn alloc_gl_buffer(&mut self) {
1093        unsafe {
1094            let mut gl_buffer = std::mem::MaybeUninit::uninit();
1095            gl::GenBuffers(1, gl_buffer.as_mut_ptr());
1096            self.gl_buffer = Some(gl_buffer.assume_init());
1097        }
1098    }
1099    
1100    pub fn update_with_f32_data(&mut self, _opengl_cx: &OpenglCx, data: &Vec<f32>) {
1101        if self.gl_buffer.is_none() {
1102            self.alloc_gl_buffer();
1103        }
1104        unsafe {
1105            gl::BindBuffer(gl::ARRAY_BUFFER, self.gl_buffer.unwrap());
1106            gl::BufferData(gl::ARRAY_BUFFER, (data.len() * mem::size_of::<f32>()) as gl::types::GLsizeiptr, data.as_ptr() as *const _, gl::STATIC_DRAW);
1107        }
1108    }
1109    
1110    pub fn update_with_u32_data(&mut self, _opengl_cx: &OpenglCx, data: &Vec<u32>) {
1111        if self.gl_buffer.is_none() {
1112            self.alloc_gl_buffer();
1113        }
1114        unsafe {
1115            gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, self.gl_buffer.unwrap());
1116            gl::BufferData(gl::ELEMENT_ARRAY_BUFFER, (data.len() * mem::size_of::<u32>()) as gl::types::GLsizeiptr, data.as_ptr() as *const _, gl::STATIC_DRAW);
1117        }
1118    }
1119}