WIP

Antonio Scandurra created

Change summary

crates/gpui3/src/geometry.rs      | 24 +++++++++
crates/gpui3/src/renderer.rs      | 31 +++++++----
crates/gpui3/src/scene.rs         | 12 +--
crates/gpui3/src/shader.vert.wgsl | 88 +++++++++++++++++++++++++++++++++
crates/gpui3/src/style.rs         | 12 ++--
5 files changed, 142 insertions(+), 25 deletions(-)

Detailed changes

crates/gpui3/src/geometry.rs 🔗

@@ -138,6 +138,9 @@ pub struct Bounds<T: Clone + Debug> {
     pub size: Size<T>,
 }
 
+unsafe impl<T: Clone + Debug + Zeroable + Pod> Zeroable for Bounds<T> {}
+unsafe impl<T: Clone + Debug + Zeroable + Pod> Pod for Bounds<T> {}
+
 impl<T: Clone + Debug + Copy + Add<T, Output = T>> Bounds<T> {
     pub fn upper_right(&self) -> Point<T> {
         Point {
@@ -174,6 +177,12 @@ pub struct Edges<T: Clone + Debug> {
     pub left: T,
 }
 
+impl<T: Clone + Debug + Copy> Copy for Edges<T> {}
+
+unsafe impl<T: Clone + Debug + Zeroable + Pod> Zeroable for Edges<T> {}
+
+unsafe impl<T: Clone + Debug + Zeroable + Pod> Pod for Edges<T> {}
+
 impl Edges<Length> {
     pub fn auto() -> Self {
         Self {
@@ -231,6 +240,21 @@ impl Edges<Pixels> {
     }
 }
 
+#[derive(Refineable, Clone, Default, Debug)]
+#[refineable(debug)]
+pub struct Corners<T: Clone + Debug> {
+    pub top_left: T,
+    pub top_right: T,
+    pub bottom_right: T,
+    pub bottom_left: T,
+}
+
+impl<T: Clone + Debug + Copy> Copy for Corners<T> {}
+
+unsafe impl<T: Clone + Debug + Zeroable + Pod> Zeroable for Corners<T> {}
+
+unsafe impl<T: Clone + Debug + Zeroable + Pod> Pod for Corners<T> {}
+
 #[derive(Clone, Copy, Default, Add, AddAssign, Sub, SubAssign, Div, PartialEq, PartialOrd)]
 #[repr(transparent)]
 pub struct Pixels(pub(crate) f32);

crates/gpui3/src/renderer.rs 🔗

@@ -8,9 +8,10 @@ pub struct Renderer {
     queue: wgpu::Queue,
     surface: wgpu::Surface,
     surface_config: wgpu::SurfaceConfiguration,
-    pipeline: wgpu::RenderPipeline,
+    quad_pipeline: wgpu::RenderPipeline,
     vertex_buffer: wgpu::Buffer,
     vertex_count: u32,
+    uniforms_buffer: wgpu::Buffer,
 }
 
 pub(crate) trait RenderTarget: HasRawWindowHandle + HasRawDisplayHandle {
@@ -52,12 +53,9 @@ impl Renderer {
                 // quality takes priority over input latency.
                 present_mode: wgpu::PresentMode::Fifo,
 
-                // Use the Premultiplied alpha mode. With premultiplication, the color components
-                // are multiplied by the alpha value before storage or blending, meaning calculations
-                // with colors already factor in the influence of alpha. This typically results
-                // in better performance and avoids a separate multiplication operation during blending.
-                alpha_mode: wgpu::CompositeAlphaMode::PreMultiplied,
-
+                // When blending, assume the RGB have not yet been multiplied by the alpha channel.
+                alpha_mode: wgpu::CompositeAlphaMode::PostMultiplied,
+PostMultiplied
                 // Specify the color formats for the views the surface can have.
                 // In this case, the format is BGRA (blue, green, red, alpha) with unsigned
                 // normalised integers in the 8-bit range and the color space is sRGB (standard RGB).
@@ -78,6 +76,13 @@ impl Renderer {
                 source: wgpu::ShaderSource::Wgsl(include_str!("shader.frag.wgsl").into()),
             });
 
+            let uniforms_buffer = device.create_buffer(&wgpu::BufferDescriptor {
+                label: Some("Uniforms Buffer"),
+                size: std::mem::size_of::<QuadUniforms>() as wgpu::BufferAddress,
+                usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
+                mapped_at_creation: false,
+            });
+
             let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
                 label: Some("Render Pipeline Layout"),
                 bind_group_layouts: &[],
@@ -91,17 +96,17 @@ impl Renderer {
                 mapped_at_creation: false,
             });
 
-            let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
+            let quad_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
                 label: Some("Render Pipeline"),
                 layout: Some(&pipeline_layout),
                 vertex: wgpu::VertexState {
                     module: &vs_module,
-                    entry_point: "main",
+                    entry_point: "quad",
                     buffers: &[],
                 },
                 fragment: Some(wgpu::FragmentState {
                     module: &fs_module,
-                    entry_point: "main",
+                    entry_point: "quad",
                     targets: &[Some(wgpu::ColorTargetState {
                         format: surface_config.format,
                         blend: Some(wgpu::BlendState::REPLACE),
@@ -122,9 +127,10 @@ impl Renderer {
                 queue,
                 surface,
                 surface_config,
-                pipeline,
+                quad_pipeline,
                 vertex_buffer,
                 vertex_count: 0,
+                uniforms_buffer,
             }
         }
         .boxed()
@@ -163,8 +169,9 @@ impl Renderer {
                 depth_stencil_attachment: None,
             });
 
-            render_pass.set_pipeline(&self.pipeline);
+            render_pass.set_pipeline(&self.quad_pipeline);
             render_pass.set_vertex_buffer(0, self.vertex_buffer.slice(..));
+            render_pass.set_bind_group(0, &self.uniforms_buffer, &[]);
             render_pass.draw(0..self.vertex_count, 0..1);
         }
 

crates/gpui3/src/scene.rs 🔗

@@ -1,6 +1,5 @@
-use crate::{FontId, GlyphId};
-
 use super::{Bounds, Hsla, Pixels, Point};
+use crate::{Corners, Edges, FontId, GlyphId};
 use bytemuck::{Pod, Zeroable};
 use plane_split::BspSplitter;
 
@@ -69,13 +68,12 @@ pub struct PrimitiveBatch {
 pub struct Quad {
     pub order: f32,
     pub bounds: Bounds<Pixels>,
+    pub clip_bounds: Bounds<Pixels>,
+    pub clip_corner_radii: Corners<Pixels>,
     pub background: Hsla,
     pub border_color: Hsla,
-    pub corner_radius: Pixels,
-    pub border_left: Pixels,
-    pub border_right: Pixels,
-    pub border_top: Pixels,
-    pub border_bottom: Pixels,
+    pub corner_radii: Corners<Pixels>,
+    pub border_widths: Edges<Pixels>,
 }
 
 impl Quad {

crates/gpui3/src/shader.vert.wgsl 🔗

@@ -0,0 +1,88 @@
+[[stage(vertex)]]
+fn quad_vertex(input: QuadVertexInput) -> QuadVertexOutput {
+    var output: QuadVertexOutput;
+
+        // Apply clip bounds
+    input.bounds_origin = max(input.bounds_origin, input.clip_bounds.xy);
+    input.bounds_size = min(input.bounds_size, input.clip_bounds.zw);
+
+    var ndc: vec2<f32> = (input.bounds_origin / uniforms.window_size) * 2.0 - 1.0;
+
+    output.position = vec4<f32>(ndc, 0.0, 1.0);
+    output.position.y = -output.position.y; // Inverting since NDC's origin is at the center and y is up
+    output.color = input.color;
+    output.bounds = input.bounds_size / uniforms.window_size; // Convert size to NDC
+    output.corner_radii = input.corner_radii / uniforms.window_size; // Convert corner radii to NDC
+    output.clip_bounds = input.clip_bounds / uniforms.window_size; // Convert clip bounds to NDC
+    output.clip_corner_radii = input.corner_radii / uniforms.window_size; // Convert clip corner radii to NDC
+
+    return output;
+}
+
+// #[derive(Debug, Clone, Copy)]
+// #[repr(C)]
+// pub struct gpui3::scene::Quad {
+//     pub order: f32,
+//     pub bounds: Bounds<Pixels>,
+//     pub clip_bounds: Bounds<Pixels>,
+//     pub clip_corner_radii: Corners<Pixels>,
+//     pub background: Hsla,
+//     pub border_color: Hsla,
+//     pub corner_radii: Corners<Pixels>,
+//     pub border_widths: Edges<Pixels>,
+// }
+
+struct QuadVertexInput {
+    [[location(0)]] order: f32;
+    [[location(1)]] bounds: vec4<f32>; // Bounds<Pixels>
+    [[location(2)]] clip_bounds: vec4<f32>; // Bounds<Pixels>
+    [[location(3)]] clip_corner_radii: vec4<f32>; // Corners<Pixels>
+    [[location(4)]] color: vec4<f32>; // Hsla
+    [[location(5)]] border_color: vec4<f32>; // Hsla
+    [[location(6)]] corner_radii: vec4<f32>; // Corners<Pixels>
+    [[location(7)]] border_widths: vec4<f32>; // Edges<Pixels>
+};
+
+[[block]]
+struct Uniforms {
+    viewport: vec2<f32>;
+};
+
+[[binding(0), group(0)]] var<uniform> uniforms: Uniforms;
+
+struct QuadVertexOutput {
+    [[builtin(position)]] position: vec4<f32>;
+    [[location(0)]] color: vec4<f32>;
+    [[location(1)]] border_color: vec4<f32>;
+    [[location(2)]] bounds: vec4<f32>;
+    [[location(3)]] corner_radii: vec4<f32>; // assuming topLeft, topRight, bottomRight, bottomLeft
+    [[location(4)]] clip_bounds: vec4<f32>;
+    [[location(5)]] clip_corner_radii: vec4<f32>;
+    [[location(6)]] border_widths: vec4<f32>;
+};
+
+[[stage(fragment)]]
+fn quad_fragment(input: QuadVertexOutput) -> [[location(0)]] vec4<f32> {
+    var output_color: vec4<f32>;
+    var sdf = rounded_quad_sdf(input.position, input.bounds, input.corner_radii);
+    var alpha = clamp(1.0 - sdf, 0.0, 1.0);
+    var border_color: vec4<f32> = input.border_color;
+    var mix_factor: f32 = 1.0 - clamp(sdf, 0.0, 1.0); // Mixing factor dependent on sdf distance
+
+     var border_width_factor: vec4<f32> = normalize(input.border_widths); // Normalizing the border width to account for directional widths
+
+        output_color = mix(input.color, border_color, mix_factor * border_width_factor); // Modulate the mix_factor with the border_width_factor to handle different border widths
+
+    output_color.a = alpha;
+
+    return output_color;
+}
+
+[[stage(fragment)]]
+fn rounded_quad_sdf(p: vec2<f32>, b: vec2<f32>, r: vec4<f32>) -> f32 {
+    var rx: vec2<f32>;
+    rx = select(r.xy, r.zw, greaterThan(p.x, 0.0));
+    rx.x = select(rx.x, rx.y, greaterThan(p.y, 0.0));
+    var q: vec2<f32> = abs(p)-b+rx.x;
+    return min(max(q.x,q.y),0.0) + length(max(q, vec2<f32>(0.0, 0.0))) - rx.x;
+}

crates/gpui3/src/style.rs 🔗

@@ -1,7 +1,7 @@
-use super::{
-    rems, AbsoluteLength, Bounds, DefiniteLength, Edges, EdgesRefinement, FontStyle, FontWeight,
-    Hsla, Length, Pixels, Point, PointRefinement, Rems, Result, RunStyle, SharedString, Size,
-    SizeRefinement, ViewContext, WindowContext,
+use crate::{
+    rems, AbsoluteLength, Bounds, Corners, CornersRefinement, DefiniteLength, Edges,
+    EdgesRefinement, FontStyle, FontWeight, Hsla, Length, Pixels, Point, PointRefinement, Rems,
+    Result, RunStyle, SharedString, Size, SizeRefinement, ViewContext, WindowContext,
 };
 use refineable::Refineable;
 pub use taffy::style::{
@@ -86,7 +86,7 @@ pub struct Style {
 
     /// The radius of the corners of this element
     #[refineable]
-    pub corner_radii: CornerRadii,
+    pub corner_radii: Corners<AbsoluteLength>,
 
     /// The color of text within this element. Cascades to children unless overridden.
     pub text_color: Option<Hsla>,
@@ -243,7 +243,7 @@ impl Default for Style {
             flex_basis: Length::Auto,
             fill: None,
             border_color: None,
-            corner_radii: CornerRadii::default(),
+            corner_radii: Corners::default(),
             text_color: None,
             font_size: Some(rems(1.)),
             font_family: None,