cognitive_renderer_gl/
renderer_gl.rs

1// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of
2// the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/
3
4//! This module contains GL renderer which allows drawing frame scenes with GL.
5
6// -------------------------------------------------------------------------------------------------
7
8use std;
9use std::time::Instant;
10
11use gl;
12use egl;
13
14use cognitive_graphics::{egl_tools, gl_tools};
15use cognitive_graphics::attributes::{DmabufAttributes, EglAttributes};
16use qualia::{SurfaceViewer, SurfaceContext, Illusion, Size, PixelFormat, SurfaceId};
17use qualia::{Buffer, DataSource, Image, MemoryView, Pixmap};
18
19use cache_gl::CacheGl;
20
21// -------------------------------------------------------------------------------------------------
22
23/// Vertex shader source code for OpenGL ES 2.0 (GLSL ES 100)
24const VERTEX_SHADER_100: &'static str = include_str!("vertex.100.glsl");
25
26/// Fragment shader source code for OpenGL ES 2.0 (GLSL ES 100)
27const FRAGMENT_SHADER_100: &'static str = include_str!("fragment.100.glsl");
28
29/// Vertex shader source code for OpenGL ES 3.0 (GLSL ES 300)
30const VERTEX_SHADER_300: &'static str = include_str!("vertex.300.glsl");
31
32/// Fragment shader source code for OpenGL ES 3.0 (GLSL ES 300)
33const FRAGMENT_SHADER_300: &'static str = include_str!("fragment.300.glsl");
34
35// -------------------------------------------------------------------------------------------------
36
37/// GL renderer.
38pub struct RendererGl {
39    egl: egl_tools::EglBucket,
40    size: Size,
41    cache: CacheGl,
42
43    // GL rendering
44    program: gl::types::GLuint,
45    loc_vertices: gl::types::GLint,
46    loc_texcoords: gl::types::GLint,
47    loc_texture: gl::types::GLint,
48    loc_screen_size: gl::types::GLint,
49    vbo_vertices: gl::types::GLuint,
50    vbo_texcoords: gl::types::GLuint,
51
52    // Pointers to extension functions
53    image_target_texture: Option<egl_tools::ImageTargetTexture2DOesFn>,
54}
55
56// -------------------------------------------------------------------------------------------------
57
58impl RendererGl {
59    /// `RendererGl` constructor.
60    pub fn new(egl: egl_tools::EglBucket, size: Size) -> Self {
61        RendererGl {
62            egl: egl,
63            size: size,
64            cache: CacheGl::new(),
65            program: gl::types::GLuint::default(),
66            loc_vertices: gl::types::GLint::default(),
67            loc_texcoords: gl::types::GLint::default(),
68            loc_texture: gl::types::GLint::default(),
69            loc_screen_size: gl::types::GLint::default(),
70            vbo_vertices: gl::types::GLuint::default(),
71            vbo_texcoords: gl::types::GLuint::default(),
72            image_target_texture: None,
73        }
74    }
75
76    /// Initialize renderer.
77    ///  - prepare shaders and program,
78    ///  - bind locations,
79    ///  - generate buffers,
80    ///  - configure textures,
81    pub fn initialize(&mut self) -> Result<(), Illusion> {
82        gl::load_with(|s| egl::get_proc_address(s) as *const std::os::raw::c_void);
83
84        let _context = self.egl.make_current()?;
85
86        // Get GLSL version
87        let (vshader_src, fshader_src) = match gl_tools::get_shading_lang_version() {
88            gl_tools::GlslVersion::Glsl100 => {
89                (VERTEX_SHADER_100.to_owned(), FRAGMENT_SHADER_100.to_owned())
90            }
91            gl_tools::GlslVersion::Glsl300 => {
92                (VERTEX_SHADER_300.to_owned(), FRAGMENT_SHADER_300.to_owned())
93            }
94            gl_tools::GlslVersion::Unknown => {
95                return Err(Illusion::General(format!("Could not figure out GLSL version")));
96            }
97        };
98
99        // Compile shades, link program and get locations
100        self.program = gl_tools::prepare_shader_program(vshader_src, fshader_src)?;
101        self.loc_vertices = gl_tools::get_attrib_location(self.program, "vertices".to_owned())?;
102        self.loc_texcoords = gl_tools::get_attrib_location(self.program, "texcoords".to_owned())?;
103        self.loc_texture = gl_tools::get_uniform_location(self.program, "texture".to_owned())?;
104        self.loc_screen_size = gl_tools::get_uniform_location(self.program,
105                                                              "screen_size".to_owned())?;
106
107        // Generate vertex buffer object
108        unsafe {
109            gl::GenBuffers(1, &mut self.vbo_vertices);
110            gl::GenBuffers(1, &mut self.vbo_texcoords);
111        }
112
113        // Get needed extension functions
114        self.image_target_texture = egl_tools::get_proc_addr_of_image_target_texture_2d_oes();
115
116        Ok(())
117    }
118
119    /// Draw passed frame scene.
120    pub fn draw(&mut self,
121                layunder: &Vec<SurfaceContext>,
122                surfaces: &Vec<SurfaceContext>,
123                layover: &Vec<SurfaceContext>,
124                viewer: &SurfaceViewer)
125                -> Result<(), Illusion> {
126        let _context = self.egl.make_current()?;
127        self.prepare_view();
128        self.draw_surfaces(layunder, viewer);
129        self.draw_surfaces(surfaces, viewer);
130        self.draw_surfaces(layover, viewer);
131        self.release_view();
132        Ok(())
133    }
134
135    /// Swap buffers.
136    pub fn swap_buffers(&mut self) -> Result<(), Illusion> {
137        let context = self.egl.make_current()?;
138        context.swap_buffers()?;
139        Ok(())
140    }
141
142    /// Reads pixels for whole screen and returns image data as `Buffer`.
143    pub fn take_screenshot(&self) -> Result<Buffer, Illusion> {
144        let _context = self.egl.make_current()?;
145
146        let format = PixelFormat::ARGB8888;
147        let stride = format.get_size() * self.size.width;
148        let size = stride * self.size.height;
149        let mut dst: Vec<u8> = Vec::with_capacity(size);
150        unsafe { dst.set_len(size) };
151
152        unsafe {
153            gl::ReadBuffer(gl::BACK);
154            gl::ReadPixels(0,
155                           0,
156                           self.size.width as i32,
157                           self.size.height as i32,
158                           gl::RGBA,
159                           gl::UNSIGNED_BYTE,
160                           dst.as_mut_ptr() as *mut std::os::raw::c_void);
161        }
162
163        // GL returns data starting from bottom. We have to reverse the order.
164        let mut data = Vec::new();
165        for chunk in dst.chunks(stride).rev() {
166            data.extend(chunk);
167        }
168
169        Ok(Buffer::new(format, self.size.width, self.size.height, stride, data))
170    }
171}
172
173// -------------------------------------------------------------------------------------------------
174
175/// Drawing helpers.
176impl RendererGl {
177    /// Prepare view for drawing.
178    fn prepare_view(&self) {
179        unsafe {
180            gl::ClearColor(0.0, 0.3, 0.5, 1.0);
181            gl::Clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT);
182
183            gl::Enable(gl::BLEND);
184            gl::BlendFunc(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA);
185
186            gl::UseProgram(self.program);
187            gl::Uniform2i(self.loc_screen_size, self.size.width as i32, self.size.height as i32);
188        }
189    }
190
191    /// Loads memory buffer as texture. Returns dimensions of the buffer.
192    fn load_buffer_as_texture(&mut self,
193                              sid: SurfaceId,
194                              buffer: &MemoryView,
195                              time_stamp: Instant)
196                              -> Option<Size> {
197        let format = {
198            match buffer.get_format() {
199                // NOTE: Mixing channels is intentional. In `PixelFormat` one reads it from
200                // right to left, and in `gl` from left to right.
201                PixelFormat::XBGR8888 => gl::RGBA,
202                PixelFormat::ABGR8888 => gl::RGBA,
203                PixelFormat::XRGB8888 => gl::BGRA,
204                PixelFormat::ARGB8888 => gl::BGRA,
205            }
206        };
207
208        // Get or generate texture info
209        let texinfo = self.cache.get_or_generate_info(sid);
210        unsafe { gl::BindTexture(gl::TEXTURE_2D, texinfo.get_texture()) };
211
212        // If buffer was updated recently - load it to GPU memory
213        if texinfo.is_younger(time_stamp) {
214            unsafe {
215                gl::TexImage2D(gl::TEXTURE_2D, // target
216                               0, // level, 0 = no mipmap
217                               gl::RGBA as gl::types::GLint, // internal format
218                               buffer.get_width() as gl::types::GLint, // width
219                               buffer.get_height() as gl::types::GLint, // height
220                               0, // always 0 in OpenGL ES
221                               format, // format
222                               gl::UNSIGNED_BYTE, // type
223                               buffer.as_ptr() as *const _);
224            }
225            self.cache.update(sid, None);
226        }
227
228        Some(buffer.get_size())
229    }
230
231    /// Loads hardware image as texture. Returns dimensions of the image.
232    fn load_image_as_texture(&mut self,
233                             sid: SurfaceId,
234                             attrs: &EglAttributes,
235                             time_stamp: Instant)
236                             -> Option<Size> {
237        // Get or generate texture info
238        let texinfo = self.cache.get_or_generate_info(sid);
239        unsafe { gl::BindTexture(gl::TEXTURE_2D, texinfo.get_texture()) };
240
241        // If buffer was updated recently - load it to GPU memory
242        if texinfo.is_younger(time_stamp) {
243            // Destroy image if it was created previously
244            if let Some(image) = texinfo.get_image() {
245                let _ = egl_tools::destroy_image(self.egl.display, image);
246            }
247
248            // Create the image
249            if let Some(image_target_texture) = self.image_target_texture {
250                let image = egl_tools::create_image(self.egl.display, attrs);
251                if let Some(ref img) = image {
252                    // Set image as texture target and update cache
253                    image_target_texture(gl::TEXTURE_2D, img.as_raw());
254                    self.cache.update(sid, image.clone());
255                    Some(attrs.get_size())
256                } else {
257                    None
258                }
259            } else {
260                None
261            }
262        } else {
263            Some(attrs.get_size())
264        }
265    }
266
267    /// Loads dmabuf as texture. Returns dimensions of the dmabuf.
268    fn load_dmabuf_as_texture(&mut self,
269                              sid: SurfaceId,
270                              attrs: &DmabufAttributes,
271                              time_stamp: Instant)
272                              -> Option<Size> {
273        // Get or generate texture info
274        let texinfo = self.cache.get_or_generate_info(sid);
275        unsafe { gl::BindTexture(gl::TEXTURE_2D, texinfo.get_texture()) };
276
277        // If buffer was updated recently - load it to GPU memory
278        if texinfo.is_younger(time_stamp) {
279            // Destroy image if it was created previously
280            if let Some(image) = texinfo.get_image() {
281                let _ = egl_tools::destroy_image(self.egl.display, image);
282            }
283
284            // Create the image
285            if let Some(image_target_texture) = self.image_target_texture {
286                let image = egl_tools::import_dmabuf(self.egl.display, attrs);
287                if let Some(ref img) = image {
288                    // Set image as texture target and update cache
289                    image_target_texture(gl::TEXTURE_2D, img.as_raw());
290                    self.cache.update(sid, image.clone());
291                    Some(attrs.get_size())
292                } else {
293                    None
294                }
295            } else {
296                None
297            }
298        } else {
299            Some(attrs.get_size())
300        }
301    }
302
303    /// Load textures and prepare vertices.
304    fn load_texture_and_prepare_vertices(&mut self,
305                                         viewer: &SurfaceViewer,
306                                         context: &SurfaceContext,
307                                         vertices: &mut [gl::types::GLfloat],
308                                         texcoords: &mut [gl::types::GLfloat]) {
309        if let Some(ref surface) = viewer.get_surface(context.id) {
310            let size = {
311                match surface.data_source {
312                    DataSource::Shm { ref source, time_stamp } => {
313                        self.load_buffer_as_texture(context.id, source, time_stamp)
314                    }
315                    DataSource::EglImage { ref source, time_stamp } => {
316                        self.load_image_as_texture(context.id, source, time_stamp)
317                    }
318                    DataSource::Dmabuf { ref source, time_stamp } => {
319                        self.load_dmabuf_as_texture(context.id, source, time_stamp)
320                    }
321                    DataSource::None => None,
322                }
323            };
324
325            if let Some(size) = size {
326                let left = (context.pos.x - surface.offset.x) as gl::types::GLfloat;
327                let top = (context.pos.y - surface.offset.y) as gl::types::GLfloat;
328                let right = left + size.width as gl::types::GLfloat;
329                let bottom = top + size.height as gl::types::GLfloat;
330
331                vertices[0] = left;
332                vertices[1] = top;
333                vertices[2] = right;
334                vertices[3] = top;
335                vertices[4] = left;
336                vertices[5] = bottom;
337                vertices[6] = right;
338                vertices[7] = top;
339                vertices[8] = right;
340                vertices[9] = bottom;
341                vertices[10] = left;
342                vertices[11] = bottom;
343
344                // TODO: Use element buffer.
345                texcoords[0] = 0.0;
346                texcoords[1] = 0.0;
347                texcoords[2] = 1.0;
348                texcoords[3] = 0.0;
349                texcoords[4] = 0.0;
350                texcoords[5] = 1.0;
351                texcoords[6] = 1.0;
352                texcoords[7] = 0.0;
353                texcoords[8] = 1.0;
354                texcoords[9] = 1.0;
355                texcoords[10] = 0.0;
356                texcoords[11] = 1.0;
357            } else {
358                log_warn3!("Renderer: No buffer for surface {}", context.id);
359            }
360        } else {
361            log_warn3!("Renderer: No info for surface {}", context.id);
362        }
363    }
364
365    /// Draws surfaces.
366    fn draw_surfaces(&mut self, surfaces: &Vec<SurfaceContext>, viewer: &SurfaceViewer) {
367        if surfaces.len() == 0 {
368            return;
369        }
370
371        // Prepare vertices positions and upload textures
372        let vertices_len = 12 * surfaces.len();
373        let vertices_size = vertices_len * std::mem::size_of::<gl::types::GLfloat>();
374        let mut vertices = vec![0.0; vertices_len];
375        let mut texcoords = vec![0.0; vertices_len];
376
377        for i in 0..surfaces.len() {
378            // Activate the target texture
379            unsafe { gl::ActiveTexture(gl::TEXTURE0 + i as u32) };
380
381            // Bind data to texture and prepare vertices
382            self.load_texture_and_prepare_vertices(viewer,
383                                                   &surfaces[i],
384                                                   &mut vertices[12 * i..12 * i + 12],
385                                                   &mut texcoords[12 * i..12 * i + 12]);
386        }
387
388        unsafe {
389            // Upload positions to vertex buffer object
390            gl::BindBuffer(gl::ARRAY_BUFFER, self.vbo_vertices);
391            gl::EnableVertexAttribArray(self.loc_vertices as gl::types::GLuint);
392            gl::VertexAttribPointer(self.loc_vertices as gl::types::GLuint,
393                                    2,
394                                    gl::FLOAT,
395                                    gl::FALSE,
396                                    2 *
397                                    std::mem::size_of::<gl::types::GLfloat>() as gl::types::GLint,
398                                    std::ptr::null());
399            gl::BufferData(gl::ARRAY_BUFFER,
400                           vertices_size as isize,
401                           vertices.as_ptr() as *const _,
402                           gl::DYNAMIC_DRAW);
403
404            // Upload positions to vertex buffer object
405            gl::BindBuffer(gl::ARRAY_BUFFER, self.vbo_texcoords);
406            gl::EnableVertexAttribArray(self.loc_texcoords as gl::types::GLuint);
407            gl::VertexAttribPointer(self.loc_texcoords as gl::types::GLuint,
408                                    2,
409                                    gl::FLOAT,
410                                    gl::FALSE,
411                                    2 *
412                                    std::mem::size_of::<gl::types::GLfloat>() as gl::types::GLint,
413                                    std::ptr::null());
414            gl::BufferData(gl::ARRAY_BUFFER,
415                           vertices_size as isize,
416                           texcoords.as_ptr() as *const _,
417                           gl::DYNAMIC_DRAW);
418
419            // Redraw everything
420            for i in 0..surfaces.len() as i32 {
421                gl::Uniform1i(self.loc_texture, i);
422                gl::DrawArrays(gl::TRIANGLES, 6 * i, 6);
423            }
424
425            // Release resources
426            gl::DisableVertexAttribArray(self.loc_texcoords as gl::types::GLuint);
427            gl::DisableVertexAttribArray(self.loc_vertices as gl::types::GLuint);
428        }
429    }
430
431    /// Unbind framebuffer and program.
432    fn release_view(&self) {
433        unsafe {
434            gl::BindFramebuffer(gl::DRAW_FRAMEBUFFER, 0);
435            gl::UseProgram(0);
436        }
437    }
438}
439
440// -------------------------------------------------------------------------------------------------