miniquad/graphics/
gl.rs

1use std::ffi::CString;
2
3use crate::{window, ResourceManager};
4
5mod cache;
6
7use super::*;
8use cache::*;
9
10/// Raw OpenGL bindings
11/// Highly unsafe, some of the functions could be missing due to incompatible GL version
12/// or all of them might be missing alltogether if rendering context is not a GL one.
13pub mod raw_gl {
14    use super::*;
15
16    #[doc(inline)]
17    pub use crate::native::gl::*;
18
19    pub fn texture_format_into_gl(format: TextureFormat) -> (GLenum, GLenum, GLenum) {
20        format.into()
21    }
22}
23
24#[derive(Clone, Copy, Debug)]
25struct Buffer {
26    gl_buf: GLuint,
27    buffer_type: BufferType,
28    size: usize,
29    // Dimension of the indices for this buffer,
30    // used only as a type argument for glDrawElements and can be
31    // 1, 2 or 4
32    index_type: Option<u32>,
33}
34
35#[derive(Debug)]
36struct ShaderUniform {
37    gl_loc: UniformLocation,
38    uniform_type: UniformType,
39    array_count: i32,
40}
41
42struct ShaderInternal {
43    program: GLuint,
44    images: Vec<ShaderImage>,
45    uniforms: Vec<ShaderUniform>,
46}
47
48#[derive(Clone, Copy, Debug)]
49enum TextureOrRenderbuffer {
50    Texture(GLuint),
51    Renderbuffer(GLuint),
52}
53impl TextureOrRenderbuffer {
54    fn texture(&self) -> Option<GLuint> {
55        match self {
56            TextureOrRenderbuffer::Texture(id) => Some(*id),
57            _ => None,
58        }
59    }
60    fn renderbuffer(&self) -> Option<GLuint> {
61        match self {
62            TextureOrRenderbuffer::Renderbuffer(id) => Some(*id),
63            _ => None,
64        }
65    }
66}
67
68#[derive(Clone, Copy, Debug)]
69struct Texture {
70    raw: TextureOrRenderbuffer,
71    params: TextureParams,
72}
73
74impl TextureFormat {
75    fn sized_internal_format(&self) -> GLenum {
76        match self {
77            TextureFormat::RGB8 => GL_RGB8,
78            TextureFormat::RGBA8 => GL_RGBA8,
79            TextureFormat::RGBA16F => GL_RGBA16F,
80            TextureFormat::Depth => GL_DEPTH_COMPONENT16,
81            TextureFormat::Depth32 => GL_DEPTH_COMPONENT32,
82            #[cfg(target_arch = "wasm32")]
83            TextureFormat::Alpha => GL_ALPHA,
84            #[cfg(not(target_arch = "wasm32"))]
85            TextureFormat::Alpha => GL_R8,
86        }
87    }
88}
89
90/// Converts from TextureFormat to (internal_format, format, pixel_type)
91impl From<TextureFormat> for (GLenum, GLenum, GLenum) {
92    fn from(format: TextureFormat) -> Self {
93        match format {
94            TextureFormat::RGB8 => (GL_RGB, GL_RGB, GL_UNSIGNED_BYTE),
95            TextureFormat::RGBA8 => (GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE),
96            TextureFormat::RGBA16F => (GL_RGBA16F, GL_RGBA, GL_FLOAT),
97            TextureFormat::Depth => (GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT),
98            TextureFormat::Depth32 => (GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT, GL_FLOAT),
99            #[cfg(target_arch = "wasm32")]
100            TextureFormat::Alpha => (GL_ALPHA, GL_ALPHA, GL_UNSIGNED_BYTE),
101            #[cfg(not(target_arch = "wasm32"))]
102            TextureFormat::Alpha => (GL_R8, GL_RED, GL_UNSIGNED_BYTE), // texture updates will swizzle Red -> Alpha to match WASM
103        }
104    }
105}
106
107impl From<TextureKind> for GLuint {
108    fn from(kind: TextureKind) -> GLuint {
109        match kind {
110            TextureKind::Texture2D => GL_TEXTURE_2D,
111            TextureKind::CubeMap => GL_TEXTURE_CUBE_MAP,
112        }
113    }
114}
115impl From<Equation> for GLenum {
116    fn from(eq: Equation) -> Self {
117        match eq {
118            Equation::Add => GL_FUNC_ADD,
119            Equation::Subtract => GL_FUNC_SUBTRACT,
120            Equation::ReverseSubtract => GL_FUNC_REVERSE_SUBTRACT,
121        }
122    }
123}
124
125impl From<BlendFactor> for GLenum {
126    fn from(factor: BlendFactor) -> GLenum {
127        match factor {
128            BlendFactor::Zero => GL_ZERO,
129            BlendFactor::One => GL_ONE,
130            BlendFactor::Value(BlendValue::SourceColor) => GL_SRC_COLOR,
131            BlendFactor::Value(BlendValue::SourceAlpha) => GL_SRC_ALPHA,
132            BlendFactor::Value(BlendValue::DestinationColor) => GL_DST_COLOR,
133            BlendFactor::Value(BlendValue::DestinationAlpha) => GL_DST_ALPHA,
134            BlendFactor::OneMinusValue(BlendValue::SourceColor) => GL_ONE_MINUS_SRC_COLOR,
135            BlendFactor::OneMinusValue(BlendValue::SourceAlpha) => GL_ONE_MINUS_SRC_ALPHA,
136            BlendFactor::OneMinusValue(BlendValue::DestinationColor) => GL_ONE_MINUS_DST_COLOR,
137            BlendFactor::OneMinusValue(BlendValue::DestinationAlpha) => GL_ONE_MINUS_DST_ALPHA,
138            BlendFactor::SourceAlphaSaturate => GL_SRC_ALPHA_SATURATE,
139        }
140    }
141}
142
143impl From<StencilOp> for GLenum {
144    fn from(op: StencilOp) -> Self {
145        match op {
146            StencilOp::Keep => GL_KEEP,
147            StencilOp::Zero => GL_ZERO,
148            StencilOp::Replace => GL_REPLACE,
149            StencilOp::IncrementClamp => GL_INCR,
150            StencilOp::DecrementClamp => GL_DECR,
151            StencilOp::Invert => GL_INVERT,
152            StencilOp::IncrementWrap => GL_INCR_WRAP,
153            StencilOp::DecrementWrap => GL_DECR_WRAP,
154        }
155    }
156}
157
158impl From<CompareFunc> for GLenum {
159    fn from(cf: CompareFunc) -> Self {
160        match cf {
161            CompareFunc::Always => GL_ALWAYS,
162            CompareFunc::Never => GL_NEVER,
163            CompareFunc::Less => GL_LESS,
164            CompareFunc::Equal => GL_EQUAL,
165            CompareFunc::LessOrEqual => GL_LEQUAL,
166            CompareFunc::Greater => GL_GREATER,
167            CompareFunc::NotEqual => GL_NOTEQUAL,
168            CompareFunc::GreaterOrEqual => GL_GEQUAL,
169        }
170    }
171}
172
173impl Texture {
174    pub fn new(
175        ctx: &mut GlContext,
176        access: TextureAccess,
177        source: TextureSource,
178        params: TextureParams,
179    ) -> Texture {
180        if let TextureSource::Bytes(bytes_data) = source {
181            assert_eq!(
182                params.format.size(params.width, params.height) as usize,
183                bytes_data.len()
184            );
185        }
186        if access != TextureAccess::RenderTarget {
187            assert!(
188                params.sample_count <= 1,
189                "Multisampling is only supported for render textures"
190            );
191        }
192        let (internal_format, format, pixel_type) = params.format.into();
193
194        if access == TextureAccess::RenderTarget && params.sample_count > 1 {
195            let mut renderbuffer: u32 = 0;
196            unsafe {
197                glGenRenderbuffers(1, &mut renderbuffer as *mut _);
198                glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer as _);
199                let internal_format = params.format.sized_internal_format();
200                glRenderbufferStorageMultisample(
201                    GL_RENDERBUFFER,
202                    params.sample_count,
203                    internal_format,
204                    params.width as _,
205                    params.height as _,
206                );
207            }
208            return Texture {
209                raw: TextureOrRenderbuffer::Renderbuffer(renderbuffer),
210                params,
211            };
212        }
213
214        ctx.cache.store_texture_binding(0);
215
216        let mut texture: GLuint = 0;
217
218        unsafe {
219            glGenTextures(1, &mut texture as *mut _);
220            ctx.cache.bind_texture(0, params.kind.into(), texture);
221            glPixelStorei(GL_UNPACK_ALIGNMENT, 1); // miniquad always uses row alignment of 1
222
223            if cfg!(not(target_arch = "wasm32")) {
224                // if not WASM
225                if params.format == TextureFormat::Alpha {
226                    // if alpha miniquad texture, the value on non-WASM is stored in red channel
227                    // swizzle red -> alpha
228                    glTexParameteri(params.kind.into(), GL_TEXTURE_SWIZZLE_A, GL_RED as _);
229                } else {
230                    // keep alpha -> alpha
231                    glTexParameteri(params.kind.into(), GL_TEXTURE_SWIZZLE_A, GL_ALPHA as _);
232                }
233            }
234
235            match source {
236                TextureSource::Empty => {
237                    // not quite sure if glTexImage2D(null) is really a requirement
238                    // but it was like this for quite a while and apparantly it works?
239                    glTexImage2D(
240                        GL_TEXTURE_2D,
241                        0,
242                        internal_format as i32,
243                        params.width as i32,
244                        params.height as i32,
245                        0,
246                        format,
247                        pixel_type,
248                        std::ptr::null() as _,
249                    );
250                }
251                TextureSource::Bytes(source) => {
252                    assert!(params.kind == TextureKind::Texture2D, "incompatible TextureKind and TextureSource. Cubemaps require TextureSource::Array of 6 textures.");
253                    glTexImage2D(
254                        GL_TEXTURE_2D,
255                        0,
256                        internal_format as i32,
257                        params.width as i32,
258                        params.height as i32,
259                        0,
260                        format,
261                        pixel_type,
262                        source.as_ptr() as *const _,
263                    );
264                }
265                TextureSource::Array(array) => {
266                    if params.kind == TextureKind::CubeMap {
267                        assert!(
268                            array.len() == 6,
269                            "Cubemaps require TextureSource::Array of 6 textures."
270                        );
271                    }
272                    for (cubemap_face, mipmaps) in array.iter().enumerate() {
273                        if mipmaps.len() != 1 {
274                            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
275                            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, array.len() as _);
276                        }
277                        for (mipmap_level, bytes) in mipmaps.iter().enumerate() {
278                            let target = match params.kind {
279                                TextureKind::Texture2D => GL_TEXTURE_2D,
280                                TextureKind::CubeMap => {
281                                    GL_TEXTURE_CUBE_MAP_POSITIVE_X + cubemap_face as u32
282                                }
283                            };
284                            glTexImage2D(
285                                target,
286                                mipmap_level as _,
287                                internal_format as i32,
288                                params.width as i32,
289                                params.height as i32,
290                                0,
291                                format,
292                                pixel_type,
293                                bytes.as_ptr() as *const _,
294                            );
295                        }
296                    }
297                }
298            }
299
300            let wrap = match params.wrap {
301                TextureWrap::Repeat => GL_REPEAT,
302                TextureWrap::Mirror => GL_MIRRORED_REPEAT,
303                TextureWrap::Clamp => GL_CLAMP_TO_EDGE,
304            };
305
306            let min_filter = Self::gl_filter(params.min_filter, params.mipmap_filter);
307            let mag_filter = match params.mag_filter {
308                FilterMode::Nearest => GL_NEAREST,
309                FilterMode::Linear => GL_LINEAR,
310            };
311
312            glTexParameteri(params.kind.into(), GL_TEXTURE_WRAP_S, wrap as i32);
313            glTexParameteri(params.kind.into(), GL_TEXTURE_WRAP_T, wrap as i32);
314            glTexParameteri(params.kind.into(), GL_TEXTURE_MIN_FILTER, min_filter as i32);
315            glTexParameteri(params.kind.into(), GL_TEXTURE_MAG_FILTER, mag_filter as i32);
316        }
317        ctx.cache.restore_texture_binding(0);
318
319        Texture {
320            raw: TextureOrRenderbuffer::Texture(texture),
321            params,
322        }
323    }
324
325    pub fn resize(&mut self, ctx: &mut GlContext, width: u32, height: u32, source: Option<&[u8]>) {
326        let raw = self
327            .raw
328            .texture()
329            .expect("Resize not yet implemented for RenderBuffer(multisampled) textures");
330        ctx.cache.store_texture_binding(0);
331        ctx.cache.bind_texture(0, self.params.kind.into(), raw);
332
333        let (internal_format, format, pixel_type) = self.params.format.into();
334
335        self.params.width = width;
336        self.params.height = height;
337
338        unsafe {
339            glTexImage2D(
340                GL_TEXTURE_2D,
341                0,
342                internal_format as i32,
343                self.params.width as i32,
344                self.params.height as i32,
345                0,
346                format,
347                pixel_type,
348                match source {
349                    Some(source) => source.as_ptr() as *const _,
350                    Option::None => std::ptr::null(),
351                },
352            );
353        }
354
355        ctx.cache.restore_texture_binding(0);
356    }
357
358    pub fn update_texture_part(
359        &self,
360        ctx: &mut GlContext,
361        x_offset: i32,
362        y_offset: i32,
363        width: i32,
364        height: i32,
365        source: &[u8],
366    ) {
367        assert_eq!(self.size(width as _, height as _), source.len());
368        assert!(x_offset + width <= self.params.width as _);
369        assert!(y_offset + height <= self.params.height as _);
370        let raw = self.raw.texture().expect(
371            "update_texture_part not yet implemented for RenderBuffer(multisampled) textures",
372        );
373
374        ctx.cache.store_texture_binding(0);
375        ctx.cache.bind_texture(0, self.params.kind.into(), raw);
376
377        let (_, format, pixel_type) = self.params.format.into();
378
379        unsafe {
380            glPixelStorei(GL_UNPACK_ALIGNMENT, 1); // miniquad always uses row alignment of 1
381
382            if cfg!(not(target_arch = "wasm32")) {
383                // if not WASM
384                if self.params.format == TextureFormat::Alpha {
385                    // if alpha miniquad texture, the value on non-WASM is stored in red channel
386                    // swizzle red -> alpha
387                    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_RED as _);
388                } else {
389                    // keep alpha -> alpha
390                    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_ALPHA as _);
391                }
392            }
393
394            glTexSubImage2D(
395                GL_TEXTURE_2D,
396                0,
397                x_offset as _,
398                y_offset as _,
399                width as _,
400                height as _,
401                format,
402                pixel_type,
403                source.as_ptr() as *const _,
404            );
405        }
406
407        ctx.cache.restore_texture_binding(0);
408    }
409
410    /// Read texture data into CPU memory
411    pub fn read_pixels(&self, bytes: &mut [u8]) {
412        let raw = self
413            .raw
414            .texture()
415            .expect("read_pixels not yet implemented for RenderBuffer(multisampled) textures");
416
417        let (_, format, pixel_type) = self.params.format.into();
418
419        let mut fbo = 0;
420        unsafe {
421            let mut binded_fbo: i32 = 0;
422            glGetIntegerv(gl::GL_DRAW_FRAMEBUFFER_BINDING, &mut binded_fbo);
423            glGenFramebuffers(1, &mut fbo);
424            glBindFramebuffer(gl::GL_FRAMEBUFFER, fbo);
425            glFramebufferTexture2D(
426                gl::GL_FRAMEBUFFER,
427                gl::GL_COLOR_ATTACHMENT0,
428                gl::GL_TEXTURE_2D,
429                raw,
430                0,
431            );
432
433            glReadPixels(
434                0,
435                0,
436                self.params.width as _,
437                self.params.height as _,
438                format,
439                pixel_type,
440                bytes.as_mut_ptr() as _,
441            );
442
443            glBindFramebuffer(gl::GL_FRAMEBUFFER, binded_fbo as _);
444            glDeleteFramebuffers(1, &fbo);
445        }
446    }
447
448    #[inline]
449    fn size(&self, width: u32, height: u32) -> usize {
450        self.params.format.size(width, height) as usize
451    }
452
453    fn gl_filter(filter: FilterMode, mipmap_filter: MipmapFilterMode) -> GLenum {
454        match filter {
455            FilterMode::Nearest => match mipmap_filter {
456                MipmapFilterMode::None => GL_NEAREST,
457                MipmapFilterMode::Nearest => GL_NEAREST_MIPMAP_NEAREST,
458                MipmapFilterMode::Linear => GL_NEAREST_MIPMAP_LINEAR,
459            },
460            FilterMode::Linear => match mipmap_filter {
461                MipmapFilterMode::None => GL_LINEAR,
462                MipmapFilterMode::Nearest => GL_LINEAR_MIPMAP_NEAREST,
463                MipmapFilterMode::Linear => GL_LINEAR_MIPMAP_LINEAR,
464            },
465        }
466    }
467}
468
469pub(crate) struct PipelineInternal {
470    layout: Vec<Option<VertexAttributeInternal>>,
471    shader: ShaderId,
472    params: PipelineParams,
473}
474
475type UniformLocation = Option<GLint>;
476
477pub struct ShaderImage {
478    gl_loc: UniformLocation,
479}
480
481fn get_uniform_location(program: GLuint, name: &str) -> Option<i32> {
482    let cname = CString::new(name).unwrap_or_else(|e| panic!("{}", e));
483    let location = unsafe { glGetUniformLocation(program, cname.as_ptr()) };
484
485    if location == -1 {
486        return None;
487    }
488
489    Some(location)
490}
491
492pub(crate) struct RenderPassInternal {
493    gl_fb: GLuint,
494    color_textures: Vec<TextureId>,
495    resolves: Option<Vec<(u32, TextureId)>>,
496    depth_texture: Option<TextureId>,
497}
498
499struct Textures(Vec<Texture>);
500impl Textures {
501    fn get(&self, texture: TextureId) -> Texture {
502        match texture.0 {
503            TextureIdInner::Raw(RawId::OpenGl(texture)) => Texture {
504                raw: TextureOrRenderbuffer::Texture(texture),
505                params: Default::default(),
506            },
507            #[cfg(target_vendor = "apple")]
508            TextureIdInner::Raw(RawId::Metal(..)) => panic!("Metal texture in OpenGL context!"),
509            TextureIdInner::Managed(texture) => self.0[texture],
510        }
511    }
512}
513pub struct GlContext {
514    shaders: ResourceManager<ShaderInternal>,
515    pipelines: ResourceManager<PipelineInternal>,
516    passes: ResourceManager<RenderPassInternal>,
517    buffers: ResourceManager<Buffer>,
518    textures: Textures,
519    default_framebuffer: GLuint,
520    pub(crate) cache: GlCache,
521    pub(crate) info: ContextInfo,
522}
523
524impl Default for GlContext {
525    fn default() -> Self {
526        Self::new()
527    }
528}
529
530impl GlContext {
531    pub fn new() -> GlContext {
532        unsafe {
533            let mut default_framebuffer: GLuint = 0;
534            glGetIntegerv(
535                GL_FRAMEBUFFER_BINDING,
536                &mut default_framebuffer as *mut _ as *mut _,
537            );
538            let mut vao = 0;
539
540            glGenVertexArrays(1, &mut vao as *mut _);
541            glBindVertexArray(vao);
542            let info = gl_info();
543            GlContext {
544                default_framebuffer,
545                shaders: ResourceManager::default(),
546                pipelines: ResourceManager::default(),
547                passes: ResourceManager::default(),
548                buffers: ResourceManager::default(),
549                textures: Textures(vec![]),
550                info,
551                cache: GlCache {
552                    stored_index_buffer: 0,
553                    stored_index_type: None,
554                    stored_vertex_buffer: 0,
555                    index_buffer: 0,
556                    index_type: None,
557                    vertex_buffer: 0,
558                    cur_pipeline: None,
559                    cur_pass: None,
560                    color_blend: None,
561                    alpha_blend: None,
562                    stencil: None,
563                    color_write: (true, true, true, true),
564                    cull_face: CullFace::Nothing,
565                    stored_texture: 0,
566                    stored_target: 0,
567                    textures: [CachedTexture {
568                        target: 0,
569                        texture: 0,
570                    }; MAX_SHADERSTAGE_IMAGES],
571                    attributes: [None; MAX_VERTEX_ATTRIBUTES],
572                },
573            }
574        }
575    }
576
577    pub fn features(&self) -> &Features {
578        &self.info.features
579    }
580}
581
582fn load_shader_internal(
583    vertex_shader: &str,
584    fragment_shader: &str,
585    meta: ShaderMeta,
586) -> Result<ShaderInternal, ShaderError> {
587    unsafe {
588        let vertex_shader = load_shader(GL_VERTEX_SHADER, vertex_shader)?;
589        let fragment_shader = load_shader(GL_FRAGMENT_SHADER, fragment_shader)?;
590
591        let program = glCreateProgram();
592        glAttachShader(program, vertex_shader);
593        glAttachShader(program, fragment_shader);
594        glLinkProgram(program);
595
596        // delete no longer used shaders
597        glDetachShader(program, vertex_shader);
598        glDeleteShader(vertex_shader);
599        glDeleteShader(fragment_shader);
600
601        let mut link_status = 0;
602        glGetProgramiv(program, GL_LINK_STATUS, &mut link_status as *mut _);
603        if link_status == 0 {
604            let mut max_length: i32 = 0;
605            glGetProgramiv(program, GL_INFO_LOG_LENGTH, &mut max_length as *mut _);
606
607            let mut error_message = vec![0u8; max_length as usize + 1];
608            glGetProgramInfoLog(
609                program,
610                max_length,
611                &mut max_length as *mut _,
612                error_message.as_mut_ptr() as *mut _,
613            );
614            assert!(max_length >= 1);
615            let error_message =
616                std::string::String::from_utf8_lossy(&error_message[0..max_length as usize - 1]);
617            return Err(ShaderError::LinkError(error_message.to_string()));
618        }
619
620        glUseProgram(program);
621
622        #[rustfmt::skip]
623        let images = meta.images.iter().map(|name| ShaderImage {
624            gl_loc: get_uniform_location(program, name),
625        }).collect();
626
627        #[rustfmt::skip]
628        let uniforms = meta.uniforms.uniforms.iter().scan(0, |offset, uniform| {
629            let res = ShaderUniform {
630                gl_loc: get_uniform_location(program, &uniform.name),
631                uniform_type: uniform.uniform_type,
632                array_count: uniform.array_count as _,
633            };
634            *offset += uniform.uniform_type.size() * uniform.array_count;
635            Some(res)
636        }).collect();
637
638        Ok(ShaderInternal {
639            program,
640            images,
641            uniforms,
642        })
643    }
644}
645
646pub fn load_shader(shader_type: GLenum, source: &str) -> Result<GLuint, ShaderError> {
647    unsafe {
648        let shader = glCreateShader(shader_type);
649        assert!(shader != 0);
650
651        let cstring = CString::new(source)?;
652        let csource = [cstring];
653        glShaderSource(shader, 1, csource.as_ptr() as *const _, std::ptr::null());
654        glCompileShader(shader);
655
656        let mut is_compiled = 0;
657        glGetShaderiv(shader, GL_COMPILE_STATUS, &mut is_compiled as *mut _);
658        if is_compiled == 0 {
659            let mut max_length: i32 = 0;
660            glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &mut max_length as *mut _);
661
662            let mut error_message = vec![0u8; max_length as usize + 1];
663            glGetShaderInfoLog(
664                shader,
665                max_length,
666                &mut max_length as *mut _,
667                error_message.as_mut_ptr() as *mut _,
668            );
669
670            assert!(max_length >= 1);
671            let mut error_message =
672                std::string::String::from_utf8_lossy(&error_message[0..max_length as usize - 1])
673                    .into_owned();
674
675            // On Wasm + Chrome, for unknown reason, string with zero-terminator is returned. On Firefox there is no zero-terminators in JavaScript string.
676            if error_message.ends_with('\0') {
677                error_message.pop();
678            }
679
680            return Err(ShaderError::CompilationError {
681                shader_type: match shader_type {
682                    GL_VERTEX_SHADER => ShaderType::Vertex,
683                    GL_FRAGMENT_SHADER => ShaderType::Fragment,
684                    _ => unreachable!(),
685                },
686                error_message,
687            });
688        }
689
690        Ok(shader)
691    }
692}
693
694impl GlContext {
695    fn set_blend(&mut self, color_blend: Option<BlendState>, alpha_blend: Option<BlendState>) {
696        if color_blend.is_none() && alpha_blend.is_some() {
697            panic!("AlphaBlend without ColorBlend");
698        }
699        if self.cache.color_blend == color_blend && self.cache.alpha_blend == alpha_blend {
700            return;
701        }
702
703        unsafe {
704            if let Some(color_blend) = color_blend {
705                if self.cache.color_blend.is_none() {
706                    glEnable(GL_BLEND);
707                }
708
709                let BlendState {
710                    equation: eq_rgb,
711                    sfactor: src_rgb,
712                    dfactor: dst_rgb,
713                } = color_blend;
714
715                if let Some(BlendState {
716                    equation: eq_alpha,
717                    sfactor: src_alpha,
718                    dfactor: dst_alpha,
719                }) = alpha_blend
720                {
721                    glBlendFuncSeparate(
722                        src_rgb.into(),
723                        dst_rgb.into(),
724                        src_alpha.into(),
725                        dst_alpha.into(),
726                    );
727                    glBlendEquationSeparate(eq_rgb.into(), eq_alpha.into());
728                } else {
729                    glBlendFunc(src_rgb.into(), dst_rgb.into());
730                    glBlendEquationSeparate(eq_rgb.into(), eq_rgb.into());
731                }
732            } else if self.cache.color_blend.is_some() {
733                glDisable(GL_BLEND);
734            }
735        }
736
737        self.cache.color_blend = color_blend;
738        self.cache.alpha_blend = alpha_blend;
739    }
740
741    fn set_stencil(&mut self, stencil_test: Option<StencilState>) {
742        if self.cache.stencil == stencil_test {
743            return;
744        }
745        unsafe {
746            if let Some(stencil) = stencil_test {
747                if self.cache.stencil.is_none() {
748                    glEnable(GL_STENCIL_TEST);
749                }
750
751                let front = &stencil.front;
752                glStencilOpSeparate(
753                    GL_FRONT,
754                    front.fail_op.into(),
755                    front.depth_fail_op.into(),
756                    front.pass_op.into(),
757                );
758                glStencilFuncSeparate(
759                    GL_FRONT,
760                    front.test_func.into(),
761                    front.test_ref,
762                    front.test_mask,
763                );
764                glStencilMaskSeparate(GL_FRONT, front.write_mask);
765
766                let back = &stencil.back;
767                glStencilOpSeparate(
768                    GL_BACK,
769                    back.fail_op.into(),
770                    back.depth_fail_op.into(),
771                    back.pass_op.into(),
772                );
773                glStencilFuncSeparate(
774                    GL_BACK,
775                    back.test_func.into(),
776                    back.test_ref,
777                    back.test_mask,
778                );
779                glStencilMaskSeparate(GL_BACK, back.write_mask);
780            } else if self.cache.stencil.is_some() {
781                glDisable(GL_STENCIL_TEST);
782            }
783        }
784
785        self.cache.stencil = stencil_test;
786    }
787
788    fn set_cull_face(&mut self, cull_face: CullFace) {
789        if self.cache.cull_face == cull_face {
790            return;
791        }
792
793        match cull_face {
794            CullFace::Nothing => unsafe {
795                glDisable(GL_CULL_FACE);
796            },
797            CullFace::Front => unsafe {
798                glEnable(GL_CULL_FACE);
799                glCullFace(GL_FRONT);
800            },
801            CullFace::Back => unsafe {
802                glEnable(GL_CULL_FACE);
803                glCullFace(GL_BACK);
804            },
805        }
806        self.cache.cull_face = cull_face;
807    }
808
809    fn set_color_write(&mut self, color_write: ColorMask) {
810        if self.cache.color_write == color_write {
811            return;
812        }
813        let (r, g, b, a) = color_write;
814        unsafe { glColorMask(r as _, g as _, b as _, a as _) }
815        self.cache.color_write = color_write;
816    }
817}
818
819#[allow(clippy::field_reassign_with_default)]
820fn gl_info() -> ContextInfo {
821    let version_string = unsafe { glGetString(super::gl::GL_VERSION) };
822    let gl_version_string = unsafe { std::ffi::CStr::from_ptr(version_string as _) }
823        .to_str()
824        .unwrap()
825        .to_string();
826    //let gles2 = !gles3 && gl_version_string.contains("OpenGL ES");
827
828    let gl2 = gl_version_string.is_empty()
829        || gl_version_string.starts_with("2")
830        || gl_version_string.starts_with("OpenGL ES 2");
831    let webgl1 = gl_version_string == "WebGL 1.0";
832
833    let features = Features {
834        instancing: !gl2,
835        resolve_attachments: !webgl1 && !gl2,
836    };
837
838    let mut glsl_support = GlslSupport::default();
839
840    // this is not quite documented,
841    // but somehow even GL2.1 usually have all the compatibility extensions to support glsl100
842    // It was tested on really old windows machines, virtual machines etc. glsl100 always works!
843    glsl_support.v100 = true;
844
845    // on wasm miniquad always creates webgl1 context, with the only glsl available being version 100
846    #[cfg(target_arch = "wasm32")]
847    {
848        // on web, miniquad always loads EXT_shader_texture_lod and OES_standard_derivatives
849        glsl_support.v100_ext = true;
850
851        let webgl2 = gl_version_string.contains("WebGL 2.0");
852        if webgl2 {
853            glsl_support.v300es = true;
854        }
855    }
856
857    #[cfg(not(target_arch = "wasm32"))]
858    {
859        let gles3 = gl_version_string.contains("OpenGL ES 3");
860
861        if gles3 {
862            glsl_support.v300es = true;
863        }
864    }
865
866    // there is no gl3.4, so 4+ and 3.3 covers all modern OpenGL
867    if gl_version_string.starts_with("3.2") {
868        glsl_support.v150 = true; // MacOS is defaulting to 3.2 and GLSL 150
869    } else if gl_version_string.starts_with("4") || gl_version_string.starts_with("3.3") {
870        glsl_support.v330 = true;
871    // gl 3.0, 3.1, 3.2 maps to 1.30, 1.40, 1.50 glsl versions
872    } else if gl_version_string.starts_with("3") {
873        glsl_support.v130 = true;
874    }
875
876    ContextInfo {
877        backend: Backend::OpenGl,
878        gl_version_string,
879        glsl_support,
880        features,
881    }
882}
883
884impl RenderingBackend for GlContext {
885    fn info(&self) -> ContextInfo {
886        self.info.clone()
887    }
888
889    fn new_shader(
890        &mut self,
891        shader: ShaderSource,
892        meta: ShaderMeta,
893    ) -> Result<ShaderId, ShaderError> {
894        let (fragment, vertex) = match shader {
895            ShaderSource::Glsl { fragment, vertex } => (fragment, vertex),
896            _ => panic!("Metal source on OpenGl context"),
897        };
898        let shader = load_shader_internal(vertex, fragment, meta)?;
899        Ok(ShaderId(self.shaders.add(shader)))
900    }
901
902    fn new_texture(
903        &mut self,
904        access: TextureAccess,
905        source: TextureSource,
906        params: TextureParams,
907    ) -> TextureId {
908        let texture = Texture::new(self, access, source, params);
909        self.textures.0.push(texture);
910        TextureId(TextureIdInner::Managed(self.textures.0.len() - 1))
911    }
912
913    fn delete_texture(&mut self, texture: TextureId) {
914        //self.cache.clear_texture_bindings();
915
916        let t = self.textures.get(texture);
917        match &t.raw {
918            TextureOrRenderbuffer::Texture(raw) => unsafe {
919                glDeleteTextures(1, raw as *const _);
920            },
921            TextureOrRenderbuffer::Renderbuffer(raw) => unsafe {
922                glDeleteRenderbuffers(1, raw as *const _);
923            },
924        }
925    }
926
927    fn delete_shader(&mut self, program: ShaderId) {
928        unsafe { glDeleteProgram(self.shaders[program.0].program) };
929        self.shaders.remove(program.0);
930        self.cache.cur_pipeline = None;
931    }
932
933    fn delete_pipeline(&mut self, pipeline: Pipeline) {
934        self.pipelines.remove(pipeline.0);
935    }
936
937    fn texture_set_wrap(&mut self, texture: TextureId, wrap_x: TextureWrap, wrap_y: TextureWrap) {
938        let t = self.textures.get(texture);
939        let raw = t
940            .raw
941            .texture()
942            .expect("texture_set_wrap not yet implemented for RenderBuffer(multisampled) textures");
943
944        self.cache.store_texture_binding(0);
945        self.cache.bind_texture(0, t.params.kind.into(), raw);
946        let wrap_x = match wrap_x {
947            TextureWrap::Repeat => GL_REPEAT,
948            TextureWrap::Mirror => GL_MIRRORED_REPEAT,
949            TextureWrap::Clamp => GL_CLAMP_TO_EDGE,
950        };
951
952        let wrap_y = match wrap_y {
953            TextureWrap::Repeat => GL_REPEAT,
954            TextureWrap::Mirror => GL_MIRRORED_REPEAT,
955            TextureWrap::Clamp => GL_CLAMP_TO_EDGE,
956        };
957
958        unsafe {
959            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap_x as i32);
960            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap_y as i32);
961        }
962        self.cache.restore_texture_binding(0);
963    }
964
965    fn texture_set_min_filter(
966        &mut self,
967        texture: TextureId,
968        filter: FilterMode,
969        mipmap_filter: MipmapFilterMode,
970    ) {
971        let t = self.textures.get(texture);
972        let raw = t.raw.texture().expect(
973            "texture_set_min_filter not yet implemented for RenderBuffer(multisampled) textures",
974        );
975
976        self.cache.store_texture_binding(0);
977        self.cache.bind_texture(0, t.params.kind.into(), raw);
978
979        let filter = Texture::gl_filter(filter, mipmap_filter);
980        unsafe {
981            glTexParameteri(t.params.kind.into(), GL_TEXTURE_MIN_FILTER, filter as i32);
982        }
983        self.cache.restore_texture_binding(0);
984    }
985    fn texture_set_mag_filter(&mut self, texture: TextureId, filter: FilterMode) {
986        let t = self.textures.get(texture);
987        let raw = t
988            .raw
989            .texture()
990            .expect("texture_set_wrap not yet implemented for RenderBuffer(multisampled) textures");
991
992        self.cache.store_texture_binding(0);
993        self.cache.bind_texture(0, t.params.kind.into(), raw);
994
995        let filter = match filter {
996            FilterMode::Nearest => GL_NEAREST,
997            FilterMode::Linear => GL_LINEAR,
998        };
999        unsafe {
1000            glTexParameteri(t.params.kind.into(), GL_TEXTURE_MAG_FILTER, filter as i32);
1001        }
1002        self.cache.restore_texture_binding(0);
1003    }
1004    fn texture_resize(
1005        &mut self,
1006        texture: TextureId,
1007        width: u32,
1008        height: u32,
1009        source: Option<&[u8]>,
1010    ) {
1011        let mut t = self.textures.get(texture);
1012        t.resize(self, width, height, source);
1013        if let TextureIdInner::Managed(tex_id) = texture.0 {
1014            self.textures.0[tex_id].params = t.params;
1015        };
1016    }
1017    fn texture_read_pixels(&mut self, texture: TextureId, source: &mut [u8]) {
1018        let t = self.textures.get(texture);
1019        t.read_pixels(source);
1020    }
1021    fn texture_generate_mipmaps(&mut self, texture: TextureId) {
1022        let t = self.textures.get(texture);
1023        let raw = t.raw.texture().expect(
1024            "texture_generate_mipmaps not yet implemented for RenderBuffer(multisampled) textures",
1025        );
1026
1027        self.cache.store_texture_binding(0);
1028        self.cache.bind_texture(0, t.params.kind.into(), raw);
1029        unsafe {
1030            glGenerateMipmap(t.params.kind.into());
1031        }
1032        self.cache.restore_texture_binding(0);
1033    }
1034    fn texture_update_part(
1035        &mut self,
1036        texture: TextureId,
1037        x_offset: i32,
1038        y_offset: i32,
1039        width: i32,
1040        height: i32,
1041        source: &[u8],
1042    ) {
1043        let t = self.textures.get(texture);
1044        t.update_texture_part(self, x_offset, y_offset, width, height, source);
1045    }
1046    fn texture_params(&self, texture: TextureId) -> TextureParams {
1047        let texture = self.textures.get(texture);
1048        texture.params
1049    }
1050    unsafe fn texture_raw_id(&self, texture: TextureId) -> RawId {
1051        let texture = self.textures.get(texture);
1052        let raw = texture
1053            .raw
1054            .texture()
1055            .expect("For multisampled texture raw_id is not supported");
1056
1057        RawId::OpenGl(raw)
1058    }
1059
1060    fn new_render_pass_mrt(
1061        &mut self,
1062        color_img: &[TextureId],
1063        resolve_img: Option<&[TextureId]>,
1064        depth_img: Option<TextureId>,
1065    ) -> RenderPass {
1066        if color_img.is_empty() && depth_img.is_none() {
1067            panic!("Render pass should have at least one non-none target");
1068        }
1069        let mut gl_fb = 0;
1070
1071        let mut resolves = None;
1072        unsafe {
1073            glGenFramebuffers(1, &mut gl_fb as *mut _);
1074            glBindFramebuffer(GL_FRAMEBUFFER, gl_fb);
1075            for (i, color_img) in color_img.iter().enumerate() {
1076                let texture = self.textures.get(*color_img);
1077                if texture.params.sample_count > 1 {
1078                    let raw = texture.raw.renderbuffer().unwrap();
1079                    glFramebufferRenderbuffer(
1080                        GL_FRAMEBUFFER,
1081                        GL_COLOR_ATTACHMENT0 + i as u32,
1082                        GL_RENDERBUFFER,
1083                        raw,
1084                    );
1085                } else {
1086                    let raw = texture.raw.texture().unwrap();
1087                    glFramebufferTexture2D(
1088                        GL_FRAMEBUFFER,
1089                        GL_COLOR_ATTACHMENT0 + i as u32,
1090                        GL_TEXTURE_2D,
1091                        raw,
1092                        0,
1093                    );
1094                }
1095            }
1096            if let Some(depth_img) = depth_img {
1097                let texture = self.textures.get(depth_img);
1098                if texture.params.sample_count > 1 {
1099                    let raw = texture.raw.texture().unwrap();
1100                    glFramebufferRenderbuffer(
1101                        GL_FRAMEBUFFER,
1102                        GL_DEPTH_ATTACHMENT,
1103                        GL_RENDERBUFFER,
1104                        raw,
1105                    );
1106                } else {
1107                    let raw = texture.raw.texture().unwrap();
1108                    glFramebufferTexture2D(
1109                        GL_FRAMEBUFFER,
1110                        GL_DEPTH_ATTACHMENT,
1111                        GL_TEXTURE_2D,
1112                        raw,
1113                        0,
1114                    );
1115                }
1116            }
1117            let mut attachments = vec![];
1118            for i in 0..color_img.len() {
1119                attachments.push(GL_COLOR_ATTACHMENT0 + i as u32);
1120            }
1121
1122            if color_img.len() > 1 {
1123                glDrawBuffers(color_img.len() as _, attachments.as_ptr() as _);
1124            }
1125
1126            if let Some(resolve_img) = resolve_img {
1127                resolves = Some(vec![]);
1128                let resolves = resolves.as_mut().unwrap();
1129                for (i, resolve_img) in resolve_img.iter().enumerate() {
1130                    let mut resolve_fb = 0;
1131                    glGenFramebuffers(1, &mut resolve_fb as *mut _);
1132                    glBindFramebuffer(GL_FRAMEBUFFER, resolve_fb);
1133                    resolves.push((resolve_fb, *resolve_img));
1134                    let texture = self.textures.get(*resolve_img);
1135                    let raw = texture.raw.texture().unwrap();
1136                    glFramebufferTexture2D(
1137                        GL_FRAMEBUFFER,
1138                        GL_COLOR_ATTACHMENT0 + i as u32,
1139                        GL_TEXTURE_2D,
1140                        raw,
1141                        0,
1142                    );
1143                    let fb_status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
1144                    assert!(fb_status != 0);
1145                    glDrawBuffers(1, attachments.as_ptr() as _);
1146                }
1147            }
1148            glBindFramebuffer(GL_FRAMEBUFFER, self.default_framebuffer);
1149        }
1150        let pass = RenderPassInternal {
1151            gl_fb,
1152            color_textures: color_img.to_vec(),
1153            resolves,
1154            depth_texture: depth_img,
1155        };
1156
1157        RenderPass(self.passes.add(pass))
1158    }
1159    fn render_pass_color_attachments(&self, render_pass: RenderPass) -> &[TextureId] {
1160        &self.passes[render_pass.0].color_textures
1161    }
1162    fn delete_render_pass(&mut self, render_pass: RenderPass) {
1163        let pass_id = render_pass.0;
1164
1165        let render_pass = self.passes.remove(pass_id);
1166
1167        unsafe { glDeleteFramebuffers(1, &render_pass.gl_fb as *const _) }
1168
1169        for color_texture in &render_pass.color_textures {
1170            self.delete_texture(*color_texture);
1171        }
1172        if let Some(depth_texture) = render_pass.depth_texture {
1173            self.delete_texture(depth_texture);
1174        }
1175    }
1176
1177    fn new_pipeline(
1178        &mut self,
1179        buffer_layout: &[BufferLayout],
1180        attributes: &[VertexAttribute],
1181        shader: ShaderId,
1182        params: PipelineParams,
1183    ) -> Pipeline {
1184        #[derive(Clone, Copy, Default)]
1185        struct BufferCacheData {
1186            stride: i32,
1187            offset: i64,
1188        }
1189
1190        let mut buffer_cache: Vec<BufferCacheData> =
1191            vec![BufferCacheData::default(); buffer_layout.len()];
1192
1193        for VertexAttribute {
1194            format,
1195            buffer_index,
1196            ..
1197        } in attributes
1198        {
1199            let layout = buffer_layout.get(*buffer_index).unwrap_or_else(|| panic!());
1200            let cache = buffer_cache
1201                .get_mut(*buffer_index)
1202                .unwrap_or_else(|| panic!());
1203
1204            if layout.stride == 0 {
1205                cache.stride += format.size_bytes();
1206            } else {
1207                cache.stride = layout.stride;
1208            }
1209            // WebGL 1 limitation
1210            assert!(cache.stride <= 255);
1211        }
1212
1213        let program = self.shaders[shader.0].program;
1214
1215        let attributes_len = attributes
1216            .iter()
1217            .map(|layout| match layout.format {
1218                VertexFormat::Mat4 => 4,
1219                _ => 1,
1220            })
1221            .sum();
1222
1223        let mut vertex_layout: Vec<Option<VertexAttributeInternal>> = vec![None; attributes_len];
1224
1225        for VertexAttribute {
1226            name,
1227            format,
1228            buffer_index,
1229            gl_pass_as_float,
1230        } in attributes
1231        {
1232            let buffer_data = &mut buffer_cache
1233                .get_mut(*buffer_index)
1234                .unwrap_or_else(|| panic!());
1235            let layout = buffer_layout.get(*buffer_index).unwrap_or_else(|| panic!());
1236
1237            let cname = CString::new(*name).unwrap_or_else(|e| panic!("{}", e));
1238            let attr_loc = unsafe { glGetAttribLocation(program, cname.as_ptr() as *const _) };
1239            let attr_loc = if attr_loc == -1 { None } else { Some(attr_loc) };
1240            let divisor = if layout.step_func == VertexStep::PerVertex {
1241                0
1242            } else {
1243                layout.step_rate
1244            };
1245
1246            let mut attributes_count: usize = 1;
1247            let mut format = *format;
1248
1249            if format == VertexFormat::Mat4 {
1250                format = VertexFormat::Float4;
1251                attributes_count = 4;
1252            }
1253            for i in 0..attributes_count {
1254                if let Some(attr_loc) = attr_loc {
1255                    let attr_loc = attr_loc as GLuint + i as GLuint;
1256
1257                    let attr = VertexAttributeInternal {
1258                        attr_loc,
1259                        size: format.components(),
1260                        type_: format.type_(),
1261                        offset: buffer_data.offset,
1262                        stride: buffer_data.stride,
1263                        buffer_index: *buffer_index,
1264                        divisor,
1265                        gl_pass_as_float: *gl_pass_as_float,
1266                    };
1267
1268                    assert!(
1269                        attr_loc < vertex_layout.len() as u32,
1270                        "attribute: {} outside of allocated attributes array len: {}",
1271                        name,
1272                        vertex_layout.len()
1273                    );
1274                    vertex_layout[attr_loc as usize] = Some(attr);
1275                }
1276                buffer_data.offset += format.size_bytes() as i64
1277            }
1278        }
1279
1280        let pipeline = PipelineInternal {
1281            layout: vertex_layout,
1282            shader,
1283            params,
1284        };
1285
1286        Pipeline(self.pipelines.add(pipeline))
1287    }
1288
1289    fn apply_pipeline(&mut self, pipeline: &Pipeline) {
1290        self.cache.cur_pipeline = Some(*pipeline);
1291
1292        {
1293            let pipeline = &self.pipelines[pipeline.0];
1294            let shader = &self.shaders[pipeline.shader.0];
1295            unsafe {
1296                glUseProgram(shader.program);
1297            }
1298
1299            unsafe {
1300                glEnable(GL_SCISSOR_TEST);
1301            }
1302
1303            if pipeline.params.depth_write {
1304                unsafe {
1305                    glEnable(GL_DEPTH_TEST);
1306                    glDepthFunc(pipeline.params.depth_test.into())
1307                }
1308            } else {
1309                unsafe {
1310                    glDisable(GL_DEPTH_TEST);
1311                }
1312            }
1313
1314            match pipeline.params.front_face_order {
1315                FrontFaceOrder::Clockwise => unsafe {
1316                    glFrontFace(GL_CW);
1317                },
1318                FrontFaceOrder::CounterClockwise => unsafe {
1319                    glFrontFace(GL_CCW);
1320                },
1321            }
1322        }
1323
1324        self.set_cull_face(self.pipelines[pipeline.0].params.cull_face);
1325        self.set_blend(
1326            self.pipelines[pipeline.0].params.color_blend,
1327            self.pipelines[pipeline.0].params.alpha_blend,
1328        );
1329
1330        self.set_stencil(self.pipelines[pipeline.0].params.stencil_test);
1331        self.set_color_write(self.pipelines[pipeline.0].params.color_write);
1332    }
1333
1334    fn new_buffer(
1335        &mut self,
1336        type_: BufferType,
1337        usage: BufferUsage,
1338        data: BufferSource,
1339    ) -> BufferId {
1340        let gl_target = gl_buffer_target(&type_);
1341        let gl_usage = gl_usage(&usage);
1342        let (size, element_size) = match &data {
1343            BufferSource::Slice(data) => (data.size, data.element_size),
1344            BufferSource::Empty { size, element_size } => (*size, *element_size),
1345        };
1346        let index_type = match type_ {
1347            BufferType::IndexBuffer
1348                if element_size == 1 || element_size == 2 || element_size == 4 =>
1349            {
1350                Some(element_size as u32)
1351            }
1352            BufferType::IndexBuffer => panic!("unsupported index buffer dimension"),
1353            BufferType::VertexBuffer => None,
1354        };
1355        let mut gl_buf: u32 = 0;
1356
1357        unsafe {
1358            glGenBuffers(1, &mut gl_buf as *mut _);
1359            self.cache.store_buffer_binding(gl_target);
1360            self.cache.bind_buffer(gl_target, gl_buf, index_type);
1361
1362            glBufferData(gl_target, size as _, std::ptr::null() as *const _, gl_usage);
1363            if let BufferSource::Slice(data) = data {
1364                debug_assert!(data.is_slice);
1365                glBufferSubData(gl_target, 0, size as _, data.ptr as _);
1366            }
1367            self.cache.restore_buffer_binding(gl_target);
1368        }
1369
1370        let buffer = Buffer {
1371            gl_buf,
1372            buffer_type: type_,
1373            size,
1374            index_type,
1375        };
1376
1377        BufferId(self.buffers.add(buffer))
1378    }
1379
1380    fn buffer_update(&mut self, buffer: BufferId, data: BufferSource) {
1381        let data = match data {
1382            BufferSource::Slice(data) => data,
1383            _ => panic!("buffer_update expects BufferSource::slice"),
1384        };
1385        debug_assert!(data.is_slice);
1386        let buffer = &self.buffers[buffer.0];
1387
1388        if matches!(buffer.buffer_type, BufferType::IndexBuffer) {
1389            assert!(buffer.index_type.is_some());
1390            assert!(data.element_size as u32 == buffer.index_type.unwrap());
1391        };
1392
1393        let size = data.size;
1394
1395        assert!(size <= buffer.size);
1396
1397        let gl_target = gl_buffer_target(&buffer.buffer_type);
1398        self.cache.store_buffer_binding(gl_target);
1399        self.cache
1400            .bind_buffer(gl_target, buffer.gl_buf, buffer.index_type);
1401        unsafe { glBufferSubData(gl_target, 0, size as _, data.ptr as _) };
1402        self.cache.restore_buffer_binding(gl_target);
1403    }
1404
1405    /// Size of buffer in bytes
1406    fn buffer_size(&mut self, buffer: BufferId) -> usize {
1407        self.buffers[buffer.0].size
1408    }
1409
1410    /// Delete GPU buffer, leaving handle unmodified.
1411    ///
1412    /// More high-level code on top of miniquad probably is going to call this in Drop implementation of some
1413    /// more RAII buffer object.
1414    ///
1415    /// There is no protection against using deleted textures later. However its not an UB in OpenGl and thats why
1416    /// this function is not marked as unsafe
1417    fn delete_buffer(&mut self, buffer: BufferId) {
1418        unsafe { glDeleteBuffers(1, &self.buffers[buffer.0].gl_buf as *const _) }
1419        self.cache.clear_buffer_bindings();
1420        self.cache.clear_vertex_attributes();
1421        self.buffers.remove(buffer.0);
1422    }
1423
1424    /// Set a new viewport rectangle.
1425    /// Should be applied after begin_pass.
1426    fn apply_viewport(&mut self, x: i32, y: i32, w: i32, h: i32) {
1427        unsafe {
1428            glViewport(x, y, w, h);
1429        }
1430    }
1431
1432    /// Set a new scissor rectangle.
1433    /// Should be applied after begin_pass.
1434    fn apply_scissor_rect(&mut self, x: i32, y: i32, w: i32, h: i32) {
1435        unsafe {
1436            glScissor(x, y, w, h);
1437        }
1438    }
1439
1440    fn apply_bindings_from_slice(
1441        &mut self,
1442        vertex_buffers: &[BufferId],
1443        index_buffer: BufferId,
1444        textures: &[TextureId],
1445    ) {
1446        let pip = &self.pipelines[self.cache.cur_pipeline.unwrap().0];
1447        let shader = &self.shaders[pip.shader.0];
1448
1449        for (n, shader_image) in shader.images.iter().enumerate() {
1450            let bindings_image = textures
1451                .get(n)
1452                .unwrap_or_else(|| panic!("Image count in bindings and shader did not match!"));
1453            if let Some(gl_loc) = shader_image.gl_loc {
1454                let texture = self.textures.get(*bindings_image);
1455                let raw = match texture.raw {
1456                    TextureOrRenderbuffer::Texture(id) => id,
1457                    TextureOrRenderbuffer::Renderbuffer(id) => id,
1458                };
1459                unsafe {
1460                    self.cache.bind_texture(n, texture.params.kind.into(), raw);
1461                    glUniform1i(gl_loc, n as i32);
1462                }
1463            }
1464        }
1465
1466        self.cache.bind_buffer(
1467            GL_ELEMENT_ARRAY_BUFFER,
1468            self.buffers[index_buffer.0].gl_buf,
1469            self.buffers[index_buffer.0].index_type,
1470        );
1471
1472        let pip = &self.pipelines[self.cache.cur_pipeline.unwrap().0];
1473
1474        for attr_index in 0..MAX_VERTEX_ATTRIBUTES {
1475            let cached_attr = &mut self.cache.attributes[attr_index];
1476
1477            let pip_attribute = pip.layout.get(attr_index).copied();
1478
1479            if let Some(Some(attribute)) = pip_attribute {
1480                assert!(
1481                    attribute.buffer_index < vertex_buffers.len(),
1482                    "Attribute index outside of vertex_buffers length"
1483                );
1484                let vb = vertex_buffers[attribute.buffer_index];
1485                let vb = self.buffers[vb.0];
1486
1487                if cached_attr.map_or(true, |cached_attr| {
1488                    attribute != cached_attr.attribute || cached_attr.gl_vbuf != vb.gl_buf
1489                }) {
1490                    self.cache
1491                        .bind_buffer(GL_ARRAY_BUFFER, vb.gl_buf, vb.index_type);
1492
1493                    unsafe {
1494                        match attribute.type_ {
1495                            GL_INT | GL_UNSIGNED_INT | GL_SHORT | GL_UNSIGNED_SHORT
1496                            | GL_UNSIGNED_BYTE | GL_BYTE
1497                                if !attribute.gl_pass_as_float =>
1498                            {
1499                                glVertexAttribIPointer(
1500                                    attr_index as GLuint,
1501                                    attribute.size,
1502                                    attribute.type_,
1503                                    attribute.stride,
1504                                    attribute.offset as *mut _,
1505                                )
1506                            }
1507                            _ => glVertexAttribPointer(
1508                                attr_index as GLuint,
1509                                attribute.size,
1510                                attribute.type_,
1511                                GL_FALSE as u8,
1512                                attribute.stride,
1513                                attribute.offset as *mut _,
1514                            ),
1515                        }
1516                        if self.info.features.instancing {
1517                            glVertexAttribDivisor(attr_index as GLuint, attribute.divisor as u32);
1518                        }
1519                        glEnableVertexAttribArray(attr_index as GLuint);
1520                    };
1521
1522                    let cached_attr = &mut self.cache.attributes[attr_index];
1523                    *cached_attr = Some(CachedAttribute {
1524                        attribute,
1525                        gl_vbuf: vb.gl_buf,
1526                    });
1527                }
1528            } else if cached_attr.is_some() {
1529                unsafe {
1530                    glDisableVertexAttribArray(attr_index as GLuint);
1531                }
1532                *cached_attr = None;
1533            }
1534        }
1535    }
1536
1537    fn apply_uniforms_from_bytes(&mut self, uniform_ptr: *const u8, size: usize) {
1538        let pip = &self.pipelines[self.cache.cur_pipeline.unwrap().0];
1539        let shader = &self.shaders[pip.shader.0];
1540
1541        let mut offset = 0;
1542
1543        for uniform in shader.uniforms.iter() {
1544            use UniformType::*;
1545
1546            assert!(
1547                offset as i32 <= size as i32 - uniform.uniform_type.size() as i32 / 4,
1548                "Uniforms struct does not match shader uniforms layout"
1549            );
1550
1551            unsafe {
1552                let data = (uniform_ptr as *const f32).add(offset);
1553                let data_int = (uniform_ptr as *const i32).add(offset);
1554
1555                if let Some(gl_loc) = uniform.gl_loc {
1556                    match uniform.uniform_type {
1557                        Float1 => {
1558                            glUniform1fv(gl_loc, uniform.array_count, data);
1559                        }
1560                        Float2 => {
1561                            glUniform2fv(gl_loc, uniform.array_count, data);
1562                        }
1563                        Float3 => {
1564                            glUniform3fv(gl_loc, uniform.array_count, data);
1565                        }
1566                        Float4 => {
1567                            glUniform4fv(gl_loc, uniform.array_count, data);
1568                        }
1569                        Int1 => {
1570                            glUniform1iv(gl_loc, uniform.array_count, data_int);
1571                        }
1572                        Int2 => {
1573                            glUniform2iv(gl_loc, uniform.array_count, data_int);
1574                        }
1575                        Int3 => {
1576                            glUniform3iv(gl_loc, uniform.array_count, data_int);
1577                        }
1578                        Int4 => {
1579                            glUniform4iv(gl_loc, uniform.array_count, data_int);
1580                        }
1581                        Mat4 => {
1582                            glUniformMatrix4fv(gl_loc, uniform.array_count, 0, data);
1583                        }
1584                    }
1585                }
1586            }
1587            offset += uniform.uniform_type.size() / 4 * uniform.array_count as usize;
1588        }
1589    }
1590
1591    fn clear(
1592        &mut self,
1593        color: Option<(f32, f32, f32, f32)>,
1594        depth: Option<f32>,
1595        stencil: Option<i32>,
1596    ) {
1597        let mut bits = 0;
1598        if let Some((r, g, b, a)) = color {
1599            bits |= GL_COLOR_BUFFER_BIT;
1600            unsafe {
1601                glClearColor(r, g, b, a);
1602            }
1603        }
1604
1605        if let Some(v) = depth {
1606            bits |= GL_DEPTH_BUFFER_BIT;
1607            unsafe {
1608                glClearDepthf(v);
1609            }
1610        }
1611
1612        if let Some(v) = stencil {
1613            bits |= GL_STENCIL_BUFFER_BIT;
1614            unsafe {
1615                glClearStencil(v);
1616            }
1617        }
1618
1619        if bits != 0 {
1620            unsafe {
1621                glClear(bits);
1622            }
1623        }
1624    }
1625
1626    fn begin_default_pass(&mut self, action: PassAction) {
1627        self.begin_pass(None, action);
1628    }
1629
1630    fn begin_pass(&mut self, pass: Option<RenderPass>, action: PassAction) {
1631        self.cache.cur_pass = pass;
1632        let (framebuffer, w, h) = match pass {
1633            None => {
1634                let (screen_width, screen_height) = window::screen_size();
1635
1636                (
1637                    self.default_framebuffer,
1638                    screen_width as i32,
1639                    screen_height as i32,
1640                )
1641            }
1642            Some(pass) => {
1643                let pass = &self.passes[pass.0];
1644                // new_render_pass will panic with both color and depth components none
1645                // so unwrap is safe here
1646                let texture = pass
1647                    .color_textures
1648                    .first()
1649                    .copied()
1650                    .or(pass.depth_texture)
1651                    .unwrap();
1652                (
1653                    pass.gl_fb,
1654                    self.textures.get(texture).params.width as i32,
1655                    self.textures.get(texture).params.height as i32,
1656                )
1657            }
1658        };
1659        unsafe {
1660            glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
1661            glViewport(0, 0, w, h);
1662            glScissor(0, 0, w, h);
1663        }
1664        match action {
1665            PassAction::Nothing => {}
1666            PassAction::Clear {
1667                color,
1668                depth,
1669                stencil,
1670            } => {
1671                self.clear(color, depth, stencil);
1672            }
1673        }
1674    }
1675
1676    fn end_render_pass(&mut self) {
1677        unsafe {
1678            if let Some(pass) = self.cache.cur_pass.take() {
1679                let pass = &self.passes[pass.0];
1680                if let Some(resolves) = &pass.resolves {
1681                    glBindFramebuffer(GL_READ_FRAMEBUFFER, pass.gl_fb);
1682                    for (i, (resolve_fb, resolve_img)) in resolves.iter().enumerate() {
1683                        let texture = self.textures.get(*resolve_img);
1684                        let w = texture.params.width;
1685                        let h = texture.params.height;
1686                        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, *resolve_fb);
1687                        glReadBuffer(GL_COLOR_ATTACHMENT0 + i as u32);
1688                        glBlitFramebuffer(
1689                            0,
1690                            0,
1691                            w as _,
1692                            h as _,
1693                            0,
1694                            0,
1695                            w as _,
1696                            h as _,
1697                            GL_COLOR_BUFFER_BIT,
1698                            GL_NEAREST,
1699                        );
1700                    }
1701                }
1702            }
1703            glBindFramebuffer(GL_FRAMEBUFFER, self.default_framebuffer);
1704            self.cache.bind_buffer(GL_ARRAY_BUFFER, 0, None);
1705            self.cache.bind_buffer(GL_ELEMENT_ARRAY_BUFFER, 0, None);
1706        }
1707    }
1708
1709    fn commit_frame(&mut self) {
1710        self.cache.clear_buffer_bindings();
1711        self.cache.clear_texture_bindings();
1712    }
1713
1714    fn draw(&self, base_element: i32, num_elements: i32, num_instances: i32) {
1715        assert!(
1716            self.cache.cur_pipeline.is_some(),
1717            "Drawing without any binded pipeline"
1718        );
1719
1720        if !self.info.features.instancing && num_instances != 1 {
1721            eprintln!("Instanced rendering is not supported by the GPU");
1722            eprintln!("Ignoring this draw call");
1723            return;
1724        }
1725
1726        let pip = &self.pipelines[self.cache.cur_pipeline.unwrap().0];
1727        let primitive_type = pip.params.primitive_type.into();
1728        let index_type = self.cache.index_type.expect("Unset index buffer type");
1729
1730        unsafe {
1731            glDrawElementsInstanced(
1732                primitive_type,
1733                num_elements,
1734                match index_type {
1735                    1 => GL_UNSIGNED_BYTE,
1736                    2 => GL_UNSIGNED_SHORT,
1737                    4 => GL_UNSIGNED_INT,
1738                    _ => panic!("Unsupported index buffer type!"),
1739                },
1740                (index_type as i32 * base_element) as *mut _,
1741                num_instances,
1742            );
1743        }
1744    }
1745}