summaryrefslogtreecommitdiffstats
path: root/glow/src/quad/compatibility.rs
diff options
context:
space:
mode:
Diffstat (limited to 'glow/src/quad/compatibility.rs')
-rw-r--r--glow/src/quad/compatibility.rs347
1 files changed, 347 insertions, 0 deletions
diff --git a/glow/src/quad/compatibility.rs b/glow/src/quad/compatibility.rs
new file mode 100644
index 00000000..e083dcf1
--- /dev/null
+++ b/glow/src/quad/compatibility.rs
@@ -0,0 +1,347 @@
+use crate::program;
+use crate::Transformation;
+use glow::HasContext;
+use iced_graphics::layer;
+use iced_native::Rectangle;
+
+// Only change `MAX_QUADS`, otherwise you could cause problems
+// by splitting a triangle into different render passes.
+const MAX_QUADS: usize = 100_000;
+const MAX_VERTICES: usize = MAX_QUADS * 4;
+const MAX_INDICES: usize = MAX_QUADS * 6;
+
+#[derive(Debug)]
+pub struct Pipeline {
+ program: <glow::Context as HasContext>::Program,
+ vertex_array: <glow::Context as HasContext>::VertexArray,
+ vertex_buffer: <glow::Context as HasContext>::Buffer,
+ index_buffer: <glow::Context as HasContext>::Buffer,
+ transform_location: <glow::Context as HasContext>::UniformLocation,
+ scale_location: <glow::Context as HasContext>::UniformLocation,
+ screen_height_location: <glow::Context as HasContext>::UniformLocation,
+ current_transform: Transformation,
+ current_scale: f32,
+ current_target_height: u32,
+}
+
+impl Pipeline {
+ pub fn new(gl: &glow::Context) -> Pipeline {
+ let program = unsafe {
+ program::create(
+ gl,
+ &[
+ (
+ glow::VERTEX_SHADER,
+ include_str!("../shader/compatibility/quad.vert"),
+ ),
+ (
+ glow::FRAGMENT_SHADER,
+ include_str!("../shader/compatibility/quad.frag"),
+ ),
+ ],
+ )
+ };
+
+ let transform_location =
+ unsafe { gl.get_uniform_location(program, "u_Transform") }
+ .expect("Get transform location");
+
+ let scale_location =
+ unsafe { gl.get_uniform_location(program, "u_Scale") }
+ .expect("Get scale location");
+
+ let screen_height_location =
+ unsafe { gl.get_uniform_location(program, "u_ScreenHeight") }
+ .expect("Get target height location");
+
+ unsafe {
+ gl.use_program(Some(program));
+
+ let matrix: [f32; 16] = Transformation::identity().into();
+ gl.uniform_matrix_4_f32_slice(
+ Some(&transform_location),
+ false,
+ &matrix,
+ );
+
+ gl.uniform_1_f32(Some(&scale_location), 1.0);
+ gl.uniform_1_f32(Some(&screen_height_location), 0.0);
+
+ gl.use_program(None);
+ }
+
+ let (vertex_array, vertex_buffer, index_buffer) =
+ unsafe { create_buffers(gl, MAX_VERTICES) };
+
+ Pipeline {
+ program,
+ vertex_array,
+ vertex_buffer,
+ index_buffer,
+ transform_location,
+ scale_location,
+ screen_height_location,
+ current_transform: Transformation::identity(),
+ current_scale: 1.0,
+ current_target_height: 0,
+ }
+ }
+
+ pub fn draw(
+ &mut self,
+ gl: &glow::Context,
+ target_height: u32,
+ instances: &[layer::Quad],
+ transformation: Transformation,
+ scale: f32,
+ bounds: Rectangle<u32>,
+ ) {
+ // TODO: Remove this allocation (probably by changing the shader and removing the need of two `position`)
+ let vertices: Vec<Vertex> = instances
+ .iter()
+ .flat_map(|quad| Vertex::from_quad(quad))
+ .collect();
+
+ // TODO: Remove this allocation (or allocate only when needed)
+ let indices: Vec<i32> = (0..instances.len().min(MAX_QUADS) as i32)
+ .flat_map(|i| {
+ [
+ 0 + i * 4,
+ 1 + i * 4,
+ 2 + i * 4,
+ 2 + i * 4,
+ 1 + i * 4,
+ 3 + i * 4,
+ ]
+ })
+ .cycle()
+ .take(instances.len() * 6)
+ .collect();
+
+ unsafe {
+ gl.enable(glow::SCISSOR_TEST);
+ gl.scissor(
+ bounds.x as i32,
+ (target_height - (bounds.y + bounds.height)) as i32,
+ bounds.width as i32,
+ bounds.height as i32,
+ );
+
+ gl.use_program(Some(self.program));
+ gl.bind_vertex_array(Some(self.vertex_array));
+ gl.bind_buffer(glow::ARRAY_BUFFER, Some(self.vertex_buffer));
+ gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, Some(self.index_buffer));
+ }
+
+ if transformation != self.current_transform {
+ unsafe {
+ let matrix: [f32; 16] = transformation.into();
+ gl.uniform_matrix_4_f32_slice(
+ Some(&self.transform_location),
+ false,
+ &matrix,
+ );
+
+ self.current_transform = transformation;
+ }
+ }
+
+ if scale != self.current_scale {
+ unsafe {
+ gl.uniform_1_f32(Some(&self.scale_location), scale);
+ }
+
+ self.current_scale = scale;
+ }
+
+ if target_height != self.current_target_height {
+ unsafe {
+ gl.uniform_1_f32(
+ Some(&self.screen_height_location),
+ target_height as f32,
+ );
+ }
+
+ self.current_target_height = target_height;
+ }
+
+ let passes = vertices
+ .chunks(MAX_VERTICES)
+ .zip(indices.chunks(MAX_INDICES));
+
+ for (vertices, indices) in passes {
+ unsafe {
+ gl.buffer_sub_data_u8_slice(
+ glow::ARRAY_BUFFER,
+ 0,
+ bytemuck::cast_slice(&vertices),
+ );
+
+ gl.buffer_sub_data_u8_slice(
+ glow::ELEMENT_ARRAY_BUFFER,
+ 0,
+ bytemuck::cast_slice(&indices),
+ );
+
+ gl.draw_elements(
+ glow::TRIANGLES,
+ indices.len() as i32,
+ glow::UNSIGNED_INT,
+ 0,
+ );
+ }
+ }
+
+ unsafe {
+ gl.bind_vertex_array(None);
+ gl.use_program(None);
+ gl.disable(glow::SCISSOR_TEST);
+ }
+ }
+}
+
+unsafe fn create_buffers(
+ gl: &glow::Context,
+ size: usize,
+) -> (
+ <glow::Context as HasContext>::VertexArray,
+ <glow::Context as HasContext>::Buffer,
+ <glow::Context as HasContext>::Buffer,
+) {
+ let vertex_array = gl.create_vertex_array().expect("Create vertex array");
+ let vertex_buffer = gl.create_buffer().expect("Create vertex buffer");
+ let index_buffer = gl.create_buffer().expect("Create index buffer");
+
+ gl.bind_vertex_array(Some(vertex_array));
+
+ gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, Some(index_buffer));
+ gl.buffer_data_size(
+ glow::ELEMENT_ARRAY_BUFFER,
+ 12 * size as i32,
+ glow::DYNAMIC_DRAW,
+ );
+
+ gl.bind_buffer(glow::ARRAY_BUFFER, Some(vertex_buffer));
+ gl.buffer_data_size(
+ glow::ARRAY_BUFFER,
+ (size * Vertex::SIZE) as i32,
+ glow::DYNAMIC_DRAW,
+ );
+
+ let stride = Vertex::SIZE 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, 2, glow::FLOAT, false, stride, 4 * 2);
+
+ gl.enable_vertex_attrib_array(2);
+ gl.vertex_attrib_pointer_f32(2, 4, glow::FLOAT, false, stride, 4 * (2 + 2));
+
+ gl.enable_vertex_attrib_array(3);
+ gl.vertex_attrib_pointer_f32(
+ 3,
+ 4,
+ glow::FLOAT,
+ false,
+ stride,
+ 4 * (2 + 2 + 4),
+ );
+
+ gl.enable_vertex_attrib_array(4);
+ gl.vertex_attrib_pointer_f32(
+ 4,
+ 1,
+ glow::FLOAT,
+ false,
+ stride,
+ 4 * (2 + 2 + 4 + 4),
+ );
+
+ gl.enable_vertex_attrib_array(5);
+ gl.vertex_attrib_pointer_f32(
+ 5,
+ 1,
+ glow::FLOAT,
+ false,
+ stride,
+ 4 * (2 + 2 + 4 + 4 + 1),
+ );
+
+ gl.enable_vertex_attrib_array(6);
+ gl.vertex_attrib_pointer_f32(
+ 6,
+ 2,
+ glow::FLOAT,
+ false,
+ stride,
+ 4 * (2 + 2 + 4 + 4 + 1 + 1),
+ );
+
+ gl.bind_vertex_array(None);
+ gl.bind_buffer(glow::ARRAY_BUFFER, None);
+ gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, None);
+
+ (vertex_array, vertex_buffer, index_buffer)
+}
+
+/// The vertex of a colored rectangle with a border.
+///
+/// This type can be directly uploaded to GPU memory.
+#[derive(Debug, Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
+#[repr(C)]
+pub struct Vertex {
+ /// The position of the [`Vertex`].
+ pub position: [f32; 2],
+
+ /// The size of the [`Vertex`].
+ pub size: [f32; 2],
+
+ /// The color of the [`Vertex`], in __linear RGB__.
+ pub color: [f32; 4],
+
+ /// The border color of the [`Vertex`], in __linear RGB__.
+ pub border_color: [f32; 4],
+
+ /// The border radius of the [`Vertex`].
+ pub border_radius: f32,
+
+ /// The border width of the [`Vertex`].
+ pub border_width: f32,
+
+ /// The __quad__ position of the [`Vertex`].
+ pub q_position: [f32; 2],
+}
+
+impl Vertex {
+ const SIZE: usize = std::mem::size_of::<Self>();
+
+ fn from_quad(quad: &layer::Quad) -> [Vertex; 4] {
+ let base = Vertex {
+ position: quad.position,
+ size: quad.size,
+ color: quad.color,
+ border_color: quad.color,
+ border_radius: quad.border_radius,
+ border_width: quad.border_width,
+ q_position: [0.0, 0.0],
+ };
+
+ [
+ base,
+ Self {
+ q_position: [0.0, 1.0],
+ ..base
+ },
+ Self {
+ q_position: [1.0, 0.0],
+ ..base
+ },
+ Self {
+ q_position: [1.0, 1.0],
+ ..base
+ },
+ ]
+ }
+}