solstice/
lib.rs

1pub use glow;
2
3#[cfg(feature = "derive")]
4extern crate solstice_derive;
5
6pub mod buffer;
7pub mod canvas;
8pub mod image;
9pub mod mesh;
10pub mod quad_batch;
11pub mod shader;
12pub mod texture;
13pub mod vertex;
14pub mod viewport;
15
16mod gl;
17
18use glow::HasContext;
19use slotmap::SlotMap;
20use std::{
21    fmt::{Debug, Error, Formatter},
22    str::FromStr,
23};
24
25#[derive(Debug)]
26pub enum GraphicsError {
27    ShaderError(shader::ShaderError),
28    TextureError,
29    BufferError,
30    FramebufferError,
31    RenderbufferError,
32}
33
34impl std::fmt::Display for GraphicsError {
35    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
36        write!(f, "{:?}", self)
37    }
38}
39
40impl std::error::Error for GraphicsError {}
41
42type GLContext = glow::Context;
43
44type GLBuffer = <GLContext as HasContext>::Buffer;
45type GLProgram = <GLContext as HasContext>::Program;
46type GLTexture = <GLContext as HasContext>::Texture;
47type GLFramebuffer = <GLContext as HasContext>::Framebuffer;
48type GLRenderbuffer = <GLContext as HasContext>::Renderbuffer;
49type GLUniformLocation = <GLContext as HasContext>::UniformLocation;
50
51slotmap::new_key_type! {
52    pub struct ShaderKey;
53    pub struct BufferKey;
54    pub struct TextureKey;
55    pub struct FramebufferKey;
56    pub struct RenderbufferKey;
57}
58
59pub struct DebugGroup<'a> {
60    ctx: &'a GLContext,
61}
62
63impl<'a> DebugGroup<'a> {
64    pub fn new(ctx: &'a GLContext, message: &str) -> Self {
65        if ctx.supports_debug() {
66            unsafe {
67                ctx.push_debug_group(glow::DEBUG_SOURCE_APPLICATION, 0, message);
68            }
69        }
70        Self { ctx }
71    }
72}
73
74impl<'a> Drop for DebugGroup<'a> {
75    fn drop(&mut self) {
76        if self.ctx.supports_debug() {
77            unsafe {
78                self.ctx.pop_debug_group();
79            }
80        }
81    }
82}
83
84#[derive(Copy, Clone, Debug, Eq, PartialEq)]
85#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
86pub enum PixelFormat {
87    Unknown,
88
89    // "regular" formats
90    LUMINANCE,
91    RG8,
92    RGB8,
93    RGBA8,
94    SRGBA8,
95    R16,
96    RG16,
97    RGBA16,
98    R16F,
99    RG16F,
100    RGBA16F,
101    R32F,
102    RG32F,
103    RGBA32F,
104    Alpha,
105
106    // depth/stencil formats
107    Stencil8,
108    Depth16,
109    Depth24,
110    Depth32F,
111    Depth24Stencil8,
112    Depth32fStencil8,
113}
114
115fn target_to_index(target: canvas::Target) -> usize {
116    match target {
117        canvas::Target::Draw => 0,
118        canvas::Target::Read => 1,
119        canvas::Target::All => 0,
120    }
121}
122
123fn buffer_type_to_index(buffer_type: buffer::BufferType) -> usize {
124    match buffer_type {
125        buffer::BufferType::Vertex => 0,
126        buffer::BufferType::Index => 1,
127    }
128}
129
130#[derive(Debug, Copy, Clone, Eq, PartialEq)]
131pub enum VertexWinding {
132    ClockWise,
133    CounterClockWise,
134}
135
136#[derive(Debug, Copy, Clone, Eq, PartialEq)]
137pub enum DepthFunction {
138    Never,
139    Less,
140    Equal,
141    LessEqual,
142    Greater,
143    NotEqual,
144    GreaterEqual,
145    Always,
146}
147
148impl DepthFunction {
149    pub fn to_gl(&self) -> u32 {
150        match self {
151            DepthFunction::Never => glow::NEVER,
152            DepthFunction::Less => glow::LESS,
153            DepthFunction::Equal => glow::EQUAL,
154            DepthFunction::LessEqual => glow::LEQUAL,
155            DepthFunction::Greater => glow::GREATER,
156            DepthFunction::NotEqual => glow::NOTEQUAL,
157            DepthFunction::GreaterEqual => glow::GEQUAL,
158            DepthFunction::Always => glow::ALWAYS,
159        }
160    }
161}
162
163#[derive(Debug, Copy, Clone, Eq, PartialEq)]
164pub enum CullFace {
165    Back,
166    Front,
167    FrontAndBack,
168}
169
170impl CullFace {
171    pub fn to_gl(&self) -> u32 {
172        match self {
173            CullFace::Back => glow::BACK,
174            CullFace::Front => glow::FRONT,
175            CullFace::FrontAndBack => glow::FRONT_AND_BACK,
176        }
177    }
178}
179
180pub enum Feature {
181    DepthTest(DepthFunction),
182    CullFace(CullFace, VertexWinding),
183}
184
185struct GLConstants {
186    max_vertex_attributes: usize,
187    max_texture_units: usize,
188}
189
190#[derive(Copy, Clone, Debug, Eq, PartialEq)]
191pub enum DrawMode {
192    Points,
193    Lines,
194    LineLoop,
195    LineStrip,
196    Triangles,
197    TriangleStrip,
198    TriangleFan,
199}
200
201#[derive(Copy, Clone, PartialEq, Eq)]
202pub struct TextureUnit {
203    index: u32,
204    gl: u32,
205}
206
207impl From<u32> for TextureUnit {
208    fn from(v: u32) -> Self {
209        let inner = match v {
210            0 => glow::TEXTURE0,
211            1 => glow::TEXTURE1,
212            2 => glow::TEXTURE2,
213            3 => glow::TEXTURE3,
214            4 => glow::TEXTURE4,
215            5 => glow::TEXTURE5,
216            6 => glow::TEXTURE6,
217            7 => glow::TEXTURE7,
218            8 => glow::TEXTURE8,
219            9 => glow::TEXTURE9,
220            10 => glow::TEXTURE10,
221            11 => glow::TEXTURE11,
222            12 => glow::TEXTURE12,
223            13 => glow::TEXTURE13,
224            14 => glow::TEXTURE14,
225            15 => glow::TEXTURE15,
226            16 => glow::TEXTURE16,
227            17 => glow::TEXTURE17,
228            18 => glow::TEXTURE18,
229            19 => glow::TEXTURE19,
230            20 => glow::TEXTURE20,
231            21 => glow::TEXTURE21,
232            22 => glow::TEXTURE22,
233            23 => glow::TEXTURE23,
234            24 => glow::TEXTURE24,
235            25 => glow::TEXTURE25,
236            26 => glow::TEXTURE26,
237            27 => glow::TEXTURE27,
238            28 => glow::TEXTURE28,
239            29 => glow::TEXTURE29,
240            30 => glow::TEXTURE30,
241            31 => glow::TEXTURE31,
242            _ => panic!("unsupported texture unit: {}", v),
243        };
244        TextureUnit {
245            index: v,
246            gl: inner,
247        }
248    }
249}
250
251impl From<i32> for TextureUnit {
252    fn from(v: i32) -> Self {
253        (v as u32).into()
254    }
255}
256
257impl From<usize> for TextureUnit {
258    fn from(v: usize) -> Self {
259        (v as u32).into()
260    }
261}
262
263#[derive(Copy, Clone, Default)]
264pub struct GLVersion {
265    major: u32,
266    minor: u32,
267    gles: bool,
268}
269
270impl FromStr for GLVersion {
271    type Err = ();
272
273    fn from_str(s: &str) -> Result<Self, Self::Err> {
274        // the webgl property is about whether the string was parsed as webgl
275        // rather than whether the gl implementation is webgl
276        let (major, minor, gles, webgl) = if s.starts_with("WebGL ") {
277            (s.chars().nth(6), s.chars().nth(8), true, true)
278        } else if s.contains("OpenGL ES ") {
279            (s.chars().nth(10), s.chars().nth(12), true, false)
280        } else {
281            (s.chars().next(), s.chars().nth(2), false, false)
282        };
283
284        // this conflates WebGL X with OpenGL ES X+1 but
285        // it's done intentionally so it's okay?
286        let major_incr = if webgl { 1 } else { 0 };
287
288        let major = major.and_then(|c| c.to_digit(10));
289        let minor = minor.and_then(|c| c.to_digit(10));
290        match (major, minor) {
291            (Some(major), Some(minor)) => Ok(Self {
292                major: major + major_incr as u32,
293                minor: minor as u32,
294                gles,
295            }),
296            _ => Err(()),
297        }
298    }
299}
300
301impl Debug for GLVersion {
302    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
303        write!(
304            f,
305            "GLVersion {{ major: {}, minor: {}, ES: {} }}",
306            self.major, self.minor, self.gles
307        )
308    }
309}
310
311// a caching, convenience and safety layer around glow
312pub struct Context {
313    ctx: GLContext,
314    version: GLVersion,
315    gl_constants: GLConstants,
316    shaders: SlotMap<ShaderKey, GLProgram>,
317    active_shader: Option<ShaderKey>,
318    buffers: SlotMap<BufferKey, GLBuffer>,
319    active_buffers: [Option<BufferKey>; 2],
320    textures: SlotMap<TextureKey, GLTexture>,
321    bound_textures: Vec<Vec<Option<GLTexture>>>,
322    framebuffers: SlotMap<FramebufferKey, GLFramebuffer>,
323    active_framebuffer: [Option<FramebufferKey>; 2],
324    renderbuffers: SlotMap<RenderbufferKey, GLRenderbuffer>,
325    active_renderbuffer: Option<RenderbufferKey>,
326    current_texture_unit: TextureUnit,
327    current_viewport: viewport::Viewport<i32>,
328    current_scissor: Option<viewport::Viewport<i32>>,
329    enabled_attributes: u32, // a bitmask that represents the vertex attribute state
330}
331
332impl Context {
333    pub fn new(ctx: GLContext) -> Self {
334        let gl_constants = GLConstants {
335            max_vertex_attributes: unsafe {
336                ctx.get_parameter_i32(glow::MAX_VERTEX_ATTRIBS) as usize
337            },
338            max_texture_units: unsafe {
339                ctx.get_parameter_i32(glow::MAX_COMBINED_TEXTURE_IMAGE_UNITS) as usize
340            },
341        };
342
343        let bound_textures = texture::TextureType::enumerate()
344            .iter()
345            .map(|_tt| vec![None; gl_constants.max_texture_units])
346            .collect();
347
348        for texture_unit in 0..gl_constants.max_texture_units {
349            unsafe {
350                ctx.active_texture(glow::TEXTURE0 + texture_unit as u32);
351                // do this for every supported texture type
352                for texture_type in texture::TextureType::enumerate() {
353                    if texture_type.is_supported() {
354                        ctx.bind_texture(gl::texture::to_gl(*texture_type), None);
355                    }
356                }
357            }
358        }
359        unsafe { ctx.active_texture(glow::TEXTURE0) }
360        unsafe {
361            // TODO: this should be left to the consumer
362            ctx.pixel_store_i32(glow::UNPACK_ALIGNMENT, 1);
363            ctx.enable(glow::BLEND);
364            ctx.blend_equation(glow::FUNC_ADD);
365            ctx.blend_func_separate(
366                glow::SRC_ALPHA,
367                glow::ONE_MINUS_SRC_ALPHA,
368                glow::ONE,
369                glow::ONE_MINUS_SRC_ALPHA,
370            );
371
372            ctx.bind_vertex_array(ctx.create_vertex_array().ok());
373        }
374
375        let version = {
376            let str_version = unsafe { ctx.get_parameter_string(glow::VERSION) };
377            str_version.parse::<GLVersion>().unwrap_or_default()
378        };
379
380        let mut ctx = Self {
381            ctx,
382            version,
383            gl_constants,
384            shaders: SlotMap::with_key(),
385            active_shader: None,
386            buffers: SlotMap::with_key(),
387            active_buffers: [None; 2],
388            textures: SlotMap::with_key(),
389            bound_textures,
390            framebuffers: SlotMap::with_key(),
391            active_framebuffer: [None; 2],
392            renderbuffers: SlotMap::with_key(),
393            active_renderbuffer: None,
394            current_texture_unit: 0.into(),
395            current_viewport: viewport::Viewport::default(),
396            current_scissor: None,
397            enabled_attributes: std::u32::MAX,
398        };
399        ctx.set_vertex_attributes(0, &[]);
400        ctx
401    }
402
403    pub fn enable(&mut self, feature: Feature) {
404        match feature {
405            Feature::DepthTest(func) => unsafe {
406                self.ctx.enable(glow::DEPTH_TEST);
407                self.ctx.depth_func(func.to_gl());
408            },
409            Feature::CullFace(cull_face, winding_order) => unsafe {
410                self.ctx.enable(glow::CULL_FACE);
411                self.ctx.cull_face(cull_face.to_gl());
412                self.ctx
413                    .front_face(gl::vertex_winding::to_gl(winding_order));
414            },
415        }
416    }
417
418    pub fn disable(&mut self, feature: Feature) {
419        match feature {
420            Feature::DepthTest(_) => unsafe { self.ctx.disable(glow::DEPTH_TEST) },
421            Feature::CullFace(_, _) => unsafe { self.ctx.disable(glow::CULL_FACE) },
422        }
423    }
424
425    pub fn new_debug_group(&self, message: &str) -> DebugGroup {
426        DebugGroup::new(&self.ctx, message)
427    }
428
429    pub fn new_buffer(
430        &mut self,
431        size: usize,
432        buffer_type: buffer::BufferType,
433        usage: buffer::Usage,
434        initial_data: Option<&[u8]>,
435    ) -> Result<BufferKey, GraphicsError> {
436        let vbo = unsafe {
437            let vbo = self
438                .ctx
439                .create_buffer()
440                .map_err(|_| GraphicsError::BufferError)?;
441            self.ctx.bind_buffer(buffer_type.into(), Some(vbo));
442            if let Some(initial_data) = initial_data {
443                self.ctx
444                    .buffer_data_u8_slice(buffer_type.into(), initial_data, usage.to_gl());
445            } else {
446                self.ctx
447                    .buffer_data_size(buffer_type.into(), size as _, usage.to_gl());
448            }
449
450            vbo
451        };
452        let buffer_key = self.buffers.insert(vbo);
453        self.active_buffers[buffer_type_to_index(buffer_type)] = Some(buffer_key);
454        Ok(buffer_key)
455    }
456
457    pub fn destroy_buffer(&mut self, buffer: &buffer::Buffer) {
458        if let Some(gl_buffer) = self.buffers.remove(buffer.handle()) {
459            unsafe {
460                self.ctx.delete_buffer(gl_buffer);
461            }
462        }
463    }
464
465    pub fn bind_buffer(&mut self, buffer_key: BufferKey, buffer_type: buffer::BufferType) {
466        if let Some(&vbo) = self.buffers.get(buffer_key) {
467            let buffer_index = buffer_type_to_index(buffer_type);
468            match self.active_buffers.get_mut(buffer_index) {
469                Some(Some(active_buffer)) => {
470                    if active_buffer != &buffer_key {
471                        *active_buffer = buffer_key;
472                        unsafe { self.ctx.bind_buffer(buffer_type.into(), Some(vbo)) };
473                    }
474                }
475                _ => {
476                    self.active_buffers[buffer_index] = Some(buffer_key);
477                    unsafe { self.ctx.bind_buffer(buffer_type.into(), Some(vbo)) };
478                }
479            }
480        }
481    }
482
483    pub fn buffer_static_draw(&self, buffer: &buffer::Buffer, data: &[u8], offset: usize) {
484        let target = buffer.buffer_type().into();
485        unsafe {
486            self.ctx
487                .buffer_sub_data_u8_slice(target, offset as i32, data)
488        }
489    }
490
491    fn buffer_stream_draw(&self, map: &buffer::MappedBuffer) {
492        let buffer = map.inner();
493        let target = buffer.buffer_type().into();
494        let data = map.memory_map();
495
496        unsafe {
497            // "orphan" current buffer to avoid implicit synchronisation on the GPU:
498            // http://www.seas.upenn.edu/~pcozzi/OpenGLInsights/OpenGLInsights-AsynchronousBufferTransfers.pdf
499            self.ctx
500                .buffer_data_size(target, buffer.size() as i32, buffer.usage().to_gl());
501            self.ctx.buffer_sub_data_u8_slice(target, 0, data);
502        }
503    }
504
505    pub fn unmap_buffer(&mut self, map: &buffer::MappedBuffer) {
506        let buffer = map.inner();
507        self.bind_buffer(buffer.handle(), buffer.buffer_type());
508        if self.buffers.get(buffer.handle()).is_some() {
509            if let Some(modified_range) = map.modified_range() {
510                let modified_offset =
511                    std::cmp::min(modified_range.offset, buffer.size().saturating_sub(1));
512                let modified_size = std::cmp::min(
513                    modified_range.size,
514                    buffer.size().saturating_sub(modified_range.offset),
515                );
516                match buffer.usage() {
517                    buffer::Usage::Stream => self.buffer_stream_draw(map),
518                    buffer::Usage::Static => self.buffer_static_draw(
519                        buffer,
520                        &map.memory_map()[modified_offset..(modified_size + modified_offset)],
521                        modified_offset,
522                    ),
523                    buffer::Usage::Dynamic => {
524                        if modified_size >= buffer.size() / 3 {
525                            self.buffer_stream_draw(map);
526                        } else {
527                            self.buffer_static_draw(
528                                buffer,
529                                &map.memory_map()
530                                    [modified_offset..(modified_size + modified_offset)],
531                                modified_offset,
532                            );
533                        }
534                    }
535                }
536            }
537        } else {
538            log::warn!(
539                "attempted to unmap non-existant buffer, {:?}",
540                buffer.handle()
541            );
542        }
543    }
544
545    pub fn new_shader(
546        &mut self,
547        vertex_source: &str,
548        fragment_source: &str,
549    ) -> Result<ShaderKey, shader::ShaderError> {
550        use shader::*;
551        let program = unsafe {
552            let gl = &self.ctx;
553            let vertex = gl
554                .create_shader(glow::VERTEX_SHADER)
555                .map_err(|_| ShaderError::ResourceCreationError)?;
556            gl.shader_source(vertex, vertex_source);
557            gl.compile_shader(vertex);
558            if !gl.get_shader_compile_status(vertex) {
559                let err = Err(ShaderError::VertexCompileError(
560                    gl.get_shader_info_log(vertex),
561                ));
562                gl.delete_shader(vertex);
563                return err;
564            }
565            let fragment = gl
566                .create_shader(glow::FRAGMENT_SHADER)
567                .expect("Failed to create Fragment shader.");
568            gl.shader_source(fragment, fragment_source);
569            gl.compile_shader(fragment);
570            if !gl.get_shader_compile_status(fragment) {
571                let err = Err(ShaderError::FragmentCompileError(
572                    gl.get_shader_info_log(fragment),
573                ));
574                gl.delete_shader(fragment);
575                return err;
576            }
577            let program = gl.create_program().expect("Failed to create program.");
578            gl.attach_shader(program, vertex);
579            gl.attach_shader(program, fragment);
580            gl.link_program(program);
581            if !gl.get_program_link_status(program) {
582                let err = Err(ShaderError::LinkError(gl.get_program_info_log(program)));
583                gl.delete_program(program);
584                return err;
585            }
586
587            program
588        };
589
590        Ok(self.shaders.insert(program))
591    }
592
593    pub fn get_shader_attributes(&self, shader: ShaderKey) -> Vec<shader::Attribute> {
594        if let Some(program) = self.shaders.get(shader).cloned() {
595            let count = unsafe { self.ctx.get_active_attributes(program) };
596            let mut attributes = Vec::with_capacity(count as usize);
597            for index in 0..count {
598                unsafe {
599                    let glow::ActiveAttribute { name, size, atype } =
600                        self.ctx.get_active_attribute(program, index).unwrap();
601                    if let Some(location) = self.ctx.get_attrib_location(program, name.as_str()) {
602                        // specifically this is for gl_InstanceID
603                        attributes.push(shader::Attribute {
604                            name,
605                            size,
606                            atype: gl::attribute::from_gl(atype),
607                            location,
608                        });
609                    }
610                }
611            }
612            attributes.sort_by(|a, b| a.location.partial_cmp(&b.location).unwrap());
613            attributes
614        } else {
615            Vec::new()
616        }
617    }
618
619    pub fn get_shader_uniforms(&self, shader: ShaderKey) -> Vec<shader::Uniform> {
620        unsafe fn get_initial_uniform_data(
621            gl: &glow::Context,
622            utype: u32,
623            program: GLProgram,
624            location: &GLUniformLocation,
625        ) -> shader::RawUniformValue {
626            use shader::RawUniformValue;
627            macro_rules! get_uniform_data {
628                (f32, 1, $uni_ty:ident, $gl:expr, $program:expr, $location:expr) => {{
629                    let mut data = [0.; 1];
630                    $gl.get_uniform_f32($program, $location, &mut data);
631                    RawUniformValue::$uni_ty(data[0].into())
632                }};
633                (i32, 1, $uni_ty:ident, $gl:expr, $program:expr, $location:expr) => {{
634                    let mut data = [0; 1];
635                    $gl.get_uniform_i32($program, $location, &mut data);
636                    RawUniformValue::$uni_ty(data[0].into())
637                }};
638                (f32, $data_size:expr, $uni_ty:ident, $gl:expr, $program:expr, $location:expr) => {{
639                    let mut data = [0.; $data_size];
640                    $gl.get_uniform_f32($program, $location, &mut data);
641                    RawUniformValue::$uni_ty(data.into())
642                }};
643                (i32, $data_size:expr, $uni_ty:ident, $gl:expr, $program:expr, $location:expr) => {{
644                    let mut data = [0; $data_size];
645                    $gl.get_uniform_i32($program, $location, &mut data);
646                    RawUniformValue::$uni_ty(data.into())
647                }};
648            }
649
650            match utype {
651                glow::FLOAT => get_uniform_data!(f32, 1, Float, gl, program, location),
652                glow::FLOAT_VEC2 => get_uniform_data!(f32, 2, Vec2, gl, program, location),
653                glow::FLOAT_VEC3 => get_uniform_data!(f32, 3, Vec3, gl, program, location),
654                glow::FLOAT_VEC4 => get_uniform_data!(f32, 4, Vec4, gl, program, location),
655                glow::FLOAT_MAT2 => get_uniform_data!(f32, 4, Mat2, gl, program, location),
656                glow::FLOAT_MAT3 => get_uniform_data!(f32, 9, Mat3, gl, program, location),
657                glow::FLOAT_MAT4 => get_uniform_data!(f32, 16, Mat4, gl, program, location),
658                glow::INT | glow::SAMPLER_2D | glow::SAMPLER_CUBE => {
659                    get_uniform_data!(i32, 1, SignedInt, gl, program, location)
660                }
661                glow::INT_VEC2 => get_uniform_data!(i32, 2, IntVec2, gl, program, location),
662                glow::INT_VEC3 => get_uniform_data!(i32, 3, IntVec3, gl, program, location),
663                glow::INT_VEC4 => get_uniform_data!(i32, 4, IntVec4, gl, program, location),
664                _ => {
665                    panic!("failed to match uniform type");
666                }
667            }
668        }
669
670        use shader::{Uniform, UniformLocation};
671        let gl = &self.ctx;
672        if let Some(program) = self.shaders.get(shader).cloned() {
673            let count = unsafe { gl.get_active_uniforms(program) };
674            let mut uniforms = Vec::with_capacity(count as usize);
675            for index in 0..count {
676                unsafe {
677                    let glow::ActiveUniform { name, size, utype } =
678                        gl.get_active_uniform(program, index).unwrap();
679                    if size > 1 {
680                        let name = name.trim_end_matches("[0]");
681                        uniforms.extend((0..size).filter_map(|i| {
682                            let name = format!("{}[{}]", name, i);
683                            gl.get_uniform_location(program, name.as_str())
684                                .map(|location| {
685                                    let initial_data =
686                                        get_initial_uniform_data(&gl, utype, program, &location);
687                                    let location = UniformLocation(location);
688                                    Uniform {
689                                        name,
690                                        size: 1,
691                                        utype,
692                                        location,
693                                        initial_data,
694                                    }
695                                })
696                        }));
697                    } else {
698                        if let Some(location) = gl.get_uniform_location(program, name.as_str()) {
699                            let initial_data =
700                                get_initial_uniform_data(&gl, utype, program, &location);
701                            let location = UniformLocation(location);
702                            uniforms.push(Uniform {
703                                name,
704                                size,
705                                utype,
706                                location,
707                                initial_data,
708                            });
709                        }
710                    }
711                }
712            }
713            uniforms
714        } else {
715            Default::default()
716        }
717    }
718
719    pub fn destroy_shader(&mut self, shader: ShaderKey) {
720        match self.shaders.remove(shader) {
721            None => (),
722            Some(shader) => unsafe {
723                self.ctx.delete_program(shader);
724            },
725        }
726    }
727
728    pub fn use_shader<S: shader::Shader + ?Sized>(&mut self, shader: Option<&S>) {
729        match shader {
730            None => {
731                if self.active_shader.is_some() {
732                    self.active_shader = None;
733                    unsafe {
734                        self.ctx.use_program(None);
735                    }
736                }
737            }
738            Some(shader) => {
739                if self.active_shader != Some(shader.handle()) {
740                    match self.shaders.get(shader.handle()) {
741                        None => log::warn!(
742                            "Attempting to bind shader not in cache: {:?}",
743                            shader.handle()
744                        ),
745                        Some(gl_shader) => {
746                            self.active_shader = Some(shader.handle());
747                            unsafe { self.ctx.use_program(Some(*gl_shader)) }
748                        }
749                    }
750                }
751            }
752        }
753    }
754
755    pub fn new_texture(
756        &mut self,
757        texture_type: texture::TextureType,
758    ) -> Result<TextureKey, GraphicsError> {
759        unsafe {
760            let handle = self
761                .ctx
762                .create_texture()
763                .map_err(|_| GraphicsError::TextureError)?;
764            let texture = self.textures.insert(handle);
765            self.ctx.active_texture(glow::TEXTURE0);
766            self.bind_texture_to_unit(texture_type, texture, 0.into());
767            Ok(texture)
768        }
769    }
770
771    pub fn destroy_texture(&mut self, texture_key: TextureKey) {
772        match self.textures.remove(texture_key) {
773            None => (),
774            Some(texture) => unsafe { self.ctx.delete_texture(texture) },
775        }
776    }
777
778    pub fn bind_texture_to_unit(
779        &mut self,
780        texture_type: texture::TextureType,
781        texture_key: TextureKey,
782        texture_unit: TextureUnit,
783    ) {
784        let TextureUnit { index, gl: unit } = texture_unit;
785        let texture_unit_index = index as usize;
786        match (
787            self.textures.get(texture_key),
788            self.bound_textures[texture_type.to_index()][texture_unit_index],
789        ) {
790            (Some(&texture), None) => {
791                if texture_unit != self.current_texture_unit {
792                    unsafe {
793                        self.ctx.active_texture(unit);
794                    }
795                    self.current_texture_unit = texture_unit;
796                }
797                self.bound_textures[texture_type.to_index()][texture_unit_index] = Some(texture);
798                unsafe {
799                    self.ctx
800                        .bind_texture(gl::texture::to_gl(texture_type), Some(texture))
801                }
802            }
803            (Some(&texture), Some(bound_texture)) => {
804                if texture != bound_texture {
805                    if texture_unit != self.current_texture_unit {
806                        unsafe {
807                            self.ctx.active_texture(unit);
808                        }
809                        self.current_texture_unit = texture_unit;
810                    }
811                    self.bound_textures[texture_type.to_index()][texture_unit_index] =
812                        Some(texture);
813                    unsafe {
814                        self.ctx
815                            .bind_texture(gl::texture::to_gl(texture_type), Some(texture))
816                    }
817                }
818            }
819            (None, Some(_)) => {
820                if texture_unit != self.current_texture_unit {
821                    unsafe {
822                        self.ctx.active_texture(unit);
823                    }
824                    self.current_texture_unit = texture_unit;
825                }
826                self.bound_textures[texture_type.to_index()][texture_unit_index] = None;
827                unsafe {
828                    self.ctx
829                        .bind_texture(gl::texture::to_gl(texture_type), None)
830                }
831            }
832            (None, None) => (),
833        }
834    }
835
836    pub fn new_framebuffer(&mut self) -> Result<FramebufferKey, GraphicsError> {
837        let framebuffer = unsafe {
838            self.ctx
839                .create_framebuffer()
840                .map_err(|_| GraphicsError::FramebufferError)?
841        };
842        Ok(self.framebuffers.insert(framebuffer))
843    }
844
845    pub fn destroy_framebuffer(&mut self, framebuffer_key: FramebufferKey) {
846        match self.framebuffers.remove(framebuffer_key) {
847            None => (),
848            Some(framebuffer) => unsafe { self.ctx.delete_framebuffer(framebuffer) },
849        }
850    }
851
852    pub fn bind_framebuffer(
853        &mut self,
854        target: canvas::Target,
855        framebuffer_key: Option<FramebufferKey>,
856    ) {
857        let target_index = target_to_index(target);
858        match (framebuffer_key, self.active_framebuffer[target_index]) {
859            (None, None) => (),
860            (Some(framebuffer_key), None) => match self.framebuffers.get(framebuffer_key) {
861                None => (),
862                Some(framebuffer) => {
863                    self.active_framebuffer[target_index] = Some(framebuffer_key);
864                    unsafe {
865                        self.ctx
866                            .bind_framebuffer(target.to_gl(), Some(*framebuffer))
867                    }
868                }
869            },
870            (Some(framebuffer_key), Some(current_framebuffer_key)) => {
871                if framebuffer_key != current_framebuffer_key {
872                    match self.framebuffers.get(framebuffer_key) {
873                        None => (),
874                        Some(framebuffer) => {
875                            self.active_framebuffer[target_index] = Some(framebuffer_key);
876                            unsafe {
877                                self.ctx
878                                    .bind_framebuffer(target.to_gl(), Some(*framebuffer))
879                            }
880                        }
881                    }
882                }
883            }
884            (None, Some(_current_framebuffer_key)) => {
885                self.active_framebuffer[target_index] = None;
886                unsafe { self.ctx.bind_framebuffer(target.to_gl(), None) }
887            }
888        }
889    }
890
891    pub fn check_framebuffer_status(&self, target: canvas::Target) -> canvas::Status {
892        match unsafe { self.ctx.check_framebuffer_status(target.to_gl()) } {
893            glow::FRAMEBUFFER_COMPLETE => canvas::Status::Complete,
894            glow::FRAMEBUFFER_INCOMPLETE_ATTACHMENT => canvas::Status::IncompleteAttachment,
895            glow::FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT => canvas::Status::MissingAttachment,
896            glow::FRAMEBUFFER_UNSUPPORTED => canvas::Status::Unsupported,
897            glow::FRAMEBUFFER_INCOMPLETE_MULTISAMPLE => canvas::Status::IncompleteMultisample,
898            _ => canvas::Status::Unknown,
899        }
900    }
901
902    pub fn get_active_framebuffer(&self, target: canvas::Target) -> Option<FramebufferKey> {
903        self.active_framebuffer[target_to_index(target)]
904    }
905
906    pub fn framebuffer_texture(
907        &mut self,
908        target: canvas::Target,
909        attachment: canvas::Attachment,
910        texture_type: texture::TextureType,
911        texture_key: TextureKey,
912        level: u32,
913    ) {
914        unsafe {
915            self.ctx.framebuffer_texture_2d(
916                target.to_gl(),
917                attachment.to_gl(),
918                gl::texture::to_gl(texture_type),
919                self.textures.get(texture_key).copied(),
920                level as i32,
921            )
922        }
923    }
924
925    pub fn new_renderbuffer(&mut self) -> Result<RenderbufferKey, GraphicsError> {
926        let renderbuffer = unsafe {
927            self.ctx
928                .create_renderbuffer()
929                .map_err(|_| GraphicsError::RenderbufferError)?
930        };
931        Ok(self.renderbuffers.insert(renderbuffer))
932    }
933
934    pub fn bind_renderbuffer(&mut self, renderbuffer: Option<RenderbufferKey>) {
935        if self.active_renderbuffer != renderbuffer {
936            self.active_renderbuffer = renderbuffer;
937            let gl_renderbuffer =
938                renderbuffer.and_then(|renderbuffer| self.renderbuffers.get(renderbuffer).cloned());
939            unsafe {
940                self.ctx
941                    .bind_renderbuffer(glow::RENDERBUFFER, gl_renderbuffer);
942            }
943        }
944    }
945
946    pub fn renderbuffer_storage(&mut self, format: PixelFormat, width: i32, height: i32) {
947        let gl_format = gl::pixel_format::to_gl(format, &self.version, true);
948        unsafe {
949            self.ctx
950                .renderbuffer_storage(glow::RENDERBUFFER, gl_format.internal, width, height)
951        }
952    }
953
954    pub fn framebuffer_renderbuffer(
955        &mut self,
956        attachment: canvas::Attachment,
957        renderbuffer: Option<RenderbufferKey>,
958    ) {
959        let gl_renderbuffer =
960            renderbuffer.and_then(|renderbuffer| self.renderbuffers.get(renderbuffer).cloned());
961        unsafe {
962            self.ctx.framebuffer_renderbuffer(
963                glow::FRAMEBUFFER,
964                attachment.to_gl(),
965                glow::RENDERBUFFER,
966                gl_renderbuffer,
967            )
968        }
969    }
970
971    pub fn destroy_renderbuffer(&mut self, renderbuffer_key: RenderbufferKey) {
972        match self.renderbuffers.remove(renderbuffer_key) {
973            None => (),
974            Some(renderbuffer) => unsafe { self.ctx.delete_renderbuffer(renderbuffer) },
975        }
976    }
977
978    pub fn set_vertex_attributes(
979        &mut self,
980        desired: u32,
981        binding_info: &[Option<mesh::BindingInfo>],
982    ) {
983        let diff = desired ^ self.enabled_attributes;
984        for i in 0..self.gl_constants.max_vertex_attributes as u32 {
985            let bit = 1 << i;
986
987            if diff & bit != 0 {
988                if desired & bit != 0 {
989                    unsafe {
990                        self.ctx.enable_vertex_attrib_array(i);
991                    }
992                } else {
993                    unsafe {
994                        self.ctx.disable_vertex_attrib_array(i);
995                    }
996                }
997            }
998
999            if desired & bit != 0 {
1000                let (vertex_format, stride, step, buffer_key, buffer_type) =
1001                    binding_info[i as usize].unwrap();
1002                self.bind_buffer(buffer_key, buffer_type);
1003                let (data_type, elements_count, _instances_count) = vertex_format.atype.to_gl();
1004                unsafe {
1005                    self.ctx.vertex_attrib_divisor(i, step);
1006                    use vertex::AttributeType;
1007                    match vertex_format.atype {
1008                        AttributeType::F32
1009                        | AttributeType::F32F32
1010                        | AttributeType::F32F32F32
1011                        | AttributeType::F32F32F32F32
1012                        | AttributeType::F32x2x2
1013                        | AttributeType::F32x3x3
1014                        | AttributeType::F32x4x4 => self.ctx.vertex_attrib_pointer_f32(
1015                            i,
1016                            elements_count,
1017                            data_type,
1018                            vertex_format.normalize,
1019                            stride as i32,
1020                            vertex_format.offset as i32,
1021                        ),
1022                        AttributeType::I32
1023                        | AttributeType::I32I32
1024                        | AttributeType::I32I32I32
1025                        | AttributeType::I32I32I32I32 => self.ctx.vertex_attrib_pointer_i32(
1026                            i,
1027                            elements_count,
1028                            data_type,
1029                            stride as i32,
1030                            vertex_format.offset as i32,
1031                        ),
1032                    }
1033                }
1034            }
1035        }
1036
1037        self.enabled_attributes = desired;
1038    }
1039
1040    pub fn set_uniform_by_location(
1041        &self,
1042        location: &shader::UniformLocation,
1043        data: &shader::RawUniformValue,
1044    ) {
1045        assert!(
1046            self.active_shader.is_some(),
1047            "Setting a uniform without an active shader."
1048        );
1049        use shader::RawUniformValue;
1050        let location = Some(&location.0);
1051        unsafe {
1052            match data {
1053                RawUniformValue::SignedInt(data) => self.ctx.uniform_1_i32(location, *data),
1054                RawUniformValue::Float(data) => self.ctx.uniform_1_f32(location, *data),
1055                RawUniformValue::Mat2(data) => self.ctx.uniform_matrix_2_f32_slice(
1056                    location,
1057                    false,
1058                    &AsRef::<[f32; 4]>::as_ref(data)[..],
1059                ),
1060                RawUniformValue::Mat3(data) => self.ctx.uniform_matrix_3_f32_slice(
1061                    location,
1062                    false,
1063                    &AsRef::<[f32; 9]>::as_ref(data)[..],
1064                ),
1065                RawUniformValue::Mat4(data) => self.ctx.uniform_matrix_4_f32_slice(
1066                    location,
1067                    false,
1068                    &AsRef::<[f32; 16]>::as_ref(data)[..],
1069                ),
1070                RawUniformValue::Vec2(data) => {
1071                    self.ctx.uniform_2_f32_slice(location, data.as_ref())
1072                }
1073                RawUniformValue::Vec3(data) => {
1074                    self.ctx.uniform_3_f32_slice(location, data.as_ref())
1075                }
1076                RawUniformValue::Vec4(data) => {
1077                    self.ctx.uniform_4_f32_slice(location, data.as_ref())
1078                }
1079                RawUniformValue::IntVec2(data) => {
1080                    self.ctx.uniform_2_i32_slice(location, data.as_ref())
1081                }
1082                RawUniformValue::IntVec3(data) => {
1083                    self.ctx.uniform_3_i32_slice(location, data.as_ref())
1084                }
1085                RawUniformValue::IntVec4(data) => {
1086                    self.ctx.uniform_4_i32_slice(location, data.as_ref())
1087                }
1088            }
1089        }
1090    }
1091
1092    pub fn draw_arrays(&self, mode: DrawMode, first: i32, count: i32) {
1093        unsafe {
1094            self.ctx
1095                .draw_arrays(gl::draw_mode::to_gl(mode), first, count);
1096        }
1097    }
1098
1099    pub fn draw_elements(&self, mode: DrawMode, count: i32, element_type: u32, offset: i32) {
1100        unsafe {
1101            self.ctx
1102                .draw_elements(gl::draw_mode::to_gl(mode), count, element_type, offset);
1103        }
1104    }
1105
1106    pub fn draw_arrays_instanced(
1107        &self,
1108        mode: DrawMode,
1109        first: i32,
1110        count: i32,
1111        instance_count: i32,
1112    ) {
1113        unsafe {
1114            self.ctx
1115                .draw_arrays_instanced(gl::draw_mode::to_gl(mode), first, count, instance_count)
1116        }
1117    }
1118
1119    pub fn draw_elements_instanced(
1120        &self,
1121        mode: DrawMode,
1122        count: i32,
1123        element_type: u32,
1124        offset: i32,
1125        instance_count: i32,
1126    ) {
1127        unsafe {
1128            self.ctx.draw_elements_instanced(
1129                gl::draw_mode::to_gl(mode),
1130                count,
1131                element_type as u32,
1132                offset,
1133                instance_count,
1134            )
1135        }
1136    }
1137
1138    pub fn set_viewport(&mut self, x: i32, y: i32, width: i32, height: i32) {
1139        let new_viewport = viewport::Viewport::new(x, y, width, height);
1140        if self.current_viewport != new_viewport {
1141            self.current_viewport = new_viewport;
1142            unsafe { self.ctx.viewport(x, y, width, height) }
1143        }
1144    }
1145
1146    pub fn viewport(&self) -> viewport::Viewport<i32> {
1147        self.current_viewport
1148    }
1149
1150    pub fn set_scissor(&mut self, region: Option<viewport::Viewport<i32>>) {
1151        match (region, &mut self.current_scissor) {
1152            (None, Some(_current)) => {
1153                unsafe {
1154                    self.ctx.disable(glow::SCISSOR_TEST);
1155                }
1156                self.current_scissor = None;
1157            }
1158            (Some(new), None) => {
1159                unsafe {
1160                    self.ctx.enable(glow::SCISSOR_TEST);
1161                    self.ctx
1162                        .scissor(new.x(), new.y(), new.width(), new.height());
1163                }
1164                self.current_scissor = Some(new);
1165            }
1166            (Some(new), Some(current)) => {
1167                if &new != current {
1168                    unsafe {
1169                        self.ctx
1170                            .scissor(new.x(), new.y(), new.width(), new.height());
1171                    }
1172                    *current = new;
1173                }
1174            }
1175            (None, None) => {}
1176        }
1177    }
1178
1179    pub fn clear_color(&self, red: f32, green: f32, blue: f32, alpha: f32) {
1180        unsafe { self.ctx.clear_color(red, green, blue, alpha) }
1181    }
1182
1183    pub fn clear(&self) {
1184        unsafe {
1185            self.ctx
1186                .clear(glow::COLOR_BUFFER_BIT | glow::DEPTH_BUFFER_BIT | glow::STENCIL_BUFFER_BIT);
1187        }
1188    }
1189
1190    pub fn read_pixels(
1191        &self,
1192        x: i32,
1193        y: i32,
1194        width: i32,
1195        height: i32,
1196        format: PixelFormat,
1197        data: &mut [u8],
1198    ) {
1199        let gl::TextureFormat { external, ty, .. } =
1200            gl::pixel_format::to_gl(format, &self.version, false);
1201        unsafe {
1202            self.ctx.read_pixels(
1203                x,
1204                y,
1205                width,
1206                height,
1207                external,
1208                ty,
1209                glow::PixelPackData::Slice(data),
1210            )
1211        }
1212    }
1213
1214    pub fn debug_message_callback<F>(&self, mut callback: F)
1215    where
1216        F: FnMut(DebugSource, DebugType, u32, DebugSeverity, &str),
1217    {
1218        if self.ctx.supports_debug() {
1219            unsafe {
1220                self.ctx.enable(glow::DEBUG_OUTPUT);
1221                self.ctx
1222                    .debug_message_callback(|source, event_type, id, severity, msg| {
1223                        let source = match source {
1224                            glow::DEBUG_SOURCE_API => DebugSource::API,
1225                            glow::DEBUG_SOURCE_WINDOW_SYSTEM => DebugSource::WindowSystem,
1226                            glow::DEBUG_SOURCE_SHADER_COMPILER => DebugSource::ShaderCompiler,
1227                            glow::DEBUG_SOURCE_THIRD_PARTY => DebugSource::ThirdParty,
1228                            glow::DEBUG_SOURCE_APPLICATION => DebugSource::Application,
1229                            glow::DEBUG_SOURCE_OTHER => DebugSource::Other,
1230                            _ => DebugSource::Other,
1231                        };
1232
1233                        let event_type = match event_type {
1234                            glow::DEBUG_TYPE_ERROR => DebugType::Error,
1235                            glow::DEBUG_TYPE_DEPRECATED_BEHAVIOR => DebugType::DeprecatedBehavior,
1236                            glow::DEBUG_TYPE_UNDEFINED_BEHAVIOR => DebugType::DeprecatedBehavior,
1237                            glow::DEBUG_TYPE_PORTABILITY => DebugType::Portability,
1238                            glow::DEBUG_TYPE_PERFORMANCE => DebugType::Performance,
1239                            glow::DEBUG_TYPE_MARKER => DebugType::Marker,
1240                            glow::DEBUG_TYPE_PUSH_GROUP => DebugType::PushGroup,
1241                            glow::DEBUG_TYPE_POP_GROUP => DebugType::PopGroup,
1242                            glow::DEBUG_TYPE_OTHER => DebugType::Other,
1243                            _ => DebugType::Other,
1244                        };
1245
1246                        let severity = match severity {
1247                            glow::DEBUG_SEVERITY_HIGH => DebugSeverity::High,
1248                            glow::DEBUG_SEVERITY_MEDIUM => DebugSeverity::Medium,
1249                            glow::DEBUG_SEVERITY_LOW => DebugSeverity::Low,
1250                            glow::DEBUG_SEVERITY_NOTIFICATION => DebugSeverity::Notification,
1251                            _ => DebugSeverity::Notification,
1252                        };
1253
1254                        callback(source, event_type, id, severity, msg)
1255                    });
1256            }
1257        }
1258    }
1259}
1260
1261#[derive(Debug)]
1262pub enum DebugSeverity {
1263    High,
1264    Medium,
1265    Low,
1266    Notification,
1267}
1268
1269#[derive(Debug)]
1270pub enum DebugType {
1271    Error,
1272    DeprecatedBehavior,
1273    UndefinedBehavior,
1274    Portability,
1275    Performance,
1276    Marker,
1277    PushGroup,
1278    PopGroup,
1279    Other,
1280}
1281
1282#[derive(Debug)]
1283pub enum DebugSource {
1284    API,
1285    WindowSystem,
1286    ShaderCompiler,
1287    ThirdParty,
1288    Application,
1289    Other,
1290}
1291
1292impl texture::TextureUpdate for Context {
1293    fn set_texture_sub_data(
1294        &mut self,
1295        texture_key: TextureKey,
1296        texture: texture::TextureInfo,
1297        texture_type: texture::TextureType,
1298        data: &[u8],
1299        x_offset: u32,
1300        y_offset: u32,
1301    ) {
1302        let gl::TextureFormat { external, ty, .. } =
1303            gl::pixel_format::to_gl(texture.get_format(), &self.version, false);
1304        let width = texture.width();
1305        let height = texture.height();
1306        let gl_target = gl::texture::to_gl(texture_type);
1307        self.bind_texture_to_unit(texture_type, texture_key, 0.into());
1308        unsafe {
1309            self.ctx.tex_sub_image_2d(
1310                gl_target,
1311                0,
1312                x_offset as i32,
1313                y_offset as i32,
1314                width as i32,
1315                height as i32,
1316                external,
1317                ty,
1318                glow::PixelUnpackData::Slice(data),
1319            );
1320            if texture.mipmaps() {
1321                self.ctx.generate_mipmap(gl_target);
1322            }
1323        }
1324    }
1325
1326    fn set_texture_data(
1327        &mut self,
1328        texture_key: TextureKey,
1329        texture: texture::TextureInfo,
1330        texture_type: texture::TextureType,
1331        data: Option<&[u8]>,
1332    ) {
1333        let gl::TextureFormat {
1334            internal,
1335            external,
1336            ty,
1337            swizzle,
1338        } = gl::pixel_format::to_gl(texture.get_format(), &self.version, false);
1339        let width = texture.width();
1340        let height = texture.height();
1341        let gl_target = gl::texture::to_gl(texture_type);
1342        self.bind_texture_to_unit(texture_type, texture_key, 0.into());
1343        unsafe {
1344            if let Some(swizzle) = swizzle {
1345                self.ctx
1346                    .tex_parameter_i32(gl_target, glow::TEXTURE_SWIZZLE_R, swizzle[0]);
1347                self.ctx
1348                    .tex_parameter_i32(gl_target, glow::TEXTURE_SWIZZLE_G, swizzle[1]);
1349                self.ctx
1350                    .tex_parameter_i32(gl_target, glow::TEXTURE_SWIZZLE_B, swizzle[2]);
1351                self.ctx
1352                    .tex_parameter_i32(gl_target, glow::TEXTURE_SWIZZLE_A, swizzle[3]);
1353            }
1354            self.ctx.tex_image_2d(
1355                gl_target,
1356                0,
1357                internal as i32,
1358                width as i32,
1359                height as i32,
1360                0,
1361                external,
1362                ty,
1363                data,
1364            );
1365            if texture.mipmaps() {
1366                self.ctx.generate_mipmap(gl_target);
1367            }
1368        }
1369    }
1370
1371    #[cfg(target_arch = "wasm32")]
1372    fn set_texture_data_with_html_image<T: texture::Texture>(
1373        &mut self,
1374        texture: T,
1375        data: &web_sys::HtmlImageElement,
1376    ) {
1377        let texture_info = texture.get_texture_info();
1378        let gl::TextureFormat {
1379            internal,
1380            external,
1381            ty,
1382            swizzle,
1383        } = gl::pixel_format::to_gl(texture_info.get_format(), &self.version, false);
1384        let gl_target = gl::texture::to_gl(texture.get_texture_type());
1385        self.bind_texture_to_unit(
1386            texture.get_texture_type(),
1387            texture.get_texture_key(),
1388            0.into(),
1389        );
1390        unsafe {
1391            if let Some(swizzle) = swizzle {
1392                self.ctx
1393                    .tex_parameter_i32(gl_target, glow::TEXTURE_SWIZZLE_R, swizzle[0]);
1394                self.ctx
1395                    .tex_parameter_i32(gl_target, glow::TEXTURE_SWIZZLE_G, swizzle[1]);
1396                self.ctx
1397                    .tex_parameter_i32(gl_target, glow::TEXTURE_SWIZZLE_B, swizzle[2]);
1398                self.ctx
1399                    .tex_parameter_i32(gl_target, glow::TEXTURE_SWIZZLE_A, swizzle[3]);
1400            }
1401            self.ctx.tex_image_2d_with_html_image(
1402                gl_target,
1403                0,
1404                internal as i32,
1405                external,
1406                ty,
1407                data,
1408            );
1409            if texture_info.mipmaps() {
1410                self.ctx.generate_mipmap(gl_target);
1411            }
1412        }
1413    }
1414
1415    fn set_texture_wrap(
1416        &mut self,
1417        texture_key: TextureKey,
1418        texture_type: texture::TextureType,
1419        wrap: texture::Wrap,
1420    ) {
1421        let gl_target = gl::texture::to_gl(texture_type);
1422        unsafe {
1423            self.bind_texture_to_unit(texture_type, texture_key, 0.into());
1424            self.ctx.tex_parameter_i32(
1425                gl_target,
1426                glow::TEXTURE_WRAP_S,
1427                gl::wrap_mode::to_gl(wrap.s()) as i32,
1428            );
1429            self.ctx.tex_parameter_i32(
1430                gl_target,
1431                glow::TEXTURE_WRAP_T,
1432                gl::wrap_mode::to_gl(wrap.t()) as i32,
1433            );
1434            use texture::TextureType;
1435            match texture_type {
1436                TextureType::Tex2D | TextureType::Tex2DArray | TextureType::Cube => (),
1437                TextureType::Volume => self.ctx.tex_parameter_i32(
1438                    gl_target,
1439                    glow::TEXTURE_WRAP_R,
1440                    gl::wrap_mode::to_gl(wrap.r()) as i32,
1441                ),
1442            }
1443        }
1444    }
1445
1446    fn set_texture_filter(
1447        &mut self,
1448        texture_key: TextureKey,
1449        texture_type: texture::TextureType,
1450        filter: texture::Filter,
1451    ) {
1452        use texture::FilterMode;
1453
1454        let gl_min = match filter.min() {
1455            FilterMode::Nearest => glow::NEAREST,
1456            FilterMode::Linear | FilterMode::None => glow::LINEAR,
1457        };
1458        let gl_mag = match filter.mag() {
1459            FilterMode::Nearest => glow::NEAREST,
1460            FilterMode::Linear | FilterMode::None => glow::LINEAR,
1461        };
1462
1463        let gl_min = match filter.mipmap() {
1464            FilterMode::None => gl_min,
1465            FilterMode::Nearest | FilterMode::Linear => match (filter.min(), filter.mipmap()) {
1466                (FilterMode::Nearest, FilterMode::Nearest) => glow::NEAREST_MIPMAP_NEAREST,
1467                (FilterMode::Nearest, FilterMode::Linear) => glow::NEAREST_MIPMAP_LINEAR,
1468                (FilterMode::Linear, FilterMode::Nearest) => glow::LINEAR_MIPMAP_NEAREST,
1469                (FilterMode::Linear, FilterMode::Linear) => glow::LINEAR_MIPMAP_LINEAR,
1470                _ => glow::LINEAR,
1471            },
1472        };
1473
1474        let gl_target = gl::texture::to_gl(texture_type);
1475        unsafe {
1476            self.bind_texture_to_unit(texture_type, texture_key, 0.into());
1477            self.ctx
1478                .tex_parameter_i32(gl_target, glow::TEXTURE_MIN_FILTER, gl_min as i32);
1479            self.ctx
1480                .tex_parameter_i32(gl_target, glow::TEXTURE_MAG_FILTER, gl_mag as i32);
1481        }
1482    }
1483}
1484
1485impl Drop for Context {
1486    fn drop(&mut self) {
1487        for (_, shader) in self.shaders.drain() {
1488            unsafe {
1489                self.ctx.delete_program(shader);
1490            }
1491        }
1492
1493        for (_, buffer) in self.buffers.drain() {
1494            unsafe { self.ctx.delete_buffer(buffer) }
1495        }
1496    }
1497}
1498
1499pub trait Renderer {
1500    fn clear(&mut self, settings: ClearSettings);
1501    fn draw<S, M>(&mut self, shader: &S, geometry: &Geometry<M>, settings: PipelineSettings)
1502    where
1503        S: shader::Shader + ?Sized,
1504        M: mesh::Mesh;
1505}
1506
1507impl Renderer for Context {
1508    fn clear(&mut self, settings: ClearSettings) {
1509        let ClearSettings {
1510            color,
1511            depth,
1512            stencil,
1513            target,
1514            scissor,
1515        } = settings;
1516        self.set_scissor(scissor);
1517
1518        let mut clear_bits = 0;
1519
1520        if let Some(color) = color {
1521            let Color::<f32> {
1522                red,
1523                blue,
1524                green,
1525                alpha,
1526            } = color.into();
1527            unsafe {
1528                self.ctx.clear_color(red, green, blue, alpha);
1529            }
1530            clear_bits |= glow::COLOR_BUFFER_BIT;
1531        }
1532
1533        if let Some(depth) = depth {
1534            unsafe {
1535                self.ctx.clear_depth_f32(depth.0);
1536            }
1537            clear_bits |= glow::DEPTH_BUFFER_BIT;
1538        }
1539
1540        if let Some(stencil) = stencil {
1541            unsafe {
1542                self.ctx.clear_stencil(stencil);
1543            }
1544            clear_bits |= glow::STENCIL_BUFFER_BIT;
1545        }
1546
1547        self.bind_framebuffer(
1548            canvas::Target::All,
1549            target.map(canvas::Canvas::get_framebuffer_key),
1550        );
1551        unsafe {
1552            self.ctx.clear(clear_bits);
1553        }
1554    }
1555
1556    fn draw<S, M>(&mut self, shader: &S, geometry: &Geometry<M>, settings: PipelineSettings)
1557    where
1558        S: shader::Shader + ?Sized,
1559        M: mesh::Mesh,
1560    {
1561        self.use_shader(Some(shader));
1562
1563        if let Some(depth_state) = settings.depth_state {
1564            self.enable(Feature::DepthTest(depth_state.function));
1565        } else {
1566            self.disable(Feature::DepthTest(DepthFunction::Never));
1567        }
1568        self.set_scissor(settings.scissor_state);
1569
1570        self.bind_framebuffer(
1571            canvas::Target::All,
1572            settings
1573                .framebuffer
1574                .map(canvas::Canvas::get_framebuffer_key),
1575        );
1576
1577        let Geometry {
1578            mesh,
1579            draw_range,
1580            draw_mode,
1581            instance_count,
1582            ..
1583        } = geometry;
1584
1585        let attached_attributes = mesh.attachments();
1586        let (desired_attribute_state, attributes) = prepare_draw(shader, &attached_attributes);
1587        self.set_vertex_attributes(desired_attribute_state, &attributes);
1588
1589        mesh.draw(
1590            self,
1591            draw_range.clone(),
1592            *draw_mode,
1593            *instance_count as usize,
1594        );
1595    }
1596}
1597
1598fn prepare_draw<'a, S: shader::Shader + ?Sized>(
1599    shader: &S,
1600    attached_attributes: &'a [mesh::AttachedAttributes],
1601) -> (u32, [Option<mesh::BindingInfo<'a>>; 32]) {
1602    // there's likely a better way to accumulate all bindings into an easy to search collection
1603    let attached_bindings = attached_attributes
1604        .iter()
1605        .flat_map(|attributes| {
1606            attributes
1607                .formats
1608                .iter()
1609                .map(|binding| {
1610                    (
1611                        binding,
1612                        attributes.stride,
1613                        attributes.step,
1614                        attributes.buffer.handle(),
1615                        attributes.buffer.buffer_type(),
1616                    )
1617                })
1618                .collect::<Vec<_>>()
1619        })
1620        .collect::<Vec<_>>();
1621
1622    let mut desired_attribute_state = 0u32;
1623    let mut attributes = [None; 32];
1624    for attr in shader::Shader::attributes(shader).iter() {
1625        let binding = attached_bindings
1626            .iter()
1627            .find(|(binding, ..)| binding.name == attr.name.as_str())
1628            .cloned();
1629        if let Some(binding) = binding {
1630            desired_attribute_state |= 1 << attr.location;
1631            attributes[attr.location as usize] = Some(binding);
1632        }
1633    }
1634    (desired_attribute_state, attributes)
1635}
1636
1637#[derive(Debug, Clone, PartialEq)]
1638pub struct Geometry<M> {
1639    pub mesh: M,
1640    pub draw_range: std::ops::Range<usize>,
1641    pub draw_mode: DrawMode,
1642    pub instance_count: u32,
1643}
1644
1645/// An non-NAN f32 value clamped between 0.0 and 1.0, inclusive.
1646/// This type can be constructed with an f32 which will be clamped into the appropriate range.
1647#[derive(Copy, Clone, Default, Debug, PartialOrd, PartialEq)]
1648pub struct ClampedF32(f32);
1649
1650impl From<f32> for ClampedF32 {
1651    fn from(v: f32) -> Self {
1652        let v = if v < 0. {
1653            0f32
1654        } else if v > 1. {
1655            1.
1656        } else if v.is_nan() {
1657            0.
1658        } else {
1659            v
1660        };
1661
1662        ClampedF32(v)
1663    }
1664}
1665
1666impl Eq for ClampedF32 {}
1667impl Ord for ClampedF32 {
1668    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
1669        self.0.partial_cmp(&other.0).expect("infallible")
1670    }
1671}
1672
1673impl std::ops::Deref for ClampedF32 {
1674    type Target = f32;
1675
1676    fn deref(&self) -> &Self::Target {
1677        &self.0
1678    }
1679}
1680
1681#[derive(Debug, Copy, Clone, Default, Ord, PartialOrd, Eq, PartialEq)]
1682pub struct Color<T> {
1683    pub red: T,
1684    pub blue: T,
1685    pub green: T,
1686    pub alpha: T,
1687}
1688
1689impl From<Color<f32>> for Color<ClampedF32> {
1690    fn from(c: Color<f32>) -> Self {
1691        Self {
1692            red: c.red.into(),
1693            blue: c.blue.into(),
1694            green: c.green.into(),
1695            alpha: c.alpha.into(),
1696        }
1697    }
1698}
1699
1700impl From<Color<ClampedF32>> for Color<f32> {
1701    fn from(c: Color<ClampedF32>) -> Self {
1702        Self {
1703            red: c.red.0,
1704            blue: c.blue.0,
1705            green: c.green.0,
1706            alpha: c.alpha.0,
1707        }
1708    }
1709}
1710
1711#[derive(Debug, Copy, Clone, PartialEq)]
1712pub struct ClearSettings<'a> {
1713    pub color: Option<Color<ClampedF32>>,
1714    pub depth: Option<ClampedF32>,
1715    pub stencil: Option<i32>, // TODO: does signed make sense here?
1716    pub target: Option<&'a canvas::Canvas>,
1717    pub scissor: Option<viewport::Viewport<i32>>,
1718}
1719
1720impl Default for ClearSettings<'_> {
1721    fn default() -> Self {
1722        Self {
1723            color: Some(Color::default()),
1724            depth: Some(ClampedF32(1.)),
1725            stencil: Some(0),
1726            target: None,
1727            scissor: None,
1728        }
1729    }
1730}
1731
1732#[derive(Debug, Clone, Eq, PartialEq)]
1733pub struct DepthState {
1734    pub function: DepthFunction,
1735    pub range: std::ops::RangeInclusive<ClampedF32>,
1736    pub write_mask: bool,
1737}
1738
1739impl Default for DepthState {
1740    fn default() -> Self {
1741        Self {
1742            function: DepthFunction::Less,
1743            range: ClampedF32(0.)..=ClampedF32(1.),
1744            write_mask: true,
1745        }
1746    }
1747}
1748
1749#[derive(Debug, Copy, Clone, Eq, PartialEq)]
1750pub struct CullingState {
1751    pub mode: CullFace,
1752    pub winding: VertexWinding,
1753}
1754
1755#[derive(Debug, Copy, Clone, PartialEq)]
1756pub struct PolygonState {
1757    pub culling_state: Option<CullingState>,
1758    pub polygon_offset_units: f32,
1759    pub polygon_offset_factor: f32,
1760}
1761
1762impl Default for PolygonState {
1763    fn default() -> Self {
1764        Self {
1765            culling_state: None,
1766            polygon_offset_units: 0.0,
1767            polygon_offset_factor: 0.0,
1768        }
1769    }
1770}
1771
1772#[derive(Debug, Copy, Clone, Eq, PartialEq)]
1773pub enum BlendSource {
1774    Zero,
1775    One,
1776    SourceColor,
1777    OneMinusSourceColor,
1778    DestinationColor,
1779    OneMinusDestinationColor,
1780    SourceAlpha,
1781    OneMinusSourceAlpha,
1782    DestinationAlpha,
1783    OneMinusDestinationAlpha,
1784    ConstantColor,
1785    OneMinusConstantColor,
1786    ConstantAlpha,
1787    OneMinusConstantAlpha,
1788    SourceAlphaSaturate,
1789}
1790
1791#[derive(Debug, Copy, Clone, Eq, PartialEq)]
1792pub enum BlendDestination {
1793    Zero,
1794    One,
1795    SourceColor,
1796    OneMinusSourceColor,
1797    DestinationColor,
1798    OneMinusDestinationColor,
1799    SourceAlpha,
1800    OneMinusSourceAlpha,
1801    DestinationAlpha,
1802    OneMinusDestinationAlpha,
1803    ConstantColor,
1804    OneMinusConstantColor,
1805    ConstantAlpha,
1806    OneMinusConstantAlpha,
1807}
1808
1809#[derive(Debug, Copy, Clone, Eq, PartialEq)]
1810pub enum BlendEquation {
1811    Add,
1812    Subtract,
1813    ReverseSubtract,
1814    Min,
1815    Max,
1816}
1817
1818#[derive(Debug, Copy, Clone, Eq, PartialEq)]
1819pub struct BlendState {
1820    pub destination_rgb: BlendDestination,
1821    pub source_rgb: BlendSource,
1822    pub destination_alpha: BlendDestination,
1823    pub source_alpha: BlendSource,
1824    pub color: Color<ClampedF32>,
1825    pub equation_rgb: BlendEquation,
1826    pub equation_alpha: BlendEquation,
1827}
1828
1829impl Default for BlendState {
1830    fn default() -> Self {
1831        Self {
1832            destination_rgb: BlendDestination::Zero,
1833            source_rgb: BlendSource::One,
1834            destination_alpha: BlendDestination::Zero,
1835            source_alpha: BlendSource::One,
1836            color: Default::default(),
1837            equation_rgb: BlendEquation::Add,
1838            equation_alpha: BlendEquation::Add,
1839        }
1840    }
1841}
1842
1843#[derive(Debug, Copy, Clone, Eq, PartialEq)]
1844pub enum StencilFunction {
1845    Never,
1846    Less,
1847    LessOrEqual,
1848    Greater,
1849    GreaterOrEqual,
1850    Equal,
1851    NoteEqual,
1852    Always,
1853}
1854
1855#[derive(Debug, Copy, Clone, Eq, PartialEq)]
1856pub struct StencilState {
1857    pub function: StencilFunction,
1858    // TODO the rest
1859}
1860
1861impl Default for StencilState {
1862    fn default() -> Self {
1863        Self {
1864            function: StencilFunction::Always,
1865        }
1866    }
1867}
1868
1869#[derive(Debug, Clone, PartialEq)]
1870pub struct PipelineSettings<'a> {
1871    pub viewport: viewport::Viewport<i32>,
1872    pub framebuffer: Option<&'a canvas::Canvas>,
1873    pub polygon_state: PolygonState,
1874    pub depth_state: Option<DepthState>,
1875    pub blend_state: Option<BlendState>,
1876    pub stencil_state: Option<StencilState>,
1877    pub scissor_state: Option<viewport::Viewport<i32>>,
1878}
1879
1880impl<'a> Default for PipelineSettings<'a> {
1881    fn default() -> Self {
1882        Self {
1883            viewport: Default::default(),
1884            framebuffer: None,
1885            depth_state: Some(DepthState::default()),
1886            polygon_state: Default::default(),
1887            blend_state: None,
1888            stencil_state: None,
1889            scissor_state: None,
1890        }
1891    }
1892}
1893
1894#[cfg(all(test, not(target_os = "linux")))]
1895mod tests {
1896    use super::*;
1897
1898    #[test]
1899    fn pipeline() {
1900        let pipeline_settings = PipelineSettings::default();
1901        println!("{:#?}", pipeline_settings);
1902
1903        let pipeline_settings = PipelineSettings {
1904            viewport: viewport::Viewport::new(0, 0, 720, 480),
1905            ..PipelineSettings::default()
1906        };
1907        println!("{:#?}", pipeline_settings);
1908
1909        let pipeline_settings = PipelineSettings {
1910            depth_state: Some(DepthState {
1911                function: DepthFunction::Never,
1912                ..DepthState::default()
1913            }),
1914            ..pipeline_settings
1915        };
1916        println!("{:#?}", pipeline_settings);
1917    }
1918
1919    #[repr(C)]
1920    #[derive(Debug, Copy, Clone, PartialEq, Default, bytemuck::Zeroable, bytemuck::Pod)]
1921    struct TestVertex {
1922        color: f32,
1923        position: f32,
1924    }
1925
1926    use vertex::VertexFormat;
1927    impl vertex::Vertex for TestVertex {
1928        fn build_bindings() -> &'static [VertexFormat] {
1929            &[
1930                VertexFormat {
1931                    name: "color",
1932                    offset: 0,
1933                    atype: vertex::AttributeType::F32,
1934                    normalize: false,
1935                },
1936                VertexFormat {
1937                    name: "position",
1938                    offset: std::mem::size_of::<f32>(),
1939                    atype: vertex::AttributeType::F32,
1940                    normalize: false,
1941                },
1942            ]
1943        }
1944    }
1945
1946    #[cfg(target_os = "windows")]
1947    fn get_headless_context(
1948        width: u32,
1949        height: u32,
1950    ) -> (glow::Context, glutin::Context<glutin::PossiblyCurrent>) {
1951        use glutin::platform::windows::EventLoopExtWindows;
1952        let el = glutin::event_loop::EventLoop::<()>::new_any_thread();
1953        let window = glutin::ContextBuilder::new()
1954            .build_headless(&el, glutin::dpi::PhysicalSize::new(width, height))
1955            .unwrap();
1956        let window = unsafe { window.make_current().unwrap() };
1957        (
1958            unsafe { glow::Context::from_loader_function(|name| window.get_proc_address(name)) },
1959            window,
1960        )
1961    }
1962
1963    #[cfg(target_os = "windows")]
1964    #[test]
1965    fn basic() {
1966        let (ctx, _window) = get_headless_context(100, 100);
1967        let ctx = Context::new(ctx);
1968        ctx.clear();
1969    }
1970
1971    #[cfg(target_os = "windows")]
1972    #[test]
1973    fn unused_vertex_attribute() {
1974        let (ctx, _window) = get_headless_context(100, 100);
1975        let mut ctx = Context::new(ctx);
1976
1977        let mesh = mesh::VertexMesh::with_data(
1978            &mut ctx,
1979            &[
1980                TestVertex {
1981                    color: 0.,
1982                    position: 1.,
1983                },
1984                TestVertex {
1985                    color: 1.,
1986                    position: 2.,
1987                },
1988                TestVertex {
1989                    color: 2.,
1990                    position: 3.,
1991                },
1992            ],
1993        )
1994        .unwrap();
1995
1996        const SRC: &str = r#"
1997varying vec4 vColor;
1998
1999#ifdef VERTEX
2000layout(location = 2) attribute vec4 position;
2001
2002void main() {
2003    gl_Position = position;
2004}
2005#endif
2006
2007#ifdef FRAGMENT
2008void main() {
2009    fragColor = vec4(1., 1., 1., 1.);
2010}
2011#endif"#;
2012
2013        let (vert, frag) = shader::DynamicShader::create_source(SRC, SRC);
2014        let shader = shader::DynamicShader::new(&mut ctx, &vert, &frag).unwrap();
2015        ctx.use_shader(Some(&shader));
2016
2017        Renderer::draw(
2018            &mut ctx,
2019            &shader,
2020            &super::Geometry {
2021                mesh: &mesh,
2022                draw_range: 0..1,
2023                draw_mode: DrawMode::Triangles,
2024                instance_count: 1,
2025            },
2026            PipelineSettings::default(),
2027        );
2028    }
2029
2030    #[cfg(target_os = "windows")]
2031    #[test]
2032    fn mapped_mesh() {
2033        let (ctx, _window) = get_headless_context(100, 100);
2034        let mut ctx = Context::new(ctx);
2035
2036        let vertices = [
2037            TestVertex {
2038                color: 0.,
2039                position: 1.,
2040            },
2041            TestVertex {
2042                color: 1.,
2043                position: 2.,
2044            },
2045            TestVertex {
2046                color: 2.,
2047                position: 3.,
2048            },
2049        ];
2050
2051        let indices = [0u32, 1, 2];
2052
2053        {
2054            let mut mesh = mesh::MappedVertexMesh::new(&mut ctx, 3).unwrap();
2055            mesh.set_vertices(&vertices, 0);
2056
2057            let mapped_verts = mesh.get_vertices();
2058            assert_eq!(vertices, mapped_verts);
2059        }
2060
2061        {
2062            let mut mesh = mesh::MappedIndexedMesh::new(&mut ctx, 3, 3).unwrap();
2063            mesh.set_vertices(&vertices, 0);
2064            mesh.set_indices(&indices, 0);
2065
2066            assert_eq!(vertices, mesh.get_vertices());
2067            assert_eq!(indices, mesh.get_indices());
2068        }
2069    }
2070
2071    #[cfg(target_os = "windows")]
2072    #[test]
2073    fn mapped_image() {
2074        use super::PixelFormat;
2075        use image::*;
2076        use texture::*;
2077
2078        let (ctx, _window) = get_headless_context(100, 100);
2079        let mut ctx = Context::new(ctx);
2080        {
2081            // RGBA
2082            let data = vec![234; 3 * 3 * 4];
2083
2084            let mut image = MappedImage::with_data(
2085                &mut ctx,
2086                TextureType::Tex2D,
2087                PixelFormat::RGBA8,
2088                3,
2089                3,
2090                data.clone(),
2091                Settings::default(),
2092            )
2093            .unwrap();
2094            let pixel_stride = image.pixel_stride();
2095            assert_eq!(image.get_pixels(), data);
2096
2097            let pixel = [1, 2, 3, 4];
2098            image.set_pixels(viewport::Viewport::new(0, 0, 1, 1), &pixel);
2099            assert_eq!(image.get_pixels()[..4], pixel);
2100
2101            let pixel = [0, 0, 1, 0];
2102            image.set_pixel(0, 0, &pixel);
2103            assert_eq!(image.get_pixels()[..4], pixel);
2104            assert_eq!(image.get_pixel(0, 0), pixel);
2105
2106            assert_eq!(image.get_pixel(0, 1), [234, 234, 234, 234]);
2107            image.set_pixel(0, 1, &pixel);
2108            assert_eq!(image.get_pixel(0, 1), pixel);
2109            assert_eq!(
2110                image.get_pixels()[(3 * pixel_stride)..(4 * pixel_stride)],
2111                pixel
2112            );
2113        }
2114
2115        {
2116            // RGB
2117            let data = vec![234; 3 * 3 * 3];
2118
2119            let mut image = MappedImage::with_data(
2120                &mut ctx,
2121                TextureType::Tex2D,
2122                PixelFormat::RGB8,
2123                3,
2124                3,
2125                data.clone(),
2126                Settings::default(),
2127            )
2128            .unwrap();
2129            let pixel_stride = image.pixel_stride();
2130            assert_eq!(image.get_pixels(), data);
2131
2132            let pixel = [1, 2, 3];
2133            image.set_pixels(viewport::Viewport::new(0, 0, 1, 1), &pixel);
2134            assert_eq!(image.get_pixels()[..pixel_stride], pixel);
2135
2136            let pixel = [0, 0, 1];
2137            image.set_pixel(0, 0, &pixel);
2138            assert_eq!(image.get_pixels()[..pixel_stride], pixel);
2139            assert_eq!(image.get_pixel(0, 0), pixel);
2140
2141            assert_eq!(image.get_pixel(0, 1), [234, 234, 234]);
2142            image.set_pixel(0, 1, &pixel);
2143            assert_eq!(image.get_pixel(0, 1), pixel);
2144            assert_eq!(
2145                image.get_pixels()[(3 * pixel_stride)..(4 * pixel_stride)],
2146                pixel
2147            );
2148        }
2149    }
2150
2151    #[cfg(target_os = "windows")]
2152    #[test]
2153    fn quad_batch_test() {
2154        let (ctx, _window) = get_headless_context(100, 100);
2155        let mut ctx = Context::new(ctx);
2156
2157        let quad = quad_batch::Quad::from(viewport::Viewport::new(0., 0., 1., 1.)).map(|(x, y)| {
2158            TestVertex {
2159                color: y,
2160                position: x,
2161            }
2162        });
2163        let mut batch = quad_batch::QuadBatch::<TestVertex>::new(&mut ctx, 1).unwrap();
2164        let index = batch.push(quad.clone());
2165
2166        assert_eq!(batch.get_quad(index).unwrap(), quad);
2167    }
2168}