Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

wgpu renderer now always requires a RenderPass being passed in, pass command encoder to prepare callback #2136

Merged
merged 4 commits into from
Oct 12, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ NOTE: [`epaint`](crates/epaint/CHANGELOG.md), [`eframe`](crates/eframe/CHANGELOG
## Unreleased
* ⚠️ BREAKING: Fix text being too small ([#2069](https://github.com/emilk/egui/pull/2069)).
* ⚠️ BREAKING: egui now expects integrations to do all color blending in gamma space ([#2071](https://github.com/emilk/egui/pull/2071)).
* ⚠️ BREAKING: wgpu-renderer no longer handles pass creation ([#2136](https://github.com/emilk/egui/pull/2136))
* ⚠️ BREAKING: wgpu-renderer prepare callback now passes `wgpu::CommandEncoder` ([#2136](https://github.com/emilk/egui/pull/2136))
Wumpf marked this conversation as resolved.
Show resolved Hide resolved

### Added
* eframe now supports wgpu on the web ([#2107](https://github.com/emilk/egui/pull/2107)
Wumpf marked this conversation as resolved.
Show resolved Hide resolved

### Fixed 🐛
* Improved text rendering ([#2071](https://github.com/emilk/egui/pull/2071)).
Expand Down
46 changes: 27 additions & 19 deletions crates/eframe/src/web/web_painter_wgpu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,10 @@ impl WebPainterWgpu {
.await
.map_err(|err| format!("Failed to find wgpu device: {}", err))?;

// TODO(Wumpf): MSAA & depth

let target_format =
egui_wgpu::preferred_framebuffer_format(&surface.get_supported_formats(&adapter));

let renderer = egui_wgpu::Renderer::new(&device, target_format, 1, 0);
let renderer = egui_wgpu::Renderer::new(&device, target_format, None, 1);
let render_state = RenderState {
device: Arc::new(device),
queue: Arc::new(queue),
Expand Down Expand Up @@ -127,9 +125,6 @@ impl WebPainter for WebPainterWgpu {
err
))
})?;
let view = frame
.texture
.create_view(&wgpu::TextureViewDescriptor::default());

let mut encoder =
render_state
Expand Down Expand Up @@ -158,24 +153,37 @@ impl WebPainter for WebPainterWgpu {
renderer.update_buffers(
&render_state.device,
&render_state.queue,
&mut encoder,
clipped_primitives,
&screen_descriptor,
);
}

// Record all render passes.
render_state.renderer.read().render(
&mut encoder,
&view,
clipped_primitives,
&screen_descriptor,
Some(wgpu::Color {
r: clear_color.r() as f64,
g: clear_color.g() as f64,
b: clear_color.b() as f64,
a: clear_color.a() as f64,
}),
);
{
let renderer = render_state.renderer.read();
let frame_view = frame
.texture
.create_view(&wgpu::TextureViewDescriptor::default());
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &frame_view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color {
r: clear_color.r() as f64,
g: clear_color.g() as f64,
b: clear_color.b() as f64,
a: clear_color.a() as f64,
}),
store: true,
},
})],
depth_stencil_attachment: None,
label: Some("egui_render"),
});

renderer.render(&mut render_pass, clipped_primitives, &screen_descriptor);
}

{
let mut renderer = render_state.renderer.write();
Expand Down
94 changes: 19 additions & 75 deletions crates/egui-wgpu/src/renderer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,13 @@ use wgpu::util::DeviceExt as _;
///
/// `prepare` is called every frame before `paint`, and can use the passed-in [`wgpu::Device`] and
/// [`wgpu::Buffer`] to allocate or modify GPU resources such as buffers.
/// Additionally, a [`wgpu::CommandEncoder`] is provided in order to allow creation of
/// custom [`wgpu::RenderPass`]/[`wgpu::ComputePass`] or perform buffer/texture copies
/// which may serve as preparation to the final `paint`.
/// (This allows reusing the same [`wgpu::CommandEncoder`] for all callbacks and egui rendering itself)
///
/// `paint` is called after `prepare` and is given access to the the [`wgpu::RenderPass`] so that it
/// can issue draw commands.
/// `paint` is called after `prepare` and is given access to the [`wgpu::RenderPass`] so that it
/// can issue draw commands into the same [`wgpu::RenderPass`] that is used for all other egui elements.
///
/// The final argument of both the `prepare` and `paint` callbacks is a the
/// [`paint_callback_resources`][crate::renderer::Renderer::paint_callback_resources].
Expand All @@ -33,15 +37,16 @@ pub struct CallbackFn {
paint: Box<PaintCallback>,
}

type PrepareCallback = dyn Fn(&wgpu::Device, &wgpu::Queue, &mut TypeMap) + Sync + Send;
type PrepareCallback =
dyn Fn(&wgpu::Device, &wgpu::Queue, &mut wgpu::CommandEncoder, &mut TypeMap) + Sync + Send;

type PaintCallback =
dyn for<'a, 'b> Fn(PaintCallbackInfo, &'a mut wgpu::RenderPass<'b>, &'b TypeMap) + Sync + Send;

impl Default for CallbackFn {
fn default() -> Self {
CallbackFn {
prepare: Box::new(|_, _, _| ()),
prepare: Box::new(|_, _, _, _| ()),
paint: Box::new(|_, _, _| ()),
}
}
Expand All @@ -55,7 +60,10 @@ impl CallbackFn {
/// Set the prepare callback
pub fn prepare<F>(mut self, prepare: F) -> Self
where
F: Fn(&wgpu::Device, &wgpu::Queue, &mut TypeMap) + Sync + Send + 'static,
F: Fn(&wgpu::Device, &wgpu::Queue, &mut wgpu::CommandEncoder, &mut TypeMap)
+ Sync
+ Send
+ 'static,
{
self.prepare = Box::new(prepare) as _;
self
Expand Down Expand Up @@ -122,7 +130,6 @@ struct SizedBuffer {
/// Renderer for a egui based GUI.
pub struct Renderer {
pipeline: wgpu::RenderPipeline,
depth_texture: Option<(wgpu::Texture, wgpu::TextureView)>,
index_buffers: Vec<SizedBuffer>,
vertex_buffers: Vec<SizedBuffer>,
uniform_buffer: SizedBuffer,
Expand All @@ -146,8 +153,8 @@ impl Renderer {
pub fn new(
device: &wgpu::Device,
output_format: wgpu::TextureFormat,
Wumpf marked this conversation as resolved.
Show resolved Hide resolved
output_depth_format: Option<wgpu::TextureFormat>,
msaa_samples: u32,
depth_bits: u8,
) -> Self {
let shader = wgpu::ShaderModuleDescriptor {
label: Some("egui"),
Expand Down Expand Up @@ -225,8 +232,8 @@ impl Renderer {
push_constant_ranges: &[],
});

let depth_stencil = (depth_bits > 0).then(|| wgpu::DepthStencilState {
format: wgpu::TextureFormat::Depth32Float,
let depth_stencil = output_depth_format.map(|format| wgpu::DepthStencilState {
format,
depth_write_enabled: false,
depth_compare: wgpu::CompareFunction::Always,
stencil: wgpu::StencilState::default(),
Expand Down Expand Up @@ -302,75 +309,11 @@ impl Renderer {
textures: HashMap::new(),
next_user_texture_id: 0,
paint_callback_resources: TypeMap::default(),
depth_texture: None,
}
}

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: Some("egui_depth_texture"),
size: wgpu::Extent3d {
width,
height,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Depth32Float,
usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING,
});

let view = texture.create_view(&wgpu::TextureViewDescriptor::default());

self.depth_texture = Some((texture, view));
}

/// Executes the renderer on its own render pass.
pub fn render(
&self,
encoder: &mut wgpu::CommandEncoder,
color_attachment: &wgpu::TextureView,
paint_jobs: &[egui::epaint::ClippedPrimitive],
screen_descriptor: &ScreenDescriptor,
clear_color: Option<wgpu::Color>,
) {
let load_operation = if let Some(color) = clear_color {
wgpu::LoadOp::Clear(color)
} else {
wgpu::LoadOp::Load
};

let depth_stencil_attachment = self.depth_texture.as_ref().map(|(_texture, view)| {
wgpu::RenderPassDepthStencilAttachment {
view,
depth_ops: Some(wgpu::Operations {
load: wgpu::LoadOp::Clear(1.0),
store: true,
}),
stencil_ops: None,
}
});

let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: color_attachment,
resolve_target: None,
ops: wgpu::Operations {
load: load_operation,
store: true,
},
})],
depth_stencil_attachment,
label: Some("egui_render"),
});

self.render_onto_renderpass(&mut render_pass, paint_jobs, screen_descriptor);
}

/// Executes the egui renderer onto an existing wgpu renderpass.
pub fn render_onto_renderpass<'rp>(
pub fn render<'rp>(
&'rp self,
render_pass: &mut wgpu::RenderPass<'rp>,
paint_jobs: &[egui::epaint::ClippedPrimitive],
Expand Down Expand Up @@ -760,6 +703,7 @@ impl Renderer {
&mut self,
device: &wgpu::Device,
queue: &wgpu::Queue,
encoder: &mut wgpu::CommandEncoder,
paint_jobs: &[egui::epaint::ClippedPrimitive],
screen_descriptor: &ScreenDescriptor,
) {
Expand Down Expand Up @@ -821,7 +765,7 @@ impl Renderer {
continue;
};

(cbfn.prepare)(device, queue, &mut self.paint_callback_resources);
(cbfn.prepare)(device, queue, encoder, &mut self.paint_callback_resources);
}
}
}
Expand Down
86 changes: 59 additions & 27 deletions crates/egui-wgpu/src/winit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ pub struct Painter<'a> {
device_descriptor: wgpu::DeviceDescriptor<'a>,
present_mode: wgpu::PresentMode,
msaa_samples: u32,
depth_bits: u8,
depth_format: Option<wgpu::TextureFormat>,
depth_texture_view: Option<wgpu::TextureView>,

instance: Instance,
adapter: Option<Adapter>,
Expand Down Expand Up @@ -56,7 +57,8 @@ impl<'a> Painter<'a> {
device_descriptor,
present_mode,
msaa_samples,
depth_bits,
depth_format: (depth_bits > 0).then(|| wgpu::TextureFormat::Depth32Float),
depth_texture_view: None,

instance,
adapter: None,
Expand All @@ -80,7 +82,7 @@ impl<'a> Painter<'a> {
let (device, queue) =
pollster::block_on(adapter.request_device(&self.device_descriptor, None)).unwrap();

let renderer = Renderer::new(&device, target_format, self.msaa_samples, self.depth_bits);
let renderer = Renderer::new(&device, target_format, self.depth_format, self.msaa_samples);

RenderState {
device: Arc::new(device),
Expand Down Expand Up @@ -141,14 +143,6 @@ impl<'a> Painter<'a> {
.configure(&render_state.device, &config);
surface_state.width = width_in_pixels;
surface_state.height = height_in_pixels;

if self.depth_bits > 0 {
render_state.renderer.write().update_depth_texture(
&render_state.device,
width_in_pixels,
height_in_pixels,
);
}
}

/// Updates (or clears) the [`winit::window::Window`] associated with the [`Painter`]
Expand Down Expand Up @@ -212,6 +206,25 @@ impl<'a> Painter<'a> {
pub fn on_window_resized(&mut self, width_in_pixels: u32, height_in_pixels: u32) {
if self.surface_state.is_some() {
self.configure_surface(width_in_pixels, height_in_pixels);
let device = &self.render_state.as_ref().unwrap().device;
self.depth_texture_view = self.depth_format.map(|depth_format| {
device
.create_texture(&wgpu::TextureDescriptor {
label: Some("egui_depth_texture"),
size: wgpu::Extent3d {
width: width_in_pixels,
height: height_in_pixels,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: depth_format,
usage: wgpu::TextureUsages::RENDER_ATTACHMENT
| wgpu::TextureUsages::TEXTURE_BINDING,
})
.create_view(&wgpu::TextureViewDescriptor::default())
});
} else {
error!("Ignoring window resize notification with no surface created via Painter::set_window()");
}
Expand Down Expand Up @@ -246,9 +259,6 @@ impl<'a> Painter<'a> {
return;
}
};
let output_view = output_frame
.texture
.create_view(&wgpu::TextureViewDescriptor::default());

let mut encoder =
render_state
Expand Down Expand Up @@ -277,24 +287,46 @@ impl<'a> Painter<'a> {
renderer.update_buffers(
&render_state.device,
&render_state.queue,
&mut encoder,
clipped_primitives,
&screen_descriptor,
);
}

// Record all render passes.
render_state.renderer.read().render(
&mut encoder,
&output_view,
clipped_primitives,
&screen_descriptor,
Some(wgpu::Color {
r: clear_color.r() as f64,
g: clear_color.g() as f64,
b: clear_color.b() as f64,
a: clear_color.a() as f64,
}),
);
{
let renderer = render_state.renderer.read();
let frame_view = output_frame
.texture
.create_view(&wgpu::TextureViewDescriptor::default());
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &frame_view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color {
r: clear_color.r() as f64,
g: clear_color.g() as f64,
b: clear_color.b() as f64,
a: clear_color.a() as f64,
}),
store: true,
},
})],
depth_stencil_attachment: self.depth_texture_view.as_ref().map(|view| {
wgpu::RenderPassDepthStencilAttachment {
view,
depth_ops: Some(wgpu::Operations {
load: wgpu::LoadOp::Clear(1.0),
store: true,
}),
stencil_ops: None,
}
}),
label: Some("egui_render"),
});

renderer.render(&mut render_pass, clipped_primitives, &screen_descriptor);
}

{
let mut renderer = render_state.renderer.write();
Expand Down
Loading