Create `egui_wgpu::RendererOptions` (#7601)
This commit is contained in:
parent
56b1def064
commit
9cb4e6a54e
|
|
@ -186,13 +186,15 @@ impl<'app> WgpuWinitApp<'app> {
|
|||
let mut painter = pollster::block_on(egui_wgpu::winit::Painter::new(
|
||||
egui_ctx.clone(),
|
||||
self.native_options.wgpu_options.clone(),
|
||||
self.native_options.multisampling.max(1) as _,
|
||||
egui_wgpu::depth_format_from_bits(
|
||||
self.native_options.depth_buffer,
|
||||
self.native_options.stencil_buffer,
|
||||
),
|
||||
self.native_options.viewport.transparent.unwrap_or(false),
|
||||
self.native_options.dithering,
|
||||
egui_wgpu::RendererOptions {
|
||||
msaa_samples: self.native_options.multisampling as _,
|
||||
depth_stencil_format: egui_wgpu::depth_format_from_bits(
|
||||
self.native_options.depth_buffer,
|
||||
self.native_options.stencil_buffer,
|
||||
),
|
||||
dithering: self.native_options.dithering,
|
||||
},
|
||||
));
|
||||
|
||||
let window = Arc::new(window);
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ pub(crate) struct WebPainterWgpu {
|
|||
surface_configuration: wgpu::SurfaceConfiguration,
|
||||
render_state: Option<RenderState>,
|
||||
on_surface_error: Arc<dyn Fn(wgpu::SurfaceError) -> SurfaceErrorAction>,
|
||||
depth_format: Option<wgpu::TextureFormat>,
|
||||
depth_stencil_format: Option<wgpu::TextureFormat>,
|
||||
depth_texture_view: Option<wgpu::TextureView>,
|
||||
screen_capture_state: Option<CaptureState>,
|
||||
capture_tx: CaptureSender,
|
||||
|
|
@ -35,7 +35,7 @@ impl WebPainterWgpu {
|
|||
height_in_pixels: u32,
|
||||
) -> Option<wgpu::TextureView> {
|
||||
let device = &render_state.device;
|
||||
self.depth_format.map(|depth_format| {
|
||||
self.depth_stencil_format.map(|depth_stencil_format| {
|
||||
device
|
||||
.create_texture(&wgpu::TextureDescriptor {
|
||||
label: Some("egui_depth_texture"),
|
||||
|
|
@ -47,9 +47,9 @@ impl WebPainterWgpu {
|
|||
mip_level_count: 1,
|
||||
sample_count: 1,
|
||||
dimension: wgpu::TextureDimension::D2,
|
||||
format: depth_format,
|
||||
format: depth_stencil_format,
|
||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
||||
view_formats: &[depth_format],
|
||||
view_formats: &[depth_stencil_format],
|
||||
})
|
||||
.create_view(&wgpu::TextureViewDescriptor::default())
|
||||
})
|
||||
|
|
@ -68,15 +68,17 @@ impl WebPainterWgpu {
|
|||
.create_surface(wgpu::SurfaceTarget::Canvas(canvas.clone()))
|
||||
.map_err(|err| format!("failed to create wgpu surface: {err}"))?;
|
||||
|
||||
let depth_format = egui_wgpu::depth_format_from_bits(options.depth_buffer, 0);
|
||||
let depth_stencil_format = egui_wgpu::depth_format_from_bits(options.depth_buffer, 0);
|
||||
|
||||
let render_state = RenderState::create(
|
||||
&options.wgpu_options,
|
||||
&instance,
|
||||
Some(&surface),
|
||||
depth_format,
|
||||
1,
|
||||
options.dithering,
|
||||
egui_wgpu::RendererOptions {
|
||||
dithering: options.dithering,
|
||||
depth_stencil_format,
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await
|
||||
.map_err(|err| err.to_string())?;
|
||||
|
|
@ -101,7 +103,7 @@ impl WebPainterWgpu {
|
|||
render_state: Some(render_state),
|
||||
surface,
|
||||
surface_configuration,
|
||||
depth_format,
|
||||
depth_stencil_format,
|
||||
depth_texture_view: None,
|
||||
on_surface_error: options.wgpu_options.on_surface_error.clone(),
|
||||
screen_capture_state: None,
|
||||
|
|
|
|||
|
|
@ -173,9 +173,7 @@ impl RenderState {
|
|||
config: &WgpuConfiguration,
|
||||
instance: &wgpu::Instance,
|
||||
compatible_surface: Option<&wgpu::Surface<'static>>,
|
||||
depth_format: Option<wgpu::TextureFormat>,
|
||||
msaa_samples: u32,
|
||||
dithering: bool,
|
||||
options: RendererOptions,
|
||||
) -> Result<Self, WgpuError> {
|
||||
profiling::scope!("RenderState::create"); // async yield give bad names using `profile_function`
|
||||
|
||||
|
|
@ -244,13 +242,7 @@ impl RenderState {
|
|||
};
|
||||
let target_format = crate::preferred_framebuffer_format(&surface_formats)?;
|
||||
|
||||
let renderer = Renderer::new(
|
||||
&device,
|
||||
target_format,
|
||||
depth_format,
|
||||
msaa_samples,
|
||||
dithering,
|
||||
);
|
||||
let renderer = Renderer::new(&device, target_format, options);
|
||||
|
||||
// On wasm, depending on feature flags, wgpu objects may or may not implement sync.
|
||||
// It doesn't make sense to switch to Rc for that special usecase, so simply disable the lint.
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
use std::{borrow::Cow, num::NonZeroU64, ops::Range};
|
||||
|
||||
use ahash::HashMap;
|
||||
use bytemuck::Zeroable as _;
|
||||
use epaint::{PaintCallbackInfo, Primitive, Vertex, emath::NumExt as _};
|
||||
|
||||
use wgpu::util::DeviceExt as _;
|
||||
|
|
@ -175,6 +176,57 @@ pub struct Texture {
|
|||
pub options: Option<epaint::textures::TextureOptions>,
|
||||
}
|
||||
|
||||
/// Ways to configure [`Renderer`] during creation.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct RendererOptions {
|
||||
/// Set the level of the multisampling anti-aliasing (MSAA).
|
||||
///
|
||||
/// Must be a power-of-two. Higher = more smooth 3D.
|
||||
///
|
||||
/// A value of `0` or `1` turns it off (default).
|
||||
///
|
||||
/// `egui` already performs anti-aliasing via "feathering"
|
||||
/// (controlled by [`egui::epaint::TessellationOptions`]),
|
||||
/// but if you are embedding 3D in egui you may want to turn on multisampling.
|
||||
pub msaa_samples: u32,
|
||||
|
||||
/// What format to use for the depth and stencil buffers,
|
||||
/// e.g. [`wgpu::TextureFormat::Depth32FloatStencil8`].
|
||||
///
|
||||
/// egui doesn't need depth/stencil, so the default value is `None` (no depth or stancil buffers).
|
||||
pub depth_stencil_format: Option<wgpu::TextureFormat>,
|
||||
|
||||
/// Controls whether to apply dithering to minimize banding artifacts.
|
||||
///
|
||||
/// Dithering assumes an sRGB output and thus will apply noise to any input value that lies between
|
||||
/// two 8bit values after applying the sRGB OETF function, i.e. if it's not a whole 8bit value in "gamma space".
|
||||
/// This means that only inputs from texture interpolation and vertex colors should be affected in practice.
|
||||
///
|
||||
/// Defaults to true.
|
||||
pub dithering: bool,
|
||||
}
|
||||
|
||||
impl RendererOptions {
|
||||
/// Set options that produce the most predicatable output.
|
||||
///
|
||||
/// Useful for image snapshot tests.
|
||||
pub const PREDICTABLE: Self = Self {
|
||||
msaa_samples: 1,
|
||||
depth_stencil_format: None,
|
||||
dithering: false,
|
||||
};
|
||||
}
|
||||
|
||||
impl Default for RendererOptions {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
msaa_samples: 0,
|
||||
depth_stencil_format: None,
|
||||
dithering: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Renderer for a egui based GUI.
|
||||
pub struct Renderer {
|
||||
pipeline: wgpu::RenderPipeline,
|
||||
|
|
@ -194,7 +246,7 @@ pub struct Renderer {
|
|||
next_user_texture_id: u64,
|
||||
samplers: HashMap<epaint::textures::TextureOptions, wgpu::Sampler>,
|
||||
|
||||
dithering: bool,
|
||||
options: RendererOptions,
|
||||
|
||||
/// Storage for resources shared with all invocations of [`CallbackTrait`]'s methods.
|
||||
///
|
||||
|
|
@ -210,9 +262,7 @@ impl Renderer {
|
|||
pub fn new(
|
||||
device: &wgpu::Device,
|
||||
output_color_format: wgpu::TextureFormat,
|
||||
output_depth_format: Option<wgpu::TextureFormat>,
|
||||
msaa_samples: u32,
|
||||
dithering: bool,
|
||||
options: RendererOptions,
|
||||
) -> Self {
|
||||
profiling::function_scope!();
|
||||
|
||||
|
|
@ -229,7 +279,7 @@ impl Renderer {
|
|||
label: Some("egui_uniform_buffer"),
|
||||
contents: bytemuck::cast_slice(&[UniformBuffer {
|
||||
screen_size_in_points: [0.0, 0.0],
|
||||
dithering: u32::from(dithering),
|
||||
dithering: u32::from(options.dithering),
|
||||
_padding: Default::default(),
|
||||
}]),
|
||||
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
||||
|
|
@ -299,13 +349,15 @@ impl Renderer {
|
|||
push_constant_ranges: &[],
|
||||
});
|
||||
|
||||
let depth_stencil = output_depth_format.map(|format| wgpu::DepthStencilState {
|
||||
format,
|
||||
depth_write_enabled: false,
|
||||
depth_compare: wgpu::CompareFunction::Always,
|
||||
stencil: wgpu::StencilState::default(),
|
||||
bias: wgpu::DepthBiasState::default(),
|
||||
});
|
||||
let depth_stencil = options
|
||||
.depth_stencil_format
|
||||
.map(|format| wgpu::DepthStencilState {
|
||||
format,
|
||||
depth_write_enabled: false,
|
||||
depth_compare: wgpu::CompareFunction::Always,
|
||||
stencil: wgpu::StencilState::default(),
|
||||
bias: wgpu::DepthBiasState::default(),
|
||||
});
|
||||
|
||||
let pipeline = {
|
||||
profiling::scope!("create_render_pipeline");
|
||||
|
|
@ -337,7 +389,7 @@ impl Renderer {
|
|||
depth_stencil,
|
||||
multisample: wgpu::MultisampleState {
|
||||
alpha_to_coverage_enabled: false,
|
||||
count: msaa_samples,
|
||||
count: options.msaa_samples.max(1),
|
||||
mask: !0,
|
||||
},
|
||||
|
||||
|
|
@ -392,17 +444,13 @@ impl Renderer {
|
|||
},
|
||||
uniform_buffer,
|
||||
// Buffers on wgpu are zero initialized, so this is indeed its current state!
|
||||
previous_uniform_buffer_content: UniformBuffer {
|
||||
screen_size_in_points: [0.0, 0.0],
|
||||
dithering: 0,
|
||||
_padding: 0,
|
||||
},
|
||||
previous_uniform_buffer_content: UniformBuffer::zeroed(),
|
||||
uniform_bind_group,
|
||||
texture_bind_group_layout,
|
||||
textures: HashMap::default(),
|
||||
next_user_texture_id: 0,
|
||||
samplers: HashMap::default(),
|
||||
dithering,
|
||||
options,
|
||||
callback_resources: CallbackResources::default(),
|
||||
}
|
||||
}
|
||||
|
|
@ -846,7 +894,7 @@ impl Renderer {
|
|||
|
||||
let uniform_buffer_content = UniformBuffer {
|
||||
screen_size_in_points,
|
||||
dithering: u32::from(self.dithering),
|
||||
dithering: u32::from(self.options.dithering),
|
||||
_padding: Default::default(),
|
||||
};
|
||||
if uniform_buffer_content != self.previous_uniform_buffer_content {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,11 @@
|
|||
#![allow(clippy::missing_errors_doc)]
|
||||
#![allow(clippy::undocumented_unsafe_blocks)]
|
||||
|
||||
use crate::capture::{CaptureReceiver, CaptureSender, CaptureState, capture_channel};
|
||||
use crate::{RenderState, SurfaceErrorAction, WgpuConfiguration, renderer};
|
||||
use crate::{
|
||||
RendererOptions,
|
||||
capture::{CaptureReceiver, CaptureSender, CaptureState, capture_channel},
|
||||
};
|
||||
use egui::{Context, Event, UserData, ViewportId, ViewportIdMap, ViewportIdSet};
|
||||
use std::{num::NonZeroU32, sync::Arc};
|
||||
|
||||
|
|
@ -21,10 +24,8 @@ struct SurfaceState {
|
|||
pub struct Painter {
|
||||
context: Context,
|
||||
configuration: WgpuConfiguration,
|
||||
msaa_samples: u32,
|
||||
options: RendererOptions,
|
||||
support_transparent_backbuffer: bool,
|
||||
dithering: bool,
|
||||
depth_format: Option<wgpu::TextureFormat>,
|
||||
screen_capture_state: Option<CaptureState>,
|
||||
|
||||
instance: wgpu::Instance,
|
||||
|
|
@ -54,10 +55,8 @@ impl Painter {
|
|||
pub async fn new(
|
||||
context: Context,
|
||||
configuration: WgpuConfiguration,
|
||||
msaa_samples: u32,
|
||||
depth_format: Option<wgpu::TextureFormat>,
|
||||
support_transparent_backbuffer: bool,
|
||||
dithering: bool,
|
||||
options: RendererOptions,
|
||||
) -> Self {
|
||||
let (capture_tx, capture_rx) = capture_channel();
|
||||
let instance = configuration.wgpu_setup.new_instance().await;
|
||||
|
|
@ -65,10 +64,8 @@ impl Painter {
|
|||
Self {
|
||||
context,
|
||||
configuration,
|
||||
msaa_samples,
|
||||
options,
|
||||
support_transparent_backbuffer,
|
||||
dithering,
|
||||
depth_format,
|
||||
screen_capture_state: None,
|
||||
|
||||
instance,
|
||||
|
|
@ -204,9 +201,7 @@ impl Painter {
|
|||
&self.configuration,
|
||||
&self.instance,
|
||||
Some(&surface),
|
||||
self.depth_format,
|
||||
self.msaa_samples,
|
||||
self.dithering,
|
||||
self.options,
|
||||
)
|
||||
.await?;
|
||||
self.render_state.get_or_insert(render_state)
|
||||
|
|
@ -279,7 +274,7 @@ impl Painter {
|
|||
|
||||
Self::configure_surface(surface_state, render_state, &self.configuration);
|
||||
|
||||
if let Some(depth_format) = self.depth_format {
|
||||
if let Some(depth_format) = self.options.depth_stencil_format {
|
||||
self.depth_texture_view.insert(
|
||||
viewport_id,
|
||||
render_state
|
||||
|
|
@ -292,7 +287,7 @@ impl Painter {
|
|||
depth_or_array_layers: 1,
|
||||
},
|
||||
mip_level_count: 1,
|
||||
sample_count: self.msaa_samples,
|
||||
sample_count: self.options.msaa_samples.max(1),
|
||||
dimension: wgpu::TextureDimension::D2,
|
||||
format: depth_format,
|
||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT
|
||||
|
|
@ -303,7 +298,7 @@ impl Painter {
|
|||
);
|
||||
}
|
||||
|
||||
if let Some(render_state) = (self.msaa_samples > 1)
|
||||
if let Some(render_state) = (self.options.msaa_samples > 1)
|
||||
.then_some(self.render_state.as_ref())
|
||||
.flatten()
|
||||
{
|
||||
|
|
@ -320,7 +315,7 @@ impl Painter {
|
|||
depth_or_array_layers: 1,
|
||||
},
|
||||
mip_level_count: 1,
|
||||
sample_count: self.msaa_samples,
|
||||
sample_count: self.options.msaa_samples.max(1),
|
||||
dimension: wgpu::TextureDimension::D2,
|
||||
format: texture_format,
|
||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
||||
|
|
@ -450,7 +445,7 @@ impl Painter {
|
|||
};
|
||||
let target_view = target_texture.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
|
||||
let (view, resolve_target) = (self.msaa_samples > 1)
|
||||
let (view, resolve_target) = (self.options.msaa_samples > 1)
|
||||
.then_some(self.msaa_texture_view.get(&viewport_id))
|
||||
.flatten()
|
||||
.map_or((&target_view, None), |texture_view| {
|
||||
|
|
|
|||
|
|
@ -67,9 +67,7 @@ pub fn create_render_state(setup: WgpuSetup) -> egui_wgpu::RenderState {
|
|||
},
|
||||
&instance,
|
||||
None,
|
||||
None,
|
||||
1,
|
||||
false,
|
||||
egui_wgpu::RendererOptions::PREDICTABLE,
|
||||
))
|
||||
.expect("Failed to create render state")
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue