#[cfg(target_os = "linux")]
fn main() {
demo::run();
}
#[cfg(not(target_os = "linux"))]
fn main() {
eprintln!(
"interop_linux is a Linux example: it exports a dma-buf and composites it through the \
window's renderer. Run it on Linux."
);
}
#[cfg(target_os = "linux")]
mod demo {
use std::cell::RefCell;
use std::rc::Rc;
use anyhow::{Context as _, Result};
use gpui::{
AnyElement, App, Bounds, Context, DmaBufHandle, GpuCanvasContext, IntoElement, Render,
SurfaceFormatKind, Window, WindowBounds, WindowOptions, div, gpu_canvas, prelude::*, px,
rgb, size,
};
use gpui_interop::{GpuDevice, SharedBuffer, Tiling, attach};
use gpui_wgpu::WgpuRenderer;
const TILE: u32 = 192;
const LINEAR_COLOUR: [f32; 4] = [0.85, 0.35, 0.13, 1.0];
const TILED_COLOUR: [f32; 4] = [0.13, 0.62, 0.58, 1.0];
pub fn run() {
gpui::application().run(|cx: &mut App| {
let bounds = Bounds::centered(None, size(px(720.), px(520.)), cx);
cx.open_window(
WindowOptions {
window_bounds: Some(WindowBounds::Windowed(bounds)),
..Default::default()
},
|_, cx| cx.new(|_| Showcase::new()),
)
.expect("a window to composite into");
cx.activate(true);
});
}
struct Showcase {
producer: Rc<RefCell<Option<Producer>>>,
captioned: bool,
}
impl Showcase {
fn new() -> Self {
Self {
producer: Rc::new(RefCell::new(None)),
captioned: false,
}
}
}
impl Render for Showcase {
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let origin = self
.producer
.borrow()
.as_ref()
.map(|producer| producer.origin);
if origin.is_none() && !self.captioned {
self.captioned = true;
cx.notify();
}
let device_line = match origin {
Some(origin) => format!("device: {origin}"),
None => "device: negotiating with the window's renderer…".to_owned(),
};
let tiles = tiles(self.producer.clone());
div()
.size_full()
.bg(rgb(0x101014))
.flex()
.flex_col()
.items_center()
.justify_center()
.gap_8()
.p_8()
.child(
div()
.flex()
.flex_col()
.items_center()
.gap_1()
.child(
div()
.text_2xl()
.text_color(rgb(0xffffff))
.child("External dma-buf surfaces"),
)
.child(
div()
.text_sm()
.text_color(rgb(0x9a9aa2))
.text_center()
.max_w(px(560.))
.child(
"gpui_interop · Linux · a producer's Vulkan image, exported as a \
dma-buf and sampled by the window's renderer — no CPU copy",
),
)
.child(div().text_sm().text_color(rgb(0x6f8fbf)).child(device_line)),
)
.child(div().flex().flex_wrap().justify_center().gap_6().children(tiles))
}
}
fn tiles(slot: Rc<RefCell<Option<Producer>>>) -> Vec<AnyElement> {
vec![
tile(
slot.clone(),
|producer| producer.linear.handle(),
"Linear · a plain buffer, always importable",
),
tile(
slot,
|producer| producer.tiled.handle(),
"Tiled · a driver modifier the renderer imports",
),
]
}
fn tile(
slot: Rc<RefCell<Option<Producer>>>,
pick: fn(&Producer) -> DmaBufHandle,
caption: &'static str,
) -> AnyElement {
panel(
gpu_canvas(move |gpu| {
let mut slot = slot.borrow_mut();
if slot.is_none() {
match Producer::build(gpu) {
Ok(producer) => *slot = Some(producer),
Err(error) => {
log::error!("interop_linux: cannot produce a dma-buf: {error:#}");
return;
}
}
}
let handle = pick(slot.as_ref().expect("the producer was just built"));
gpu.paint_surface(handle);
})
.bg(rgb(0x23232c))
.size_full(),
caption,
)
}
struct Producer {
origin: &'static str,
linear: SharedBuffer,
tiled: SharedBuffer,
_device: GpuDevice,
}
impl Producer {
fn build(gpu: &mut GpuCanvasContext) -> Result<Self> {
let attached = attach(gpu.try_device::<WgpuRenderer>())
.ok()
.and_then(|interop| interop.adapter().gpu_device());
let (device, origin) = match attached {
Some(device) => (device, "the renderer's GPU, via Adapter::gpu_device()"),
None => (
GpuDevice::open().context("opening a device of the producer's own")?,
"a device the producer opened itself",
),
};
let linear = cleared(&device, Tiling::Linear, LINEAR_COLOUR)?;
let tiled = cleared(&device, Tiling::Tiled, TILED_COLOUR)?;
Ok(Self {
origin,
linear,
tiled,
_device: device,
})
}
}
fn cleared(device: &GpuDevice, tiling: Tiling, colour: [f32; 4]) -> Result<SharedBuffer> {
let buffer = SharedBuffer::new(device, TILE, TILE, SurfaceFormatKind::bgra8(), tiling)
.context("allocating a shareable buffer")?;
buffer
.clear(device, colour)
.context("clearing the buffer")?;
Ok(buffer)
}
fn panel(content: impl IntoElement, caption: &str) -> AnyElement {
div()
.flex()
.flex_col()
.items_center()
.gap_2()
.w(px(240.))
.child(div().w(px(TILE as f32)).h(px(TILE as f32)).child(content))
.child(
div()
.text_color(rgb(0xd8d8d8))
.text_sm()
.text_center()
.child(caption.to_owned()),
)
.into_any_element()
}
}