diff --git a/Cargo.lock b/Cargo.lock index 28626f1..7d89386 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -287,7 +287,7 @@ checksum = "bcfcc3cd946cb52f0bbfdbbcfa2f4e24f75ebb6c0e1002f7c25904fada18b9ec" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.87", ] [[package]] @@ -435,7 +435,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn", + "syn 2.0.87", ] [[package]] @@ -574,6 +574,29 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "crevice" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae753ee79d32a337b7c353db8fa045d2920021f78fc406416a2af7be3aeb407c" +dependencies = [ + "bytemuck", + "cgmath", + "crevice-derive", + "mint", +] + +[[package]] +name = "crevice-derive" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2320c07ceb3e491e2bd09ade90a91c29a42d9553f1bde60c945cb5c34958b26e" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "crossbeam-channel" version = "0.5.13" @@ -660,7 +683,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn", + "syn 2.0.87", ] [[package]] @@ -671,7 +694,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn", + "syn 2.0.87", ] [[package]] @@ -693,7 +716,7 @@ checksum = "d150dea618e920167e5973d70ae6ece4385b7164e0d799fe7c122dd0a5d912ad" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.87", ] [[package]] @@ -747,7 +770,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.87", ] [[package]] @@ -934,8 +957,10 @@ name = "ferrite-gui" version = "0.1.0" dependencies = [ "anyhow", + "bytemuck", "cgmath", "cosmic-text", + "crevice", "ferrite-cli", "ferrite-core", "ferrite-tui", @@ -1125,7 +1150,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.87", ] [[package]] @@ -1528,7 +1553,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.87", ] [[package]] @@ -1640,7 +1665,7 @@ dependencies = [ "pretty_assertions", "proc-macro2", "quote", - "syn", + "syn 2.0.87", ] [[package]] @@ -1899,6 +1924,12 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +[[package]] +name = "mint" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e53debba6bda7a793e5f99b8dacf19e626084f525f7829104ba9898f367d85ff" + [[package]] name = "mio" version = "1.0.2" @@ -1926,6 +1957,7 @@ dependencies = [ "hexf-parse", "indexmap", "log", + "pp-rs", "rustc-hash 1.1.0", "spirv", "termcolor", @@ -2072,7 +2104,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn", + "syn 2.0.87", ] [[package]] @@ -2335,6 +2367,15 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22686f4785f02a4fcc856d3b3bb19bf6c8160d103f7a99cc258bddd0251dc7f2" +[[package]] +name = "pp-rs" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb458bb7f6e250e6eb79d5026badc10a3ebb8f9a15d1fff0f13d17c71f4d6dee" +dependencies = [ + "unicode-xid", +] + [[package]] name = "ppv-lite86" version = "0.2.20" @@ -2738,7 +2779,7 @@ checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.87", ] [[package]] @@ -2927,7 +2968,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn", + "syn 2.0.87", ] [[package]] @@ -2963,6 +3004,17 @@ dependencies = [ "zeno", ] +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "syn" version = "2.0.87" @@ -2982,7 +3034,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.87", ] [[package]] @@ -3043,7 +3095,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.87", ] [[package]] @@ -3170,7 +3222,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.87", ] [[package]] @@ -3731,7 +3783,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn", + "syn 2.0.87", "wasm-bindgen-shared", ] @@ -3765,7 +3817,7 @@ checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.87", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -4104,7 +4156,7 @@ checksum = "f6fc35f58ecd95a9b71c4f2329b911016e6bec66b3f2e6a4aad86bd2e99e2f9b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.87", ] [[package]] @@ -4115,7 +4167,7 @@ checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.87", ] [[package]] @@ -4126,7 +4178,7 @@ checksum = "08990546bf4edef8f431fa6326e032865f27138718c587dc21bc0265bbcb57cc" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.87", ] [[package]] @@ -4137,7 +4189,7 @@ checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.87", ] [[package]] @@ -4575,7 +4627,7 @@ checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.87", "synstructure", ] @@ -4603,7 +4655,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.87", ] [[package]] @@ -4623,7 +4675,7 @@ checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.87", "synstructure", ] @@ -4646,5 +4698,5 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.87", ] diff --git a/crates/ferrite-core/src/buffer.rs b/crates/ferrite-core/src/buffer.rs index 5a1ce1d..bef23dc 100644 --- a/crates/ferrite-core/src/buffer.rs +++ b/crates/ferrite-core/src/buffer.rs @@ -686,11 +686,11 @@ impl Buffer { let mut width = 0; let mut byte_idx = 0; for grapeheme in next_line.grapehemes() { - width += grapeheme.width(width); - byte_idx += grapeheme.len_bytes(); if width >= before_cursor { break; } + width += grapeheme.width(width); + byte_idx += grapeheme.len_bytes(); } next_line_start + byte_idx }; @@ -749,11 +749,11 @@ impl Buffer { let mut width = 0; let mut byte_idx = 0; for grapeheme in next_line.grapehemes() { - width += grapeheme.width(width); - byte_idx += grapeheme.len_bytes(); if width >= before_cursor { break; } + width += grapeheme.width(width); + byte_idx += grapeheme.len_bytes(); } next_line_start + byte_idx }; @@ -2047,11 +2047,11 @@ impl Buffer { let mut width = 0; let mut byte_idx = 0; for grapeheme in next_line.grapehemes() { - width += grapeheme.width(width); - byte_idx += grapeheme.len_bytes(); if width >= col { break; } + width += grapeheme.width(width); + byte_idx += grapeheme.len_bytes(); } self.views[view_id].cursors[cursor_index].position = next_line_start + byte_idx; } diff --git a/crates/ferrite-gui/Cargo.toml b/crates/ferrite-gui/Cargo.toml index 811e414..dcce783 100644 --- a/crates/ferrite-gui/Cargo.toml +++ b/crates/ferrite-gui/Cargo.toml @@ -5,16 +5,18 @@ edition = "2021" [dependencies] anyhow = { workspace = true } +bytemuck = "1.20.0" cgmath = "0.18.0" +cosmic-text = { version = "0.12.1", features = ["shape-run-cache"] } +crevice = { version = "0.17.0", features = ["cgmath"] } ferrite-cli = { workspace = true } ferrite-core = { workspace = true } ferrite-tui = { workspace = true } ferrite-utility = { workspace = true } glyphon = "0.7.0" pollster = "0.3.0" -cosmic-text = { version = "0.12.1", features = ["shape-run-cache"] } tracing = { workspace = true } tui = { workspace = true, default-features = false } unicode-width = { workspace = true } -wgpu = "23.0.0" +wgpu = { version = "23.0.0", features = ["glsl"] } winit = "0.29.15" \ No newline at end of file diff --git a/crates/ferrite-gui/src/backend.rs b/crates/ferrite-gui/src/backend.rs index 356a69b..a552512 100644 --- a/crates/ferrite-gui/src/backend.rs +++ b/crates/ferrite-gui/src/backend.rs @@ -1,8 +1,11 @@ +use std::mem; + use ferrite_core::theme::EditorTheme; use glyphon::{ cosmic_text::Scroll, Attrs, AttrsList, Buffer, BufferLine, Cache, Family, FontSystem, Metrics, Resolution, Shaping, SwashCache, TextArea, TextAtlas, TextBounds, TextRenderer, Viewport, }; +use quad_renderer::{Quad, QuadRenderer}; use tui::{ backend::WindowSize, buffer::Cell, @@ -13,12 +16,15 @@ use wgpu::RenderPass; use crate::glue::convert_style; +mod quad_renderer; + pub struct WgpuBackend { font_system: FontSystem, swash_cache: SwashCache, atlas: TextAtlas, text_renderer: TextRenderer, viewport: Viewport, + quad_renderer: QuadRenderer, width: f32, height: f32, cell_width: f32, @@ -27,14 +33,14 @@ pub struct WgpuBackend { pub lines: u16, pub redraw: bool, buffer: Buffer, - cells: Vec<(Vec, bool)>, + cells: Vec>, } impl WgpuBackend { pub fn new( device: &wgpu::Device, queue: &wgpu::Queue, - surface_format: wgpu::TextureFormat, + config: &wgpu::SurfaceConfiguration, width: f32, height: f32, ) -> Self { @@ -42,7 +48,7 @@ impl WgpuBackend { font_system.db_mut().set_monospace_family("Fira Code"); let swash_cache = SwashCache::new(); let cache = Cache::new(device); - let mut atlas = TextAtlas::new(device, queue, &cache, surface_format); + let mut atlas = TextAtlas::new(device, queue, &cache, config.format); let text_renderer = TextRenderer::new( &mut atlas, device, @@ -84,15 +90,18 @@ impl WgpuBackend { for _ in 0..columns { line.push(Cell::default()); } - cells.push((line, true)); + cells.push(line); } + let quad_renderer = QuadRenderer::new(device, config); + Self { font_system, swash_cache, viewport, atlas, text_renderer, + quad_renderer, width, height, cell_width, @@ -106,6 +115,7 @@ impl WgpuBackend { } pub fn resize(&mut self, width: f32, height: f32) { + self.quad_renderer.resize(width, height); self.width = width; self.height = height; self.columns = (width / self.cell_width) as u16; @@ -116,24 +126,25 @@ impl WgpuBackend { for _ in 0..self.columns { line.push(Cell::default()); } - self.cells.push((line, true)); + self.cells.push(line); } let _ = self.clear(); } pub fn prepare(&mut self, device: &wgpu::Device, queue: &wgpu::Queue, theme: &EditorTheme) { + self.quad_renderer.clear(); let mut text_areas = Vec::new(); self.buffer .set_size(&mut self.font_system, Some(self.width), Some(self.height)); - let fg = convert_style(&theme.text) + let default_fg = convert_style(&theme.text) .0 .unwrap_or(glyphon::Color::rgb(0, 0, 0)); - /*et bg = convert_style(&theme.background) - .1 - .unwrap_or(glyphon::Color::rgb(1, 1, 1));*/ + let default_bg = convert_style(&theme.background) + .1 + .unwrap_or(glyphon::Color::rgb(255, 255, 255)); - let default_attrs = Attrs::new().color(fg).family(Family::Monospace); + let default_attrs = Attrs::new().color(default_fg).family(Family::Monospace); self.buffer.lines.resize( self.cells.len(), BufferLine::new( @@ -143,32 +154,51 @@ impl WgpuBackend { Shaping::Basic, ), ); - for (i, (line, dirty)) in self.cells.iter_mut().enumerate() { - if !*dirty { - continue; - } + for (line_idx, line) in self.cells.iter_mut().enumerate() { let mut attr_list = AttrsList::new(default_attrs); let mut line_text = String::new(); let mut idx = 0; - for cell in line { + // TODO handle cells that are wider then 1 + for (col_idx, cell) in line.iter().enumerate() { let mut attrs = default_attrs; + let mut fg = default_fg; + let mut bg = default_bg; if let tui::style::Color::Rgb(r, g, b) = cell.fg { - let color = glyphon::Color::rgb(r, g, b); - attrs = attrs.color(color); + fg = glyphon::Color::rgb(r, g, b); } + + if let tui::style::Color::Rgb(r, g, b) = cell.bg { + bg = glyphon::Color::rgb(r, g, b); + } + + if cell.modifier.contains(tui::style::Modifier::REVERSED) { + mem::swap(&mut fg, &mut bg); + } + + attrs = attrs.color(fg); let symbol = cell.symbol(); line_text.push_str(symbol); attr_list.add_span(idx..(idx + symbol.len()), attrs); idx += symbol.len(); + //if bg != default_bg { + self.quad_renderer.push_quad( + Quad { + x: col_idx as f32 * self.cell_width, + y: line_idx as f32 * self.cell_height, + width: self.cell_width, + height: self.cell_height, + }, + bg, + ); + //} } - self.buffer.lines[i] = BufferLine::new( + self.buffer.lines[line_idx] = BufferLine::new( &line_text, glyphon::cosmic_text::LineEnding::Lf, attr_list, Shaping::Advanced, ); - *dirty = false; } self.buffer.set_scroll(Scroll { @@ -198,7 +228,7 @@ impl WgpuBackend { right: self.width as i32, bottom: self.height as i32, }, - default_color: glyphon::Color::rgb(205, 214, 244), + default_color: default_fg, custom_glyphs: &[], }); @@ -213,9 +243,12 @@ impl WgpuBackend { &mut self.swash_cache, ) .unwrap(); + + self.quad_renderer.prepare(device, queue); } pub fn render<'rpass>(&'rpass mut self, rpass: &mut RenderPass<'rpass>) { + self.quad_renderer.render(rpass); self.text_renderer .render(&self.atlas, &self.viewport, rpass) .unwrap(); @@ -228,9 +261,8 @@ impl Backend for WgpuBackend { I: Iterator, { for (column, line, cell) in content { - let (line, dirty) = &mut self.cells[line as usize]; + let line = &mut self.cells[line as usize]; line[column as usize] = cell.clone(); - *dirty = true; self.redraw = true; } Ok(()) @@ -246,11 +278,10 @@ impl Backend for WgpuBackend { fn clear(&mut self) -> std::io::Result<()> { self.buffer.lines.clear(); - for (line, dirty) in &mut self.cells { + for line in &mut self.cells { for cell in line { cell.reset(); } - *dirty = true; } Ok(()) } diff --git a/crates/ferrite-gui/src/backend/quad_renderer.rs b/crates/ferrite-gui/src/backend/quad_renderer.rs new file mode 100644 index 0000000..8f37c8d --- /dev/null +++ b/crates/ferrite-gui/src/backend/quad_renderer.rs @@ -0,0 +1,308 @@ +use std::mem; + +use cgmath::{Matrix4, Ortho, SquareMatrix}; +use crevice::std140::AsStd140; +use glyphon::Color; +use wgpu::util::DeviceExt; + +#[rustfmt::skip] +pub const OPENGL_TO_WGPU_MATRIX: cgmath::Matrix4 = cgmath::Matrix4::new( + 1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 0.5, 0.0, + 0.0, 0.0, 0.5, 1.0, +); + +#[derive(Debug, Clone, Copy)] +pub struct Quad { + pub x: f32, + pub y: f32, + pub width: f32, + pub height: f32, +} + +#[repr(C)] +#[derive(Copy, Clone, Debug, Default)] +struct Vertex { + x: f32, + y: f32, + r: f32, + g: f32, + b: f32, + a: f32, +} + +unsafe impl bytemuck::Pod for Vertex {} +unsafe impl bytemuck::Zeroable for Vertex {} + +impl Vertex { + pub fn desc<'a>() -> wgpu::VertexBufferLayout<'a> { + wgpu::VertexBufferLayout { + array_stride: mem::size_of::() as wgpu::BufferAddress, + step_mode: wgpu::VertexStepMode::Vertex, + attributes: &[ + wgpu::VertexAttribute { + offset: 0, + shader_location: 0, + format: wgpu::VertexFormat::Float32x2, + }, + wgpu::VertexAttribute { + offset: mem::size_of::<[f32; 2]>() as wgpu::BufferAddress, + shader_location: 1, + format: wgpu::VertexFormat::Float32x4, + }, + ], + } + } +} + +#[repr(C)] +#[derive(crevice::std140::AsStd140, Debug, Copy, Clone)] +struct Uniform { + matrix: Matrix4, +} + +impl Default for Uniform { + fn default() -> Self { + Self { + matrix: Matrix4::identity(), + } + } +} + +impl Uniform { + fn get_bind_group_layout(device: &wgpu::Device) -> wgpu::BindGroupLayout { + device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + entries: &[wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::VERTEX_FRAGMENT, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Uniform, + has_dynamic_offset: false, + min_binding_size: None, + }, + count: None, + }], + label: Some("Quad fragment bind group"), + }) + } + + pub fn from_size(width: f32, height: f32) -> Self { + let ortho = Ortho { + left: 0.0, + right: width, + top: 0.0, + bottom: height, + near: 0.0, + far: 1.0, + }; + Self { + matrix: OPENGL_TO_WGPU_MATRIX * Matrix4::from(ortho), + } + } +} + +pub struct QuadRenderer { + pipeline: wgpu::RenderPipeline, + vertex_buffer: wgpu::Buffer, + index_buffer: wgpu::Buffer, + num_indices: u32, + uniform: Uniform, + uniform_buffer: wgpu::Buffer, + uniform_bind_group: wgpu::BindGroup, + vertices: Vec, + indices: Vec, +} + +impl QuadRenderer { + pub fn new(device: &wgpu::Device, config: &wgpu::SurfaceConfiguration) -> Self { + let render_pipeline_layout = + device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: Some("Quad render pipeline layout"), + bind_group_layouts: &[&Uniform::get_bind_group_layout(&device)], + push_constant_ranges: &[], + }); + + let vertex = device.create_shader_module(wgpu::ShaderModuleDescriptor { + label: Some("Quad vertex"), + source: wgpu::ShaderSource::Glsl { + shader: include_str!("../../../../shaders/quad.vert").into(), + stage: wgpu::naga::ShaderStage::Vertex, + defines: Default::default(), + }, + }); + + let fragment = device.create_shader_module(wgpu::ShaderModuleDescriptor { + label: Some("Quad fragment"), + source: wgpu::ShaderSource::Glsl { + shader: include_str!("../../../../shaders/quad.frag").into(), + stage: wgpu::naga::ShaderStage::Fragment, + defines: Default::default(), + }, + }); + + let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: Some("Quad Render Pipeline"), + layout: Some(&render_pipeline_layout), + vertex: wgpu::VertexState { + module: &vertex, + entry_point: Some("main"), + buffers: &[Vertex::desc()], + compilation_options: Default::default(), + }, + fragment: Some(wgpu::FragmentState { + module: &fragment, + entry_point: Some("main"), + targets: &[Some(wgpu::ColorTargetState { + format: config.format, + blend: Some(wgpu::BlendState::ALPHA_BLENDING), + write_mask: wgpu::ColorWrites::ALL, + })], + compilation_options: Default::default(), + }), + primitive: wgpu::PrimitiveState { + topology: wgpu::PrimitiveTopology::TriangleList, + strip_index_format: None, + front_face: wgpu::FrontFace::Ccw, + cull_mode: None, + polygon_mode: wgpu::PolygonMode::Fill, + unclipped_depth: false, + conservative: false, + }, + depth_stencil: None, + multisample: wgpu::MultisampleState { + count: 1, + mask: !0, + alpha_to_coverage_enabled: false, + }, + multiview: None, + cache: None, + }); + + let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Quad Vertex Buffer"), + contents: bytemuck::cast_slice(&[Vertex::default()]), + usage: wgpu::BufferUsages::VERTEX, + }); + + let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Quad Index Buffer"), + contents: bytemuck::cast_slice(&[0]), + usage: wgpu::BufferUsages::INDEX, + }); + + let uniform = Uniform::from_size(config.width as f32, config.height as f32); + + let value_std140 = Uniform::default().as_std140(); + let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Vertex uniform buffer"), + contents: value_std140.as_bytes(), + usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, + }); + + let uniform_bind_group_layout = Uniform::get_bind_group_layout(&device); + let uniform_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + layout: &uniform_bind_group_layout, + entries: &[wgpu::BindGroupEntry { + binding: 0, + resource: uniform_buffer.as_entire_binding(), + }], + label: Some("Vertex uniform bind group"), + }); + + Self { + pipeline, + vertex_buffer, + index_buffer, + num_indices: 0, + uniform, + uniform_buffer, + uniform_bind_group, + vertices: Vec::new(), + indices: Vec::new(), + } + } + + pub fn clear(&mut self) { + self.vertices.clear(); + self.indices.clear(); + } + + pub fn push_quad(&mut self, quad: Quad, color: Color) { + self.indices.push(self.vertices.len() as u32 + 0); + self.indices.push(self.vertices.len() as u32 + 1); + self.indices.push(self.vertices.len() as u32 + 2); + self.indices.push(self.vertices.len() as u32 + 2); + self.indices.push(self.vertices.len() as u32 + 1); + self.indices.push(self.vertices.len() as u32 + 3); + let r = (color.r() as f32 / 255.0).powf(2.0); + let g = (color.g() as f32 / 255.0).powf(2.0); + let b = (color.b() as f32 / 255.0).powf(2.0); + let a = color.a() as f32 / 255.0; + self.vertices.push(Vertex { + x: quad.x, + y: quad.y, + r, + g, + b, + a, + }); + self.vertices.push(Vertex { + x: quad.x + quad.width, + y: quad.y, + r, + g, + b, + a, + }); + self.vertices.push(Vertex { + x: quad.x, + y: quad.y + quad.height, + r, + g, + b, + a, + }); + self.vertices.push(Vertex { + x: quad.x + quad.width, + y: quad.y + quad.height, + r, + g, + b, + a, + }); + } + + pub fn resize(&mut self, width: f32, height: f32) { + self.uniform = Uniform::from_size(width, height); + } + + pub fn prepare(&mut self, device: &wgpu::Device, queue: &wgpu::Queue) { + let value_std140 = self.uniform.as_std140(); + queue.write_buffer(&self.uniform_buffer, 0, value_std140.as_bytes()); + + self.vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Quad Vertex Buffer"), + contents: bytemuck::cast_slice(&self.vertices), + usage: wgpu::BufferUsages::VERTEX, + }); + + self.index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Quad Index Buffer"), + contents: bytemuck::cast_slice(&self.indices), + usage: wgpu::BufferUsages::INDEX, + }); + + self.num_indices = self.indices.len() as u32; + } + + pub fn render<'rpass>(&'rpass mut self, rpass: &mut wgpu::RenderPass<'rpass>) { + if self.num_indices > 0 { + rpass.set_pipeline(&self.pipeline); + rpass.set_bind_group(0, &self.uniform_bind_group, &[]); + rpass.set_vertex_buffer(0, self.vertex_buffer.slice(..)); + rpass.set_index_buffer(self.index_buffer.slice(..), wgpu::IndexFormat::Uint32); + rpass.draw_indexed(0..self.num_indices, 0, 0..1); + } + } +} diff --git a/crates/ferrite-gui/src/lib.rs b/crates/ferrite-gui/src/lib.rs index 2805446..9334c62 100644 --- a/crates/ferrite-gui/src/lib.rs +++ b/crates/ferrite-gui/src/lib.rs @@ -142,7 +142,7 @@ impl GuiApp { let backend = WgpuBackend::new( &device, &queue, - surface_format, + &config, size.width as f32, size.height as f32, ); @@ -261,7 +261,7 @@ impl GuiApp { } } WindowEvent::KeyboardInput { event, .. } => { - tracing::info!("{:?}", event); + tracing::trace!("{:?}", event); let mut control_flow = self.control_flow; if !event.state.is_pressed() { if let Key::Named(key) = event.logical_key { @@ -394,9 +394,9 @@ impl GuiApp { resolve_target: None, ops: wgpu::Operations { load: wgpu::LoadOp::Clear(wgpu::Color { - r: color.r.powf(2.2) as f64, - g: color.g.powf(2.2) as f64, - b: color.b.powf(2.2) as f64, + r: color.r.powf(2.0) as f64, + g: color.g.powf(2.0) as f64, + b: color.b.powf(2.0) as f64, a: 1.0, }), store: wgpu::StoreOp::Store, diff --git a/shaders/quad.frag b/shaders/quad.frag new file mode 100644 index 0000000..5a05ba4 --- /dev/null +++ b/shaders/quad.frag @@ -0,0 +1,8 @@ +#version 440 + +layout(location = 0) in vec4 v_color; +layout(location = 0) out vec4 frag_color; + +void main() { + frag_color = v_color; +} diff --git a/shaders/quad.vert b/shaders/quad.vert new file mode 100644 index 0000000..2f393d7 --- /dev/null +++ b/shaders/quad.vert @@ -0,0 +1,14 @@ +#version 440 + +layout(location = 0) in vec2 position; +layout(location = 1) in vec4 color; +layout(location = 0) out vec4 v_color; + +layout(std140, set = 0, binding = 0) uniform InputUniform { + mat4 matrix; +}; + +void main() { + v_color = color; + gl_Position = matrix * vec4(position, 0.0, 1.0); +}