Skip to content

Commit

Permalink
Re-enable bilinear interpolation again (#1860)
Browse files Browse the repository at this point in the history
* Revert "Revert "Implement billinear filtering of textures (#1850)" (#1859)"

This reverts commit 625d2bd.

* Split rectangle.wgsl into fragme/vertex parts to work around naga bug
  • Loading branch information
emilk authored Apr 17, 2023
1 parent 87906dc commit adf9856
Show file tree
Hide file tree
Showing 6 changed files with 188 additions and 102 deletions.
80 changes: 8 additions & 72 deletions crates/re_renderer/shader/rectangle.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ const COLOR_MAPPER_OFF = 1u;
const COLOR_MAPPER_FUNCTION = 2u;
const COLOR_MAPPER_TEXTURE = 3u;

const FILTER_NEAREST = 1u;
const FILTER_BILINEAR = 2u;

struct UniformBuffer {
/// Top left corner position in world space.
top_left_corner_position: Vec3,
Expand Down Expand Up @@ -48,6 +51,9 @@ struct UniformBuffer {
/// Exponent to raise the normalized texture value.
/// Inverse brightness.
gamma: f32,

minification_filter: u32,
magnification_filter: u32,
};

@group(1) @binding(0)
Expand All @@ -71,80 +77,10 @@ var colormap_texture: texture_2d<f32>;
@group(1) @binding(6)
var texture_float_filterable: texture_2d<f32>;


struct VertexOut {
@builtin(position) position: Vec4,
@location(0) texcoord: Vec2,
};

@vertex
fn vs_main(@builtin(vertex_index) v_idx: u32) -> VertexOut {
let texcoord = Vec2(f32(v_idx / 2u), f32(v_idx % 2u));
let pos = texcoord.x * rect_info.extent_u + texcoord.y * rect_info.extent_v +
rect_info.top_left_corner_position;

var out: VertexOut;
out.position = apply_depth_offset(frame.projection_from_world * Vec4(pos, 1.0), rect_info.depth_offset);
out.texcoord = texcoord;

return out;
}

@fragment
fn fs_main(in: VertexOut) -> @location(0) Vec4 {
// Sample the main texture:
var sampled_value: Vec4;
if rect_info.sample_type == SAMPLE_TYPE_FLOAT_FILTER {
// TODO(emilk): support mipmaps
sampled_value = textureSampleLevel(texture_float_filterable, texture_sampler, in.texcoord, 0.0);
} else if rect_info.sample_type == SAMPLE_TYPE_FLOAT_NOFILTER {
let icoords = IVec2(in.texcoord * Vec2(textureDimensions(texture_float).xy));
sampled_value = Vec4(textureLoad(texture_float, icoords, 0));
} else if rect_info.sample_type == SAMPLE_TYPE_SINT_NOFILTER {
let icoords = IVec2(in.texcoord * Vec2(textureDimensions(texture_sint).xy));
sampled_value = Vec4(textureLoad(texture_sint, icoords, 0));
} else if rect_info.sample_type == SAMPLE_TYPE_UINT_NOFILTER {
let icoords = IVec2(in.texcoord * Vec2(textureDimensions(texture_uint).xy));
sampled_value = Vec4(textureLoad(texture_uint, icoords, 0));
} else {
return ERROR_RGBA; // unknown sample type
}

// Normalize the sample:
let range = rect_info.range_min_max;
var normalized_value: Vec4 = (sampled_value - range.x) / (range.y - range.x);

// Apply gamma:
normalized_value = vec4(pow(normalized_value.rgb, vec3(rect_info.gamma)), normalized_value.a); // TODO(emilk): handle premultiplied alpha

// Apply colormap, if any:
var texture_color: Vec4;
if rect_info.color_mapper == COLOR_MAPPER_OFF {
texture_color = normalized_value;
} else if rect_info.color_mapper == COLOR_MAPPER_FUNCTION {
let rgb = colormap_linear(rect_info.colormap_function, normalized_value.r);
texture_color = Vec4(rgb, 1.0);
} else if rect_info.color_mapper == COLOR_MAPPER_TEXTURE {
let colormap_size = textureDimensions(colormap_texture).xy;
let color_index = normalized_value.r * f32(colormap_size.x * colormap_size.y);
// TODO(emilk): interpolate between neighboring colors for non-integral color indices
let color_index_i32 = i32(color_index);
let x = color_index_i32 % colormap_size.x;
let y = color_index_i32 / colormap_size.x;
texture_color = textureLoad(colormap_texture, IVec2(x, y), 0);
} else {
return ERROR_RGBA; // unknown color mapper
}

return texture_color * rect_info.multiplicative_tint;
}

@fragment
fn fs_main_picking_layer(in: VertexOut) -> @location(0) UVec4 {
return UVec4(0u, 0u, 0u, 0u); // TODO(andreas): Implement picking layer id pass-through.
}

@fragment
fn fs_main_outline_mask(in: VertexOut) -> @location(0) UVec2 {
return rect_info.outline_mask;
}
// The fragment and vertex shaders are in two separate files in order
// to work around this bug: https://github.com/gfx-rs/naga/issues/1743
108 changes: 108 additions & 0 deletions crates/re_renderer/shader/rectangle_fs.wgsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
#import <./rectangle.wgsl>

fn is_magnifying(pixel_coord: Vec2) -> bool {
return fwidth(pixel_coord.x) < 1.0;
}

fn tex_filter(pixel_coord: Vec2) -> u32 {
if is_magnifying(pixel_coord) {
return rect_info.magnification_filter;
} else {
return rect_info.minification_filter;
}
}

@fragment
fn fs_main(in: VertexOut) -> @location(0) Vec4 {
// Sample the main texture:
var sampled_value: Vec4;
if rect_info.sample_type == SAMPLE_TYPE_FLOAT_FILTER {
// TODO(emilk): support mipmaps
sampled_value = textureSampleLevel(texture_float_filterable, texture_sampler, in.texcoord, 0.0);
} else if rect_info.sample_type == SAMPLE_TYPE_FLOAT_NOFILTER {
let coord = in.texcoord * Vec2(textureDimensions(texture_float).xy);
if tex_filter(coord) == FILTER_NEAREST {
// nearest
sampled_value = textureLoad(texture_float, IVec2(coord + vec2(0.5)), 0);
} else {
// bilinear
let v00 = textureLoad(texture_float, IVec2(coord) + IVec2(0, 0), 0);
let v01 = textureLoad(texture_float, IVec2(coord) + IVec2(0, 1), 0);
let v10 = textureLoad(texture_float, IVec2(coord) + IVec2(1, 0), 0);
let v11 = textureLoad(texture_float, IVec2(coord) + IVec2(1, 1), 0);
let top = mix(v00, v10, fract(coord.x));
let bottom = mix(v01, v11, fract(coord.x));
sampled_value = mix(top, bottom, fract(coord.y));
}
} else if rect_info.sample_type == SAMPLE_TYPE_SINT_NOFILTER {
let coord = in.texcoord * Vec2(textureDimensions(texture_sint).xy);
if tex_filter(coord) == FILTER_NEAREST {
// nearest
sampled_value = Vec4(textureLoad(texture_sint, IVec2(coord + vec2(0.5)), 0));
} else {
// bilinear
let v00 = Vec4(textureLoad(texture_sint, IVec2(coord) + IVec2(0, 0), 0));
let v01 = Vec4(textureLoad(texture_sint, IVec2(coord) + IVec2(0, 1), 0));
let v10 = Vec4(textureLoad(texture_sint, IVec2(coord) + IVec2(1, 0), 0));
let v11 = Vec4(textureLoad(texture_sint, IVec2(coord) + IVec2(1, 1), 0));
let top = mix(v00, v10, fract(coord.x));
let bottom = mix(v01, v11, fract(coord.x));
sampled_value = mix(top, bottom, fract(coord.y));
}
} else if rect_info.sample_type == SAMPLE_TYPE_UINT_NOFILTER {
let coord = in.texcoord * Vec2(textureDimensions(texture_uint).xy);
if tex_filter(coord) == FILTER_NEAREST {
// nearest
sampled_value = Vec4(textureLoad(texture_uint, IVec2(coord + vec2(0.5)), 0));
} else {
// bilinear
let v00 = Vec4(textureLoad(texture_uint, IVec2(coord) + IVec2(0, 0), 0));
let v01 = Vec4(textureLoad(texture_uint, IVec2(coord) + IVec2(0, 1), 0));
let v10 = Vec4(textureLoad(texture_uint, IVec2(coord) + IVec2(1, 0), 0));
let v11 = Vec4(textureLoad(texture_uint, IVec2(coord) + IVec2(1, 1), 0));
let top = mix(v00, v10, fract(coord.x));
let bottom = mix(v01, v11, fract(coord.x));
sampled_value = mix(top, bottom, fract(coord.y));
}
} else {
return ERROR_RGBA; // unknown sample type
}

// Normalize the sample:
let range = rect_info.range_min_max;
var normalized_value: Vec4 = (sampled_value - range.x) / (range.y - range.x);

// Apply gamma:
normalized_value = vec4(pow(normalized_value.rgb, vec3(rect_info.gamma)), normalized_value.a); // TODO(emilk): handle premultiplied alpha

// Apply colormap, if any:
var texture_color: Vec4;
if rect_info.color_mapper == COLOR_MAPPER_OFF {
texture_color = normalized_value;
} else if rect_info.color_mapper == COLOR_MAPPER_FUNCTION {
let rgb = colormap_linear(rect_info.colormap_function, normalized_value.r);
texture_color = Vec4(rgb, 1.0);
} else if rect_info.color_mapper == COLOR_MAPPER_TEXTURE {
let colormap_size = textureDimensions(colormap_texture).xy;
let color_index = normalized_value.r * f32(colormap_size.x * colormap_size.y);
// TODO(emilk): interpolate between neighboring colors for non-integral color indices
let color_index_i32 = i32(color_index);
let x = color_index_i32 % colormap_size.x;
let y = color_index_i32 / colormap_size.x;
texture_color = textureLoad(colormap_texture, IVec2(x, y), 0);
} else {
return ERROR_RGBA; // unknown color mapper
}

return texture_color * rect_info.multiplicative_tint;
}

@fragment
fn fs_main_picking_layer(in: VertexOut) -> @location(0) UVec4 {
return UVec4(0u, 0u, 0u, 0u); // TODO(andreas): Implement picking layer id pass-through.
}

@fragment
fn fs_main_outline_mask(in: VertexOut) -> @location(0) UVec2 {
return rect_info.outline_mask;
}
14 changes: 14 additions & 0 deletions crates/re_renderer/shader/rectangle_vs.wgsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#import <./rectangle.wgsl>

@vertex
fn vs_main(@builtin(vertex_index) v_idx: u32) -> VertexOut {
let texcoord = Vec2(f32(v_idx / 2u), f32(v_idx % 2u));
let pos = texcoord.x * rect_info.extent_u + texcoord.y * rect_info.extent_v +
rect_info.top_left_corner_position;

var out: VertexOut;
out.position = apply_depth_offset(frame.projection_from_world * Vec4(pos, 1.0), rect_info.depth_offset);
out.texcoord = texcoord;

return out;
}
31 changes: 25 additions & 6 deletions crates/re_renderer/src/renderer/rectangles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,9 @@ mod gpu_data {
const COLOR_MAPPER_FUNCTION: u32 = 2;
const COLOR_MAPPER_TEXTURE: u32 = 3;

const FILTER_NEAREST: u32 = 1;
const FILTER_BILINEAR: u32 = 2;

#[repr(C, align(256))]
#[derive(Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
pub struct UniformBuffer {
Expand All @@ -213,7 +216,8 @@ mod gpu_data {

color_mapper: u32,
gamma: f32,
_row_padding: [u32; 2],
minification_filter: u32,
magnification_filter: u32,

_end_padding: [wgpu_buffer_types::PaddingRow; 16 - 6],
}
Expand Down Expand Up @@ -275,6 +279,15 @@ mod gpu_data {
}
}

let minification_filter = match rectangle.texture_filter_minification {
super::TextureFilterMin::Linear => FILTER_BILINEAR,
super::TextureFilterMin::Nearest => FILTER_NEAREST,
};
let magnification_filter = match rectangle.texture_filter_magnification {
super::TextureFilterMag::Linear => FILTER_BILINEAR,
super::TextureFilterMag::Nearest => FILTER_NEAREST,
};

Ok(Self {
top_left_corner_position: rectangle.top_left_corner_position.into(),
colormap_function,
Expand All @@ -287,7 +300,8 @@ mod gpu_data {
range_min_max: (*range).into(),
color_mapper: color_mapper_int,
gamma: *gamma,
_row_padding: Default::default(),
minification_filter,
magnification_filter,
_end_padding: Default::default(),
})
}
Expand Down Expand Up @@ -564,19 +578,24 @@ impl Renderer for RectangleRenderer {
&pools.bind_group_layouts,
);

let shader_module = pools.shader_modules.get_or_create(
let shader_module_vs = pools.shader_modules.get_or_create(
device,
resolver,
&include_shader_module!("../../shader/rectangle_vs.wgsl"),
);
let shader_module_fs = pools.shader_modules.get_or_create(
device,
resolver,
&include_shader_module!("../../shader/rectangle.wgsl"),
&include_shader_module!("../../shader/rectangle_fs.wgsl"),
);

let render_pipeline_desc_color = RenderPipelineDesc {
label: "RectangleRenderer::render_pipeline_color".into(),
pipeline_layout,
vertex_entrypoint: "vs_main".into(),
vertex_handle: shader_module,
vertex_handle: shader_module_vs,
fragment_entrypoint: "fs_main".into(),
fragment_handle: shader_module,
fragment_handle: shader_module_fs,
vertex_buffers: smallvec![],
render_targets: smallvec![Some(wgpu::ColorTargetState {
format: ViewBuilder::MAIN_TARGET_COLOR_FORMAT,
Expand Down
12 changes: 12 additions & 0 deletions crates/re_renderer/src/workspace_shaders.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,18 @@ pub fn init() {
fs.create_file(virtpath, content).unwrap();
}

{
let virtpath = Path::new("shader/rectangle_fs.wgsl");
let content = include_str!("../shader/rectangle_fs.wgsl").into();
fs.create_file(virtpath, content).unwrap();
}

{
let virtpath = Path::new("shader/rectangle_vs.wgsl");
let content = include_str!("../shader/rectangle_vs.wgsl").into();
fs.create_file(virtpath, content).unwrap();
}

{
let virtpath = Path::new("shader/screen_triangle.wgsl");
let content = include_str!("../shader/screen_triangle.wgsl").into();
Expand Down
45 changes: 21 additions & 24 deletions crates/re_viewer/src/ui/view_tensor/ui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -426,32 +426,29 @@ impl TextureSettings {
});
ui.end_row();

// TODO(#1612): support texture filtering again
if false {
re_ui
.grid_left_hand_label(ui, "Filtering")
.on_hover_text("Filtering to use when magnifying");

fn tf_to_string(tf: egui::TextureFilter) -> &'static str {
match tf {
egui::TextureFilter::Nearest => "Nearest",
egui::TextureFilter::Linear => "Linear",
}
re_ui
.grid_left_hand_label(ui, "Filtering")
.on_hover_text("Filtering to use when magnifying");

fn tf_to_string(tf: egui::TextureFilter) -> &'static str {
match tf {
egui::TextureFilter::Nearest => "Nearest",
egui::TextureFilter::Linear => "Linear",
}
egui::ComboBox::from_id_source("texture_filter")
.selected_text(tf_to_string(options.magnification))
.show_ui(ui, |ui| {
ui.style_mut().wrap = Some(false);
ui.set_min_width(64.0);

let mut selectable_value = |ui: &mut egui::Ui, e| {
ui.selectable_value(&mut options.magnification, e, tf_to_string(e))
};
selectable_value(ui, egui::TextureFilter::Linear);
selectable_value(ui, egui::TextureFilter::Nearest);
});
ui.end_row();
}
egui::ComboBox::from_id_source("texture_filter")
.selected_text(tf_to_string(options.magnification))
.show_ui(ui, |ui| {
ui.style_mut().wrap = Some(false);
ui.set_min_width(64.0);

let mut selectable_value = |ui: &mut egui::Ui, e| {
ui.selectable_value(&mut options.magnification, e, tf_to_string(e))
};
selectable_value(ui, egui::TextureFilter::Nearest);
selectable_value(ui, egui::TextureFilter::Linear);
});
ui.end_row();
}
}

Expand Down

1 comment on commit adf9856

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rust Benchmark

Benchmark suite Current: adf9856 Previous: 43aa22c Ratio
datastore/num_rows=1000/num_instances=1000/packed=false/insert/default 3884493 ns/iter (± 224684) 3453605 ns/iter (± 85619) 1.12
datastore/num_rows=1000/num_instances=1000/packed=false/latest_at/default 370 ns/iter (± 1) 382 ns/iter (± 1) 0.97
datastore/num_rows=1000/num_instances=1000/packed=false/latest_at_missing/primary/default 261 ns/iter (± 0) 260 ns/iter (± 0) 1.00
datastore/num_rows=1000/num_instances=1000/packed=false/latest_at_missing/secondaries/default 420 ns/iter (± 0) 429 ns/iter (± 0) 0.98
datastore/num_rows=1000/num_instances=1000/packed=false/range/default 3839235 ns/iter (± 208720) 3587312 ns/iter (± 78211) 1.07
datastore/num_rows=1000/num_instances=1000/gc/default 2386181 ns/iter (± 7225) 2393440 ns/iter (± 5822) 1.00
mono_points_arrow/generate_message_bundles 31121592 ns/iter (± 444364) 28645528 ns/iter (± 1103451) 1.09
mono_points_arrow/generate_messages 127961340 ns/iter (± 1094579) 126079280 ns/iter (± 1122217) 1.01
mono_points_arrow/encode_log_msg 161906054 ns/iter (± 2440212) 158006436 ns/iter (± 1489958) 1.02
mono_points_arrow/encode_total 319505901 ns/iter (± 2471660) 316136034 ns/iter (± 2245072) 1.01
mono_points_arrow/decode_log_msg 193377167 ns/iter (± 1176387) 190644173 ns/iter (± 1090403) 1.01
mono_points_arrow/decode_message_bundles 71953200 ns/iter (± 627613) 70246697 ns/iter (± 643546) 1.02
mono_points_arrow/decode_total 262749089 ns/iter (± 1904379) 258257827 ns/iter (± 1651580) 1.02
mono_points_arrow_batched/generate_message_bundles 25979718 ns/iter (± 1444642) 24318215 ns/iter (± 1613499) 1.07
mono_points_arrow_batched/generate_messages 5046246 ns/iter (± 489858) 4626122 ns/iter (± 292249) 1.09
mono_points_arrow_batched/encode_log_msg 1395661 ns/iter (± 4692) 1374366 ns/iter (± 2270) 1.02
mono_points_arrow_batched/encode_total 32890333 ns/iter (± 1815439) 32679020 ns/iter (± 1819398) 1.01
mono_points_arrow_batched/decode_log_msg 786757 ns/iter (± 3628) 782142 ns/iter (± 3285) 1.01
mono_points_arrow_batched/decode_message_bundles 8010830 ns/iter (± 467979) 7679188 ns/iter (± 155523) 1.04
mono_points_arrow_batched/decode_total 9320700 ns/iter (± 528144) 8692322 ns/iter (± 286545) 1.07
batch_points_arrow/generate_message_bundles 193675 ns/iter (± 435) 191478 ns/iter (± 494) 1.01
batch_points_arrow/generate_messages 5140 ns/iter (± 10) 5195 ns/iter (± 8) 0.99
batch_points_arrow/encode_log_msg 266170 ns/iter (± 1730) 264805 ns/iter (± 1647) 1.01
batch_points_arrow/encode_total 495605 ns/iter (± 3605) 485864 ns/iter (± 3357) 1.02
batch_points_arrow/decode_log_msg 213925 ns/iter (± 1642) 212794 ns/iter (± 649) 1.01
batch_points_arrow/decode_message_bundles 1870 ns/iter (± 8) 1899 ns/iter (± 2) 0.98
batch_points_arrow/decode_total 223284 ns/iter (± 1391) 221980 ns/iter (± 1781) 1.01
arrow_mono_points/insert 2520560471 ns/iter (± 5545821) 2525712931 ns/iter (± 6101896) 1.00
arrow_mono_points/query 1219187 ns/iter (± 15121) 1195489 ns/iter (± 6901) 1.02
arrow_batch_points/insert 1153495 ns/iter (± 10456) 1143470 ns/iter (± 5090) 1.01
arrow_batch_points/query 15049 ns/iter (± 70) 14892 ns/iter (± 29) 1.01
arrow_batch_vecs/insert 26690 ns/iter (± 82) 28145 ns/iter (± 54) 0.95
arrow_batch_vecs/query 325241 ns/iter (± 830) 368260 ns/iter (± 226) 0.88
tuid/Tuid::random 34 ns/iter (± 0) 34 ns/iter (± 0) 1

This comment was automatically generated by workflow using github-action-benchmark.

Please sign in to comment.