Reduce how often invert is called

This commit is contained in:
Maximilian Ammann 2022-03-25 20:52:47 +01:00
parent ae618da5b1
commit 53021bdbb2
5 changed files with 110 additions and 42 deletions

View File

@ -18,7 +18,7 @@ build = "build.rs"
license = "MIT OR Apache-2.0"
[package.metadata.wasm-pack.profile.release]
wasm-opt = false
wasm-opt = true
[profile.release]
lto = true
@ -44,6 +44,7 @@ js-sys = "0.3"
wasm-bindgen = "0.2"
wasm-bindgen-futures = "0.4"
console_log = { version = "0.2", features = ["color"] }
# stdweb variant is way faster!
instant = { version = "0.1", features = ["stdweb"] } # FIXME: Untrusted dependency
[target.'cfg(any(target_os = "macos", target_os = "ios", target_os = "linux", target_os = "android"))'.dependencies]

View File

@ -25,10 +25,13 @@ impl UpdateState for PanHandler {
{
let perspective = &state.perspective;
let view_proj = reference_camera.calc_view_proj(perspective);
let inverted_view_proj = view_proj.invert();
let delta = if let (Some(start), Some(current)) = (
reference_camera.window_to_world_at_ground(&start_window_position, &view_proj),
reference_camera.window_to_world_at_ground(&window_position, &view_proj),
reference_camera
.window_to_world_at_ground(&start_window_position, &inverted_view_proj),
reference_camera
.window_to_world_at_ground(&window_position, &inverted_view_proj),
) {
start - current
} else {

View File

@ -28,10 +28,11 @@ impl UpdateState for ZoomHandler {
let perspective = &state.perspective;
let view_proj = state.camera.calc_view_proj(perspective);
let inverted_view_proj = view_proj.invert();
if let Some(cursor_position) = state
.camera
.window_to_world_at_ground(&window_position, &view_proj)
.window_to_world_at_ground(&window_position, &inverted_view_proj)
{
let scale = 2.0.pow(next_zoom - current_zoom);

View File

@ -1,3 +1,4 @@
use cgmath::num_traits::Inv;
use cgmath::prelude::*;
use cgmath::{Matrix4, Point2, Point3, Vector2, Vector3, Vector4};
@ -20,6 +21,48 @@ pub const FLIP_Y: cgmath::Matrix4<f64> = cgmath::Matrix4::new(
0.0, 0.0, 0.0, 1.0,
);
pub struct ViewProjection(Matrix4<f64>);
impl ViewProjection {
pub fn invert(&self) -> InvertedViewProjection {
InvertedViewProjection(self.0.invert().expect("Unable to invert view projection"))
}
pub fn project(&self, vector: Vector4<f64>) -> Vector4<f64> {
self.0 * vector
}
pub fn to_model_view_projection(&self, projection: Matrix4<f64>) -> ModelViewProjection {
ModelViewProjection(self.0 * projection)
}
pub fn downcast(&self) -> Matrix4<f32> {
self.0
.cast::<f32>()
.expect("Unable to cast view projection to f32")
.into()
}
}
pub struct InvertedViewProjection(Matrix4<f64>);
impl InvertedViewProjection {
pub fn project(&self, vector: Vector4<f64>) -> Vector4<f64> {
self.0 * vector
}
}
pub struct ModelViewProjection(Matrix4<f64>);
impl ModelViewProjection {
pub fn downcast(&self) -> Matrix4<f32> {
self.0
.cast::<f32>()
.expect("Unable to cast view projection to f32")
.into()
}
}
#[derive(Debug, Clone)]
pub struct Camera {
pub position: cgmath::Point3<f64>,
@ -64,14 +107,14 @@ impl Camera {
)
}
pub fn calc_view_proj(&self, perspective: &Perspective) -> Matrix4<f64> {
FLIP_Y * perspective.calc_matrix() * self.calc_matrix()
pub fn calc_view_proj(&self, perspective: &Perspective) -> ViewProjection {
ViewProjection(FLIP_Y * perspective.calc_matrix() * self.calc_matrix())
}
pub fn create_camera_uniform(&self, perspective: &Perspective) -> ShaderCamera {
let view_proj = self.calc_view_proj(perspective);
ShaderCamera::new(
view_proj.cast::<f32>().unwrap().into(),
view_proj.downcast().into(),
self.position.to_homogeneous().cast::<f32>().unwrap().into(),
)
}
@ -144,7 +187,11 @@ impl Camera {
/// `w` is lost.
///
/// OpenGL explanation: https://www.khronos.org/opengl/wiki/Compute_eye_space_from_window_space#From_window_to_ndc
fn window_to_world(&self, window: &Vector3<f64>, view_proj: &Matrix4<f64>) -> Vector3<f64> {
fn window_to_world(
&self,
window: &Vector3<f64>,
inverted_view_proj: &InvertedViewProjection,
) -> Vector3<f64> {
#[rustfmt::skip]
let fixed_window = Vector4::new(
window.x,
@ -154,7 +201,7 @@ impl Camera {
);
let ndc = self.clip_to_window_transform().invert().unwrap() * fixed_window;
let unprojected = view_proj.invert().unwrap() * ndc;
let unprojected = inverted_view_proj.project(ndc);
Vector3::new(
unprojected.x / unprojected.w,
@ -168,7 +215,7 @@ impl Camera {
/// Adopted from [here](https://docs.rs/nalgebra-glm/latest/src/nalgebra_glm/ext/matrix_projection.rs.html#164-181).
fn window_to_world_nalgebra(
window: &Vector3<f64>,
view_proj: &Matrix4<f64>,
inverted_view_proj: &InvertedViewProjection,
width: f64,
height: f64,
) -> Vector3<f64> {
@ -178,7 +225,7 @@ impl Camera {
window.z,
1.0,
);
let unprojected = view_proj.invert().unwrap() * pt;
let unprojected = inverted_view_proj.project(pt);
Vector3::new(
unprojected.x / unprojected.w,
@ -191,11 +238,13 @@ impl Camera {
pub fn window_to_world_at_ground(
&self,
window: &Vector2<f64>,
view_proj: &Matrix4<f64>,
inverted_view_proj: &InvertedViewProjection,
) -> Option<Vector3<f64>> {
let near_world = self.window_to_world(&Vector3::new(window.x, window.y, 0.0), view_proj);
let near_world =
self.window_to_world(&Vector3::new(window.x, window.y, 0.0), inverted_view_proj);
let far_world = self.window_to_world(&Vector3::new(window.x, window.y, 1.0), view_proj);
let far_world =
self.window_to_world(&Vector3::new(window.x, window.y, 1.0), inverted_view_proj);
// for z = 0 in world coordinates
// Idea comes from: https://dondi.lmu.build/share/cg/unproject-explained.pdf
@ -217,16 +266,17 @@ impl Camera {
///
/// *Note:* It is possible that no such bounding box exists. This is the case if the `z=0` plane
/// is not in view.
pub fn view_region_bounding_box(&self, perspective: &Perspective) -> Option<Aabb2<f64>> {
let view_proj = self.calc_view_proj(perspective);
pub fn view_region_bounding_box(
&self,
inverted_view_proj: &InvertedViewProjection,
) -> Option<Aabb2<f64>> {
let screen_bounding_box = [
Vector2::new(0.0, 0.0),
Vector2::new(self.width, 0.0),
Vector2::new(self.width, self.height),
Vector2::new(0.0, self.height),
]
.map(|point| self.window_to_world_at_ground(&point, &view_proj));
.map(|point| self.window_to_world_at_ground(&point, &inverted_view_proj));
let mut min: Option<Point2<f64>> = None;
let mut max: Option<Point2<f64>> = None;
@ -266,9 +316,9 @@ impl Camera {
/// the intersection points between an Aabb3 and a plane. The resulting Aabb2 is returned.
pub fn view_region_bounding_box_ndc(&self, perspective: &Perspective) -> Option<Aabb2<f64>> {
let view_proj = self.calc_view_proj(perspective);
let a = view_proj * Vector4::new(0.0, 0.0, 0.0, 1.0);
let b = view_proj * Vector4::new(1.0, 0.0, 0.0, 1.0);
let c = view_proj * Vector4::new(1.0, 1.0, 0.0, 1.0);
let a = view_proj.project(Vector4::new(0.0, 0.0, 0.0, 1.0));
let b = view_proj.project(Vector4::new(1.0, 0.0, 0.0, 1.0));
let c = view_proj.project(Vector4::new(1.0, 1.0, 0.0, 1.0));
let a_ndc = self.clip_to_window(&a).truncate();
let b_ndc = self.clip_to_window(&b).truncate();
@ -285,10 +335,14 @@ impl Camera {
Point3::new(1.0, 1.0, 1.0),
));
let inverted_view_proj = view_proj.invert();
let from_ndc = Vector3::new(self.width, self.height, 1.0);
let vec = points
.iter()
.map(|point| self.window_to_world(&point.mul_element_wise(from_ndc), &view_proj))
.map(|point| {
self.window_to_world(&point.mul_element_wise(from_ndc), &inverted_view_proj)
})
.collect::<Vec<_>>();
let min_x = vec
@ -348,6 +402,7 @@ impl Perspective {
#[cfg(test)]
mod tests {
use crate::render::camera::{InvertedViewProjection, ViewProjection};
use cgmath::{AbsDiffEq, Matrix4, SquareMatrix, Vector2, Vector3, Vector4};
use super::{Camera, Perspective};
@ -371,12 +426,13 @@ mod tests {
0.1,
100000.0,
);
let view_proj: Matrix4<f64> = camera.calc_view_proj(&perspective);
let view_proj: ViewProjection = camera.calc_view_proj(&perspective);
let inverted_view_proj: InvertedViewProjection = view_proj.invert();
let world_pos: Vector4<f64> = Vector4::new(0.0, 0.0, 0.0, 1.0);
let clip = view_proj * world_pos;
let origin_clip_space = view_proj * Vector4::new(0.0, 0.0, 0.0, 1.0);
let origin_clip_space = view_proj.project(Vector4::new(0.0, 0.0, 0.0, 1.0));
println!("origin w in clip space: {:?}", origin_clip_space.w);
println!("world_pos: {:?}", world_pos);
@ -391,21 +447,26 @@ mod tests {
println!(
"r world (nalgebra): {:?}",
Camera::window_to_world_nalgebra(&window.truncate(), &view_proj, width, height)
Camera::window_to_world_nalgebra(
&window.truncate(),
&inverted_view_proj,
width,
height
)
);
// -------- far vs. near plane trick
let near_world = Camera::window_to_world_nalgebra(
&Vector3::new(window.x, window.y, 0.0),
&view_proj,
&inverted_view_proj,
width,
height,
);
let far_world = Camera::window_to_world_nalgebra(
&Vector3::new(window.x, window.y, 1.0),
&view_proj,
&inverted_view_proj,
width,
height,
);
@ -422,9 +483,11 @@ mod tests {
let window = Vector2::new(960.0, 631.0); // 0, 4096: passt nicht
//let window = Vector2::new(962.0, 1.0); // 0, 300: passt nicht
//let window = Vector2::new(960.0, 540.0); // 0, 0 passt
let near_world = camera.window_to_world(&Vector3::new(window.x, window.y, 0.0), &view_proj);
let near_world =
camera.window_to_world(&Vector3::new(window.x, window.y, 0.0), &inverted_view_proj);
let far_world = camera.window_to_world(&Vector3::new(window.x, window.y, 1.0), &view_proj);
let far_world =
camera.window_to_world(&Vector3::new(window.x, window.y, 1.0), &inverted_view_proj);
// for z = 0 in world coordinates
let u = -near_world.z / (far_world.z - near_world.z);

View File

@ -356,9 +356,10 @@ impl RenderState {
pub fn upload_tile_geometry(&mut self, scheduler: &mut IOScheduler) {
let visible_z = self.visible_z();
let inverted_view_proj = self.camera.calc_view_proj(&self.perspective).invert();
let view_region = self
.camera
.view_region_bounding_box(&self.perspective)
.view_region_bounding_box(&inverted_view_proj)
.map(|bounding_box| ViewRegion::new(bounding_box, 2, self.zoom, visible_z));
// Fetch tiles which are currently in view
@ -367,14 +368,13 @@ impl RenderState {
.style
.layers
.iter()
.filter_map(|layer| layer.source_layer.as_ref())
.cloned()
.filter_map(|layer| layer.source_layer.clone())
.collect();
for coords in view_region.iter() {
let tile_request = TileRequest {
coords,
layers: source_layers.clone(),
layers: source_layers.clone(), // TODO: Optimize: This is expensive
};
scheduler.try_request_tile(tile_request).unwrap();
}
@ -395,9 +395,8 @@ impl RenderState {
let zoom_factor = 2.0_f64.powf(world_coords.z as f64 - self.zoom) as f32;
let transform: Matrix4<f32> = (view_proj
* world_coords.transform_for_zoom(self.zoom))
.cast()
.unwrap();
.to_model_view_projection(world_coords.transform_for_zoom(self.zoom)))
.downcast();
self.buffer_pool.update_tile_metadata(
&self.queue,
@ -420,6 +419,7 @@ impl RenderState {
for world_coords in view_region.iter() {
let loaded_layers = self.buffer_pool.get_loaded_layers_at(&world_coords);
// TODO: Optimize: Dropping this is expensive
let available_layers =
scheduler.get_tessellated_layers_at(&world_coords, &loaded_layers);
@ -469,10 +469,10 @@ impl RenderState {
// We are casting here from 64bit to 32bit, because 32bit is more performant and is
// better supported.
let transform: Matrix4<f32> = (view_proj
* world_coords.transform_for_zoom(self.zoom))
.cast()
.unwrap();
let transform: Matrix4<f32> = (view_proj.to_model_view_projection(
world_coords.transform_for_zoom(self.zoom),
))
.downcast();
self.buffer_pool.allocate_tile_geometry(
&self.queue,
@ -556,10 +556,10 @@ impl RenderState {
{
let visible_z = self.visible_z();
let inverted_view_proj = self.camera.calc_view_proj(&self.perspective).invert();
let view_region = self
.camera
.view_region_bounding_box(&self.perspective)
.view_region_bounding_box(&inverted_view_proj)
.map(|bounding_box| ViewRegion::new(bounding_box, 2, self.zoom, visible_z));
let index = self.buffer_pool.index();