From e5c86a549d944e737deec58e9ecbab599b860c42 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Tue, 7 Oct 2025 16:29:28 +0200 Subject: [PATCH] Clump together wgpu options in a struct --- crates/eframe/src/native/wgpu_integration.rs | 6 ++- crates/egui-wgpu/src/lib.rs | 11 +--- crates/egui-wgpu/src/renderer.rs | 53 +++++++++++++++----- crates/egui-wgpu/src/winit.rs | 25 +++++---- 4 files changed, 59 insertions(+), 36 deletions(-) diff --git a/crates/eframe/src/native/wgpu_integration.rs b/crates/eframe/src/native/wgpu_integration.rs index 53269c51e..849b91f2b 100644 --- a/crates/eframe/src/native/wgpu_integration.rs +++ b/crates/eframe/src/native/wgpu_integration.rs @@ -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.max(1) as _, + dithering: self.native_options.dithering, + }, )); let window = Arc::new(window); diff --git a/crates/egui-wgpu/src/lib.rs b/crates/egui-wgpu/src/lib.rs index 2cd2235de..b4e003a33 100644 --- a/crates/egui-wgpu/src/lib.rs +++ b/crates/egui-wgpu/src/lib.rs @@ -174,8 +174,7 @@ impl RenderState { instance: &wgpu::Instance, compatible_surface: Option<&wgpu::Surface<'static>>, depth_format: Option, - msaa_samples: u32, - dithering: bool, + options: RendererOptions, ) -> Result { profiling::scope!("RenderState::create"); // async yield give bad names using `profile_function` @@ -244,13 +243,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, depth_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. diff --git a/crates/egui-wgpu/src/renderer.rs b/crates/egui-wgpu/src/renderer.rs index 2cb5bda5b..b3a539155 100644 --- a/crates/egui-wgpu/src/renderer.rs +++ b/crates/egui-wgpu/src/renderer.rs @@ -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,39 @@ pub struct Texture { pub options: Option, } +/// 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` 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, + + /// 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 Default for RendererOptions { + fn default() -> Self { + Self { + msaa_samples: 0, + dithering: true, + } + } +} + /// Renderer for a egui based GUI. pub struct Renderer { pipeline: wgpu::RenderPipeline, @@ -194,7 +228,7 @@ pub struct Renderer { next_user_texture_id: u64, samplers: HashMap, - dithering: bool, + options: RendererOptions, /// Storage for resources shared with all invocations of [`CallbackTrait`]'s methods. /// @@ -211,8 +245,7 @@ impl Renderer { device: &wgpu::Device, output_color_format: wgpu::TextureFormat, output_depth_format: Option, - msaa_samples: u32, - dithering: bool, + options: RendererOptions, ) -> Self { profiling::function_scope!(); @@ -229,7 +262,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, @@ -337,7 +370,7 @@ impl Renderer { depth_stencil, multisample: wgpu::MultisampleState { alpha_to_coverage_enabled: false, - count: msaa_samples, + count: options.msaa_samples, mask: !0, }, @@ -392,17 +425,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 +875,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 { diff --git a/crates/egui-wgpu/src/winit.rs b/crates/egui-wgpu/src/winit.rs index 917e704fa..1c0a2ca3d 100644 --- a/crates/egui-wgpu/src/winit.rs +++ b/crates/egui-wgpu/src/winit.rs @@ -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,9 +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, screen_capture_state: Option, @@ -54,10 +56,9 @@ impl Painter { pub async fn new( context: Context, configuration: WgpuConfiguration, - msaa_samples: u32, depth_format: Option, 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,9 +66,8 @@ impl Painter { Self { context, configuration, - msaa_samples, + options, support_transparent_backbuffer, - dithering, depth_format, screen_capture_state: None, @@ -205,8 +205,7 @@ impl Painter { &self.instance, Some(&surface), self.depth_format, - self.msaa_samples, - self.dithering, + self.options, ) .await?; self.render_state.get_or_insert(render_state) @@ -292,7 +291,7 @@ impl Painter { depth_or_array_layers: 1, }, mip_level_count: 1, - sample_count: self.msaa_samples, + sample_count: self.options.msaa_samples, dimension: wgpu::TextureDimension::D2, format: depth_format, usage: wgpu::TextureUsages::RENDER_ATTACHMENT @@ -303,7 +302,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 +319,7 @@ impl Painter { depth_or_array_layers: 1, }, mip_level_count: 1, - sample_count: self.msaa_samples, + sample_count: self.options.msaa_samples, dimension: wgpu::TextureDimension::D2, format: texture_format, usage: wgpu::TextureUsages::RENDER_ATTACHMENT, @@ -450,7 +449,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| {