Add depth buffer support for egui-wgpu's render pass (#2002)

* add a depth texture for wgpu callbacks

* egui-wgpu: use depth from native_options

* add wgpu caveat to depth_buffer docstring
This commit is contained in:
Romet Tagobert 2022-09-06 15:37:07 +03:00 committed by GitHub
parent 162210f90e
commit 64aac30f36
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 70 additions and 5 deletions

View file

@ -259,6 +259,10 @@ pub struct NativeOptions {
/// Sets the number of bits in the depth buffer.
///
/// `egui` doesn't need the depth buffer, so the default value is 0.
///
/// On `wgpu` backends, due to limited depth texture format options, this
/// will be interpreted as a boolean (non-zero = true) for whether or not
/// specifically a `Depth32Float` buffer is used.
pub depth_buffer: u8,
/// Sets the number of bits in the stencil buffer.
@ -266,7 +270,7 @@ pub struct NativeOptions {
/// `egui` doesn't need the stencil buffer, so the default value is 0.
pub stencil_buffer: u8,
/// Specify wether or not hardware acceleration is preferred, required, or not.
/// Specify whether or not hardware acceleration is preferred, required, or not.
///
/// Default: [`HardwareAcceleration::Preferred`].
pub hardware_acceleration: HardwareAcceleration,

View file

@ -683,6 +683,12 @@ mod wgpu_integration {
storage: Option<Box<dyn epi::Storage>>,
window: winit::window::Window,
) {
let mut limits = wgpu::Limits::downlevel_webgl2_defaults();
if self.native_options.depth_buffer > 0 {
// When using a depth buffer, we have to be able to create a texture large enough for the entire surface.
limits.max_texture_dimension_2d = 8192;
}
#[allow(unsafe_code, unused_mut, unused_unsafe)]
let painter = unsafe {
let mut painter = egui_wgpu::winit::Painter::new(
@ -691,10 +697,11 @@ mod wgpu_integration {
wgpu::DeviceDescriptor {
label: None,
features: wgpu::Features::default(),
limits: wgpu::Limits::downlevel_webgl2_defaults(),
limits,
},
wgpu::PresentMode::Fifo,
self.native_options.multisampling.max(1) as _,
self.native_options.depth_buffer,
);
painter.set_window(Some(&window));
painter

View file

@ -121,6 +121,7 @@ struct SizedBuffer {
/// Render pass to render a egui based GUI.
pub struct RenderPass {
render_pipeline: wgpu::RenderPipeline,
depth_texture: Option<(wgpu::Texture, wgpu::TextureView)>,
index_buffers: Vec<SizedBuffer>,
vertex_buffers: Vec<SizedBuffer>,
uniform_buffer: SizedBuffer,
@ -144,6 +145,7 @@ impl RenderPass {
device: &wgpu::Device,
output_format: wgpu::TextureFormat,
msaa_samples: u32,
depth_bits: u8,
) -> Self {
let shader = wgpu::ShaderModuleDescriptor {
label: Some("egui_shader"),
@ -221,6 +223,14 @@ impl RenderPass {
push_constant_ranges: &[],
});
let depth_stencil = (depth_bits > 0).then(|| wgpu::DepthStencilState {
format: wgpu::TextureFormat::Depth32Float,
depth_write_enabled: false,
depth_compare: wgpu::CompareFunction::Always,
stencil: wgpu::StencilState::default(),
bias: wgpu::DepthBiasState::default(),
});
let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("egui_pipeline"),
layout: Some(&pipeline_layout),
@ -249,7 +259,7 @@ impl RenderPass {
polygon_mode: wgpu::PolygonMode::default(),
strip_index_format: None,
},
depth_stencil: None,
depth_stencil,
multisample: wgpu::MultisampleState {
alpha_to_coverage_enabled: false,
count: msaa_samples,
@ -289,9 +299,30 @@ impl RenderPass {
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) {
let texture = device.create_texture(&wgpu::TextureDescriptor {
label: None,
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 egui render pass.
pub fn execute(
&self,
@ -307,6 +338,17 @@ impl RenderPass {
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 rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: color_attachment,
@ -316,7 +358,7 @@ impl RenderPass {
store: true,
},
})],
depth_stencil_attachment: None,
depth_stencil_attachment,
label: Some("egui main render pass"),
});
rpass.push_debug_group("egui_pass");

View file

@ -30,6 +30,7 @@ pub struct Painter<'a> {
device_descriptor: wgpu::DeviceDescriptor<'a>,
present_mode: wgpu::PresentMode,
msaa_samples: u32,
depth_bits: u8,
instance: Instance,
adapter: Option<Adapter>,
@ -56,6 +57,7 @@ impl<'a> Painter<'a> {
device_descriptor: wgpu::DeviceDescriptor<'a>,
present_mode: wgpu::PresentMode,
msaa_samples: u32,
depth_bits: u8,
) -> Self {
let instance = wgpu::Instance::new(backends);
@ -64,6 +66,7 @@ impl<'a> Painter<'a> {
device_descriptor,
present_mode,
msaa_samples,
depth_bits,
instance,
adapter: None,
@ -87,7 +90,8 @@ impl<'a> Painter<'a> {
let (device, queue) =
pollster::block_on(adapter.request_device(&self.device_descriptor, None)).unwrap();
let rpass = renderer::RenderPass::new(&device, target_format, self.msaa_samples);
let rpass =
renderer::RenderPass::new(&device, target_format, self.msaa_samples, self.depth_bits);
RenderState {
device: Arc::new(device),
@ -145,6 +149,14 @@ 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.egui_rpass.write().update_depth_texture(
&render_state.device,
width_in_pixels,
height_in_pixels,
);
}
}
/// Updates (or clears) the [`winit::window::Window`] associated with the [`Painter`]