makepad_platform/os/linux/
opengl.rs

1use {
2    self::super::gl_sys, 
3    gl_sys::LibGl,
4    crate::{
5        cx::{Cx, OsType}, 
6        draw_list::DrawListId, 
7        draw_shader::{CxDrawShaderMapping, DrawShaderTextureInput}, 
8        event::{Event, TextureHandleReadyEvent}, 
9        makepad_live_id::*, 
10        makepad_math::{DVec2, Vec4}, 
11        makepad_shader_compiler::generate_glsl, 
12        pass::{PassClearColor, PassClearDepth, PassId}, 
13        texture::{CxTexture, Texture, TextureFormat, TexturePixel, TextureUpdated}
14    }, 
15    std::{
16        ffi::{c_char, CStr}, fs::{remove_file, File}, io::prelude::*, mem, ptr
17    }
18};
19
20impl Cx {
21    
22    pub (crate) fn render_view(
23        &mut self,
24        pass_id: PassId,
25        draw_list_id: DrawListId,
26        zbias: &mut f32,
27        zbias_step: f32,
28    ) {
29        let mut to_dispatch = Vec::new();
30        //self.draw_lists[draw_list_id].draw_list_uniforms.view_transform = Mat4::identity();
31        // tad ugly otherwise the borrow checker locks 'self' and we can't recur
32        let draw_items_len = self.draw_lists[draw_list_id].draw_items.len();
33        
34        #[cfg(use_gles_3)]
35        {
36            let draw_list = &mut self.draw_lists[draw_list_id];
37            draw_list.os.draw_list_uniforms.update_uniform_buffer(self.os.gl(), draw_list.draw_list_uniforms.as_slice());
38        }
39        
40        for draw_item_id in 0..draw_items_len {
41            if let Some(sub_list_id) = self.draw_lists[draw_list_id].draw_items[draw_item_id].kind.sub_list() {
42                self.render_view(
43                    pass_id,
44                    sub_list_id,
45                    zbias,
46                    zbias_step,
47                );
48            }
49            else {
50                let gl = self.os.gl();
51                
52                let draw_list = &mut self.draw_lists[draw_list_id];
53                let draw_item = &mut draw_list.draw_items[draw_item_id];
54                
55                let draw_call = if let Some(draw_call) = draw_item.kind.draw_call_mut() {
56                    draw_call
57                }else {
58                    continue;
59                };
60                
61                let sh = &self.draw_shaders.shaders[draw_call.draw_shader.draw_shader_id];
62                if sh.os_shader_id.is_none() { // shader didnt compile somehow
63                    continue;
64                }
65                if sh.mapping.uses_time {
66                    self.demo_time_repaint = true;
67                }
68                let shp = &mut self.draw_shaders.os_shaders[sh.os_shader_id.unwrap()];
69                
70                let shader_variant = self.passes[pass_id].os.shader_variant;
71                                 
72                if shp.gl_shader[shader_variant].is_none(){
73                    shp.gl_shader[shader_variant] = Some(GlShader::new(
74                        self.os.gl(),
75                        &shp.vertex[shader_variant],
76                        &shp.pixel[shader_variant],
77                        &sh.mapping,
78                        &self.os_type,
79                    ));
80                }
81                let shgl = shp.gl_shader[shader_variant].as_ref().unwrap();
82                
83                if draw_call.instance_dirty || draw_item.os.inst_vb.gl_buffer.is_none(){
84                    draw_call.instance_dirty = false;
85                    draw_item.os.inst_vb.update_array_buffer(gl,draw_item.instances.as_ref().unwrap());
86                }
87                
88                // update the zbias uniform if we have it.
89                draw_call.draw_call_uniforms.set_zbias(*zbias);
90                *zbias += zbias_step;
91                
92                #[cfg(use_gles_3)]
93                draw_item.os.draw_call_uniforms.update_uniform_buffer(gl, draw_call.draw_call_uniforms.as_slice());
94                
95                let instances = (draw_item.instances.as_ref().unwrap().len() / sh.mapping.instances.total_slots) as u64;
96                
97                if instances == 0 {
98                    continue;
99                }
100                
101                let geometry_id = if let Some(geometry_id) = draw_call.geometry_id {geometry_id}
102                else {
103                    continue;
104                };
105                                
106                let geometry = &mut self.geometries[geometry_id];
107                if geometry.dirty || geometry.os.vb.gl_buffer.is_none() || geometry.os.ib.gl_buffer.is_none() {
108                    geometry.os.vb.update_array_buffer(gl,&geometry.vertices);
109                    geometry.os.ib.update_index_buffer(gl,&geometry.indices);
110                    geometry.dirty = false;
111                }      
112                
113                let indices = geometry.indices.len();
114                
115                if draw_call.uniforms_dirty {
116                    draw_call.uniforms_dirty = false;
117                    #[cfg(use_gles_3)]
118                    if draw_call.user_uniforms.len() != 0 {
119                        draw_item.os.user_uniforms.update_uniform_buffer(gl, &mut draw_call.user_uniforms);
120                    }
121                }
122                          
123                // update geometry?
124                let geometry = &mut self.geometries[geometry_id];
125                
126                // lets check if our vao is still valid
127                if draw_item.os.vao.is_none() {
128                    draw_item.os.vao = Some(CxOsDrawCallVao {
129                        vao: None,
130                        shader_id: None,
131                        inst_vb: None,
132                        geom_vb: None,
133                        geom_ib: None,
134                    });
135                }
136                                
137                let vao = draw_item.os.vao.as_mut().unwrap();
138                if vao.inst_vb != draw_item.os.inst_vb.gl_buffer
139                    || vao.geom_vb != geometry.os.vb.gl_buffer
140                    || vao.geom_ib != geometry.os.ib.gl_buffer
141                    || vao.shader_id != Some(draw_call.draw_shader.draw_shader_id) {
142                        
143                    if let Some(vao) = vao.vao.take(){
144                        unsafe{(gl.glDeleteVertexArrays)(1, &vao)};
145                    }
146                        
147                    vao.vao = Some(unsafe {
148                        let mut vao = 0u32;
149                        (gl.glGenVertexArrays)(1, &mut vao);
150                        vao
151                    });    
152                    
153                    vao.shader_id = Some(draw_call.draw_shader.draw_shader_id);
154                    vao.inst_vb = draw_item.os.inst_vb.gl_buffer;
155                    vao.geom_vb = geometry.os.vb.gl_buffer;
156                    vao.geom_ib = geometry.os.ib.gl_buffer;
157                    unsafe {
158                        (gl.glBindVertexArray)(vao.vao.unwrap());
159                        // bind the vertex and indexbuffers
160                        (gl.glBindBuffer)(gl_sys::ARRAY_BUFFER, vao.geom_vb.unwrap());
161                        for attr in &shgl.geometries {
162                            if let Some(loc) = attr.loc{
163                                (gl.glVertexAttribPointer)(loc, attr.size, gl_sys::FLOAT, 0, attr.stride, attr.offset as *const () as *const _);
164                                (gl.glEnableVertexAttribArray)(loc);
165                            }
166                        }
167                        (gl.glBindBuffer)(gl_sys::ARRAY_BUFFER, vao.inst_vb.unwrap());
168                        for attr in &shgl.instances {
169                            if let Some(loc) = attr.loc{
170                                (gl.glVertexAttribPointer)(loc, attr.size, gl_sys::FLOAT, 0, attr.stride, attr.offset as *const () as *const _);
171                                (gl.glEnableVertexAttribArray)(loc);
172                                (gl.glVertexAttribDivisor)(loc, 1 as gl_sys::GLuint);
173                            }
174                        }
175                        
176                        // bind the indexbuffer
177                        (gl.glBindBuffer)(gl_sys::ELEMENT_ARRAY_BUFFER, vao.geom_ib.unwrap());
178                        (gl.glBindVertexArray)(0);
179                        (gl.glBindBuffer)(gl_sys::ARRAY_BUFFER, 0);
180                        (gl.glBindBuffer)(gl_sys::ELEMENT_ARRAY_BUFFER, 0);
181                    }
182                }
183                unsafe {
184                    (gl.glUseProgram)(shgl.program);
185                    (gl.glBindVertexArray)(draw_item.os.vao.as_ref().unwrap().vao.unwrap());
186                    let instances = (draw_item.instances.as_ref().unwrap().len() / sh.mapping.instances.total_slots) as u64;
187                    // bind all uniform buffers
188                    #[cfg(use_gles_3)]{
189                        shgl.uniforms.pass_uniforms_binding.bind_buffer(gl, &self.passes[pass_id].os.pass_uniforms);
190                        shgl.uniforms.draw_list_uniforms_binding.bind_buffer(gl, &draw_list.os.draw_list_uniforms);
191                        shgl.uniforms.draw_call_uniforms_binding.bind_buffer(gl, &draw_item.os.draw_call_uniforms);
192                        if draw_call.user_uniforms.len() != 0 {
193                            shgl.uniforms.user_uniforms_binding.bind_buffer(gl, &draw_item.os.user_uniforms);
194                        }
195                        shgl.uniforms.live_uniforms_binding.bind_buffer(gl, &shgl.uniforms.live_uniforms);
196                    }
197                    #[cfg(not(use_gles_3))]{
198                        let pass_uniforms = self.passes[pass_id].pass_uniforms.as_slice();
199                        let draw_list_uniforms = draw_list.draw_list_uniforms.as_slice();
200                        let draw_call_uniforms = draw_call.draw_call_uniforms.as_slice();
201                        GlShader::set_uniform_array(gl, &shgl.uniforms.pass_uniforms, pass_uniforms);
202                        GlShader::set_uniform_array(gl, &shgl.uniforms.draw_list_uniforms, draw_list_uniforms);
203                        GlShader::set_uniform_array(gl, &shgl.uniforms.draw_call_uniforms, draw_call_uniforms);
204                        GlShader::set_uniform_array(gl, &shgl.uniforms.user_uniforms, &draw_call.user_uniforms);
205                    }
206                    
207                    // give openXR a chance to set its depth texture
208                    #[cfg(target_os="android")]
209                    if self.os.in_xr_mode{self.os.openxr.depth_texture_hook(gl, shgl, &sh.mapping)};
210                    
211                    for i in 0..sh.mapping.textures.len() {
212                        let texture_id = if let Some(texture) = &draw_call.texture_slots[i] {
213                            texture.texture_id()
214                        }else {
215                            continue;
216                        };
217                        let cxtexture = &mut self.textures[texture_id];
218
219                        if cxtexture.format.is_vec(){
220                            cxtexture.update_vec_texture(gl, &self.os_type);
221                        } else if cxtexture.format.is_video() {
222                            let is_initial_setup = cxtexture.setup_video_texture(gl);
223                            if is_initial_setup {
224                                let e = Event::TextureHandleReady(
225                                    TextureHandleReadyEvent {
226                                        texture_id,
227                                        handle: cxtexture.os.gl_texture.unwrap()
228                                    }
229                                );
230                                to_dispatch.push(e);
231                            }
232                        }
233                    }
234                    for i in 0..sh.mapping.textures.len() {
235                        let texture_id = if let Some(texture) = &draw_call.texture_slots[i] {
236                            texture.texture_id()
237                        }else {
238                            continue;
239                        };
240                        let cxtexture = &mut self.textures[texture_id];
241                        let gl = self.os.gl();
242                        // get the loc
243                        (gl.glActiveTexture)(gl_sys::TEXTURE0 + i as u32);
244                        if let Some(texture) = cxtexture.os.gl_texture {
245                            // Video playback with SurfaceTexture requires TEXTURE_EXTERNAL_OES, for any other format we assume regular 2D textures
246                            match cxtexture.format {
247                                TextureFormat::VideoRGB => (gl.glBindTexture)(gl_sys::TEXTURE_EXTERNAL_OES, texture),
248                                _ => (gl.glBindTexture)(gl_sys::TEXTURE_2D, texture)     
249                            }
250                        }
251                        else {
252                            match cxtexture.format {
253                                TextureFormat::VideoRGB => (gl.glBindTexture)(gl_sys::TEXTURE_EXTERNAL_OES, 0),
254                                _ => (gl.glBindTexture)(gl_sys::TEXTURE_2D, 0)     
255                            }
256                        }
257                        if let Some(loc) = shgl.textures[i].loc{
258                            (gl.glUniform1i)(loc, i as i32);
259                        }
260                    }
261                    
262                    (gl.glDrawElementsInstanced)(
263                        gl_sys::TRIANGLES,
264                        indices as i32,
265                        gl_sys::UNSIGNED_INT,
266                        ptr::null(),
267                        instances as i32
268                    );
269                    (gl.glBindVertexArray)(0);
270                    (gl.glUseProgram)(0);
271                }
272                
273            }
274        }
275        for event in to_dispatch.iter() {
276            self.call_event_handler(&event);
277        }
278    }
279    
280    pub fn set_default_depth_and_blend_mode(gl: &LibGl) {
281        unsafe {
282            (gl.glEnable)(gl_sys::DEPTH_TEST);
283            (gl.glDepthFunc)(gl_sys::LEQUAL);
284            (gl.glBlendEquationSeparate)(gl_sys::FUNC_ADD, gl_sys::FUNC_ADD);
285            (gl.glBlendFuncSeparate)(gl_sys::ONE, gl_sys::ONE_MINUS_SRC_ALPHA, gl_sys::ONE, gl_sys::ONE_MINUS_SRC_ALPHA);
286            (gl.glEnable)(gl_sys::BLEND);
287        }
288    }
289    
290    pub fn setup_render_pass(&mut self, pass_id: PassId,) -> Option<(DVec2,f64)> {
291        
292        let dpi_factor = self.passes[pass_id].dpi_factor.unwrap();
293        let pass_rect = self.get_pass_rect(pass_id, dpi_factor).unwrap();
294        let pass = &mut self.passes[pass_id];
295        pass.paint_dirty = false;
296        
297        if pass_rect.size.x <0.5 || pass_rect.size.y < 0.5 {
298            return None
299        }
300        
301        pass.set_ortho_matrix(pass_rect.pos, pass_rect.size);
302        pass.set_dpi_factor(dpi_factor);
303        
304        #[cfg(use_gles_3)]
305        pass.os.pass_uniforms.update_uniform_buffer(self.os.gl(), pass.pass_uniforms.as_slice());
306        
307        Some((pass_rect.size, dpi_factor))
308    }
309
310    pub fn draw_pass_to_texture(
311        &mut self,
312        pass_id: PassId,
313        override_pass_texture: Option<&Texture>,
314    ) {
315        let draw_list_id = self.passes[pass_id].main_draw_list_id.unwrap();
316        
317        let (pass_size, dpi_factor) = if let Some(pz) = self.setup_render_pass(pass_id) {
318            pz
319        }
320        else {
321            return
322        };
323        
324        let mut clear_color = Vec4::default();
325        let mut clear_depth = 1.0;
326        let mut clear_flags = 0;
327        let gl = self.os.gl();
328        // make a framebuffer
329        if self.passes[pass_id].os.gl_framebuffer.is_none() {
330            unsafe {
331                let mut gl_framebuffer = std::mem::MaybeUninit::uninit();
332                (gl.glGenFramebuffers)(1, gl_framebuffer.as_mut_ptr());
333                self.passes[pass_id].os.gl_framebuffer = Some(gl_framebuffer.assume_init());
334            }
335        }
336        
337        // bind the framebuffer
338        unsafe {
339            (gl.glBindFramebuffer)(gl_sys::FRAMEBUFFER, self.passes[pass_id].os.gl_framebuffer.unwrap());
340        }
341
342        let color_textures_from_fb_texture = override_pass_texture.map(|texture| {
343            [crate::pass::CxPassColorTexture {
344                clear_color: PassClearColor::ClearWith(self.passes[pass_id].clear_color),
345                texture: texture.clone(),
346            }]
347        });
348        let color_textures = color_textures_from_fb_texture
349            .as_ref().map_or(&self.passes[pass_id].color_textures[..], |xs| &xs[..]);
350
351        for (index, color_texture) in color_textures.iter().enumerate() {
352            match color_texture.clear_color {
353                PassClearColor::InitWith(_clear_color) => {
354                    let cxtexture = &mut self.textures[color_texture.texture.texture_id()];
355                    let size = dpi_factor * pass_size;
356                    cxtexture.update_render_target(gl, size.x as usize, size.y as usize);
357                    if cxtexture.take_initial(){
358                       clear_color = _clear_color;
359                       clear_flags |= gl_sys::COLOR_BUFFER_BIT;
360                    }
361                },
362                PassClearColor::ClearWith(_clear_color) => {
363                    let cxtexture = &mut self.textures[color_texture.texture.texture_id()];
364                    let size = dpi_factor * pass_size;
365                    cxtexture.update_render_target(gl, size.x as usize, size.y as usize);
366                    clear_color = _clear_color;
367                    clear_flags |= gl_sys::COLOR_BUFFER_BIT;
368                }
369            }
370            if let Some(gl_texture) = self.textures[color_texture.texture.texture_id()].os.gl_texture {
371                unsafe {
372                    (gl.glFramebufferTexture2D)(gl_sys::FRAMEBUFFER, gl_sys::COLOR_ATTACHMENT0 + index as u32, gl_sys::TEXTURE_2D, gl_texture, 0);
373                }
374            }
375        }
376        
377        // attach/clear depth buffers, if any
378        if let Some(depth_texture) = &self.passes[pass_id].depth_texture {
379            match self.passes[pass_id].clear_depth {
380                PassClearDepth::InitWith(_clear_depth) => {
381                    let cxtexture = &mut self.textures[depth_texture.texture_id()];
382                    let size = dpi_factor * pass_size;
383                    cxtexture.update_depth_stencil(gl, size.x as usize, size.y as usize);
384                    if cxtexture.take_initial(){
385                        clear_depth = _clear_depth;
386                        clear_flags |= gl_sys::DEPTH_BUFFER_BIT;
387                    }
388                },
389                PassClearDepth::ClearWith(_clear_depth) => {
390                    let cxtexture = &mut self.textures[depth_texture.texture_id()];
391                    let size = dpi_factor * pass_size;
392                    cxtexture.update_depth_stencil(gl, size.x as usize, size.y as usize);
393                    clear_depth = _clear_depth;
394                    clear_flags |= gl_sys::DEPTH_BUFFER_BIT;
395                }
396            }
397        }
398        else {
399            /* unsafe { // BUGFIX. we have to create a depthbuffer for rtt without depthbuffer use otherwise it fails if there is another pass with depth
400                if self.passes[pass_id].os.gl_bugfix_depthbuffer.is_none() {
401                    let mut gl_renderbuf = std::mem::MaybeUninit::uninit();
402                    (gl.glGenRenderbuffers)(1, gl_renderbuf.as_mut_ptr());
403                    let gl_renderbuffer = gl_renderbuf.assume_init();
404                    (gl.glBindRenderbuffer)(gl_sys::RENDERBUFFER, gl_renderbuffer);
405                    (gl.glRenderbufferStorage)(
406                        gl_sys::RENDERBUFFER,
407                        gl_sys::DEPTH_COMPONENT16,
408                        (pass_size.x * dpi_factor) as i32,
409                        (pass_size.y * dpi_factor) as i32
410                    );
411                    (gl.glBindRenderbuffer)(gl_sys::RENDERBUFFER, 0);
412                    self.passes[pass_id].os.gl_bugfix_depthbuffer = Some(gl_renderbuffer);
413                }
414                clear_depth = 1.0;
415                clear_flags |= gl_sys::DEPTH_BUFFER_BIT;
416                (gl.glDisable)(gl_sys::DEPTH_TEST);
417                (gl.glFramebufferRenderbuffer)(gl_sys::FRAMEBUFFER, gl_sys::DEPTH_ATTACHMENT, gl_sys::RENDERBUFFER, self.passes[pass_id].os.gl_bugfix_depthbuffer.unwrap());
418            }*/
419        }
420
421        // HACK(eddyb) drain error queue, so that we can check erors below.
422        //while unsafe { (gl.glGetError)() } != 0 {}
423
424        unsafe {
425            let (x, mut y) = (0, 0);
426            let width = (pass_size.x * dpi_factor) as u32;
427            let height = (pass_size.y * dpi_factor) as u32;
428
429            // HACK(eddyb) to try and match DirectX and Metal conventions, we
430            // need the viewport to be placed on the other end of the Y axis.
431            if let [color_texture] = color_textures {
432                let cxtexture = &mut self.textures[color_texture.texture.texture_id()];
433                if cxtexture.os.gl_texture.is_some() {
434                    let alloc_height = cxtexture.alloc.as_ref().unwrap().height as u32;
435                    if alloc_height > height {
436                        y = alloc_height - height;
437                    }
438                }
439            }
440
441            (gl.glViewport)(x as i32, y as i32, width as i32, height as i32);
442            
443           //assert_eq!((gl.glGetError)(), 0, "glViewport({x}, {y}, {width}, {height}) failed");
444        }
445
446        if clear_flags != 0 {
447            unsafe {
448                if clear_flags & gl_sys::DEPTH_BUFFER_BIT != 0 {
449                    (gl.glClearDepthf)(clear_depth);
450                }
451                (gl.glClearColor)(clear_color.x, clear_color.y, clear_color.z, clear_color.w);
452                (gl.glClear)(clear_flags);
453            }
454        }
455        Self::set_default_depth_and_blend_mode(self.os.gl());
456        
457        let mut zbias = 0.0;
458        let zbias_step = self.passes[pass_id].zbias_step;
459        
460        self.render_view(
461            pass_id,
462            draw_list_id,
463            &mut zbias,
464            zbias_step,
465        );
466        
467        unsafe {
468            (self.os.gl().glBindFramebuffer)(gl_sys::FRAMEBUFFER, 0);
469            //(gl.glFinish)();
470        }
471    }
472    
473    pub fn opengl_compile_shaders(&mut self) {
474        //let p = profile_start();
475        for draw_shader_ptr in &self.draw_shaders.compile_set {
476            if let Some(item) = self.draw_shaders.ptr_to_item.get(&draw_shader_ptr) {
477                let cx_shader = &mut self.draw_shaders.shaders[item.draw_shader_id];
478                let draw_shader_def = self.shader_registry.draw_shader_defs.get(&draw_shader_ptr);
479                
480                #[cfg(use_gles_3)]
481                let glsl_options = generate_glsl::GlslOptions{
482                    use_ovr_multiview: self.os_type.has_xr_mode(),
483                    use_uniform_buffers: true,
484                    use_inout: true,
485                };
486                #[cfg(not(use_gles_3))]
487                let glsl_options = generate_glsl::GlslOptions{
488                    use_ovr_multiview: false,
489                    use_uniform_buffers: false,
490                    use_inout: false,
491                };
492                                
493                let vertex = generate_glsl::generate_vertex_shader(
494                    draw_shader_def.as_ref().unwrap(),
495                    &cx_shader.mapping.const_table,
496                    &self.shader_registry,
497                    glsl_options
498                );
499                
500                let pixel = generate_glsl::generate_pixel_shader(
501                    draw_shader_def.as_ref().unwrap(),
502                    &cx_shader.mapping.const_table,
503                    &self.shader_registry,
504                   glsl_options
505                );
506                
507                if cx_shader.mapping.flags.debug {
508                    crate::log!("{}\n{}", vertex, pixel);
509                }
510                
511                // lets see if we have the shader already
512                for (index, ds) in self.draw_shaders.os_shaders.iter().enumerate() {
513                    if ds.vertex[0] == vertex && ds.pixel[0] == pixel {
514                        cx_shader.os_shader_id = Some(index);
515                        break;
516                    }
517                }
518                
519                if cx_shader.os_shader_id.is_none() {
520                    let shp = CxOsDrawShader::new(self.os.gl(), &vertex, &pixel, &self.os_type);
521                    cx_shader.os_shader_id = Some(self.draw_shaders.os_shaders.len());
522                    self.draw_shaders.os_shaders.push(shp);
523                }
524            }
525        }
526        self.draw_shaders.compile_set.clear();
527    }
528/*
529    pub fn maybe_warn_hardware_support(&self) {
530        // Temporary warning for Adreno failing at compiling shaders that use samplerExternalOES.
531        
532    }*/
533}
534
535const NUM_SHADER_VARIANTS:usize = 2;
536
537pub struct CxOsDrawShader {
538    pub gl_shader: [Option<GlShader>;NUM_SHADER_VARIANTS],
539    pub in_vertex: String,
540    pub in_pixel: String,
541    pub vertex: [String;NUM_SHADER_VARIANTS],
542    pub pixel: [String;NUM_SHADER_VARIANTS],
543    //pub const_table_uniforms: OpenglBuffer,
544    pub live_uniforms: OpenglBuffer
545}
546
547#[cfg(not(use_gles_3))]
548pub struct GlShaderUniforms{
549    pub pass_uniforms: OpenglUniform,
550    pub draw_list_uniforms: OpenglUniform,
551    pub draw_call_uniforms: OpenglUniform,
552    pub user_uniforms: OpenglUniform,
553    pub live_uniforms: OpenglUniform,
554    pub const_table_uniform: OpenglUniform,
555}
556#[cfg(not(use_gles_3))]
557impl GlShaderUniforms{
558    fn new(gl:&LibGl, program:u32, _mapping:&CxDrawShaderMapping)->Self{
559        Self{
560            pass_uniforms: GlShader::opengl_get_uniform(gl, program, "pass_table"),
561            draw_list_uniforms: GlShader::opengl_get_uniform(gl, program, "draw_list_table"),
562            draw_call_uniforms: GlShader::opengl_get_uniform(gl, program, "draw_call_table"),
563            user_uniforms: GlShader::opengl_get_uniform(gl, program, "user_table"),
564            live_uniforms: GlShader::opengl_get_uniform(gl, program, "live_table"),
565            const_table_uniform: GlShader::opengl_get_uniform(gl, program, "const_table"),
566        }
567    }
568}
569
570#[cfg(use_gles_3)]
571pub struct GlShaderUniforms{
572    pub pass_uniforms_binding: OpenglUniformBlockBinding,
573    pub draw_list_uniforms_binding: OpenglUniformBlockBinding,
574    pub draw_call_uniforms_binding: OpenglUniformBlockBinding,
575    pub user_uniforms_binding: OpenglUniformBlockBinding,
576    pub live_uniforms_binding: OpenglUniformBlockBinding,
577    pub const_table_uniform: OpenglUniform,
578    pub live_uniforms: OpenglBuffer,
579}
580#[cfg(use_gles_3)]
581impl GlShaderUniforms{
582    fn new(gl:&LibGl, program:u32, mapping:&CxDrawShaderMapping)->Self{
583        let mut live_uniforms = OpenglBuffer::default();
584        live_uniforms.update_uniform_buffer(gl, mapping.live_uniforms_buf.as_ref());
585        
586        Self{
587            pass_uniforms_binding: GlShader::opengl_get_uniform_block_binding(gl, program, "passUniforms"),
588            draw_list_uniforms_binding: GlShader::opengl_get_uniform_block_binding(gl, program, "draw_listUniforms"),
589            draw_call_uniforms_binding: GlShader::opengl_get_uniform_block_binding(gl, program, "draw_callUniforms"),
590            user_uniforms_binding: GlShader::opengl_get_uniform_block_binding(gl, program, "userUniforms"),
591            live_uniforms_binding: GlShader::opengl_get_uniform_block_binding(gl, program, "liveUniforms"),
592            const_table_uniform: GlShader::opengl_get_uniform(gl, program, "const_table"),
593            live_uniforms,
594        }
595    }
596}
597
598pub struct GlShader {
599    pub program: u32,
600    pub geometries: Vec<OpenglAttribute>,
601    pub instances: Vec<OpenglAttribute>,
602    pub textures: Vec<OpenglUniform>,
603    pub xr_depth_texture: OpenglUniform, 
604    // all these things need to be uniform buffers
605    pub uniforms: GlShaderUniforms
606        
607}
608
609impl GlShader{
610    pub fn new(gl: &LibGl, vertex: &str, pixel: &str, mapping: &CxDrawShaderMapping, os_type: &OsType)->Self{
611        // On OpenHarmony, re-using cached shaders doesn't work properly yet.
612        #[cfg(ohos_sim)]
613        unsafe fn read_cache(_gl: &LibOpenGl, _vertex: &str, _pixel: &str, _os_type: &OsType) -> Option<gl_sys::GLuint> {
614            None
615        }
616                
617        #[cfg(not(ohos_sim))]
618        unsafe fn read_cache(gl: &LibGl, vertex: &str, pixel: &str, os_type: &OsType) -> Option<gl_sys::GLuint> {
619            if let Some(cache_dir) = os_type.get_cache_dir() {
620                let shader_hash = live_id!(shader).str_append(&vertex).str_append(&pixel);
621                let mut base_filename = format!("{}/shader_{:08x}", cache_dir, shader_hash.0);
622
623                match os_type {
624                    OsType::Android(params) => {
625                        base_filename = format!("{}_av{}_bn{}_kv{}", base_filename, params.android_version, params.build_number, params.kernel_version);
626                    },
627                    _ => (),
628                };
629
630                let filename = format!("{}.bin", base_filename);
631
632                if let Ok(mut cache_file) = File::open(&filename) {
633                    let mut binary = Vec::new();
634                    let mut format_bytes = [0u8; 4];
635                    match cache_file.read(&mut format_bytes) {
636                        Ok(_bytes_read) => {
637                            let binary_format = u32::from_be_bytes(format_bytes);
638                            match cache_file.read_to_end(&mut binary) {
639                                Ok(_full_bytes) => {
640                                    let mut version_consistency_conflict = false;
641                                    // On Android, invalidate the cached file if there have been significant system updates
642                                    match os_type {
643                                        OsType::Android(params) => {
644                                            let current_filename = format!("{}/shader_{:08x}_av{}_bn{}_kv{}.bin", cache_dir, shader_hash.0, params.android_version, params.build_number, params.kernel_version);
645                                            version_consistency_conflict = filename != current_filename;
646                                        },
647                                        _ => (),
648                                    };
649                    
650                                    if !version_consistency_conflict {
651                                        let program = (gl.glCreateProgram)();
652                                        (gl.glProgramBinary)(program, binary_format, binary.as_ptr() as *const _, binary.len() as i32);
653                                        if let Some(error) = GlShader::opengl_has_shader_error(gl, false, program as usize, "") {
654                                            crate::error!("ERROR::SHADER::CACHE::PROGRAM_BINARY_FAILED\n{}", error);
655                                            return None;
656                                        }
657                                        return Some(program);
658                                    } else {
659                                        // Version mismatch, delete the old cache file
660                                        let _ = remove_file(&filename);
661                                    }
662                                }
663                                Err(e) => {
664                                    crate::warning!("Failed to read the full shader cache file {filename}, error: {e}");
665                                }
666                            }
667                        }
668                        Err(e) => {
669                            crate::warning!("Failed to read format bytes from shader cache file {filename}, error: {e}");
670                        }
671                    }
672                } else {
673                    // crate::debug!("File was not in shader cache: {filename}");
674                }
675            } else {
676                //crate::warning!("No cache directory available for shader cache");
677            }
678            None
679        }
680        
681        unsafe {
682            let program = if let Some(program) = read_cache(gl, &vertex,&pixel,os_type){
683                program
684            }
685            else{ 
686                let vs = (gl.glCreateShader)(gl_sys::VERTEX_SHADER);
687                (gl.glShaderSource)(vs, 1, [vertex.as_ptr() as *const _].as_ptr(), ptr::null());
688                (gl.glCompileShader)(vs);
689                //println!("{}", Self::opengl_get_info_log(true, vs as usize, &vertex));
690                if let Some(error) = Self::opengl_has_shader_error(gl, true, vs as usize, &vertex) {
691                    panic!("ERROR::SHADER::VERTEX::COMPILATION_FAILED\n{}", error);
692                }
693                let fs = (gl.glCreateShader)(gl_sys::FRAGMENT_SHADER);
694                (gl.glShaderSource)(fs, 1, [pixel.as_ptr() as *const _].as_ptr(), ptr::null());
695                (gl.glCompileShader)(fs);
696                //println!("{}", Self::opengl_get_info_log(true, fs as usize, &fragment));
697                if let Some(error) = Self::opengl_has_shader_error(gl, true, fs as usize, &pixel) {
698                    panic!("ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n{}", error);
699                }
700                
701                let program = (gl.glCreateProgram)();
702                (gl.glAttachShader)(program, vs);
703                (gl.glAttachShader)(program, fs);
704                (gl.glLinkProgram)(program);
705                if let Some(error) = Self::opengl_has_shader_error(gl, false, program as usize, "") {
706                    panic!("ERROR::SHADER::LINK::COMPILATION_FAILED\n{}", error);
707                }
708                (gl.glDeleteShader)(vs);
709                (gl.glDeleteShader)(fs);
710            
711                #[cfg(not(ohos_sim))] // caching doesn't work properly on OpenHarmony
712                if let Some(cache_dir) = os_type.get_cache_dir() {
713                    let mut binary = Vec::new();
714                    let mut binary_len = 0;
715                    (gl.glGetProgramiv)(program, gl_sys::PROGRAM_BINARY_LENGTH, &mut binary_len);
716                    if binary_len != 0 {
717                        binary.resize(binary_len as usize, 0u8);
718                        let mut return_size = 0i32;
719                        let mut binary_format = 0u32;
720                        (gl.glGetProgramBinary)(program, binary.len() as i32, &mut return_size as *mut _, &mut binary_format as *mut _, binary.as_mut_ptr() as *mut _);
721                        if return_size != 0 {
722                            // crate::log!("GOT FORMAT {}", format);
723                            let shader_hash = live_id!(shader).str_append(&vertex).str_append(&pixel);
724                            let mut filename = format!("{}/shader_{:08x}", cache_dir, shader_hash.0);
725
726                            match os_type {
727                                OsType::Android(params) => {
728                                    filename = format!("{}_av{}_bn{}_kv{}", filename, params.android_version, params.build_number, params.kernel_version);
729                                },
730                                _ => (),
731                            };
732
733                            filename = format!("{}.bin", filename);
734
735                            binary.resize(return_size as usize, 0u8);
736                            match File::create(&filename) {
737                                Ok(mut cache)  => {
738                                    let _res1 = cache.write_all(&binary_format.to_be_bytes());
739                                    let _res2 = cache.write_all(&binary);
740                                    if _res1.is_err() || _res2.is_err() {
741                                        crate::error!("Failed to write shader binary to shader cache {filename}");
742                                    }
743                                }
744                                Err(e) => {
745                                    crate::error!("Failed to write shader cache to {filename}, error: {e}");
746                                }
747                            }
748                        }
749                    }
750                }
751                program
752            };
753            
754            (gl.glUseProgram)(program);
755            
756            let uniforms = GlShaderUniforms::new(gl, program, mapping);
757            
758            #[cfg(use_gles_3)]
759            uniforms.live_uniforms_binding.bind_buffer(gl, &uniforms.live_uniforms);
760            
761            #[cfg(not(use_gles_3))]
762            GlShader::set_uniform_array(gl, &uniforms.live_uniforms, &mapping.live_uniforms_buf);
763
764            let ct = &mapping.const_table.table;
765            if ct.len()>0 {
766                GlShader::set_uniform_array(gl, &uniforms.const_table_uniform, ct);
767            }
768            
769            (gl.glUseProgram)(0);              
770                        
771            let t = Self{
772                program,
773                geometries:Self::opengl_get_attributes(gl, program, "packed_geometry_", mapping.geometries.total_slots),
774                instances: Self::opengl_get_attributes(gl, program, "packed_instance_", mapping.instances.total_slots),
775                textures: Self::opengl_get_texture_slots(gl, program, &mapping.textures),
776                xr_depth_texture: Self::opengl_get_uniform(gl, program, "xr_depth_texture"),
777                uniforms
778            };
779            t
780        }
781    }
782
783    
784    pub fn set_uniform_array(gl: &LibGl, loc: &OpenglUniform, array: &[f32]) {
785        if let Some(loc) = loc.loc{
786            unsafe {
787                (gl.glUniform1fv)(loc, array.len() as i32, array.as_ptr());
788            }
789        }
790    }
791    
792    pub fn opengl_get_uniform(gl: &LibGl, program: u32, name: &str) -> OpenglUniform {
793        unsafe {
794            let loc = (gl.glGetUniformLocation)(program, std::ffi::CString::new(name).unwrap().as_ptr());
795            OpenglUniform {
796                loc: if loc < 0{None} else {Some(loc)},
797            }
798        }
799    }
800    
801    pub fn opengl_get_uniform_block_binding(gl: &LibGl, program: u32, name: &str) -> OpenglUniformBlockBinding{
802        unsafe {
803            let index = (gl.glGetUniformBlockIndex)(program, std::ffi::CString::new(name).unwrap().as_ptr()) as i32;
804            if index < 0{
805                return OpenglUniformBlockBinding {
806                    index: None,
807                }
808            }
809            // make the binding the same as the index for ease of use
810            (gl.glUniformBlockBinding)(program, index as u32, index as u32);
811            
812            OpenglUniformBlockBinding {
813                index: Some(index as u32),
814            }
815        }
816    }
817    
818    pub fn opengl_get_info_log(gl: &LibGl, compile: bool, shader: usize, source: &str) -> String {
819        unsafe {
820            let mut length = 0;
821            if compile {
822                (gl.glGetShaderiv)(shader as u32, gl_sys::INFO_LOG_LENGTH, &mut length);
823            } else {
824                (gl.glGetProgramiv)(shader as u32, gl_sys::INFO_LOG_LENGTH, &mut length);
825            }
826            let mut log = Vec::with_capacity(length as usize);
827            if compile {
828                (gl.glGetShaderInfoLog)(shader as u32, length, ptr::null_mut(), log.as_mut_ptr());
829            } else {
830                (gl.glGetProgramInfoLog)(shader as u32, length, ptr::null_mut(), log.as_mut_ptr());
831            }
832            log.set_len(length as usize);
833            let mut r = "".to_string();
834            r.push_str(CStr::from_ptr(log.as_ptr()).to_str().unwrap());
835            r.push_str("\n");
836            let split = source.split("\n");
837            for (line, chunk) in split.enumerate() {
838                r.push_str(&(line + 1).to_string());
839                r.push_str(":");
840                r.push_str(chunk);
841                r.push_str("\n");
842            }
843            r
844        }
845    }
846    
847    pub fn opengl_has_shader_error(gl: &LibGl, compile: bool, shader: usize, source: &str) -> Option<String> {
848        //None
849        unsafe {
850            
851            let mut success = gl_sys::TRUE as i32;
852            
853            if compile {
854                (gl.glGetShaderiv)(shader as u32, gl_sys::COMPILE_STATUS, &mut success);
855            }
856            else {
857                (gl.glGetProgramiv)(shader as u32, gl_sys::LINK_STATUS, &mut success);
858            };
859            
860            if success != gl_sys::TRUE as i32 {
861                Some(Self::opengl_get_info_log(gl, compile, shader, source))
862            }
863            else {
864                None
865            }
866        }
867    }
868    
869    pub fn opengl_get_attributes(gl: &LibGl, program: u32, prefix: &str, slots: usize) -> Vec<OpenglAttribute> {
870        let mut attribs = Vec::new();
871        
872        fn ceil_div4(base: usize) -> usize {
873            let r = base >> 2;
874            if base & 3 != 0 {
875                return r + 1
876            }
877            r
878        }
879        
880        let stride = (slots * mem::size_of::<f32>()) as i32;
881        let num_attr = ceil_div4(slots);
882        for i in 0..num_attr {
883            let mut name0 = prefix.to_string();
884            name0.push_str(&i.to_string());
885            name0.push_str("\0");
886            
887            let mut size = ((slots - i * 4)) as i32;
888            if size > 4 {
889                size = 4;
890            }
891            unsafe {
892                attribs.push(
893                    OpenglAttribute {
894                        name: name0.to_string(),
895                        loc: {
896                            let loc = (gl.glGetAttribLocation)(program, name0.as_ptr() as *const _);
897                            if loc < 0{None}else{Some(loc as u32)}
898                            
899                        },
900                        offset: (i * 4 * mem::size_of::<f32>()) as usize,
901                        size: size,
902                        stride: stride
903                    }
904                )
905            }
906        }
907        attribs
908    }
909    
910    
911    pub fn opengl_get_texture_slots(gl: &LibGl, program: u32, texture_slots: &Vec<DrawShaderTextureInput>) -> Vec<OpenglUniform> {
912        let mut gl_texture_slots = Vec::new();
913        
914        for slot in texture_slots {
915            let mut name0 = "ds_".to_string();
916            name0.push_str(&slot.id.to_string());
917            name0.push_str("\0");
918            unsafe {
919                let loc = (gl.glGetUniformLocation)(program, name0.as_ptr().cast());
920                // crate::warning!("opengl_get_texture_slots(): texture slot: ({:?}, {:?}), name0: {:X?}, loc: {loc:#X}", slot.id, slot.ty, name0.as_bytes());
921                gl_texture_slots.push(OpenglUniform { loc: if loc<0{None}else{Some(loc)} });
922            }
923        }
924        gl_texture_slots
925    }
926
927    pub fn free_resources(self, gl: &LibGl){
928        unsafe{
929            (gl.glDeleteShader)(self.program);
930        }
931    }
932}
933
934impl CxOsDrawShader {
935    pub fn new(gl:&LibGl, in_vertex: &str, in_pixel: &str, os_type: &OsType) -> Self {
936        // Check if GL_OES_EGL_image_external extension is available in the current device, otherwise do not attempt to use in the shaders.
937        let available_extensions = get_gl_string(gl, gl_sys::EXTENSIONS);
938        let is_external_texture_supported = available_extensions.split_whitespace().any(|ext| ext == "GL_OES_EGL_image_external");
939
940        // GL_OES_EGL_image_external is not well supported on Android emulators with macOS hosts.
941        // Because there's no bullet-proof way to check the emualtor host at runtime, we're currently disabling external texture support on all emulators.
942        let is_emulator = match os_type {
943            OsType::Android(params) => params.is_emulator,
944            OsType::OpenHarmony(_) => true, // TODO FIXME: detect whether we're running on an OHOS emulator
945            _ => false,
946        };
947
948        // Some Android devices running Adreno GPUs suddenly stopped compiling shaders when passing the samplerExternalOES sampler to texture2D functions. 
949        // This seems like a driver bug (no confirmation from Qualcomm yet).
950        // Therefore we're disabling the external texture support for Adreno until this is fixed.
951        let is_vendor_adreno = get_gl_string(gl, gl_sys::RENDERER).contains("Adreno"); 
952        
953        let (tex_ext_import, tex_ext_sampler) = if is_external_texture_supported && !is_vendor_adreno && !is_emulator {(
954            "#extension GL_OES_EGL_image_external : require\n",
955            "vec4 sample2dOES(samplerExternalOES sampler, vec2 pos){ return texture2D(sampler, vec2(pos.x, pos.y));}"
956        )}
957        else{
958            ("","")
959        };
960        
961        // Currently, these shaders are only compatible with `#version 100` through `#version 300 es`.
962        // Version 310 and later have removed/deprecated some features that we currently use:
963        // * error C7616: global function texture2D is removed after version 310
964        // * error C1121: transpose: function (builtin) redefinition/overload not allowed
965        // * error C5514: 'attribute' is deprecated and removed from this profile, use 'in/out' instead
966        // * error C7614: GLSL ES doesn't allow use of reserved word attribute
967        // * error C7614: GLSL ES doesn't allow use of reserved word varying
968        
969        // check if we are running in XR or not
970        
971        //#extension GL_OVR_multiview2 : require
972       // layout(num_views=2) in;
973        let depth_clip = "
974            uniform sampler2DArray xr_depth_texture;
975            vec4 depth_clip(vec4 world, vec4 color, float clip){
976                vec4 cube_depth_camera_position = pass.depth_projection[VIEW_ID] * pass.depth_view[VIEW_ID] * world;
977                
978                vec3 cube_depth_camera_position_hc = cube_depth_camera_position.xyz / cube_depth_camera_position.w;
979                cube_depth_camera_position_hc = cube_depth_camera_position_hc*0.5f + 0.5f;
980                
981                vec3 depth_view_coord = vec3(cube_depth_camera_position_hc.xy, VIEW_ID);
982                
983                gl_FragDepth = cube_depth_camera_position_hc.z;
984                
985                float depth_view_eye_z = texture(xr_depth_texture, depth_view_coord).r;
986                if(clip  < 0.5 || depth_view_eye_z >= cube_depth_camera_position_hc.z){
987                    return color;
988                }
989                return vec4(0.0,0.0,0.0,0.0);
990            }
991        ";
992        #[cfg(use_gles_3)]
993        let nop_depth_clip="
994            vec4 depth_clip(vec4 w, vec4 c, float clip){return c;}
995        ";
996        #[cfg(not(use_gles_3))]
997        let nop_depth_clip="";
998                
999        #[cfg(use_gles_3)]
1000        let (version, vertex_exts, pixel_exts, vertex_defs, pixel_defs, sampler) = if os_type.has_xr_mode(){(
1001            "#version 300 es",
1002            // Vertex shader
1003            "
1004            #define VIEW_ID 0
1005            #extension GL_OVR_multiview2 : require
1006            layout(num_views=2) in;
1007            ",
1008            // Pixel shader
1009            "
1010            #define VIEW_ID 0
1011            #extension GL_OVR_multiview2 : require
1012            ",
1013            "",
1014            "out vec4 fragColor;",
1015            "
1016            vec4 depth_clip(vec4 w, vec4 c, float clip);
1017            vec4 sample2d(sampler2D sampler, vec2 pos){{return texture(sampler, vec2(pos.x, pos.y));}} 
1018            vec4 sample2d_rt(sampler2D sampler, vec2 pos){{return texture(sampler, vec2(pos.x, 1.0 - pos.y));}} 
1019            " 
1020        )}
1021        else{(
1022            "#version 300 es",
1023            "",
1024            "",
1025            "",
1026            "out vec4 fragColor;",
1027            "
1028            vec4 depth_clip(vec4 w, vec4 c, float clip);
1029            vec4 sample2d(sampler2D sampler, vec2 pos){{return texture(sampler, vec2(pos.x, pos.y));}} 
1030            vec4 sample2d_rt(sampler2D sampler, vec2 pos){{return texture(sampler, vec2(pos.x, 1.0 - pos.y));}} 
1031            " 
1032        )};
1033        
1034        #[cfg(not(use_gles_3))]
1035        let (version, vertex_exts, pixel_exts, vertex_defs, pixel_defs, sampler) = (
1036            "#version 100",
1037            "",
1038            "",
1039            "",
1040            "",
1041            "
1042            vec4 depth_clip(vec4 w, vec4 c, float clip){return c;}
1043            vec4 sample2d(sampler2D sampler, vec2 pos){{return texture2D(sampler, vec2(pos.x, pos.y));}} 
1044            vec4 sample2d_rt(sampler2D sampler, vec2 pos){{return texture2D(sampler, vec2(pos.x, 1.0 - pos.y));}} 
1045            " 
1046        );
1047        
1048        /*
1049        let transpose_impl = "
1050        mat4 transpose(mat4 m){{return mat4(m[0][0],m[1][0],m[2][0],m[3][0],m[0][1],m[1][1],m[2][1],m[3][1],m[0][2],m[1][2],m[2][2],m[3][3], m[3][0], m[3][1], m[3][2], m[3][3]);}}
1051        mat3 transpose(mat3 m){{return mat3(m[0][0],m[1][0],m[2][0],m[0][1],m[1][1],m[2][1],m[0][2],m[1][2],m[2][2]);}}
1052        mat2 transpose(mat2 m){{return mat2(m[0][0],m[1][0],m[0][1],m[1][1]);}}
1053        ";
1054        */
1055        let vertex = format!("{version}
1056            {vertex_exts}
1057            {tex_ext_import}
1058            precision highp float;
1059            precision highp int;
1060            {sampler}
1061            {tex_ext_sampler}
1062            {vertex_defs}
1063            {in_vertex}\0",
1064        );
1065        //crate::log!("{}", vertex.replace("int mvo = 0;","int mvo = gl_ViewID_OVR==0?0:16;"));
1066        let pixel = format!("{version}
1067            {pixel_exts}
1068            {tex_ext_import}
1069            #extension GL_OES_standard_derivatives : enable
1070            precision highp float;
1071            precision highp int;
1072            {sampler}
1073            {tex_ext_sampler}
1074            {pixel_defs}
1075            {in_pixel}
1076            {nop_depth_clip}
1077            \0",
1078        );
1079        // lets fetch the uniform positions for our uniforms
1080        CxOsDrawShader {
1081            in_vertex: in_vertex.to_string(),
1082            in_pixel: in_pixel.to_string(),
1083            vertex: [
1084                vertex.clone(), 
1085                vertex.replace("#define VIEW_ID 0","#define VIEW_ID gl_ViewID_OVR")
1086            ],
1087            pixel: [
1088                pixel.clone(),
1089                pixel
1090                .replace("#define VIEW_ID 0","#define VIEW_ID gl_ViewID_OVR")
1091                .replace(nop_depth_clip, depth_clip),
1092            ],
1093            gl_shader: [None,None],
1094            //const_table_uniforms: Default::default(),
1095            live_uniforms: Default::default(),
1096        }
1097    }
1098
1099    pub fn free_resources(&mut self, gl: &LibGl){
1100        for gl_shader in &mut self.gl_shader{
1101            if let Some(gl_shader) = gl_shader.take(){
1102                gl_shader.free_resources(gl);
1103            }
1104        }
1105    }
1106}
1107
1108
1109fn get_gl_string(gl: &LibGl, key: gl_sys::GLenum) -> String {
1110    unsafe {
1111        let string_ptr = (gl.glGetString)(key) as *const c_char;
1112        if string_ptr == ptr::null(){
1113            return String::new()
1114        }
1115        CStr::from_ptr(string_ptr).to_string_lossy().into_owned()
1116    }
1117}
1118
1119#[derive(Default, Clone, Debug)]
1120pub struct OpenglAttribute {
1121    pub name:String,
1122    pub loc: Option<u32>,
1123    pub size: i32,
1124    pub offset: usize,
1125    pub stride: i32
1126}
1127
1128#[derive(Debug, Default, Clone)]
1129pub struct OpenglUniform {
1130    pub loc: Option<i32>,
1131    //pub name: String,
1132}
1133
1134
1135#[derive(Debug, Default, Clone)]
1136pub struct OpenglUniformBlockBinding {
1137    pub index: Option<u32>,
1138}
1139
1140impl OpenglUniformBlockBinding{
1141    #[allow(unused)]
1142    fn bind_buffer(&self, gl: &LibGl, buf: &OpenglBuffer,){
1143        if let Some(gl_buf) = buf.gl_buffer{
1144            if let Some(index) = self.index{
1145                unsafe{(gl.glBindBufferBase)(gl_sys::UNIFORM_BUFFER, index, gl_buf)};
1146            }
1147        }
1148    }
1149}
1150
1151#[derive(Clone, Default)]
1152pub struct CxOsGeometry {
1153    pub vb: OpenglBuffer,
1154    pub ib: OpenglBuffer,
1155}
1156
1157impl CxOsGeometry{
1158    pub fn free_resources(&mut self, gl:&LibGl){
1159        self.vb.free_resources(gl);
1160        self.ib.free_resources(gl);
1161    }
1162}
1163    
1164
1165/*
1166#[derive(Default, Clone)]
1167pub struct OpenglTextureSlot {
1168    pub loc: isize,
1169    pub name: String
1170}
1171*/
1172#[derive(Clone, Default)]
1173pub struct CxOsDrawList {
1174    #[allow(unused)]
1175    draw_list_uniforms: OpenglBuffer,
1176}
1177
1178#[derive(Default, Clone)]
1179pub struct CxOsDrawCallVao {
1180    pub vao: Option<u32>,
1181    pub shader_id: Option<usize>,
1182    pub inst_vb: Option<u32>,
1183    pub geom_vb: Option<u32>,
1184    pub geom_ib: Option<u32>,
1185}
1186
1187impl CxOsDrawCallVao {
1188    pub fn free(self, gl: &LibGl){
1189        if let Some(vao) = self.vao{
1190            unsafe{(gl.glDeleteVertexArrays)(1, &vao)};
1191        }
1192    }    
1193}
1194
1195#[derive(Default, Clone)]
1196pub struct CxOsDrawCall {
1197    pub draw_call_uniforms: OpenglBuffer,
1198    pub user_uniforms: OpenglBuffer,
1199    pub inst_vb: OpenglBuffer,
1200    pub vao: Option<CxOsDrawCallVao>,
1201}
1202
1203impl CxOsDrawCall {
1204    pub fn free_resources(&mut self, gl:&LibGl){
1205        self.inst_vb.free_resources(gl);
1206        if let Some(vao) = self.vao.take(){
1207            vao.free(gl);
1208        }
1209    }    
1210}
1211
1212#[derive(Clone, Default)]
1213pub struct CxOsTexture {
1214    pub gl_texture: Option<u32>,
1215    pub gl_renderbuffer: Option<u32>,
1216}
1217
1218impl CxTexture {
1219
1220    /// Updates or creates a texture based on the current texture format.
1221    ///
1222    /// This method optimizes texture management by:
1223    /// 1. Reusing existing OpenGL textures when possible.
1224    /// 2. Using `glTexSubImage2D` for updates when dimensions haven't changed.
1225    /// 3. Falling back to `glTexImage2D` for new textures or when dimensions change.
1226    ///
1227    /// Internal workings:
1228    /// - If a previous platform resource exists, it's reused to avoid unnecessary allocations.
1229    /// - If no texture exists, a new OpenGL texture is generated.
1230    /// - The method checks current texture dimensions to decide between `glTexSubImage2D` (update) 
1231    ///   and `glTexImage2D` (new allocation).
1232    ///
1233    /// Note: This method assumes that the texture format doesn't change between updates. 
1234    /// This is safe because when allocating textures at the Cx level, there are compatibility checks.
1235    pub fn update_vec_texture(&mut self, gl: &LibGl, _os_type: &OsType) {
1236        let mut needs_realloc = false;
1237        if self.alloc_vec() {
1238            if let Some(previous) = self.previous_platform_resource.take() {
1239                self.os = previous;
1240            } 
1241            if self.os.gl_texture.is_none() {
1242                unsafe {
1243                    let mut gl_texture = std::mem::MaybeUninit::uninit();
1244                    (gl.glGenTextures)(1, gl_texture.as_mut_ptr());
1245                    self.os.gl_texture = Some(gl_texture.assume_init());
1246                }
1247            }
1248            needs_realloc = true;
1249        }
1250    
1251        let updated = self.take_updated();
1252        if updated.is_empty() {
1253            return;
1254        }
1255        
1256        unsafe {
1257            (gl.glBindTexture)(gl_sys::TEXTURE_2D, self.os.gl_texture.unwrap());
1258            (gl.glTexParameteri)(gl_sys::TEXTURE_2D, gl_sys::TEXTURE_WRAP_S, gl_sys::CLAMP_TO_EDGE as i32);
1259            (gl.glTexParameteri)(gl_sys::TEXTURE_2D, gl_sys::TEXTURE_WRAP_T, gl_sys::CLAMP_TO_EDGE as i32);
1260    
1261            // Set texture parameters based on the format
1262            let (width, height, internal_format, format, data_type, data, bytes_per_pixel, use_mipmaps) = match &mut self.format {
1263                TextureFormat::VecBGRAu8_32{width, height, data, ..} => {
1264                    let (internal_format, format) = {
1265                        #[cfg(ohos_sim)] {
1266                            // The OHOS emulators only support RGBA texture formats, so we swap the `R` and `B` channels.
1267                            // TODO: test this on *real* OHOS hardware, it may behave differently.
1268                            for p in data.as_mut().unwrap() {
1269                                let orig = *p;
1270                                *p = *p & 0xFF00FF00 | (orig & 0x000000FF) << 16 | (orig & 0x00FF0000) >> 16;
1271                            }
1272                            (gl_sys::RGBA, gl_sys::RGBA)
1273                        }
1274                        #[cfg(not(ohos_sim))] {
1275                            // The default for all other devices: use the BGRA texture format
1276                            (gl_sys::BGRA, gl_sys::BGRA)
1277                        }
1278                    };
1279
1280                    (*width, *height, internal_format, format, gl_sys::UNSIGNED_BYTE, data.as_ref().unwrap().as_ptr() as *const std::ffi::c_void, 4, false)
1281                }
1282                TextureFormat::VecMipBGRAu8_32{width, height, data, max_level: _, ..} => 
1283                    (*width, *height, gl_sys::BGRA, gl_sys::BGRA, gl_sys::UNSIGNED_BYTE, data.as_ref().unwrap().as_ptr() as *const std::ffi::c_void, 4, true),
1284                TextureFormat::VecRGBAf32{width, height, data, ..} => 
1285                    (*width, *height, gl_sys::RGBA, gl_sys::RGBA, gl_sys::FLOAT, data.as_ref().unwrap().as_ptr() as *const std::ffi::c_void, 16, false),
1286                TextureFormat::VecRu8{width, height, data, unpack_row_length, ..} => {
1287                    //(gl.glPixelStorei)(gl_sys::UNPACK_ALIGNMENT, 1);
1288                    if let Some(row_length) = unpack_row_length {
1289                        (gl.glPixelStorei)(gl_sys::UNPACK_ROW_LENGTH, *row_length as i32);
1290                    }
1291                    (*width, *height, gl_sys::R8, gl_sys::RED, gl_sys::UNSIGNED_BYTE, data.as_ref().unwrap().as_ptr() as *const std::ffi::c_void, 1, false)
1292                },
1293                TextureFormat::VecRGu8{width, height, data, unpack_row_length, ..} => {
1294                    //(gl.glPixelStorei)(gl_sys::UNPACK_ALIGNMENT, 1);
1295                    if let Some(row_length) = unpack_row_length {
1296                        (gl.glPixelStorei)(gl_sys::UNPACK_ROW_LENGTH, *row_length as i32);
1297                    }
1298                    (*width, *height, gl_sys::RG, gl_sys::RG, gl_sys::UNSIGNED_BYTE, data.as_ref().unwrap().as_ptr() as *const std::ffi::c_void, 2, false)
1299                },
1300                TextureFormat::VecRf32{width, height, data, ..} => 
1301                    (*width, *height, gl_sys::RED, gl_sys::RED, gl_sys::FLOAT, data.as_ref().unwrap().as_ptr() as *const std::ffi::c_void, 4, false),
1302                _ => panic!("Unsupported texture format"),
1303            };
1304
1305            // Partial texture updates don't (yet) work on OHOS simulators/emulators.
1306            
1307            // DISABLE PARTIAL TEXTURE UPDATES ENTIRELY. Its broken.
1308            const DO_PARTIAL_TEXTURE_UPDATES: bool = false;//cfg!(not(ohos_sim));
1309
1310            match updated {
1311                TextureUpdated::Partial(rect) if DO_PARTIAL_TEXTURE_UPDATES => {
1312                    if needs_realloc {
1313                        (gl.glTexImage2D)(
1314                            gl_sys::TEXTURE_2D,
1315                            0,
1316                            internal_format as i32,
1317                            width as i32, height as i32,
1318                            0,
1319                            format,
1320                            data_type,
1321                            0 as *const _
1322                        );
1323                    }
1324
1325                    (gl.glPixelStorei)(gl_sys::UNPACK_ALIGNMENT, bytes_per_pixel);
1326                    (gl.glPixelStorei)(gl_sys::UNPACK_ROW_LENGTH, width as _);
1327                    (gl.glPixelStorei)(gl_sys::UNPACK_SKIP_PIXELS, rect.origin.x as i32);
1328                    (gl.glPixelStorei)(gl_sys::UNPACK_SKIP_ROWS,rect.origin.y as i32);
1329                    (gl.glTexSubImage2D)(
1330                        gl_sys::TEXTURE_2D,
1331                        0,
1332                        rect.origin.x as i32,
1333                        rect.origin.y as i32 ,
1334                        rect.size.width as i32,
1335                        rect.size.height as i32,
1336                        format,
1337                        data_type,
1338                        data
1339                    );
1340                },
1341                // Note: this `Partial(_)` case will only match if `DO_PARTIAL_TEXTURE_UPDATES` is false.
1342                TextureUpdated::Partial(_) | TextureUpdated::Full => {
1343                    (gl.glPixelStorei)(gl_sys::UNPACK_ALIGNMENT, bytes_per_pixel);
1344                    (gl.glPixelStorei)(gl_sys::UNPACK_ROW_LENGTH, width as _);
1345                    (gl.glPixelStorei)(gl_sys::UNPACK_SKIP_PIXELS, 0);
1346                    (gl.glPixelStorei)(gl_sys::UNPACK_SKIP_ROWS, 0);
1347                    (gl.glTexImage2D)(
1348                        gl_sys::TEXTURE_2D,
1349                        0,
1350                        internal_format as i32,
1351                        width as i32, height as i32,
1352                        0,
1353                        format,
1354                        data_type,
1355                        data
1356                    );
1357                },
1358                TextureUpdated::Empty => panic!("already asserted that updated is not empty"),
1359            };
1360    
1361            (gl.glTexParameteri)(gl_sys::TEXTURE_2D, gl_sys::TEXTURE_MIN_FILTER, if use_mipmaps { gl_sys::LINEAR_MIPMAP_LINEAR } else { gl_sys::LINEAR } as i32);
1362            (gl.glTexParameteri)(gl_sys::TEXTURE_2D, gl_sys::TEXTURE_MAG_FILTER, gl_sys::LINEAR as i32);
1363
1364            if use_mipmaps {
1365                if let TextureFormat::VecMipBGRAu8_32{max_level, ..} = &self.format {
1366                    (gl.glTexParameteri)(gl_sys::TEXTURE_2D, gl_sys::TEXTURE_BASE_LEVEL, 0);
1367                    (gl.glTexParameteri)(gl_sys::TEXTURE_2D, gl_sys::TEXTURE_MAX_LEVEL, max_level.unwrap_or(1000) as i32);
1368                    (gl.glGenerateMipmap)(gl_sys::TEXTURE_2D);
1369                }
1370            }
1371
1372            (gl.glBindTexture)(gl_sys::TEXTURE_2D, 0);
1373        }
1374    }
1375    
1376    pub fn setup_video_texture(&mut self, gl: &LibGl) -> bool {
1377        while unsafe { (gl.glGetError)() } != 0 {}
1378
1379        if self.alloc_video() {
1380            self.free_previous_resources(gl);
1381            if self.os.gl_texture.is_none() { 
1382                unsafe {
1383                    let mut gl_texture = std::mem::MaybeUninit::uninit();
1384                    (gl.glGenTextures)(1, gl_texture.as_mut_ptr());
1385                    self.os.gl_texture = Some(gl_texture.assume_init());
1386                }
1387            }
1388        }
1389        if self.take_initial() {
1390            unsafe{
1391                                
1392                let gpu_renderer = get_gl_string(gl, gl_sys::RENDERER);
1393                if gpu_renderer.contains("Adreno") {
1394                    crate::warning!("WARNING: This device is using {gpu_renderer} renderer.
1395                    OpenGL external textures (GL_OES_EGL_image_external extension) are currently not working on makepad for most Adreno GPUs.
1396                    This is likely due to a driver bug. External texture support is being disabled, which means you won't be able to use the Video widget on this device.");
1397                }
1398                
1399                (gl.glBindTexture)(gl_sys::TEXTURE_EXTERNAL_OES, self.os.gl_texture.unwrap());
1400        
1401                (gl.glTexParameteri)(gl_sys::TEXTURE_EXTERNAL_OES, gl_sys::TEXTURE_WRAP_S, gl_sys::CLAMP_TO_EDGE as i32);
1402                (gl.glTexParameteri)(gl_sys::TEXTURE_EXTERNAL_OES, gl_sys::TEXTURE_WRAP_T, gl_sys::CLAMP_TO_EDGE as i32);
1403
1404                (gl.glTexParameteri)(gl_sys::TEXTURE_EXTERNAL_OES, gl_sys::TEXTURE_MIN_FILTER, gl_sys::LINEAR as i32);
1405                (gl.glTexParameteri)(gl_sys::TEXTURE_EXTERNAL_OES, gl_sys::TEXTURE_MAG_FILTER, gl_sys::LINEAR as i32);
1406        
1407                (gl.glBindTexture)(gl_sys::TEXTURE_EXTERNAL_OES, 0);
1408
1409                assert_eq!((gl.glGetError)(), 0, "UPDATE VIDEO TEXTURE ERROR {}", self.os.gl_texture.unwrap());
1410            }
1411            return true;
1412        }
1413        false
1414    }
1415    
1416    pub fn update_render_target(&mut self, gl: &LibGl, width: usize, height: usize) {
1417        if self.alloc_render(width, height){
1418            let alloc = self.alloc.as_ref().unwrap();
1419            if self.os.gl_texture.is_none() {
1420                let mut gl_texture = std::mem::MaybeUninit::uninit();
1421                unsafe{
1422                    (gl.glGenTextures)(1, gl_texture.as_mut_ptr());
1423                    self.os.gl_texture = Some(gl_texture.assume_init());
1424                }
1425            }
1426            unsafe{(gl.glBindTexture)(gl_sys::TEXTURE_2D, self.os.gl_texture.unwrap())};
1427            match &alloc.pixel {
1428                TexturePixel::BGRAu8 => unsafe{
1429                    (gl.glTexParameteri)(gl_sys::TEXTURE_2D, gl_sys::TEXTURE_MIN_FILTER, gl_sys::NEAREST as i32);
1430                    (gl.glTexParameteri)(gl_sys::TEXTURE_2D, gl_sys::TEXTURE_MAG_FILTER, gl_sys::NEAREST as i32);
1431                    (gl.glTexImage2D)(
1432                        gl_sys::TEXTURE_2D,
1433                        0,
1434                        gl_sys::RGBA as i32,
1435                        width as i32,
1436                        height as i32,
1437                        0,
1438                        gl_sys::RGBA,
1439                        gl_sys::UNSIGNED_BYTE,
1440                        ptr::null()
1441                    );
1442                },
1443                TexturePixel::RGBAf16 => unsafe{
1444                    (gl.glTexParameteri)(gl_sys::TEXTURE_2D, gl_sys::TEXTURE_MIN_FILTER, gl_sys::NEAREST as i32);
1445                    (gl.glTexParameteri)(gl_sys::TEXTURE_2D, gl_sys::TEXTURE_MAG_FILTER, gl_sys::NEAREST as i32);
1446                    (gl.glTexImage2D)(
1447                        gl_sys::TEXTURE_2D,
1448                        0,
1449                        gl_sys::RGBA as i32,
1450                        width as i32,
1451                        height as i32,
1452                        0,
1453                        gl_sys::RGBA,
1454                        gl_sys::HALF_FLOAT,
1455                        ptr::null()
1456                    );
1457                }
1458                TexturePixel::RGBAf32 => unsafe{
1459                    (gl.glTexParameteri)(gl_sys::TEXTURE_2D, gl_sys::TEXTURE_MIN_FILTER, gl_sys::NEAREST as i32);
1460                    (gl.glTexParameteri)(gl_sys::TEXTURE_2D, gl_sys::TEXTURE_MAG_FILTER, gl_sys::NEAREST as i32);
1461                    (gl.glTexImage2D)(
1462                        gl_sys::TEXTURE_2D,
1463                        0,
1464                        gl_sys::RGBA as i32,
1465                        width as i32,
1466                        height as i32,
1467                        0,
1468                        gl_sys::RGBA,
1469                        gl_sys::FLOAT,
1470                        ptr::null()
1471                    );
1472                }
1473                _ => panic!()
1474            }
1475            unsafe{
1476                (gl.glBindTexture)(gl_sys::TEXTURE_2D, 0);
1477            }
1478        }
1479    }
1480    
1481    fn update_depth_stencil(
1482        &mut self,
1483        gl: &LibGl,
1484        width: usize,
1485        height: usize
1486    ) {
1487        if self.alloc_depth(width, height){
1488                   
1489            let alloc = self.alloc.as_ref().unwrap();
1490            match &alloc.pixel {
1491                TexturePixel::D32 => unsafe{
1492                    if self.os.gl_renderbuffer.is_none() {
1493                        let mut gl_renderbuf = std::mem::MaybeUninit::uninit();
1494                        (gl.glGenRenderbuffers)(1, gl_renderbuf.as_mut_ptr());
1495                        let gl_renderbuffer = gl_renderbuf.assume_init();
1496                        self.os.gl_renderbuffer = Some(gl_renderbuffer);
1497                    }
1498                        
1499                    (gl.glBindRenderbuffer)(gl_sys::RENDERBUFFER, self.os.gl_renderbuffer.unwrap());
1500                    (gl.glRenderbufferStorage)(
1501                        gl_sys::RENDERBUFFER,
1502                        gl_sys::DEPTH_COMPONENT32F,
1503                        width as i32,
1504                        height as i32
1505                    );
1506                    (gl.glBindRenderbuffer)(gl_sys::RENDERBUFFER, 0);
1507                },
1508                _ => {
1509                    println!("update_platform_render_targete unsupported texture format");
1510                }
1511            }
1512        }
1513    }
1514    
1515    pub fn free_previous_resources(&mut self, gl: &LibGl){
1516        if let Some(mut old_os) = self.previous_platform_resource.take(){
1517            if let Some(gl_texture) = old_os.gl_texture.take(){
1518                unsafe{(gl.glDeleteTextures)(1, &gl_texture)};
1519                crate::log!("Deleted texture: {}", gl_texture);
1520            }
1521            if let Some(gl_renderbuffer) = old_os.gl_renderbuffer.take(){
1522                unsafe{(gl.glDeleteRenderbuffers)(1, &gl_renderbuffer)};
1523            }
1524        }
1525    }
1526}
1527
1528#[derive(Default, Clone)]
1529pub struct CxOsPass {
1530    pub shader_variant: usize,
1531    pub pass_uniforms: OpenglBuffer,
1532    pub gl_framebuffer: Option<u32>,
1533}
1534
1535impl CxOsPass{
1536    
1537    pub fn free_resources(&mut self, gl: &LibGl){
1538        if let Some(gl_framebuffer) = self.gl_framebuffer.take(){
1539            unsafe{(gl.glDeleteFramebuffers)(1, &gl_framebuffer)};
1540        }
1541    }    
1542}
1543
1544#[derive(Default, Clone)]
1545pub struct OpenglBuffer {
1546    pub gl_buffer: Option<u32>
1547}
1548
1549impl OpenglBuffer {
1550    
1551    pub fn alloc_gl_buffer(&mut self, gl: &LibGl) {
1552        unsafe {
1553            let mut gl_buffer = 0;
1554            (gl.glGenBuffers)(1, &mut gl_buffer);
1555            self.gl_buffer = Some(gl_buffer);
1556        }
1557    }
1558    
1559    pub fn update_array_buffer(&mut self, gl: &LibGl, data: &[f32]) {
1560        if self.gl_buffer.is_none() {
1561            self.alloc_gl_buffer(gl);
1562        }
1563        unsafe {
1564            (gl.glBindBuffer)(gl_sys::ARRAY_BUFFER, self.gl_buffer.unwrap());
1565            (gl.glBufferData)(
1566                gl_sys::ARRAY_BUFFER,
1567                (data.len() * mem::size_of::<f32>()) as gl_sys::GLsizeiptr,
1568                data.as_ptr() as *const _,
1569                gl_sys::STATIC_DRAW
1570            );
1571            (gl.glBindBuffer)(gl_sys::ARRAY_BUFFER, 0);
1572        }
1573    }
1574    
1575    pub fn update_uniform_buffer(&mut self, gl: &LibGl, data: &[f32]) {
1576        if self.gl_buffer.is_none() {
1577            self.alloc_gl_buffer(gl);
1578        }
1579        unsafe {
1580            (gl.glBindBuffer)(gl_sys::UNIFORM_BUFFER, self.gl_buffer.unwrap());
1581            (gl.glBufferData)(
1582                gl_sys::UNIFORM_BUFFER,
1583                (data.len() * mem::size_of::<f32>()) as gl_sys::GLsizeiptr,
1584                data.as_ptr() as *const _,
1585                gl_sys::STATIC_DRAW
1586            );
1587            (gl.glBindBuffer)(gl_sys::UNIFORM_BUFFER, 0);
1588            crate::gl_log_error!(gl);
1589        }
1590    }
1591    
1592    pub fn update_index_buffer(&mut self, gl: &LibGl, data: &[u32]) {
1593        if self.gl_buffer.is_none() {
1594            self.alloc_gl_buffer(gl);
1595        }
1596        unsafe {
1597            (gl.glBindBuffer)(gl_sys::ELEMENT_ARRAY_BUFFER, self.gl_buffer.unwrap());
1598            (gl.glBufferData)(
1599                gl_sys::ELEMENT_ARRAY_BUFFER,
1600                (data.len() * mem::size_of::<u32>()) as gl_sys::GLsizeiptr,
1601                data.as_ptr() as *const _,
1602                gl_sys::STATIC_DRAW
1603            );
1604            (gl.glBindBuffer)(gl_sys::ELEMENT_ARRAY_BUFFER, 0);
1605        }
1606    }
1607    
1608    pub fn free_resources(&mut self, gl: &LibGl){
1609        if let Some(gl_buffer) = self.gl_buffer.take(){
1610            unsafe{(gl.glDeleteBuffers)(1, &gl_buffer)};
1611        }
1612    }
1613
1614}