glium_graphics/
back_end.rs

1use glium::backend::Facade;
2use glium::index::{NoIndices, PrimitiveType};
3use glium::{Frame, Program, Surface, VertexBuffer};
4use graphics::color::gamma_srgb_to_linear;
5use graphics::{self, DrawState, Graphics, Viewport};
6use shader_version::glsl::GLSL;
7use shader_version::{OpenGL, Shaders};
8use Texture;
9
10use draw_state;
11
12const CHUNKS: usize = 100;
13
14#[derive(Copy, Clone)]
15struct PlainVertex {
16    color: [f32; 4],
17    pos: [f32; 2],
18}
19
20implement_vertex!(PlainVertex, color, pos);
21
22#[derive(Copy, Clone)]
23struct TexturedVertex {
24    pos: [f32; 2],
25    uv: [f32; 2],
26}
27
28implement_vertex!(TexturedVertex, pos, uv);
29
30#[derive(Copy, Clone)]
31struct TexturedColorVertex {
32    pos: [f32; 2],
33    uv: [f32; 2],
34    color: [f32; 4],
35}
36
37implement_vertex!(TexturedColorVertex, pos, uv, color);
38
39/// The resources needed for rendering 2D.
40pub struct Glium2d {
41    // The offset in vertices for colored rendering.
42    colored_offset: usize,
43    // The current draw state for colored rendering.
44    colored_draw_state: DrawState,
45    plain_buffer: VertexBuffer<PlainVertex>,
46    textured_buffer: VertexBuffer<TexturedVertex>,
47    textured_color_buffer: VertexBuffer<TexturedColorVertex>,
48    shader_texture_color: Program,
49    shader_texture: Program,
50    shader_color: Program,
51}
52
53impl Glium2d {
54    /// Creates a new `Glium2d`.
55    pub fn new<W>(opengl: OpenGL, window: &W) -> Glium2d
56    where
57        W: Facade,
58    {
59        use shaders::{colored, textured, textured_color};
60
61        let src = |bytes| unsafe { ::std::str::from_utf8_unchecked(bytes) };
62        let glsl = opengl.to_glsl();
63
64        let plain_buffer =
65            VertexBuffer::empty_dynamic(window, CHUNKS * graphics::BACK_END_MAX_VERTEX_COUNT)
66                .unwrap();
67        Glium2d {
68            colored_offset: 0,
69            colored_draw_state: Default::default(),
70            plain_buffer: plain_buffer,
71            textured_buffer: VertexBuffer::empty_dynamic(
72                window,
73                graphics::BACK_END_MAX_VERTEX_COUNT,
74            )
75            .unwrap(),
76            textured_color_buffer: VertexBuffer::empty_dynamic(
77                window,
78                graphics::BACK_END_MAX_VERTEX_COUNT,
79            )
80            .unwrap(),
81            shader_texture_color: Program::from_source(
82                window,
83                Shaders::new()
84                    .set(GLSL::V1_20, src(textured_color::VERTEX_GLSL_120))
85                    .set(GLSL::V1_50, src(textured_color::VERTEX_GLSL_150_CORE))
86                    .get(glsl)
87                    .unwrap(),
88                Shaders::new()
89                    .set(GLSL::V1_20, src(textured_color::FRAGMENT_GLSL_120))
90                    .set(GLSL::V1_50, src(textured_color::FRAGMENT_GLSL_150_CORE))
91                    .get(glsl)
92                    .unwrap(),
93                None,
94            )
95            .ok()
96            .expect("failed to initialize textured color shader"),
97            shader_texture: Program::from_source(
98                window,
99                Shaders::new()
100                    .set(GLSL::V1_20, src(textured::VERTEX_GLSL_120))
101                    .set(GLSL::V1_50, src(textured::VERTEX_GLSL_150_CORE))
102                    .get(glsl)
103                    .unwrap(),
104                Shaders::new()
105                    .set(GLSL::V1_20, src(textured::FRAGMENT_GLSL_120))
106                    .set(GLSL::V1_50, src(textured::FRAGMENT_GLSL_150_CORE))
107                    .get(glsl)
108                    .unwrap(),
109                None,
110            )
111            .ok()
112            .expect("failed to initialize textured shader"),
113            shader_color: Program::from_source(
114                window,
115                Shaders::new()
116                    .set(GLSL::V1_20, src(colored::VERTEX_GLSL_120))
117                    .set(GLSL::V1_50, src(colored::VERTEX_GLSL_150_CORE))
118                    .get(glsl)
119                    .unwrap(),
120                Shaders::new()
121                    .set(GLSL::V1_20, src(colored::FRAGMENT_GLSL_120))
122                    .set(GLSL::V1_50, src(colored::FRAGMENT_GLSL_150_CORE))
123                    .get(glsl)
124                    .unwrap(),
125                None,
126            )
127            .ok()
128            .expect("failed to initialize colored shader"),
129        }
130    }
131
132    /// Renders 2D graphics.
133    pub fn draw<F, U>(&mut self, target: &mut Frame, viewport: Viewport, f: F) -> U
134    where
135        F: FnOnce(graphics::Context, &mut GliumGraphics<Frame>) -> U,
136    {
137        use graphics::Context;
138
139        let ref mut g = GliumGraphics::new(self, target);
140        let c = Context::new_viewport(viewport);
141        let res = f(c, g);
142        if g.system.colored_offset > 0 {
143            g.flush_colored();
144        }
145        res
146    }
147}
148
149/// Graphics back-end.
150pub struct GliumGraphics<'d, 's, S: 's> {
151    system: &'d mut Glium2d,
152    surface: &'s mut S,
153}
154
155impl<'d, 's, S: Surface> GliumGraphics<'d, 's, S> {
156    /// Creates a new graphics object.
157    pub fn new(system: &'d mut Glium2d, surface: &'s mut S) -> GliumGraphics<'d, 's, S> {
158        GliumGraphics {
159            system: system,
160            surface: surface,
161        }
162    }
163
164    fn flush_colored(&mut self) {
165        let slice = self
166            .system
167            .plain_buffer
168            .slice(0..self.system.colored_offset)
169            .unwrap();
170
171        self.surface
172            .draw(
173                slice,
174                &NoIndices(PrimitiveType::TrianglesList),
175                &self.system.shader_color,
176                &uniform! {},
177                &draw_state::convert_draw_state(&self.system.colored_draw_state),
178            )
179            .ok()
180            .expect("failed to draw triangle list");
181
182        self.system.colored_offset = 0;
183        self.system.plain_buffer.invalidate();
184    }
185}
186
187/// Implemented by all graphics back-ends.
188impl<'d, 's, S: Surface> Graphics for GliumGraphics<'d, 's, S> {
189    type Texture = Texture;
190
191    /// Clears background with a color.
192    fn clear_color(&mut self, color: [f32; 4]) {
193        let color = gamma_srgb_to_linear(color);
194        let (r, g, b, a) = (color[0], color[1], color[2], color[3]);
195        self.surface.clear_color(r, g, b, a);
196    }
197
198    fn clear_stencil(&mut self, value: u8) {
199        self.surface.clear_stencil(value as i32);
200    }
201
202    /// Renders list of 2d triangles.
203    fn tri_list<F>(&mut self, draw_state: &DrawState, color: &[f32; 4], mut f: F)
204    where
205        F: FnMut(&mut dyn FnMut(&[[f32; 2]])),
206    {
207        let color = gamma_srgb_to_linear(*color);
208        // Flush when draw state changes.
209        if &self.system.colored_draw_state != draw_state {
210            self.flush_colored();
211            self.system.colored_draw_state = *draw_state;
212        }
213        f(&mut |vertices: &[[f32; 2]]| {
214            let n = vertices.len();
215            if self.system.colored_offset + n > CHUNKS * graphics::BACK_END_MAX_VERTEX_COUNT {
216                self.flush_colored();
217            }
218            let slice = self
219                .system
220                .plain_buffer
221                .slice(self.system.colored_offset..self.system.colored_offset + n)
222                .unwrap();
223            slice.write({
224                &(0..n)
225                    .map(|i| PlainVertex {
226                        color: color,
227                        pos: vertices[i],
228                    })
229                    .collect::<Vec<_>>()
230            });
231            self.system.colored_offset += n;
232        })
233    }
234
235    fn tri_list_c<F>(&mut self, draw_state: &DrawState, mut f: F)
236    where
237        F: FnMut(&mut dyn FnMut(&[[f32; 2]], &[[f32; 4]])),
238    {
239        // Flush when draw state changes.
240        if &self.system.colored_draw_state != draw_state {
241            self.flush_colored();
242            self.system.colored_draw_state = *draw_state;
243        }
244        f(&mut |vertices: &[[f32; 2]], colors: &[[f32; 4]]| {
245            let n = vertices.len();
246            if self.system.colored_offset + n > CHUNKS * graphics::BACK_END_MAX_VERTEX_COUNT {
247                self.flush_colored();
248            }
249            let slice = self
250                .system
251                .plain_buffer
252                .slice(self.system.colored_offset..self.system.colored_offset + n)
253                .unwrap();
254            slice.write({
255                &(0..n)
256                    .map(|i| PlainVertex {
257                        color: gamma_srgb_to_linear(colors[i]),
258                        pos: vertices[i],
259                    })
260                    .collect::<Vec<_>>()
261            });
262            self.system.colored_offset += n;
263        })
264    }
265
266    /// Renders list of 2d triangles.
267    ///
268    /// A texture coordinate is assigned per vertex.
269    /// The texture coordinates refers to the current texture.
270    fn tri_list_uv<F>(
271        &mut self,
272        draw_state: &DrawState,
273        color: &[f32; 4],
274        texture: &Texture,
275        mut f: F,
276    ) where
277        F: FnMut(&mut dyn FnMut(&[[f32; 2]], &[[f32; 2]])),
278    {
279        use glium::uniforms::{Sampler, SamplerWrapFunction};
280        use std::cmp::min;
281
282        let mut sampler = Sampler::new(&texture.0);
283        sampler.1.wrap_function = (texture.1[0], texture.1[1], SamplerWrapFunction::Clamp);
284
285        let color = gamma_srgb_to_linear(*color);
286        if self.system.colored_offset > 0 {
287            self.flush_colored();
288        }
289        f(&mut |vertices: &[[f32; 2]], texture_coords: &[[f32; 2]]| {
290            let len = min(vertices.len(), texture_coords.len());
291
292            self.system.textured_buffer.invalidate();
293            let slice = self.system.textured_buffer.slice(0..len).unwrap();
294
295            slice.write({
296                &(0..len)
297                    .map(|i| TexturedVertex {
298                        pos: vertices[i],
299                        // FIXME: The `1.0 - ...` is because of a wrong convention
300                        uv: [texture_coords[i][0], 1.0 - texture_coords[i][1]],
301                    })
302                    .collect::<Vec<_>>()
303            });
304
305            self.surface
306                .draw(
307                    slice,
308                    &NoIndices(PrimitiveType::TrianglesList),
309                    &self.system.shader_texture,
310                    &uniform! {
311                        color: color,
312                        s_texture: sampler
313                    },
314                    &draw_state::convert_draw_state(draw_state),
315                )
316                .ok()
317                .expect("failed to draw triangle list");
318        })
319    }
320
321    fn tri_list_uv_c<F>(
322        &mut self,
323        draw_state: &DrawState,
324        texture: &Texture,
325        mut f: F,
326    ) where
327        F: FnMut(&mut dyn FnMut(&[[f32; 2]], &[[f32; 2]], &[[f32; 4]])),
328    {
329        use glium::uniforms::{Sampler, SamplerWrapFunction};
330        use std::cmp::min;
331
332        let mut sampler = Sampler::new(&texture.0);
333        sampler.1.wrap_function = (texture.1[0], texture.1[1], SamplerWrapFunction::Clamp);
334
335        if self.system.colored_offset > 0 {
336            self.flush_colored();
337        }
338        f(&mut |vertices: &[[f32; 2]], texture_coords: &[[f32; 2]], colors: &[[f32; 4]]| {
339            let len = min(min(vertices.len(), texture_coords.len()), colors.len());
340
341            self.system.textured_color_buffer.invalidate();
342            let slice = self.system.textured_color_buffer.slice(0..len).unwrap();
343
344            slice.write({
345                &(0..len)
346                    .map(|i| TexturedColorVertex {
347                        pos: vertices[i],
348                        // FIXME: The `1.0 - ...` is because of a wrong convention
349                        uv: [texture_coords[i][0], 1.0 - texture_coords[i][1]],
350                        color: gamma_srgb_to_linear(colors[i]),
351                    })
352                    .collect::<Vec<_>>()
353            });
354
355            self.surface
356                .draw(
357                    slice,
358                    &NoIndices(PrimitiveType::TrianglesList),
359                    &self.system.shader_texture_color,
360                    &uniform! {
361                        s_texture: sampler
362                    },
363                    &draw_state::convert_draw_state(draw_state),
364                )
365                .ok()
366                .expect("failed to draw triangle list");
367        })
368    }
369}