diff options
Diffstat (limited to 'glow')
-rw-r--r-- | glow/Cargo.toml | 16 | ||||
-rw-r--r-- | glow/src/backend.rs | 35 | ||||
-rw-r--r-- | glow/src/image.rs | 230 | ||||
-rw-r--r-- | glow/src/image/storage.rs | 78 | ||||
-rw-r--r-- | glow/src/lib.rs | 2 | ||||
-rw-r--r-- | glow/src/quad/compatibility.rs | 6 | ||||
-rw-r--r-- | glow/src/quad/core.rs | 6 | ||||
-rw-r--r-- | glow/src/shader/common/gradient.frag | 59 | ||||
-rw-r--r-- | glow/src/shader/common/image.frag | 22 | ||||
-rw-r--r-- | glow/src/shader/common/image.vert | 9 | ||||
-rw-r--r-- | glow/src/shader/common/triangle.frag | 6 | ||||
-rw-r--r-- | glow/src/shader/common/triangle.vert | 8 | ||||
-rw-r--r-- | glow/src/triangle.rs | 226 | ||||
-rw-r--r-- | glow/src/triangle/gradient.rs | 162 | ||||
-rw-r--r-- | glow/src/triangle/solid.rs | 91 | ||||
-rw-r--r-- | glow/src/window/compositor.rs | 7 |
16 files changed, 798 insertions, 165 deletions
diff --git a/glow/Cargo.toml b/glow/Cargo.toml index 18215e9b..a50fd375 100644 --- a/glow/Cargo.toml +++ b/glow/Cargo.toml @@ -8,12 +8,22 @@ license = "MIT AND OFL-1.1" repository = "https://github.com/iced-rs/iced" [features] +svg = ["iced_graphics/svg"] +image = ["iced_graphics/image"] +png = ["iced_graphics/png"] +jpeg = ["iced_graphics/jpeg"] +jpeg_rayon = ["iced_graphics/jpeg_rayon"] +gif = ["iced_graphics/gif"] +webp = ["iced_graphics/webp"] +pnm = ["iced_graphics/pnm"] +ico = ["iced_graphics/ico"] +bmp = ["iced_graphics/bmp"] +hdr = ["iced_graphics/hdr"] +dds = ["iced_graphics/dds"] +farbfeld = ["iced_graphics/farbfeld"] canvas = ["iced_graphics/canvas"] qr_code = ["iced_graphics/qr_code"] default_system_font = ["iced_graphics/font-source"] -# Not supported yet! -image = [] -svg = [] [dependencies] glow = "0.11.1" diff --git a/glow/src/backend.rs b/glow/src/backend.rs index 78d4229e..1a41d540 100644 --- a/glow/src/backend.rs +++ b/glow/src/backend.rs @@ -1,7 +1,8 @@ -use crate::program; +#[cfg(any(feature = "image", feature = "svg"))] +use crate::image; use crate::quad; use crate::text; -use crate::triangle; +use crate::{program, triangle}; use crate::{Settings, Transformation, Viewport}; use iced_graphics::backend; @@ -16,6 +17,8 @@ use iced_native::{Font, Size}; /// [`iced`]: https://github.com/iced-rs/iced #[derive(Debug)] pub struct Backend { + #[cfg(any(feature = "image", feature = "svg"))] + image_pipeline: image::Pipeline, quad_pipeline: quad::Pipeline, text_pipeline: text::Pipeline, triangle_pipeline: triangle::Pipeline, @@ -33,10 +36,14 @@ impl Backend { let shader_version = program::Version::new(gl); + #[cfg(any(feature = "image", feature = "svg"))] + let image_pipeline = image::Pipeline::new(gl, &shader_version); let quad_pipeline = quad::Pipeline::new(gl, &shader_version); let triangle_pipeline = triangle::Pipeline::new(gl, &shader_version); Self { + #[cfg(any(feature = "image", feature = "svg"))] + image_pipeline, quad_pipeline, text_pipeline, triangle_pipeline, @@ -71,6 +78,9 @@ impl Backend { viewport_size.height, ); } + + #[cfg(any(feature = "image", feature = "svg"))] + self.image_pipeline.trim_cache(gl); } fn flush( @@ -105,14 +115,23 @@ impl Backend { * Transformation::scale(scale_factor, scale_factor); self.triangle_pipeline.draw( + &layer.meshes, gl, target_height, scaled, scale_factor, - &layer.meshes, ); } + #[cfg(any(feature = "image", feature = "svg"))] + if !layer.images.is_empty() { + let scaled = transformation + * Transformation::scale(scale_factor, scale_factor); + + self.image_pipeline + .draw(gl, scaled, scale_factor, &layer.images); + } + if !layer.text.is_empty() { for text in layer.text.iter() { // Target physical coordinates directly to avoid blurry text @@ -239,8 +258,8 @@ impl backend::Text for Backend { #[cfg(feature = "image")] impl backend::Image for Backend { - fn dimensions(&self, _handle: &iced_native::image::Handle) -> (u32, u32) { - (50, 50) + fn dimensions(&self, handle: &iced_native::image::Handle) -> Size<u32> { + self.image_pipeline.dimensions(handle) } } @@ -248,8 +267,8 @@ impl backend::Image for Backend { impl backend::Svg for Backend { fn viewport_dimensions( &self, - _handle: &iced_native::svg::Handle, - ) -> (u32, u32) { - (50, 50) + handle: &iced_native::svg::Handle, + ) -> Size<u32> { + self.image_pipeline.viewport_dimensions(handle) } } diff --git a/glow/src/image.rs b/glow/src/image.rs new file mode 100644 index 00000000..f906cd4c --- /dev/null +++ b/glow/src/image.rs @@ -0,0 +1,230 @@ +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::Size; + +use glow::HasContext; + +use std::cell::RefCell; + +#[derive(Debug)] +pub(crate) struct Pipeline { + program: <glow::Context as HasContext>::Program, + vertex_array: <glow::Context as HasContext>::VertexArray, + vertex_buffer: <glow::Context as HasContext>::Buffer, + transform_location: <glow::Context as HasContext>::UniformLocation, + storage: Storage, + #[cfg(feature = "image")] + raster_cache: RefCell<raster::Cache<Storage>>, + #[cfg(feature = "svg")] + vector_cache: RefCell<vector::Cache<Storage>>, +} + +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<u32> { + self.raster_cache.borrow_mut().load(handle).dimensions() + } + + #[cfg(feature = "svg")] + pub fn viewport_dimensions( + &self, + handle: &iced_native::svg::Handle, + ) -> Size<u32> { + 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, + transformation: Transformation, + _scale_factor: f32, + images: &[layer::Image], + ) { + 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)); + } + + #[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 { + 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); + } + } + + 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); + } +} diff --git a/glow/src/image/storage.rs b/glow/src/image/storage.rs new file mode 100644 index 00000000..9bc20641 --- /dev/null +++ b/glow/src/image/storage.rs @@ -0,0 +1,78 @@ +use iced_graphics::image; +use iced_graphics::Size; + +use glow::HasContext; + +#[derive(Debug, Default)] +pub struct Storage; + +impl image::Storage for Storage { + type Entry = Entry; + type State<'a> = &'a glow::Context; + + fn upload( + &mut self, + width: u32, + height: u32, + data: &[u8], + gl: &mut &glow::Context, + ) -> Option<Self::Entry> { + unsafe { + let texture = gl.create_texture().expect("create texture"); + gl.bind_texture(glow::TEXTURE_2D, Some(texture)); + gl.tex_image_2d( + glow::TEXTURE_2D, + 0, + glow::SRGB8_ALPHA8 as i32, + width as i32, + height as i32, + 0, + glow::RGBA, + glow::UNSIGNED_BYTE, + Some(data), + ); + gl.tex_parameter_i32( + glow::TEXTURE_2D, + glow::TEXTURE_WRAP_S, + glow::CLAMP_TO_EDGE as _, + ); + gl.tex_parameter_i32( + glow::TEXTURE_2D, + glow::TEXTURE_WRAP_T, + glow::CLAMP_TO_EDGE as _, + ); + gl.tex_parameter_i32( + glow::TEXTURE_2D, + glow::TEXTURE_MIN_FILTER, + glow::LINEAR as _, + ); + gl.tex_parameter_i32( + glow::TEXTURE_2D, + glow::TEXTURE_MAG_FILTER, + glow::LINEAR as _, + ); + gl.bind_texture(glow::TEXTURE_2D, None); + + Some(Entry { + size: Size::new(width, height), + texture, + }) + } + } + + fn remove(&mut self, entry: &Entry, gl: &mut &glow::Context) { + unsafe { gl.delete_texture(entry.texture) } + } +} + +#[derive(Debug)] +pub struct Entry { + size: Size<u32>, + pub(super) texture: glow::NativeTexture, +} + +impl image::storage::Entry for Entry { + fn size(&self) -> Size<u32> { + self.size + } +} diff --git a/glow/src/lib.rs b/glow/src/lib.rs index de9c0002..e3690a69 100644 --- a/glow/src/lib.rs +++ b/glow/src/lib.rs @@ -24,6 +24,8 @@ pub use glow; mod backend; +#[cfg(any(feature = "image", feature = "svg"))] +mod image; mod program; mod quad; mod text; diff --git a/glow/src/quad/compatibility.rs b/glow/src/quad/compatibility.rs index eb3fb7e0..28a5ea7c 100644 --- a/glow/src/quad/compatibility.rs +++ b/glow/src/quad/compatibility.rs @@ -70,11 +70,10 @@ impl Pipeline { unsafe { gl.use_program(Some(program)); - let matrix: [f32; 16] = Transformation::identity().into(); gl.uniform_matrix_4_f32_slice( Some(&transform_location), false, - &matrix, + Transformation::identity().as_ref(), ); gl.uniform_1_f32(Some(&scale_location), 1.0); @@ -139,11 +138,10 @@ impl Pipeline { if transformation != self.current_transform { unsafe { - let matrix: [f32; 16] = transformation.into(); gl.uniform_matrix_4_f32_slice( Some(&self.transform_location), false, - &matrix, + transformation.as_ref(), ); self.current_transform = transformation; diff --git a/glow/src/quad/core.rs b/glow/src/quad/core.rs index 3e51b1ca..16bec385 100644 --- a/glow/src/quad/core.rs +++ b/glow/src/quad/core.rs @@ -65,11 +65,10 @@ impl Pipeline { unsafe { gl.use_program(Some(program)); - let matrix: [f32; 16] = Transformation::identity().into(); gl.uniform_matrix_4_f32_slice( Some(&transform_location), false, - &matrix, + Transformation::identity().as_ref(), ); gl.uniform_1_f32(Some(&scale_location), 1.0); @@ -119,11 +118,10 @@ impl Pipeline { if transformation != self.current_transform { unsafe { - let matrix: [f32; 16] = transformation.into(); gl.uniform_matrix_4_f32_slice( Some(&self.transform_location), false, - &matrix, + transformation.as_ref(), ); self.current_transform = transformation; diff --git a/glow/src/shader/common/gradient.frag b/glow/src/shader/common/gradient.frag new file mode 100644 index 00000000..42d0201f --- /dev/null +++ b/glow/src/shader/common/gradient.frag @@ -0,0 +1,59 @@ +#ifdef GL_ES + #ifdef GL_FRAGMENT_PRECISION_HIGH + precision highp float; + #else + precision mediump float; + #endif +#endif + +#ifdef HIGHER_THAN_300 + layout (location = 0) out vec4 fragColor; + #define gl_FragColor fragColor +#endif + +in vec2 raw_position; + +uniform vec4 gradient_direction; +uniform uint color_stops_size; +// GLSL does not support dynamically sized arrays without SSBOs so this is capped to 16 stops +//stored as color(vec4) -> offset(vec4) sequentially; +uniform vec4 color_stops[32]; + +//TODO: rewrite without branching to make ALUs happy +void main() { + vec2 start = gradient_direction.xy; + vec2 end = gradient_direction.zw; + vec2 gradient_vec = vec2(end - start); + vec2 current_vec = vec2(raw_position.xy - start); + vec2 unit = normalize(gradient_vec); + float coord_offset = dot(unit, current_vec) / length(gradient_vec); + //if a gradient has a start/end stop that is identical, the mesh will have a transparent fill + fragColor = vec4(0.0, 0.0, 0.0, 0.0); + + float min_offset = color_stops[1].x; + float max_offset = color_stops[color_stops_size - 1u].x; + + for (uint i = 0u; i < color_stops_size - 2u; i += 2u) { + float curr_offset = color_stops[i+1u].x; + float next_offset = color_stops[i+3u].x; + + if (coord_offset <= min_offset) { + //current coordinate is before the first defined offset, set it to the start color + fragColor = color_stops[0]; + } + + if (curr_offset <= coord_offset && coord_offset <= next_offset) { + //current fragment is between the current offset processing & the next one, interpolate colors + fragColor = mix(color_stops[i], color_stops[i+2u], smoothstep( + curr_offset, + next_offset, + coord_offset + )); + } + + if (coord_offset >= max_offset) { + //current coordinate is before the last defined offset, set it to the last color + fragColor = color_stops[color_stops_size - 2u]; + } + } +} diff --git a/glow/src/shader/common/image.frag b/glow/src/shader/common/image.frag new file mode 100644 index 00000000..5e05abdf --- /dev/null +++ b/glow/src/shader/common/image.frag @@ -0,0 +1,22 @@ +#ifdef GL_ES +#ifdef GL_FRAGMENT_PRECISION_HIGH +precision highp float; +#else +precision mediump float; +#endif +#endif + +uniform sampler2D tex; +in vec2 tex_pos; + +#ifdef HIGHER_THAN_300 +out vec4 fragColor; +#define gl_FragColor fragColor +#endif +#ifdef GL_ES +#define texture texture2D +#endif + +void main() { + gl_FragColor = texture(tex, tex_pos); +} diff --git a/glow/src/shader/common/image.vert b/glow/src/shader/common/image.vert new file mode 100644 index 00000000..93e541f2 --- /dev/null +++ b/glow/src/shader/common/image.vert @@ -0,0 +1,9 @@ +uniform mat4 u_Transform; + +in vec2 i_Position; +out vec2 tex_pos; + +void main() { + gl_Position = u_Transform * vec4(i_Position, 0.0, 1.0); + tex_pos = i_Position; +} diff --git a/glow/src/shader/common/triangle.frag b/glow/src/shader/common/triangle.frag index e8689f2e..ead40fe5 100644 --- a/glow/src/shader/common/triangle.frag +++ b/glow/src/shader/common/triangle.frag @@ -11,8 +11,8 @@ out vec4 fragColor; #define gl_FragColor fragColor #endif -in vec4 v_Color; +uniform vec4 color; void main() { - gl_FragColor = v_Color; -}
\ No newline at end of file + fragColor = color; +} diff --git a/glow/src/shader/common/triangle.vert b/glow/src/shader/common/triangle.vert index d0494a5f..fe505997 100644 --- a/glow/src/shader/common/triangle.vert +++ b/glow/src/shader/common/triangle.vert @@ -1,11 +1,9 @@ uniform mat4 u_Transform; in vec2 i_Position; -in vec4 i_Color; - -out vec4 v_Color; +out vec2 raw_position; void main() { gl_Position = u_Transform * vec4(i_Position, 0.0, 1.0); - v_Color = i_Color; -}
\ No newline at end of file + raw_position = i_Position; +} diff --git a/glow/src/triangle.rs b/glow/src/triangle.rs index ae4f83ef..0e27bcf2 100644 --- a/glow/src/triangle.rs +++ b/glow/src/triangle.rs @@ -1,66 +1,32 @@ //! Draw meshes of triangles. -use crate::program::{self, Shader}; +mod gradient; +mod solid; + +use crate::program; use crate::Transformation; -use glow::HasContext; -use iced_graphics::layer; -use std::marker::PhantomData; -pub use iced_graphics::triangle::{Mesh2D, Vertex2D}; +use iced_graphics::layer::mesh::{self, Mesh}; +use iced_graphics::triangle::{self, Vertex2D}; -const VERTEX_BUFFER_SIZE: usize = 10_000; -const INDEX_BUFFER_SIZE: usize = 10_000; +use glow::HasContext; +use std::marker::PhantomData; #[derive(Debug)] pub(crate) struct Pipeline { - program: <glow::Context as HasContext>::Program, vertex_array: <glow::Context as HasContext>::VertexArray, vertices: Buffer<Vertex2D>, indices: Buffer<u32>, - transform_location: <glow::Context as HasContext>::UniformLocation, - current_transform: Transformation, + programs: ProgramList, } -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/triangle.vert"), - ); - let fragment_shader = Shader::fragment( - gl, - shader_version, - include_str!("shader/common/triangle.frag"), - ); - - program::create( - gl, - &[vertex_shader, fragment_shader], - &[(0, "i_Position"), (1, "i_Color")], - ) - }; - - 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); - } +#[derive(Debug)] +struct ProgramList { + solid: solid::Program, + gradient: gradient::Program, +} +impl Pipeline { + pub fn new(gl: &glow::Context, shader_version: &program::Version) -> Self { let vertex_array = unsafe { gl.create_vertex_array().expect("Create vertex array") }; @@ -73,7 +39,7 @@ impl Pipeline { gl, glow::ARRAY_BUFFER, glow::DYNAMIC_DRAW, - VERTEX_BUFFER_SIZE, + std::mem::size_of::<Vertex2D>() as usize, ) }; @@ -82,7 +48,7 @@ impl Pipeline { gl, glow::ELEMENT_ARRAY_BUFFER, glow::DYNAMIC_DRAW, - INDEX_BUFFER_SIZE, + std::mem::size_of::<u32>() as usize, ) }; @@ -92,83 +58,63 @@ impl Pipeline { 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, + Self { vertex_array, vertices, indices, - transform_location, - current_transform: Transformation::identity(), + programs: ProgramList { + solid: solid::Program::new(gl, shader_version), + gradient: gradient::Program::new(gl, shader_version), + }, } } pub fn draw( &mut self, + meshes: &[Mesh<'_>], gl: &glow::Context, target_height: u32, transformation: Transformation, scale_factor: f32, - meshes: &[layer::Mesh<'_>], ) { unsafe { gl.enable(glow::MULTISAMPLE); gl.enable(glow::SCISSOR_TEST); - gl.use_program(Some(self.program)); - gl.bind_vertex_array(Some(self.vertex_array)); + 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 + //count the total amount of vertices & indices we need to handle + let (total_vertices, total_indices) = mesh::attribute_count_of(meshes); + + // Then we ensure the current attribute 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; + let mut vertex_offset = 0; + let mut index_offset = 0; - for layer::Mesh { buffers, .. } in meshes { + for mesh 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), + (vertex_offset * std::mem::size_of::<Vertex2D>()) as i32, + bytemuck::cast_slice(&mesh.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), + (index_offset * std::mem::size_of::<u32>()) as i32, + bytemuck::cast_slice(&mesh.buffers.indices), ); - last_vertex += buffers.vertices.len(); - last_index += buffers.indices.len(); + vertex_offset += mesh.buffers.vertices.len(); + index_offset += mesh.buffers.indices.len(); } } @@ -176,29 +122,13 @@ impl Pipeline { 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); + for mesh in meshes { + let transform = transformation + * Transformation::translate(mesh.origin.x, mesh.origin.y); - let clip_bounds = (*clip_bounds * scale_factor).snap(); + let clip_bounds = (mesh.clip_bounds * scale_factor).snap(); unsafe { - if self.current_transform != transform { - let matrix: [f32; 16] = transform.into(); - gl.uniform_matrix_4_f32_slice( - Some(&self.transform_location), - false, - &matrix, - ); - - self.current_transform = transform; - } - gl.scissor( clip_bounds.x as i32, (target_height - (clip_bounds.y + clip_bounds.height)) @@ -207,50 +137,74 @@ impl Pipeline { clip_bounds.height as i32, ); + match mesh.style { + triangle::Style::Solid(color) => { + self.programs.solid.use_program(gl, color, &transform); + } + triangle::Style::Gradient(gradient) => { + self.programs + .gradient + .use_program(gl, gradient, &transform); + } + } + gl.draw_elements_base_vertex( glow::TRIANGLES, - buffers.indices.len() as i32, + mesh.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(); + last_vertex += mesh.buffers.vertices.len(); + last_index += mesh.buffers.indices.len(); } } unsafe { gl.bind_vertex_array(None); - gl.use_program(None); gl.disable(glow::SCISSOR_TEST); gl.disable(glow::MULTISAMPLE); } } } -#[repr(C)] -#[derive(Debug, Clone, Copy)] -struct Uniforms { - transform: [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(), - } +/// A simple shader program. Uses [`triangle.vert`] for its vertex shader and only binds position +/// attribute location. +pub(super) fn program( + gl: &glow::Context, + shader_version: &program::Version, + fragment_shader: &'static str, +) -> <glow::Context as HasContext>::Program { + unsafe { + let vertex_shader = program::Shader::vertex( + gl, + shader_version, + include_str!("shader/common/triangle.vert"), + ); + + let fragment_shader = + program::Shader::fragment(gl, shader_version, fragment_shader); + + program::create( + gl, + &[vertex_shader, fragment_shader], + &[(0, "i_Position")], + ) } } -impl From<Transformation> for Uniforms { - fn from(transformation: Transformation) -> Uniforms { - Self { - transform: transformation.into(), - } +pub fn set_transform( + gl: &glow::Context, + location: <glow::Context as HasContext>::UniformLocation, + transform: Transformation, +) { + unsafe { + gl.uniform_matrix_4_f32_slice( + Some(&location), + false, + transform.as_ref(), + ); } } diff --git a/glow/src/triangle/gradient.rs b/glow/src/triangle/gradient.rs new file mode 100644 index 00000000..5225612e --- /dev/null +++ b/glow/src/triangle/gradient.rs @@ -0,0 +1,162 @@ +use crate::program::Version; +use crate::triangle; +use glow::{Context, HasContext, NativeProgram}; +use iced_graphics::gradient::Gradient; +use iced_graphics::gradient::Linear; +use iced_graphics::Transformation; + +#[derive(Debug)] +pub struct Program { + pub program: <Context as HasContext>::Program, + pub uniform_data: UniformData, +} + +#[derive(Debug)] +pub struct UniformData { + gradient: Gradient, + transform: Transformation, + uniform_locations: UniformLocations, +} + +#[derive(Debug)] +struct UniformLocations { + gradient_direction_location: <Context as HasContext>::UniformLocation, + color_stops_size_location: <Context as HasContext>::UniformLocation, + //currently the maximum number of stops is 16 due to lack of SSBO in GL2.1 + color_stops_location: <Context as HasContext>::UniformLocation, + transform_location: <Context as HasContext>::UniformLocation, +} + +impl Program { + pub fn new(gl: &Context, shader_version: &Version) -> Self { + let program = triangle::program( + gl, + shader_version, + include_str!("../shader/common/gradient.frag"), + ); + + Self { + program, + uniform_data: UniformData::new(gl, program), + } + } + + pub fn write_uniforms( + &mut self, + gl: &Context, + gradient: &Gradient, + transform: &Transformation, + ) { + if transform != &self.uniform_data.transform { + triangle::set_transform( + gl, + self.uniform_data.uniform_locations.transform_location, + *transform, + ); + } + + if &self.uniform_data.gradient != gradient { + match gradient { + Gradient::Linear(linear) => unsafe { + gl.uniform_4_f32( + Some( + &self + .uniform_data + .uniform_locations + .gradient_direction_location, + ), + linear.start.x, + linear.start.y, + linear.end.x, + linear.end.y, + ); + + gl.uniform_1_u32( + Some( + &self + .uniform_data + .uniform_locations + .color_stops_size_location, + ), + (linear.color_stops.len() * 2) as u32, + ); + + 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 + .uniform_data + .uniform_locations + .color_stops_location, + ), + &stops, + ); + }, + } + + self.uniform_data.gradient = gradient.clone(); + } + } + + pub fn use_program( + &mut self, + gl: &Context, + gradient: &Gradient, + transform: &Transformation, + ) { + unsafe { gl.use_program(Some(self.program)) } + self.write_uniforms(gl, gradient, transform); + } +} + +impl UniformData { + fn new(gl: &Context, program: NativeProgram) -> Self { + let gradient_direction_location = + unsafe { gl.get_uniform_location(program, "gradient_direction") } + .expect("Gradient - Get gradient_direction."); + + let color_stops_size_location = + unsafe { gl.get_uniform_location(program, "color_stops_size") } + .expect("Gradient - Get color_stops_size."); + + let color_stops_location = unsafe { + gl.get_uniform_location(program, "color_stops") + .expect("Gradient - Get color_stops.") + }; + + let transform_location = + unsafe { gl.get_uniform_location(program, "u_Transform") } + .expect("Gradient - Get u_Transform."); + + Self { + gradient: Gradient::Linear(Linear { + start: Default::default(), + end: Default::default(), + color_stops: vec![], + }), + transform: Transformation::identity(), + uniform_locations: UniformLocations { + gradient_direction_location, + color_stops_size_location, + color_stops_location, + transform_location, + }, + } + } +} diff --git a/glow/src/triangle/solid.rs b/glow/src/triangle/solid.rs new file mode 100644 index 00000000..fb3d40c3 --- /dev/null +++ b/glow/src/triangle/solid.rs @@ -0,0 +1,91 @@ +use crate::program::Version; +use crate::{triangle, Color}; +use glow::{Context, HasContext, NativeProgram}; +use iced_graphics::Transformation; + +#[derive(Debug)] +pub struct Program { + program: <Context as HasContext>::Program, + uniform_data: UniformData, +} + +#[derive(Debug)] +struct UniformData { + pub color: Color, + pub color_location: <Context as HasContext>::UniformLocation, + pub transform: Transformation, + pub transform_location: <Context as HasContext>::UniformLocation, +} + +impl UniformData { + fn new(gl: &Context, program: NativeProgram) -> Self { + Self { + color: Color::TRANSPARENT, + color_location: unsafe { + gl.get_uniform_location(program, "color") + } + .expect("Solid - Get color."), + transform: Transformation::identity(), + transform_location: unsafe { + gl.get_uniform_location(program, "u_Transform") + } + .expect("Solid - Get u_Transform."), + } + } +} + +impl Program { + pub fn new(gl: &Context, shader_version: &Version) -> Self { + let program = triangle::program( + gl, + shader_version, + include_str!("../shader/common/triangle.frag"), + ); + + Self { + program, + uniform_data: UniformData::new(gl, program), + } + } + + pub fn write_uniforms( + &mut self, + gl: &Context, + color: &Color, + transform: &Transformation, + ) { + if transform != &self.uniform_data.transform { + triangle::set_transform( + gl, + self.uniform_data.transform_location, + *transform, + ) + } + + if color != &self.uniform_data.color { + let [r, g, b, a] = color.into_linear(); + + unsafe { + gl.uniform_4_f32( + Some(&self.uniform_data.color_location), + r, + g, + b, + a, + ); + } + + self.uniform_data.color = *color; + } + } + + pub fn use_program( + &mut self, + gl: &Context, + color: &Color, + transform: &Transformation, + ) { + unsafe { gl.use_program(Some(self.program)) } + self.write_uniforms(gl, color, transform) + } +} diff --git a/glow/src/window/compositor.rs b/glow/src/window/compositor.rs index f6afaa68..20756032 100644 --- a/glow/src/window/compositor.rs +++ b/glow/src/window/compositor.rs @@ -26,8 +26,11 @@ impl<Theme> iced_graphics::window::GLCompositor for Compositor<Theme> { log::info!("{:#?}", settings); let version = gl.version(); - log::info!("Version: {:?}", version); - log::info!("Embedded: {}", version.is_embedded); + log::info!( + "OpenGL version: {:?} (Embedded: {})", + version, + version.is_embedded + ); let renderer = gl.get_parameter_string(glow::RENDERER); log::info!("Renderer: {}", renderer); |