From fa4dfba04315f2d1af92b8414e71c72fe783defd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Hrastnik?= Date: Thu, 14 Apr 2022 17:46:10 +0900 Subject: [PATCH] checkpoint --- Cargo.lock | 109 +++++++++++++++++++++-- Cargo.toml | 3 +- helix-ui/Cargo.toml | 3 +- helix-ui/src/main.rs | 181 +++++++++++++++++++++++++++++++++++---- helix-ui/src/shader.wgsl | 24 ++++-- 5 files changed, 292 insertions(+), 28 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 366d5994f..adbe8301e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -52,6 +52,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c5d78ce20460b82d3fa150275ed9d55e21064fc7951177baacf86a145c4a4b1f" +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + [[package]] name = "arrayvec" version = "0.7.2" @@ -136,9 +142,23 @@ checksum = "72feb31ffc86498dacdbd0fcebb56138e7177a8cc5cea4516031d15ae85a742e" [[package]] name = "bytemuck" -version = "1.8.0" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdead85bdec19c194affaeeb670c0e41fe23de31459efd1c174d049269cf02cc" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e851ca7c24871e7336801608a4797d7376545b6928a10d32d75685687141ead" +checksum = "562e382481975bc61d11275ac5e62a19abd00b0547d99516a415336f183dcd0e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] name = "byteorder" @@ -595,6 +615,15 @@ dependencies = [ "thiserror", ] +[[package]] +name = "euclid" +version = "0.22.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b52c2ef4a78da0ba68fbe1fd920627411096d2ac478f7f4c9f3a54ba6705bade" +dependencies = [ + "num-traits", +] + [[package]] name = "fern" version = "0.6.1" @@ -604,6 +633,15 @@ dependencies = [ "log", ] +[[package]] +name = "float_next_after" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fc612c5837986b7104a87a0df74a5460931f1c5274be12f8d0f40aa2f30d632" +dependencies = [ + "num-traits", +] + [[package]] name = "fnv" version = "1.0.7" @@ -1033,6 +1071,7 @@ dependencies = [ name = "helix-ui" version = "0.1.0" dependencies = [ + "bytemuck", "console_error_panic_hook", "env_logger", "glam", @@ -1040,6 +1079,7 @@ dependencies = [ "helix-view", "image", "instant", + "lyon", "pollster", "resource", "swash", @@ -1296,6 +1336,56 @@ dependencies = [ "url", ] +[[package]] +name = "lyon" +version = "0.17.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf0510ed5e3e2fb80f3db2061ef5ca92d87bfda1a624bb1eacf3bd50226e4cbb" +dependencies = [ + "lyon_algorithms", + "lyon_tessellation", +] + +[[package]] +name = "lyon_algorithms" +version = "0.17.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8037f716541ba0d84d3de05c0069f8068baf73990d55980558b84d944c8a244a" +dependencies = [ + "lyon_path", + "sid", +] + +[[package]] +name = "lyon_geom" +version = "0.17.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce99ce77c22bfd8f39a95b9c749dffbfc3e2491ea30c874764c801a8b1485489" +dependencies = [ + "arrayvec 0.5.2", + "euclid", + "num-traits", +] + +[[package]] +name = "lyon_path" +version = "0.17.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0a59fdf767ca0d887aa61d1b48d4bbf6a124c1a45503593f7d38ab945bfbc0" +dependencies = [ + "lyon_geom", +] + +[[package]] +name = "lyon_tessellation" +version = "0.17.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7230e08dd0638048e46f387f255dbe7a7344a3e6705beab53242b5af25635760" +dependencies = [ + "float_next_after", + "lyon_path", +] + [[package]] name = "malloc_buf" version = "0.0.6" @@ -1935,6 +2025,15 @@ dependencies = [ "libc", ] +[[package]] +name = "sid" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd5ac56c121948b4879bba9e519852c211bcdd8f014efff766441deff0b91bdb" +dependencies = [ + "num-traits", +] + [[package]] name = "signal-hook" version = "0.3.13" @@ -2476,7 +2575,7 @@ version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b97cd781ff044d6d697b632a2e212032c2e957d1afaa21dbf58069cbb8f78567" dependencies = [ - "arrayvec", + "arrayvec 0.7.2", "js-sys", "log", "naga", @@ -2497,7 +2596,7 @@ version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4688c000eb841ca55f7b35db659b78d6e1cd77d7caf8fb929f4e181f754047d" dependencies = [ - "arrayvec", + "arrayvec 0.7.2", "bitflags", "cfg_aliases", "codespan-reporting", @@ -2520,7 +2619,7 @@ version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93b1a9400e8d7f32dd4dd909bb9a391015d70633d639775ddd3f14d1104bc970" dependencies = [ - "arrayvec", + "arrayvec 0.7.2", "ash", "bit-set", "bitflags", diff --git a/Cargo.toml b/Cargo.toml index 14ef30723..c577f6ca0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,8 @@ members = [ ] default-members = [ - "helix-term" + "helix-term", + "helix-ui" ] resolver = "2" diff --git a/helix-ui/Cargo.toml b/helix-ui/Cargo.toml index f101c87ce..3bdcfb421 100644 --- a/helix-ui/Cargo.toml +++ b/helix-ui/Cargo.toml @@ -20,9 +20,10 @@ instant = "0.1.12" swash = { git = "https://github.com/dfrg/swash" } # parley = { git = "https://github.com/dfrg/parley" } -# lyon = "0.17.10" +lyon = "0.17.10" wgpu = "0.12" pollster = "0.2" glam = "0.20" env_logger = "0.6" +bytemuck = { version = "1.9.1", features = ["derive"] } diff --git a/helix-ui/src/main.rs b/helix-ui/src/main.rs index d5037613d..61d4ebda4 100644 --- a/helix-ui/src/main.rs +++ b/helix-ui/src/main.rs @@ -5,6 +5,8 @@ use winit::{ window::Window, }; +use wgpu::util::DeviceExt; + // new femto-like framework: // wgpu renderer // kurbo, (alternative is euclid + lyon) @@ -25,6 +27,28 @@ use swash::{ Attributes, CacheKey, Charmap, FontRef, }; +use lyon::{ + math::{point, Transform}, + path::{builder::*, Path}, + tessellation::{BuffersBuilder, FillOptions, FillTessellator, FillVertex, VertexBuffers}, +}; + +use bytemuck::{Pod, Zeroable}; + +// Vertex for lines drawn by lyon +#[repr(C)] +#[derive(Clone, Copy, Debug, Pod, Zeroable)] +struct Vertex { + position: [f32; 2], + // color: [f32; 4], // Use this when I want more colors +} + +#[repr(C)] +#[derive(Clone, Copy, Debug, Pod, Zeroable)] +struct View { + size: [f32; 2], +} + pub struct Font { // Full content of the font file data: Vec, @@ -71,7 +95,8 @@ impl Font { } } } -fn font() { + +fn font() -> VertexBuffers { let font = Font::from_file("assets/fonts/Inter Variable/Inter.ttf", 0).unwrap(); let font = font.as_ref(); @@ -114,6 +139,8 @@ fn font() { // c.glyphs }); + // -- Scaling + let mut context = ScaleContext::new(); let mut scaler = context .builder(font) @@ -124,33 +151,79 @@ fn font() { let glyph_id = font.charmap().map('Q'); let outline = scaler.scale_outline(glyph_id).unwrap(); - append_outline((), outline.verbs(), outline.points()); + // -- Tesselation - // -- Scaling + // let mut encoder = Path::builder().transformed(Transform::new( + // 0.01, 0., // + // 0., 0.01, // + // 0., 0., + // )); + + let mut encoder = Path::builder(); + + append_outline(&mut encoder, outline.verbs(), outline.points()); + + let path = encoder.build(); + + let mut geometry: VertexBuffers = VertexBuffers::new(); + + let mut tessellator = FillTessellator::new(); + { + // Compute the tessellation. + tessellator + .tessellate_path( + &path, + &FillOptions::default(), + &mut BuffersBuilder::new(&mut geometry, |vertex: FillVertex| Vertex { + position: vertex.position().to_array(), + }), + ) + .unwrap(); + } + + println!("{:?}", geometry); + geometry } -fn append_outline(_encoder: (), verbs: &[Verb], points: &[Vector]) { +fn append_outline( + encoder: &mut T, + verbs: &[Verb], + points: &[Vector], +) { + let mut i = 0; for verb in verbs { - println!("{:?}", verb); match verb { Verb::MoveTo => { - // + let p = points[i]; + // TODO: can MoveTo appear halfway through? + encoder.begin(point(p.x, p.y)); + i += 1; } Verb::LineTo => { - // + let p = points[i]; + encoder.line_to(point(p.x, p.y)); + i += 1; } Verb::QuadTo => { - // + let p1 = points[i]; + let p2 = points[i + 1]; + encoder.quadratic_bezier_to(point(p1.x, p1.y), point(p2.x, p2.y)); + i += 2; } Verb::CurveTo => { - // + let p1 = points[i]; + let p2 = points[i + 1]; + let p3 = points[i + 2]; + encoder.cubic_bezier_to(point(p1.x, p1.y), point(p2.x, p2.y), point(p3.x, p3.y)); + i += 3; } Verb::Close => { - // + encoder.close(); } } } } + async fn run(event_loop: EventLoop<()>, window: Window) { let size = window.inner_size(); let instance = wgpu::Instance::new(wgpu::Backends::all()); @@ -186,10 +259,62 @@ async fn run(event_loop: EventLoop<()>, window: Window) { source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("shader.wgsl"))), }); + // --- + + let geometry = font(); + + let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Vertex Buffer"), + contents: bytemuck::cast_slice(&geometry.vertices), + usage: wgpu::BufferUsages::VERTEX, + }); + + let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Index Buffer"), + contents: bytemuck::cast_slice(&geometry.indices), + usage: wgpu::BufferUsages::INDEX, + }); + + // + + let data = View { size: [0.0, 0.0] }; + + let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Uniform Buffer"), + contents: bytemuck::cast_slice(&[data]), + usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, + }); + + let uniform_bind_group_layout = + device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + entries: &[wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::VERTEX, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Uniform, + has_dynamic_offset: false, + min_binding_size: None, + }, + count: None, + }], + label: Some("uniform_bind_group_layout"), + }); + + 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("uniform_bind_group"), + }); + + // + let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: None, - bind_group_layouts: &[], - push_constant_ranges: &[], + bind_group_layouts: &[&uniform_bind_group_layout], // &texture_bind_group_layout + push_constant_ranges: &[], // TODO: could use push constants for uniforms but that's not available on web }); let swapchain_format = surface.get_preferred_format(&adapter).unwrap(); @@ -200,7 +325,11 @@ async fn run(event_loop: EventLoop<()>, window: Window) { vertex: wgpu::VertexState { module: &shader, entry_point: "vs_main", - buffers: &[], + buffers: &[wgpu::VertexBufferLayout { + array_stride: std::mem::size_of::() as wgpu::BufferAddress, + step_mode: wgpu::VertexStepMode::Vertex, + attributes: &wgpu::vertex_attr_array![0 => Float32x2], + }], }, fragment: Some(wgpu::FragmentState { module: &shader, @@ -223,7 +352,7 @@ async fn run(event_loop: EventLoop<()>, window: Window) { surface.configure(&device, &config); - font(); + // event_loop.run(move |event, _, control_flow| { // Have the closure take ownership of the resources. @@ -253,6 +382,20 @@ async fn run(event_loop: EventLoop<()>, window: Window) { .create_view(&wgpu::TextureViewDescriptor::default()); let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); + + // TODO: need to use queue.write_buffer or staging_belt to write to it + + // Pass the current window size in + let dpi_factor = window.scale_factor(); + let size = window.inner_size(); + let winit::dpi::LogicalSize { width, height } = size.to_logical::(dpi_factor); + + let data = View { + size: [width, height], + }; + + queue.write_buffer(&uniform_buffer, 0, bytemuck::cast_slice(&[data])); + { let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { label: None, @@ -260,14 +403,20 @@ async fn run(event_loop: EventLoop<()>, window: Window) { view: &view, resolve_target: None, ops: wgpu::Operations { - load: wgpu::LoadOp::Clear(wgpu::Color::GREEN), + load: wgpu::LoadOp::Clear(wgpu::Color::BLACK), store: true, }, }], depth_stencil_attachment: None, }); + + // rpass.set_viewport(); + rpass.set_pipeline(&render_pipeline); - rpass.draw(0..3, 0..1); + rpass.set_bind_group(0, &uniform_bind_group, &[]); + rpass.set_index_buffer(index_buffer.slice(..), wgpu::IndexFormat::Uint16); + rpass.set_vertex_buffer(0, vertex_buffer.slice(..)); + rpass.draw_indexed(0..(geometry.indices.len() as u32), 0, 0..1); } queue.submit(Some(encoder.finish())); diff --git a/helix-ui/src/shader.wgsl b/helix-ui/src/shader.wgsl index a1ef447d6..f8d654d70 100644 --- a/helix-ui/src/shader.wgsl +++ b/helix-ui/src/shader.wgsl @@ -1,11 +1,25 @@ + +// struct Vertex { +// [[location(0)]] position: vec2; +// }; + +struct View { + size: vec2; +}; + +[[group(0), binding(0)]] +var view: View; + [[stage(vertex)]] -fn vs_main([[builtin(vertex_index)]] in_vertex_index: u32) -> [[builtin(position)]] vec4 { - let x = f32(i32(in_vertex_index) - 1); - let y = f32(i32(in_vertex_index & 1u) * 2 - 1); - return vec4(x, y, 0.0, 1.0); +fn vs_main([[location(0)]] input: vec2) -> [[builtin(position)]] vec4 { + // TODO: scale by hidpi factor? + return vec4( + input.xy / view.size.xy * 2.0 * 1.5, + 0.0, 1.0 + ); } [[stage(fragment)]] fn fs_main() -> [[location(0)]] vec4 { - return vec4(1.0, 0.0, 0.0, 1.0); + return vec4(1.0, 1.0, 1.0, 1.0); }