use crate::program::{self, Shader}; use crate::Transformation; use glow::HasContext; use iced_graphics::layer; use iced_native::Rectangle; const MAX_INSTANCES: usize = 100_000; #[derive(Debug)] pub struct Pipeline { program: ::Program, vertex_array: ::VertexArray, instances: ::Buffer, transform_location: ::UniformLocation, scale_location: ::UniformLocation, screen_height_location: ::UniformLocation, current_transform: Transformation, current_scale: f32, current_target_height: u32, } impl Pipeline { pub fn new( gl: &glow::Context, shader_version: &program::Version, ) -> Pipeline { let program = unsafe { let vertex_shader = Shader::vertex( gl, shader_version, include_str!("../shader/core/quad.vert"), ); let fragment_shader = Shader::fragment( gl, shader_version, include_str!("../shader/core/quad.frag"), ); program::create( gl, &[vertex_shader, fragment_shader], &[ (0, "i_Pos"), (1, "i_Scale"), (2, "i_Color"), (3, "i_BorderColor"), (4, "i_BorderRadius"), (5, "i_BorderWidth"), ], ) }; 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)); gl.uniform_matrix_4_f32_slice( Some(&transform_location), false, Transformation::identity().as_ref(), ); 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, instances) = unsafe { create_instance_buffer(gl, MAX_INSTANCES) }; Pipeline { program, vertex_array, instances, 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, ) { 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.instances)); } if transformation != self.current_transform { unsafe { gl.uniform_matrix_4_f32_slice( Some(&self.transform_location), false, transformation.as_ref(), ); 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; } for instances in instances.chunks(MAX_INSTANCES) { unsafe { gl.buffer_sub_data_u8_slice( glow::ARRAY_BUFFER, 0, bytemuck::cast_slice(instances), ); gl.draw_arrays_instanced( glow::TRIANGLE_STRIP, 0, 4, instances.len() as i32, ); } } unsafe { gl.bind_vertex_array(None); gl.use_program(None); gl.disable(glow::SCISSOR_TEST); } } } unsafe fn create_instance_buffer( gl: &glow::Context, size: usize, ) -> ( ::VertexArray, ::Buffer, ) { let vertex_array = gl.create_vertex_array().expect("Create vertex array"); let buffer = gl.create_buffer().expect("Create instance buffer"); gl.bind_vertex_array(Some(vertex_array)); gl.bind_buffer(glow::ARRAY_BUFFER, Some(buffer)); gl.buffer_data_size( glow::ARRAY_BUFFER, (size * std::mem::size_of::()) as i32, glow::DYNAMIC_DRAW, ); let stride = std::mem::size_of::() as i32; gl.enable_vertex_attrib_array(0); gl.vertex_attrib_pointer_f32(0, 2, glow::FLOAT, false, stride, 0); gl.vertex_attrib_divisor(0, 1); gl.enable_vertex_attrib_array(1); gl.vertex_attrib_pointer_f32(1, 2, glow::FLOAT, false, stride, 4 * 2); gl.vertex_attrib_divisor(1, 1); gl.enable_vertex_attrib_array(2); gl.vertex_attrib_pointer_f32(2, 4, glow::FLOAT, false, stride, 4 * (2 + 2)); gl.vertex_attrib_divisor(2, 1); gl.enable_vertex_attrib_array(3); gl.vertex_attrib_pointer_f32( 3, 4, glow::FLOAT, false, stride, 4 * (2 + 2 + 4), ); gl.vertex_attrib_divisor(3, 1); gl.enable_vertex_attrib_array(4); gl.vertex_attrib_pointer_f32( 4, 1, glow::FLOAT, false, stride, 4 * (2 + 2 + 4 + 4), ); gl.vertex_attrib_divisor(4, 1); gl.enable_vertex_attrib_array(5); gl.vertex_attrib_pointer_f32( 5, 1, glow::FLOAT, false, stride, 4 * (2 + 2 + 4 + 4 + 1), ); gl.vertex_attrib_divisor(5, 1); gl.bind_vertex_array(None); gl.bind_buffer(glow::ARRAY_BUFFER, None); (vertex_array, buffer) }