1
0
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:
Andreas Reich
2022-10-05 20:14:18 +02:00
committed by GitHub
parent c441f33ecf
commit c2a37f4bd8
20 changed files with 432 additions and 211 deletions

View File

@@ -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
}

View File

@@ -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 {

View File

@@ -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
}