1use gpui::{DevicePixels, Size, size};
32
33pub(crate) mod heavy;
34#[cfg_attr(
36 not(any(
37 target_os = "macos",
38 all(any(target_os = "linux", target_os = "windows"), feature = "wgpu")
39 )),
40 allow(dead_code)
41)]
42mod ring;
43pub use heavy::{HeavyEngine, HeavyStatus};
44
45#[cfg(target_os = "macos")]
46mod macos;
47#[cfg(target_os = "macos")]
48use macos as platform;
49#[cfg(target_os = "macos")]
50pub use macos::*;
51
52#[cfg(all(target_os = "linux", feature = "wgpu"))]
53mod linux;
54#[cfg(all(target_os = "linux", feature = "wgpu"))]
55use linux as platform;
56#[cfg(all(target_os = "linux", feature = "wgpu"))]
57pub use linux::*;
58
59#[cfg(all(target_os = "windows", feature = "wgpu"))]
60mod d3d;
61#[cfg(all(target_os = "windows", feature = "wgpu"))]
62use d3d as platform;
63#[cfg(all(target_os = "windows", feature = "wgpu"))]
64pub use d3d::*;
65
66#[cfg(any(
67 target_os = "macos",
68 all(any(target_os = "linux", target_os = "windows"), feature = "wgpu")
69))]
70mod surface;
71#[cfg(any(
72 target_os = "macos",
73 all(any(target_os = "linux", target_os = "windows"), feature = "wgpu")
74))]
75pub use surface::{EngineSurface, SurfaceSlot};
76
77#[cfg(all(any(target_os = "macos", target_os = "windows"), feature = "wgpu"))]
79pub use ::wgpu;
80#[cfg(all(target_os = "linux", feature = "wgpu"))]
82pub use gpui_wgpu::wgpu;
83
84#[derive(Clone, Copy, Debug, PartialEq, Eq)]
86pub enum SurfaceFormat {
87 Bgra8Srgb,
89 Rgba16FloatLinearP3,
94}
95impl SurfaceFormat {
96 pub fn bytes_per_pixel(self) -> u32 {
98 match self {
99 Self::Bgra8Srgb => 4,
100 Self::Rgba16FloatLinearP3 => 8,
101 }
102 }
103 pub fn color_mode(self, wide: bool) -> gpui::ImageColorMode {
106 match self {
107 Self::Bgra8Srgb => gpui::ImageColorMode::EncodedSrgb,
108 #[cfg(any(target_os = "macos", target_os = "windows"))]
109 Self::Rgba16FloatLinearP3 if wide => gpui::ImageColorMode::LinearP3Wide,
110 Self::Rgba16FloatLinearP3 => {
111 let _ = wide;
112 gpui::ImageColorMode::LinearP3Preview
113 }
114 }
115 }
116 pub fn supports_storage(self) -> bool {
118 matches!(self, Self::Rgba16FloatLinearP3)
119 }
120}
121
122#[derive(Clone, Copy, Debug, PartialEq, Eq)]
124pub struct SurfaceDesc {
125 pub width: u32,
126 pub height: u32,
127 pub format: SurfaceFormat,
128 pub slots: usize,
133}
134impl SurfaceDesc {
135 pub fn validate(&self) -> anyhow::Result<()> {
137 anyhow::ensure!(
138 (1..=16384).contains(&self.width) && (1..=16384).contains(&self.height),
139 "surface dimensions must be 1..=16384"
140 );
141 anyhow::ensure!(
142 (2..=8).contains(&self.slots),
143 "surface ring must hold 2..=8 slots"
144 );
145 let bytes = self.width as u64
146 * self.height as u64
147 * self.format.bytes_per_pixel() as u64
148 * self.slots as u64;
149 anyhow::ensure!(bytes <= 512 * 1024 * 1024, "surface ring exceeds 512 MiB");
150 Ok(())
151 }
152 pub fn extent(&self) -> Size<DevicePixels> {
154 size(
155 DevicePixels(self.width as i32),
156 DevicePixels(self.height as i32),
157 )
158 }
159}
160
161#[derive(Clone, Copy, Debug, PartialEq, Eq)]
163pub enum AcquireError {
164 WouldBlock,
167 Invalidated,
170}
171impl std::fmt::Display for AcquireError {
172 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
173 match self {
174 Self::WouldBlock => f.write_str("every surface slot is in use"),
175 Self::Invalidated => f.write_str("the surface was invalidated"),
176 }
177 }
178}
179impl std::error::Error for AcquireError {}
180
181#[derive(Debug)]
183pub struct Unsupported(pub String);
184impl std::fmt::Display for Unsupported {
185 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
186 write!(f, "engine surface unsupported: {}", self.0)
187 }
188}
189impl std::error::Error for Unsupported {}
190
191#[derive(Clone, Copy, Debug, Default, PartialEq)]
193pub struct SurfaceStats {
194 pub free: usize,
196 pub writing: usize,
198 pub published: usize,
201 pub presents: u64,
203 pub would_block: u64,
205 pub waiting: usize,
208 pub composited: u64,
210 pub unshown: u64,
213 pub composite_latency_ms: f32,
217}
218
219#[derive(Clone, Copy, Debug, PartialEq, Eq)]
221pub enum Transport {
222 ZeroCopy,
224}
225
226#[cfg(all(
228 test,
229 feature = "wgpu",
230 any(target_os = "macos", target_os = "linux", target_os = "windows")
231))]
232pub(crate) mod test_engine {
233 use crate::wgpu;
234
235 pub fn compute_fill(
238 device: &wgpu::Device,
239 texture: &wgpu::Texture,
240 color: [f32; 4],
241 ) -> wgpu::CommandBuffer {
242 let [r, g, b, a] = color;
243 let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
244 label: Some("engine_compute_fill"),
245 source: wgpu::ShaderSource::Wgsl(
246 format!(
247 "@group(0) @binding(0) var slot: texture_storage_2d<rgba16float, write>;
248 @compute @workgroup_size(8, 8)
249 fn main(@builtin(global_invocation_id) id: vec3<u32>) {{
250 let size = textureDimensions(slot);
251 if (id.x < size.x && id.y < size.y) {{
252 textureStore(slot, vec2<i32>(id.xy), vec4<f32>({r:?}, {g:?}, {b:?}, {a:?}));
253 }}
254 }}"
255 )
256 .into(),
257 ),
258 });
259 let pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
260 label: Some("engine_compute_fill"),
261 layout: None,
262 module: &shader,
263 entry_point: Some("main"),
264 compilation_options: Default::default(),
265 cache: None,
266 });
267 let view = texture.create_view(&Default::default());
268 let bind = device.create_bind_group(&wgpu::BindGroupDescriptor {
269 label: Some("engine_compute_fill"),
270 layout: &pipeline.get_bind_group_layout(0),
271 entries: &[wgpu::BindGroupEntry {
272 binding: 0,
273 resource: wgpu::BindingResource::TextureView(&view),
274 }],
275 });
276 let mut encoder = device.create_command_encoder(&Default::default());
277 {
278 let mut pass = encoder.begin_compute_pass(&Default::default());
279 pass.set_pipeline(&pipeline);
280 pass.set_bind_group(0, &bind, &[]);
281 let size = texture.size();
282 pass.dispatch_workgroups(size.width.div_ceil(8), size.height.div_ceil(8), 1);
283 }
284 encoder.finish()
285 }
286}
287
288#[cfg(test)]
292#[cfg_attr(
294 not(any(
295 target_os = "macos",
296 all(any(target_os = "linux", target_os = "windows"), feature = "wgpu")
297 )),
298 allow(dead_code)
299)]
300pub(crate) mod colour_reference {
301 pub const STRIPE: u32 = 8;
303 pub const HEIGHT: u32 = 8;
304
305 pub const PATCHES: [(&str, [f32; 4]); 5] = [
308 ("near-black", [0.001953125, 0.001953125, 0.001953125, 1.]),
309 ("P3 green (outside sRGB)", [0., 1., 0., 1.]),
310 ("P3 red (outside sRGB)", [1., 0., 0., 1.]),
311 ("half alpha", [0.25, 0.125, 0., 0.5]),
312 ("mid grey", [0.5, 0.5, 0.5, 1.]),
313 ];
314
315 pub const SRGB_CONTROLS: [(&str, [u8; 4]); 3] = [
317 ("sRGB colour", [128, 64, 200, 255]),
318 ("sRGB near-black", [3, 3, 3, 255]),
319 ("sRGB half alpha", [255, 128, 0, 128]),
320 ];
321
322 pub fn width() -> u32 {
323 PATCHES.len() as u32 * STRIPE
324 }
325
326 pub fn centre(i: usize) -> u32 {
328 i as u32 * STRIPE + STRIPE / 2
329 }
330
331 pub fn f16_bits(value: f32) -> u16 {
333 let bits = value.to_bits();
334 let sign = ((bits >> 16) & 0x8000) as u16;
335 let exponent = ((bits >> 23) & 0xff) as i32 - 127 + 15;
336 let mantissa = bits & 0x7f_ffff;
337 if value == 0. {
338 return sign;
339 }
340 if exponent >= 31 {
341 return sign | 0x7c00;
342 }
343 if exponent <= 0 {
344 let shift = 14 - exponent;
345 let m = (mantissa | 0x80_0000) as u64;
346 let half = 1u64 << (shift - 1);
347 let rounded = (m + half - 1 + ((m >> shift) & 1)) >> shift;
348 return sign | rounded as u16;
349 }
350 let rounded = (mantissa + 0xfff + ((mantissa >> 13) & 1)) >> 13;
351 sign | (((exponent as u32) << 10) + rounded) as u16
352 }
353
354 pub fn strip_rgba16f() -> Vec<u8> {
356 let mut bytes = Vec::new();
357 for _ in 0..HEIGHT {
358 for (_, patch) in PATCHES {
359 for _ in 0..STRIPE {
360 for channel in patch {
361 bytes.extend_from_slice(&f16_bits(channel).to_ne_bytes());
362 }
363 }
364 }
365 }
366 bytes
367 }
368
369 pub fn controls_bgra8() -> Vec<u8> {
371 let mut bytes = Vec::new();
372 for _ in 0..HEIGHT {
373 for (_, [r, g, b, a]) in SRGB_CONTROLS {
374 for _ in 0..STRIPE {
375 bytes.extend_from_slice(&[b, g, r, a]);
376 }
377 }
378 }
379 bytes
380 }
381
382 const P3_TO_SRGB: [[f64; 3]; 3] = [
384 [1.224_940_2, -0.224_940_2, 0.],
385 [-0.042_056_95, 1.042_056_95, 0.],
386 [-0.019_637_55, -0.078_636_05, 1.098_273_6],
387 ];
388
389 fn encode(linear: f64) -> f64 {
390 let v = linear.clamp(0., 1.);
391 if v <= 0.003_130_8 {
392 v * 12.92
393 } else {
394 1.055 * v.powf(1. / 2.4) - 0.055
395 }
396 }
397
398 pub fn preview(patch: [f32; 4], opacity: f32) -> [f64; 4] {
402 let alpha = patch[3] as f64;
403 let straight: Vec<f64> = (0..3)
404 .map(|c| {
405 if alpha > 0. {
406 patch[c] as f64 / alpha
407 } else {
408 0.
409 }
410 })
411 .collect();
412 let coverage = alpha * opacity as f64;
413 let mut out = [0.; 4];
414 for (row, value) in P3_TO_SRGB.iter().zip(out.iter_mut()) {
415 let linear: f64 = row.iter().zip(&straight).map(|(m, v)| m * v).sum();
416 *value = encode(linear) * 255. * coverage;
417 }
418 out[3] = coverage * 255.;
419 out
420 }
421
422 pub fn control(rgba: [u8; 4], opacity: f32) -> [f64; 4] {
424 let coverage = rgba[3] as f64 / 255. * opacity as f64;
425 [
426 rgba[0] as f64 * coverage,
427 rgba[1] as f64 * coverage,
428 rgba[2] as f64 * coverage,
429 coverage * 255.,
430 ]
431 }
432
433 pub fn distance(actual: [u8; 4], expected: [f64; 4]) -> f64 {
438 (0..3)
439 .map(|c| (actual[c] as f64 - expected[c]).abs())
440 .fold(0., f64::max)
441 }
442}
443
444#[cfg(all(test, target_os = "macos"))]
446pub(crate) fn colour_reference_f16(bits: u16) -> f32 {
447 let sign = if bits & 0x8000 != 0 { -1. } else { 1. };
448 let exponent = ((bits >> 10) & 0x1f) as i32;
449 let mantissa = (bits & 0x3ff) as f32;
450 sign * match exponent {
451 0 => mantissa * 2f32.powi(-24),
452 31 => f32::INFINITY,
453 _ => (1. + mantissa / 1024.) * 2f32.powi(exponent - 15),
454 }
455}
456
457#[cfg(test)]
458mod tests {
459 use super::*;
460 #[test]
461 fn validates_descriptions() {
462 let ok = SurfaceDesc {
463 width: 64,
464 height: 32,
465 format: SurfaceFormat::Rgba16FloatLinearP3,
466 slots: 3,
467 };
468 assert!(ok.validate().is_ok());
469 for bad in [
470 SurfaceDesc { width: 0, ..ok },
471 SurfaceDesc {
472 height: 16385,
473 ..ok
474 },
475 SurfaceDesc { slots: 1, ..ok },
476 SurfaceDesc { slots: 9, ..ok },
477 SurfaceDesc {
478 width: 16384,
479 height: 16384,
480 ..ok
481 },
482 ] {
483 assert!(bad.validate().is_err(), "{bad:?}");
484 }
485 }
486}