//! Draw meshes of triangles. use crate::program; use crate::Transformation; use iced_graphics::gradient::Gradient; use iced_graphics::layer::mesh::{self, Mesh}; use iced_graphics::triangle::{ColoredVertex2D, Vertex2D}; use glow::HasContext; use std::marker::PhantomData; #[cfg(feature = "tracing")] use tracing::info_span; const DEFAULT_VERTICES: usize = 1_000; const DEFAULT_INDICES: usize = 1_000; #[derive(Debug)] pub(crate) struct Pipeline { indices: Buffer, solid: solid::Program, gradient: gradient::Program, } impl Pipeline { pub fn new(gl: &glow::Context, shader_version: &program::Version) -> Self { let mut indices = unsafe { Buffer::new( gl, glow::ELEMENT_ARRAY_BUFFER, glow::DYNAMIC_DRAW, DEFAULT_INDICES, ) }; let solid = solid::Program::new(gl, shader_version); let gradient = gradient::Program::new(gl, shader_version); unsafe { gl.bind_vertex_array(Some(solid.vertex_array)); indices.bind(gl, 0); gl.bind_vertex_array(Some(gradient.vertex_array)); indices.bind(gl, 0); gl.bind_vertex_array(None); } Self { indices, solid, gradient, } } pub fn draw( &mut self, meshes: &[Mesh<'_>], gl: &glow::Context, target_height: u32, transformation: Transformation, scale_factor: f32, ) { #[cfg(feature = "tracing")] let _ = info_span!("Glow::Triangle", "DRAW").enter(); unsafe { gl.enable(glow::MULTISAMPLE); gl.enable(glow::SCISSOR_TEST); } // Count the total amount of vertices & indices we need to handle let count = mesh::attribute_count_of(meshes); // Then we ensure the current attribute buffers are big enough, resizing if necessary unsafe { self.indices.bind(gl, count.indices); } // We upload all the vertices and indices upfront let mut solid_vertex_offset = 0; let mut gradient_vertex_offset = 0; let mut index_offset = 0; for mesh in meshes { let indices = mesh.indices(); unsafe { gl.buffer_sub_data_u8_slice( glow::ELEMENT_ARRAY_BUFFER, (index_offset * std::mem::size_of::()) as i32, bytemuck::cast_slice(indices), ); index_offset += indices.len(); } match mesh { Mesh::Solid { buffers, .. } => { unsafe { self.solid.vertices.bind(gl, count.solid_vertices); gl.buffer_sub_data_u8_slice( glow::ARRAY_BUFFER, (solid_vertex_offset * std::mem::size_of::()) as i32, bytemuck::cast_slice(&buffers.vertices), ); } solid_vertex_offset += buffers.vertices.len(); } Mesh::Gradient { buffers, .. } => { unsafe { self.gradient .vertices .bind(gl, count.gradient_vertices); gl.buffer_sub_data_u8_slice( glow::ARRAY_BUFFER, (gradient_vertex_offset * std::mem::size_of::()) as i32, bytemuck::cast_slice(&buffers.vertices), ); } gradient_vertex_offset += buffers.vertices.len(); } } } // Then we draw each mesh using offsets let mut last_solid_vertex = 0; let mut last_gradient_vertex = 0; let mut last_index = 0; for mesh in meshes { let indices = mesh.indices(); let origin = mesh.origin(); let transform = transformation * Transformation::translate(origin.x, origin.y); let clip_bounds = (mesh.clip_bounds() * scale_factor).snap(); unsafe { 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, ); } match mesh { Mesh::Solid { buffers, .. } => unsafe { gl.use_program(Some(self.solid.program)); gl.bind_vertex_array(Some(self.solid.vertex_array)); if transform != self.solid.uniforms.transform { gl.uniform_matrix_4_f32_slice( Some(&self.solid.uniforms.transform_location), false, transform.as_ref(), ); self.solid.uniforms.transform = transform; } gl.draw_elements_base_vertex( glow::TRIANGLES, indices.len() as i32, glow::UNSIGNED_INT, (last_index * std::mem::size_of::()) as i32, last_solid_vertex as i32, ); last_solid_vertex += buffers.vertices.len(); }, Mesh::Gradient { buffers, gradient, .. } => unsafe { gl.use_program(Some(self.gradient.program)); gl.bind_vertex_array(Some(self.gradient.vertex_array)); if transform != self.gradient.uniforms.transform { gl.uniform_matrix_4_f32_slice( Some(&self.gradient.uniforms.locations.transform), false, transform.as_ref(), ); self.gradient.uniforms.transform = transform; } if &self.gradient.uniforms.gradient != *gradient { match gradient { Gradient::Linear(linear) => { gl.uniform_4_f32( Some( &self .gradient .uniforms .locations .gradient_direction, ), linear.start.x, linear.start.y, linear.end.x, linear.end.y, ); gl.uniform_1_i32( Some( &self .gradient .uniforms .locations .color_stops_size, ), (linear.color_stops.len() * 2) as i32, ); let mut stops = [0.0; 128]; for (index, stop) in linear .color_stops .iter() .enumerate() .take(16) { let [r, g, b, a] = stop.color.into_linear(); stops[index * 8] = r; stops[(index * 8) + 1] = g; stops[(index * 8) + 2] = b; stops[(index * 8) + 3] = a; stops[(index * 8) + 4] = stop.offset; stops[(index * 8) + 5] = 0.; stops[(index * 8) + 6] = 0.; stops[(index * 8) + 7] = 0.; } gl.uniform_4_f32_slice( Some( &self .gradient .uniforms .locations .color_stops, ), &stops, ); } } self.gradient.uniforms.gradient = (*gradient).clone(); } gl.draw_elements_base_vertex( glow::TRIANGLES, indices.len() as i32, glow::UNSIGNED_INT, (last_index * std::mem::size_of::()) as i32, last_gradient_vertex as i32, ); last_gradient_vertex += buffers.vertices.len(); }, } last_index += indices.len(); } unsafe { gl.bind_vertex_array(None); gl.disable(glow::SCISSOR_TEST); gl.disable(glow::MULTISAMPLE); } } } #[derive(Debug)] pub struct Buffer { raw: ::Buffer, target: u32, usage: u32, size: usize, phantom: PhantomData, } impl Buffer { 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::()) as i32, self.usage, ); self.size = size; } } } mod solid { use crate::program; use crate::triangle; use glow::{Context, HasContext, NativeProgram}; use iced_graphics::triangle::ColoredVertex2D; use iced_graphics::Transformation; #[derive(Debug)] pub struct Program { pub program: ::Program, pub vertex_array: ::VertexArray, pub vertices: triangle::Buffer, pub uniforms: Uniforms, } impl Program { pub fn new(gl: &Context, shader_version: &program::Version) -> Self { let program = unsafe { let vertex_shader = program::Shader::vertex( gl, shader_version, include_str!("shader/common/solid.vert"), ); let fragment_shader = program::Shader::fragment( gl, shader_version, include_str!("shader/common/solid.frag"), ); program::create( gl, &[vertex_shader, fragment_shader], &[(0, "i_Position"), (1, "i_Color")], ) }; let vertex_array = unsafe { gl.create_vertex_array().expect("Create vertex array") }; let vertices = unsafe { triangle::Buffer::new( gl, glow::ARRAY_BUFFER, glow::DYNAMIC_DRAW, super::DEFAULT_VERTICES, ) }; unsafe { gl.bind_vertex_array(Some(vertex_array)); 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.enable_vertex_attrib_array(1); gl.vertex_attrib_pointer_f32( 1, 4, glow::FLOAT, false, stride, 4 * 2, ); gl.bind_vertex_array(None); }; Self { program, vertex_array, vertices, uniforms: Uniforms::new(gl, program), } } } #[derive(Debug)] pub struct Uniforms { pub transform: Transformation, pub transform_location: ::UniformLocation, } impl Uniforms { fn new(gl: &Context, program: NativeProgram) -> Self { let transform = Transformation::identity(); let transform_location = unsafe { gl.get_uniform_location(program, "u_Transform") } .expect("Solid - Get u_Transform."); unsafe { gl.use_program(Some(program)); gl.uniform_matrix_4_f32_slice( Some(&transform_location), false, transform.as_ref(), ); gl.use_program(None); } Self { transform, transform_location, } } } } mod gradient { use crate::program; use crate::triangle; use glow::{Context, HasContext, NativeProgram}; use iced_graphics::gradient::{self, Gradient}; use iced_graphics::triangle::Vertex2D; use iced_graphics::Transformation; #[derive(Debug)] pub struct Program { pub program: ::Program, pub vertex_array: ::VertexArray, pub vertices: triangle::Buffer, pub uniforms: Uniforms, } impl Program { pub fn new(gl: &Context, shader_version: &program::Version) -> Self { let program = unsafe { let vertex_shader = program::Shader::vertex( gl, shader_version, include_str!("shader/common/gradient.vert"), ); let fragment_shader = program::Shader::fragment( gl, shader_version, include_str!("shader/common/gradient.frag"), ); program::create( gl, &[vertex_shader, fragment_shader], &[(0, "i_Position")], ) }; let vertex_array = unsafe { gl.create_vertex_array().expect("Create vertex array") }; let vertices = unsafe { triangle::Buffer::new( gl, glow::ARRAY_BUFFER, glow::DYNAMIC_DRAW, super::DEFAULT_VERTICES, ) }; unsafe { gl.bind_vertex_array(Some(vertex_array)); 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.bind_vertex_array(None); }; Self { program, vertex_array, vertices, uniforms: Uniforms::new(gl, program), } } } #[derive(Debug)] pub struct Uniforms { pub gradient: Gradient, pub transform: Transformation, pub locations: Locations, } #[derive(Debug)] pub struct Locations { pub gradient_direction: ::UniformLocation, pub color_stops_size: ::UniformLocation, //currently the maximum number of stops is 16 due to lack of SSBO in GL2.1 pub color_stops: ::UniformLocation, pub transform: ::UniformLocation, } impl Uniforms { fn new(gl: &Context, program: NativeProgram) -> Self { let gradient_direction = unsafe { gl.get_uniform_location(program, "gradient_direction") } .expect("Gradient - Get gradient_direction."); let color_stops_size = unsafe { gl.get_uniform_location(program, "color_stops_size") } .expect("Gradient - Get color_stops_size."); let color_stops = unsafe { gl.get_uniform_location(program, "color_stops") .expect("Gradient - Get color_stops.") }; let transform = Transformation::identity(); let transform_location = unsafe { gl.get_uniform_location(program, "u_Transform") } .expect("Solid - Get u_Transform."); unsafe { gl.use_program(Some(program)); gl.uniform_matrix_4_f32_slice( Some(&transform_location), false, transform.as_ref(), ); gl.use_program(None); } Self { gradient: Gradient::Linear(gradient::Linear { start: Default::default(), end: Default::default(), color_stops: vec![], }), transform: Transformation::identity(), locations: Locations { gradient_direction, color_stops_size, color_stops, transform: transform_location, }, } } } }