mirror of
https://github.com/zed-industries/zed.git
synced 2026-07-09 16:00:35 +00:00
gpui: Store GPU-facing bools as PaddedBool32 to avoid uninitialized padding (#60482)
`PolychromeSprite` in `crates/gpui/src/scene.rs` is `#[repr(C)]` and had a `grayscale: bool` field followed by 3 compiler-inserted padding bytes that were never written. The wgpu renderer's `instance_bytes` reinterprets `&[PolychromeSprite]` as `&[u8]` via `slice::from_raw_parts` and passes it to `queue.write_buffer`, so those uninitialized padding bytes were exposed behind a shared `&[u8]` on every frame that draws an image or emoji, which is undefined behavior. Rather than widening the field to a raw `u32` (which would suggest values other than 0 and 1 are meaningful), this introduces `PaddedBool32`: a `#[repr(transparent)]` wrapper around `u32` whose only public constructor is `From<bool>`, so the 0-or-1 invariant is enforced by the type while the layout has no padding. `Underline.wavy`, which was already a raw `u32` for the same reason, is converted too. cbindgen emits the wrapper as `typedef uint32_t PaddedBool32;`, so the generated Metal header and shaders are unchanged. The WGSL and HLSL shaders already declared these fields as `u32`/`uint`; their `& 0xFFu` masks, which existed to ignore the garbage padding bytes, are now simplified to plain comparisons. Release Notes: - N/A
This commit is contained in:
parent
10504e3ce1
commit
f281770034
4 changed files with 23 additions and 9 deletions
|
|
@ -22,6 +22,20 @@ pub type PathVertex_ScaledPixels = PathVertex<ScaledPixels>;
|
|||
#[expect(missing_docs)]
|
||||
pub type DrawOrder = u32;
|
||||
|
||||
/// A boolean stored as a `u32` so that GPU-facing structs contain no
|
||||
/// compiler-inserted padding bytes, which would be undefined behavior to
|
||||
/// reinterpret as `&[u8]` when writing instance buffers. Guaranteed to be
|
||||
/// `0` or `1` by construction; shaders read it as a `u32`/`uint`.
|
||||
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
|
||||
#[repr(transparent)]
|
||||
pub struct PaddedBool32(u32);
|
||||
|
||||
impl From<bool> for PaddedBool32 {
|
||||
fn from(value: bool) -> Self {
|
||||
PaddedBool32(value as u32)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
#[expect(missing_docs)]
|
||||
pub struct Scene {
|
||||
|
|
@ -511,7 +525,7 @@ pub struct Underline {
|
|||
pub content_mask: ContentMask<ScaledPixels>,
|
||||
pub color: Hsla,
|
||||
pub thickness: ScaledPixels,
|
||||
pub wavy: u32,
|
||||
pub wavy: PaddedBool32,
|
||||
}
|
||||
|
||||
impl From<Underline> for Primitive {
|
||||
|
|
@ -701,7 +715,7 @@ impl From<SubpixelSprite> for Primitive {
|
|||
pub struct PolychromeSprite {
|
||||
pub order: DrawOrder,
|
||||
pub pad: u32,
|
||||
pub grayscale: bool,
|
||||
pub grayscale: PaddedBool32,
|
||||
pub opacity: f32,
|
||||
pub bounds: Bounds<ScaledPixels>,
|
||||
pub content_mask: ContentMask<ScaledPixels>,
|
||||
|
|
|
|||
|
|
@ -3812,7 +3812,7 @@ impl Window {
|
|||
content_mask: self.snapped_content_mask(),
|
||||
color: style.color.unwrap_or_default().opacity(element_opacity),
|
||||
thickness,
|
||||
wavy: if style.wavy { 1 } else { 0 },
|
||||
wavy: style.wavy.into(),
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -3842,7 +3842,7 @@ impl Window {
|
|||
content_mask: self.snapped_content_mask(),
|
||||
thickness: self.snap_stroke(style.thickness),
|
||||
color: style.color.unwrap_or_default().opacity(opacity),
|
||||
wavy: 0,
|
||||
wavy: false.into(),
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -4002,7 +4002,7 @@ impl Window {
|
|||
self.next_frame.scene.insert_primitive(PolychromeSprite {
|
||||
order: 0,
|
||||
pad: 0,
|
||||
grayscale: false,
|
||||
grayscale: false.into(),
|
||||
bounds,
|
||||
corner_radii: Default::default(),
|
||||
content_mask,
|
||||
|
|
@ -4117,7 +4117,7 @@ impl Window {
|
|||
self.next_frame.scene.insert_primitive(PolychromeSprite {
|
||||
order: 0,
|
||||
pad: 0,
|
||||
grayscale,
|
||||
grayscale: grayscale.into(),
|
||||
bounds,
|
||||
content_mask,
|
||||
corner_radii,
|
||||
|
|
|
|||
|
|
@ -1191,7 +1191,7 @@ fn fs_underline(input: UnderlineVarying) -> @location(0) vec4<f32> {
|
|||
}
|
||||
|
||||
let underline = b_underlines[input.underline_id];
|
||||
if ((underline.wavy & 0xFFu) == 0u)
|
||||
if (underline.wavy == 0u)
|
||||
{
|
||||
return blend_color(input.color, input.color.a);
|
||||
}
|
||||
|
|
@ -1305,7 +1305,7 @@ fn fs_poly_sprite(input: PolySpriteVarying) -> @location(0) vec4<f32> {
|
|||
let distance = quad_sdf(input.position.xy, sprite.bounds, sprite.corner_radii);
|
||||
|
||||
var color = sample;
|
||||
if ((sprite.grayscale & 0xFFu) != 0u) {
|
||||
if (sprite.grayscale != 0u) {
|
||||
let grayscale = dot(color.rgb, GRAYSCALE_FACTORS);
|
||||
color = vec4<f32>(vec3<f32>(grayscale), sample.a);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1249,7 +1249,7 @@ float4 polychrome_sprite_fragment(PolychromeSpriteFragmentInput input): SV_Targe
|
|||
float distance = quad_sdf(input.position.xy, sprite.bounds, sprite.corner_radii);
|
||||
|
||||
float4 color = sample;
|
||||
if ((sprite.grayscale & 0xFFu) != 0u) {
|
||||
if (sprite.grayscale != 0u) {
|
||||
float3 grayscale = dot(color.rgb, GRAYSCALE_FACTORS);
|
||||
color = float4(grayscale, sample.a);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue