Fix drawing of 0-width borders when quad has other borders (#27511)

Michael Sloan created

Closes #27485

Release Notes:

- N/A

Change summary

crates/gpui/src/platform/blade/shaders.wgsl | 18 +++++++++++-----
crates/gpui/src/platform/mac/shaders.metal  | 24 ++++++++++++++++------
2 files changed, 29 insertions(+), 13 deletions(-)

Detailed changes

crates/gpui/src/platform/blade/shaders.wgsl 🔗

@@ -532,6 +532,12 @@ fn fs_quad(input: QuadVarying) -> @location(0) vec4<f32> {
             quad.border_widths.top,
             center_to_point.y < 0.0));
 
+    // 0-width borders are reduced so that `inner_sdf >= antialias_threshold`.
+    // The purpose of this is to not draw antialiasing pixels in this case.
+    let reduced_border =
+        vec2<f32>(select(border.x, -antialias_threshold, border.x == 0.0),
+                  select(border.y, -antialias_threshold, border.y == 0.0));
+
     // Vector from the corner of the quad bounds to the point, after mirroring
     // the point into the bottom right quadrant. Both components are <= 0.
     let corner_to_point = abs(center_to_point) - half_size;
@@ -546,15 +552,15 @@ fn fs_quad(input: QuadVarying) -> @location(0) vec4<f32> {
             corner_center_to_point.y >= 0;
 
     // Vector from straight border inner corner to point.
-    let straight_border_inner_corner_to_point = corner_to_point + border;
+    let straight_border_inner_corner_to_point = corner_to_point + reduced_border;
 
     // Whether the point is beyond the inner edge of the straight border.
     let is_beyond_inner_straight_border =
             straight_border_inner_corner_to_point.x > 0 ||
             straight_border_inner_corner_to_point.y > 0;
 
-    // Whether the point is far enough inside the straight border such that
-    // pixels are not affected by it.
+    // Whether the point is far enough inside the quad, such that the pixels are
+    // not affected by the straight border.
     let is_within_inner_straight_border =
         straight_border_inner_corner_to_point.x < -antialias_threshold &&
         straight_border_inner_corner_to_point.y < -antialias_threshold;
@@ -589,11 +595,11 @@ fn fs_quad(input: QuadVarying) -> @location(0) vec4<f32> {
     } else if (is_beyond_inner_straight_border) {
         // Fast path for points that must be outside the inner edge.
         inner_sdf = -1.0;
-    } else if (border.x == border.y) {
+    } else if (reduced_border.x == reduced_border.y) {
         // Fast path for circular inner edge.
-        inner_sdf = -(outer_sdf + border.x);
+        inner_sdf = -(outer_sdf + reduced_border.x);
     } else {
-        let ellipse_radii = max(vec2<f32>(0.0), corner_radius - border);
+        let ellipse_radii = max(vec2<f32>(0.0), corner_radius - reduced_border);
         inner_sdf = quarter_ellipse_sdf(corner_center_to_point, ellipse_radii);
     }
 

crates/gpui/src/platform/mac/shaders.metal 🔗

@@ -133,6 +133,12 @@ fragment float4 quad_fragment(QuadFragmentInput input [[stage_in]],
     center_to_point.y < 0.0 ? quad.border_widths.top : quad.border_widths.bottom
   );
 
+  // 0-width borders are reduced so that `inner_sdf >= antialias_threshold`.
+  // The purpose of this is to not draw antialiasing pixels in this case.
+  float2 reduced_border = float2(
+    border.x == 0.0 ? -antialias_threshold : border.x,
+    border.y == 0.0 ? -antialias_threshold : border.y);
+
   // Vector from the corner of the quad bounds to the point, after mirroring
   // the point into the bottom right quadrant. Both components are <= 0.
   float2 corner_to_point = fabs(center_to_point) - half_size;
@@ -146,16 +152,20 @@ fragment float4 quad_fragment(QuadFragmentInput input [[stage_in]],
     corner_center_to_point.x >= 0.0 &&
     corner_center_to_point.y >= 0.0;
 
-  // Vector from straight border inner corner to point
-  float2 straight_border_inner_corner_to_point = corner_to_point + border;
+  // Vector from straight border inner corner to point.
+  //
+  // 0-width borders are turned into width -1 so that inner_sdf is > 1.0 near
+  // the border. Without this, antialiasing pixels would be drawn.
+  float2 straight_border_inner_corner_to_point = corner_to_point + reduced_border;
 
   // Whether the point is beyond the inner edge of the straight border
   bool is_beyond_inner_straight_border =
     straight_border_inner_corner_to_point.x > 0.0 ||
     straight_border_inner_corner_to_point.y > 0.0;
 
-  // Whether the point is far enough inside the straight border such that
-  // pixels are not affected by it
+
+  // Whether the point is far enough inside the quad, such that the pixels are
+  // not affected by the straight border.
   bool is_within_inner_straight_border =
     straight_border_inner_corner_to_point.x < -antialias_threshold &&
     straight_border_inner_corner_to_point.y < -antialias_threshold;
@@ -184,11 +194,11 @@ fragment float4 quad_fragment(QuadFragmentInput input [[stage_in]],
   } else if (is_beyond_inner_straight_border) {
     // Fast path for points that must be outside the inner edge
     inner_sdf = -1.0;
-  } else if (border.x == border.y) {
+  } else if (reduced_border.x == reduced_border.y) {
     // Fast path for circular inner edge.
-    inner_sdf = -(outer_sdf + border.x);
+    inner_sdf = -(outer_sdf + reduced_border.x);
   } else {
-    float2 ellipse_radii = max(float2(0.0), float2(corner_radius) - border);
+    float2 ellipse_radii = max(float2(0.0), float2(corner_radius) - reduced_border);
     inner_sdf = quarter_ellipse_sdf(corner_center_to_point, ellipse_radii);
   }