From 60dcfc354e844757d2291bf44cb21c624bc270c2 Mon Sep 17 00:00:00 2001
From: Héctor Ramón Jiménez <hector0193@gmail.com>
Date: Thu, 21 May 2020 19:07:33 +0200
Subject: Draft `triangle` pipeline in `iced_glow`

---
 glow/src/lib.rs               |   1 +
 glow/src/program.rs           |  39 +++++++
 glow/src/quad.rs              |  41 +------
 glow/src/shader/triangle.frag |   8 ++
 glow/src/shader/triangle.vert |  13 +++
 glow/src/triangle.rs          | 251 +++++++++++++++++++++++++++++++++++++++---
 6 files changed, 301 insertions(+), 52 deletions(-)
 create mode 100644 glow/src/program.rs
 create mode 100644 glow/src/shader/triangle.frag
 create mode 100644 glow/src/shader/triangle.vert

diff --git a/glow/src/lib.rs b/glow/src/lib.rs
index d40ed0ae..a32c787e 100644
--- a/glow/src/lib.rs
+++ b/glow/src/lib.rs
@@ -5,6 +5,7 @@
 #![forbid(rust_2018_idioms)]
 
 mod backend;
+mod program;
 mod quad;
 mod text;
 mod triangle;
diff --git a/glow/src/program.rs b/glow/src/program.rs
new file mode 100644
index 00000000..489a194f
--- /dev/null
+++ b/glow/src/program.rs
@@ -0,0 +1,39 @@
+use glow::HasContext;
+
+pub unsafe fn create(
+    gl: &glow::Context,
+    shader_sources: &[(u32, &str)],
+) -> <glow::Context as HasContext>::Program {
+    let program = gl.create_program().expect("Cannot create program");
+
+    let mut shaders = Vec::with_capacity(shader_sources.len());
+
+    for (shader_type, shader_source) in shader_sources.iter() {
+        let shader = gl
+            .create_shader(*shader_type)
+            .expect("Cannot create shader");
+
+        gl.shader_source(shader, shader_source);
+        gl.compile_shader(shader);
+
+        if !gl.get_shader_compile_status(shader) {
+            panic!(gl.get_shader_info_log(shader));
+        }
+
+        gl.attach_shader(program, shader);
+
+        shaders.push(shader);
+    }
+
+    gl.link_program(program);
+    if !gl.get_program_link_status(program) {
+        panic!(gl.get_program_info_log(program));
+    }
+
+    for shader in shaders {
+        gl.detach_shader(program, shader);
+        gl.delete_shader(shader);
+    }
+
+    program
+}
diff --git a/glow/src/quad.rs b/glow/src/quad.rs
index acac3219..3a051268 100644
--- a/glow/src/quad.rs
+++ b/glow/src/quad.rs
@@ -1,3 +1,4 @@
+use crate::program;
 use crate::Transformation;
 use glow::HasContext;
 use iced_graphics::layer;
@@ -18,7 +19,7 @@ pub struct Pipeline {
 impl Pipeline {
     pub fn new(gl: &glow::Context) -> Pipeline {
         let program = unsafe {
-            create_program(
+            program::create(
                 gl,
                 &[
                     (glow::VERTEX_SHADER, include_str!("shader/quad.vert")),
@@ -138,44 +139,6 @@ impl Pipeline {
     }
 }
 
-unsafe fn create_program(
-    gl: &glow::Context,
-    shader_sources: &[(u32, &str)],
-) -> <glow::Context as HasContext>::Program {
-    let program = gl.create_program().expect("Cannot create program");
-
-    let mut shaders = Vec::with_capacity(shader_sources.len());
-
-    for (shader_type, shader_source) in shader_sources.iter() {
-        let shader = gl
-            .create_shader(*shader_type)
-            .expect("Cannot create shader");
-
-        gl.shader_source(shader, shader_source);
-        gl.compile_shader(shader);
-
-        if !gl.get_shader_compile_status(shader) {
-            panic!(gl.get_shader_info_log(shader));
-        }
-
-        gl.attach_shader(program, shader);
-
-        shaders.push(shader);
-    }
-
-    gl.link_program(program);
-    if !gl.get_program_link_status(program) {
-        panic!(gl.get_program_info_log(program));
-    }
-
-    for shader in shaders {
-        gl.detach_shader(program, shader);
-        gl.delete_shader(shader);
-    }
-
-    program
-}
-
 unsafe fn create_instance_buffer(
     gl: &glow::Context,
     size: usize,
diff --git a/glow/src/shader/triangle.frag b/glow/src/shader/triangle.frag
new file mode 100644
index 00000000..e39c45e7
--- /dev/null
+++ b/glow/src/shader/triangle.frag
@@ -0,0 +1,8 @@
+#version 450
+
+layout(location = 0) in vec4 i_Color;
+layout(location = 0) out vec4 o_Color;
+
+void main() {
+    o_Color = i_Color;
+}
diff --git a/glow/src/shader/triangle.vert b/glow/src/shader/triangle.vert
new file mode 100644
index 00000000..cfa4e995
--- /dev/null
+++ b/glow/src/shader/triangle.vert
@@ -0,0 +1,13 @@
+#version 450
+
+layout(location = 0) uniform mat4 u_Transform;
+
+layout(location = 0) in vec2 i_Position;
+layout(location = 1) in vec4 i_Color;
+
+layout(location = 0) out vec4 o_Color;
+
+void main() {
+    gl_Position = u_Transform * vec4(i_Position, 0.0, 1.0);
+    o_Color = i_Color;
+}
diff --git a/glow/src/triangle.rs b/glow/src/triangle.rs
index 5836f0cf..3f4aaa1b 100644
--- a/glow/src/triangle.rs
+++ b/glow/src/triangle.rs
@@ -1,22 +1,106 @@
 //! Draw meshes of triangles.
-use crate::{settings, Transformation};
+use crate::program;
+use crate::settings;
+use crate::Transformation;
+use glow::HasContext;
 use iced_graphics::layer;
+use std::marker::PhantomData;
 
-pub use iced_graphics::triangle::Mesh2D;
+pub use iced_graphics::triangle::{Mesh2D, Vertex2D};
 
-const UNIFORM_BUFFER_SIZE: usize = 100;
 const VERTEX_BUFFER_SIZE: usize = 10_000;
 const INDEX_BUFFER_SIZE: usize = 10_000;
 
 #[derive(Debug)]
-pub(crate) struct Pipeline {}
+pub(crate) struct Pipeline {
+    program: <glow::Context as HasContext>::Program,
+    vertex_array: <glow::Context as HasContext>::VertexArray,
+    vertices: Buffer<Vertex2D>,
+    indices: Buffer<u32>,
+    current_transform: Transformation,
+}
 
 impl Pipeline {
     pub fn new(
         gl: &glow::Context,
         antialiasing: Option<settings::Antialiasing>,
     ) -> Pipeline {
-        Pipeline {}
+        let program = unsafe {
+            program::create(
+                gl,
+                &[
+                    (glow::VERTEX_SHADER, include_str!("shader/triangle.vert")),
+                    (
+                        glow::FRAGMENT_SHADER,
+                        include_str!("shader/triangle.frag"),
+                    ),
+                ],
+            )
+        };
+
+        unsafe {
+            gl.use_program(Some(program));
+
+            gl.uniform_matrix_4_f32_slice(
+                Some(0),
+                false,
+                &Transformation::identity().into(),
+            );
+
+            gl.use_program(None);
+        }
+
+        let vertex_array =
+            unsafe { gl.create_vertex_array().expect("Create vertex array") };
+
+        unsafe {
+            gl.bind_vertex_array(Some(vertex_array));
+        }
+
+        let vertices = unsafe {
+            Buffer::new(
+                gl,
+                glow::ARRAY_BUFFER,
+                glow::DYNAMIC_DRAW,
+                VERTEX_BUFFER_SIZE,
+            )
+        };
+
+        let indices = unsafe {
+            Buffer::new(
+                gl,
+                glow::ELEMENT_ARRAY_BUFFER,
+                glow::DYNAMIC_DRAW,
+                INDEX_BUFFER_SIZE,
+            )
+        };
+
+        unsafe {
+            let stride = std::mem::size_of::<Vertex2D>() as i32;
+
+            gl.enable_vertex_attrib_array(0);
+            gl.vertex_attrib_pointer_f32(0, 2, glow::FLOAT, false, stride, 0);
+
+            gl.enable_vertex_attrib_array(1);
+            gl.vertex_attrib_pointer_f32(
+                1,
+                4,
+                glow::FLOAT,
+                false,
+                stride,
+                4 * 2,
+            );
+
+            gl.bind_vertex_array(None);
+        }
+
+        Pipeline {
+            program,
+            vertex_array,
+            vertices,
+            indices,
+            current_transform: Transformation::identity(),
+        }
     }
 
     pub fn draw(
@@ -28,6 +112,106 @@ impl Pipeline {
         scale_factor: f32,
         meshes: &[layer::Mesh<'_>],
     ) {
+        unsafe {
+            gl.enable(glow::SCISSOR_TEST);
+            gl.use_program(Some(self.program));
+            gl.bind_vertex_array(Some(self.vertex_array));
+        }
+
+        // This looks a bit crazy, but we are just counting how many vertices
+        // and indices we will need to handle.
+        // TODO: Improve readability
+        let (total_vertices, total_indices) = meshes
+            .iter()
+            .map(|layer::Mesh { buffers, .. }| {
+                (buffers.vertices.len(), buffers.indices.len())
+            })
+            .fold((0, 0), |(total_v, total_i), (v, i)| {
+                (total_v + v, total_i + i)
+            });
+
+        // Then we ensure the current buffers are big enough, resizing if
+        // necessary
+        unsafe {
+            self.vertices.bind(gl, total_vertices);
+            self.indices.bind(gl, total_indices);
+        }
+
+        // We upload all the vertices and indices upfront
+        let mut last_vertex = 0;
+        let mut last_index = 0;
+
+        for layer::Mesh { buffers, .. } in meshes {
+            unsafe {
+                gl.buffer_sub_data_u8_slice(
+                    glow::ARRAY_BUFFER,
+                    (last_vertex * std::mem::size_of::<Vertex2D>()) as i32,
+                    bytemuck::cast_slice(&buffers.vertices),
+                );
+
+                gl.buffer_sub_data_u8_slice(
+                    glow::ELEMENT_ARRAY_BUFFER,
+                    (last_index * std::mem::size_of::<u32>()) as i32,
+                    bytemuck::cast_slice(&buffers.indices),
+                );
+
+                last_vertex += buffers.vertices.len();
+                last_index += buffers.indices.len();
+            }
+        }
+
+        // Then we draw each mesh using offsets
+        let mut last_vertex = 0;
+        let mut last_index = 0;
+
+        for layer::Mesh {
+            buffers,
+            origin,
+            clip_bounds,
+        } in meshes
+        {
+            let transform =
+                transformation * Transformation::translate(origin.x, origin.y);
+
+            let clip_bounds = (*clip_bounds * scale_factor).round();
+
+            unsafe {
+                if self.current_transform != transform {
+                    gl.uniform_matrix_4_f32_slice(
+                        Some(0),
+                        false,
+                        &transform.into(),
+                    );
+
+                    self.current_transform = transform;
+                }
+
+                gl.scissor(
+                    clip_bounds.x as i32,
+                    (target_height - (clip_bounds.y + clip_bounds.height))
+                        as i32,
+                    clip_bounds.width as i32,
+                    clip_bounds.height as i32,
+                );
+
+                gl.draw_elements_base_vertex(
+                    glow::TRIANGLES,
+                    buffers.indices.len() as i32,
+                    glow::UNSIGNED_INT,
+                    (last_index * std::mem::size_of::<u32>()) as i32,
+                    last_vertex as i32,
+                );
+
+                last_vertex += buffers.vertices.len();
+                last_index += buffers.indices.len();
+            }
+        }
+
+        unsafe {
+            gl.bind_vertex_array(None);
+            gl.use_program(None);
+            gl.disable(glow::SCISSOR_TEST);
+        }
     }
 }
 
@@ -35,18 +219,15 @@ impl Pipeline {
 #[derive(Debug, Clone, Copy)]
 struct Uniforms {
     transform: [f32; 16],
-    // We need to align this to 256 bytes to please `wgpu`...
-    // TODO: Be smarter and stop wasting memory!
-    _padding_a: [f32; 32],
-    _padding_b: [f32; 16],
 }
 
+unsafe impl bytemuck::Zeroable for Uniforms {}
+unsafe impl bytemuck::Pod for Uniforms {}
+
 impl Default for Uniforms {
     fn default() -> Self {
         Self {
             transform: *Transformation::identity().as_ref(),
-            _padding_a: [0.0; 32],
-            _padding_b: [0.0; 16],
         }
     }
 }
@@ -55,8 +236,52 @@ impl From<Transformation> for Uniforms {
     fn from(transformation: Transformation) -> Uniforms {
         Self {
             transform: transformation.into(),
-            _padding_a: [0.0; 32],
-            _padding_b: [0.0; 16],
+        }
+    }
+}
+
+#[derive(Debug)]
+struct Buffer<T> {
+    raw: <glow::Context as HasContext>::Buffer,
+    target: u32,
+    usage: u32,
+    size: usize,
+    phantom: PhantomData<T>,
+}
+
+impl<T> Buffer<T> {
+    pub unsafe fn new(
+        gl: &glow::Context,
+        target: u32,
+        usage: u32,
+        size: usize,
+    ) -> Self {
+        let raw = gl.create_buffer().expect("Create buffer");
+
+        let mut buffer = Buffer {
+            raw,
+            target,
+            usage,
+            size: 0,
+            phantom: PhantomData,
+        };
+
+        buffer.bind(gl, size);
+
+        buffer
+    }
+
+    pub unsafe fn bind(&mut self, gl: &glow::Context, size: usize) {
+        gl.bind_buffer(self.target, Some(self.raw));
+
+        if self.size < size {
+            gl.buffer_data_size(
+                self.target,
+                (size * std::mem::size_of::<T>()) as i32,
+                self.usage,
+            );
+
+            self.size = size;
         }
     }
 }
-- 
cgit