mirror of
https://github.com/emilk/egui.git
synced 2026-08-31 05:40:03 -04:00
eframe support for wgpu on the web (#2107)
* basic working wgpu @ webgl on websys * fix glow compile error * introduced WebPainter trait, provide wgpu renderstate * WebPainterWgpu destroy implemented * make custom3d demo work on wgpu backend * changelog entry for wgpu support eframe wasm * remove temporary logging hack * stop using pollster for web we're actually not allowed to block - this only worked because wgpu on webgl doesn't actually cause anything blocking. However, when trying webgpu this became an issue * revert cargo update * compile error if neither glow nor wgpu features are enabled * code cleanup * Error handling * Update changelog with link * Make sure --all-features work * Select best framebuffer format from the available ones * update to wasm-bindgen 0.2.83 * Fix typo * Clean up Cargo.toml * Log about using the wgpu painter * fixup wgpu labels * fix custom3d_wgpu_shader ub padding * remove duplicated uniforms struct in wgsl shader for custom3d * Update docs: add async/await to the web 'start' function Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
This commit is contained in:
@@ -17,5 +17,28 @@ pub use renderer::Renderer;
|
||||
#[cfg(feature = "winit")]
|
||||
pub mod winit;
|
||||
|
||||
#[cfg(feature = "winit")]
|
||||
pub use crate::winit::RenderState;
|
||||
use egui::mutex::RwLock;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Access to the render state for egui, which can be useful in combination with
|
||||
/// [`egui::PaintCallback`]s for custom rendering using WGPU.
|
||||
#[derive(Clone)]
|
||||
pub struct RenderState {
|
||||
pub device: Arc<wgpu::Device>,
|
||||
pub queue: Arc<wgpu::Queue>,
|
||||
pub target_format: wgpu::TextureFormat,
|
||||
pub renderer: Arc<RwLock<Renderer>>,
|
||||
}
|
||||
|
||||
/// Find the framebuffer format that egui prefers
|
||||
pub fn preferred_framebuffer_format(formats: &[wgpu::TextureFormat]) -> wgpu::TextureFormat {
|
||||
for &format in formats {
|
||||
if matches!(
|
||||
format,
|
||||
wgpu::TextureFormat::Rgba8Unorm | wgpu::TextureFormat::Bgra8Unorm
|
||||
) {
|
||||
return format;
|
||||
}
|
||||
}
|
||||
formats[0] // take the first
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#![allow(unsafe_code)]
|
||||
|
||||
use std::num::NonZeroU64;
|
||||
use std::{borrow::Cow, collections::HashMap, num::NonZeroU32};
|
||||
|
||||
use egui::{epaint::Primitive, PaintCallbackInfo};
|
||||
@@ -26,7 +27,7 @@ use wgpu::util::DeviceExt as _;
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// See the [`custom3d_glow`](https://github.com/emilk/egui/blob/master/crates/egui_demo_app/src/apps/custom3d_wgpu.rs) demo source for a detailed usage example.
|
||||
/// See the [`custom3d_wgpu`](https://github.com/emilk/egui/blob/master/crates/egui_demo_app/src/apps/custom3d_wgpu.rs) demo source for a detailed usage example.
|
||||
pub struct CallbackFn {
|
||||
prepare: Box<PrepareCallback>,
|
||||
paint: Box<PaintCallback>,
|
||||
@@ -149,7 +150,7 @@ impl Renderer {
|
||||
depth_bits: u8,
|
||||
) -> Self {
|
||||
let shader = wgpu::ShaderModuleDescriptor {
|
||||
label: Some("egui_shader"),
|
||||
label: Some("egui"),
|
||||
source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("egui.wgsl"))),
|
||||
};
|
||||
let module = device.create_shader_module(shader);
|
||||
@@ -175,7 +176,7 @@ impl Renderer {
|
||||
visibility: wgpu::ShaderStages::VERTEX,
|
||||
ty: wgpu::BindingType::Buffer {
|
||||
has_dynamic_offset: false,
|
||||
min_binding_size: None,
|
||||
min_binding_size: NonZeroU64::new(std::mem::size_of::<UniformBuffer>() as _),
|
||||
ty: wgpu::BufferBindingType::Uniform,
|
||||
},
|
||||
count: None,
|
||||
@@ -306,8 +307,9 @@ impl Renderer {
|
||||
}
|
||||
|
||||
pub fn update_depth_texture(&mut self, device: &wgpu::Device, width: u32, height: u32) {
|
||||
// TODO(wumpf) don't recreate texture if size hasn't changed
|
||||
let texture = device.create_texture(&wgpu::TextureDescriptor {
|
||||
label: None,
|
||||
label: Some("egui_depth_texture"),
|
||||
size: wgpu::Extent3d {
|
||||
width,
|
||||
height,
|
||||
@@ -361,7 +363,7 @@ impl Renderer {
|
||||
},
|
||||
})],
|
||||
depth_stencil_attachment,
|
||||
label: Some("egui_render_pass"),
|
||||
label: Some("egui_render"),
|
||||
});
|
||||
|
||||
self.render_onto_renderpass(&mut render_pass, paint_jobs, screen_descriptor);
|
||||
@@ -559,9 +561,13 @@ impl Renderer {
|
||||
origin,
|
||||
);
|
||||
} else {
|
||||
// TODO(Wumpf): Create only a new texture if we need to
|
||||
// allocate a new texture
|
||||
// Use same label for all resources associated with this texture id (no point in retyping the type)
|
||||
let label_str = format!("egui_texid_{:?}", id);
|
||||
let label = Some(label_str.as_str());
|
||||
let texture = device.create_texture(&wgpu::TextureDescriptor {
|
||||
label: None,
|
||||
label,
|
||||
size,
|
||||
mip_level_count: 1,
|
||||
sample_count: 1,
|
||||
@@ -573,14 +579,15 @@ impl Renderer {
|
||||
egui::TextureFilter::Nearest => wgpu::FilterMode::Nearest,
|
||||
egui::TextureFilter::Linear => wgpu::FilterMode::Linear,
|
||||
};
|
||||
// TODO(Wumpf): Reuse this sampler.
|
||||
let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
|
||||
label: None,
|
||||
label,
|
||||
mag_filter: filter,
|
||||
min_filter: filter,
|
||||
..Default::default()
|
||||
});
|
||||
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
label: None,
|
||||
label,
|
||||
layout: &self.texture_bind_group_layout,
|
||||
entries: &[
|
||||
wgpu::BindGroupEntry {
|
||||
@@ -633,13 +640,7 @@ impl Renderer {
|
||||
device,
|
||||
texture,
|
||||
wgpu::SamplerDescriptor {
|
||||
label: Some(
|
||||
format!(
|
||||
"egui_user_image_{}_texture_sampler",
|
||||
self.next_user_texture_id
|
||||
)
|
||||
.as_str(),
|
||||
),
|
||||
label: Some(format!("egui_user_image_{}", self.next_user_texture_id).as_str()),
|
||||
mag_filter: texture_filter,
|
||||
min_filter: texture_filter,
|
||||
..Default::default()
|
||||
@@ -661,13 +662,7 @@ impl Renderer {
|
||||
device,
|
||||
texture,
|
||||
wgpu::SamplerDescriptor {
|
||||
label: Some(
|
||||
format!(
|
||||
"egui_user_image_{}_texture_sampler",
|
||||
self.next_user_texture_id
|
||||
)
|
||||
.as_str(),
|
||||
),
|
||||
label: Some(format!("egui_user_image_{}", self.next_user_texture_id).as_str()),
|
||||
mag_filter: texture_filter,
|
||||
min_filter: texture_filter,
|
||||
..Default::default()
|
||||
@@ -698,13 +693,7 @@ impl Renderer {
|
||||
});
|
||||
|
||||
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
label: Some(
|
||||
format!(
|
||||
"egui_user_image_{}_texture_bind_group",
|
||||
self.next_user_texture_id
|
||||
)
|
||||
.as_str(),
|
||||
),
|
||||
label: Some(format!("egui_user_image_{}", self.next_user_texture_id).as_str()),
|
||||
layout: &self.texture_bind_group_layout,
|
||||
entries: &[
|
||||
wgpu::BindGroupEntry {
|
||||
@@ -748,9 +737,7 @@ impl Renderer {
|
||||
});
|
||||
|
||||
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
label: Some(
|
||||
format!("egui_user_{}_texture_bind_group", self.next_user_texture_id).as_str(),
|
||||
),
|
||||
label: Some(format!("egui_user_image_{}", self.next_user_texture_id).as_str()),
|
||||
layout: &self.texture_bind_group_layout,
|
||||
entries: &[
|
||||
wgpu::BindGroupEntry {
|
||||
|
||||
@@ -4,17 +4,7 @@ use egui::mutex::RwLock;
|
||||
use tracing::error;
|
||||
use wgpu::{Adapter, Instance, Surface};
|
||||
|
||||
use crate::{renderer, Renderer};
|
||||
|
||||
/// Access to the render state for egui, which can be useful in combination with
|
||||
/// [`egui::PaintCallback`]s for custom rendering using WGPU.
|
||||
#[derive(Clone)]
|
||||
pub struct RenderState {
|
||||
pub device: Arc<wgpu::Device>,
|
||||
pub queue: Arc<wgpu::Queue>,
|
||||
pub target_format: wgpu::TextureFormat,
|
||||
pub renderer: Arc<RwLock<Renderer>>,
|
||||
}
|
||||
use crate::{renderer, RenderState, Renderer};
|
||||
|
||||
struct SurfaceState {
|
||||
surface: Surface,
|
||||
@@ -119,7 +109,7 @@ impl<'a> Painter<'a> {
|
||||
let adapter = self.adapter.as_ref().unwrap();
|
||||
|
||||
let swapchain_format =
|
||||
select_framebuffer_format(&surface.get_supported_formats(adapter));
|
||||
crate::preferred_framebuffer_format(&surface.get_supported_formats(adapter));
|
||||
|
||||
let rs = pollster::block_on(self.init_render_state(adapter, swapchain_format));
|
||||
self.render_state = Some(rs);
|
||||
@@ -324,15 +314,3 @@ impl<'a> Painter<'a> {
|
||||
// TODO(emilk): something here?
|
||||
}
|
||||
}
|
||||
|
||||
fn select_framebuffer_format(formats: &[wgpu::TextureFormat]) -> wgpu::TextureFormat {
|
||||
for &format in formats {
|
||||
if matches!(
|
||||
format,
|
||||
wgpu::TextureFormat::Rgba8Unorm | wgpu::TextureFormat::Bgra8Unorm
|
||||
) {
|
||||
return format;
|
||||
}
|
||||
}
|
||||
formats[0] // take the first
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user