mirror of
https://github.com/maplibre/maplibre-rs.git
synced 2025-12-08 19:05:57 +00:00
Refactor render loop to use a single pattern for the current view
This commit is contained in:
parent
a1b511db45
commit
575cd07924
@ -67,6 +67,39 @@ impl TileCache {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn has_tile(&self, coords: &WorldTileCoords) -> bool {
|
||||||
|
coords
|
||||||
|
.build_quad_key()
|
||||||
|
.and_then(|key| {
|
||||||
|
self.cache_index.get(&key).and_then(|entries| {
|
||||||
|
if entries.is_empty() {
|
||||||
|
None
|
||||||
|
} else if entries.iter().all(|entry| match entry {
|
||||||
|
LayerTessellateResult::UnavailableLayer { .. } => true,
|
||||||
|
LayerTessellateResult::TessellatedLayer { .. } => false,
|
||||||
|
}) {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(entries)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_tile_coords_fallback(&self, coords: &WorldTileCoords) -> Option<WorldTileCoords> {
|
||||||
|
let mut current = *coords;
|
||||||
|
loop {
|
||||||
|
if self.has_tile(¤t) {
|
||||||
|
return Some(current);
|
||||||
|
} else if let Some(parent) = current.get_parent() {
|
||||||
|
current = parent
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn iter_tessellated_layers_at(
|
pub fn iter_tessellated_layers_at(
|
||||||
&self,
|
&self,
|
||||||
coords: &WorldTileCoords,
|
coords: &WorldTileCoords,
|
||||||
|
|||||||
@ -286,7 +286,7 @@ impl<Q: Queue<B>, B, V: bytemuck::Pod, I: bytemuck::Pod, TM: bytemuck::Pod, FM:
|
|||||||
|
|
||||||
pub struct BackingBufferDescriptor<B> {
|
pub struct BackingBufferDescriptor<B> {
|
||||||
/// The buffer which is used
|
/// The buffer which is used
|
||||||
buffer: B,
|
pub buffer: B,
|
||||||
/// The size of buffer
|
/// The size of buffer
|
||||||
inner_size: wgpu::BufferAddress,
|
inner_size: wgpu::BufferAddress,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,7 +6,7 @@ mod options;
|
|||||||
mod piplines;
|
mod piplines;
|
||||||
mod shaders;
|
mod shaders;
|
||||||
mod texture;
|
mod texture;
|
||||||
mod tile_mask_pattern;
|
mod tile_view_pattern;
|
||||||
|
|
||||||
pub mod camera;
|
pub mod camera;
|
||||||
pub mod render_state;
|
pub mod render_state;
|
||||||
|
|||||||
@ -7,5 +7,6 @@ pub const INDEX_FORMAT: wgpu::IndexFormat = wgpu::IndexFormat::Uint32; // Must m
|
|||||||
pub const VERTEX_BUFFER_SIZE: BufferAddress = 1024 * 1024 * 32;
|
pub const VERTEX_BUFFER_SIZE: BufferAddress = 1024 * 1024 * 32;
|
||||||
pub const FEATURE_METADATA_BUFFER_SIZE: BufferAddress = 1024 * 1024 * 32;
|
pub const FEATURE_METADATA_BUFFER_SIZE: BufferAddress = 1024 * 1024 * 32;
|
||||||
pub const INDICES_BUFFER_SIZE: BufferAddress = 1024 * 1024 * 16;
|
pub const INDICES_BUFFER_SIZE: BufferAddress = 1024 * 1024 * 16;
|
||||||
|
pub const TILE_VIEW_BUFFER_SIZE: BufferAddress = 4096;
|
||||||
|
|
||||||
pub const TILE_META_COUNT: BufferAddress = 1024 * 24;
|
pub const TILE_META_COUNT: BufferAddress = 1024 * 24;
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
use cgmath::{Matrix4, Vector4};
|
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::default::Default;
|
use std::default::Default;
|
||||||
use std::fmt::Formatter;
|
use std::fmt::Formatter;
|
||||||
@ -6,27 +5,28 @@ use std::io::sink;
|
|||||||
use std::time::{Instant, SystemTime};
|
use std::time::{Instant, SystemTime};
|
||||||
use std::{cmp, fmt, iter};
|
use std::{cmp, fmt, iter};
|
||||||
|
|
||||||
use crate::coords::{ViewRegion, TILE_SIZE};
|
use cgmath::{Matrix4, Vector4};
|
||||||
|
use tracing;
|
||||||
use crate::io::scheduler::IOScheduler;
|
|
||||||
use crate::io::LayerTessellateResult;
|
|
||||||
use style_spec::layer::{LayerPaint, StyleLayer};
|
|
||||||
use style_spec::{EncodedSrgb, Style};
|
|
||||||
use wgpu::{Buffer, Limits, Queue};
|
use wgpu::{Buffer, Limits, Queue};
|
||||||
use winit::dpi::PhysicalSize;
|
use winit::dpi::PhysicalSize;
|
||||||
use winit::window::Window;
|
use winit::window::Window;
|
||||||
|
|
||||||
|
use style_spec::layer::{LayerPaint, StyleLayer};
|
||||||
|
use style_spec::{EncodedSrgb, Style};
|
||||||
|
|
||||||
|
use crate::coords::{ViewRegion, TILE_SIZE};
|
||||||
|
use crate::io::scheduler::IOScheduler;
|
||||||
|
use crate::io::LayerTessellateResult;
|
||||||
use crate::platform::{COLOR_TEXTURE_FORMAT, MIN_BUFFER_SIZE};
|
use crate::platform::{COLOR_TEXTURE_FORMAT, MIN_BUFFER_SIZE};
|
||||||
use crate::render::buffer_pool::{BackingBufferDescriptor, BufferPool, IndexEntry};
|
use crate::render::buffer_pool::{BackingBufferDescriptor, BufferPool, IndexEntry};
|
||||||
use crate::render::camera;
|
use crate::render::camera;
|
||||||
use crate::render::camera::ViewProjection;
|
use crate::render::camera::ViewProjection;
|
||||||
use crate::render::options::{
|
use crate::render::options::{
|
||||||
DEBUG_WIREFRAME, FEATURE_METADATA_BUFFER_SIZE, INDEX_FORMAT, INDICES_BUFFER_SIZE,
|
DEBUG_WIREFRAME, FEATURE_METADATA_BUFFER_SIZE, INDEX_FORMAT, INDICES_BUFFER_SIZE,
|
||||||
TILE_META_COUNT, VERTEX_BUFFER_SIZE,
|
TILE_META_COUNT, TILE_VIEW_BUFFER_SIZE, VERTEX_BUFFER_SIZE,
|
||||||
};
|
};
|
||||||
use crate::render::tile_mask_pattern::TileMaskPattern;
|
use crate::render::tile_view_pattern::{TileInView, TileViewPattern};
|
||||||
use crate::tessellation::{IndexDataType, OverAlignedVertexBuffer};
|
use crate::tessellation::{IndexDataType, OverAlignedVertexBuffer};
|
||||||
|
|
||||||
use crate::util::FPSMeter;
|
use crate::util::FPSMeter;
|
||||||
|
|
||||||
use super::piplines::*;
|
use super::piplines::*;
|
||||||
@ -34,8 +34,6 @@ use super::shaders;
|
|||||||
use super::shaders::*;
|
use super::shaders::*;
|
||||||
use super::texture::Texture;
|
use super::texture::Texture;
|
||||||
|
|
||||||
use tracing;
|
|
||||||
|
|
||||||
pub struct RenderState {
|
pub struct RenderState {
|
||||||
instance: wgpu::Instance,
|
instance: wgpu::Instance,
|
||||||
|
|
||||||
@ -70,7 +68,7 @@ pub struct RenderState {
|
|||||||
ShaderFeatureStyle,
|
ShaderFeatureStyle,
|
||||||
>,
|
>,
|
||||||
|
|
||||||
tile_mask_pattern: TileMaskPattern,
|
tile_view_pattern: TileViewPattern<Queue, Buffer>,
|
||||||
|
|
||||||
pub camera: camera::Camera,
|
pub camera: camera::Camera,
|
||||||
pub perspective: camera::Perspective,
|
pub perspective: camera::Perspective,
|
||||||
@ -162,14 +160,21 @@ impl RenderState {
|
|||||||
mapped_at_creation: false,
|
mapped_at_creation: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let tile_view_buffer = device.create_buffer(&wgpu::BufferDescriptor {
|
||||||
|
label: None,
|
||||||
|
size: TILE_VIEW_BUFFER_SIZE,
|
||||||
|
usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
|
||||||
|
mapped_at_creation: false,
|
||||||
|
});
|
||||||
|
|
||||||
let globals_buffer_byte_size =
|
let globals_buffer_byte_size =
|
||||||
cmp::max(MIN_BUFFER_SIZE, std::mem::size_of::<ShaderGlobals>() as u64);
|
cmp::max(MIN_BUFFER_SIZE, std::mem::size_of::<ShaderGlobals>() as u64);
|
||||||
|
|
||||||
let metadata_buffer_size =
|
let layer_metadata_buffer_size =
|
||||||
std::mem::size_of::<ShaderLayerMetadata>() as u64 * TILE_META_COUNT;
|
std::mem::size_of::<ShaderLayerMetadata>() as u64 * TILE_META_COUNT;
|
||||||
let metadata_buffer = device.create_buffer(&wgpu::BufferDescriptor {
|
let layer_metadata_buffer = device.create_buffer(&wgpu::BufferDescriptor {
|
||||||
label: Some("Tiles ubo"),
|
label: Some("Layer Metadata ubo"),
|
||||||
size: metadata_buffer_size,
|
size: layer_metadata_buffer_size,
|
||||||
usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
|
usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
|
||||||
mapped_at_creation: false,
|
mapped_at_creation: false,
|
||||||
});
|
});
|
||||||
@ -297,10 +302,13 @@ impl RenderState {
|
|||||||
buffer_pool: BufferPool::new(
|
buffer_pool: BufferPool::new(
|
||||||
BackingBufferDescriptor::new(vertex_buffer, VERTEX_BUFFER_SIZE),
|
BackingBufferDescriptor::new(vertex_buffer, VERTEX_BUFFER_SIZE),
|
||||||
BackingBufferDescriptor::new(indices_buffer, INDICES_BUFFER_SIZE),
|
BackingBufferDescriptor::new(indices_buffer, INDICES_BUFFER_SIZE),
|
||||||
BackingBufferDescriptor::new(metadata_buffer, metadata_buffer_size),
|
BackingBufferDescriptor::new(layer_metadata_buffer, layer_metadata_buffer_size),
|
||||||
BackingBufferDescriptor::new(feature_metadata_buffer, FEATURE_METADATA_BUFFER_SIZE),
|
BackingBufferDescriptor::new(feature_metadata_buffer, FEATURE_METADATA_BUFFER_SIZE),
|
||||||
),
|
),
|
||||||
tile_mask_pattern: TileMaskPattern::new(),
|
tile_view_pattern: TileViewPattern::new(BackingBufferDescriptor::new(
|
||||||
|
tile_view_buffer,
|
||||||
|
TILE_VIEW_BUFFER_SIZE,
|
||||||
|
)),
|
||||||
zoom: 0.0,
|
zoom: 0.0,
|
||||||
style,
|
style,
|
||||||
}
|
}
|
||||||
@ -379,11 +387,16 @@ impl RenderState {
|
|||||||
/// tile metadata in the the `buffer_pool` gets updated exactly once and not twice.
|
/// tile metadata in the the `buffer_pool` gets updated exactly once and not twice.
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
fn update_metadata(
|
fn update_metadata(
|
||||||
&self,
|
&mut self,
|
||||||
_scheduler: &mut IOScheduler,
|
scheduler: &mut IOScheduler,
|
||||||
view_region: &ViewRegion,
|
view_region: &ViewRegion,
|
||||||
view_proj: &ViewProjection,
|
view_proj: &ViewProjection,
|
||||||
) {
|
) {
|
||||||
|
self.tile_view_pattern
|
||||||
|
.update_pattern(view_region, scheduler.get_tile_cache(), self.zoom);
|
||||||
|
self.tile_view_pattern
|
||||||
|
.upload_pattern(&self.queue, &view_proj);
|
||||||
|
|
||||||
/*let animated_one = 0.5
|
/*let animated_one = 0.5
|
||||||
* (1.0
|
* (1.0
|
||||||
+ ((SystemTime::now()
|
+ ((SystemTime::now()
|
||||||
@ -396,80 +409,59 @@ impl RenderState {
|
|||||||
// Factor which determines how much we need to adjust the width of lines for example.
|
// Factor which determines how much we need to adjust the width of lines for example.
|
||||||
// If zoom == z -> zoom_factor == 1
|
// If zoom == z -> zoom_factor == 1
|
||||||
|
|
||||||
for entries in self.buffer_pool.index().iter() {
|
/* for entries in self.buffer_pool.index().iter() {
|
||||||
for entry in entries {
|
for entry in entries {
|
||||||
let world_coords = entry.coords;
|
let world_coords = entry.coords;*/
|
||||||
|
|
||||||
// FIXME: Does not take into account rendering tiles with different z
|
// TODO: Update features
|
||||||
/*if !view_region.is_in_view(&entry.coords) {
|
/*let source_layer = entry.style_layer.source_layer.as_ref().unwrap();
|
||||||
continue;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
let zoom_factor = 2.0_f64.powf(world_coords.z as f64 - self.zoom) as f32;
|
if let Some(result) = scheduler
|
||||||
|
.get_tile_cache()
|
||||||
|
.iter_tessellated_layers_at(&world_coords)
|
||||||
|
.unwrap()
|
||||||
|
.find(|layer| source_layer.as_str() == layer.layer_name())
|
||||||
|
{
|
||||||
|
let color: Option<Vec4f32> = entry
|
||||||
|
.style_layer
|
||||||
|
.paint
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|paint| paint.get_color())
|
||||||
|
.map(|mut color| {
|
||||||
|
color.color.b = animated_one as f32;
|
||||||
|
color.into()
|
||||||
|
});
|
||||||
|
|
||||||
let transform: Matrix4<f32> = (view_proj
|
match result {
|
||||||
.to_model_view_projection(world_coords.transform_for_zoom(self.zoom)))
|
LayerTessellateResult::UnavailableLayer { .. } => {}
|
||||||
.downcast();
|
LayerTessellateResult::TessellatedLayer {
|
||||||
|
layer_data,
|
||||||
|
feature_indices,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
|
||||||
self.buffer_pool.update_layer_metadata(
|
let feature_metadata = layer_data
|
||||||
&self.queue,
|
.features()
|
||||||
entry,
|
.iter()
|
||||||
ShaderLayerMetadata::new(
|
.enumerate()
|
||||||
transform.into(),
|
.flat_map(|(i, _feature)| {
|
||||||
zoom_factor,
|
iter::repeat(ShaderFeatureStyle {
|
||||||
entry.style_layer.index as f32,
|
color: color.unwrap(),
|
||||||
),
|
})
|
||||||
);
|
.take(feature_indices[i] as usize)
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
// TODO: Update features
|
self.buffer_pool.update_feature_metadata(
|
||||||
/*let source_layer = entry.style_layer.source_layer.as_ref().unwrap();
|
&self.queue,
|
||||||
|
entry,
|
||||||
if let Some(result) = scheduler
|
&feature_metadata,
|
||||||
.get_tile_cache()
|
);
|
||||||
.iter_tessellated_layers_at(&world_coords)
|
}
|
||||||
.unwrap()
|
|
||||||
.find(|layer| source_layer.as_str() == layer.layer_name())
|
|
||||||
{
|
|
||||||
let color: Option<Vec4f32> = entry
|
|
||||||
.style_layer
|
|
||||||
.paint
|
|
||||||
.as_ref()
|
|
||||||
.and_then(|paint| paint.get_color())
|
|
||||||
.map(|mut color| {
|
|
||||||
color.color.b = animated_one as f32;
|
|
||||||
color.into()
|
|
||||||
});
|
|
||||||
|
|
||||||
match result {
|
|
||||||
LayerTessellateResult::UnavailableLayer { .. } => {}
|
|
||||||
LayerTessellateResult::TessellatedLayer {
|
|
||||||
layer_data,
|
|
||||||
feature_indices,
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
|
|
||||||
let feature_metadata = layer_data
|
|
||||||
.features()
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.flat_map(|(i, _feature)| {
|
|
||||||
iter::repeat(ShaderFeatureStyle {
|
|
||||||
color: color.unwrap(),
|
|
||||||
})
|
|
||||||
.take(feature_indices[i] as usize)
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
self.buffer_pool.update_feature_metadata(
|
|
||||||
&self.queue,
|
|
||||||
entry,
|
|
||||||
&feature_metadata,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
|
/* }
|
||||||
|
}*/
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
@ -481,10 +473,6 @@ impl RenderState {
|
|||||||
) {
|
) {
|
||||||
let visible_z = self.visible_z();
|
let visible_z = self.visible_z();
|
||||||
|
|
||||||
// Factor which determines how much we need to adjust the width of lines for example.
|
|
||||||
// If zoom == z -> zoom_factor == 1
|
|
||||||
let zoom_factor = 2.0_f64.powf(visible_z as f64 - self.zoom) as f32; // TODO deduplicate
|
|
||||||
|
|
||||||
// Upload all tessellated layers which are in view
|
// Upload all tessellated layers which are in view
|
||||||
for world_coords in view_region.iter() {
|
for world_coords in view_region.iter() {
|
||||||
let loaded_layers = self
|
let loaded_layers = self
|
||||||
@ -538,23 +526,12 @@ impl RenderState {
|
|||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
// We are casting here from 64bit to 32bit, because 32bit is more performant and is
|
|
||||||
// better supported.
|
|
||||||
let transform: Matrix4<f32> = (view_proj.to_model_view_projection(
|
|
||||||
world_coords.transform_for_zoom(self.zoom),
|
|
||||||
))
|
|
||||||
.downcast();
|
|
||||||
|
|
||||||
self.buffer_pool.allocate_layer_geometry(
|
self.buffer_pool.allocate_layer_geometry(
|
||||||
&self.queue,
|
&self.queue,
|
||||||
*coords,
|
*coords,
|
||||||
style_layer.clone(),
|
style_layer.clone(),
|
||||||
buffer,
|
buffer,
|
||||||
ShaderLayerMetadata::new(
|
ShaderLayerMetadata::new(style_layer.index as f32),
|
||||||
transform.into(),
|
|
||||||
zoom_factor,
|
|
||||||
style_layer.index as f32,
|
|
||||||
),
|
|
||||||
&feature_metadata,
|
&feature_metadata,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -567,18 +544,23 @@ impl RenderState {
|
|||||||
|
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
pub fn prepare_render_data(&mut self, scheduler: &mut IOScheduler) {
|
pub fn prepare_render_data(&mut self, scheduler: &mut IOScheduler) {
|
||||||
let visible_z = self.visible_z();
|
let render_setup_span = tracing::span!(tracing::Level::TRACE, "setup view region");
|
||||||
|
let _guard = render_setup_span.enter();
|
||||||
|
|
||||||
let view_region = self
|
let visible_z = self.visible_z();
|
||||||
.camera
|
|
||||||
.view_region_bounding_box(&self.camera.calc_view_proj(&self.perspective).invert())
|
|
||||||
.map(|bounding_box| ViewRegion::new(bounding_box, 1, self.zoom, visible_z));
|
|
||||||
|
|
||||||
let view_proj = self.camera.calc_view_proj(&self.perspective);
|
let view_proj = self.camera.calc_view_proj(&self.perspective);
|
||||||
|
|
||||||
|
let view_region = self
|
||||||
|
.camera
|
||||||
|
.view_region_bounding_box(&view_proj.invert())
|
||||||
|
.map(|bounding_box| ViewRegion::new(bounding_box, 1, self.zoom, visible_z));
|
||||||
|
|
||||||
|
drop(_guard);
|
||||||
|
|
||||||
if let Some(view_region) = &view_region {
|
if let Some(view_region) = &view_region {
|
||||||
self.update_metadata(scheduler, &view_region, &view_proj);
|
|
||||||
self.upload_tile_geometry(&view_proj, &view_region, scheduler);
|
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);
|
self.request_tiles_in_view(view_region, scheduler);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -597,6 +579,9 @@ impl RenderState {
|
|||||||
|
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
pub fn render(&mut self) -> Result<(), wgpu::SurfaceError> {
|
pub fn render(&mut self) -> Result<(), wgpu::SurfaceError> {
|
||||||
|
let render_setup_span = tracing::span!(tracing::Level::TRACE, "render prepare");
|
||||||
|
let _guard = render_setup_span.enter();
|
||||||
|
|
||||||
let frame = self.surface.get_current_texture()?;
|
let frame = self.surface.get_current_texture()?;
|
||||||
let frame_view = frame
|
let frame_view = frame
|
||||||
.texture
|
.texture
|
||||||
@ -608,88 +593,84 @@ impl RenderState {
|
|||||||
label: Some("Encoder"),
|
label: Some("Encoder"),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
drop(_guard);
|
||||||
|
|
||||||
{
|
{
|
||||||
let color_attachment = if let Some(multisampling_target) = &self.multisampling_texture {
|
let _span_ = tracing::span!(tracing::Level::TRACE, "render pass").entered();
|
||||||
wgpu::RenderPassColorAttachment {
|
|
||||||
view: &multisampling_target.view,
|
|
||||||
ops: wgpu::Operations {
|
|
||||||
load: wgpu::LoadOp::Clear(wgpu::Color::WHITE),
|
|
||||||
store: true,
|
|
||||||
},
|
|
||||||
resolve_target: Some(&frame_view),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
wgpu::RenderPassColorAttachment {
|
|
||||||
view: &frame_view,
|
|
||||||
ops: wgpu::Operations {
|
|
||||||
load: wgpu::LoadOp::Clear(wgpu::Color::WHITE),
|
|
||||||
store: true,
|
|
||||||
},
|
|
||||||
resolve_target: None,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
|
||||||
label: None,
|
|
||||||
color_attachments: &[color_attachment],
|
|
||||||
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
|
|
||||||
view: &self.depth_texture.view,
|
|
||||||
depth_ops: Some(wgpu::Operations {
|
|
||||||
load: wgpu::LoadOp::Clear(0.0),
|
|
||||||
store: true,
|
|
||||||
}),
|
|
||||||
stencil_ops: Some(wgpu::Operations {
|
|
||||||
load: wgpu::LoadOp::Clear(0),
|
|
||||||
store: true,
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
pass.set_bind_group(0, &self.bind_group, &[]);
|
|
||||||
|
|
||||||
{
|
{
|
||||||
let _span_ = tracing::span!(tracing::Level::TRACE, "render pass").entered();
|
let color_attachment =
|
||||||
|
if let Some(multisampling_target) = &self.multisampling_texture {
|
||||||
|
wgpu::RenderPassColorAttachment {
|
||||||
|
view: &multisampling_target.view,
|
||||||
|
ops: wgpu::Operations {
|
||||||
|
load: wgpu::LoadOp::Clear(wgpu::Color::WHITE),
|
||||||
|
store: true,
|
||||||
|
},
|
||||||
|
resolve_target: Some(&frame_view),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
wgpu::RenderPassColorAttachment {
|
||||||
|
view: &frame_view,
|
||||||
|
ops: wgpu::Operations {
|
||||||
|
load: wgpu::LoadOp::Clear(wgpu::Color::WHITE),
|
||||||
|
store: true,
|
||||||
|
},
|
||||||
|
resolve_target: None,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let visible_z = self.visible_z();
|
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||||
let inverted_view_proj = self.camera.calc_view_proj(&self.perspective).invert();
|
label: None,
|
||||||
let view_region = self
|
color_attachments: &[color_attachment],
|
||||||
.camera
|
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
|
||||||
.view_region_bounding_box(&inverted_view_proj)
|
view: &self.depth_texture.view,
|
||||||
.map(|bounding_box| ViewRegion::new(bounding_box, 1, self.zoom, visible_z));
|
depth_ops: Some(wgpu::Operations {
|
||||||
|
load: wgpu::LoadOp::Clear(0.0),
|
||||||
|
store: true,
|
||||||
|
}),
|
||||||
|
stencil_ops: Some(wgpu::Operations {
|
||||||
|
load: wgpu::LoadOp::Clear(0),
|
||||||
|
store: true,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
let index = self.buffer_pool.index();
|
pass.set_bind_group(0, &self.bind_group, &[]);
|
||||||
|
|
||||||
if let Some(view_region) = &view_region {
|
{
|
||||||
for world_coords in view_region.iter() {
|
let index = self.buffer_pool.index();
|
||||||
tracing::trace!("Drawing tile at {world_coords}");
|
|
||||||
|
|
||||||
if let Some(entries) = index.get_layers_fallback(&world_coords) {
|
for TileInView { shape, fallback } in self.tile_view_pattern.iter() {
|
||||||
let mut to_render: Vec<&IndexEntry> = Vec::from_iter(entries);
|
let coords = shape.coords;
|
||||||
to_render.sort_by_key(|entry| entry.style_layer.index);
|
tracing::trace!("Drawing tile at {coords}");
|
||||||
|
|
||||||
let reference = self
|
let shape_to_render = fallback.as_ref().unwrap_or(shape);
|
||||||
.tile_mask_pattern
|
|
||||||
.stencil_reference_value(&world_coords)
|
|
||||||
as u32;
|
|
||||||
|
|
||||||
// Draw mask
|
let reference = self
|
||||||
if let Some(mask_entry) = entries.front() {
|
.tile_view_pattern
|
||||||
{
|
.stencil_reference_value(&shape_to_render.coords)
|
||||||
tracing::trace!("Drawing mask {}", &mask_entry.coords);
|
as u32;
|
||||||
|
|
||||||
pass.set_pipeline(&self.mask_pipeline);
|
// Draw mask
|
||||||
pass.set_stencil_reference(reference);
|
{
|
||||||
pass.set_vertex_buffer(
|
tracing::trace!("Drawing mask {}", &coords);
|
||||||
0,
|
|
||||||
self.buffer_pool
|
|
||||||
.metadata()
|
|
||||||
.slice(mask_entry.layer_metadata_buffer_range()),
|
|
||||||
);
|
|
||||||
pass.draw(0..6, 0..1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for entry in to_render {
|
pass.set_pipeline(&self.mask_pipeline);
|
||||||
|
pass.set_stencil_reference(reference);
|
||||||
|
pass.set_vertex_buffer(
|
||||||
|
0,
|
||||||
|
self.tile_view_pattern
|
||||||
|
.buffer()
|
||||||
|
.slice(shape.buffer_range.clone()),
|
||||||
|
);
|
||||||
|
pass.draw(0..6, 0..1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(entries) = index.get_layers(&shape_to_render.coords) {
|
||||||
|
let mut layers_to_render: Vec<&IndexEntry> = Vec::from_iter(entries);
|
||||||
|
layers_to_render.sort_by_key(|entry| entry.style_layer.index);
|
||||||
|
|
||||||
|
for entry in layers_to_render {
|
||||||
// Draw tile
|
// Draw tile
|
||||||
{
|
{
|
||||||
tracing::trace!(
|
tracing::trace!(
|
||||||
@ -714,12 +695,18 @@ impl RenderState {
|
|||||||
);
|
);
|
||||||
pass.set_vertex_buffer(
|
pass.set_vertex_buffer(
|
||||||
1,
|
1,
|
||||||
|
self.tile_view_pattern
|
||||||
|
.buffer()
|
||||||
|
.slice(shape_to_render.buffer_range.clone()),
|
||||||
|
);
|
||||||
|
pass.set_vertex_buffer(
|
||||||
|
2,
|
||||||
self.buffer_pool
|
self.buffer_pool
|
||||||
.metadata()
|
.metadata()
|
||||||
.slice(entry.layer_metadata_buffer_range()),
|
.slice(entry.layer_metadata_buffer_range()),
|
||||||
);
|
);
|
||||||
pass.set_vertex_buffer(
|
pass.set_vertex_buffer(
|
||||||
2,
|
3,
|
||||||
self.buffer_pool
|
self.buffer_pool
|
||||||
.feature_metadata()
|
.feature_metadata()
|
||||||
.slice(entry.feature_metadata_buffer_range()),
|
.slice(entry.feature_metadata_buffer_range()),
|
||||||
@ -733,8 +720,16 @@ impl RenderState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.queue.submit(Some(encoder.finish()));
|
{
|
||||||
frame.present();
|
let _span = tracing::span!(tracing::Level::TRACE, "render finish").entered();
|
||||||
|
tracing::trace!("Finished drawing");
|
||||||
|
|
||||||
|
self.queue.submit(Some(encoder.finish()));
|
||||||
|
tracing::trace!("Submitted queue");
|
||||||
|
|
||||||
|
frame.present();
|
||||||
|
tracing::trace!("Presented frame");
|
||||||
|
}
|
||||||
|
|
||||||
self.fps_meter.update_and_print();
|
self.fps_meter.update_and_print();
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@ -82,7 +82,7 @@ impl VertexShaderState {
|
|||||||
pub mod tile {
|
pub mod tile {
|
||||||
use super::{ShaderLayerMetadata, ShaderVertex};
|
use super::{ShaderLayerMetadata, ShaderVertex};
|
||||||
use crate::platform::COLOR_TEXTURE_FORMAT;
|
use crate::platform::COLOR_TEXTURE_FORMAT;
|
||||||
use crate::render::shaders::ShaderFeatureStyle;
|
use crate::render::shaders::{ShaderFeatureStyle, ShaderTileMetadata};
|
||||||
|
|
||||||
use super::{FragmentShaderState, VertexShaderState};
|
use super::{FragmentShaderState, VertexShaderState};
|
||||||
|
|
||||||
@ -110,7 +110,7 @@ pub mod tile {
|
|||||||
},
|
},
|
||||||
// tile metadata
|
// tile metadata
|
||||||
wgpu::VertexBufferLayout {
|
wgpu::VertexBufferLayout {
|
||||||
array_stride: std::mem::size_of::<ShaderLayerMetadata>() as u64,
|
array_stride: std::mem::size_of::<ShaderTileMetadata>() as u64,
|
||||||
step_mode: wgpu::VertexStepMode::Instance,
|
step_mode: wgpu::VertexStepMode::Instance,
|
||||||
attributes: &[
|
attributes: &[
|
||||||
// translate
|
// translate
|
||||||
@ -134,20 +134,28 @@ pub mod tile {
|
|||||||
format: wgpu::VertexFormat::Float32x4,
|
format: wgpu::VertexFormat::Float32x4,
|
||||||
shader_location: 7,
|
shader_location: 7,
|
||||||
},
|
},
|
||||||
|
// zoom_factor
|
||||||
wgpu::VertexAttribute {
|
wgpu::VertexAttribute {
|
||||||
offset: 4 * wgpu::VertexFormat::Float32x4.size(),
|
offset: 4 * wgpu::VertexFormat::Float32x4.size(),
|
||||||
format: wgpu::VertexFormat::Float32,
|
format: wgpu::VertexFormat::Float32,
|
||||||
shader_location: 9,
|
shader_location: 9,
|
||||||
},
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
// layer metadata
|
||||||
|
wgpu::VertexBufferLayout {
|
||||||
|
array_stride: std::mem::size_of::<ShaderLayerMetadata>() as u64,
|
||||||
|
step_mode: wgpu::VertexStepMode::Instance,
|
||||||
|
attributes: &[
|
||||||
|
// z_index
|
||||||
wgpu::VertexAttribute {
|
wgpu::VertexAttribute {
|
||||||
offset: 4 * wgpu::VertexFormat::Float32x4.size()
|
offset: 0,
|
||||||
+ wgpu::VertexFormat::Float32.size(),
|
|
||||||
format: wgpu::VertexFormat::Float32,
|
format: wgpu::VertexFormat::Float32,
|
||||||
shader_location: 10,
|
shader_location: 10,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
// vertex style
|
// features
|
||||||
wgpu::VertexBufferLayout {
|
wgpu::VertexBufferLayout {
|
||||||
array_stride: std::mem::size_of::<ShaderFeatureStyle>() as u64,
|
array_stride: std::mem::size_of::<ShaderFeatureStyle>() as u64,
|
||||||
step_mode: wgpu::VertexStepMode::Vertex,
|
step_mode: wgpu::VertexStepMode::Vertex,
|
||||||
@ -188,7 +196,7 @@ pub mod tile {
|
|||||||
pub mod tile_mask {
|
pub mod tile_mask {
|
||||||
use crate::platform::COLOR_TEXTURE_FORMAT;
|
use crate::platform::COLOR_TEXTURE_FORMAT;
|
||||||
use crate::render::options::DEBUG_STENCIL_PATTERN;
|
use crate::render::options::DEBUG_STENCIL_PATTERN;
|
||||||
use crate::render::shaders::ShaderLayerMetadata;
|
use crate::render::shaders::{ShaderLayerMetadata, ShaderTileMetadata};
|
||||||
use wgpu::ColorWrites;
|
use wgpu::ColorWrites;
|
||||||
|
|
||||||
use super::{FragmentShaderState, VertexShaderState};
|
use super::{FragmentShaderState, VertexShaderState};
|
||||||
@ -196,7 +204,7 @@ pub mod tile_mask {
|
|||||||
pub const VERTEX: VertexShaderState = VertexShaderState::new(
|
pub const VERTEX: VertexShaderState = VertexShaderState::new(
|
||||||
include_str!("tile_mask.vertex.wgsl"),
|
include_str!("tile_mask.vertex.wgsl"),
|
||||||
&[wgpu::VertexBufferLayout {
|
&[wgpu::VertexBufferLayout {
|
||||||
array_stride: std::mem::size_of::<ShaderLayerMetadata>() as u64,
|
array_stride: std::mem::size_of::<ShaderTileMetadata>() as u64,
|
||||||
step_mode: wgpu::VertexStepMode::Instance,
|
step_mode: wgpu::VertexStepMode::Instance,
|
||||||
attributes: &[
|
attributes: &[
|
||||||
// translate
|
// translate
|
||||||
@ -309,17 +317,27 @@ pub struct ShaderFeatureStyle {
|
|||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Copy, Clone, Pod, Zeroable)]
|
#[derive(Copy, Clone, Pod, Zeroable)]
|
||||||
pub struct ShaderLayerMetadata {
|
pub struct ShaderLayerMetadata {
|
||||||
pub transform: Mat4x4f32,
|
|
||||||
pub zoom_factor: f32,
|
|
||||||
pub z_index: f32,
|
pub z_index: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ShaderLayerMetadata {
|
impl ShaderLayerMetadata {
|
||||||
pub fn new(transform: Mat4x4f32, zoom_factor: f32, z_index: f32) -> Self {
|
pub fn new(z_index: f32) -> Self {
|
||||||
|
Self { z_index }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone, Pod, Zeroable)]
|
||||||
|
pub struct ShaderTileMetadata {
|
||||||
|
pub transform: Mat4x4f32,
|
||||||
|
pub zoom_factor: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ShaderTileMetadata {
|
||||||
|
pub fn new(transform: Mat4x4f32, zoom_factor: f32) -> Self {
|
||||||
Self {
|
Self {
|
||||||
transform,
|
transform,
|
||||||
zoom_factor,
|
zoom_factor,
|
||||||
z_index,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,21 +0,0 @@
|
|||||||
use crate::coords::WorldTileCoords;
|
|
||||||
|
|
||||||
/// The tile mask pattern assigns each tile a value which can be used for stencil testing.
|
|
||||||
/// The pattern can be reviewed [here](https://maxammann.org/mapr/docs/stencil-masking.html).
|
|
||||||
pub struct TileMaskPattern {}
|
|
||||||
|
|
||||||
impl TileMaskPattern {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn stencil_reference_value(&self, world_coords: &WorldTileCoords) -> u8 {
|
|
||||||
match (world_coords.x, world_coords.y) {
|
|
||||||
(x, y) if x % 2 == 0 && y % 2 == 0 => 2,
|
|
||||||
(x, y) if x % 2 == 0 && y % 2 != 0 => 1,
|
|
||||||
(x, y) if x % 2 != 0 && y % 2 == 0 => 4,
|
|
||||||
(x, y) if x % 2 != 0 && y % 2 != 0 => 3,
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
157
src/render/tile_view_pattern.rs
Normal file
157
src/render/tile_view_pattern.rs
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
use crate::coords::{Quadkey, ViewRegion, WorldTileCoords};
|
||||||
|
use crate::io::tile_cache::TileCache;
|
||||||
|
use crate::render::buffer_pool::{BackingBufferDescriptor, Queue};
|
||||||
|
use crate::render::camera::ViewProjection;
|
||||||
|
use crate::render::shaders::ShaderTileMetadata;
|
||||||
|
use cgmath::Matrix4;
|
||||||
|
use lyon::geom::euclid::approxeq::ApproxEq;
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
use std::mem::size_of;
|
||||||
|
use std::ops::Range;
|
||||||
|
|
||||||
|
/// The tile mask pattern assigns each tile a value which can be used for stencil testing.
|
||||||
|
pub struct TileViewPattern<Q, B> {
|
||||||
|
in_view: Vec<TileInView>,
|
||||||
|
buffer: BackingBuffer<B>,
|
||||||
|
phantom_q: PhantomData<Q>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TileShape {
|
||||||
|
pub zoom_factor: f64,
|
||||||
|
|
||||||
|
pub coords: WorldTileCoords,
|
||||||
|
|
||||||
|
pub transform: Matrix4<f64>,
|
||||||
|
pub buffer_range: Range<wgpu::BufferAddress>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TileInView {
|
||||||
|
pub shape: TileShape,
|
||||||
|
|
||||||
|
pub fallback: Option<TileShape>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct BackingBuffer<B> {
|
||||||
|
/// The internal structure which is used for storage
|
||||||
|
inner: B,
|
||||||
|
/// The size of the `inner` buffer
|
||||||
|
inner_size: wgpu::BufferAddress,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<B> BackingBuffer<B> {
|
||||||
|
fn new(inner: B, inner_size: wgpu::BufferAddress) -> Self {
|
||||||
|
Self { inner, inner_size }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Q: Queue<B>, B> TileViewPattern<Q, B> {
|
||||||
|
pub fn new(buffer: BackingBufferDescriptor<B>) -> Self {
|
||||||
|
Self {
|
||||||
|
in_view: Vec::with_capacity(64),
|
||||||
|
buffer: BackingBuffer::new(buffer.buffer, buffer.inner_size),
|
||||||
|
phantom_q: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(skip_all)]
|
||||||
|
pub fn update_pattern(&mut self, view_region: &ViewRegion, tile_cache: &TileCache, zoom: f64) {
|
||||||
|
self.in_view.clear();
|
||||||
|
|
||||||
|
let stride = size_of::<ShaderTileMetadata>() as u64;
|
||||||
|
|
||||||
|
let mut index = 0;
|
||||||
|
|
||||||
|
for coords in view_region.iter() {
|
||||||
|
if let None = coords.build_quad_key() {
|
||||||
|
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,
|
||||||
|
};
|
||||||
|
|
||||||
|
index += 1;
|
||||||
|
|
||||||
|
let fallback = {
|
||||||
|
if !tile_cache.has_tile(&coords) {
|
||||||
|
if let Some(fallback_coords) = tile_cache.get_tile_coords_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,
|
||||||
|
};
|
||||||
|
|
||||||
|
index += 1;
|
||||||
|
Some(shape)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
self.in_view.push(TileInView { shape, fallback });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn iter(&self) -> impl Iterator<Item = &TileInView> + '_ {
|
||||||
|
self.in_view.iter()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn buffer(&self) -> &B {
|
||||||
|
&self.buffer.inner
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(skip_all)]
|
||||||
|
pub fn upload_pattern(&self, queue: &Q, view_proj: &ViewProjection) {
|
||||||
|
let mut buffer = Vec::with_capacity(self.in_view.len());
|
||||||
|
|
||||||
|
for tile in &self.in_view {
|
||||||
|
buffer.push(ShaderTileMetadata {
|
||||||
|
// We are casting here from 64bit to 32bit, because 32bit is more performant and is
|
||||||
|
// better supported.
|
||||||
|
transform: view_proj
|
||||||
|
.to_model_view_projection(tile.shape.transform)
|
||||||
|
.downcast()
|
||||||
|
.into(),
|
||||||
|
zoom_factor: tile.shape.zoom_factor as f32,
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Some(fallback_shape) = &tile.fallback {
|
||||||
|
buffer.push(ShaderTileMetadata {
|
||||||
|
// We are casting here from 64bit to 32bit, because 32bit is more performant and is
|
||||||
|
// better supported.
|
||||||
|
transform: view_proj
|
||||||
|
.to_model_view_projection(fallback_shape.transform)
|
||||||
|
.downcast()
|
||||||
|
.into(),
|
||||||
|
zoom_factor: fallback_shape.zoom_factor as f32,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
queue.write_buffer(
|
||||||
|
&self.buffer.inner,
|
||||||
|
0,
|
||||||
|
&bytemuck::cast_slice(&buffer.as_slice()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn stencil_reference_value(&self, world_coords: &WorldTileCoords) -> u8 {
|
||||||
|
world_coords.z * 5
|
||||||
|
+ match (world_coords.x, world_coords.y) {
|
||||||
|
(x, y) if x % 2 == 0 && y % 2 == 0 => 2,
|
||||||
|
(x, y) if x % 2 == 0 && y % 2 != 0 => 1,
|
||||||
|
(x, y) if x % 2 != 0 && y % 2 == 0 => 4,
|
||||||
|
(x, y) if x % 2 != 0 && y % 2 != 0 => 3,
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user