mirror of
https://github.com/emilk/egui.git
synced 2026-08-29 04:40:03 -04:00
Fill in DisplayHandle automatically on web painter just like it's done on winit (#8006)
* Closes https://github.com/emilk/egui/issues/8001 * Follow-up to https://github.com/emilk/egui/pull/7990 Also simplified/shortened the comments around display handle a bit. Lots a repetition there made it hard to upgrade otherwise.
This commit is contained in:
@@ -25,6 +25,24 @@ pub(crate) struct WebPainterWgpu {
|
|||||||
needs_reconfigure: bool,
|
needs_reconfigure: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Owned web display handle that is `Send + Sync`.
|
||||||
|
///
|
||||||
|
/// `DisplayHandle` from `raw-window-handle` is `!Send`/`!Sync` because the enum
|
||||||
|
/// contains platform variants with raw pointers. On web the handle is always empty,
|
||||||
|
/// so this wrapper is safe.
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
struct WebDisplay;
|
||||||
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
impl egui_wgpu::wgpu::rwh::HasDisplayHandle for WebDisplay {
|
||||||
|
fn display_handle(
|
||||||
|
&self,
|
||||||
|
) -> Result<egui_wgpu::wgpu::rwh::DisplayHandle<'_>, egui_wgpu::wgpu::rwh::HandleError> {
|
||||||
|
Ok(egui_wgpu::wgpu::rwh::DisplayHandle::web())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl WebPainterWgpu {
|
impl WebPainterWgpu {
|
||||||
pub fn render_state(&self) -> Option<RenderState> {
|
pub fn render_state(&self) -> Option<RenderState> {
|
||||||
self.render_state.clone()
|
self.render_state.clone()
|
||||||
@@ -64,7 +82,17 @@ impl WebPainterWgpu {
|
|||||||
) -> Result<Self, String> {
|
) -> Result<Self, String> {
|
||||||
log::debug!("Creating wgpu painter");
|
log::debug!("Creating wgpu painter");
|
||||||
|
|
||||||
let instance = options.wgpu_options.wgpu_setup.new_instance().await;
|
// Inject the display handle into the wgpu setup so that wgpu can create surfaces on WebGL.
|
||||||
|
let mut wgpu_options = options.wgpu_options.clone();
|
||||||
|
if let egui_wgpu::WgpuSetup::CreateNew(ref mut create_new) = wgpu_options.wgpu_setup
|
||||||
|
&& create_new.display_handle.is_none()
|
||||||
|
{
|
||||||
|
// Force WebGL, useful for quick & dirty testing:
|
||||||
|
//create_new.instance_descriptor.backends = wgpu::Backends::GL;
|
||||||
|
create_new.display_handle = Some(Box::new(WebDisplay));
|
||||||
|
}
|
||||||
|
|
||||||
|
let instance = wgpu_options.wgpu_setup.new_instance().await;
|
||||||
let surface = instance
|
let surface = instance
|
||||||
.create_surface(wgpu::SurfaceTarget::Canvas(canvas.clone()))
|
.create_surface(wgpu::SurfaceTarget::Canvas(canvas.clone()))
|
||||||
.map_err(|err| format!("failed to create wgpu surface: {err}"))?;
|
.map_err(|err| format!("failed to create wgpu surface: {err}"))?;
|
||||||
@@ -72,7 +100,7 @@ impl WebPainterWgpu {
|
|||||||
let depth_stencil_format = egui_wgpu::depth_format_from_bits(options.depth_buffer, 0);
|
let depth_stencil_format = egui_wgpu::depth_format_from_bits(options.depth_buffer, 0);
|
||||||
|
|
||||||
let render_state = RenderState::create(
|
let render_state = RenderState::create(
|
||||||
&options.wgpu_options,
|
&wgpu_options,
|
||||||
&instance,
|
&instance,
|
||||||
Some(&surface),
|
Some(&surface),
|
||||||
egui_wgpu::RendererOptions {
|
egui_wgpu::RendererOptions {
|
||||||
@@ -90,7 +118,7 @@ impl WebPainterWgpu {
|
|||||||
|
|
||||||
let surface_configuration = wgpu::SurfaceConfiguration {
|
let surface_configuration = wgpu::SurfaceConfiguration {
|
||||||
format: render_state.target_format,
|
format: render_state.target_format,
|
||||||
present_mode: options.wgpu_options.present_mode,
|
present_mode: wgpu_options.present_mode,
|
||||||
view_formats: vec![render_state.target_format],
|
view_formats: vec![render_state.target_format],
|
||||||
..default_configuration
|
..default_configuration
|
||||||
};
|
};
|
||||||
@@ -106,7 +134,7 @@ impl WebPainterWgpu {
|
|||||||
surface_configuration,
|
surface_configuration,
|
||||||
depth_stencil_format,
|
depth_stencil_format,
|
||||||
depth_texture_view: None,
|
depth_texture_view: None,
|
||||||
on_surface_status: Arc::clone(&options.wgpu_options.on_surface_status) as _,
|
on_surface_status: Arc::clone(&wgpu_options.on_surface_status) as _,
|
||||||
screen_capture_state: None,
|
screen_capture_state: None,
|
||||||
capture_tx,
|
capture_tx,
|
||||||
capture_rx,
|
capture_rx,
|
||||||
|
|||||||
@@ -2,24 +2,19 @@ use std::sync::Arc;
|
|||||||
|
|
||||||
/// A cloneable display handle for use with [`wgpu::InstanceDescriptor`].
|
/// A cloneable display handle for use with [`wgpu::InstanceDescriptor`].
|
||||||
///
|
///
|
||||||
/// This trait exists so that a [`winit::event_loop::OwnedDisplayHandle`] (or similar platform
|
/// [`wgpu::InstanceDescriptor`] stores its display handle as a non-cloneable
|
||||||
/// display handle) can be stored, cloned, and later passed to wgpu.
|
/// `Box<dyn WgpuHasDisplayHandle>`. This trait wraps it so it can be cloned
|
||||||
|
/// alongside the rest of the egui wgpu configuration.
|
||||||
///
|
///
|
||||||
/// wgpu requires an explicit display handle for GLES on some platforms (notably Wayland).
|
/// Automatically implemented for all types that satisfy the bounds
|
||||||
/// Because [`wgpu::InstanceDescriptor`] contains a `Box<dyn WgpuHasDisplayHandle>` which is
|
/// (including [`winit::event_loop::OwnedDisplayHandle`]).
|
||||||
/// not cloneable, we wrap the handle in this trait so it can be cloned alongside the rest of
|
|
||||||
/// the egui wgpu configuration.
|
|
||||||
///
|
|
||||||
/// This is automatically implemented for all types that satisfy the bounds (including
|
|
||||||
/// [`winit::event_loop::OwnedDisplayHandle`]).
|
|
||||||
pub trait EguiDisplayHandle:
|
pub trait EguiDisplayHandle:
|
||||||
wgpu::rwh::HasDisplayHandle + std::fmt::Debug + Send + Sync + 'static
|
wgpu::rwh::HasDisplayHandle + std::fmt::Debug + Send + Sync + 'static
|
||||||
{
|
{
|
||||||
/// Clone this handle into a `Box<dyn WgpuHasDisplayHandle>` suitable for setting on
|
/// Clone into a `Box<dyn WgpuHasDisplayHandle>` for [`wgpu::InstanceDescriptor::display`].
|
||||||
/// [`wgpu::InstanceDescriptor::display`].
|
|
||||||
fn clone_for_wgpu(&self) -> Box<dyn wgpu::wgt::WgpuHasDisplayHandle>;
|
fn clone_for_wgpu(&self) -> Box<dyn wgpu::wgt::WgpuHasDisplayHandle>;
|
||||||
|
|
||||||
/// Clone this handle into a new `Box<dyn EguiDisplayHandle>`.
|
/// Clone into a new `Box<dyn EguiDisplayHandle>`.
|
||||||
fn clone_display_handle(&self) -> Box<dyn EguiDisplayHandle>;
|
fn clone_display_handle(&self) -> Box<dyn EguiDisplayHandle>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,27 +63,14 @@ pub enum WgpuSetup {
|
|||||||
impl WgpuSetup {
|
impl WgpuSetup {
|
||||||
/// Creates a new [`WgpuSetup::CreateNew`] with the given display handle.
|
/// Creates a new [`WgpuSetup::CreateNew`] with the given display handle.
|
||||||
///
|
///
|
||||||
/// This is the recommended constructor. Most platforms (Windows, macOS/iOS, Android, web)
|
/// See [`WgpuSetupCreateNew::from_display_handle`] for details.
|
||||||
/// work fine without a display handle, but some (e.g. Wayland on Linux with GLES) require
|
|
||||||
/// one. Providing it unconditionally ensures your app works everywhere.
|
|
||||||
///
|
|
||||||
/// If you don't have a display handle available, use [`Self::without_display_handle`]
|
|
||||||
/// instead — it will still work on the majority of platforms.
|
|
||||||
///
|
|
||||||
/// With winit, pass [`EventLoop::owned_display_handle`](winit::event_loop::EventLoop::owned_display_handle).
|
|
||||||
pub fn from_display_handle(display_handle: impl EguiDisplayHandle) -> Self {
|
pub fn from_display_handle(display_handle: impl EguiDisplayHandle) -> Self {
|
||||||
Self::CreateNew(WgpuSetupCreateNew::from_display_handle(display_handle))
|
Self::CreateNew(WgpuSetupCreateNew::from_display_handle(display_handle))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new [`WgpuSetup::CreateNew`] without a display handle.
|
/// Creates a new [`WgpuSetup::CreateNew`] without a display handle.
|
||||||
///
|
///
|
||||||
/// A display handle is not required for headless operation (offscreen rendering, tests,
|
/// See [`WgpuSetupCreateNew::without_display_handle`] for details.
|
||||||
/// compute-only workloads). It also isn't needed on most platforms even when presenting
|
|
||||||
/// to a window — only some configurations (e.g. Wayland on Linux with GLES) require one.
|
|
||||||
///
|
|
||||||
/// If you do have a display handle available, prefer [`Self::from_display_handle`] for
|
|
||||||
/// maximum compatibility. With winit you can obtain one via
|
|
||||||
/// [`EventLoop::owned_display_handle`](winit::event_loop::EventLoop::owned_display_handle).
|
|
||||||
pub fn without_display_handle() -> Self {
|
pub fn without_display_handle() -> Self {
|
||||||
Self::CreateNew(WgpuSetupCreateNew::without_display_handle())
|
Self::CreateNew(WgpuSetupCreateNew::without_display_handle())
|
||||||
}
|
}
|
||||||
@@ -175,44 +157,32 @@ pub type NativeAdapterSelectorMethod = Arc<
|
|||||||
///
|
///
|
||||||
/// Used for [`WgpuSetup::CreateNew`].
|
/// Used for [`WgpuSetup::CreateNew`].
|
||||||
///
|
///
|
||||||
/// Use [`Self::from_display_handle`] when you have a display handle available — this is the
|
/// Prefer [`Self::from_display_handle`] when you have a display handle available.
|
||||||
/// recommended constructor. With winit you can obtain one via
|
/// Most platforms work without one, but some (e.g. Wayland with GLES, or WebGL)
|
||||||
/// [`EventLoop::owned_display_handle`](winit::event_loop::EventLoop::owned_display_handle).
|
/// require it, so providing one ensures maximum compatibility.
|
||||||
/// Most platforms (Windows, macOS/iOS, Android, web) work fine without one, but some
|
/// With winit, pass [`EventLoop::owned_display_handle`](winit::event_loop::EventLoop::owned_display_handle).
|
||||||
/// (e.g. Wayland on Linux with GLES) require it. Providing it unconditionally ensures your
|
|
||||||
/// app works everywhere.
|
|
||||||
///
|
///
|
||||||
/// If you don't have a display handle, use [`Self::without_display_handle`] — it will still
|
/// Note: The display handle is stored in [`Self::display_handle`] rather than in
|
||||||
/// work on the majority of platforms, and is appropriate for headless rendering, tests, or
|
/// [`Self::instance_descriptor`] so the config can be cloned
|
||||||
/// web targets.
|
/// ([`wgpu::InstanceDescriptor`] is not `Clone`). It is injected at instance creation time.
|
||||||
///
|
|
||||||
/// Note: The [`wgpu::InstanceDescriptor::display`] field is always stored as `None` in
|
|
||||||
/// [`Self::instance_descriptor`]. The display handle is stored separately so it can be cloned
|
|
||||||
/// (since [`wgpu::InstanceDescriptor`] itself does not implement `Clone`), and is injected
|
|
||||||
/// into the descriptor at instance creation time.
|
|
||||||
pub struct WgpuSetupCreateNew {
|
pub struct WgpuSetupCreateNew {
|
||||||
/// Instance descriptor for creating a wgpu instance.
|
/// Descriptor for the wgpu instance.
|
||||||
///
|
///
|
||||||
/// The [`wgpu::InstanceDescriptor::display`] field should be left as `None`; use the
|
/// Leave [`wgpu::InstanceDescriptor::display`] as `None` — use [`Self::display_handle`]
|
||||||
/// [`Self::display_handle`] field instead (it will be injected when the instance is created).
|
/// instead (injected at instance creation time).
|
||||||
///
|
///
|
||||||
/// The most important field is [`wgpu::InstanceDescriptor::backends`], which
|
/// The most important field is [`wgpu::InstanceDescriptor::backends`], which controls
|
||||||
/// controls which backends are supported (wgpu will pick one of these).
|
/// which backends are supported (wgpu will pick one of these). For example, set it to
|
||||||
/// If you only want to support WebGL (and not WebGPU),
|
/// [`wgpu::Backends::GL`] to use only WebGL. By default on web, WebGPU is preferred
|
||||||
/// you can set this to [`wgpu::Backends::GL`].
|
/// with WebGL as a fallback (requires the `webgl` feature of crate `wgpu`).
|
||||||
/// By default on web, WebGPU will be used if available.
|
|
||||||
/// WebGL will only be used as a fallback,
|
|
||||||
/// and only if you have enabled the `webgl` feature of crate `wgpu`.
|
|
||||||
pub instance_descriptor: wgpu::InstanceDescriptor,
|
pub instance_descriptor: wgpu::InstanceDescriptor,
|
||||||
|
|
||||||
/// The display handle to pass to wgpu when creating the instance.
|
/// Display handle passed to wgpu at instance creation time.
|
||||||
///
|
///
|
||||||
/// Most platforms (Windows, macOS/iOS, Android, web) work without this, but some
|
/// Required on some platforms (e.g. Wayland with GLES, WebGL); optional elsewhere.
|
||||||
/// (e.g. Wayland on Linux with GLES) require it. If you have a display handle
|
/// With winit, use [`winit::event_loop::OwnedDisplayHandle`].
|
||||||
/// available, providing it ensures maximum compatibility.
|
|
||||||
///
|
///
|
||||||
/// When using winit, this is typically the
|
/// `eframe` 's winit & web integrations will attempt to fill the display handle automatically if it is left empty.
|
||||||
/// [`winit::event_loop::OwnedDisplayHandle`] obtained from the event loop.
|
|
||||||
pub display_handle: Option<Box<dyn EguiDisplayHandle>>,
|
pub display_handle: Option<Box<dyn EguiDisplayHandle>>,
|
||||||
|
|
||||||
/// Power preference for the adapter if [`Self::native_adapter_selector`] is not set or targeting web.
|
/// Power preference for the adapter if [`Self::native_adapter_selector`] is not set or targeting web.
|
||||||
@@ -258,8 +228,11 @@ impl WgpuSetupCreateNew {
|
|||||||
/// to a window — only some configurations (e.g. Wayland on Linux with GLES) require one.
|
/// to a window — only some configurations (e.g. Wayland on Linux with GLES) require one.
|
||||||
///
|
///
|
||||||
/// If you do have a display handle available, prefer [`Self::from_display_handle`] for
|
/// If you do have a display handle available, prefer [`Self::from_display_handle`] for
|
||||||
/// maximum compatibility. With winit you can obtain one via
|
/// maximum compatibility.
|
||||||
/// [`EventLoop::owned_display_handle`](winit::event_loop::EventLoop::owned_display_handle).
|
///
|
||||||
|
/// With winit you can obtain one via [`EventLoop::owned_display_handle`](winit::event_loop::EventLoop::owned_display_handle).
|
||||||
|
///
|
||||||
|
/// `eframe` 's winit & web integrations will attempt to fill the display handle automatically if it is left empty.
|
||||||
pub fn without_display_handle() -> Self {
|
pub fn without_display_handle() -> Self {
|
||||||
Self {
|
Self {
|
||||||
instance_descriptor: wgpu::InstanceDescriptor {
|
instance_descriptor: wgpu::InstanceDescriptor {
|
||||||
|
|||||||
Reference in New Issue
Block a user