mirror of
https://github.com/maplibre/maplibre-rs.git
synced 2025-12-08 19:05:57 +00:00
Add Zoom type and ChangeObserver, also improve performance by tessellating less
This commit is contained in:
parent
56ab38b29e
commit
37a29539d7
@ -2,9 +2,10 @@
|
||||
|
||||
use std::fmt;
|
||||
use std::fmt::Formatter;
|
||||
use std::ops::Add;
|
||||
|
||||
use cgmath::num_traits::Pow;
|
||||
use cgmath::{Matrix4, Point3, Vector3};
|
||||
use cgmath::{AbsDiffEq, Matrix4, Point3, Vector3};
|
||||
|
||||
use style_spec::source::TileAddressingScheme;
|
||||
|
||||
@ -40,6 +41,68 @@ impl fmt::Debug for Quadkey {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct Zoom(f64);
|
||||
|
||||
impl Zoom {
|
||||
pub fn new(zoom: f64) -> Self {
|
||||
Zoom(zoom)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Zoom {
|
||||
fn default() -> Self {
|
||||
Zoom(0.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Zoom {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", (self.0 * 100.0).round() / 100.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Add for Zoom {
|
||||
type Output = Zoom;
|
||||
|
||||
fn add(self, rhs: Self) -> Self::Output {
|
||||
Zoom(self.0 + rhs.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Sub for Zoom {
|
||||
type Output = Zoom;
|
||||
|
||||
fn sub(self, rhs: Self) -> Self::Output {
|
||||
Zoom(self.0 - rhs.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Zoom {
|
||||
pub fn scale_to_tile(&self, coords: &WorldTileCoords) -> f64 {
|
||||
2.0_f64.powf(coords.z as f64 - self.0)
|
||||
}
|
||||
|
||||
pub fn scale_to_zoom_level(&self, z: u8) -> f64 {
|
||||
2.0_f64.powf(z as f64 - self.0)
|
||||
}
|
||||
|
||||
pub fn scale_delta(&self, zoom: &Zoom) -> f64 {
|
||||
2.0_f64.powf(zoom.0 - self.0)
|
||||
}
|
||||
|
||||
pub fn level(&self) -> u8 {
|
||||
self.0.floor() as u8
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Zoom {}
|
||||
impl PartialEq for Zoom {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.0.abs_diff_eq(&other.0, 0.05)
|
||||
}
|
||||
}
|
||||
|
||||
/// Within each tile there is a separate coordinate system. Usually this coordinate system is
|
||||
/// within [`crate::coords::EXTENT`]. Therefore, `x` and `y` must be within the bounds of
|
||||
/// [`crate::coords::EXTENT`].
|
||||
@ -146,7 +209,7 @@ impl WorldTileCoords {
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub fn transform_for_zoom(&self, zoom: f64) -> Matrix4<f64> {
|
||||
pub fn transform_for_zoom(&self, zoom: Zoom) -> Matrix4<f64> {
|
||||
/*
|
||||
For tile.z = zoom:
|
||||
=> scale = 512
|
||||
@ -155,7 +218,7 @@ impl WorldTileCoords {
|
||||
If tile.z > zoom:
|
||||
=> scale < 512
|
||||
*/
|
||||
let tile_scale = TILE_SIZE * 2.0.pow(zoom - self.z as f64);
|
||||
let tile_scale = TILE_SIZE * Zoom::new(self.z as f64).scale_delta(&zoom);
|
||||
|
||||
let translate = Matrix4::from_translation(Vector3::new(
|
||||
self.x as f64 * tile_scale,
|
||||
@ -309,10 +372,6 @@ pub struct WorldCoords {
|
||||
pub y: f64,
|
||||
}
|
||||
|
||||
fn world_size_at_zoom(zoom: f64) -> f64 {
|
||||
TILE_SIZE * 2.0.pow(zoom)
|
||||
}
|
||||
|
||||
fn tiles_with_z(z: u8) -> f64 {
|
||||
2.0.pow(z)
|
||||
}
|
||||
@ -322,8 +381,8 @@ impl WorldCoords {
|
||||
Self { x, y }
|
||||
}
|
||||
|
||||
pub fn into_world_tile(self, z: u8, zoom: f64) -> WorldTileCoords {
|
||||
let tile_scale = 2.0.pow(z as f64 - zoom) / TILE_SIZE; // TODO: Deduplicate
|
||||
pub fn into_world_tile(self, z: u8, zoom: Zoom) -> WorldTileCoords {
|
||||
let tile_scale = zoom.scale_to_zoom_level(z) / TILE_SIZE; // TODO: Deduplicate
|
||||
let x = self.x * tile_scale;
|
||||
let y = self.y * tile_scale;
|
||||
|
||||
@ -371,7 +430,7 @@ pub struct ViewRegion {
|
||||
}
|
||||
|
||||
impl ViewRegion {
|
||||
pub fn new(view_region: Aabb2<f64>, padding: i32, zoom: f64, z: u8) -> Self {
|
||||
pub fn new(view_region: Aabb2<f64>, padding: i32, zoom: Zoom, z: u8) -> Self {
|
||||
let min_world: WorldCoords = WorldCoords::at_ground(view_region.min.x, view_region.min.y);
|
||||
let min_world_tile: WorldTileCoords = min_world.into_world_tile(z, zoom);
|
||||
let max_world: WorldCoords = WorldCoords::at_ground(view_region.max.x, view_region.max.y);
|
||||
@ -439,6 +498,7 @@ impl fmt::Display for WorldCoords {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use cgmath::{Point2, Vector4};
|
||||
|
||||
use style_spec::source::TileAddressingScheme;
|
||||
|
||||
use crate::coords::{Quadkey, TileCoords, ViewRegion, WorldCoords, WorldTileCoords, EXTENT};
|
||||
@ -447,7 +507,7 @@ mod tests {
|
||||
const TOP_LEFT: Vector4<f64> = Vector4::new(0.0, 0.0, 0.0, 1.0);
|
||||
const BOTTOM_RIGHT: Vector4<f64> = Vector4::new(EXTENT, EXTENT, 0.0, 1.0);
|
||||
|
||||
fn to_from_world(tile: (i32, i32, u8), zoom: f64) {
|
||||
fn to_from_world(tile: (i32, i32, u8), zoom: Zoom) {
|
||||
let tile = WorldTileCoords::from(tile);
|
||||
let p1 = tile.transform_for_zoom(zoom) * TOP_LEFT;
|
||||
let p2 = tile.transform_for_zoom(zoom) * BOTTOM_RIGHT;
|
||||
|
||||
@ -63,8 +63,8 @@ impl QueryHandler {
|
||||
let view_proj = state.camera.calc_view_proj(perspective);
|
||||
let inverted_view_proj = view_proj.invert();
|
||||
|
||||
let z = state.visible_z();
|
||||
let zoom = state.zoom;
|
||||
let z = state.visible_z(); // FIXME: can be wrong, if tiles of different z are visible
|
||||
let zoom = state.zoom();
|
||||
|
||||
if let Some(coordinates) = state
|
||||
.camera
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
use super::UpdateState;
|
||||
|
||||
use crate::coords::Zoom;
|
||||
use crate::render::render_state::RenderState;
|
||||
use crate::Scheduler;
|
||||
use cgmath::num_traits::Pow;
|
||||
@ -8,20 +9,19 @@ use std::time::Duration;
|
||||
|
||||
pub struct ZoomHandler {
|
||||
window_position: Option<Vector2<f64>>,
|
||||
zoom_delta: f64,
|
||||
zoom_delta: Option<Zoom>,
|
||||
sensitivity: f64,
|
||||
}
|
||||
|
||||
impl UpdateState for ZoomHandler {
|
||||
fn update_state(&mut self, state: &mut RenderState, _scheduler: &Scheduler, _dt: Duration) {
|
||||
if self.zoom_delta != 0.0 {
|
||||
if let Some(zoom_delta) = self.zoom_delta {
|
||||
if let Some(window_position) = self.window_position {
|
||||
let current_zoom = state.zoom;
|
||||
let next_zoom = current_zoom + self.zoom_delta;
|
||||
let current_zoom = state.zoom();
|
||||
let next_zoom = current_zoom + zoom_delta;
|
||||
|
||||
state.zoom = next_zoom;
|
||||
self.zoom_delta = 0.0;
|
||||
println!("zoom: {}", state.zoom);
|
||||
state.update_zoom(next_zoom);
|
||||
self.zoom_delta = None;
|
||||
|
||||
let perspective = &state.perspective;
|
||||
let view_proj = state.camera.calc_view_proj(perspective);
|
||||
@ -31,7 +31,7 @@ impl UpdateState for ZoomHandler {
|
||||
.camera
|
||||
.window_to_world_at_ground(&window_position, &inverted_view_proj)
|
||||
{
|
||||
let scale = 2.0.pow(next_zoom - current_zoom);
|
||||
let scale = current_zoom.scale_delta(&next_zoom);
|
||||
|
||||
let delta = Vector3::new(
|
||||
cursor_position.x * scale,
|
||||
@ -50,7 +50,7 @@ impl ZoomHandler {
|
||||
pub fn new(sensitivity: f64) -> Self {
|
||||
Self {
|
||||
window_position: None,
|
||||
zoom_delta: 0.0,
|
||||
zoom_delta: None,
|
||||
sensitivity,
|
||||
}
|
||||
}
|
||||
@ -64,14 +64,22 @@ impl ZoomHandler {
|
||||
true
|
||||
}
|
||||
|
||||
pub fn update_zoom(&mut self, delta: f64) {
|
||||
self.zoom_delta = Some(self.zoom_delta.unwrap_or_default() + Zoom::new(delta));
|
||||
}
|
||||
|
||||
pub fn process_scroll(&mut self, delta: &winit::event::MouseScrollDelta) {
|
||||
self.zoom_delta += match delta {
|
||||
winit::event::MouseScrollDelta::LineDelta(_horizontal, vertical) => *vertical as f64,
|
||||
winit::event::MouseScrollDelta::PixelDelta(winit::dpi::PhysicalPosition {
|
||||
y: scroll,
|
||||
..
|
||||
}) => *scroll / 100.0,
|
||||
} * self.sensitivity;
|
||||
self.update_zoom(
|
||||
match delta {
|
||||
winit::event::MouseScrollDelta::LineDelta(_horizontal, vertical) => {
|
||||
*vertical as f64
|
||||
}
|
||||
winit::event::MouseScrollDelta::PixelDelta(winit::dpi::PhysicalPosition {
|
||||
y: scroll,
|
||||
..
|
||||
}) => *scroll / 100.0,
|
||||
} * self.sensitivity,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn process_key_press(
|
||||
@ -87,11 +95,11 @@ impl ZoomHandler {
|
||||
|
||||
match key {
|
||||
winit::event::VirtualKeyCode::Plus | winit::event::VirtualKeyCode::I => {
|
||||
self.zoom_delta += amount;
|
||||
self.update_zoom(amount);
|
||||
true
|
||||
}
|
||||
winit::event::VirtualKeyCode::Minus | winit::event::VirtualKeyCode::K => {
|
||||
self.zoom_delta -= amount;
|
||||
self.update_zoom(-amount);
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
|
||||
@ -9,7 +9,7 @@ use geozero::geo_types::GeoWriter;
|
||||
use geozero::{ColumnValue, FeatureProcessor, GeomProcessor, PropertyProcessor};
|
||||
use rstar::{Envelope, PointDistance, RTree, RTreeObject, AABB};
|
||||
|
||||
use crate::coords::{InnerCoords, Quadkey, WorldCoords, WorldTileCoords, EXTENT, TILE_SIZE};
|
||||
use crate::coords::{InnerCoords, Quadkey, WorldCoords, WorldTileCoords, Zoom, EXTENT, TILE_SIZE};
|
||||
use crate::util::math::bounds_from_points;
|
||||
|
||||
pub struct GeometryIndex {
|
||||
@ -33,7 +33,7 @@ impl GeometryIndex {
|
||||
&self,
|
||||
world_coords: &WorldCoords,
|
||||
z: u8,
|
||||
zoom: f64,
|
||||
zoom: Zoom,
|
||||
) -> Option<Vec<&IndexedGeometry<f64>>> {
|
||||
let world_tile_coords = world_coords.into_world_tile(z, zoom);
|
||||
|
||||
@ -41,7 +41,7 @@ impl GeometryIndex {
|
||||
.build_quad_key()
|
||||
.and_then(|key| self.index.get(&key))
|
||||
{
|
||||
let scale = 2.0f64.pow(z as f64 - zoom); // TODO deduplicate
|
||||
let scale = zoom.scale_delta(&Zoom::new(z as f64)); // FIXME: can be wrong, if tiles of different z are visible
|
||||
|
||||
let delta_x = world_coords.x / TILE_SIZE * scale - world_tile_coords.x as f64;
|
||||
let delta_y = world_coords.y / TILE_SIZE * scale - world_tile_coords.y as f64;
|
||||
|
||||
@ -9,7 +9,7 @@ use std::sync::{Arc, Mutex};
|
||||
use vector_tile::parse_tile_bytes;
|
||||
|
||||
/// Describes through which channels work-requests travel. It describes the flow of work.
|
||||
use crate::coords::{WorldCoords, WorldTileCoords};
|
||||
use crate::coords::{WorldCoords, WorldTileCoords, Zoom};
|
||||
use crate::io::tile_cache::TileCache;
|
||||
use crate::io::{
|
||||
LayerTessellateMessage, TessellateMessage, TileFetchResult, TileRequest, TileRequestID,
|
||||
@ -155,7 +155,7 @@ impl ThreadLocalState {
|
||||
&self,
|
||||
world_coords: &WorldCoords,
|
||||
z: u8,
|
||||
zoom: f64,
|
||||
zoom: Zoom,
|
||||
) -> Option<Vec<IndexedGeometry<f64>>> {
|
||||
if let Ok(mut geometry_index) = self.geometry_index.lock() {
|
||||
geometry_index
|
||||
@ -256,7 +256,7 @@ impl ThreadLocalState {
|
||||
}
|
||||
}
|
||||
|
||||
tracing::info!("tile at {} finished", &coords);
|
||||
tracing::info!("tile at {} finished", &tile_request.coords);
|
||||
|
||||
self.tessellate_result_sender
|
||||
.send(TessellateMessage::Tile(TileTessellateMessage {
|
||||
@ -330,9 +330,9 @@ impl Scheduler {
|
||||
&mut self,
|
||||
coords: &WorldTileCoords,
|
||||
layers: &HashSet<String>,
|
||||
) -> Result<(), SendError<TileRequest>> {
|
||||
) -> Result<bool, SendError<TileRequest>> {
|
||||
if !self.tile_cache.is_layers_missing(coords, layers) {
|
||||
return Ok(());
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
if let Ok(mut tile_request_state) = self.tile_request_state.try_lock() {
|
||||
@ -372,9 +372,11 @@ impl Scheduler {
|
||||
self.schedule_method.schedule(self, future_fn).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
Ok(false)
|
||||
} else {
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_tile_cache(&self) -> &TileCache {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
use cgmath::prelude::*;
|
||||
use cgmath::{Matrix4, Point2, Point3, Vector2, Vector3, Vector4};
|
||||
use cgmath::{AbsDiffEq, Matrix4, Point2, Point3, Vector2, Vector3, Vector4};
|
||||
|
||||
use crate::render::shaders::ShaderCamera;
|
||||
use crate::util::math::{bounds_from_points, Aabb2, Aabb3, Plane};
|
||||
@ -73,6 +73,15 @@ pub struct Camera {
|
||||
pub height: f64,
|
||||
}
|
||||
|
||||
impl Eq for Camera {}
|
||||
impl PartialEq for Camera {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.position.abs_diff_eq(&other.position, 0.05)
|
||||
&& self.yaw.abs_diff_eq(&other.yaw, 0.05)
|
||||
&& self.pitch.abs_diff_eq(&other.pitch, 0.05)
|
||||
}
|
||||
}
|
||||
|
||||
impl Camera {
|
||||
pub fn new<
|
||||
V: Into<cgmath::Point3<f64>>,
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
use std::collections::HashSet;
|
||||
use std::default::Default;
|
||||
|
||||
use cgmath::AbsDiffEq;
|
||||
use std::{cmp, iter};
|
||||
|
||||
use tracing;
|
||||
@ -10,7 +11,7 @@ use winit::window::Window;
|
||||
|
||||
use style_spec::Style;
|
||||
|
||||
use crate::coords::{ViewRegion, TILE_SIZE};
|
||||
use crate::coords::{ViewRegion, Zoom, TILE_SIZE};
|
||||
use crate::io::scheduler::Scheduler;
|
||||
use crate::io::LayerTessellateMessage;
|
||||
use crate::platform::{COLOR_TEXTURE_FORMAT, MIN_BUFFER_SIZE};
|
||||
@ -23,7 +24,7 @@ use crate::render::options::{
|
||||
};
|
||||
use crate::render::tile_view_pattern::{TileInView, TileViewPattern};
|
||||
use crate::tessellation::IndexDataType;
|
||||
use crate::util::FPSMeter;
|
||||
use crate::util::{ChangeObserver, FPSMeter};
|
||||
|
||||
use super::piplines::*;
|
||||
use super::shaders;
|
||||
@ -66,14 +67,24 @@ pub struct RenderState {
|
||||
|
||||
tile_view_pattern: TileViewPattern<Queue, Buffer>,
|
||||
|
||||
pub camera: camera::Camera,
|
||||
pub camera: ChangeObserver<camera::Camera>,
|
||||
pub perspective: camera::Perspective,
|
||||
pub zoom: f64,
|
||||
zoom: ChangeObserver<Zoom>,
|
||||
try_failed: bool,
|
||||
|
||||
style: Box<Style>,
|
||||
}
|
||||
|
||||
impl RenderState {
|
||||
pub fn zoom(&self) -> Zoom {
|
||||
*self.zoom
|
||||
}
|
||||
|
||||
pub fn update_zoom(&mut self, new_zoom: Zoom) {
|
||||
*self.zoom = new_zoom;
|
||||
log::info!("zoom: {}", new_zoom);
|
||||
}
|
||||
|
||||
pub async fn new(window: &Window, style: Box<Style>) -> Self {
|
||||
let sample_count = 4;
|
||||
|
||||
@ -292,7 +303,7 @@ impl RenderState {
|
||||
sample_count,
|
||||
globals_uniform_buffer,
|
||||
fps_meter: FPSMeter::new(),
|
||||
camera,
|
||||
camera: ChangeObserver::new(camera),
|
||||
perspective: projection,
|
||||
suspended: false, // Initially the app is not suspended
|
||||
buffer_pool: BufferPool::new(
|
||||
@ -305,7 +316,8 @@ impl RenderState {
|
||||
tile_view_buffer,
|
||||
TILE_VIEW_BUFFER_SIZE,
|
||||
)),
|
||||
zoom: 0.0,
|
||||
zoom: ChangeObserver::default(),
|
||||
try_failed: false,
|
||||
style,
|
||||
}
|
||||
}
|
||||
@ -356,12 +368,13 @@ impl RenderState {
|
||||
}
|
||||
|
||||
pub fn visible_z(&self) -> u8 {
|
||||
self.zoom.floor() as u8
|
||||
self.zoom.level()
|
||||
}
|
||||
|
||||
/// Request tiles which are currently in view
|
||||
#[tracing::instrument(skip_all)]
|
||||
fn request_tiles_in_view(&self, view_region: &ViewRegion, scheduler: &mut Scheduler) {
|
||||
fn request_tiles_in_view(&self, view_region: &ViewRegion, scheduler: &mut Scheduler) -> bool {
|
||||
let mut try_failed = false;
|
||||
let source_layers: HashSet<String> = self
|
||||
.style
|
||||
.layers
|
||||
@ -372,9 +385,10 @@ impl RenderState {
|
||||
for coords in view_region.iter() {
|
||||
if coords.build_quad_key().is_some() {
|
||||
// TODO: Make tesselation depend on style?
|
||||
scheduler.try_request_tile(&coords, &source_layers).unwrap();
|
||||
try_failed = scheduler.try_request_tile(&coords, &source_layers).unwrap();
|
||||
}
|
||||
}
|
||||
try_failed
|
||||
}
|
||||
|
||||
/// Update tile metadata for all required tiles on the GPU according to current zoom, camera and perspective
|
||||
@ -383,16 +397,12 @@ impl RenderState {
|
||||
#[tracing::instrument(skip_all)]
|
||||
fn update_metadata(
|
||||
&mut self,
|
||||
scheduler: &mut Scheduler,
|
||||
_scheduler: &mut Scheduler,
|
||||
view_region: &ViewRegion,
|
||||
view_proj: &ViewProjection,
|
||||
) {
|
||||
self.tile_view_pattern.update_pattern(
|
||||
view_region,
|
||||
scheduler.get_tile_cache(),
|
||||
&self.buffer_pool,
|
||||
self.zoom,
|
||||
);
|
||||
self.tile_view_pattern
|
||||
.update_pattern(view_region, &self.buffer_pool, *self.zoom);
|
||||
self.tile_view_pattern
|
||||
.upload_pattern(&self.queue, view_proj);
|
||||
|
||||
@ -511,6 +521,12 @@ impl RenderState {
|
||||
buffer,
|
||||
..
|
||||
} => {
|
||||
let allocate_feature_metadata = tracing::span!(
|
||||
tracing::Level::TRACE,
|
||||
"allocate_feature_metadata"
|
||||
);
|
||||
|
||||
let guard = allocate_feature_metadata.enter();
|
||||
let feature_metadata = layer_data
|
||||
.features()
|
||||
.iter()
|
||||
@ -522,6 +538,7 @@ impl RenderState {
|
||||
.take(feature_indices[i] as usize)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
drop(guard);
|
||||
|
||||
tracing::trace!("Allocating geometry at {}", &coords);
|
||||
self.buffer_pool.allocate_layer_geometry(
|
||||
@ -552,27 +569,36 @@ impl RenderState {
|
||||
let view_region = self
|
||||
.camera
|
||||
.view_region_bounding_box(&view_proj.invert())
|
||||
.map(|bounding_box| ViewRegion::new(bounding_box, 1, self.zoom, visible_z));
|
||||
.map(|bounding_box| ViewRegion::new(bounding_box, 0, *self.zoom, visible_z));
|
||||
|
||||
drop(_guard);
|
||||
|
||||
if let Some(view_region) = &view_region {
|
||||
self.upload_tile_geometry(&view_proj, view_region, scheduler);
|
||||
self.update_metadata(scheduler, view_region, &view_proj);
|
||||
self.request_tiles_in_view(view_region, scheduler);
|
||||
}
|
||||
|
||||
// TODO: Could we draw inspiration from StagingBelt (https://docs.rs/wgpu/latest/wgpu/util/struct.StagingBelt.html)?
|
||||
// TODO: What is StagingBelt for?
|
||||
|
||||
// Update globals
|
||||
self.queue.write_buffer(
|
||||
&self.globals_uniform_buffer,
|
||||
0,
|
||||
bytemuck::cast_slice(&[ShaderGlobals::new(
|
||||
self.camera.create_camera_uniform(&self.perspective),
|
||||
)]),
|
||||
);
|
||||
if self.camera.did_change() || self.zoom.did_change() || self.try_failed {
|
||||
if let Some(view_region) = &view_region {
|
||||
// FIXME: We also need to request tiles from layers above if we are over the maximum zoom level
|
||||
self.try_failed = self.request_tiles_in_view(view_region, scheduler);
|
||||
}
|
||||
|
||||
// Update globals
|
||||
self.queue.write_buffer(
|
||||
&self.globals_uniform_buffer,
|
||||
0,
|
||||
bytemuck::cast_slice(&[ShaderGlobals::new(
|
||||
self.camera.create_camera_uniform(&self.perspective),
|
||||
)]),
|
||||
);
|
||||
}
|
||||
|
||||
self.camera.finished_observing();
|
||||
self.zoom.finished_observing();
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use crate::coords::{ViewRegion, WorldTileCoords};
|
||||
use crate::coords::{ViewRegion, WorldTileCoords, Zoom};
|
||||
use crate::io::tile_cache::TileCache;
|
||||
use crate::render::buffer_pool::{BackingBufferDescriptor, BufferPool, Queue};
|
||||
use crate::render::camera::ViewProjection;
|
||||
@ -28,6 +28,18 @@ pub struct TileShape {
|
||||
pub buffer_range: Range<wgpu::BufferAddress>,
|
||||
}
|
||||
|
||||
impl TileShape {
|
||||
fn new(coords: WorldTileCoords, zoom: Zoom, index: u64) -> Self {
|
||||
const STRIDE: u64 = size_of::<ShaderTileMetadata>() as u64;
|
||||
Self {
|
||||
coords,
|
||||
zoom_factor: zoom.scale_to_tile(&coords),
|
||||
transform: coords.transform_for_zoom(zoom),
|
||||
buffer_range: index as u64 * STRIDE..(index as u64 + 1) * STRIDE,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TileInView {
|
||||
pub shape: TileShape,
|
||||
|
||||
@ -61,7 +73,6 @@ impl<Q: Queue<B>, B> TileViewPattern<Q, B> {
|
||||
pub fn update_pattern(
|
||||
&mut self,
|
||||
view_region: &ViewRegion,
|
||||
tile_cache: &TileCache,
|
||||
buffer_pool: &BufferPool<
|
||||
wgpu::Queue,
|
||||
Buffer,
|
||||
@ -70,43 +81,31 @@ impl<Q: Queue<B>, B> TileViewPattern<Q, B> {
|
||||
ShaderLayerMetadata,
|
||||
ShaderFeatureStyle,
|
||||
>,
|
||||
zoom: f64,
|
||||
zoom: Zoom,
|
||||
) {
|
||||
self.in_view.clear();
|
||||
|
||||
let stride = size_of::<ShaderTileMetadata>() as u64;
|
||||
|
||||
let mut index = 0;
|
||||
|
||||
let pool_index = buffer_pool.index();
|
||||
|
||||
for coords in view_region.iter() {
|
||||
if coords.build_quad_key().is_none() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let shape = TileShape {
|
||||
coords,
|
||||
zoom_factor: 2.0_f64.powf(coords.z as f64 - zoom),
|
||||
transform: coords.transform_for_zoom(zoom),
|
||||
buffer_range: index as u64 * stride..(index as u64 + 1) * stride,
|
||||
};
|
||||
let shape = TileShape::new(coords, zoom, index);
|
||||
|
||||
index += 1;
|
||||
|
||||
let fallback = {
|
||||
if !buffer_pool.index().has_tile(&coords) {
|
||||
if let Some(fallback_coords) =
|
||||
buffer_pool.index().get_tile_coords_fallback(&coords)
|
||||
{
|
||||
if !pool_index.has_tile(&coords) {
|
||||
if let Some(fallback_coords) = pool_index.get_tile_coords_fallback(&coords) {
|
||||
tracing::trace!(
|
||||
"Could not find data at {coords}. Falling back to {fallback_coords}"
|
||||
);
|
||||
|
||||
let shape = TileShape {
|
||||
coords: fallback_coords,
|
||||
zoom_factor: 2.0_f64.powf(fallback_coords.z as f64 - zoom),
|
||||
transform: fallback_coords.transform_for_zoom(zoom),
|
||||
buffer_range: index as u64 * stride..(index as u64 + 1) * stride,
|
||||
};
|
||||
let shape = TileShape::new(fallback_coords, zoom, index);
|
||||
|
||||
index += 1;
|
||||
Some(shape)
|
||||
|
||||
@ -5,6 +5,7 @@ pub mod math;
|
||||
|
||||
use crate::coords::WorldTileCoords;
|
||||
pub use fps_meter::FPSMeter;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
struct MinMaxBoundingBox {
|
||||
min_x: i32,
|
||||
@ -49,3 +50,61 @@ impl MinMaxBoundingBox {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ChangeObserver<T> {
|
||||
inner: T,
|
||||
last_value: Option<T>,
|
||||
}
|
||||
|
||||
impl<T> ChangeObserver<T> {
|
||||
pub fn new(value: T) -> Self {
|
||||
Self {
|
||||
inner: value,
|
||||
last_value: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ChangeObserver<T>
|
||||
where
|
||||
T: Clone + Eq,
|
||||
{
|
||||
pub fn finished_observing(&mut self) {
|
||||
self.last_value = Some(self.inner.clone());
|
||||
}
|
||||
|
||||
pub fn did_change(&self) -> bool {
|
||||
if let Some(last_value) = &self.last_value {
|
||||
if !last_value.eq(&self.inner) {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Default for ChangeObserver<T>
|
||||
where
|
||||
T: Default,
|
||||
{
|
||||
fn default() -> Self {
|
||||
ChangeObserver::new(T::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for ChangeObserver<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DerefMut for ChangeObserver<T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.inner
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user