use gpui::{DevicePixels, Size, size};
pub(crate) mod heavy;
#[cfg_attr(
not(any(
target_os = "macos",
all(any(target_os = "linux", target_os = "windows"), feature = "wgpu")
)),
allow(dead_code)
)]
mod ring;
pub use heavy::{HeavyEngine, HeavyStatus};
#[cfg(target_os = "macos")]
mod macos;
#[cfg(target_os = "macos")]
use macos as platform;
#[cfg(target_os = "macos")]
pub use macos::*;
#[cfg(all(target_os = "linux", feature = "wgpu"))]
mod linux;
#[cfg(all(target_os = "linux", feature = "wgpu"))]
use linux as platform;
#[cfg(all(target_os = "linux", feature = "wgpu"))]
pub use linux::*;
#[cfg(all(target_os = "windows", feature = "wgpu"))]
mod d3d;
#[cfg(all(target_os = "windows", feature = "wgpu"))]
use d3d as platform;
#[cfg(all(target_os = "windows", feature = "wgpu"))]
pub use d3d::*;
#[cfg(any(
target_os = "macos",
all(any(target_os = "linux", target_os = "windows"), feature = "wgpu")
))]
mod surface;
#[cfg(any(
target_os = "macos",
all(any(target_os = "linux", target_os = "windows"), feature = "wgpu")
))]
pub use surface::{EngineSurface, SurfaceSlot};
#[cfg(all(any(target_os = "macos", target_os = "windows"), feature = "wgpu"))]
pub use ::wgpu;
#[cfg(all(target_os = "linux", feature = "wgpu"))]
pub use gpui_wgpu::wgpu;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum SurfaceFormat {
Bgra8Srgb,
Rgba16FloatLinearP3,
}
impl SurfaceFormat {
pub fn bytes_per_pixel(self) -> u32 {
match self {
Self::Bgra8Srgb => 4,
Self::Rgba16FloatLinearP3 => 8,
}
}
pub fn color_mode(self, wide: bool) -> gpui::ImageColorMode {
match self {
Self::Bgra8Srgb => gpui::ImageColorMode::EncodedSrgb,
#[cfg(any(target_os = "macos", target_os = "windows"))]
Self::Rgba16FloatLinearP3 if wide => gpui::ImageColorMode::LinearP3Wide,
Self::Rgba16FloatLinearP3 => {
let _ = wide;
gpui::ImageColorMode::LinearP3Preview
}
}
}
pub fn supports_storage(self) -> bool {
matches!(self, Self::Rgba16FloatLinearP3)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct SurfaceDesc {
pub width: u32,
pub height: u32,
pub format: SurfaceFormat,
pub slots: usize,
}
impl SurfaceDesc {
pub fn validate(&self) -> anyhow::Result<()> {
anyhow::ensure!(
(1..=16384).contains(&self.width) && (1..=16384).contains(&self.height),
"surface dimensions must be 1..=16384"
);
anyhow::ensure!(
(2..=8).contains(&self.slots),
"surface ring must hold 2..=8 slots"
);
let bytes = self.width as u64
* self.height as u64
* self.format.bytes_per_pixel() as u64
* self.slots as u64;
anyhow::ensure!(bytes <= 512 * 1024 * 1024, "surface ring exceeds 512 MiB");
Ok(())
}
pub fn extent(&self) -> Size<DevicePixels> {
size(
DevicePixels(self.width as i32),
DevicePixels(self.height as i32),
)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum AcquireError {
WouldBlock,
Invalidated,
}
impl std::fmt::Display for AcquireError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::WouldBlock => f.write_str("every surface slot is in use"),
Self::Invalidated => f.write_str("the surface was invalidated"),
}
}
}
impl std::error::Error for AcquireError {}
#[derive(Debug)]
pub struct Unsupported(pub String);
impl std::fmt::Display for Unsupported {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "engine surface unsupported: {}", self.0)
}
}
impl std::error::Error for Unsupported {}
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct SurfaceStats {
pub free: usize,
pub writing: usize,
pub published: usize,
pub presents: u64,
pub would_block: u64,
pub waiting: usize,
pub composited: u64,
pub unshown: u64,
pub composite_latency_ms: f32,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Transport {
ZeroCopy,
}
#[cfg(all(
test,
feature = "wgpu",
any(target_os = "macos", target_os = "linux", target_os = "windows")
))]
pub(crate) mod test_engine {
use crate::wgpu;
pub fn compute_fill(
device: &wgpu::Device,
texture: &wgpu::Texture,
color: [f32; 4],
) -> wgpu::CommandBuffer {
let [r, g, b, a] = color;
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
label: Some("engine_compute_fill"),
source: wgpu::ShaderSource::Wgsl(
format!(
"@group(0) @binding(0) var slot: texture_storage_2d<rgba16float, write>;
@compute @workgroup_size(8, 8)
fn main(@builtin(global_invocation_id) id: vec3<u32>) {{
let size = textureDimensions(slot);
if (id.x < size.x && id.y < size.y) {{
textureStore(slot, vec2<i32>(id.xy), vec4<f32>({r:?}, {g:?}, {b:?}, {a:?}));
}}
}}"
)
.into(),
),
});
let pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
label: Some("engine_compute_fill"),
layout: None,
module: &shader,
entry_point: Some("main"),
compilation_options: Default::default(),
cache: None,
});
let view = texture.create_view(&Default::default());
let bind = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("engine_compute_fill"),
layout: &pipeline.get_bind_group_layout(0),
entries: &[wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::TextureView(&view),
}],
});
let mut encoder = device.create_command_encoder(&Default::default());
{
let mut pass = encoder.begin_compute_pass(&Default::default());
pass.set_pipeline(&pipeline);
pass.set_bind_group(0, &bind, &[]);
let size = texture.size();
pass.dispatch_workgroups(size.width.div_ceil(8), size.height.div_ceil(8), 1);
}
encoder.finish()
}
}
#[cfg(test)]
#[cfg_attr(
not(any(
target_os = "macos",
all(any(target_os = "linux", target_os = "windows"), feature = "wgpu")
)),
allow(dead_code)
)]
pub(crate) mod colour_reference {
pub const STRIPE: u32 = 8;
pub const HEIGHT: u32 = 8;
pub const PATCHES: [(&str, [f32; 4]); 5] = [
("near-black", [0.001953125, 0.001953125, 0.001953125, 1.]),
("P3 green (outside sRGB)", [0., 1., 0., 1.]),
("P3 red (outside sRGB)", [1., 0., 0., 1.]),
("half alpha", [0.25, 0.125, 0., 0.5]),
("mid grey", [0.5, 0.5, 0.5, 1.]),
];
pub const SRGB_CONTROLS: [(&str, [u8; 4]); 3] = [
("sRGB colour", [128, 64, 200, 255]),
("sRGB near-black", [3, 3, 3, 255]),
("sRGB half alpha", [255, 128, 0, 128]),
];
pub fn width() -> u32 {
PATCHES.len() as u32 * STRIPE
}
pub fn centre(i: usize) -> u32 {
i as u32 * STRIPE + STRIPE / 2
}
pub fn f16_bits(value: f32) -> u16 {
let bits = value.to_bits();
let sign = ((bits >> 16) & 0x8000) as u16;
let exponent = ((bits >> 23) & 0xff) as i32 - 127 + 15;
let mantissa = bits & 0x7f_ffff;
if value == 0. {
return sign;
}
if exponent >= 31 {
return sign | 0x7c00;
}
if exponent <= 0 {
let shift = 14 - exponent;
let m = (mantissa | 0x80_0000) as u64;
let half = 1u64 << (shift - 1);
let rounded = (m + half - 1 + ((m >> shift) & 1)) >> shift;
return sign | rounded as u16;
}
let rounded = (mantissa + 0xfff + ((mantissa >> 13) & 1)) >> 13;
sign | (((exponent as u32) << 10) + rounded) as u16
}
pub fn strip_rgba16f() -> Vec<u8> {
let mut bytes = Vec::new();
for _ in 0..HEIGHT {
for (_, patch) in PATCHES {
for _ in 0..STRIPE {
for channel in patch {
bytes.extend_from_slice(&f16_bits(channel).to_ne_bytes());
}
}
}
}
bytes
}
pub fn controls_bgra8() -> Vec<u8> {
let mut bytes = Vec::new();
for _ in 0..HEIGHT {
for (_, [r, g, b, a]) in SRGB_CONTROLS {
for _ in 0..STRIPE {
bytes.extend_from_slice(&[b, g, r, a]);
}
}
}
bytes
}
const P3_TO_SRGB: [[f64; 3]; 3] = [
[1.224_940_2, -0.224_940_2, 0.],
[-0.042_056_95, 1.042_056_95, 0.],
[-0.019_637_55, -0.078_636_05, 1.098_273_6],
];
fn encode(linear: f64) -> f64 {
let v = linear.clamp(0., 1.);
if v <= 0.003_130_8 {
v * 12.92
} else {
1.055 * v.powf(1. / 2.4) - 0.055
}
}
pub fn preview(patch: [f32; 4], opacity: f32) -> [f64; 4] {
let alpha = patch[3] as f64;
let straight: Vec<f64> = (0..3)
.map(|c| {
if alpha > 0. {
patch[c] as f64 / alpha
} else {
0.
}
})
.collect();
let coverage = alpha * opacity as f64;
let mut out = [0.; 4];
for (row, value) in P3_TO_SRGB.iter().zip(out.iter_mut()) {
let linear: f64 = row.iter().zip(&straight).map(|(m, v)| m * v).sum();
*value = encode(linear) * 255. * coverage;
}
out[3] = coverage * 255.;
out
}
pub fn control(rgba: [u8; 4], opacity: f32) -> [f64; 4] {
let coverage = rgba[3] as f64 / 255. * opacity as f64;
[
rgba[0] as f64 * coverage,
rgba[1] as f64 * coverage,
rgba[2] as f64 * coverage,
coverage * 255.,
]
}
pub fn distance(actual: [u8; 4], expected: [f64; 4]) -> f64 {
(0..3)
.map(|c| (actual[c] as f64 - expected[c]).abs())
.fold(0., f64::max)
}
}
#[cfg(all(test, target_os = "macos"))]
pub(crate) fn colour_reference_f16(bits: u16) -> f32 {
let sign = if bits & 0x8000 != 0 { -1. } else { 1. };
let exponent = ((bits >> 10) & 0x1f) as i32;
let mantissa = (bits & 0x3ff) as f32;
sign * match exponent {
0 => mantissa * 2f32.powi(-24),
31 => f32::INFINITY,
_ => (1. + mantissa / 1024.) * 2f32.powi(exponent - 15),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn validates_descriptions() {
let ok = SurfaceDesc {
width: 64,
height: 32,
format: SurfaceFormat::Rgba16FloatLinearP3,
slots: 3,
};
assert!(ok.validate().is_ok());
for bad in [
SurfaceDesc { width: 0, ..ok },
SurfaceDesc {
height: 16385,
..ok
},
SurfaceDesc { slots: 1, ..ok },
SurfaceDesc { slots: 9, ..ok },
SurfaceDesc {
width: 16384,
height: 16384,
..ok
},
] {
assert!(bad.validate().is_err(), "{bad:?}");
}
}
}