opengl_graphics/
back_end.rs

1//! OpenGL back-end for Piston-Graphics.
2
3// External crates.
4use gl::types::{GLint, GLsizei, GLuint};
5use graphics::color::gamma_srgb_to_linear;
6use graphics::BACK_END_MAX_VERTEX_COUNT as BUFFER_SIZE;
7use graphics::{Context, DrawState, Graphics, Viewport};
8use shader_version::glsl::GLSL;
9use shader_version::{OpenGL, Shaders};
10use std::ffi::CString;
11
12// Local crate.
13use crate::draw_state;
14use crate::shader_utils::{compile_shader, DynamicAttribute};
15use crate::Texture;
16
17// The number of chunks to fill up before rendering.
18// Amount of memory used: `BUFFER_SIZE * CHUNKS * 4 * (2 + 4)`
19// `4` for bytes per f32, and `2 + 4` for position and color.
20const CHUNKS: usize = 100;
21
22// Whether to use WebGL-specific features; currently used to select appropriate shaders.
23const USE_WEBGL: bool = cfg!(all(target_arch = "wasm32", target_os = "unknown"))
24    || cfg!(target_os = "emscripten")
25    || cfg!(feature = "webgl");
26
27/// Describes how to render colored objects.
28pub struct Colored {
29    vao: GLuint,
30    vertex_shader: GLuint,
31    fragment_shader: GLuint,
32    program: GLuint,
33    pos: DynamicAttribute,
34    color: DynamicAttribute,
35    pos_buffer: Vec<[f32; 2]>,
36    color_buffer: Vec<[f32; 4]>,
37    offset: usize,
38}
39
40impl Drop for Colored {
41    fn drop(&mut self) {
42        unsafe {
43            gl::DeleteVertexArrays(1, &self.vao);
44            gl::DeleteProgram(self.program);
45            gl::DeleteShader(self.vertex_shader);
46            gl::DeleteShader(self.fragment_shader);
47        }
48    }
49}
50
51impl Colored {
52    /// Generate using pass-through shaders.
53    ///
54    /// # Panics
55    /// If the default pass-through shaders fail to compile
56    pub fn new(glsl: GLSL) -> Self {
57        use shaders::colored;
58        let src = |bytes| unsafe { ::std::str::from_utf8_unchecked(bytes) };
59
60        let mut vertex_shaders = Shaders::new();
61        if USE_WEBGL {
62            vertex_shaders
63                .set(GLSL::V1_20, src(colored::VERTEX_GLSL_120_WEBGL))
64                .set(GLSL::V1_50, src(colored::VERTEX_GLSL_150_CORE_WEBGL))
65        } else {
66            vertex_shaders
67                .set(GLSL::V1_20, src(colored::VERTEX_GLSL_120))
68                .set(GLSL::V1_50, src(colored::VERTEX_GLSL_150_CORE))
69        };
70
71        let mut fragment_shaders = Shaders::new();
72        if USE_WEBGL {
73            fragment_shaders
74                .set(GLSL::V1_20, src(colored::FRAGMENT_GLSL_120_WEBGL))
75                .set(GLSL::V1_50, src(colored::FRAGMENT_GLSL_150_CORE_WEBGL))
76        } else {
77            fragment_shaders
78                .set(GLSL::V1_20, src(colored::FRAGMENT_GLSL_120))
79                .set(GLSL::V1_50, src(colored::FRAGMENT_GLSL_150_CORE))
80        };
81
82        Colored::from_vs_fs(glsl, &vertex_shaders, &fragment_shaders).unwrap()
83    }
84
85    /// Generate using custom vertex and fragment shaders.
86    pub fn from_vs_fs(
87        glsl: GLSL,
88        vertex_shaders: &Shaders<GLSL, str>,
89        fragment_shaders: &Shaders<GLSL, str>,
90    ) -> Result<Self, String> {
91        let v_shader = vertex_shaders
92            .get(glsl)
93            .ok_or("No compatible vertex shader")?;
94
95        let v_shader_compiled = compile_shader(gl::VERTEX_SHADER, v_shader)
96            .map_err(|s| format!("Error compiling vertex shader: {}", s))?;
97
98        let f_shader = fragment_shaders
99            .get(glsl)
100            .ok_or("No compatible fragment shader")?;
101
102        let f_shader_compiled = compile_shader(gl::FRAGMENT_SHADER, f_shader)
103            .map_err(|s| format!("Error compiling fragment shader: {}", s))?;
104
105        let program;
106        unsafe {
107            program = gl::CreateProgram();
108            gl::AttachShader(program, v_shader_compiled);
109            gl::AttachShader(program, f_shader_compiled);
110
111            let c_o_color = CString::new("o_Color").unwrap();
112            if !USE_WEBGL {
113                gl::BindFragDataLocation(program, 0, c_o_color.as_ptr());
114            }
115            drop(c_o_color);
116        }
117
118        let mut vao = 0;
119        unsafe {
120            gl::GenVertexArrays(1, &mut vao);
121            gl::LinkProgram(program);
122        }
123        let pos = DynamicAttribute::xy(program, "pos", vao).unwrap();
124        let color = DynamicAttribute::rgba(program, "color", vao).unwrap();
125        Ok(Colored {
126            vao,
127            vertex_shader: v_shader_compiled,
128            fragment_shader: f_shader_compiled,
129            program,
130            pos,
131            color,
132            pos_buffer: vec![[0.0; 2]; CHUNKS * BUFFER_SIZE],
133            color_buffer: vec![[0.0; 4]; CHUNKS * BUFFER_SIZE],
134            offset: 0,
135        })
136    }
137
138    fn flush(&mut self) {
139        unsafe {
140            gl::BindVertexArray(self.vao);
141            // Render triangles whether they are facing
142            // clockwise or counter clockwise.
143            gl::Disable(gl::CULL_FACE);
144            self.color.set(&self.color_buffer[..self.offset]);
145            self.pos.set(&self.pos_buffer[..self.offset]);
146            gl::DrawArrays(gl::TRIANGLES, 0, self.offset as i32);
147            gl::BindVertexArray(0);
148        }
149
150        self.offset = 0;
151    }
152}
153
154/// Describes how to render textured objects.
155pub struct Textured {
156    vertex_shader: GLuint,
157    fragment_shader: GLuint,
158    program: GLuint,
159    vao: GLuint,
160    color: GLint,
161    pos: DynamicAttribute,
162    uv: DynamicAttribute,
163    pos_buffer: Vec<[f32; 2]>,
164    uv_buffer: Vec<[f32; 2]>,
165    offset: usize,
166    last_texture_id: GLuint,
167    last_color: [f32; 4],
168}
169
170impl Drop for Textured {
171    fn drop(&mut self) {
172        unsafe {
173            gl::DeleteVertexArrays(1, &self.vao);
174            gl::DeleteProgram(self.program);
175            gl::DeleteShader(self.vertex_shader);
176            gl::DeleteShader(self.fragment_shader);
177        }
178    }
179}
180
181impl Textured {
182    /// Generate using pass-through shaders.
183    ///
184    /// # Panics
185    /// If the default pass-through shaders fail to compile
186    pub fn new(glsl: GLSL) -> Self {
187        use shaders::textured;
188        let src = |bytes| unsafe { ::std::str::from_utf8_unchecked(bytes) };
189
190        let mut vertex_shaders = Shaders::new();
191        if USE_WEBGL {
192            vertex_shaders
193                .set(GLSL::V1_20, src(textured::VERTEX_GLSL_120_WEBGL))
194                .set(GLSL::V1_50, src(textured::VERTEX_GLSL_150_CORE_WEBGL))
195        } else {
196            vertex_shaders
197                .set(GLSL::V1_20, src(textured::VERTEX_GLSL_120))
198                .set(GLSL::V1_50, src(textured::VERTEX_GLSL_150_CORE))
199        };
200
201        let mut fragment_shaders = Shaders::new();
202        if USE_WEBGL {
203            fragment_shaders
204                .set(GLSL::V1_20, src(textured::FRAGMENT_GLSL_120_WEBGL))
205                .set(GLSL::V1_50, src(textured::FRAGMENT_GLSL_150_CORE_WEBGL))
206        } else {
207            fragment_shaders
208                .set(GLSL::V1_20, src(textured::FRAGMENT_GLSL_120))
209                .set(GLSL::V1_50, src(textured::FRAGMENT_GLSL_150_CORE))
210        };
211
212        Textured::from_vs_fs(glsl, &vertex_shaders, &fragment_shaders).unwrap()
213    }
214
215    /// Generate using custom vertex and fragment shaders.
216    pub fn from_vs_fs(
217        glsl: GLSL,
218        vertex_shaders: &Shaders<GLSL, str>,
219        fragment_shaders: &Shaders<GLSL, str>,
220    ) -> Result<Self, String> {
221        let v_shader = vertex_shaders
222            .get(glsl)
223            .ok_or("No compatible vertex shader")?;
224
225        let v_shader_compiled = compile_shader(gl::VERTEX_SHADER, v_shader)
226            .map_err(|s| format!("Error compiling vertex shader: {}", s))?;
227
228        let f_shader = fragment_shaders
229            .get(glsl)
230            .ok_or("No compatible fragment shader")?;
231
232        let f_shader_compiled = compile_shader(gl::FRAGMENT_SHADER, f_shader)
233            .map_err(|s| format!("Error compiling fragment shader: {}", s))?;
234
235        let program;
236        unsafe {
237            program = gl::CreateProgram();
238            gl::AttachShader(program, v_shader_compiled);
239            gl::AttachShader(program, f_shader_compiled);
240
241            let c_o_color = CString::new("o_Color").unwrap();
242            if !USE_WEBGL {
243                gl::BindFragDataLocation(program, 0, c_o_color.as_ptr());
244            }
245            drop(c_o_color);
246        }
247
248        let mut vao = 0;
249        unsafe {
250            gl::GenVertexArrays(1, &mut vao);
251            gl::LinkProgram(program);
252        }
253        let pos = DynamicAttribute::xy(program, "pos", vao).unwrap();
254        let c_color = CString::new("color").unwrap();
255        let color = unsafe { gl::GetUniformLocation(program, c_color.as_ptr()) };
256        drop(c_color);
257        if color == -1 {
258            panic!("Could not find uniform `color`");
259        }
260        let uv = DynamicAttribute::uv(program, "uv", vao).unwrap();
261        Ok(Textured {
262            vao,
263            vertex_shader: v_shader_compiled,
264            fragment_shader: f_shader_compiled,
265            program,
266            pos,
267            color,
268            uv,
269            pos_buffer: vec![[0.0; 2]; CHUNKS * BUFFER_SIZE],
270            uv_buffer: vec![[0.0; 2]; CHUNKS * BUFFER_SIZE],
271            offset: 0,
272            last_texture_id: 0,
273            last_color: [0.0; 4],
274        })
275    }
276
277    fn flush(&mut self) {
278        let texture_id = self.last_texture_id;
279        let color = self.last_color;
280        unsafe {
281            gl::BindVertexArray(self.vao);
282            gl::BindTexture(gl::TEXTURE_2D, texture_id);
283            gl::Uniform4f(self.color, color[0], color[1], color[2], color[3]);
284            // Render triangles whether they are facing
285            // clockwise or counter clockwise.
286            gl::Disable(gl::CULL_FACE);
287            self.pos.set(&self.pos_buffer[..self.offset]);
288            self.uv.set(&self.uv_buffer[..self.offset]);
289            gl::DrawArrays(gl::TRIANGLES, 0, self.offset as i32);
290            gl::BindVertexArray(0);
291        }
292
293        self.offset = 0;
294    }
295}
296
297/// Describes how to render textured objects with individual vertex colors.
298pub struct TexturedColor {
299    vertex_shader: GLuint,
300    fragment_shader: GLuint,
301    program: GLuint,
302    vao: GLuint,
303    pos: DynamicAttribute,
304    uv: DynamicAttribute,
305    color: DynamicAttribute,
306    pos_buffer: Vec<[f32; 2]>,
307    uv_buffer: Vec<[f32; 2]>,
308    color_buffer: Vec<[f32; 4]>,
309    offset: usize,
310    last_texture_id: GLuint,
311}
312
313impl Drop for TexturedColor {
314    fn drop(&mut self) {
315        unsafe {
316            gl::DeleteVertexArrays(1, &self.vao);
317            gl::DeleteProgram(self.program);
318            gl::DeleteShader(self.vertex_shader);
319            gl::DeleteShader(self.fragment_shader);
320        }
321    }
322}
323
324impl TexturedColor {
325    /// Generate using pass-through shaders.
326    ///
327    /// # Panics
328    /// If the default pass-through shaders fail to compile
329    pub fn new(glsl: GLSL) -> Self {
330        use shaders::textured_color;
331        let src = |bytes| unsafe { ::std::str::from_utf8_unchecked(bytes) };
332
333        let mut vertex_shaders = Shaders::new();
334        if USE_WEBGL {
335            vertex_shaders
336                .set(GLSL::V1_20, src(textured_color::VERTEX_GLSL_120_WEBGL))
337                .set(GLSL::V1_50, src(textured_color::VERTEX_GLSL_150_CORE_WEBGL))
338        } else {
339            vertex_shaders
340                .set(GLSL::V1_20, src(textured_color::VERTEX_GLSL_120))
341                .set(GLSL::V1_50, src(textured_color::VERTEX_GLSL_150_CORE))
342        };
343
344        let mut fragment_shaders = Shaders::new();
345        if USE_WEBGL {
346            fragment_shaders
347                .set(GLSL::V1_20, src(textured_color::FRAGMENT_GLSL_120_WEBGL))
348                .set(
349                    GLSL::V1_50,
350                    src(textured_color::FRAGMENT_GLSL_150_CORE_WEBGL),
351                )
352        } else {
353            fragment_shaders
354                .set(GLSL::V1_20, src(textured_color::FRAGMENT_GLSL_120))
355                .set(GLSL::V1_50, src(textured_color::FRAGMENT_GLSL_150_CORE))
356        };
357
358        TexturedColor::from_vs_fs(glsl, &vertex_shaders, &fragment_shaders).unwrap()
359    }
360
361    /// Generate using custom vertex and fragment shaders.
362    pub fn from_vs_fs(
363        glsl: GLSL,
364        vertex_shaders: &Shaders<GLSL, str>,
365        fragment_shaders: &Shaders<GLSL, str>,
366    ) -> Result<Self, String> {
367        let v_shader = vertex_shaders
368            .get(glsl)
369            .ok_or("No compatible vertex shader")?;
370
371        let v_shader_compiled = compile_shader(gl::VERTEX_SHADER, v_shader)
372            .map_err(|s| format!("Error compiling vertex shader: {}", s))?;
373
374        let f_shader = fragment_shaders
375            .get(glsl)
376            .ok_or("No compatible fragment shader")?;
377
378        let f_shader_compiled = compile_shader(gl::FRAGMENT_SHADER, f_shader)
379            .map_err(|s| format!("Error compiling fragment shader: {}", s))?;
380
381        let program;
382        unsafe {
383            program = gl::CreateProgram();
384            gl::AttachShader(program, v_shader_compiled);
385            gl::AttachShader(program, f_shader_compiled);
386
387            let c_o_color = CString::new("o_Color").unwrap();
388            if !USE_WEBGL {
389                gl::BindFragDataLocation(program, 0, c_o_color.as_ptr());
390            }
391            drop(c_o_color);
392        }
393
394        let mut vao = 0;
395        unsafe {
396            gl::GenVertexArrays(1, &mut vao);
397            gl::LinkProgram(program);
398        }
399        let pos = DynamicAttribute::xy(program, "pos", vao).unwrap();
400        let color = DynamicAttribute::rgba(program, "color", vao).unwrap();
401        let uv = DynamicAttribute::uv(program, "uv", vao).unwrap();
402        Ok(TexturedColor {
403            vao,
404            vertex_shader: v_shader_compiled,
405            fragment_shader: f_shader_compiled,
406            program,
407            pos,
408            color,
409            uv,
410            pos_buffer: vec![[0.0; 2]; CHUNKS * BUFFER_SIZE],
411            uv_buffer: vec![[0.0; 2]; CHUNKS * BUFFER_SIZE],
412            color_buffer: vec![[0.0; 4]; CHUNKS * BUFFER_SIZE],
413            offset: 0,
414            last_texture_id: 0,
415        })
416    }
417
418    fn flush(&mut self) {
419        let texture_id = self.last_texture_id;
420        unsafe {
421            gl::BindVertexArray(self.vao);
422            gl::BindTexture(gl::TEXTURE_2D, texture_id);
423            // Render triangles whether they are facing
424            // clockwise or counter clockwise.
425            gl::Disable(gl::CULL_FACE);
426            self.pos.set(&self.pos_buffer[..self.offset]);
427            self.uv.set(&self.uv_buffer[..self.offset]);
428            self.color.set(&self.color_buffer[..self.offset]);
429            gl::DrawArrays(gl::TRIANGLES, 0, self.offset as i32);
430            gl::BindVertexArray(0);
431        }
432
433        self.offset = 0;
434    }
435}
436
437// Newlines and indents for cleaner panic message.
438const GL_FUNC_NOT_LOADED: &str = "
439    OpenGL function pointers must be loaded before creating the `Gl` backend!
440    For more info, see the following issue on GitHub:
441    https://github.com/PistonDevelopers/opengl_graphics/issues/103
442";
443
444/// Contains OpenGL data.
445pub struct GlGraphics {
446    colored: Colored,
447    textured: Textured,
448    textured_color: TexturedColor,
449    // Keeps track of the current shader program.
450    current_program: Option<GLuint>,
451    // Keeps track of the current draw state.
452    current_draw_state: Option<DrawState>,
453    // Keeps track of the current viewport
454    current_viewport: Option<Viewport>,
455}
456
457impl GlGraphics {
458    /// Creates a new OpenGL back-end.
459    ///
460    /// # Panics
461    /// If the OpenGL function pointers have not been loaded yet.
462    /// See https://github.com/PistonDevelopers/opengl_graphics/issues/103 for more info.
463    pub fn new(opengl: OpenGL) -> Self {
464        assert!(gl::Enable::is_loaded(), "{}", GL_FUNC_NOT_LOADED);
465
466        let glsl = opengl.to_glsl();
467        // Load the vertices, color and texture coord buffers.
468        GlGraphics {
469            colored: Colored::new(glsl),
470            textured: Textured::new(glsl),
471            textured_color: TexturedColor::new(glsl),
472            current_program: None,
473            current_draw_state: None,
474            current_viewport: None,
475        }
476    }
477
478    /// Create a new OpenGL back-end with `Colored`, `Textured` and `TexturedColor`
479    /// structs to describe how to render objects.
480    ///
481    /// # Panics
482    /// If the OpenGL function pointers have not been loaded yet.
483    /// See https://github.com/PistonDevelopers/opengl_graphics/issues/103 for more info.
484    pub fn from_pieces(
485        colored: Colored,
486        textured: Textured,
487        textured_color: TexturedColor,
488    ) -> Self {
489        assert!(gl::Enable::is_loaded(), "{}", GL_FUNC_NOT_LOADED);
490
491        // Load the vertices, color and texture coord buffers.
492        GlGraphics {
493            colored,
494            textured,
495            textured_color,
496            current_program: None,
497            current_draw_state: None,
498            current_viewport: None,
499        }
500    }
501
502    /// Sets viewport with normalized coordinates and center as origin.
503    fn viewport(&mut self, x: i32, y: i32, w: i32, h: i32) {
504        unsafe {
505            gl::Viewport(x as GLint, y as GLint, w as GLsizei, h as GLsizei);
506        }
507    }
508
509    /// Returns the current program
510    pub fn get_current_program(&self) -> Option<GLuint> {
511        self.current_program
512    }
513
514    /// Sets the current program only if the program is not in use.
515    pub fn use_program(&mut self, program: GLuint) {
516        match self.current_program {
517            None => {}
518            Some(current_program) => {
519                if program == current_program {
520                    return;
521                }
522            }
523        }
524
525        unsafe {
526            gl::UseProgram(program);
527        }
528        self.current_program = Some(program);
529    }
530
531    /// Unset the current program.
532    ///
533    /// This forces the current program to be set on next drawing call.
534    pub fn clear_program(&mut self) {
535        self.current_program = None
536    }
537
538    /// Sets the current draw state, by detecting changes.
539    pub fn use_draw_state(&mut self, draw_state: &DrawState) {
540        match self.current_draw_state {
541            None => {
542                draw_state::bind_scissor(draw_state.scissor, &self.current_viewport);
543                draw_state::bind_stencil(draw_state.stencil);
544                draw_state::bind_blend(draw_state.blend);
545            }
546            Some(ref old_state) => {
547                draw_state::bind_state(old_state, draw_state, &self.current_viewport);
548            }
549        }
550        self.current_draw_state = Some(*draw_state);
551    }
552
553    /// Unsets the current draw state.
554    ///
555    /// This forces the current draw state to be set on next drawing call.
556    pub fn clear_draw_state(&mut self) {
557        self.current_draw_state = None;
558    }
559
560    /// Setup that should be called at the start of a frame's draw call.
561    pub fn draw_begin(&mut self, viewport: Viewport) -> Context {
562        let rect = viewport.rect;
563        let (x, y, w, h) = (rect[0], rect[1], rect[2], rect[3]);
564        self.viewport(x, y, w, h);
565        self.current_viewport = Some(viewport);
566        self.clear_program();
567        unsafe {
568            gl::Enable(gl::FRAMEBUFFER_SRGB);
569        }
570        Context::new_viewport(viewport)
571    }
572
573    /// Finalize the frame's draw calls.
574    pub fn draw_end(&mut self) {
575        if self.colored.offset > 0 {
576            let program = self.colored.program;
577            self.use_program(program);
578            self.colored.flush();
579        }
580        if self.textured.offset > 0 {
581            let program = self.textured.program;
582            self.use_program(program);
583            self.textured.flush();
584        }
585        if self.textured_color.offset > 0 {
586            let program = self.textured_color.program;
587            self.use_program(program);
588            self.textured_color.flush();
589        }
590    }
591
592    /// Convenience for wrapping draw calls with the begin and end methods.
593    ///
594    /// This is preferred over using the draw_begin & draw_end methods
595    /// explicitly but may be less flexible.
596    pub fn draw<F, U>(&mut self, viewport: Viewport, f: F) -> U
597    where
598        F: FnOnce(Context, &mut Self) -> U,
599    {
600        let c = self.draw_begin(viewport);
601        let res = f(c, self);
602        self.draw_end();
603        res
604    }
605
606    /// Assume all textures has alpha channel for now.
607    pub fn has_texture_alpha(&self, _texture: &Texture) -> bool {
608        true
609    }
610}
611
612impl Graphics for GlGraphics {
613    type Texture = Texture;
614
615    fn clear_color(&mut self, color: [f32; 4]) {
616        let color = gamma_srgb_to_linear(color);
617        unsafe {
618            let (r, g, b, a) = (color[0], color[1], color[2], color[3]);
619            gl::ClearColor(r, g, b, a);
620            gl::Clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT);
621        }
622    }
623
624    fn clear_stencil(&mut self, value: u8) {
625        unsafe {
626            gl::ClearStencil(value as i32);
627            gl::Clear(gl::STENCIL_BUFFER_BIT);
628        }
629    }
630
631    fn tri_list<F>(&mut self, draw_state: &DrawState, color: &[f32; 4], mut f: F)
632    where
633        F: FnMut(&mut dyn FnMut(&[[f32; 2]])),
634    {
635        let color = gamma_srgb_to_linear(*color);
636
637        if self.textured.offset > 0 {
638            let program = self.textured.program;
639            self.use_program(program);
640            self.textured.flush();
641        }
642        if self.textured_color.offset > 0 {
643            let program = self.textured_color.program;
644            self.use_program(program);
645            self.textured_color.flush();
646        }
647
648        // Flush when draw state changes.
649        if self.current_draw_state.is_none()
650            || self.current_draw_state.as_ref().unwrap() != draw_state
651        {
652            let program = self.colored.program;
653            self.use_program(program);
654            if self.current_draw_state.is_none() {
655                self.use_draw_state(&Default::default());
656            }
657            if self.colored.offset > 0 {
658                self.colored.flush();
659            }
660            self.use_draw_state(draw_state);
661        }
662
663        f(&mut |vertices: &[[f32; 2]]| {
664            let items = vertices.len();
665
666            // Render if there is not enough room.
667            if self.colored.offset + items > BUFFER_SIZE * CHUNKS {
668                let program = self.colored.program;
669                self.use_program(program);
670                self.colored.flush();
671            }
672
673            let shader = &mut self.colored;
674            for i in 0..items {
675                shader.color_buffer[shader.offset + i] = color;
676            }
677            shader.pos_buffer[shader.offset..shader.offset + items].copy_from_slice(vertices);
678            shader.offset += items;
679        });
680    }
681
682    fn tri_list_c<F>(&mut self, draw_state: &DrawState, mut f: F)
683    where
684        F: FnMut(&mut dyn FnMut(&[[f32; 2]], &[[f32; 4]])),
685    {
686        if self.textured.offset > 0 {
687            let program = self.textured.program;
688            self.use_program(program);
689            self.textured.flush();
690        }
691        if self.textured_color.offset > 0 {
692            let program = self.textured_color.program;
693            self.use_program(program);
694            self.textured_color.flush();
695        }
696
697        // Flush when draw state changes.
698        if self.current_draw_state.is_none()
699            || self.current_draw_state.as_ref().unwrap() != draw_state
700        {
701            let program = self.colored.program;
702            self.use_program(program);
703            if self.current_draw_state.is_none() {
704                self.use_draw_state(&Default::default());
705            }
706            if self.colored.offset > 0 {
707                self.colored.flush();
708            }
709            self.use_draw_state(draw_state);
710        }
711
712        f(&mut |vertices: &[[f32; 2]], colors: &[[f32; 4]]| {
713            let items = vertices.len();
714
715            // Render if there is not enough room.
716            if self.colored.offset + items > BUFFER_SIZE * CHUNKS {
717                let program = self.colored.program;
718                self.use_program(program);
719                self.colored.flush();
720            }
721
722            let shader = &mut self.colored;
723            for (i, color) in colors.iter().enumerate() {
724                shader.color_buffer[shader.offset + i] = gamma_srgb_to_linear(*color);
725            }
726            shader.pos_buffer[shader.offset..shader.offset + items].copy_from_slice(vertices);
727            shader.offset += items;
728        });
729    }
730
731    fn tri_list_uv<F>(
732        &mut self,
733        draw_state: &DrawState,
734        color: &[f32; 4],
735        texture: &Texture,
736        mut f: F,
737    ) where
738        F: FnMut(&mut dyn FnMut(&[[f32; 2]], &[[f32; 2]])),
739    {
740        let color = gamma_srgb_to_linear(*color);
741
742        if self.colored.offset > 0 {
743            let program = self.colored.program;
744            self.use_program(program);
745            self.colored.flush();
746        }
747        if self.textured_color.offset > 0 {
748            let program = self.textured_color.program;
749            self.use_program(program);
750            self.textured_color.flush();
751        }
752
753        // Flush when draw state changes.
754        if self.current_draw_state.is_none()
755            || self.current_draw_state.as_ref().unwrap() != draw_state
756            || self.textured.last_texture_id != texture.get_id()
757            || self.textured.last_color != color
758        {
759            let program = self.textured.program;
760            if self.current_draw_state.is_none() {
761                self.use_draw_state(&Default::default());
762            }
763            if self.textured.offset > 0 {
764                self.use_program(program);
765                self.textured.flush();
766            }
767            self.use_draw_state(draw_state);
768        }
769
770        self.textured.last_texture_id = texture.get_id();
771        self.textured.last_color = color;
772        f(&mut |vertices: &[[f32; 2]], texture_coords: &[[f32; 2]]| {
773            let items = vertices.len();
774
775            // Render if there is not enough room.
776            if self.textured.offset + items > BUFFER_SIZE * CHUNKS {
777                let shader_program = self.textured.program;
778                self.use_program(shader_program);
779                self.textured.flush();
780            }
781
782            let shader = &mut self.textured;
783            shader.pos_buffer[shader.offset..shader.offset + items].copy_from_slice(vertices);
784            shader.uv_buffer[shader.offset..shader.offset + items].copy_from_slice(texture_coords);
785            shader.offset += items;
786        });
787    }
788
789    fn tri_list_uv_c<F>(&mut self, draw_state: &DrawState, texture: &Texture, mut f: F)
790    where
791        F: FnMut(&mut dyn FnMut(&[[f32; 2]], &[[f32; 2]], &[[f32; 4]])),
792    {
793        if self.colored.offset > 0 {
794            let program = self.colored.program;
795            self.use_program(program);
796            self.colored.flush();
797        }
798        if self.textured.offset > 0 {
799            let program = self.textured.program;
800            self.use_program(program);
801            self.textured.flush();
802        }
803
804        // Flush when draw state changes.
805        if self.current_draw_state.is_none()
806            || self.current_draw_state.as_ref().unwrap() != draw_state
807            || self.textured_color.last_texture_id != texture.get_id()
808        {
809            let program = self.textured_color.program;
810            if self.current_draw_state.is_none() {
811                self.use_draw_state(&Default::default());
812            }
813            if self.textured_color.offset > 0 {
814                self.use_program(program);
815                self.textured_color.flush();
816            }
817            self.use_draw_state(draw_state);
818        }
819
820        self.textured_color.last_texture_id = texture.get_id();
821        f(
822            &mut |vertices: &[[f32; 2]], texture_coords: &[[f32; 2]], colors: &[[f32; 4]]| {
823                let items = vertices.len();
824
825                // Render if there is not enough room.
826                if self.textured_color.offset + items > BUFFER_SIZE * CHUNKS {
827                    let shader_program = self.textured_color.program;
828                    self.use_program(shader_program);
829                    self.textured_color.flush();
830                }
831
832                let shader = &mut self.textured_color;
833                for (i, color) in colors.iter().enumerate() {
834                    shader.color_buffer[shader.offset + i] = gamma_srgb_to_linear(*color);
835                }
836                shader.pos_buffer[shader.offset..shader.offset + items].copy_from_slice(vertices);
837                shader.uv_buffer[shader.offset..shader.offset + items]
838                    .copy_from_slice(texture_coords);
839                shader.offset += items;
840            },
841        );
842    }
843}
844
845// Might not fail if previous tests loaded functions.
846#[test]
847#[should_panic]
848fn test_gl_loaded() {
849    GlGraphics::new(OpenGL::V3_2);
850}