Refactor render loop to use a single pattern for the current view

This commit is contained in:
Maximilian Ammann 2022-04-04 12:29:55 +02:00
parent a1b511db45
commit 575cd07924
8 changed files with 403 additions and 220 deletions

View File

@ -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(&current) {
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,

View File

@ -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,
} }

View File

@ -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;

View File

@ -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;

View File

@ -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(())

View File

@ -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,
} }
} }
} }

View File

@ -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!(),
}
}
}

View 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!(),
}
}
}