mirror of
https://github.com/emilk/egui.git
synced 2026-08-31 05:40:03 -04:00
Make kittest's predictable texture filtering honor TextureOptions::NEAREST (#8441)
This commit is contained in:
@@ -98,20 +98,31 @@ fn vs_main(
|
||||
@group(1) @binding(0) var r_tex_color: texture_2d<f32>;
|
||||
@group(1) @binding(1) var r_tex_sampler: sampler;
|
||||
|
||||
/// 1 if the texture sampler uses nearest filtering, 0 if linear.
|
||||
/// Only read when `predictable_texture_filtering` is on.
|
||||
@group(1) @binding(2) var<uniform> r_tex_nearest_filtering: u32;
|
||||
|
||||
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 max_coord = texture_size - vec2<i32>(1, 1);
|
||||
|
||||
if r_tex_nearest_filtering == 1 {
|
||||
// Nearest filtering: load the texel under the sample position.
|
||||
let texel = clamp(vec2<i32>(in.tex_coord * texture_size_f), vec2<i32>(0, 0), max_coord);
|
||||
return textureLoad(r_tex_color, texel, 0);
|
||||
}
|
||||
|
||||
// Manual bilinear filtering with four taps at pixel centers using textureLoad
|
||||
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);
|
||||
|
||||
@@ -245,6 +245,12 @@ pub struct Renderer {
|
||||
uniform_bind_group: wgpu::BindGroup,
|
||||
texture_bind_group_layout: wgpu::BindGroupLayout,
|
||||
|
||||
/// Uniform buffers each holding a single `u32`:
|
||||
/// 1 if the texture sampler uses nearest filtering, 0 otherwise.
|
||||
/// Indexed by that flag value.
|
||||
/// Read by the shader when `predictable_texture_filtering` is on.
|
||||
nearest_filtering_flag_buffers: [wgpu::Buffer; 2],
|
||||
|
||||
/// Map of egui texture IDs to textures and their associated bindgroups (texture view +
|
||||
/// sampler). The texture may be None if the `TextureId` is just a handle to a user-provided
|
||||
/// sampler.
|
||||
@@ -347,10 +353,28 @@ impl Renderer {
|
||||
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
|
||||
count: None,
|
||||
},
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 2,
|
||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||
ty: wgpu::BindingType::Buffer {
|
||||
has_dynamic_offset: false,
|
||||
min_binding_size: NonZeroU64::new(core::mem::size_of::<u32>() as _),
|
||||
ty: wgpu::BufferBindingType::Uniform,
|
||||
},
|
||||
count: None,
|
||||
},
|
||||
],
|
||||
})
|
||||
};
|
||||
|
||||
let nearest_filtering_flag_buffers = [0_u32, 1_u32].map(|flag| {
|
||||
device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||
label: Some(&format!("egui_nearest_filtering_flag_{flag}")),
|
||||
contents: bytemuck::bytes_of(&flag),
|
||||
usage: wgpu::BufferUsages::UNIFORM,
|
||||
})
|
||||
});
|
||||
|
||||
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||
label: Some("egui_pipeline_layout"),
|
||||
bind_group_layouts: &[
|
||||
@@ -458,6 +482,7 @@ impl Renderer {
|
||||
previous_uniform_buffer_content: UniformBuffer::zeroed(),
|
||||
uniform_bind_group,
|
||||
texture_bind_group_layout,
|
||||
nearest_filtering_flag_buffers,
|
||||
textures: HashMap::default(),
|
||||
next_user_texture_id: 0,
|
||||
samplers: HashMap::default(),
|
||||
@@ -709,6 +734,8 @@ impl Renderer {
|
||||
};
|
||||
|
||||
let bind_group = bind_group.unwrap_or_else(|| {
|
||||
let nearest =
|
||||
image_delta.options.magnification == epaint::textures::TextureFilter::Nearest;
|
||||
let sampler = self
|
||||
.samplers
|
||||
.entry(image_delta.options)
|
||||
@@ -727,6 +754,11 @@ impl Renderer {
|
||||
binding: 1,
|
||||
resource: wgpu::BindingResource::Sampler(sampler),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 2,
|
||||
resource: self.nearest_filtering_flag_buffers[usize::from(nearest)]
|
||||
.as_entire_binding(),
|
||||
},
|
||||
],
|
||||
})
|
||||
});
|
||||
@@ -829,6 +861,7 @@ impl Renderer {
|
||||
) -> epaint::TextureId {
|
||||
profiling::function_scope!();
|
||||
|
||||
let nearest = sampler_descriptor.mag_filter == wgpu::FilterMode::Nearest;
|
||||
let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
|
||||
compare: None,
|
||||
..sampler_descriptor
|
||||
@@ -846,6 +879,11 @@ impl Renderer {
|
||||
binding: 1,
|
||||
resource: wgpu::BindingResource::Sampler(&sampler),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 2,
|
||||
resource: self.nearest_filtering_flag_buffers[usize::from(nearest)]
|
||||
.as_entire_binding(),
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
@@ -885,6 +923,7 @@ impl Renderer {
|
||||
.get_mut(&id)
|
||||
.expect("Tried to update a texture that has not been allocated yet.");
|
||||
|
||||
let nearest = sampler_descriptor.mag_filter == wgpu::FilterMode::Nearest;
|
||||
let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
|
||||
compare: None,
|
||||
..sampler_descriptor
|
||||
@@ -902,6 +941,11 @@ impl Renderer {
|
||||
binding: 1,
|
||||
resource: wgpu::BindingResource::Sampler(&sampler),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 2,
|
||||
resource: self.nearest_filtering_flag_buffers[usize::from(nearest)]
|
||||
.as_entire_binding(),
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user