Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Re-enable bilinear interpolation again #1860

Merged
merged 2 commits into from
Apr 17, 2023
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
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
Loading