wgpu renderer now always requires a RenderPass being passed in, pass command encoder to prepare callback (#2136)
* wgpu renderer now always requires a RenderPass being passed in This also implies that it no longer owns the depth buffer! (why would it anyways!) * wgpu-renderer now passes a command encoder to prepare * add changelog entries * fixup changelogs, fix variable name
This commit is contained in:
parent
7803285221
commit
c414af7aa2
6 changed files with 115 additions and 128 deletions
|
@ -16,6 +16,7 @@ NOTE: [`egui-winit`](../egui-winit/CHANGELOG.md), [`egui_glium`](../egui_glium/C
|
|||
* Web: You can now use WebGL on top of `wgpu` by enabling the `wgpu` feature (and disabling `glow` via disabling default features) ([#2107](https://github.com/emilk/egui/pull/2107)).
|
||||
|
||||
|
||||
|
||||
## 0.19.0 - 2022-08-20
|
||||
* MSRV (Minimum Supported Rust Version) is now `1.61.0` ([#1846](https://github.com/emilk/egui/pull/1846)).
|
||||
* Added `wgpu` rendering backed ([#1564](https://github.com/emilk/egui/pull/1564)):
|
||||
|
|
|
@ -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),
|
||||
|
@ -127,9 +125,6 @@ impl WebPainter for WebPainterWgpu {
|
|||
err
|
||||
))
|
||||
})?;
|
||||
let view = frame
|
||||
.texture
|
||||
.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
|
||||
let mut encoder =
|
||||
render_state
|
||||
|
@ -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();
|
||||
|
|
|
@ -5,8 +5,10 @@ All notable changes to the `egui-wgpu` integration will be noted in this file.
|
|||
## Unreleased
|
||||
* Renamed `RenderPass` to `Renderer`.
|
||||
* Renamed `RenderPass::execute` to `RenderPass::render`.
|
||||
* Renamed `RenderPass::execute_with_renderpass` to `Renderer::render_onto_renderpass`.
|
||||
* Renamed `RenderPass::execute_with_renderpass` to `Renderer::render` (replacing existing `Renderer::render`)
|
||||
* Reexported `Renderer`.
|
||||
* `Renderer` no longer handles pass creation and depth buffer creation ([#2136](https://github.com/emilk/egui/pull/2136))
|
||||
* `PrepareCallback` now passes `wgpu::CommandEncoder` ([#2136](https://github.com/emilk/egui/pull/2136))
|
||||
|
||||
|
||||
## 0.19.0 - 2022-08-20
|
||||
|
|
|
@ -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].
|
||||
|
@ -33,7 +37,8 @@ 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;
|
||||
|
@ -41,7 +46,7 @@ type PaintCallback =
|
|||
impl Default for CallbackFn {
|
||||
fn default() -> Self {
|
||||
CallbackFn {
|
||||
prepare: Box::new(|_, _, _| ()),
|
||||
prepare: Box::new(|_, _, _, _| ()),
|
||||
paint: Box::new(|_, _, _| ()),
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -141,13 +148,13 @@ pub struct Renderer {
|
|||
impl Renderer {
|
||||
/// Creates a renderer for a egui UI.
|
||||
///
|
||||
/// `output_format` should preferably be [`wgpu::TextureFormat::Rgba8Unorm`] or
|
||||
/// `output_color_format` should preferably be [`wgpu::TextureFormat::Rgba8Unorm`] or
|
||||
/// [`wgpu::TextureFormat::Bgra8Unorm`], i.e. in gamma-space.
|
||||
pub fn new(
|
||||
device: &wgpu::Device,
|
||||
output_format: wgpu::TextureFormat,
|
||||
output_color_format: wgpu::TextureFormat,
|
||||
output_depth_format: Option<wgpu::TextureFormat>,
|
||||
msaa_samples: u32,
|
||||
depth_bits: u8,
|
||||
) -> Self {
|
||||
let shader = wgpu::ShaderModuleDescriptor {
|
||||
label: Some("egui"),
|
||||
|
@ -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(),
|
||||
|
@ -266,14 +273,14 @@ impl Renderer {
|
|||
|
||||
fragment: Some(wgpu::FragmentState {
|
||||
module: &module,
|
||||
entry_point: if output_format.describe().srgb {
|
||||
tracing::warn!("Detected a linear (sRGBA aware) framebuffer {:?}. egui prefers Rgba8Unorm or Bgra8Unorm", output_format);
|
||||
entry_point: if output_color_format.describe().srgb {
|
||||
tracing::warn!("Detected a linear (sRGBA aware) framebuffer {:?}. egui prefers Rgba8Unorm or Bgra8Unorm", output_color_format);
|
||||
"fs_main_linear_framebuffer"
|
||||
} else {
|
||||
"fs_main_gamma_framebuffer" // this is what we prefer
|
||||
},
|
||||
targets: &[Some(wgpu::ColorTargetState {
|
||||
format: output_format,
|
||||
format: output_color_format,
|
||||
blend: Some(wgpu::BlendState {
|
||||
color: wgpu::BlendComponent {
|
||||
src_factor: wgpu::BlendFactor::One,
|
||||
|
@ -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],
|
||||
|
@ -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,
|
||||
) {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>,
|
||||
|
@ -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,
|
||||
|
@ -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),
|
||||
|
@ -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`]
|
||||
|
@ -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()");
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
|
|
@ -138,7 +138,7 @@ impl Custom3d {
|
|||
// The paint callback is called after prepare and is given access to the render pass, which
|
||||
// can be used to issue draw commands.
|
||||
let cb = egui_wgpu::CallbackFn::new()
|
||||
.prepare(move |device, queue, paint_callback_resources| {
|
||||
.prepare(move |device, queue, _encoder, paint_callback_resources| {
|
||||
let resources: &TriangleRenderResources = paint_callback_resources.get().unwrap();
|
||||
resources.prepare(device, queue, angle);
|
||||
})
|
||||
|
|
Loading…
Reference in a new issue