gpui: Add wgpu Surface element and GPU device requirements API for Linux (#39)
This commit is contained in:
@@ -1121,6 +1121,14 @@ impl App {
|
||||
self.platform.window_stack()
|
||||
}
|
||||
|
||||
/// Register additional GPU device requirements (extra features and/or
|
||||
/// limits) before opening any windows. The `Box` must contain a
|
||||
/// `gpui_wgpu::WgpuDeviceRequirements`.
|
||||
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
|
||||
pub fn set_gpu_requirements(&self, requirements: Box<dyn std::any::Any>) {
|
||||
self.platform.set_gpu_requirements(requirements);
|
||||
}
|
||||
|
||||
/// Returns a handle to the window that is currently focused at the platform level, if one exists.
|
||||
pub fn active_window(&self) -> Option<AnyWindowHandle> {
|
||||
self.platform.active_window()
|
||||
|
||||
@@ -2,16 +2,55 @@ use crate::{
|
||||
App, Bounds, Element, ElementId, GlobalElementId, InspectorElementId, IntoElement, LayoutId,
|
||||
ObjectFit, Pixels, Style, StyleRefinement, Styled, Window,
|
||||
};
|
||||
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
|
||||
use crate::{DevicePixels, Size};
|
||||
#[cfg(target_os = "macos")]
|
||||
use core_video::pixel_buffer::CVPixelBuffer;
|
||||
use refineable::Refineable;
|
||||
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
|
||||
use std::sync::Arc;
|
||||
|
||||
/// A source of a surface's content.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum SurfaceSource {
|
||||
/// A macOS image buffer from CoreVideo
|
||||
#[cfg(target_os = "macos")]
|
||||
Surface(CVPixelBuffer),
|
||||
/// A GPU texture handle (type-erased to avoid depending on wgpu)
|
||||
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
|
||||
Texture {
|
||||
/// The GPU texture, type-erased (expected to be `Arc<wgpu::Texture>`)
|
||||
texture: Arc<dyn std::any::Any + Send + Sync>,
|
||||
/// Dimensions of the texture in device pixels
|
||||
size: Size<DevicePixels>,
|
||||
},
|
||||
}
|
||||
|
||||
impl Clone for SurfaceSource {
|
||||
fn clone(&self) -> Self {
|
||||
match self {
|
||||
#[cfg(target_os = "macos")]
|
||||
SurfaceSource::Surface(buf) => SurfaceSource::Surface(buf.clone()),
|
||||
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
|
||||
SurfaceSource::Texture { texture, size } => SurfaceSource::Texture {
|
||||
texture: Arc::clone(texture),
|
||||
size: *size,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for SurfaceSource {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
#[cfg(target_os = "macos")]
|
||||
SurfaceSource::Surface(buf) => f.debug_tuple("Surface").field(buf).finish(),
|
||||
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
|
||||
SurfaceSource::Texture { size, .. } => f
|
||||
.debug_struct("Texture")
|
||||
.field("size", size)
|
||||
.finish_non_exhaustive(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
@@ -29,7 +68,6 @@ pub struct Surface {
|
||||
}
|
||||
|
||||
/// Create a new surface element.
|
||||
#[cfg(target_os = "macos")]
|
||||
pub fn surface(source: impl Into<SurfaceSource>) -> Surface {
|
||||
Surface {
|
||||
source: source.into(),
|
||||
@@ -86,10 +124,10 @@ impl Element for Surface {
|
||||
&mut self,
|
||||
_global_id: Option<&GlobalElementId>,
|
||||
_inspector_id: Option<&InspectorElementId>,
|
||||
#[cfg_attr(not(target_os = "macos"), allow(unused_variables))] bounds: Bounds<Pixels>,
|
||||
bounds: Bounds<Pixels>,
|
||||
_: &mut Self::RequestLayoutState,
|
||||
_: &mut Self::PrepaintState,
|
||||
#[cfg_attr(not(target_os = "macos"), allow(unused_variables))] window: &mut Window,
|
||||
window: &mut Window,
|
||||
_: &mut App,
|
||||
) {
|
||||
match &self.source {
|
||||
@@ -100,8 +138,11 @@ impl Element for Surface {
|
||||
// TODO: Add support for corner_radii
|
||||
window.paint_surface(new_bounds, surface.clone());
|
||||
}
|
||||
#[allow(unreachable_patterns)]
|
||||
_ => {}
|
||||
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
|
||||
SurfaceSource::Texture { texture, size } => {
|
||||
let new_bounds = self.object_fit.get_bounds(bounds, *size);
|
||||
window.paint_surface(new_bounds, Arc::clone(texture), *size);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -242,6 +242,12 @@ pub trait Platform: 'static {
|
||||
fn keyboard_layout(&self) -> Box<dyn PlatformKeyboardLayout>;
|
||||
fn keyboard_mapper(&self) -> Rc<dyn PlatformKeyboardMapper>;
|
||||
fn on_keyboard_layout_change(&self, callback: Box<dyn FnMut()>);
|
||||
|
||||
/// Register additional GPU device requirements (features, limits) before
|
||||
/// the first window is opened. The concrete type inside the `Box` must be
|
||||
/// `gpui_wgpu::WgpuDeviceRequirements`.
|
||||
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
|
||||
fn set_gpu_requirements(&self, _requirements: Box<dyn std::any::Any>) {}
|
||||
}
|
||||
|
||||
/// A handle to a platform's display, e.g. a monitor or laptop screen.
|
||||
@@ -706,6 +712,13 @@ pub trait PlatformWindow: HasWindowHandle + HasDisplayHandle {
|
||||
fn set_client_inset(&self, _inset: Pixels) {}
|
||||
fn gpu_specs(&self) -> Option<GpuSpecs>;
|
||||
|
||||
/// Returns the GPU context for this window's renderer.
|
||||
/// The returned `Box` contains `(Arc<wgpu::Device>, Arc<wgpu::Queue>)`.
|
||||
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
|
||||
fn gpu_context(&self) -> Option<Box<dyn std::any::Any>> {
|
||||
None
|
||||
}
|
||||
|
||||
fn update_ime_position(&self, _bounds: Bounds<Pixels>);
|
||||
|
||||
fn play_system_bell(&self) {}
|
||||
|
||||
@@ -723,6 +723,10 @@ pub struct PaintSurface {
|
||||
pub content_mask: ContentMask<ScaledPixels>,
|
||||
#[cfg(target_os = "macos")]
|
||||
pub image_buffer: core_video::pixel_buffer::CVPixelBuffer,
|
||||
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
|
||||
pub texture: std::sync::Arc<dyn std::any::Any + Send + Sync>,
|
||||
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
|
||||
pub texture_size: Size<crate::DevicePixels>,
|
||||
}
|
||||
|
||||
impl From<PaintSurface> for Primitive {
|
||||
|
||||
@@ -4086,6 +4086,32 @@ impl Window {
|
||||
});
|
||||
}
|
||||
|
||||
/// Paint a surface into the scene for the next frame at the current z-index.
|
||||
///
|
||||
/// This method should only be called as part of the paint phase of element drawing.
|
||||
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
|
||||
pub fn paint_surface(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
texture: std::sync::Arc<dyn std::any::Any + Send + Sync>,
|
||||
texture_size: Size<DevicePixels>,
|
||||
) {
|
||||
use crate::PaintSurface;
|
||||
|
||||
self.invalidator.debug_assert_paint();
|
||||
|
||||
let scale_factor = self.scale_factor();
|
||||
let bounds = bounds.scale(scale_factor);
|
||||
let content_mask = self.content_mask().scale(scale_factor);
|
||||
self.next_frame.scene.insert_primitive(PaintSurface {
|
||||
order: 0,
|
||||
bounds,
|
||||
content_mask,
|
||||
texture,
|
||||
texture_size,
|
||||
});
|
||||
}
|
||||
|
||||
/// Removes an image from the sprite atlas.
|
||||
pub fn drop_image(&mut self, data: Arc<RenderImage>) -> Result<()> {
|
||||
for frame_index in 0..data.frame_count() {
|
||||
@@ -5386,6 +5412,13 @@ impl Window {
|
||||
self.platform_window.gpu_specs()
|
||||
}
|
||||
|
||||
/// Returns the GPU context (device + queue) if available.
|
||||
/// The returned `Box` contains `(Arc<wgpu::Device>, Arc<wgpu::Queue>)`.
|
||||
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
|
||||
pub fn gpu_context(&self) -> Option<Box<dyn std::any::Any>> {
|
||||
self.platform_window.gpu_context()
|
||||
}
|
||||
|
||||
/// Perform titlebar double-click action.
|
||||
/// This is macOS specific.
|
||||
pub fn titlebar_double_click(&self) {
|
||||
|
||||
@@ -92,6 +92,7 @@ pub(crate) trait LinuxClient {
|
||||
fn read_from_clipboard(&self) -> Option<ClipboardItem>;
|
||||
fn active_window(&self) -> Option<AnyWindowHandle>;
|
||||
fn window_stack(&self) -> Option<Vec<AnyWindowHandle>>;
|
||||
fn set_gpu_requirements(&self, _requirements: Box<dyn std::any::Any>) {}
|
||||
fn run(&self);
|
||||
|
||||
#[cfg(any(feature = "wayland", feature = "x11"))]
|
||||
@@ -316,6 +317,10 @@ impl<P: LinuxClient + 'static> Platform for LinuxPlatform<P> {
|
||||
self.inner.open_window(handle, options)
|
||||
}
|
||||
|
||||
fn set_gpu_requirements(&self, requirements: Box<dyn std::any::Any>) {
|
||||
self.inner.set_gpu_requirements(requirements);
|
||||
}
|
||||
|
||||
fn open_url(&self, url: &str) {
|
||||
self.inner.open_uri(url);
|
||||
}
|
||||
|
||||
@@ -217,6 +217,7 @@ pub(crate) struct WaylandClientState {
|
||||
globals: Globals,
|
||||
pub gpu_context: GpuContext,
|
||||
pub compositor_gpu: Option<CompositorGpuHint>,
|
||||
pub gpu_requirements: Option<gpui_wgpu::WgpuDeviceRequirements>,
|
||||
wl_seat: wl_seat::WlSeat, // TODO: Multi seat support
|
||||
wl_pointer: Option<wl_pointer::WlPointer>,
|
||||
pinch_gesture: Option<zwp_pointer_gesture_pinch_v1::ZwpPointerGesturePinchV1>,
|
||||
@@ -667,6 +668,7 @@ impl WaylandClient {
|
||||
globals,
|
||||
gpu_context,
|
||||
compositor_gpu,
|
||||
gpu_requirements: None,
|
||||
wl_seat: seat,
|
||||
wl_pointer: None,
|
||||
wl_keyboard: None,
|
||||
@@ -800,6 +802,14 @@ impl LinuxClient for WaylandClient {
|
||||
sources_rx
|
||||
}
|
||||
|
||||
fn set_gpu_requirements(&self, requirements: Box<dyn std::any::Any>) {
|
||||
if let Ok(reqs) = requirements.downcast::<gpui_wgpu::WgpuDeviceRequirements>() {
|
||||
self.0.borrow_mut().gpu_requirements = Some(*reqs);
|
||||
} else {
|
||||
log::warn!("set_gpu_requirements: unexpected type, expected WgpuDeviceRequirements");
|
||||
}
|
||||
}
|
||||
|
||||
fn open_window(
|
||||
&self,
|
||||
handle: AnyWindowHandle,
|
||||
@@ -820,11 +830,13 @@ impl LinuxClient for WaylandClient {
|
||||
|
||||
let appearance = state.common.appearance;
|
||||
let compositor_gpu = state.compositor_gpu.take();
|
||||
let gpu_requirements = state.gpu_requirements.clone();
|
||||
let (window, surface_id) = WaylandWindow::new(
|
||||
handle,
|
||||
state.globals.clone(),
|
||||
state.gpu_context.clone(),
|
||||
compositor_gpu,
|
||||
gpu_requirements,
|
||||
WaylandClientStatePtr(Rc::downgrade(&self.0)),
|
||||
params,
|
||||
appearance,
|
||||
|
||||
@@ -328,6 +328,7 @@ impl WaylandWindowState {
|
||||
globals: Globals,
|
||||
gpu_context: gpui_wgpu::GpuContext,
|
||||
compositor_gpu: Option<CompositorGpuHint>,
|
||||
gpu_requirements: Option<gpui_wgpu::WgpuDeviceRequirements>,
|
||||
options: WindowParams,
|
||||
parent: Option<WaylandWindowStatePtr>,
|
||||
) -> anyhow::Result<Self> {
|
||||
@@ -350,7 +351,13 @@ impl WaylandWindowState {
|
||||
// Prefer Mailbox to avoid blocking. Falls back to FIFO if Mailbox is unsupported.
|
||||
preferred_present_mode: Some(wgpu::PresentMode::Mailbox),
|
||||
};
|
||||
WgpuRenderer::new(gpu_context, &raw_window, config, compositor_gpu)?
|
||||
WgpuRenderer::new(
|
||||
gpu_context,
|
||||
&raw_window,
|
||||
config,
|
||||
compositor_gpu,
|
||||
gpu_requirements,
|
||||
)?
|
||||
};
|
||||
|
||||
if let WaylandSurfaceState::Xdg(ref xdg_state) = surface_state {
|
||||
@@ -514,6 +521,7 @@ impl WaylandWindow {
|
||||
globals: Globals,
|
||||
gpu_context: gpui_wgpu::GpuContext,
|
||||
compositor_gpu: Option<CompositorGpuHint>,
|
||||
gpu_requirements: Option<gpui_wgpu::WgpuDeviceRequirements>,
|
||||
client: WaylandClientStatePtr,
|
||||
params: WindowParams,
|
||||
appearance: WindowAppearance,
|
||||
@@ -544,6 +552,7 @@ impl WaylandWindow {
|
||||
globals,
|
||||
gpu_context,
|
||||
compositor_gpu,
|
||||
gpu_requirements,
|
||||
params,
|
||||
parent,
|
||||
)?)),
|
||||
@@ -1513,6 +1522,11 @@ impl PlatformWindow for WaylandWindow {
|
||||
self.borrow().renderer.gpu_specs().into()
|
||||
}
|
||||
|
||||
fn gpu_context(&self) -> Option<Box<dyn std::any::Any>> {
|
||||
let (device, queue) = self.borrow().renderer.gpu_context();
|
||||
Some(Box::new((device, queue)))
|
||||
}
|
||||
|
||||
fn play_system_bell(&self) {
|
||||
let state = self.borrow();
|
||||
let surface = if state.surface_state.toplevel().is_some() {
|
||||
|
||||
@@ -180,6 +180,7 @@ pub struct X11ClientState {
|
||||
|
||||
pub(crate) gpu_context: GpuContext,
|
||||
pub(crate) compositor_gpu: Option<CompositorGpuHint>,
|
||||
pub(crate) gpu_requirements: Option<gpui_wgpu::WgpuDeviceRequirements>,
|
||||
|
||||
pub(crate) scale_factor: f32,
|
||||
|
||||
@@ -523,6 +524,7 @@ impl X11Client {
|
||||
pinch_scale: 1.0,
|
||||
gpu_context: Rc::new(RefCell::new(None)),
|
||||
compositor_gpu,
|
||||
gpu_requirements: None,
|
||||
scale_factor,
|
||||
|
||||
xkb_context,
|
||||
@@ -1586,6 +1588,14 @@ impl LinuxClient for X11Client {
|
||||
gpui::scap_screen_capture::scap_screen_sources(&self.0.borrow().common.foreground_executor)
|
||||
}
|
||||
|
||||
fn set_gpu_requirements(&self, requirements: Box<dyn std::any::Any>) {
|
||||
if let Ok(reqs) = requirements.downcast::<gpui_wgpu::WgpuDeviceRequirements>() {
|
||||
self.0.borrow_mut().gpu_requirements = Some(*reqs);
|
||||
} else {
|
||||
log::warn!("set_gpu_requirements: unexpected type, expected WgpuDeviceRequirements");
|
||||
}
|
||||
}
|
||||
|
||||
fn open_window(
|
||||
&self,
|
||||
handle: AnyWindowHandle,
|
||||
@@ -1608,6 +1618,7 @@ impl LinuxClient for X11Client {
|
||||
let scale_factor = state.scale_factor;
|
||||
let appearance = state.common.appearance;
|
||||
let compositor_gpu = state.compositor_gpu.take();
|
||||
let gpu_requirements = state.gpu_requirements.clone();
|
||||
let supports_xinput_gestures = state.supports_xinput_gestures;
|
||||
let is_bgr = state
|
||||
.resource_database
|
||||
@@ -1619,6 +1630,7 @@ impl LinuxClient for X11Client {
|
||||
state.common.foreground_executor.clone(),
|
||||
state.gpu_context.clone(),
|
||||
compositor_gpu,
|
||||
gpu_requirements,
|
||||
params,
|
||||
&xcb_connection,
|
||||
client_side_decorations_supported,
|
||||
|
||||
@@ -416,6 +416,7 @@ impl X11WindowState {
|
||||
executor: ForegroundExecutor,
|
||||
gpu_context: gpui_wgpu::GpuContext,
|
||||
compositor_gpu: Option<CompositorGpuHint>,
|
||||
gpu_requirements: Option<gpui_wgpu::WgpuDeviceRequirements>,
|
||||
params: WindowParams,
|
||||
xcb: &Rc<XCBConnection>,
|
||||
client_side_decorations_supported: bool,
|
||||
@@ -724,7 +725,13 @@ impl X11WindowState {
|
||||
transparent: false,
|
||||
preferred_present_mode: None,
|
||||
};
|
||||
WgpuRenderer::new(gpu_context, &raw_window, config, compositor_gpu)?
|
||||
WgpuRenderer::new(
|
||||
gpu_context,
|
||||
&raw_window,
|
||||
config,
|
||||
compositor_gpu,
|
||||
gpu_requirements,
|
||||
)?
|
||||
};
|
||||
|
||||
renderer.set_subpixel_layout(is_bgr);
|
||||
@@ -878,6 +885,7 @@ impl X11Window {
|
||||
executor: ForegroundExecutor,
|
||||
gpu_context: gpui_wgpu::GpuContext,
|
||||
compositor_gpu: Option<CompositorGpuHint>,
|
||||
gpu_requirements: Option<gpui_wgpu::WgpuDeviceRequirements>,
|
||||
params: WindowParams,
|
||||
xcb: &Rc<XCBConnection>,
|
||||
client_side_decorations_supported: bool,
|
||||
@@ -897,6 +905,7 @@ impl X11Window {
|
||||
executor,
|
||||
gpu_context,
|
||||
compositor_gpu,
|
||||
gpu_requirements,
|
||||
params,
|
||||
xcb,
|
||||
client_side_decorations_supported,
|
||||
@@ -1887,6 +1896,11 @@ impl PlatformWindow for X11Window {
|
||||
self.0.state.borrow().renderer.gpu_specs().into()
|
||||
}
|
||||
|
||||
fn gpu_context(&self) -> Option<Box<dyn std::any::Any>> {
|
||||
let (device, queue) = self.0.state.borrow().renderer.gpu_context();
|
||||
Some(Box::new((device, queue)))
|
||||
}
|
||||
|
||||
fn play_system_bell(&self) {
|
||||
// Volume 0% means don't increase or decrease from system volume
|
||||
let _ = self.0.xcb.bell(0);
|
||||
|
||||
@@ -1320,16 +1320,8 @@ struct SurfaceParams {
|
||||
}
|
||||
|
||||
@group(1) @binding(0) var<uniform> surface_locals: SurfaceParams;
|
||||
@group(1) @binding(1) var t_y: texture_2d<f32>;
|
||||
@group(1) @binding(2) var t_cb_cr: texture_2d<f32>;
|
||||
@group(1) @binding(3) var s_surface: sampler;
|
||||
|
||||
const ycbcr_to_RGB = mat4x4<f32>(
|
||||
vec4<f32>( 1.0000f, 1.0000f, 1.0000f, 0.0),
|
||||
vec4<f32>( 0.0000f, -0.3441f, 1.7720f, 0.0),
|
||||
vec4<f32>( 1.4020f, -0.7141f, 0.0000f, 0.0),
|
||||
vec4<f32>(-0.7010f, 0.5291f, -0.8860f, 1.0),
|
||||
);
|
||||
@group(1) @binding(1) var t_surface: texture_2d<f32>;
|
||||
@group(1) @binding(2) var s_surface: sampler;
|
||||
|
||||
struct SurfaceVarying {
|
||||
@builtin(position) position: vec4<f32>,
|
||||
@@ -1350,15 +1342,9 @@ fn vs_surface(@builtin(vertex_index) vertex_id: u32) -> SurfaceVarying {
|
||||
|
||||
@fragment
|
||||
fn fs_surface(input: SurfaceVarying) -> @location(0) vec4<f32> {
|
||||
// Alpha clip after using the derivatives.
|
||||
if (any(input.clip_distances < vec4<f32>(0.0))) {
|
||||
return vec4<f32>(0.0);
|
||||
}
|
||||
|
||||
let y_cb_cr = vec4<f32>(
|
||||
textureSampleLevel(t_y, s_surface, input.texture_position, 0.0).r,
|
||||
textureSampleLevel(t_cb_cr, s_surface, input.texture_position, 0.0).rg,
|
||||
1.0);
|
||||
|
||||
return ycbcr_to_RGB * y_cb_cr;
|
||||
return textureSampleLevel(t_surface, s_surface, input.texture_position, 0.0);
|
||||
}
|
||||
|
||||
@@ -22,14 +22,29 @@ pub struct CompositorGpuHint {
|
||||
pub device_id: u32,
|
||||
}
|
||||
|
||||
/// Extra wgpu features and limits that an application can request on top of
|
||||
/// gpui's baseline. Pass an instance to the platform via
|
||||
/// [`gpui::App::set_gpu_requirements`] *before* opening any windows.
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct WgpuDeviceRequirements {
|
||||
/// Additional [`wgpu::Features`] to enable. These are OR-ed with gpui's
|
||||
/// own required features.
|
||||
pub features: wgpu::Features,
|
||||
/// Additional [`wgpu::Limits`] to request. Each field is merged by taking
|
||||
/// `max(gpui_limit, app_limit)` for upper-bound limits and
|
||||
/// `min(gpui_limit, app_limit)` for alignment/lower-bound limits.
|
||||
pub limits: wgpu::Limits,
|
||||
}
|
||||
|
||||
impl WgpuContext {
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
pub fn new(
|
||||
instance: wgpu::Instance,
|
||||
surface: &wgpu::Surface<'_>,
|
||||
compositor_gpu: Option<CompositorGpuHint>,
|
||||
extra_requirements: Option<&WgpuDeviceRequirements>,
|
||||
) -> anyhow::Result<Self> {
|
||||
Self::new_with_options(instance, surface, compositor_gpu, false)
|
||||
Self::new_with_options(instance, surface, compositor_gpu, false, extra_requirements)
|
||||
}
|
||||
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
@@ -37,8 +52,9 @@ impl WgpuContext {
|
||||
instance: wgpu::Instance,
|
||||
surface: &wgpu::Surface<'_>,
|
||||
compositor_gpu: Option<CompositorGpuHint>,
|
||||
extra_requirements: Option<&WgpuDeviceRequirements>,
|
||||
) -> anyhow::Result<Self> {
|
||||
Self::new_with_options(instance, surface, compositor_gpu, true)
|
||||
Self::new_with_options(instance, surface, compositor_gpu, true, extra_requirements)
|
||||
}
|
||||
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
@@ -47,6 +63,7 @@ impl WgpuContext {
|
||||
surface: &wgpu::Surface<'_>,
|
||||
compositor_gpu: Option<CompositorGpuHint>,
|
||||
reject_software: bool,
|
||||
extra_requirements: Option<&WgpuDeviceRequirements>,
|
||||
) -> anyhow::Result<Self> {
|
||||
let device_id_filter = match std::env::var("ZED_DEVICE_ID") {
|
||||
Ok(val) => parse_pci_id(&val)
|
||||
@@ -69,6 +86,7 @@ impl WgpuContext {
|
||||
surface,
|
||||
compositor_gpu.as_ref(),
|
||||
reject_software,
|
||||
extra_requirements,
|
||||
))?;
|
||||
|
||||
let device_lost = Arc::new(AtomicBool::new(false));
|
||||
@@ -126,7 +144,7 @@ impl WgpuContext {
|
||||
|
||||
let device_lost = Arc::new(AtomicBool::new(false));
|
||||
let (device, queue, dual_source_blending, color_texture_format) =
|
||||
Self::create_device(&adapter).await?;
|
||||
Self::create_device(&adapter, None).await?;
|
||||
|
||||
Ok(Self {
|
||||
instance,
|
||||
@@ -141,6 +159,7 @@ impl WgpuContext {
|
||||
|
||||
async fn create_device(
|
||||
adapter: &wgpu::Adapter,
|
||||
extra_requirements: Option<&WgpuDeviceRequirements>,
|
||||
) -> anyhow::Result<(wgpu::Device, wgpu::Queue, bool, TextureFormat)> {
|
||||
let dual_source_blending = adapter
|
||||
.features()
|
||||
@@ -158,13 +177,21 @@ impl WgpuContext {
|
||||
|
||||
let color_atlas_texture_format = Self::select_color_texture_format(adapter)?;
|
||||
|
||||
let mut required_limits = wgpu::Limits::downlevel_defaults()
|
||||
.using_resolution(adapter.limits())
|
||||
.using_alignment(adapter.limits());
|
||||
|
||||
// Merge application-requested requirements.
|
||||
if let Some(reqs) = extra_requirements {
|
||||
required_features |= reqs.features;
|
||||
required_limits = required_limits.or_better_values_from(&reqs.limits);
|
||||
}
|
||||
|
||||
let (device, queue) = adapter
|
||||
.request_device(&wgpu::DeviceDescriptor {
|
||||
label: Some("gpui_device"),
|
||||
required_features,
|
||||
required_limits: wgpu::Limits::downlevel_defaults()
|
||||
.using_resolution(adapter.limits())
|
||||
.using_alignment(adapter.limits()),
|
||||
required_limits,
|
||||
memory_hints: wgpu::MemoryHints::MemoryUsage,
|
||||
trace: wgpu::Trace::Off,
|
||||
experimental_features: wgpu::ExperimentalFeatures::disabled(),
|
||||
@@ -218,6 +245,7 @@ impl WgpuContext {
|
||||
surface: &wgpu::Surface<'_>,
|
||||
compositor_gpu: Option<&CompositorGpuHint>,
|
||||
reject_software: bool,
|
||||
extra_requirements: Option<&WgpuDeviceRequirements>,
|
||||
) -> anyhow::Result<(
|
||||
wgpu::Adapter,
|
||||
wgpu::Device,
|
||||
@@ -319,7 +347,7 @@ impl WgpuContext {
|
||||
|
||||
log::info!("Testing adapter: {} ({:?})...", info.name, info.backend);
|
||||
|
||||
match Self::try_adapter_with_surface(&adapter, surface).await {
|
||||
match Self::try_adapter_with_surface(&adapter, surface, extra_requirements).await {
|
||||
Ok((device, queue, dual_source_blending, color_atlas_texture_format)) => {
|
||||
log::info!(
|
||||
"Selected GPU (passed configuration test): {} ({:?})",
|
||||
@@ -354,6 +382,7 @@ impl WgpuContext {
|
||||
async fn try_adapter_with_surface(
|
||||
adapter: &wgpu::Adapter,
|
||||
surface: &wgpu::Surface<'_>,
|
||||
extra_requirements: Option<&WgpuDeviceRequirements>,
|
||||
) -> anyhow::Result<(wgpu::Device, wgpu::Queue, bool, TextureFormat)> {
|
||||
let caps = surface.get_capabilities(adapter);
|
||||
if caps.formats.is_empty() {
|
||||
@@ -364,7 +393,7 @@ impl WgpuContext {
|
||||
}
|
||||
|
||||
let (device, queue, dual_source_blending, color_atlas_texture_format) =
|
||||
Self::create_device(adapter).await?;
|
||||
Self::create_device(adapter, extra_requirements).await?;
|
||||
let error_scope = device.push_error_scope(wgpu::ErrorFilter::Validation);
|
||||
|
||||
let test_config = wgpu::SurfaceConfiguration {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use crate::{CompositorGpuHint, WgpuAtlas, WgpuContext};
|
||||
use crate::{CompositorGpuHint, WgpuAtlas, WgpuContext, WgpuDeviceRequirements};
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
use gpui::{
|
||||
AtlasTextureId, Background, Bounds, DevicePixels, GpuSpecs, MonochromeSprite, Path, Point,
|
||||
PolychromeSprite, PrimitiveBatch, Quad, ScaledPixels, Scene, Shadow, Size, SubpixelSprite,
|
||||
Underline, get_gamma_correction_ratios,
|
||||
AtlasTextureId, Background, Bounds, DevicePixels, GpuSpecs, MonochromeSprite, PaintSurface,
|
||||
Path, Point, PolychromeSprite, PrimitiveBatch, Quad, ScaledPixels, Scene, Shadow, Size,
|
||||
SubpixelSprite, Underline, get_gamma_correction_ratios,
|
||||
};
|
||||
use log::warn;
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
@@ -90,7 +90,6 @@ struct WgpuPipelines {
|
||||
mono_sprites: wgpu::RenderPipeline,
|
||||
subpixel_sprites: Option<wgpu::RenderPipeline>,
|
||||
poly_sprites: wgpu::RenderPipeline,
|
||||
#[allow(dead_code)]
|
||||
surfaces: wgpu::RenderPipeline,
|
||||
}
|
||||
|
||||
@@ -112,6 +111,8 @@ struct WgpuResources {
|
||||
pipelines: WgpuPipelines,
|
||||
bind_group_layouts: WgpuBindGroupLayouts,
|
||||
atlas_sampler: wgpu::Sampler,
|
||||
surface_sampler: wgpu::Sampler,
|
||||
surface_uniform_buffer: wgpu::Buffer,
|
||||
globals_buffer: wgpu::Buffer,
|
||||
globals_bind_group: wgpu::BindGroup,
|
||||
path_globals_bind_group: wgpu::BindGroup,
|
||||
@@ -138,6 +139,9 @@ pub struct WgpuRenderer {
|
||||
/// Compositor GPU hint for adapter selection (unused on WASM).
|
||||
#[allow(dead_code)]
|
||||
compositor_gpu: Option<CompositorGpuHint>,
|
||||
/// Application-requested extra wgpu features/limits, stored for device recovery.
|
||||
#[allow(dead_code)]
|
||||
extra_requirements: Option<WgpuDeviceRequirements>,
|
||||
resources: Option<WgpuResources>,
|
||||
surface_config: wgpu::SurfaceConfiguration,
|
||||
atlas: Arc<WgpuAtlas>,
|
||||
@@ -188,6 +192,7 @@ impl WgpuRenderer {
|
||||
window: &W,
|
||||
config: WgpuSurfaceConfig,
|
||||
compositor_gpu: Option<CompositorGpuHint>,
|
||||
extra_requirements: Option<WgpuDeviceRequirements>,
|
||||
) -> anyhow::Result<Self>
|
||||
where
|
||||
W: HasWindowHandle + HasDisplayHandle + std::fmt::Debug + Send + Sync + Clone + 'static,
|
||||
@@ -226,7 +231,12 @@ impl WgpuRenderer {
|
||||
context.check_compatible_with_surface(&surface)?;
|
||||
context
|
||||
}
|
||||
None => ctx_ref.insert(WgpuContext::new(instance, &surface, compositor_gpu)?),
|
||||
None => ctx_ref.insert(WgpuContext::new(
|
||||
instance,
|
||||
&surface,
|
||||
compositor_gpu,
|
||||
extra_requirements.as_ref(),
|
||||
)?),
|
||||
};
|
||||
|
||||
let atlas = Arc::new(WgpuAtlas::from_context(context));
|
||||
@@ -237,6 +247,7 @@ impl WgpuRenderer {
|
||||
surface,
|
||||
config,
|
||||
compositor_gpu,
|
||||
extra_requirements,
|
||||
atlas,
|
||||
)
|
||||
}
|
||||
@@ -254,7 +265,7 @@ impl WgpuRenderer {
|
||||
|
||||
let atlas = Arc::new(WgpuAtlas::from_context(context));
|
||||
|
||||
Self::new_internal(None, context, surface, config, None, atlas)
|
||||
Self::new_internal(None, context, surface, config, None, None, atlas)
|
||||
}
|
||||
|
||||
fn new_internal(
|
||||
@@ -263,6 +274,7 @@ impl WgpuRenderer {
|
||||
surface: wgpu::Surface<'static>,
|
||||
config: WgpuSurfaceConfig,
|
||||
compositor_gpu: Option<CompositorGpuHint>,
|
||||
extra_requirements: Option<WgpuDeviceRequirements>,
|
||||
atlas: Arc<WgpuAtlas>,
|
||||
) -> anyhow::Result<Self> {
|
||||
let surface_caps = surface.get_capabilities(&context.adapter);
|
||||
@@ -368,6 +380,20 @@ impl WgpuRenderer {
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
let surface_sampler = device.create_sampler(&wgpu::SamplerDescriptor {
|
||||
label: Some("surface_sampler"),
|
||||
mag_filter: wgpu::FilterMode::Linear,
|
||||
min_filter: wgpu::FilterMode::Linear,
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
let surface_uniform_buffer = device.create_buffer(&wgpu::BufferDescriptor {
|
||||
label: Some("surface_uniform_buffer"),
|
||||
size: std::mem::size_of::<SurfaceParams>() as u64,
|
||||
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
||||
mapped_at_creation: false,
|
||||
});
|
||||
|
||||
let uniform_alignment = device.limits().min_uniform_buffer_offset_alignment as u64;
|
||||
let globals_size = std::mem::size_of::<GlobalParams>() as u64;
|
||||
let gamma_size = std::mem::size_of::<GammaParams>() as u64;
|
||||
@@ -453,6 +479,8 @@ impl WgpuRenderer {
|
||||
pipelines,
|
||||
bind_group_layouts,
|
||||
atlas_sampler,
|
||||
surface_sampler,
|
||||
surface_uniform_buffer,
|
||||
globals_buffer,
|
||||
globals_bind_group,
|
||||
path_globals_bind_group,
|
||||
@@ -468,6 +496,7 @@ impl WgpuRenderer {
|
||||
Ok(Self {
|
||||
context: gpu_context,
|
||||
compositor_gpu,
|
||||
extra_requirements,
|
||||
resources: Some(resources),
|
||||
surface_config,
|
||||
atlas,
|
||||
@@ -591,16 +620,6 @@ impl WgpuRenderer {
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 2,
|
||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||
ty: wgpu::BindingType::Texture {
|
||||
sample_type: wgpu::TextureSampleType::Float { filterable: true },
|
||||
view_dimension: wgpu::TextureViewDimension::D2,
|
||||
multisampled: false,
|
||||
},
|
||||
count: None,
|
||||
},
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 3,
|
||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
|
||||
count: None,
|
||||
},
|
||||
@@ -1066,6 +1085,11 @@ impl WgpuRenderer {
|
||||
self.dual_source_blending
|
||||
}
|
||||
|
||||
pub fn gpu_context(&self) -> (Arc<wgpu::Device>, Arc<wgpu::Queue>) {
|
||||
let resources = self.resources();
|
||||
(resources.device.clone(), resources.queue.clone())
|
||||
}
|
||||
|
||||
pub fn gpu_specs(&self) -> GpuSpecs {
|
||||
GpuSpecs {
|
||||
is_software_emulated: self.adapter_info.device_type == wgpu::DeviceType::Cpu,
|
||||
@@ -1300,10 +1324,8 @@ impl WgpuRenderer {
|
||||
&mut instance_offset,
|
||||
&mut pass,
|
||||
),
|
||||
PrimitiveBatch::Surfaces(_surfaces) => {
|
||||
// Surfaces are macOS-only for video playback
|
||||
// Not implemented for Linux/wgpu
|
||||
true
|
||||
PrimitiveBatch::Surfaces(range) => {
|
||||
self.draw_surfaces(&scene.surfaces[range], &mut pass)
|
||||
}
|
||||
};
|
||||
if !ok {
|
||||
@@ -1427,6 +1449,55 @@ impl WgpuRenderer {
|
||||
)
|
||||
}
|
||||
|
||||
fn draw_surfaces(&self, surfaces: &[PaintSurface], pass: &mut wgpu::RenderPass<'_>) -> bool {
|
||||
let resources = self.resources();
|
||||
for surface in surfaces {
|
||||
let Some(wgpu_texture) = surface.texture.downcast_ref::<wgpu::Texture>() else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let texture_view = wgpu_texture.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
|
||||
let params = SurfaceParams {
|
||||
bounds: surface.bounds.into(),
|
||||
content_mask: surface.content_mask.bounds.into(),
|
||||
};
|
||||
|
||||
resources.queue.write_buffer(
|
||||
&resources.surface_uniform_buffer,
|
||||
0,
|
||||
bytemuck::bytes_of(¶ms),
|
||||
);
|
||||
|
||||
let bind_group = resources
|
||||
.device
|
||||
.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
label: Some("surface_bind_group"),
|
||||
layout: &resources.bind_group_layouts.surfaces,
|
||||
entries: &[
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: resources.surface_uniform_buffer.as_entire_binding(),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 1,
|
||||
resource: wgpu::BindingResource::TextureView(&texture_view),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 2,
|
||||
resource: wgpu::BindingResource::Sampler(&resources.surface_sampler),
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
pass.set_pipeline(&resources.pipelines.surfaces);
|
||||
pass.set_bind_group(0, &resources.globals_bind_group, &[]);
|
||||
pass.set_bind_group(1, &bind_group, &[]);
|
||||
pass.draw(0..4, 0..1);
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
fn draw_polychrome_sprites(
|
||||
&self,
|
||||
sprites: &[PolychromeSprite],
|
||||
@@ -1808,8 +1879,12 @@ impl WgpuRenderer {
|
||||
|
||||
let instance = WgpuContext::instance(Box::new(window.clone()));
|
||||
let surface = create_surface(&instance, window_handle.as_raw())?;
|
||||
let new_context =
|
||||
WgpuContext::new_rejecting_software(instance, &surface, self.compositor_gpu)?;
|
||||
let new_context = WgpuContext::new_rejecting_software(
|
||||
instance,
|
||||
&surface,
|
||||
self.compositor_gpu,
|
||||
self.extra_requirements.as_ref(),
|
||||
)?;
|
||||
*gpu_context.borrow_mut() = Some(new_context);
|
||||
surface
|
||||
} else {
|
||||
@@ -1833,12 +1908,14 @@ impl WgpuRenderer {
|
||||
self.resources = None;
|
||||
self.atlas.handle_device_lost(context);
|
||||
|
||||
let extra_reqs = self.extra_requirements.clone();
|
||||
*self = Self::new_internal(
|
||||
Some(gpu_context.clone()),
|
||||
context,
|
||||
surface,
|
||||
config,
|
||||
self.compositor_gpu,
|
||||
extra_reqs,
|
||||
self.atlas.clone(),
|
||||
)?;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user