ul_next/gpu_driver/
glium.rs

1//! A custom [`GpuDriver`] implementation for the `glium` backend.
2
3use std::{borrow::Cow, collections::HashMap, rc::Rc, sync::mpsc};
4
5use glium::{
6    backend::{Context, Facade},
7    framebuffer::SimpleFrameBuffer,
8    program,
9    texture::{ClientFormat, MipmapsOption, RawImage2d, SrgbTexture2d, UncompressedFloatFormat},
10    uniform,
11    uniforms::UniformBuffer,
12    vertex::{AttributeType, VertexBufferAny},
13    Blend, DrawParameters, Program, Surface, Texture2d,
14};
15
16use crate::{
17    bitmap::{BitmapFormat, OwnedBitmap},
18    gpu_driver::ShaderType,
19};
20
21use super::{GpuCommand, GpuDriver, IndexBuffer, RenderBuffer, VertexBuffer, VertexBufferFormat};
22pub use either_texture::{EitherSampler, EitherTexture};
23
24mod either_texture;
25
26type StaticVertexFormatBinding =
27    Cow<'static, [(Cow<'static, str>, usize, i32, AttributeType, bool)]>;
28
29lazy_static::lazy_static! {
30    static ref BINDING_2F4UB2F2F28F: StaticVertexFormatBinding = Cow::Owned(vec![
31        (
32            Cow::Borrowed("in_Position"),
33            0,
34            -1,
35            glium::vertex::AttributeType::F32F32,
36            false,
37        ),
38        (
39            Cow::Borrowed("in_Color"),
40            2 * ::std::mem::size_of::<f32>(),
41            -1,
42            glium::vertex::AttributeType::U8U8U8U8,
43            true,
44        ),
45        (
46            Cow::Borrowed("in_TexCoord"),
47            2 * ::std::mem::size_of::<f32>() + 4 * ::std::mem::size_of::<u8>(),
48            -1,
49            glium::vertex::AttributeType::F32F32,
50            false,
51        ),
52        (
53            Cow::Borrowed("in_ObjCoord"),
54            4 * ::std::mem::size_of::<f32>() + 4 * ::std::mem::size_of::<u8>(),
55            -1,
56            glium::vertex::AttributeType::F32F32,
57            false,
58        ),
59        (
60            Cow::Borrowed("in_Data0"),
61            6 * ::std::mem::size_of::<f32>() + 4 * ::std::mem::size_of::<u8>(),
62            -1,
63            glium::vertex::AttributeType::F32F32F32F32,
64            false,
65        ),
66        (
67            Cow::Borrowed("in_Data1"),
68            10 * ::std::mem::size_of::<f32>() + 4 * ::std::mem::size_of::<u8>(),
69            -1,
70            glium::vertex::AttributeType::F32F32F32F32,
71            false,
72        ),
73        (
74            Cow::Borrowed("in_Data2"),
75            14 * ::std::mem::size_of::<f32>() + 4 * ::std::mem::size_of::<u8>(),
76            -1,
77            glium::vertex::AttributeType::F32F32F32F32,
78            false,
79        ),
80        (
81            Cow::Borrowed("in_Data3"),
82            18 * ::std::mem::size_of::<f32>() + 4 * ::std::mem::size_of::<u8>(),
83            -1,
84            glium::vertex::AttributeType::F32F32F32F32,
85            false,
86        ),
87        (
88            Cow::Borrowed("in_Data4"),
89            22 * ::std::mem::size_of::<f32>() + 4 * ::std::mem::size_of::<u8>(),
90            -1,
91            glium::vertex::AttributeType::F32F32F32F32,
92            false,
93        ),
94        (
95            Cow::Borrowed("in_Data5"),
96            26 * ::std::mem::size_of::<f32>() + 4 * ::std::mem::size_of::<u8>(),
97            -1,
98            glium::vertex::AttributeType::F32F32F32F32,
99            false,
100        ),
101        (
102            Cow::Borrowed("in_Data6"),
103            30 * ::std::mem::size_of::<f32>() + 4 * ::std::mem::size_of::<u8>(),
104            -1,
105            glium::vertex::AttributeType::F32F32F32F32,
106            false,
107        ),
108    ]);
109
110    static ref BINDING_2F4UB2F: StaticVertexFormatBinding = Cow::Owned(vec![
111        (
112            Cow::Borrowed("in_Position"),
113            0,
114            -1,
115            glium::vertex::AttributeType::F32F32,
116            false,
117        ),
118        (
119            Cow::Borrowed("in_Color"),
120            2 * ::std::mem::size_of::<f32>(),
121            -1,
122            glium::vertex::AttributeType::U8U8U8U8,
123            true,
124        ),
125        (
126            Cow::Borrowed("in_TexCoord"),
127            2 * ::std::mem::size_of::<f32>() + 4 * ::std::mem::size_of::<u8>(),
128            -1,
129            glium::vertex::AttributeType::F32F32,
130            false,
131        ),
132    ]);
133
134}
135
136/// Errors can occure when calling [`create_gpu_driver`]
137#[derive(Debug, thiserror::Error)]
138pub enum GliumGpuDriverError {
139    #[error("Failed to create `glium` textures")]
140    TextureCreationError(#[from] glium::texture::TextureCreationError),
141    #[error("Failed to shader program in `glium`")]
142    ProgramCreationError(#[from] glium::program::ProgramChooserCreationError),
143    #[error("Failed to create `glium` index buffer")]
144    IndexBufferCreationError(#[from] glium::index::BufferCreationError),
145    #[error("Failed to create `glium` vertex buffer")]
146    VertexBufferCreationError(#[from] glium::vertex::BufferCreationError),
147    #[error("Failed to create `glium` buffer")]
148    BufferCreationError(#[from] glium::buffer::BufferCreationError),
149    #[error("Failed to create `glium` framebuffer")]
150    FrameBufferCreationError(#[from] glium::framebuffer::ValidationError),
151    #[error("Failed to draw")]
152    DrawError(#[from] glium::DrawError),
153    #[error(
154        "The index offset ({draw_index_offset}) and size ({draw_index_size}) used in draw is out of range from the selected index buffer (size = {index_buffer_size})"
155    )]
156    DrawIndexOutOfRange {
157        index_buffer_size: usize,
158        draw_index_offset: u32,
159        draw_index_size: u32,
160    },
161}
162
163/// Creates a GPU driver for `glium`.
164///
165/// `glium` context must run in one thread, but the `gpu_driver` require `Send`,
166/// since it works by creating multiple callbacks to `ultralight` C library.
167///
168/// We cannot guarantee that `ultralight` callbacks will be called in the same thread as `glium`,
169/// and thus, we create two objects. One is the sender, which implements `GpuDriver`,
170/// and should be used by the `ultralight` library. And the other is the receiver,
171/// which handles all gpu rendering logic.
172///
173/// **Make sure that both the sender and the receiver are alive for the whole**
174/// **lifetime of the [`Renderer`](crate::renderer::Renderer)**
175///
176/// # Examples
177/// ```no_run,ignore
178/// let (sender, mut receiver) = create_gpu_driver(&display);
179/// platform::set_gpu_driver(lib.clone(), sender);
180///
181/// renderer.render(); // will dispatch and send all events to `reciever` from `ultralight`
182/// receiver.render(); // will render all events received from `sender`
183/// ```
184pub fn create_gpu_driver<F>(
185    facade: &F,
186) -> Result<(GliumGpuDriverSender, GliumGpuDriverReceiver), GliumGpuDriverError>
187where
188    F: Facade + ?Sized,
189{
190    let (sender, receiver) = mpsc::channel();
191    Ok((
192        GliumGpuDriverSender {
193            next_texture_id: 0,
194            next_render_buffer_id: 0,
195            next_geometry_id: 0,
196            sender,
197        },
198        GliumGpuDriverReceiver::new(receiver, facade.get_context())?,
199    ))
200}
201
202enum GliumGpuCommand {
203    CreateTexture(u32, OwnedBitmap),
204    UpdateTexture(u32, OwnedBitmap),
205    DestroyTexture(u32),
206    CreateRenderBuffer(u32, RenderBuffer),
207    DestroyRenderBuffer(u32),
208    CreateGeometry(u32, GliumDriverVertexBuffer, IndexBuffer),
209    UpdateGeometry(u32, GliumDriverVertexBuffer, IndexBuffer),
210    DestroyGeometry(u32),
211    UpdateCommandList(Vec<GpuCommand>),
212}
213
214// a wrapper around the vertex buffer format
215enum GliumDriverVertexBuffer {
216    Format2f4ub2f(Vec<ul_sys::ULVertex_2f_4ub_2f>),
217    Format2f4ub2f2f28f(Vec<ul_sys::ULVertex_2f_4ub_2f_2f_28f>),
218}
219
220impl GliumDriverVertexBuffer {
221    fn into_glium_vertex_buffer<F>(
222        self,
223        context: &F,
224    ) -> Result<VertexBufferAny, GliumGpuDriverError>
225    where
226        F: Facade + ?Sized,
227    {
228        match self {
229            GliumDriverVertexBuffer::Format2f4ub2f(buf) => {
230                let element_size = std::mem::size_of::<ul_sys::ULVertex_2f_4ub_2f>();
231
232                // SAFETY: we know the structure of `ul_sys::ULVertex_2f_4ub_2f`
233                // and we know that we match it with the format description.
234                Ok(unsafe {
235                    glium::VertexBuffer::new_raw(context, &buf, &BINDING_2F4UB2F, element_size)
236                }?
237                .into())
238            }
239            GliumDriverVertexBuffer::Format2f4ub2f2f28f(buf) => {
240                let element_size = std::mem::size_of::<ul_sys::ULVertex_2f_4ub_2f_2f_28f>();
241
242                // SAFETY: we know the structure of `ul_sys::ULVertex_2f_4ub_2f_2f_28f`
243                // and we know that we match it with the format description.
244                Ok(unsafe {
245                    glium::VertexBuffer::new_raw(context, &buf, &BINDING_2F4UB2F2F28F, element_size)
246                }?
247                .into())
248            }
249        }
250    }
251}
252
253/// A [`GpuDriver`] implemented for integrating with `glium`.
254///
255/// Since `glium` is a single threaded library, and [`GpuDriver`] need to implement
256/// [`Send`] to be used in [`platform::set_gpu_driver`](crate::platform::set_gpu_driver),
257/// we used a **Sender/Receiver** design here, where this is the sender and the receiver is
258/// [`GliumGpuDriverReceiver`], which will handle all the sent commands from here,
259/// and render them into textures.
260pub struct GliumGpuDriverSender {
261    next_texture_id: u32,
262    next_render_buffer_id: u32,
263    next_geometry_id: u32,
264    sender: mpsc::Sender<GliumGpuCommand>,
265}
266
267impl GpuDriver for GliumGpuDriverSender {
268    fn begin_synchronize(&mut self) {
269        // unhandled
270    }
271
272    fn end_synchronize(&mut self) {
273        // unhandled
274    }
275
276    fn next_texture_id(&mut self) -> u32 {
277        self.next_texture_id += 1;
278        self.next_texture_id
279    }
280
281    fn create_texture(&mut self, texture_id: u32, bitmap: OwnedBitmap) {
282        self.sender
283            .send(GliumGpuCommand::CreateTexture(texture_id, bitmap))
284            .unwrap();
285    }
286
287    fn update_texture(&mut self, texture_id: u32, bitmap: OwnedBitmap) {
288        self.sender
289            .send(GliumGpuCommand::UpdateTexture(texture_id, bitmap))
290            .unwrap();
291    }
292
293    fn destroy_texture(&mut self, texture_id: u32) {
294        self.sender
295            .send(GliumGpuCommand::DestroyTexture(texture_id))
296            .unwrap();
297    }
298
299    fn next_render_buffer_id(&mut self) -> u32 {
300        self.next_render_buffer_id += 1;
301        self.next_render_buffer_id
302    }
303
304    fn create_render_buffer(&mut self, render_buffer_id: u32, render_buffer: RenderBuffer) {
305        self.sender
306            .send(GliumGpuCommand::CreateRenderBuffer(
307                render_buffer_id,
308                render_buffer,
309            ))
310            .unwrap();
311    }
312
313    fn destroy_render_buffer(&mut self, render_buffer_id: u32) {
314        self.sender
315            .send(GliumGpuCommand::DestroyRenderBuffer(render_buffer_id))
316            .unwrap();
317    }
318
319    fn next_geometry_id(&mut self) -> u32 {
320        self.next_geometry_id += 1;
321        self.next_geometry_id
322    }
323
324    fn create_geometry(
325        &mut self,
326        geometry_id: u32,
327        vertex_buffer: VertexBuffer,
328        index_buffer: IndexBuffer,
329    ) {
330        let glium_vertex_buffer = match vertex_buffer.format {
331            VertexBufferFormat::Format_2f_4ub_2f => {
332                // SAFETY: since the source is `u8`, and we check that the `head`
333                // and `tail` are empty, we make sure that all the bytes are
334                // used in the format correctly.
335                let (head, body, tail) = unsafe {
336                    vertex_buffer
337                        .buffer
338                        .as_slice()
339                        .align_to::<ul_sys::ULVertex_2f_4ub_2f>()
340                };
341                assert!(head.is_empty());
342                assert!(tail.is_empty());
343
344                GliumDriverVertexBuffer::Format2f4ub2f(body.to_vec())
345            }
346            VertexBufferFormat::Format_2f_4ub_2f_2f_28f => {
347                // SAFETY: since the source is `u8`, and we check that the `head`
348                // and `tail` are empty, we make sure that all the bytes are
349                // used in the format correctly.
350                let (head, body, tail) = unsafe {
351                    vertex_buffer
352                        .buffer
353                        .as_slice()
354                        .align_to::<ul_sys::ULVertex_2f_4ub_2f_2f_28f>()
355                };
356                assert!(head.is_empty());
357                assert!(tail.is_empty());
358
359                GliumDriverVertexBuffer::Format2f4ub2f2f28f(body.to_vec())
360            }
361        };
362
363        self.sender
364            .send(GliumGpuCommand::CreateGeometry(
365                geometry_id,
366                glium_vertex_buffer,
367                index_buffer,
368            ))
369            .unwrap();
370    }
371
372    fn update_geometry(
373        &mut self,
374        geometry_id: u32,
375        vertex_buffer: VertexBuffer,
376        index_buffer: IndexBuffer,
377    ) {
378        let glium_vertex_buffer = match vertex_buffer.format {
379            VertexBufferFormat::Format_2f_4ub_2f => {
380                // SAFETY: since the source is `u8`, and we check that the `head`
381                // and `tail` are empty, we make sure that all the bytes are
382                // used in the format correctly.
383                let (head, body, tail) = unsafe {
384                    vertex_buffer
385                        .buffer
386                        .as_slice()
387                        .align_to::<ul_sys::ULVertex_2f_4ub_2f>()
388                };
389                assert!(head.is_empty());
390                assert!(tail.is_empty());
391
392                GliumDriverVertexBuffer::Format2f4ub2f(body.to_vec())
393            }
394            VertexBufferFormat::Format_2f_4ub_2f_2f_28f => {
395                // SAFETY: since the source is `u8`, and we check that the `head`
396                // and `tail` are empty, we make sure that all the bytes are
397                // used in the format correctly.
398                let (head, body, tail) = unsafe {
399                    vertex_buffer
400                        .buffer
401                        .as_slice()
402                        .align_to::<ul_sys::ULVertex_2f_4ub_2f_2f_28f>()
403                };
404                assert!(head.is_empty());
405                assert!(tail.is_empty());
406
407                GliumDriverVertexBuffer::Format2f4ub2f2f28f(body.to_vec())
408            }
409        };
410
411        self.sender
412            .send(GliumGpuCommand::UpdateGeometry(
413                geometry_id,
414                glium_vertex_buffer,
415                index_buffer,
416            ))
417            .unwrap();
418    }
419
420    fn destroy_geometry(&mut self, geometry_id: u32) {
421        self.sender
422            .send(GliumGpuCommand::DestroyGeometry(geometry_id))
423            .unwrap();
424    }
425
426    fn update_command_list(&mut self, command_list: Vec<GpuCommand>) {
427        self.sender
428            .send(GliumGpuCommand::UpdateCommandList(command_list))
429            .unwrap();
430    }
431}
432
433struct GluimContextWrapper {
434    context: Rc<Context>,
435}
436
437impl Facade for GluimContextWrapper {
438    fn get_context(&self) -> &Rc<Context> {
439        &self.context
440    }
441}
442
443/// The receiver part of [`GliumGpuDriverSender`].
444///
445/// Since `glium` is a single threaded library, and [`GpuDriver`] need to implement
446/// [`Send`] to be used in [`platform::set_gpu_driver`](crate::platform::set_gpu_driver),
447/// we used a **Sender/Receiver** design here, where [`GliumGpuDriverSender`]
448/// is the sender and the receiver is this struct, when calling [`GliumGpuDriverReceiver::render`],
449/// we will render all the commands we get from [`GliumGpuDriverSender`] into textures
450/// which can be obtained by [`GliumGpuDriverReceiver::get_texture`].
451pub struct GliumGpuDriverReceiver {
452    /// receiver for the commands from the sender
453    receiver: mpsc::Receiver<GliumGpuCommand>,
454    /// glium context
455    context: GluimContextWrapper,
456
457    /// create a small texture, which will be used when
458    /// the gpu driver doesn't set a texture for a draw call
459    empty_texture: EitherTexture,
460    /// map for (id -> texture), and storing the `render_buffer` id if applicable.
461    texture_map: HashMap<u32, (EitherTexture, Option<u32>)>,
462    /// map for (id -> render_buffer metadata), the render_buffer itself is a texture
463    /// stored in the `texture_map`, we only create a framebuffer when drawing.
464    render_buffer_map: HashMap<u32, RenderBuffer>,
465    /// map for (id -> (vertex_buffer, index_buffer)).
466    geometry_map: HashMap<u32, (VertexBufferAny, glium::IndexBuffer<u32>)>,
467
468    /// Shader program for path rendering commands.
469    path_program: Program,
470    /// Shader program for fill rendering commands.
471    fill_program: Program,
472}
473
474impl GliumGpuDriverReceiver {
475    fn new(
476        receiver: mpsc::Receiver<GliumGpuCommand>,
477        context: &Rc<Context>,
478    ) -> Result<Self, GliumGpuDriverError> {
479        let context = GluimContextWrapper {
480            context: context.clone(),
481        };
482        let empty_texture = EitherTexture::Regular2d(Texture2d::empty(&context, 1, 1)?);
483
484        let texture_map = HashMap::new();
485        let render_buffer_map = HashMap::new();
486        let geometry_map = HashMap::new();
487
488        let path_program = program!(&context,
489        150 => {
490            vertex: include_str!("./shaders/v2f_c4f_t2f_vert.glsl"),
491            fragment: include_str!("./shaders/path_frag.glsl")
492        })?;
493        let fill_program = program!(&context,
494        150 => {
495            vertex: include_str!("./shaders/v2f_c4f_t2f_t2f_d28f_vert.glsl"),
496            fragment: include_str!("./shaders/fill_frag.glsl")
497        })?;
498
499        Ok(GliumGpuDriverReceiver {
500            receiver,
501            context,
502            empty_texture,
503            texture_map,
504            render_buffer_map,
505            geometry_map,
506
507            path_program,
508            fill_program,
509        })
510    }
511
512    /// helper function to create a texture based on bitmap
513    fn create_texture(&self, bitmap: &OwnedBitmap) -> Result<EitherTexture, GliumGpuDriverError> {
514        if bitmap.is_empty() {
515            Texture2d::empty(&self.context, bitmap.width(), bitmap.height())
516                .map_err(|e| e.into())
517                .map(EitherTexture::Regular2d)
518        } else {
519            // since its not empty, it should have a valid pixels.
520            let bitmap_pixels = bitmap.pixels().unwrap();
521
522            match bitmap.format() {
523                BitmapFormat::A8Unorm => {
524                    let img = RawImage2d {
525                        data: Cow::Borrowed(bitmap_pixels),
526                        width: bitmap.width(),
527                        height: bitmap.height(),
528                        format: ClientFormat::U8,
529                    };
530
531                    Texture2d::with_format(
532                        &self.context,
533                        img,
534                        UncompressedFloatFormat::U8,
535                        MipmapsOption::NoMipmap,
536                    )
537                    .map_err(|e| e.into())
538                    .map(EitherTexture::Regular2d)
539                }
540                BitmapFormat::Bgra8UnormSrgb => {
541                    // FIXME: the number of pixels sometimes may not be `width * height * 4`
542                    // because the bitmap will have padding for each row.
543                    // Normally, this is fixable by using `UNPACK_ROW_LENGTH` in OpenGL,
544                    // but glium doesn't support it for now
545
546                    let expected_row_bytes = bitmap.width() * 4;
547                    let data = if bitmap.row_bytes() != expected_row_bytes {
548                        let mut new_data = Vec::with_capacity(
549                            bitmap.height() as usize * expected_row_bytes as usize,
550                        );
551                        for row in bitmap_pixels.chunks(bitmap.row_bytes() as usize) {
552                            new_data.extend_from_slice(&row[..expected_row_bytes as usize]);
553                        }
554                        Cow::Owned(new_data)
555                    } else {
556                        Cow::Borrowed(bitmap_pixels)
557                    };
558
559                    let img = RawImage2d {
560                        data,
561                        width: bitmap.width(),
562                        height: bitmap.height(),
563                        format: ClientFormat::U8U8U8U8,
564                    };
565
566                    SrgbTexture2d::with_format(
567                        &self.context,
568                        img,
569                        glium::texture::SrgbFormat::U8U8U8U8,
570                        MipmapsOption::NoMipmap,
571                    )
572                    .map_err(|e| e.into())
573                    .map(EitherTexture::Srgb2d)
574                }
575            }
576        }
577    }
578}
579
580impl GliumGpuDriverReceiver {
581    /// Fetch `glium` texture by id, this id can be obtained from the current
582    /// `render_target` of a `view` by [`View::render_target`](crate::view::View::render_target).
583    ///
584    /// Example:
585    /// ```no_run,ignore
586    /// let render_target = view.render_target().unwrap();
587    /// let texture = receiver.get_texture(&render_target.texture_id);
588    /// ```
589    pub fn get_texture(&self, id: &u32) -> Option<&EitherTexture> {
590        self.texture_map.get(id).map(|(t, _)| t)
591    }
592
593    /// Flushes and renders all pending GPU commands recieved from [`GliumGpuDriverSender`],
594    /// which will be generated when calling [`Renderer::render`](crate::renderer::Renderer::render).
595    ///
596    /// **Note that this must be called for rendering to actually occure, as using**
597    /// **[`platform::set_gpu_driver`](crate::platform::set_gpu_driver) alone**
598    /// **with [`GliumGpuDriverSender`] is not enough.**
599    pub fn render(&mut self) -> Result<(), GliumGpuDriverError> {
600        while let Ok(cmd) = self.receiver.try_recv() {
601            match cmd {
602                GliumGpuCommand::CreateTexture(id, bitmap) => {
603                    let t = self.create_texture(&bitmap)?;
604                    self.texture_map.insert(id, (t, None));
605                }
606                GliumGpuCommand::UpdateTexture(id, bitmap) => {
607                    assert!(self.texture_map.contains_key(&id));
608
609                    let t = self.create_texture(&bitmap)?;
610
611                    let entry = self.texture_map.get_mut(&id).unwrap();
612                    entry.0 = t;
613                }
614                GliumGpuCommand::DestroyTexture(id) => {
615                    assert!(self.texture_map.contains_key(&id));
616                    self.texture_map.remove(&id);
617                }
618                GliumGpuCommand::CreateRenderBuffer(id, render_buffer) => {
619                    let entry = self.texture_map.get_mut(&render_buffer.texture_id).unwrap();
620                    entry.1 = Some(id);
621                    // make sure same texture sizes
622                    assert!(entry.0.width() == render_buffer.width);
623                    assert!(entry.0.height() == render_buffer.height);
624
625                    self.render_buffer_map.insert(id, render_buffer);
626                }
627                GliumGpuCommand::DestroyRenderBuffer(id) => {
628                    assert!(self.render_buffer_map.contains_key(&id));
629                    let render_buffer = self.render_buffer_map.remove(&id).unwrap();
630                    if let Some(entry) = self.texture_map.get_mut(&render_buffer.texture_id) {
631                        entry.1 = None;
632                    }
633                }
634                GliumGpuCommand::CreateGeometry(id, vert, index) => {
635                    let index_buffer = glium::IndexBuffer::new(
636                        &self.context,
637                        glium::index::PrimitiveType::TrianglesList,
638                        &index.buffer,
639                    )?;
640
641                    self.geometry_map.insert(
642                        id,
643                        (vert.into_glium_vertex_buffer(&self.context)?, index_buffer),
644                    );
645                }
646                GliumGpuCommand::UpdateGeometry(id, vert, index) => {
647                    assert!(self.geometry_map.contains_key(&id));
648
649                    let index_buffer = glium::IndexBuffer::new(
650                        &self.context,
651                        glium::index::PrimitiveType::TrianglesList,
652                        &index.buffer,
653                    )?;
654
655                    *self.geometry_map.get_mut(&id).unwrap() =
656                        (vert.into_glium_vertex_buffer(&self.context)?, index_buffer);
657                }
658                GliumGpuCommand::DestroyGeometry(id) => {
659                    assert!(self.geometry_map.contains_key(&id));
660                    self.geometry_map.remove(&id);
661                }
662                GliumGpuCommand::UpdateCommandList(cmd_list) => {
663                    for cmd in cmd_list {
664                        match cmd {
665                            GpuCommand::ClearRenderBuffer {
666                                render_buffer_id: id,
667                            } => {
668                                assert!(self.render_buffer_map.contains_key(&id));
669                                let render_buffer = self.render_buffer_map.get(&id).unwrap();
670
671                                // TODO: add support
672                                assert!(!render_buffer.has_stencil_buffer);
673                                assert!(!render_buffer.has_depth_buffer);
674
675                                let t = self.texture_map.get(&render_buffer.texture_id).unwrap();
676
677                                let mut frame_buffer = SimpleFrameBuffer::new(&self.context, &t.0)?;
678
679                                frame_buffer.clear(
680                                    None,
681                                    Some((0.0, 0.0, 0.0, 0.0)),
682                                    false,
683                                    None,
684                                    None,
685                                );
686                            }
687                            GpuCommand::DrawGeometry {
688                                gpu_state,
689                                geometry_id,
690                                indices_count,
691                                indices_offset,
692                            } => {
693                                assert!(self.geometry_map.contains_key(&geometry_id));
694                                let (vertex_buffer, index_buffer) =
695                                    self.geometry_map.get(&geometry_id).unwrap();
696
697                                assert!(self
698                                    .render_buffer_map
699                                    .contains_key(&gpu_state.render_buffer_id));
700                                let render_buffer = self
701                                    .render_buffer_map
702                                    .get(&gpu_state.render_buffer_id)
703                                    .unwrap();
704
705                                // TODO: add support
706                                assert!(!render_buffer.has_stencil_buffer);
707                                assert!(!render_buffer.has_depth_buffer);
708
709                                let index_buffer_slice = index_buffer
710                                    .slice(
711                                        indices_offset as usize
712                                            ..(indices_offset as usize + indices_count as usize),
713                                    )
714                                    .ok_or(GliumGpuDriverError::DrawIndexOutOfRange {
715                                        index_buffer_size: index_buffer.len(),
716                                        draw_index_offset: indices_offset,
717                                        draw_index_size: indices_count,
718                                    })?;
719
720                                let (t, _) =
721                                    self.texture_map.get(&render_buffer.texture_id).unwrap();
722
723                                let mut frame_buffer = SimpleFrameBuffer::new(&self.context, t)?;
724
725                                let used_program = match gpu_state.shader_type {
726                                    ShaderType::Fill => &self.fill_program,
727                                    ShaderType::FillPath => &self.path_program,
728                                };
729
730                                let scalar_data =
731                                    UniformBuffer::new(&self.context, gpu_state.uniform_scalar)?;
732                                let vector_data =
733                                    UniformBuffer::new(&self.context, gpu_state.uniform_vector)?;
734                                let clip_data = UniformBuffer::new(&self.context, gpu_state.clip)?;
735
736                                // Orthographic Projection matrix applied to
737                                // the `transformation` matrix.
738                                let orth_projection_matrix = [
739                                    [2.0 / gpu_state.viewport_width as f32, 0.0, 0.0, 0.0],
740                                    [0.0, 2.0 / gpu_state.viewport_height as f32, 0.0, 0.0],
741                                    [0.0, 0.0, -0.000002, 0.0],
742                                    [-1.0, -1.0, 0.818183, 1.0],
743                                ];
744                                // trasform matrix to project matrix
745                                let mut transformation = [
746                                    [0., 0., 0., 0.],
747                                    [0., 0., 0., 0.],
748                                    [0., 0., 0., 0.],
749                                    [0., 0., 0., 0.],
750                                ];
751
752                                // multiply matrices
753                                #[allow(clippy::needless_range_loop)]
754                                for i in 0..4 {
755                                    for j in 0..4 {
756                                        for k in 0..4 {
757                                            transformation[i][j] += gpu_state.transform[i * 4 + k]
758                                                * orth_projection_matrix[k][j];
759                                        }
760                                    }
761                                }
762
763                                // we use the supplied texture if it exists, or
764                                // an empty texture if it doesn't.
765                                let texture1 = if let Some(id) = gpu_state.texture_1_id {
766                                    let (t, _) = self.texture_map.get(&id).unwrap();
767                                    t
768                                } else {
769                                    &self.empty_texture
770                                };
771                                let texture2 = if let Some(id) = gpu_state.texture_2_id {
772                                    let (t, _) = self.texture_map.get(&id).unwrap();
773                                    t
774                                } else {
775                                    &self.empty_texture
776                                };
777                                let texture3 = if let Some(id) = gpu_state.texture_3_id {
778                                    let (t, _) = self.texture_map.get(&id).unwrap();
779                                    t
780                                } else {
781                                    &self.empty_texture
782                                };
783
784                                let uniforms = uniform! {
785                                    // TODO: state time
786                                    State: [0.0, gpu_state.viewport_width as f32, gpu_state.viewport_height as f32, 1.0],
787                                    Transform: transformation,
788                                    Scalar: &scalar_data,
789                                    Vector: &vector_data,
790                                    ClipSize: gpu_state.clip_size,
791                                    Clip: &clip_data,
792                                    Texture1: texture1.sampled(),
793                                    Texture2: texture2.sampled(),
794                                    Texture3: texture3.sampled(),
795                                };
796
797                                let params = DrawParameters {
798                                    viewport: Some(glium::Rect {
799                                        left: 0,
800                                        bottom: 0,
801                                        width: gpu_state.viewport_width,
802                                        height: gpu_state.viewport_height,
803                                    }),
804                                    scissor: if gpu_state.enable_scissor {
805                                        Some(glium::Rect {
806                                            left: gpu_state.scissor_rect.left as u32,
807                                            bottom: gpu_state.scissor_rect.top as u32,
808                                            width: (gpu_state.scissor_rect.right
809                                                - gpu_state.scissor_rect.left)
810                                                as u32,
811                                            height: (gpu_state.scissor_rect.bottom
812                                                - gpu_state.scissor_rect.top)
813                                                as u32,
814                                        })
815                                    } else {
816                                        None
817                                    },
818                                    blend: if gpu_state.enable_blend {
819                                        Blend::alpha_blending()
820                                    } else {
821                                        Blend::default()
822                                    },
823                                    ..DrawParameters::default()
824                                };
825
826                                frame_buffer.draw(
827                                    vertex_buffer,
828                                    index_buffer_slice,
829                                    used_program,
830                                    &uniforms,
831                                    &params,
832                                )?;
833                            }
834                        }
835                    }
836                }
837            }
838        }
839
840        Ok(())
841    }
842}