mod storage; use storage::Storage; pub use iced_graphics::triangle::{Mesh2D, Vertex2D}; use crate::program::{self, Shader}; use crate::Transformation; #[cfg(feature = "image")] use iced_graphics::image::raster; #[cfg(feature = "svg")] use iced_graphics::image::vector; use iced_graphics::layer; use iced_graphics::Rectangle; use iced_graphics::Size; use glow::HasContext; use std::cell::RefCell; #[derive(Debug)] pub(crate) struct Pipeline { program: ::Program, vertex_array: ::VertexArray, vertex_buffer: ::Buffer, transform_location: ::UniformLocation, storage: Storage, #[cfg(feature = "image")] raster_cache: RefCell>, #[cfg(feature = "svg")] vector_cache: RefCell>, } 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/common/image.vert"), ); let fragment_shader = Shader::fragment( gl, shader_version, include_str!("shader/common/image.frag"), ); program::create( gl, &[vertex_shader, fragment_shader], &[(0, "i_Position")], ) }; let transform_location = unsafe { gl.get_uniform_location(program, "u_Transform") } .expect("Get transform location"); unsafe { gl.use_program(Some(program)); let transform: [f32; 16] = Transformation::identity().into(); gl.uniform_matrix_4_f32_slice( Some(&transform_location), false, &transform, ); gl.use_program(None); } let vertex_buffer = unsafe { gl.create_buffer().expect("Create vertex buffer") }; let vertex_array = unsafe { gl.create_vertex_array().expect("Create vertex array") }; unsafe { gl.bind_vertex_array(Some(vertex_array)); gl.bind_buffer(glow::ARRAY_BUFFER, Some(vertex_buffer)); let vertices = &[0u8, 0, 1, 0, 0, 1, 1, 1]; gl.buffer_data_size( glow::ARRAY_BUFFER, vertices.len() as i32, glow::STATIC_DRAW, ); gl.buffer_sub_data_u8_slice( glow::ARRAY_BUFFER, 0, bytemuck::cast_slice(vertices), ); gl.enable_vertex_attrib_array(0); gl.vertex_attrib_pointer_f32( 0, 2, glow::UNSIGNED_BYTE, false, 0, 0, ); gl.bind_buffer(glow::ARRAY_BUFFER, None); gl.bind_vertex_array(None); } Pipeline { program, vertex_array, vertex_buffer, transform_location, storage: Storage::default(), #[cfg(feature = "image")] raster_cache: RefCell::new(raster::Cache::default()), #[cfg(feature = "svg")] vector_cache: RefCell::new(vector::Cache::default()), } } #[cfg(feature = "image")] pub fn dimensions(&self, handle: &iced_native::image::Handle) -> Size { self.raster_cache.borrow_mut().load(handle).dimensions() } #[cfg(feature = "svg")] pub fn viewport_dimensions( &self, handle: &iced_native::svg::Handle, ) -> Size { let mut cache = self.vector_cache.borrow_mut(); let svg = cache.load(handle); svg.viewport_dimensions() } pub fn draw( &mut self, mut gl: &glow::Context, target_height: u32, transformation: Transformation, _scale_factor: f32, images: &[layer::Image], layer_bounds: Rectangle, ) { unsafe { 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.enable(glow::SCISSOR_TEST); } #[cfg(feature = "image")] let mut raster_cache = self.raster_cache.borrow_mut(); #[cfg(feature = "svg")] let mut vector_cache = self.vector_cache.borrow_mut(); for image in images { let (entry, bounds) = match &image { #[cfg(feature = "image")] layer::Image::Raster { handle, bounds } => ( raster_cache.upload(handle, &mut gl, &mut self.storage), bounds, ), #[cfg(not(feature = "image"))] layer::Image::Raster { handle: _, bounds } => (None, bounds), #[cfg(feature = "svg")] layer::Image::Vector { handle, bounds } => { let size = [bounds.width, bounds.height]; ( vector_cache.upload( handle, size, _scale_factor, &mut gl, &mut self.storage, ), bounds, ) } #[cfg(not(feature = "svg"))] layer::Image::Vector { handle: _, bounds } => (None, bounds), }; unsafe { gl.scissor( layer_bounds.x as i32, (target_height - (layer_bounds.y + layer_bounds.height)) as i32, layer_bounds.width as i32, layer_bounds.height as i32, ); if let Some(storage::Entry { texture, .. }) = entry { gl.bind_texture(glow::TEXTURE_2D, Some(*texture)) } else { continue; } let translate = Transformation::translate(bounds.x, bounds.y); let scale = Transformation::scale(bounds.width, bounds.height); let transformation = transformation * translate * scale; let matrix: [f32; 16] = transformation.into(); gl.uniform_matrix_4_f32_slice( Some(&self.transform_location), false, &matrix, ); gl.draw_arrays(glow::TRIANGLE_STRIP, 0, 4); gl.bind_texture(glow::TEXTURE_2D, None); } } unsafe { gl.bind_buffer(glow::ARRAY_BUFFER, None); gl.bind_vertex_array(None); gl.use_program(None); gl.disable(glow::SCISSOR_TEST); } } pub fn trim_cache(&mut self, mut gl: &glow::Context) { #[cfg(feature = "image")] self.raster_cache .borrow_mut() .trim(&mut self.storage, &mut gl); #[cfg(feature = "svg")] self.vector_cache .borrow_mut() .trim(&mut self.storage, &mut gl); } }