Respect paths' content masks when copying them from MSAA texture to drawable (#35688)
Fixes a regression introduced in https://github.com/zed-industries/zed/pull/34992 ### Background Paths are rendered first to an intermediate MSAA texture, and then copied to the final drawable. Because paths can have transparency, it's important that pixels are not copied repeatedly if paths have overlapping bounding boxes. When N paths have the same draw order, we infer that they must have disjoint bounding boxes, so that we can copy them each individually (as opposed to copying a single rect that contains them all). Previously, the bounding box that we were using to copy paths was not accounting for the path's content mask (but it is accounted for in the bounds tree that determines their draw order). This cause bugs like this, where certain path pixels spuriously had their opacity doubled: https://github.com/user-attachments/assets/d792e60c-790b-49ad-b435-6695daba430f This PR fixes that bug. * [x] mac * [x] linux * [x] windows Release Notes: - Fixed a bug where a selection's opacity was computed incorrectly when it overlapped with another editor's selections in a certain way.
This commit is contained in:
parent
a884e861e9
commit
40129147c6
4 changed files with 27 additions and 12 deletions
|
@ -606,7 +606,7 @@ impl BladeRenderer {
|
|||
xy_position: v.xy_position,
|
||||
st_position: v.st_position,
|
||||
color: path.color,
|
||||
bounds: path.bounds.intersect(&path.content_mask.bounds),
|
||||
bounds: path.clipped_bounds(),
|
||||
}));
|
||||
}
|
||||
let vertex_buf = unsafe { self.instance_belt.alloc_typed(&vertices, &self.gpu) };
|
||||
|
@ -735,13 +735,13 @@ impl BladeRenderer {
|
|||
paths
|
||||
.iter()
|
||||
.map(|path| PathSprite {
|
||||
bounds: path.bounds,
|
||||
bounds: path.clipped_bounds(),
|
||||
})
|
||||
.collect()
|
||||
} else {
|
||||
let mut bounds = first_path.bounds;
|
||||
let mut bounds = first_path.clipped_bounds();
|
||||
for path in paths.iter().skip(1) {
|
||||
bounds = bounds.union(&path.bounds);
|
||||
bounds = bounds.union(&path.clipped_bounds());
|
||||
}
|
||||
vec![PathSprite { bounds }]
|
||||
};
|
||||
|
|
|
@ -791,13 +791,13 @@ impl MetalRenderer {
|
|||
sprites = paths
|
||||
.iter()
|
||||
.map(|path| PathSprite {
|
||||
bounds: path.bounds,
|
||||
bounds: path.clipped_bounds(),
|
||||
})
|
||||
.collect();
|
||||
} else {
|
||||
let mut bounds = first_path.bounds;
|
||||
let mut bounds = first_path.clipped_bounds();
|
||||
for path in paths.iter().skip(1) {
|
||||
bounds = bounds.union(&path.bounds);
|
||||
bounds = bounds.union(&path.clipped_bounds());
|
||||
}
|
||||
sprites = vec![PathSprite { bounds }];
|
||||
}
|
||||
|
|
|
@ -435,7 +435,7 @@ impl DirectXRenderer {
|
|||
xy_position: v.xy_position,
|
||||
st_position: v.st_position,
|
||||
color: path.color,
|
||||
bounds: path.bounds.intersect(&path.content_mask.bounds),
|
||||
bounds: path.clipped_bounds(),
|
||||
}));
|
||||
}
|
||||
|
||||
|
@ -487,13 +487,13 @@ impl DirectXRenderer {
|
|||
paths
|
||||
.iter()
|
||||
.map(|path| PathSprite {
|
||||
bounds: path.bounds,
|
||||
bounds: path.clipped_bounds(),
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
} else {
|
||||
let mut bounds = first_path.bounds;
|
||||
let mut bounds = first_path.clipped_bounds();
|
||||
for path in paths.iter().skip(1) {
|
||||
bounds = bounds.union(&path.bounds);
|
||||
bounds = bounds.union(&path.clipped_bounds());
|
||||
}
|
||||
vec![PathSprite { bounds }]
|
||||
};
|
||||
|
|
|
@ -8,7 +8,12 @@ use crate::{
|
|||
AtlasTextureId, AtlasTile, Background, Bounds, ContentMask, Corners, Edges, Hsla, Pixels,
|
||||
Point, Radians, ScaledPixels, Size, bounds_tree::BoundsTree, point,
|
||||
};
|
||||
use std::{fmt::Debug, iter::Peekable, ops::Range, slice};
|
||||
use std::{
|
||||
fmt::Debug,
|
||||
iter::Peekable,
|
||||
ops::{Add, Range, Sub},
|
||||
slice,
|
||||
};
|
||||
|
||||
#[allow(non_camel_case_types, unused)]
|
||||
pub(crate) type PathVertex_ScaledPixels = PathVertex<ScaledPixels>;
|
||||
|
@ -793,6 +798,16 @@ impl Path<Pixels> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> Path<T>
|
||||
where
|
||||
T: Clone + Debug + Default + PartialEq + PartialOrd + Add<T, Output = T> + Sub<Output = T>,
|
||||
{
|
||||
#[allow(unused)]
|
||||
pub(crate) fn clipped_bounds(&self) -> Bounds<T> {
|
||||
self.bounds.intersect(&self.content_mask.bounds)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Path<ScaledPixels>> for Primitive {
|
||||
fn from(path: Path<ScaledPixels>) -> Self {
|
||||
Primitive::Path(path)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue