1
0
mirror of https://github.com/emilk/egui.git synced 2026-08-30 13:20:05 -04:00

Use software texture filtering in kittest (#7602)

This commit is contained in:
Emil Ernerfeldt
2025-10-08 16:24:02 +02:00
committed by GitHub
parent 718a82b013
commit 47a437403f
26 changed files with 103 additions and 60 deletions

View File

@@ -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;
@@ -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.
@@ -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.

View File

@@ -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 {
@@ -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 {
@@ -214,6 +219,7 @@ impl RendererOptions {
msaa_samples: 1,
depth_stencil_format: None,
dithering: false,
predictable_texture_filtering: true,
};
}
@@ -223,6 +229,7 @@ impl Default for RendererOptions {
msaa_samples: 0,
depth_stencil_format: None,
dithering: true,
predictable_texture_filtering: false,
}
}
}
@@ -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,
});
@@ -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");