Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions crates/eframe/src/native/wgpu_integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ impl<'app> WgpuWinitApp<'app> {
self.native_options.stencil_buffer,
),
dithering: self.native_options.dithering,
..Default::default()
},
));

Expand Down
47 changes: 41 additions & 6 deletions crates/egui-wgpu/src/egui.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,13 @@ struct VertexOutput {

struct Locals {
screen_size: vec2<f32>,
dithering: u32, // 1 if dithering is enabled, 0 otherwise
// Uniform buffers need to be at least 16 bytes in WebGL.
// See https://github.com/gfx-rs/wgpu/issues/2072
_padding: u32,

/// 1 if dithering is enabled, 0 otherwise
dithering: u32,

/// 1 to do manual filtering for more predictable kittest snapshot images.
/// See also https://github.com/emilk/egui/issues/5295
predictable_texture_filtering: u32,
};
@group(0) @binding(0) var<uniform> r_locals: Locals;

Expand Down Expand Up @@ -95,10 +98,42 @@ fn vs_main(
@group(1) @binding(0) var r_tex_color: texture_2d<f32>;
@group(1) @binding(1) var r_tex_sampler: sampler;

fn sample_texture(in: VertexOutput) -> vec4<f32> {
if r_locals.predictable_texture_filtering == 0 {
// Hardware filtering: fast, but varies across GPUs and drivers.
return textureSample(r_tex_color, r_tex_sampler, in.tex_coord);
} else {
// Manual bilinear filtering with four taps at pixel centers using textureLoad
let texture_size = vec2<i32>(textureDimensions(r_tex_color, 0));
let texture_size_f = vec2<f32>(texture_size);
let pixel_coord = in.tex_coord * texture_size_f - 0.5;
let pixel_fract = fract(pixel_coord);
let pixel_floor = vec2<i32>(floor(pixel_coord));

// Manual texture clamping
let max_coord = texture_size - vec2<i32>(1, 1);
let p00 = clamp(pixel_floor + vec2<i32>(0, 0), vec2<i32>(0, 0), max_coord);
let p10 = clamp(pixel_floor + vec2<i32>(1, 0), vec2<i32>(0, 0), max_coord);
let p01 = clamp(pixel_floor + vec2<i32>(0, 1), vec2<i32>(0, 0), max_coord);
let p11 = clamp(pixel_floor + vec2<i32>(1, 1), vec2<i32>(0, 0), max_coord);

// Load at pixel centers
let tl = textureLoad(r_tex_color, p00, 0);
let tr = textureLoad(r_tex_color, p10, 0);
let bl = textureLoad(r_tex_color, p01, 0);
let br = textureLoad(r_tex_color, p11, 0);

// Manual bilinear interpolation
let top = mix(tl, tr, pixel_fract.x);
let bottom = mix(bl, br, pixel_fract.x);
return mix(top, bottom, pixel_fract.y);
}
}

@fragment
fn fs_main_linear_framebuffer(in: VertexOutput) -> @location(0) vec4<f32> {
// We expect "normal" textures that are NOT sRGB-aware.
let tex_gamma = textureSample(r_tex_color, r_tex_sampler, in.tex_coord);
let tex_gamma = sample_texture(in);
var out_color_gamma = in.color * tex_gamma;
// Dither the float color down to eight bits to reduce banding.
// This step is optional for egui backends.
Expand All @@ -115,7 +150,7 @@ fn fs_main_linear_framebuffer(in: VertexOutput) -> @location(0) vec4<f32> {
@fragment
fn fs_main_gamma_framebuffer(in: VertexOutput) -> @location(0) vec4<f32> {
// We expect "normal" textures that are NOT sRGB-aware.
let tex_gamma = textureSample(r_tex_color, r_tex_sampler, in.tex_coord);
let tex_gamma = sample_texture(in);
var out_color_gamma = in.color * tex_gamma;
// Dither the float color down to eight bits to reduce banding.
// This step is optional for egui backends.
Expand Down
31 changes: 19 additions & 12 deletions crates/egui-wgpu/src/renderer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,21 +141,16 @@ impl ScreenDescriptor {
}

/// Uniform buffer used when rendering.
#[derive(Clone, Copy, Debug, bytemuck::Pod, bytemuck::Zeroable)]
#[derive(Clone, Copy, Debug, PartialEq, bytemuck::Pod, bytemuck::Zeroable)]
#[repr(C)]
struct UniformBuffer {
screen_size_in_points: [f32; 2],
dithering: u32,
// Uniform buffers need to be at least 16 bytes in WebGL.
// See https://github.com/gfx-rs/wgpu/issues/2072
_padding: u32,
}

impl PartialEq for UniformBuffer {
fn eq(&self, other: &Self) -> bool {
self.screen_size_in_points == other.screen_size_in_points
&& self.dithering == other.dithering
}
/// 1 to do manual filtering for more predictable kittest snapshot images.
///
/// See also <https://github.com/emilk/egui/issues/5295>.
predictable_texture_filtering: u32,
}

struct SlicedBuffer {
Expand Down Expand Up @@ -204,6 +199,16 @@ pub struct RendererOptions {
///
/// Defaults to true.
pub dithering: bool,

/// Perform texture filtering in software?
///
/// This is useful when you want predictable rendering across
/// different hardware, e.g. for kittest snapshots.
///
/// Default is `false`.
///
/// See also <https://github.com/emilk/egui/issues/5295>.
pub predictable_texture_filtering: bool,
}

impl RendererOptions {
Expand All @@ -214,6 +219,7 @@ impl RendererOptions {
msaa_samples: 1,
depth_stencil_format: None,
dithering: false,
predictable_texture_filtering: true,
};
}

Expand All @@ -223,6 +229,7 @@ impl Default for RendererOptions {
msaa_samples: 0,
depth_stencil_format: None,
dithering: true,
predictable_texture_filtering: false,
}
}
}
Expand Down Expand Up @@ -280,7 +287,7 @@ impl Renderer {
contents: bytemuck::cast_slice(&[UniformBuffer {
screen_size_in_points: [0.0, 0.0],
dithering: u32::from(options.dithering),
_padding: Default::default(),
predictable_texture_filtering: u32::from(options.predictable_texture_filtering),
}]),
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
});
Expand Down Expand Up @@ -895,7 +902,7 @@ impl Renderer {
let uniform_buffer_content = UniformBuffer {
screen_size_in_points,
dithering: u32::from(self.options.dithering),
_padding: Default::default(),
predictable_texture_filtering: u32::from(self.options.predictable_texture_filtering),
};
if uniform_buffer_content != self.previous_uniform_buffer_content {
profiling::scope!("update uniforms");
Expand Down
4 changes: 2 additions & 2 deletions crates/egui_demo_app/tests/snapshots/imageviewer.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions crates/egui_demo_lib/tests/snapshots/demos/Scene.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.