mirror of
https://github.com/maplibre/maplibre-rs.git
synced 2025-12-08 19:05:57 +00:00
Add debug lines and improve tile queuing (#226)
* Add debug lines * Upgrade package lock * Improve tile view pattern * Improve naming and simplify * Render children in parent is not yet available * Increase buffer pool size * Use 3d compatible stencil value * Only use coords intead of whole TileShape in TileView * Add some comments * Fix stencil value
This commit is contained in:
parent
f453ede220
commit
2b917e9e08
@ -5,27 +5,6 @@
|
||||
|
||||
use maplibre_build_tools::wgsl::validate_project_wgsl;
|
||||
|
||||
/*
|
||||
fn generate_type_def() {
|
||||
use std::fs::File;
|
||||
use std::io::BufReader;
|
||||
use serde_json::Value;
|
||||
let f = File::open("style-spec-v8.json").unwrap();
|
||||
let mut reader = BufReader::new(f);
|
||||
let result = serde_json::from_reader::<_, Value>(&mut reader).unwrap();
|
||||
|
||||
let spec_root = result.as_object()?;
|
||||
let version = &spec_root["$version"].as_i64()?;
|
||||
let root = &spec_root["$root"].as_object()?;
|
||||
|
||||
for x in spec_root {
|
||||
|
||||
}
|
||||
|
||||
println!("cargo:warning={:?}", version);
|
||||
}
|
||||
*/
|
||||
|
||||
#[cfg(feature = "embed-static-tiles")]
|
||||
fn embed_tiles_statically() {
|
||||
use std::{env, path::Path};
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
//! Tile cache.
|
||||
|
||||
use std::collections::{btree_map, BTreeMap};
|
||||
use std::collections::{btree_map, btree_map::Entry, BTreeMap};
|
||||
|
||||
use bytemuck::Pod;
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::{
|
||||
coords::{Quadkey, WorldTileCoords},
|
||||
@ -85,6 +86,13 @@ impl StoredTile {
|
||||
}
|
||||
}
|
||||
}
|
||||
#[derive(Error, Debug)]
|
||||
pub enum MarkError {
|
||||
#[error("no pending tile at coords")]
|
||||
NoPendingTile,
|
||||
#[error("unable to construct quadkey")]
|
||||
QuadKey,
|
||||
}
|
||||
|
||||
/// Stores and provides access to a quad tree of cached tiles with world tile coords.
|
||||
#[derive(Default)]
|
||||
@ -99,10 +107,6 @@ impl TileRepository {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.tree.clear();
|
||||
}
|
||||
|
||||
/// Inserts a tessellated layer into the quad tree at its world tile coords.
|
||||
/// If the space is vacant, the tessellated layer is inserted into a new
|
||||
/// [crate::io::tile_repository::StoredLayer].
|
||||
@ -126,12 +130,6 @@ impl TileRepository {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn put_tile(&mut self, tile: StoredTile) {
|
||||
if let Some(key) = tile.coords.build_quad_key() {
|
||||
self.tree.insert(key, tile);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the list of tessellated layers at the given world tile coords. None if tile is
|
||||
/// missing from the cache.
|
||||
pub fn iter_layers_at(
|
||||
@ -141,7 +139,14 @@ impl TileRepository {
|
||||
coords
|
||||
.build_quad_key()
|
||||
.and_then(|key| self.tree.get(&key))
|
||||
.map(|results| results.layers.iter())
|
||||
.and_then(|tile| {
|
||||
if tile.status == TileStatus::Success {
|
||||
Some(tile)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.map(|tile| tile.layers.iter())
|
||||
}
|
||||
|
||||
/// Returns the list of tessellated layers at the given world tile coords, which are loaded in
|
||||
@ -160,18 +165,8 @@ impl TileRepository {
|
||||
})
|
||||
}
|
||||
|
||||
/// Create a new tile.
|
||||
pub fn create_tile(&mut self, coords: WorldTileCoords) -> bool {
|
||||
if let Some(btree_map::Entry::Vacant(entry)) =
|
||||
coords.build_quad_key().map(|key| self.tree.entry(key))
|
||||
{
|
||||
entry.insert(StoredTile::pending(coords));
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
/// Checks if a layer has been fetched.
|
||||
pub fn has_tile(&self, coords: &WorldTileCoords) -> bool {
|
||||
/// Checks fetching of a tile has been started
|
||||
pub fn is_tile_pending_or_done(&self, coords: &WorldTileCoords) -> bool {
|
||||
if coords
|
||||
.build_quad_key()
|
||||
.and_then(|key| self.tree.get(&key))
|
||||
@ -182,22 +177,52 @@ impl TileRepository {
|
||||
true
|
||||
}
|
||||
|
||||
pub fn mark_tile_succeeded(&mut self, coords: &WorldTileCoords) {
|
||||
if let Some(cached_tile) = coords
|
||||
.build_quad_key()
|
||||
.and_then(|key| self.tree.get_mut(&key))
|
||||
{
|
||||
cached_tile.status = TileStatus::Success;
|
||||
/// Mark the tile at `coords` pending in this tile repository.
|
||||
pub fn mark_tile_pending(&mut self, coords: WorldTileCoords) -> Result<(), MarkError> {
|
||||
let Some(key) = coords.build_quad_key() else { return Err(MarkError::QuadKey); };
|
||||
|
||||
match self.tree.entry(key) {
|
||||
Entry::Vacant(entry) => {
|
||||
entry.insert(StoredTile::pending(coords));
|
||||
}
|
||||
Entry::Occupied(mut entry) => {
|
||||
entry.get_mut().status = TileStatus::Pending;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Mark the tile at `coords` succeeded in this tile repository. Only succeeds if there is a
|
||||
/// pending tile at `coords`.
|
||||
pub fn mark_tile_succeeded(&mut self, coords: &WorldTileCoords) -> Result<(), MarkError> {
|
||||
self.mark_tile(coords, TileStatus::Success)
|
||||
}
|
||||
|
||||
/// Mark the tile at `coords` failed in this tile repository. Only succeeds if there is a
|
||||
/// pending tile at `coords`.
|
||||
pub fn mark_tile_failed(&mut self, coords: &WorldTileCoords) -> Result<(), MarkError> {
|
||||
self.mark_tile(coords, TileStatus::Failed)
|
||||
}
|
||||
|
||||
fn mark_tile(&mut self, coords: &WorldTileCoords, status: TileStatus) -> Result<(), MarkError> {
|
||||
let Some(key) = coords.build_quad_key() else { return Err(MarkError::QuadKey); };
|
||||
|
||||
if let Entry::Occupied(mut entry) = self.tree.entry(key) {
|
||||
entry.get_mut().status = status;
|
||||
Ok(())
|
||||
} else {
|
||||
Err(MarkError::NoPendingTile)
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if a layer has been fetched.
|
||||
pub fn mark_tile_failed(&mut self, coords: &WorldTileCoords) {
|
||||
if let Some(cached_tile) = coords
|
||||
.build_quad_key()
|
||||
.and_then(|key| self.tree.get_mut(&key))
|
||||
{
|
||||
cached_tile.status = TileStatus::Failed;
|
||||
pub fn put_tile(&mut self, tile: StoredTile) {
|
||||
if let Some(key) = tile.coords.build_quad_key() {
|
||||
self.tree.insert(key, tile);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.tree.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@ -93,7 +93,7 @@ where
|
||||
.build()
|
||||
.initialize_renderer::<E::MapWindowConfig>(&self.window)
|
||||
.await
|
||||
.map_err(|e| MapError::DeviceInit(e))?;
|
||||
.map_err(MapError::DeviceInit)?;
|
||||
|
||||
let window_size = self.window.size();
|
||||
|
||||
|
||||
65
maplibre/src/render/debug_pass.rs
Normal file
65
maplibre/src/render/debug_pass.rs
Normal file
@ -0,0 +1,65 @@
|
||||
use std::ops::Deref;
|
||||
|
||||
use crate::render::{
|
||||
graph::{Node, NodeRunError, RenderContext, RenderGraphContext, SlotInfo},
|
||||
render_commands::DrawDebugOutlines,
|
||||
render_phase::RenderCommand,
|
||||
resource::TrackedRenderPass,
|
||||
Eventually::Initialized,
|
||||
RenderState,
|
||||
};
|
||||
|
||||
/// Pass which renders debug information on top of the map.
|
||||
pub struct DebugPassNode {}
|
||||
|
||||
impl DebugPassNode {
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
||||
impl Node for DebugPassNode {
|
||||
fn input(&self) -> Vec<SlotInfo> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
fn update(&mut self, _state: &mut RenderState) {}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
_graph: &mut RenderGraphContext,
|
||||
render_context: &mut RenderContext,
|
||||
state: &RenderState,
|
||||
) -> Result<(), NodeRunError> {
|
||||
let Initialized(render_target) = &state.render_target else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let color_attachment = wgpu::RenderPassColorAttachment {
|
||||
view: render_target.deref(),
|
||||
ops: wgpu::Operations {
|
||||
// Draws on-top of previously rendered data
|
||||
load: wgpu::LoadOp::Load,
|
||||
store: true,
|
||||
},
|
||||
resolve_target: None,
|
||||
};
|
||||
|
||||
let render_pass =
|
||||
render_context
|
||||
.command_encoder
|
||||
.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||
label: None,
|
||||
color_attachments: &[Some(color_attachment)],
|
||||
depth_stencil_attachment: None,
|
||||
});
|
||||
|
||||
let mut tracked_pass = TrackedRenderPass::new(render_pass);
|
||||
|
||||
for item in &state.mask_phase.items {
|
||||
DrawDebugOutlines::render(state, item, &mut tracked_pass);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@ -14,12 +14,6 @@ pub enum RenderError {
|
||||
|
||||
impl RenderError {
|
||||
pub fn should_exit(&self) -> bool {
|
||||
match self {
|
||||
RenderError::Surface(e) => match e {
|
||||
wgpu::SurfaceError::OutOfMemory => true,
|
||||
_ => false,
|
||||
},
|
||||
_ => true,
|
||||
}
|
||||
matches!(self, RenderError::Surface(wgpu::SurfaceError::OutOfMemory))
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,7 +27,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Eventually<T>
|
||||
impl<T> Eventually<T>
|
||||
where
|
||||
T: HasChanged,
|
||||
{
|
||||
|
||||
@ -36,17 +36,13 @@ impl Node for MainPassNode {
|
||||
render_context: &mut RenderContext,
|
||||
state: &RenderState,
|
||||
) -> Result<(), NodeRunError> {
|
||||
let (render_target, multisampling_texture, depth_texture) = if let (
|
||||
Initialized(render_target),
|
||||
Initialized(multisampling_texture),
|
||||
Initialized(depth_texture),
|
||||
) = (
|
||||
&state.render_target,
|
||||
&state.multisampling_texture,
|
||||
&state.depth_texture,
|
||||
) {
|
||||
(render_target, multisampling_texture, depth_texture)
|
||||
} else {
|
||||
let Initialized(render_target) = &state.render_target else {
|
||||
return Ok(());
|
||||
};
|
||||
let Initialized(multisampling_texture) = &state.multisampling_texture else {
|
||||
return Ok(());
|
||||
};
|
||||
let Initialized(depth_texture) = &state.depth_texture else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
@ -98,6 +94,7 @@ impl Node for MainPassNode {
|
||||
for item in &state.tile_phase.items {
|
||||
DrawTiles::render(state, item, &mut tracked_pass);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,7 +27,7 @@ use crate::{
|
||||
resource::{BufferPool, Globals, Head, IndexEntry, Surface, Texture, TextureView},
|
||||
settings::{RendererSettings, WgpuSettings},
|
||||
shaders::{ShaderFeatureStyle, ShaderLayerMetadata},
|
||||
tile_view_pattern::{TileInView, TileShape, TileViewPattern},
|
||||
tile_view_pattern::{TileShape, TileViewPattern},
|
||||
},
|
||||
tessellation::IndexDataType,
|
||||
};
|
||||
@ -37,6 +37,7 @@ pub mod resource;
|
||||
pub mod stages;
|
||||
|
||||
// Rendering internals
|
||||
mod debug_pass;
|
||||
mod graph_runner;
|
||||
mod main_pass;
|
||||
mod render_commands;
|
||||
@ -57,6 +58,7 @@ pub use stages::register_default_render_stages;
|
||||
|
||||
use crate::{
|
||||
render::{
|
||||
debug_pass::DebugPassNode,
|
||||
error::RenderError,
|
||||
graph::{EmptyNode, RenderGraph, RenderGraphError},
|
||||
main_pass::{MainPassDriverNode, MainPassNode},
|
||||
@ -83,6 +85,7 @@ pub struct RenderState {
|
||||
|
||||
tile_pipeline: Eventually<wgpu::RenderPipeline>,
|
||||
mask_pipeline: Eventually<wgpu::RenderPipeline>,
|
||||
debug_pipeline: Eventually<wgpu::RenderPipeline>,
|
||||
|
||||
globals_bind_group: Eventually<Globals>,
|
||||
|
||||
@ -91,7 +94,7 @@ pub struct RenderState {
|
||||
|
||||
surface: Surface,
|
||||
|
||||
mask_phase: RenderPhase<TileInView>,
|
||||
mask_phase: RenderPhase<TileShape>,
|
||||
tile_phase: RenderPhase<(IndexEntry, TileShape)>,
|
||||
}
|
||||
|
||||
@ -103,6 +106,7 @@ impl RenderState {
|
||||
tile_view_pattern: Default::default(),
|
||||
tile_pipeline: Default::default(),
|
||||
mask_pipeline: Default::default(),
|
||||
debug_pipeline: Default::default(),
|
||||
globals_bind_group: Default::default(),
|
||||
depth_texture: Default::default(),
|
||||
multisampling_texture: Default::default(),
|
||||
@ -242,7 +246,7 @@ impl Renderer {
|
||||
let adapter = instance
|
||||
.request_adapter(request_adapter_options)
|
||||
.await
|
||||
.ok_or_else(|| wgpu::RequestDeviceError)?;
|
||||
.ok_or(wgpu::RequestDeviceError)?;
|
||||
|
||||
let adapter_info = adapter.get_info();
|
||||
|
||||
@ -497,6 +501,7 @@ pub mod draw_graph {
|
||||
// Labels for non-input nodes
|
||||
pub mod node {
|
||||
pub const MAIN_PASS: &str = "main_pass";
|
||||
pub const DEBUG_PASS: &str = "debug_pass";
|
||||
#[cfg(feature = "headless")]
|
||||
pub const COPY: &str = "copy";
|
||||
}
|
||||
@ -506,9 +511,15 @@ pub fn create_default_render_graph() -> Result<RenderGraph, RenderGraphError> {
|
||||
let mut graph = RenderGraph::default();
|
||||
|
||||
let mut draw_graph = RenderGraph::default();
|
||||
// Draw nodes
|
||||
draw_graph.add_node(draw_graph::node::MAIN_PASS, MainPassNode::new());
|
||||
draw_graph.add_node(draw_graph::node::DEBUG_PASS, DebugPassNode::new());
|
||||
// Input node
|
||||
let input_node_id = draw_graph.set_input(vec![]);
|
||||
// Edges
|
||||
draw_graph.add_node_edge(input_node_id, draw_graph::node::MAIN_PASS)?;
|
||||
// TODO: Enable debug pass via runtime flag
|
||||
draw_graph.add_node_edge(draw_graph::node::MAIN_PASS, draw_graph::node::DEBUG_PASS)?;
|
||||
|
||||
graph.add_sub_graph(draw_graph::NAME, draw_graph);
|
||||
graph.add_node(main_graph::node::MAIN_PASS_DEPENDENCIES, EmptyNode);
|
||||
|
||||
@ -5,11 +5,11 @@ use crate::render::{
|
||||
eventually::Eventually::Initialized,
|
||||
render_phase::{PhaseItem, RenderCommand, RenderCommandResult},
|
||||
resource::{Globals, IndexEntry, TrackedRenderPass},
|
||||
tile_view_pattern::{TileInView, TileShape},
|
||||
tile_view_pattern::TileShape,
|
||||
RenderState, INDEX_FORMAT,
|
||||
};
|
||||
|
||||
impl PhaseItem for TileInView {
|
||||
impl PhaseItem for TileShape {
|
||||
type SortKey = ();
|
||||
|
||||
fn sort_key(&self) -> Self::SortKey {}
|
||||
@ -49,6 +49,19 @@ impl<P: PhaseItem> RenderCommand<P> for SetMaskPipeline {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SetDebugPipeline;
|
||||
impl<P: PhaseItem> RenderCommand<P> for SetDebugPipeline {
|
||||
fn render<'w>(
|
||||
state: &'w RenderState,
|
||||
_item: &P,
|
||||
pass: &mut TrackedRenderPass<'w>,
|
||||
) -> RenderCommandResult {
|
||||
let Initialized(pipeline) = &state.debug_pipeline else { return RenderCommandResult::Failure; };
|
||||
pass.set_render_pipeline(pipeline);
|
||||
RenderCommandResult::Success
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SetTilePipeline;
|
||||
impl<P: PhaseItem> RenderCommand<P> for SetTilePipeline {
|
||||
fn render<'w>(
|
||||
@ -63,25 +76,50 @@ impl<P: PhaseItem> RenderCommand<P> for SetTilePipeline {
|
||||
}
|
||||
|
||||
pub struct DrawMask;
|
||||
impl RenderCommand<TileInView> for DrawMask {
|
||||
impl RenderCommand<TileShape> for DrawMask {
|
||||
fn render<'w>(
|
||||
state: &'w RenderState,
|
||||
TileInView { shape, fallback }: &TileInView,
|
||||
source_shape: &TileShape,
|
||||
pass: &mut TrackedRenderPass<'w>,
|
||||
) -> RenderCommandResult {
|
||||
let Initialized(tile_view_pattern) = &state.tile_view_pattern else { return RenderCommandResult::Failure; };
|
||||
tracing::trace!("Drawing mask {}", &shape.coords);
|
||||
tracing::trace!("Drawing mask {}", &source_shape.coords());
|
||||
|
||||
let shape_to_render = fallback.as_ref().unwrap_or(shape);
|
||||
|
||||
let reference = tile_view_pattern.stencil_reference_value(&shape_to_render.coords) as u32;
|
||||
// Draw mask with stencil value of e.g. parent
|
||||
let reference = tile_view_pattern.stencil_reference_value_3d(&source_shape.coords()) as u32;
|
||||
|
||||
pass.set_stencil_reference(reference);
|
||||
pass.set_vertex_buffer(
|
||||
0,
|
||||
tile_view_pattern.buffer().slice(shape.buffer_range.clone()),
|
||||
// Mask is of the requested shape
|
||||
tile_view_pattern
|
||||
.buffer()
|
||||
.slice(source_shape.buffer_range()),
|
||||
);
|
||||
pass.draw(0..6, 0..1);
|
||||
const TILE_MASK_SHADER_VERTICES: u32 = 6;
|
||||
pass.draw(0..TILE_MASK_SHADER_VERTICES, 0..1);
|
||||
|
||||
RenderCommandResult::Success
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DrawDebugOutline;
|
||||
impl RenderCommand<TileShape> for DrawDebugOutline {
|
||||
fn render<'w>(
|
||||
state: &'w RenderState,
|
||||
source_shape: &TileShape,
|
||||
pass: &mut TrackedRenderPass<'w>,
|
||||
) -> RenderCommandResult {
|
||||
let Initialized(tile_view_pattern) = &state.tile_view_pattern else { return RenderCommandResult::Failure; };
|
||||
pass.set_vertex_buffer(
|
||||
0,
|
||||
tile_view_pattern
|
||||
.buffer()
|
||||
.slice(source_shape.buffer_range()),
|
||||
);
|
||||
const TILE_MASK_SHADER_VERTICES: u32 = 24;
|
||||
pass.draw(0..TILE_MASK_SHADER_VERTICES, 0..1);
|
||||
|
||||
RenderCommandResult::Success
|
||||
}
|
||||
}
|
||||
@ -96,7 +134,8 @@ impl RenderCommand<(IndexEntry, TileShape)> for DrawTile {
|
||||
let (Initialized(buffer_pool), Initialized(tile_view_pattern)) =
|
||||
(&state.buffer_pool, &state.tile_view_pattern) else { return RenderCommandResult::Failure; };
|
||||
|
||||
let reference = tile_view_pattern.stencil_reference_value(&shape.coords) as u32;
|
||||
// Uses stencil value of requested tile and the shape of the requested tile
|
||||
let reference = tile_view_pattern.stencil_reference_value_3d(&shape.coords()) as u32;
|
||||
|
||||
tracing::trace!(
|
||||
"Drawing layer {:?} at {}",
|
||||
@ -113,10 +152,7 @@ impl RenderCommand<(IndexEntry, TileShape)> for DrawTile {
|
||||
0,
|
||||
buffer_pool.vertices().slice(entry.vertices_buffer_range()),
|
||||
);
|
||||
pass.set_vertex_buffer(
|
||||
1,
|
||||
tile_view_pattern.buffer().slice(shape.buffer_range.clone()),
|
||||
);
|
||||
pass.set_vertex_buffer(1, tile_view_pattern.buffer().slice(shape.buffer_range()));
|
||||
pass.set_vertex_buffer(
|
||||
2,
|
||||
buffer_pool
|
||||
@ -137,3 +173,5 @@ impl RenderCommand<(IndexEntry, TileShape)> for DrawTile {
|
||||
pub type DrawTiles = (SetTilePipeline, SetViewBindGroup<0>, DrawTile);
|
||||
|
||||
pub type DrawMasks = (SetMaskPipeline, DrawMask);
|
||||
|
||||
pub type DrawDebugOutlines = (SetDebugPipeline, DrawDebugOutline);
|
||||
|
||||
@ -17,11 +17,12 @@ use crate::{
|
||||
tessellation::OverAlignedVertexBuffer,
|
||||
};
|
||||
|
||||
pub const VERTEX_SIZE: wgpu::BufferAddress = 1_000_000;
|
||||
pub const INDICES_SIZE: wgpu::BufferAddress = 1_000_000;
|
||||
// FIXME: Too low values can cause a back-and-forth between unloading and loading layers
|
||||
pub const VERTEX_SIZE: wgpu::BufferAddress = 10 * 1_000_000;
|
||||
pub const INDICES_SIZE: wgpu::BufferAddress = 10 * 1_000_000;
|
||||
|
||||
pub const FEATURE_METADATA_SIZE: wgpu::BufferAddress = 1024 * 1000;
|
||||
pub const LAYER_METADATA_SIZE: wgpu::BufferAddress = 1024;
|
||||
pub const FEATURE_METADATA_SIZE: wgpu::BufferAddress = 10 * 1024 * 1000;
|
||||
pub const LAYER_METADATA_SIZE: wgpu::BufferAddress = 10 * 1024;
|
||||
|
||||
/// This is inspired by the memory pool in Vulkan documented
|
||||
/// [here](https://gpuopen-librariesandsdks.github.io/VulkanMemoryAllocator/html/custom_memory_pools.html).
|
||||
@ -476,9 +477,14 @@ impl IndexEntry {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct RingIndexEntry {
|
||||
layers: VecDeque<IndexEntry>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct RingIndex {
|
||||
tree_index: BTreeMap<Quadkey, VecDeque<IndexEntry>>,
|
||||
tree_index: BTreeMap<Quadkey, RingIndexEntry>,
|
||||
linear_index: VecDeque<Quadkey>,
|
||||
}
|
||||
|
||||
@ -496,44 +502,33 @@ impl RingIndex {
|
||||
}
|
||||
|
||||
pub fn front(&self) -> Option<&IndexEntry> {
|
||||
self.linear_index
|
||||
.front()
|
||||
.and_then(|key| self.tree_index.get(key).and_then(|entries| entries.front()))
|
||||
self.linear_index.front().and_then(|key| {
|
||||
self.tree_index
|
||||
.get(key)
|
||||
.and_then(|entry| entry.layers.front())
|
||||
})
|
||||
}
|
||||
|
||||
pub fn back(&self) -> Option<&IndexEntry> {
|
||||
self.linear_index
|
||||
.back()
|
||||
.and_then(|key| self.tree_index.get(key).and_then(|entries| entries.back()))
|
||||
self.linear_index.back().and_then(|key| {
|
||||
self.tree_index
|
||||
.get(key)
|
||||
.and_then(|entry| entry.layers.back())
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_layers(&self, coords: &WorldTileCoords) -> Option<&VecDeque<IndexEntry>> {
|
||||
coords
|
||||
.build_quad_key()
|
||||
.and_then(|key| self.tree_index.get(&key))
|
||||
}
|
||||
|
||||
pub fn get_layers_fallback(&self, coords: &WorldTileCoords) -> Option<&VecDeque<IndexEntry>> {
|
||||
let mut current = *coords;
|
||||
loop {
|
||||
if let Some(entries) = self.get_layers(¤t) {
|
||||
return Some(entries);
|
||||
} else if let Some(parent) = current.get_parent() {
|
||||
current = parent
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
.map(|entry| &entry.layers)
|
||||
}
|
||||
|
||||
pub fn has_tile(&self, coords: &WorldTileCoords) -> bool {
|
||||
coords
|
||||
.build_quad_key()
|
||||
.and_then(|key| self.tree_index.get(&key))
|
||||
.is_some()
|
||||
self.get_layers(coords).is_some()
|
||||
}
|
||||
|
||||
pub fn get_tile_coords_fallback(&self, coords: &WorldTileCoords) -> Option<WorldTileCoords> {
|
||||
pub fn get_available_parent(&self, coords: &WorldTileCoords) -> Option<WorldTileCoords> {
|
||||
let mut current = *coords;
|
||||
loop {
|
||||
if self.has_tile(¤t) {
|
||||
@ -546,19 +541,45 @@ impl RingIndex {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_available_children(
|
||||
&self,
|
||||
coords: &WorldTileCoords,
|
||||
search_depth: usize,
|
||||
) -> Option<Vec<WorldTileCoords>> {
|
||||
let mut children = coords.get_children().to_vec();
|
||||
|
||||
let mut output = Vec::new();
|
||||
|
||||
for _ in 0..search_depth {
|
||||
let mut new_children = Vec::with_capacity(children.len() * 4);
|
||||
|
||||
for child in children {
|
||||
if self.has_tile(&child) {
|
||||
output.push(child);
|
||||
} else {
|
||||
new_children.extend(child.get_children())
|
||||
}
|
||||
}
|
||||
|
||||
children = new_children;
|
||||
}
|
||||
|
||||
Some(output)
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> impl Iterator<Item = impl Iterator<Item = &IndexEntry>> + '_ {
|
||||
self.linear_index
|
||||
.iter()
|
||||
.flat_map(|key| self.tree_index.get(key).map(|entries| entries.iter()))
|
||||
.flat_map(|key| self.tree_index.get(key).map(|entry| entry.layers.iter()))
|
||||
}
|
||||
|
||||
fn pop_front(&mut self) -> Option<IndexEntry> {
|
||||
if let Some(entries) = self
|
||||
if let Some(entry) = self
|
||||
.linear_index
|
||||
.pop_front()
|
||||
.and_then(|key| self.tree_index.get_mut(&key))
|
||||
{
|
||||
entries.pop_front()
|
||||
entry.layers.pop_front()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@ -568,10 +589,12 @@ impl RingIndex {
|
||||
if let Some(key) = entry.coords.build_quad_key() {
|
||||
match self.tree_index.entry(key) {
|
||||
btree_map::Entry::Vacant(index_entry) => {
|
||||
index_entry.insert(VecDeque::from([entry]));
|
||||
index_entry.insert(RingIndexEntry {
|
||||
layers: VecDeque::from([entry]),
|
||||
});
|
||||
}
|
||||
btree_map::Entry::Occupied(mut index_entry) => {
|
||||
index_entry.get_mut().push_back(entry);
|
||||
index_entry.get_mut().layers.push_back(entry);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -278,7 +278,7 @@ impl Surface {
|
||||
match &mut self.head {
|
||||
Head::Headed(window) => {
|
||||
if window.has_changed(&(self.size.width(), self.size.height())) {
|
||||
window.resize_and_configure(self.size.height(), self.size.width(), device);
|
||||
window.resize_and_configure(self.size.width(), self.size.height(), device);
|
||||
}
|
||||
}
|
||||
Head::Headless(_) => {}
|
||||
|
||||
@ -27,12 +27,17 @@ pub trait Shader {
|
||||
pub struct TileMaskShader {
|
||||
pub format: wgpu::TextureFormat,
|
||||
pub draw_colors: bool,
|
||||
pub debug_lines: bool,
|
||||
}
|
||||
|
||||
impl Shader for TileMaskShader {
|
||||
fn describe_vertex(&self) -> VertexState {
|
||||
VertexState {
|
||||
source: include_str!("tile_mask.vertex.wgsl"),
|
||||
source: if self.debug_lines {
|
||||
include_str!("tile_debug.vertex.wgsl")
|
||||
} else {
|
||||
include_str!("tile_mask.vertex.wgsl")
|
||||
},
|
||||
entry_point: "main",
|
||||
buffers: vec![VertexBufferLayout {
|
||||
array_stride: std::mem::size_of::<ShaderTileMetadata>() as u64,
|
||||
@ -66,7 +71,7 @@ impl Shader for TileMaskShader {
|
||||
|
||||
fn describe_fragment(&self) -> FragmentState {
|
||||
FragmentState {
|
||||
source: include_str!("tile_mask.fragment.wgsl"),
|
||||
source: include_str!("basic.fragment.wgsl"),
|
||||
entry_point: "main",
|
||||
targets: vec![Some(wgpu::ColorTargetState {
|
||||
format: self.format,
|
||||
@ -176,22 +181,10 @@ impl Shader for TileShader {
|
||||
|
||||
fn describe_fragment(&self) -> FragmentState {
|
||||
FragmentState {
|
||||
source: include_str!("tile.fragment.wgsl"),
|
||||
source: include_str!("basic.fragment.wgsl"),
|
||||
entry_point: "main",
|
||||
targets: vec![Some(wgpu::ColorTargetState {
|
||||
format: self.format,
|
||||
/*blend: Some(wgpu::BlendState {
|
||||
color: wgpu::BlendComponent {
|
||||
src_factor: wgpu::BlendFactor::SrcAlpha,
|
||||
dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
|
||||
operation: wgpu::BlendOperation::Add,
|
||||
},
|
||||
alpha: wgpu::BlendComponent {
|
||||
src_factor: wgpu::BlendFactor::SrcAlpha,
|
||||
dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
|
||||
operation: wgpu::BlendOperation::Add,
|
||||
},
|
||||
}),*/
|
||||
blend: None,
|
||||
write_mask: wgpu::ColorWrites::ALL,
|
||||
})],
|
||||
|
||||
75
maplibre/src/render/shaders/tile_debug.vertex.wgsl
Normal file
75
maplibre/src/render/shaders/tile_debug.vertex.wgsl
Normal file
@ -0,0 +1,75 @@
|
||||
struct VertexOutput {
|
||||
@location(0) v_color: vec4<f32>,
|
||||
@builtin(position) position: vec4<f32>,
|
||||
};
|
||||
|
||||
var<private> EXTENT: f32 = 4096.0;
|
||||
var<private> DEBUG_COLOR: vec4<f32> = vec4<f32>(1.0, 0.0, 0.0, 1.0);
|
||||
|
||||
@vertex
|
||||
fn main(
|
||||
@location(4) translate1: vec4<f32>,
|
||||
@location(5) translate2: vec4<f32>,
|
||||
@location(6) translate3: vec4<f32>,
|
||||
@location(7) translate4: vec4<f32>,
|
||||
@builtin(vertex_index) vertex_idx: u32,
|
||||
@builtin(instance_index) instance_idx: u32 // instance_index is used when we have multiple instances of the same "object"
|
||||
) -> VertexOutput {
|
||||
let z = 0.0;
|
||||
|
||||
let target_width = 1.0;
|
||||
let target_height = 1.0;
|
||||
|
||||
let WIDTH = EXTENT / 1024.0;
|
||||
|
||||
var VERTICES: array<vec3<f32>, 24> = array<vec3<f32>, 24>(
|
||||
// Debug lines vertices
|
||||
// left
|
||||
vec3<f32>(0.0, 0.0, z),
|
||||
vec3<f32>(0.0, EXTENT, z),
|
||||
vec3<f32>(WIDTH, EXTENT, z),
|
||||
|
||||
vec3<f32>(0.0, 0.0, z),
|
||||
vec3<f32>(WIDTH, EXTENT, z),
|
||||
vec3<f32>(WIDTH, 0.0, z),
|
||||
|
||||
// right
|
||||
vec3<f32>(EXTENT - WIDTH, 0.0, z),
|
||||
vec3<f32>(EXTENT - WIDTH, EXTENT, z),
|
||||
vec3<f32>(EXTENT, EXTENT, z),
|
||||
|
||||
vec3<f32>(EXTENT - WIDTH, 0.0, z),
|
||||
vec3<f32>(EXTENT, EXTENT, z),
|
||||
vec3<f32>(EXTENT, 0.0, z),
|
||||
|
||||
// top
|
||||
vec3<f32>(0.0, 0.0, z),
|
||||
vec3<f32>(0.0, WIDTH, z),
|
||||
vec3<f32>(EXTENT, WIDTH, z),
|
||||
|
||||
vec3<f32>(0.0, 0.0, z),
|
||||
vec3<f32>(EXTENT, WIDTH, z),
|
||||
vec3<f32>(EXTENT, 0.0, z),
|
||||
|
||||
// bottom
|
||||
vec3<f32>(0.0, EXTENT - WIDTH, z),
|
||||
vec3<f32>(0.0, EXTENT, z),
|
||||
vec3<f32>(EXTENT, EXTENT, z),
|
||||
|
||||
vec3<f32>(0.0, EXTENT - WIDTH, z),
|
||||
vec3<f32>(EXTENT, EXTENT, z),
|
||||
vec3<f32>(EXTENT, EXTENT - WIDTH, z)
|
||||
);
|
||||
|
||||
let a_position = VERTICES[vertex_idx];
|
||||
|
||||
let scaling: mat3x3<f32> = mat3x3<f32>(
|
||||
vec3<f32>(target_width, 0.0, 0.0),
|
||||
vec3<f32>(0.0, target_height, 0.0),
|
||||
vec3<f32>(0.0, 0.0, 1.0)
|
||||
);
|
||||
|
||||
var position = mat4x4<f32>(translate1, translate2, translate3, translate4) * vec4<f32>((scaling * a_position), 1.0);
|
||||
position.z = 1.0;
|
||||
return VertexOutput(DEBUG_COLOR, position);
|
||||
}
|
||||
@ -1,8 +0,0 @@
|
||||
struct Output {
|
||||
@location(0) out_color: vec4<f32>,
|
||||
};
|
||||
|
||||
@fragment
|
||||
fn main(@location(0) v_color: vec4<f32>) -> Output {
|
||||
return Output(v_color);
|
||||
}
|
||||
@ -1,20 +1,10 @@
|
||||
struct ShaderCamera {
|
||||
view_proj: mat4x4<f32>,
|
||||
view_position: vec4<f32>,
|
||||
};
|
||||
|
||||
struct ShaderGlobal {
|
||||
camera: ShaderCamera,
|
||||
};
|
||||
|
||||
@group(0) @binding(0) var<uniform> globals: ShaderGlobal;
|
||||
|
||||
struct VertexOutput {
|
||||
@location(0) v_color: vec4<f32>,
|
||||
@location(0) v_color: vec4<f32>,
|
||||
@builtin(position) position: vec4<f32>,
|
||||
};
|
||||
|
||||
let EXTENT = 4096.0;
|
||||
var<private> EXTENT: f32 = 4096.0;
|
||||
var<private> DEBUG_COLOR: vec4<f32> = vec4<f32>(1.0, 0.0, 0.0, 1.0);
|
||||
|
||||
@vertex
|
||||
fn main(
|
||||
@ -29,15 +19,15 @@ fn main(
|
||||
|
||||
let target_width = 1.0;
|
||||
let target_height = 1.0;
|
||||
let debug_color = vec4<f32>(1.0, 0.0, 0.0, 1.0);
|
||||
|
||||
var VERTICES: array<vec3<f32>, 6> = array<vec3<f32>, 6>(
|
||||
// Tile vertices
|
||||
vec3<f32>(0.0, 0.0, z),
|
||||
vec3<f32>(0.0, EXTENT, z),
|
||||
vec3<f32>(EXTENT, 0.0, z),
|
||||
vec3<f32>(EXTENT, 0.0, z),
|
||||
vec3<f32>(0.0, EXTENT, z),
|
||||
vec3<f32>(EXTENT, EXTENT, z)
|
||||
vec3<f32>(EXTENT, EXTENT, z),
|
||||
);
|
||||
let a_position = VERTICES[vertex_idx];
|
||||
|
||||
@ -51,5 +41,5 @@ fn main(
|
||||
// FIXME: how to fix z-fighting?
|
||||
position.z = 1.0;
|
||||
|
||||
return VertexOutput(debug_color, position);
|
||||
return VertexOutput(DEBUG_COLOR, position);
|
||||
}
|
||||
|
||||
@ -2,10 +2,7 @@
|
||||
|
||||
use crate::{
|
||||
context::MapContext,
|
||||
render::{
|
||||
eventually::Eventually::Initialized, resource::IndexEntry, tile_view_pattern::TileInView,
|
||||
RenderState, Renderer,
|
||||
},
|
||||
render::{eventually::Eventually::Initialized, resource::IndexEntry, RenderState, Renderer},
|
||||
schedule::Stage,
|
||||
};
|
||||
|
||||
@ -40,28 +37,28 @@ impl Stage for QueueStage {
|
||||
|
||||
let index = buffer_pool.index();
|
||||
|
||||
for tile_in_view in tile_view_pattern.iter() {
|
||||
let TileInView { shape, fallback } = &tile_in_view;
|
||||
let coords = shape.coords;
|
||||
for view_tile in tile_view_pattern.iter() {
|
||||
let coords = &view_tile.coords();
|
||||
tracing::trace!("Drawing tile at {coords}");
|
||||
|
||||
let shape_to_render = fallback.as_ref().unwrap_or(shape);
|
||||
// draw tile normal or the source e.g. parent or children
|
||||
view_tile.render(|source_shape| {
|
||||
// Draw masks for all source_shapes
|
||||
mask_phase.add(source_shape.clone());
|
||||
|
||||
// Draw mask
|
||||
mask_phase.add(tile_in_view.clone());
|
||||
|
||||
let Some(entries) = index.get_layers(&shape_to_render.coords) else {
|
||||
tracing::trace!("No layers found at {}", &shape_to_render.coords);
|
||||
continue;
|
||||
let Some(entries) = index.get_layers(&source_shape.coords()) else {
|
||||
tracing::trace!("No layers found at {}", &source_shape.coords());
|
||||
return;
|
||||
};
|
||||
|
||||
let mut layers_to_render: Vec<&IndexEntry> = Vec::from_iter(entries);
|
||||
layers_to_render.sort_by_key(|entry| entry.style_layer.index);
|
||||
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
|
||||
tile_phase.add((entry.clone(), shape_to_render.clone()))
|
||||
}
|
||||
for entry in layers_to_render {
|
||||
// Draw tile
|
||||
tile_phase.add((entry.clone(), source_shape.clone()))
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,7 +9,7 @@ use crate::{
|
||||
shaders,
|
||||
shaders::{Shader, ShaderTileMetadata},
|
||||
tile_pipeline::TilePipeline,
|
||||
tile_view_pattern::{TileViewPattern, DEFAULT_TILE_VIEW_SIZE},
|
||||
tile_view_pattern::{TileViewPattern, DEFAULT_TILE_VIEW_PATTERN_SIZE},
|
||||
Renderer,
|
||||
},
|
||||
schedule::Stage,
|
||||
@ -83,7 +83,7 @@ impl Stage for ResourceStage {
|
||||
let tile_view_buffer_desc = wgpu::BufferDescriptor {
|
||||
label: Some("tile view buffer"),
|
||||
size: size_of::<ShaderTileMetadata>() as wgpu::BufferAddress
|
||||
* DEFAULT_TILE_VIEW_SIZE,
|
||||
* DEFAULT_TILE_VIEW_PATTERN_SIZE,
|
||||
usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
|
||||
mapped_at_creation: false,
|
||||
};
|
||||
@ -104,9 +104,11 @@ impl Stage for ResourceStage {
|
||||
tile_shader.describe_vertex(),
|
||||
tile_shader.describe_fragment(),
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
)
|
||||
.describe_render_pipeline()
|
||||
.initialize(device);
|
||||
@ -122,6 +124,7 @@ impl Stage for ResourceStage {
|
||||
let mask_shader = shaders::TileMaskShader {
|
||||
format: surface.surface_format(),
|
||||
draw_colors: false,
|
||||
debug_lines: false,
|
||||
};
|
||||
|
||||
TilePipeline::new(
|
||||
@ -130,6 +133,30 @@ impl Stage for ResourceStage {
|
||||
mask_shader.describe_fragment(),
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
)
|
||||
.describe_render_pipeline()
|
||||
.initialize(device)
|
||||
});
|
||||
|
||||
state.debug_pipeline.initialize(|| {
|
||||
let mask_shader = shaders::TileMaskShader {
|
||||
format: surface.surface_format(),
|
||||
draw_colors: true,
|
||||
debug_lines: true,
|
||||
};
|
||||
|
||||
TilePipeline::new(
|
||||
*settings,
|
||||
mask_shader.describe_vertex(),
|
||||
mask_shader.describe_fragment(),
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
)
|
||||
|
||||
@ -133,7 +133,7 @@ impl UploadStage {
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
buffer_pool.update_feature_metadata(&queue, entry, &feature_metadata);
|
||||
buffer_pool.update_feature_metadata(queue, entry, &feature_metadata);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -163,15 +163,16 @@ impl UploadStage {
|
||||
view_region: &ViewRegion,
|
||||
) {
|
||||
let Initialized(buffer_pool) = buffer_pool else { return; };
|
||||
|
||||
// Upload all tessellated layers which are in view
|
||||
for coords in view_region.iter() {
|
||||
let Some(available_layers) =
|
||||
tile_repository.iter_loaded_layers_at(&buffer_pool, &coords) else { continue; };
|
||||
tile_repository.iter_loaded_layers_at(buffer_pool, &coords) else { continue; };
|
||||
|
||||
for style_layer in &style.layers {
|
||||
let source_layer = style_layer.source_layer.as_ref().unwrap(); // TODO: Remove unwrap
|
||||
|
||||
let Some(message) = available_layers
|
||||
let Some(stored_layer) = available_layers
|
||||
.iter()
|
||||
.find(|layer| source_layer.as_str() == layer.layer_name()) else { continue; };
|
||||
|
||||
@ -181,7 +182,7 @@ impl UploadStage {
|
||||
.and_then(|paint| paint.get_color())
|
||||
.map(|color| color.into());
|
||||
|
||||
match message {
|
||||
match stored_layer {
|
||||
StoredLayer::UnavailableLayer { .. } => {}
|
||||
StoredLayer::TessellatedLayer {
|
||||
coords,
|
||||
@ -190,7 +191,7 @@ impl UploadStage {
|
||||
..
|
||||
} => {
|
||||
let allocate_feature_metadata =
|
||||
tracing::span!(tracing::Level::TRACE, "allocate_layer_geometry");
|
||||
tracing::span!(tracing::Level::TRACE, "allocate_feature_metadata");
|
||||
|
||||
let guard = allocate_feature_metadata.enter();
|
||||
let feature_metadata = (0..feature_indices.len()) // FIXME: Iterate over actual featrues
|
||||
|
||||
@ -13,9 +13,14 @@ use crate::{
|
||||
|
||||
pub struct TilePipeline {
|
||||
bind_globals: bool,
|
||||
/// Is the depth stencil used?
|
||||
depth_stencil_enabled: bool,
|
||||
/// This pipeline updates the stenctil
|
||||
update_stencil: bool,
|
||||
/// Force a write and ignore stencil
|
||||
debug_stencil: bool,
|
||||
wireframe: bool,
|
||||
multisampling: bool,
|
||||
settings: RendererSettings,
|
||||
|
||||
vertex_state: VertexState,
|
||||
@ -28,15 +33,19 @@ impl TilePipeline {
|
||||
vertex_state: VertexState,
|
||||
fragment_state: FragmentState,
|
||||
bind_globals: bool,
|
||||
depth_stencil_enabled: bool,
|
||||
update_stencil: bool,
|
||||
debug_stencil: bool,
|
||||
wireframe: bool,
|
||||
multisampling: bool,
|
||||
) -> Self {
|
||||
TilePipeline {
|
||||
bind_globals,
|
||||
depth_stencil_enabled,
|
||||
update_stencil,
|
||||
debug_stencil,
|
||||
wireframe,
|
||||
multisampling,
|
||||
settings,
|
||||
vertex_state,
|
||||
fragment_state,
|
||||
@ -104,20 +113,28 @@ impl RenderPipeline for TilePipeline {
|
||||
conservative: false,
|
||||
unclipped_depth: false,
|
||||
},
|
||||
depth_stencil: Some(wgpu::DepthStencilState {
|
||||
format: self.settings.depth_texture_format,
|
||||
depth_write_enabled: !self.update_stencil,
|
||||
depth_compare: wgpu::CompareFunction::Greater,
|
||||
stencil: wgpu::StencilState {
|
||||
front: stencil_state,
|
||||
back: stencil_state,
|
||||
read_mask: 0xff, // Applied to stencil values being read from the stencil buffer
|
||||
write_mask: 0xff, // Applied to fragment stencil values before being written to the stencil buffer
|
||||
},
|
||||
bias: wgpu::DepthBiasState::default(),
|
||||
}),
|
||||
depth_stencil: if !self.depth_stencil_enabled {
|
||||
None
|
||||
} else {
|
||||
Some(wgpu::DepthStencilState {
|
||||
format: self.settings.depth_texture_format,
|
||||
depth_write_enabled: !self.update_stencil,
|
||||
depth_compare: wgpu::CompareFunction::Greater,
|
||||
stencil: wgpu::StencilState {
|
||||
front: stencil_state,
|
||||
back: stencil_state,
|
||||
read_mask: 0xff, // Applied to stencil values being read from the stencil buffer
|
||||
write_mask: 0xff, // Applied to fragment stencil values before being written to the stencil buffer
|
||||
},
|
||||
bias: wgpu::DepthBiasState::default(),
|
||||
})
|
||||
},
|
||||
multisample: wgpu::MultisampleState {
|
||||
count: self.settings.msaa.samples,
|
||||
count: if self.multisampling {
|
||||
self.settings.msaa.samples
|
||||
} else {
|
||||
1
|
||||
},
|
||||
mask: !0,
|
||||
alpha_to_coverage_enabled: false,
|
||||
},
|
||||
|
||||
@ -15,42 +15,91 @@ use crate::{
|
||||
tessellation::IndexDataType,
|
||||
};
|
||||
|
||||
pub const DEFAULT_TILE_VIEW_SIZE: wgpu::BufferAddress = 32 * 4;
|
||||
|
||||
/// 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 const DEFAULT_TILE_VIEW_PATTERN_SIZE: wgpu::BufferAddress = 32 * 4;
|
||||
pub const CHILDREN_SEARCH_DEPTH: usize = 4;
|
||||
|
||||
/// Defines the exact location where a specific tile on the map is rendered. It defines the shape
|
||||
/// of the tile with its location for the current zoom factor.
|
||||
#[derive(Clone)]
|
||||
pub struct TileShape {
|
||||
pub zoom_factor: f64,
|
||||
coords: WorldTileCoords,
|
||||
|
||||
pub coords: WorldTileCoords,
|
||||
// TODO: optimization, `zoom_factor` and `transform` are no longer required if `buffer_range` is Some()
|
||||
zoom_factor: f64,
|
||||
transform: Matrix4<f64>,
|
||||
|
||||
pub transform: Matrix4<f64>,
|
||||
pub buffer_range: Range<wgpu::BufferAddress>,
|
||||
buffer_range: Option<Range<wgpu::BufferAddress>>,
|
||||
}
|
||||
|
||||
impl TileShape {
|
||||
fn new(coords: WorldTileCoords, zoom: Zoom, index: u64) -> Self {
|
||||
const STRIDE: u64 = size_of::<ShaderTileMetadata>() as u64;
|
||||
fn new(coords: WorldTileCoords, zoom: Zoom) -> Self {
|
||||
Self {
|
||||
coords,
|
||||
zoom_factor: zoom.scale_to_tile(&coords),
|
||||
transform: coords.transform_for_zoom(zoom),
|
||||
buffer_range: index * STRIDE..(index + 1) * STRIDE,
|
||||
buffer_range: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn set_buffer_range(&mut self, index: u64) {
|
||||
const STRIDE: u64 = size_of::<ShaderTileMetadata>() as u64;
|
||||
self.buffer_range = Some(index * STRIDE..(index + 1) * STRIDE);
|
||||
}
|
||||
|
||||
pub fn buffer_range(&self) -> Range<wgpu::BufferAddress> {
|
||||
self.buffer_range.as_ref().unwrap().clone()
|
||||
}
|
||||
|
||||
pub fn coords(&self) -> WorldTileCoords {
|
||||
self.coords
|
||||
}
|
||||
}
|
||||
|
||||
/// This defines the source tile shaped from which the content for the `target` is taken.
|
||||
/// For example if the target is `(0, 0, 1)` (of [`ViewTile`]) , we might use
|
||||
/// `SourceShapes::Parent((0, 0, 0))` as source.
|
||||
/// Similarly if we have the target `(0, 0, 0)` we might use
|
||||
/// `SourceShapes::Children((0, 0, 1), (0, 1, 1), (1, 0, 1), (1, 1, 1))` as sources.
|
||||
#[derive(Clone)]
|
||||
pub struct TileInView {
|
||||
pub shape: TileShape,
|
||||
pub enum SourceShapes {
|
||||
/// Parent tile is the source. We construct the `target` from parts of a parent.
|
||||
Parent(TileShape),
|
||||
/// Children are the source. We construct the `target` from multiple children.
|
||||
Children(Vec<TileShape>),
|
||||
/// Source and target are equal, so no need to differentiate. We render the `source` shape
|
||||
/// exactly at the `target`.
|
||||
SourceEqTarget(TileShape),
|
||||
/// No data available so nothing to render
|
||||
None,
|
||||
}
|
||||
|
||||
pub fallback: Option<TileShape>,
|
||||
/// Defines the `target` tile and its `source` from which data tile data comes.
|
||||
#[derive(Clone)]
|
||||
pub struct ViewTile {
|
||||
target: WorldTileCoords,
|
||||
source: SourceShapes,
|
||||
}
|
||||
|
||||
impl ViewTile {
|
||||
pub fn coords(&self) -> WorldTileCoords {
|
||||
self.target
|
||||
}
|
||||
|
||||
pub fn render<F>(&self, mut callback: F)
|
||||
where
|
||||
F: FnMut(&TileShape),
|
||||
{
|
||||
match &self.source {
|
||||
SourceShapes::Parent(source_shape) => callback(source_shape),
|
||||
SourceShapes::Children(source_shapes) => {
|
||||
for shape in source_shapes {
|
||||
callback(shape)
|
||||
}
|
||||
}
|
||||
SourceShapes::SourceEqTarget(source_shape) => callback(source_shape),
|
||||
SourceShapes::None => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -67,6 +116,13 @@ impl<B> BackingBuffer<B> {
|
||||
}
|
||||
}
|
||||
|
||||
/// The tile mask pattern assigns each tile a value which can be used for stencil testing.
|
||||
pub struct TileViewPattern<Q, B> {
|
||||
in_view: Vec<ViewTile>,
|
||||
buffer: BackingBuffer<B>,
|
||||
phantom_q: PhantomData<Q>,
|
||||
}
|
||||
|
||||
impl<Q: Queue<B>, B> TileViewPattern<Q, B> {
|
||||
pub fn new(buffer: BackingBufferDescriptor<B>) -> Self {
|
||||
Self {
|
||||
@ -92,8 +148,6 @@ impl<Q: Queue<B>, B> TileViewPattern<Q, B> {
|
||||
) {
|
||||
self.in_view.clear();
|
||||
|
||||
let mut index = 0;
|
||||
|
||||
let pool_index = buffer_pool.index();
|
||||
|
||||
for coords in view_region.iter() {
|
||||
@ -101,34 +155,39 @@ impl<Q: Queue<B>, B> TileViewPattern<Q, B> {
|
||||
continue;
|
||||
}
|
||||
|
||||
let shape = TileShape::new(coords, zoom, index);
|
||||
let source_shapes = {
|
||||
if pool_index.has_tile(&coords) {
|
||||
SourceShapes::SourceEqTarget(TileShape::new(coords, zoom))
|
||||
} else if let Some(parent_coords) = pool_index.get_available_parent(&coords) {
|
||||
log::info!("Could not find data at {coords}. Falling back to {parent_coords}");
|
||||
|
||||
index += 1;
|
||||
SourceShapes::Parent(TileShape::new(parent_coords, zoom))
|
||||
} else if let Some(children_coords) =
|
||||
pool_index.get_available_children(&coords, CHILDREN_SEARCH_DEPTH)
|
||||
{
|
||||
log::info!(
|
||||
"Could not find data at {coords}. Falling back children: {children_coords:?}"
|
||||
);
|
||||
|
||||
let fallback = {
|
||||
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::new(fallback_coords, zoom, index);
|
||||
|
||||
index += 1;
|
||||
Some(shape)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
SourceShapes::Children(
|
||||
children_coords
|
||||
.iter()
|
||||
.map(|child_coord| TileShape::new(*child_coord, zoom))
|
||||
.collect(),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
SourceShapes::None
|
||||
}
|
||||
};
|
||||
|
||||
self.in_view.push(TileInView { shape, fallback });
|
||||
self.in_view.push(ViewTile {
|
||||
target: coords,
|
||||
source: source_shapes,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> impl Iterator<Item = &TileInView> + '_ {
|
||||
pub fn iter(&self) -> impl Iterator<Item = &ViewTile> + '_ {
|
||||
self.in_view.iter()
|
||||
}
|
||||
|
||||
@ -137,30 +196,34 @@ impl<Q: Queue<B>, B> TileViewPattern<Q, B> {
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub fn upload_pattern(&self, queue: &Q, view_proj: &ViewProjection) {
|
||||
pub fn upload_pattern(&mut self, queue: &Q, view_proj: &ViewProjection) {
|
||||
let mut buffer = Vec::with_capacity(self.in_view.len());
|
||||
|
||||
for tile in &self.in_view {
|
||||
let mut add_to_buffer = |shape: &mut TileShape| {
|
||||
shape.set_buffer_range(buffer.len() as u64);
|
||||
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)
|
||||
.to_model_view_projection(shape.transform)
|
||||
.downcast()
|
||||
.into(),
|
||||
zoom_factor: tile.shape.zoom_factor as f32,
|
||||
zoom_factor: 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,
|
||||
});
|
||||
for view_tile in &mut self.in_view {
|
||||
match &mut view_tile.source {
|
||||
SourceShapes::Parent(source_shape) => {
|
||||
add_to_buffer(source_shape);
|
||||
}
|
||||
SourceShapes::Children(source_shapes) => {
|
||||
for source_shape in source_shapes {
|
||||
add_to_buffer(source_shape);
|
||||
}
|
||||
}
|
||||
SourceShapes::SourceEqTarget(source_shape) => add_to_buffer(source_shape),
|
||||
SourceShapes::None => {}
|
||||
}
|
||||
}
|
||||
|
||||
@ -173,7 +236,9 @@ impl<Q: Queue<B>, B> TileViewPattern<Q, B> {
|
||||
queue.write_buffer(&self.buffer.inner, 0, raw_buffer);
|
||||
}
|
||||
|
||||
pub fn stencil_reference_value(&self, world_coords: &WorldTileCoords) -> u8 {
|
||||
/// 2D version of [`TileViewPattern::stencil_reference_value_3d`]. This is kept for reference.
|
||||
/// For the 2D case we do not take into account the Z value, so only 4 cases exist.
|
||||
pub fn stencil_reference_value_2d(&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,
|
||||
@ -182,4 +247,18 @@ impl<Q: Queue<B>, B> TileViewPattern<Q, B> {
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns unique stencil reference values for WorldTileCoords which are 3D.
|
||||
/// Tiles from arbitrary `z` can lie next to each other, because we mix tiles from
|
||||
/// different levels based on availability.
|
||||
pub fn stencil_reference_value_3d(&self, world_coords: &WorldTileCoords) -> u8 {
|
||||
const CASES: u8 = 4;
|
||||
match (world_coords.x, world_coords.y, u8::from(world_coords.z)) {
|
||||
(x, y, z) if x % 2 == 0 && y % 2 == 0 => 0 + z * CASES,
|
||||
(x, y, z) if x % 2 == 0 && y % 2 != 0 => 1 + z * CASES,
|
||||
(x, y, z) if x % 2 != 0 && y % 2 == 0 => 2 + z * CASES,
|
||||
(x, y, z) if x % 2 != 0 && y % 2 != 0 => 3 + z * CASES,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -39,7 +39,7 @@ pub struct HeadedPipelineProcessor<T: Transferables, HC: HttpClient, C: Context<
|
||||
phantom_hc: PhantomData<HC>,
|
||||
}
|
||||
|
||||
impl<'c, T: Transferables, HC: HttpClient, C: Context<T, HC>> PipelineProcessor
|
||||
impl<T: Transferables, HC: HttpClient, C: Context<T, HC>> PipelineProcessor
|
||||
for HeadedPipelineProcessor<T, HC, C>
|
||||
{
|
||||
fn tile_finished(&mut self, coords: &WorldTileCoords) -> Result<(), PipelineError> {
|
||||
|
||||
@ -42,6 +42,7 @@ impl<E: Environment> Stage for PopulateTileStore<E> {
|
||||
// available this might cause frame drops.
|
||||
while let Some(result) = self.kernel.apc().receive() {
|
||||
match result {
|
||||
// TODO: deduplicate
|
||||
Message::TileTessellated(message) => {
|
||||
let coords = message.coords();
|
||||
tracing::event!(tracing::Level::ERROR, %coords, "tile request done: {}", &coords);
|
||||
@ -49,9 +50,8 @@ impl<E: Environment> Stage for PopulateTileStore<E> {
|
||||
tracing::trace!("Tile at {} finished loading", coords);
|
||||
log::warn!("Tile at {} finished loading", coords);
|
||||
|
||||
tile_repository.mark_tile_succeeded(&coords);
|
||||
tile_repository.mark_tile_succeeded(&coords).unwrap(); // TODO: unwrap
|
||||
}
|
||||
// FIXME: deduplicate
|
||||
Message::LayerUnavailable(message) => {
|
||||
let layer: StoredLayer = message.to_stored_layer();
|
||||
|
||||
|
||||
@ -142,13 +142,8 @@ impl<E: Environment> RequestStage<E> {
|
||||
coords: WorldTileCoords,
|
||||
layers: &HashSet<String>,
|
||||
) {
|
||||
/* TODO: is this still required?
|
||||
if !tile_repository.is_layers_missing(coords, layers) {
|
||||
return Ok(false);
|
||||
}*/
|
||||
|
||||
if tile_repository.has_tile(&coords) {
|
||||
tile_repository.create_tile(coords);
|
||||
if tile_repository.is_tile_pending_or_done(&coords) {
|
||||
tile_repository.mark_tile_pending(coords).unwrap(); // TODO: Remove unwrap
|
||||
|
||||
tracing::event!(tracing::Level::ERROR, %coords, "tile request started: {}", &coords);
|
||||
|
||||
|
||||
209
web/demo/package-lock.json
generated
209
web/demo/package-lock.json
generated
@ -26,7 +26,6 @@
|
||||
}
|
||||
},
|
||||
"../lib": {
|
||||
"name": "maplibre-rs",
|
||||
"version": "0.0.1",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@ -262,13 +261,13 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/express": {
|
||||
"version": "4.17.14",
|
||||
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.14.tgz",
|
||||
"integrity": "sha512-TEbt+vaPFQ+xpxFLFssxUDXj5cWCxZJjIcB7Yg0k0GMHGtgtQgpvx/MUQUeAkNbA9AAGrwkAsoeItdTgS7FMyg==",
|
||||
"version": "4.17.15",
|
||||
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.15.tgz",
|
||||
"integrity": "sha512-Yv0k4bXGOH+8a+7bELd2PqHQsuiANB+A8a4gnQrkRWzrkKlb6KHaVvyXhqs04sVW/OWlbPyYxRgYlIXLfrufMQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/body-parser": "*",
|
||||
"@types/express-serve-static-core": "^4.17.18",
|
||||
"@types/express-serve-static-core": "^4.17.31",
|
||||
"@types/qs": "*",
|
||||
"@types/serve-static": "*"
|
||||
}
|
||||
@ -312,9 +311,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "18.11.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.9.tgz",
|
||||
"integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==",
|
||||
"version": "18.11.15",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.15.tgz",
|
||||
"integrity": "sha512-VkhBbVo2+2oozlkdHXLrb3zjsRkpdnaU2bXmX8Wgle3PUi569eLRaHGlgETQHR7lLL1w7GiG3h9SnePhxNDecw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/qs": {
|
||||
@ -621,9 +620,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/ajv": {
|
||||
"version": "8.11.0",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz",
|
||||
"integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==",
|
||||
"version": "8.11.2",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.2.tgz",
|
||||
"integrity": "sha512-E4bfmKAhGiSTvMfL1Myyycaub+cUEU2/IvpylXkUu7CHBkBj1f/ikdzbD7YQ6FKUbixDxeYvB/xY4fvyroDlQg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
@ -702,9 +701,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/anymatch": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
|
||||
"integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
|
||||
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"normalize-path": "^3.0.0",
|
||||
@ -896,9 +895,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001430",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001430.tgz",
|
||||
"integrity": "sha512-IB1BXTZKPDVPM7cnV4iaKaHxckvdr/3xtctB3f7Hmenx3qYBhGtTZ//7EllK66aKXW98Lx0+7Yr0kxBtIt3tzg==",
|
||||
"version": "1.0.30001439",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001439.tgz",
|
||||
"integrity": "sha512-1MgUzEkoMO6gKfXflStpYgZDlFM7M/ck/bgfVCACO5vnAf0fXoNVHdWtqGU+MYca+4bL9Z5bpOVmR33cWW9G2A==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@ -1398,9 +1397,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/enhanced-resolve": {
|
||||
"version": "5.10.0",
|
||||
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz",
|
||||
"integrity": "sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ==",
|
||||
"version": "5.12.0",
|
||||
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz",
|
||||
"integrity": "sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"graceful-fs": "^4.2.4",
|
||||
@ -1640,9 +1639,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/fastq": {
|
||||
"version": "1.13.0",
|
||||
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz",
|
||||
"integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==",
|
||||
"version": "1.14.0",
|
||||
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.14.0.tgz",
|
||||
"integrity": "sha512-eR2D+V9/ExcbF9ls441yIuN6TI2ED1Y2ZcA5BmMtJsOkWOFRJQ0Jt0g1UwqXJJVAb+V+umH5Dfr8oh4EVP7VVg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"reusify": "^1.0.4"
|
||||
@ -1907,9 +1906,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/globby": {
|
||||
"version": "13.1.2",
|
||||
"resolved": "https://registry.npmjs.org/globby/-/globby-13.1.2.tgz",
|
||||
"integrity": "sha512-LKSDZXToac40u8Q1PQtZihbNdTYSNMuWe+K5l+oa6KgDzSvVrHXlJy40hUP522RjAIoNLJYBJi7ow+rbFpIhHQ==",
|
||||
"version": "13.1.3",
|
||||
"resolved": "https://registry.npmjs.org/globby/-/globby-13.1.3.tgz",
|
||||
"integrity": "sha512-8krCNHXvlCgHDpegPzleMq07yMYTO2sXKASmZmquEYWEmCx6J5UTRbp5RwMJkTJGtcQ44YpiUYUiN0b9mzy8Bw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"dir-glob": "^3.0.1",
|
||||
@ -2178,9 +2177,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/ignore": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz",
|
||||
"integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==",
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.1.tgz",
|
||||
"integrity": "sha512-d2qQLzTJ9WxQftPAuEQpSPmKqzxePjzVbpAVv62AQ64NTL+wR4JkrVqR/LqFsFEUsHDAiId52mJteHDFuDkElA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 4"
|
||||
@ -2449,9 +2448,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/loader-utils": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.3.tgz",
|
||||
"integrity": "sha512-THWqIsn8QRnvLl0shHYVBN9syumU8pYWEHPTmkiVGd+7K5eFNVSY6AJhRvgGF70gg1Dz+l/k8WicvFCxdEs60A==",
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz",
|
||||
"integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"big.js": "^5.2.2",
|
||||
@ -2521,9 +2520,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/memfs": {
|
||||
"version": "3.4.10",
|
||||
"resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.10.tgz",
|
||||
"integrity": "sha512-0bCUP+L79P4am30yP1msPzApwuMQG23TjwlwdHeEV5MxioDR1a0AgB0T9FfggU52eJuDCq8WVwb5ekznFyWiTQ==",
|
||||
"version": "3.4.12",
|
||||
"resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.12.tgz",
|
||||
"integrity": "sha512-BcjuQn6vfqP+k100e0E9m61Hyqa//Brp+I3f0OBmN0ATHlFA8vx3Lt8z57R3u2bPqe3WGDBC+nF72fTH7isyEw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"fs-monkey": "^1.0.3"
|
||||
@ -2689,9 +2688,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/node-releases": {
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz",
|
||||
"integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==",
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.7.tgz",
|
||||
"integrity": "sha512-EJ3rzxL9pTWPjk5arA0s0dgXpnyiAbJDE6wHT62g7VsgrgQgmmZ+Ru++M1BFofncWja+Pnn3rEr3fieRySAdKQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/normalize-path": {
|
||||
@ -3699,9 +3698,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/terser": {
|
||||
"version": "5.15.1",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.15.1.tgz",
|
||||
"integrity": "sha512-K1faMUvpm/FBxjBXud0LWVAGxmvoPbZbfTCYbSgaaYQaIXI3/TdI7a7ZGA73Zrou6Q8Zmz3oeUTsp/dj+ag2Xw==",
|
||||
"version": "5.16.1",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.16.1.tgz",
|
||||
"integrity": "sha512-xvQfyfA1ayT0qdK47zskQgRZeWLoOQ8JQ6mIgRGVNwZKdQMU+5FkCBjmv4QjcrTzyZquRw2FVtlJSRUmMKQslw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/source-map": "^0.3.2",
|
||||
@ -3833,9 +3832,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/ts-loader": {
|
||||
"version": "9.4.1",
|
||||
"resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.4.1.tgz",
|
||||
"integrity": "sha512-384TYAqGs70rn9F0VBnh6BPTfhga7yFNdC5gXbQpDrBj9/KsT4iRkGqKXhziofHOlE2j6YEaiTYVGKKvPhGWvw==",
|
||||
"version": "9.4.2",
|
||||
"resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.4.2.tgz",
|
||||
"integrity": "sha512-OmlC4WVmFv5I0PpaxYb+qGeGOdm5giHU7HwDDUjw59emP2UYMHy9fFSDcYgSNoH8sXcj4hGCSEhlDZ9ULeDraA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"chalk": "^4.1.0",
|
||||
@ -3914,9 +3913,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "4.8.4",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz",
|
||||
"integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==",
|
||||
"version": "4.9.4",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz",
|
||||
"integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
@ -4038,9 +4037,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/webpack": {
|
||||
"version": "5.74.0",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.74.0.tgz",
|
||||
"integrity": "sha512-A2InDwnhhGN4LYctJj6M1JEaGL7Luj6LOmyBHjcI8529cm5p6VXiTIW2sn6ffvEAKmveLzvu4jrihwXtPojlAA==",
|
||||
"version": "5.75.0",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.75.0.tgz",
|
||||
"integrity": "sha512-piaIaoVJlqMsPtX/+3KTTO6jfvrSYgauFVdt8cr9LTHKmcq/AMd4mhzsiP7ZF/PGRNPGA8336jldh9l2Kt2ogQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/eslint-scope": "^3.7.3",
|
||||
@ -4340,9 +4339,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "8.10.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.10.0.tgz",
|
||||
"integrity": "sha512-+s49uSmZpvtAsd2h37vIPy1RBusaLawVe8of+GyEPsaJTCMpj/2v8NpeK1SHXjBlQ95lQTmQofOJnFiLoaN3yw==",
|
||||
"version": "8.11.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz",
|
||||
"integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
@ -4574,13 +4573,13 @@
|
||||
"dev": true
|
||||
},
|
||||
"@types/express": {
|
||||
"version": "4.17.14",
|
||||
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.14.tgz",
|
||||
"integrity": "sha512-TEbt+vaPFQ+xpxFLFssxUDXj5cWCxZJjIcB7Yg0k0GMHGtgtQgpvx/MUQUeAkNbA9AAGrwkAsoeItdTgS7FMyg==",
|
||||
"version": "4.17.15",
|
||||
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.15.tgz",
|
||||
"integrity": "sha512-Yv0k4bXGOH+8a+7bELd2PqHQsuiANB+A8a4gnQrkRWzrkKlb6KHaVvyXhqs04sVW/OWlbPyYxRgYlIXLfrufMQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/body-parser": "*",
|
||||
"@types/express-serve-static-core": "^4.17.18",
|
||||
"@types/express-serve-static-core": "^4.17.31",
|
||||
"@types/qs": "*",
|
||||
"@types/serve-static": "*"
|
||||
}
|
||||
@ -4624,9 +4623,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "18.11.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.9.tgz",
|
||||
"integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==",
|
||||
"version": "18.11.15",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.15.tgz",
|
||||
"integrity": "sha512-VkhBbVo2+2oozlkdHXLrb3zjsRkpdnaU2bXmX8Wgle3PUi569eLRaHGlgETQHR7lLL1w7GiG3h9SnePhxNDecw==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/qs": {
|
||||
@ -4906,9 +4905,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"ajv": {
|
||||
"version": "8.11.0",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz",
|
||||
"integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==",
|
||||
"version": "8.11.2",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.2.tgz",
|
||||
"integrity": "sha512-E4bfmKAhGiSTvMfL1Myyycaub+cUEU2/IvpylXkUu7CHBkBj1f/ikdzbD7YQ6FKUbixDxeYvB/xY4fvyroDlQg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
@ -4957,9 +4956,9 @@
|
||||
}
|
||||
},
|
||||
"anymatch": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
|
||||
"integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
|
||||
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"normalize-path": "^3.0.0",
|
||||
@ -5112,9 +5111,9 @@
|
||||
}
|
||||
},
|
||||
"caniuse-lite": {
|
||||
"version": "1.0.30001430",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001430.tgz",
|
||||
"integrity": "sha512-IB1BXTZKPDVPM7cnV4iaKaHxckvdr/3xtctB3f7Hmenx3qYBhGtTZ//7EllK66aKXW98Lx0+7Yr0kxBtIt3tzg==",
|
||||
"version": "1.0.30001439",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001439.tgz",
|
||||
"integrity": "sha512-1MgUzEkoMO6gKfXflStpYgZDlFM7M/ck/bgfVCACO5vnAf0fXoNVHdWtqGU+MYca+4bL9Z5bpOVmR33cWW9G2A==",
|
||||
"dev": true
|
||||
},
|
||||
"chalk": {
|
||||
@ -5487,9 +5486,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"enhanced-resolve": {
|
||||
"version": "5.10.0",
|
||||
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz",
|
||||
"integrity": "sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ==",
|
||||
"version": "5.12.0",
|
||||
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz",
|
||||
"integrity": "sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"graceful-fs": "^4.2.4",
|
||||
@ -5684,9 +5683,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"fastq": {
|
||||
"version": "1.13.0",
|
||||
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz",
|
||||
"integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==",
|
||||
"version": "1.14.0",
|
||||
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.14.0.tgz",
|
||||
"integrity": "sha512-eR2D+V9/ExcbF9ls441yIuN6TI2ED1Y2ZcA5BmMtJsOkWOFRJQ0Jt0g1UwqXJJVAb+V+umH5Dfr8oh4EVP7VVg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"reusify": "^1.0.4"
|
||||
@ -5873,9 +5872,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"globby": {
|
||||
"version": "13.1.2",
|
||||
"resolved": "https://registry.npmjs.org/globby/-/globby-13.1.2.tgz",
|
||||
"integrity": "sha512-LKSDZXToac40u8Q1PQtZihbNdTYSNMuWe+K5l+oa6KgDzSvVrHXlJy40hUP522RjAIoNLJYBJi7ow+rbFpIhHQ==",
|
||||
"version": "13.1.3",
|
||||
"resolved": "https://registry.npmjs.org/globby/-/globby-13.1.3.tgz",
|
||||
"integrity": "sha512-8krCNHXvlCgHDpegPzleMq07yMYTO2sXKASmZmquEYWEmCx6J5UTRbp5RwMJkTJGtcQ44YpiUYUiN0b9mzy8Bw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"dir-glob": "^3.0.1",
|
||||
@ -6079,9 +6078,9 @@
|
||||
}
|
||||
},
|
||||
"ignore": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz",
|
||||
"integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==",
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.1.tgz",
|
||||
"integrity": "sha512-d2qQLzTJ9WxQftPAuEQpSPmKqzxePjzVbpAVv62AQ64NTL+wR4JkrVqR/LqFsFEUsHDAiId52mJteHDFuDkElA==",
|
||||
"dev": true
|
||||
},
|
||||
"import-local": {
|
||||
@ -6268,9 +6267,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"loader-utils": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.3.tgz",
|
||||
"integrity": "sha512-THWqIsn8QRnvLl0shHYVBN9syumU8pYWEHPTmkiVGd+7K5eFNVSY6AJhRvgGF70gg1Dz+l/k8WicvFCxdEs60A==",
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz",
|
||||
"integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"big.js": "^5.2.2",
|
||||
@ -6338,9 +6337,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"memfs": {
|
||||
"version": "3.4.10",
|
||||
"resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.10.tgz",
|
||||
"integrity": "sha512-0bCUP+L79P4am30yP1msPzApwuMQG23TjwlwdHeEV5MxioDR1a0AgB0T9FfggU52eJuDCq8WVwb5ekznFyWiTQ==",
|
||||
"version": "3.4.12",
|
||||
"resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.12.tgz",
|
||||
"integrity": "sha512-BcjuQn6vfqP+k100e0E9m61Hyqa//Brp+I3f0OBmN0ATHlFA8vx3Lt8z57R3u2bPqe3WGDBC+nF72fTH7isyEw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fs-monkey": "^1.0.3"
|
||||
@ -6467,9 +6466,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node-releases": {
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz",
|
||||
"integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==",
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.7.tgz",
|
||||
"integrity": "sha512-EJ3rzxL9pTWPjk5arA0s0dgXpnyiAbJDE6wHT62g7VsgrgQgmmZ+Ru++M1BFofncWja+Pnn3rEr3fieRySAdKQ==",
|
||||
"dev": true
|
||||
},
|
||||
"normalize-path": {
|
||||
@ -7219,9 +7218,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"terser": {
|
||||
"version": "5.15.1",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.15.1.tgz",
|
||||
"integrity": "sha512-K1faMUvpm/FBxjBXud0LWVAGxmvoPbZbfTCYbSgaaYQaIXI3/TdI7a7ZGA73Zrou6Q8Zmz3oeUTsp/dj+ag2Xw==",
|
||||
"version": "5.16.1",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.16.1.tgz",
|
||||
"integrity": "sha512-xvQfyfA1ayT0qdK47zskQgRZeWLoOQ8JQ6mIgRGVNwZKdQMU+5FkCBjmv4QjcrTzyZquRw2FVtlJSRUmMKQslw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jridgewell/source-map": "^0.3.2",
|
||||
@ -7311,9 +7310,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"ts-loader": {
|
||||
"version": "9.4.1",
|
||||
"resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.4.1.tgz",
|
||||
"integrity": "sha512-384TYAqGs70rn9F0VBnh6BPTfhga7yFNdC5gXbQpDrBj9/KsT4iRkGqKXhziofHOlE2j6YEaiTYVGKKvPhGWvw==",
|
||||
"version": "9.4.2",
|
||||
"resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.4.2.tgz",
|
||||
"integrity": "sha512-OmlC4WVmFv5I0PpaxYb+qGeGOdm5giHU7HwDDUjw59emP2UYMHy9fFSDcYgSNoH8sXcj4hGCSEhlDZ9ULeDraA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"chalk": "^4.1.0",
|
||||
@ -7360,9 +7359,9 @@
|
||||
}
|
||||
},
|
||||
"typescript": {
|
||||
"version": "4.8.4",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz",
|
||||
"integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==",
|
||||
"version": "4.9.4",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz",
|
||||
"integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==",
|
||||
"dev": true
|
||||
},
|
||||
"unpipe": {
|
||||
@ -7446,9 +7445,9 @@
|
||||
}
|
||||
},
|
||||
"webpack": {
|
||||
"version": "5.74.0",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.74.0.tgz",
|
||||
"integrity": "sha512-A2InDwnhhGN4LYctJj6M1JEaGL7Luj6LOmyBHjcI8529cm5p6VXiTIW2sn6ffvEAKmveLzvu4jrihwXtPojlAA==",
|
||||
"version": "5.75.0",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.75.0.tgz",
|
||||
"integrity": "sha512-piaIaoVJlqMsPtX/+3KTTO6jfvrSYgauFVdt8cr9LTHKmcq/AMd4mhzsiP7ZF/PGRNPGA8336jldh9l2Kt2ogQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/eslint-scope": "^3.7.3",
|
||||
@ -7648,9 +7647,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"ws": {
|
||||
"version": "8.10.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.10.0.tgz",
|
||||
"integrity": "sha512-+s49uSmZpvtAsd2h37vIPy1RBusaLawVe8of+GyEPsaJTCMpj/2v8NpeK1SHXjBlQ95lQTmQofOJnFiLoaN3yw==",
|
||||
"version": "8.11.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz",
|
||||
"integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==",
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user