Add CLI to demo (#160)

* Introduce a CLI

* Fix headless rendering
This commit is contained in:
Max Ammann 2022-09-08 11:12:22 +02:00 committed by GitHub
parent 0b9404a518
commit 1934555013
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 184 additions and 114 deletions

View File

@ -1,6 +1,6 @@
<component name="ProjectRunConfigurationManager"> <component name="ProjectRunConfigurationManager">
<configuration default="false" name="Run demo (debug+enable-tracing)" type="CargoCommandRunConfiguration" factoryName="Cargo Command"> <configuration default="false" name="Run headed demo (debug+enable-tracing)" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
<option name="command" value="run -p maplibre-demo --features trace" /> <option name="command" value="run -p maplibre-demo --features trace -- headed" />
<option name="workingDirectory" value="file://$PROJECT_DIR$" /> <option name="workingDirectory" value="file://$PROJECT_DIR$" />
<option name="channel" value="DEFAULT" /> <option name="channel" value="DEFAULT" />
<option name="requiredFeatures" value="true" /> <option name="requiredFeatures" value="true" />

View File

@ -1,6 +1,6 @@
<component name="ProjectRunConfigurationManager"> <component name="ProjectRunConfigurationManager">
<configuration default="false" name="Run demo (release+enable-tracing)" type="CargoCommandRunConfiguration" factoryName="Cargo Command"> <configuration default="false" name="Run headed demo (release+enable-tracing)" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
<option name="command" value="run -p maplibre-demo --release --features trace" /> <option name="command" value="run -p maplibre-demo --release --features trace -- headed" />
<option name="workingDirectory" value="file://$PROJECT_DIR$" /> <option name="workingDirectory" value="file://$PROJECT_DIR$" />
<option name="channel" value="DEFAULT" /> <option name="channel" value="DEFAULT" />
<option name="requiredFeatures" value="true" /> <option name="requiredFeatures" value="true" />

View File

@ -23,3 +23,5 @@ tracing = "0.1.35"
tracing-subscriber = { version = "0.3.14", optional = true } tracing-subscriber = { version = "0.3.14", optional = true }
tracing-tracy = { version = "0.8", optional = true } tracing-tracy = { version = "0.8", optional = true }
tracy-client = { version = "0.12.7", optional = true } tracy-client = { version = "0.12.7", optional = true }
clap = { version = "3.2.12", features = ["derive"] }

View File

@ -0,0 +1,16 @@
use maplibre::{
platform::{http_client::ReqwestHttpClient, schedule_method::TokioScheduleMethod},
MapBuilder,
};
use maplibre_winit::winit::WinitMapWindowConfig;
pub async fn run_headed() {
MapBuilder::new()
.with_map_window_config(WinitMapWindowConfig::new("maplibre".to_string()))
.with_http_client(ReqwestHttpClient::new(None))
.with_schedule_method(TokioScheduleMethod::new())
.build()
.initialize()
.await
.run()
}

View File

@ -0,0 +1,55 @@
use maplibre::{
coords::{LatLon, WorldTileCoords},
error::Error,
headless::HeadlessMapWindowConfig,
platform::{http_client::ReqwestHttpClient, schedule_method::TokioScheduleMethod},
render::settings::{RendererSettings, TextureFormat},
util::grid::google_mercator,
window::WindowSize,
MapBuilder,
};
use tile_grid::{extent_wgs84_to_merc, Extent, GridIterator};
pub async fn run_headless(tile_size: u32, min: LatLon, max: LatLon) {
let mut map = MapBuilder::new()
.with_map_window_config(HeadlessMapWindowConfig {
size: WindowSize::new(tile_size, tile_size).unwrap(),
})
.with_http_client(ReqwestHttpClient::new(None))
.with_schedule_method(TokioScheduleMethod::new())
.with_renderer_settings(RendererSettings {
texture_format: TextureFormat::Rgba8UnormSrgb,
..RendererSettings::default()
})
.build()
.initialize_headless()
.await;
let tile_limits = google_mercator().tile_limits(
extent_wgs84_to_merc(&Extent {
minx: min.longitude,
miny: min.latitude,
maxx: max.longitude,
maxy: max.latitude,
}),
0,
);
for (z, x, y) in GridIterator::new(10, 10, tile_limits) {
let coords = WorldTileCoords::from((x as i32, y as i32, z.into()));
println!("Rendering {}", &coords);
map.map_schedule
.fetch_process(&coords)
.await
.expect("Failed to fetch and process!");
match map.map_schedule_mut().update_and_redraw() {
Ok(_) => {}
Err(Error::Render(e)) => {
eprintln!("{}", e);
if e.should_exit() {}
}
e => eprintln!("{:?}", e),
};
}
}

View File

@ -1,31 +1,12 @@
use std::collections::HashSet; use std::io::ErrorKind;
use maplibre::{ use clap::{builder::ValueParser, Parser, Subcommand};
benchmarking::tessellation::{IndexDataType, OverAlignedVertexBuffer}, use maplibre::{coords::LatLon, platform::run_multithreaded};
coords::{TileCoords, ViewRegion, WorldTileCoords, ZoomLevel},
error::Error, use crate::{headed::run_headed, headless::run_headless};
headless::{utils::HeadlessPipelineProcessor, HeadlessMapWindowConfig},
io::{ mod headed;
pipeline::{PipelineContext, PipelineProcessor, Processable}, mod headless;
source_client::{HttpClient, HttpSourceClient},
tile_pipelines::build_vector_tile_pipeline,
tile_repository::StoredLayer,
RawLayer, TileRequest,
},
platform::{
http_client::ReqwestHttpClient, run_multithreaded, schedule_method::TokioScheduleMethod,
},
render::{
settings::{RendererSettings, TextureFormat},
ShaderVertex,
},
style::source::TileAddressingScheme,
util::{grid::google_mercator, math::Aabb2},
window::{EventLoop, WindowSize},
MapBuilder,
};
use maplibre_winit::winit::WinitMapWindowConfig;
use tile_grid::{extent_wgs84_to_merc, Extent, GridIterator};
#[cfg(feature = "trace")] #[cfg(feature = "trace")]
fn enable_tracing() { fn enable_tracing() {
@ -36,63 +17,46 @@ fn enable_tracing() {
tracing::subscriber::set_global_default(subscriber).expect("setting default subscriber failed"); tracing::subscriber::set_global_default(subscriber).expect("setting default subscriber failed");
} }
fn run_in_window() { #[derive(Parser)]
run_multithreaded(async { #[clap(author, version, about, long_about = None)]
MapBuilder::new() #[clap(propagate_version = true)]
.with_map_window_config(WinitMapWindowConfig::new("maplibre".to_string())) struct Cli {
.with_http_client(ReqwestHttpClient::new(None)) #[clap(subcommand)]
.with_schedule_method(TokioScheduleMethod::new()) command: Commands,
.build()
.initialize()
.await
.run()
})
} }
fn run_headless() { fn parse_lat_long(env: &str) -> Result<LatLon, std::io::Error> {
run_multithreaded(async { let split = env.split(',').collect::<Vec<_>>();
let mut map = MapBuilder::new() if let (Some(latitude), Some(longitude)) = (split.get(0), split.get(1)) {
.with_map_window_config(HeadlessMapWindowConfig { Ok(LatLon::new(
size: WindowSize::new(1000, 1000).unwrap(), latitude.parse::<f64>().unwrap(),
}) longitude.parse::<f64>().unwrap(),
.with_http_client(ReqwestHttpClient::new(None)) ))
.with_schedule_method(TokioScheduleMethod::new()) } else {
.with_renderer_settings(RendererSettings { Err(std::io::Error::new(
texture_format: TextureFormat::Rgba8UnormSrgb, ErrorKind::InvalidData,
..RendererSettings::default() "Failed to parse latitude and longitude.",
}) ))
.build() }
.initialize_headless() }
.await;
let tile_limits = google_mercator().tile_limits( #[derive(Subcommand)]
extent_wgs84_to_merc(&Extent { enum Commands {
minx: 11.3475219363, Headed {},
miny: 48.0345697188, Headless {
maxx: 11.7917815798, #[clap(default_value_t = 400)]
maxy: 48.255861, tile_size: u32,
}), #[clap(
0, value_parser = ValueParser::new(parse_lat_long),
); default_value_t = LatLon::new(48.0345697188, 11.3475219363)
)]
for (z, x, y) in GridIterator::new(10, 10, tile_limits) { min: LatLon,
let coords = WorldTileCoords::from((x as i32, y as i32, z.into())); #[clap(
println!("Rendering {}", &coords); value_parser = ValueParser::new(parse_lat_long),
map.map_schedule default_value_t = LatLon::new(48.255861, 11.7917815798)
.fetch_process(&coords) )]
.await max: LatLon,
.expect("Failed to fetch and process!"); },
match map.map_schedule_mut().update_and_redraw() {
Ok(_) => {}
Err(Error::Render(e)) => {
eprintln!("{}", e);
if e.should_exit() {}
}
e => eprintln!("{:?}", e),
};
}
})
} }
fn main() { fn main() {
@ -101,6 +65,20 @@ fn main() {
#[cfg(feature = "trace")] #[cfg(feature = "trace")]
enable_tracing(); enable_tracing();
//run_headless(); let cli = Cli::parse();
run_in_window();
// You can check for the existence of subcommands, and if found use their
// matches just as you would the top level cmd
match &cli.command {
Commands::Headed {} => {
run_multithreaded(async { run_headed().await });
}
Commands::Headless {
tile_size,
min,
max,
} => {
run_multithreaded(async { run_headless(*tile_size, *min, *max).await });
}
}
} }

View File

@ -20,15 +20,14 @@ pub struct ViewState {
impl ViewState { impl ViewState {
pub fn new<P: Into<cgmath::Rad<f64>>>( pub fn new<P: Into<cgmath::Rad<f64>>>(
window_size: &WindowSize, window_size: &WindowSize,
position: WorldCoords,
zoom: Zoom, zoom: Zoom,
center: LatLon,
pitch: f64, pitch: f64,
fovy: P, fovy: P,
) -> Self { ) -> Self {
let tile_center = TILE_SIZE / 2.0; let tile_center = TILE_SIZE / 2.0;
let fovy = fovy.into(); let fovy = fovy.into();
let height = tile_center / (fovy / 2.0).tan(); let height = tile_center / (fovy / 2.0).tan();
let position = WorldCoords::from_lat_lon(center, zoom);
let camera = Camera::new( let camera = Camera::new(
(position.x, position.y, height), (position.x, position.y, height),

View File

@ -1,6 +1,10 @@
//! Provides utilities related to coordinates. //! Provides utilities related to coordinates.
use std::{f64::consts::PI, fmt}; use std::{
f64::consts::PI,
fmt,
fmt::{Display, Formatter},
};
use cgmath::{num_traits::Pow, AbsDiffEq, Matrix4, Point3, Vector3}; use cgmath::{num_traits::Pow, AbsDiffEq, Matrix4, Point3, Vector3};
@ -112,17 +116,32 @@ impl Into<u8> for ZoomLevel {
} }
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
pub struct LatLon(f64, f64); pub struct LatLon {
pub latitude: f64,
pub longitude: f64,
}
impl LatLon { impl LatLon {
pub fn new(latitude: f64, longitude: f64) -> Self { pub fn new(latitude: f64, longitude: f64) -> Self {
LatLon(latitude, longitude) LatLon {
latitude,
longitude,
}
} }
} }
impl Default for LatLon { impl Default for LatLon {
fn default() -> Self { fn default() -> Self {
LatLon(0.0, 0.0) LatLon {
latitude: 0.0,
longitude: 0.0,
}
}
}
impl Display for LatLon {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{},{}", self.latitude, self.longitude)
} }
} }
@ -477,10 +496,10 @@ impl WorldCoords {
pub fn from_lat_lon(lat_lon: LatLon, zoom: Zoom) -> WorldCoords { pub fn from_lat_lon(lat_lon: LatLon, zoom: Zoom) -> WorldCoords {
let tile_size = TILE_SIZE * 2.0_f64.powf(zoom.0); let tile_size = TILE_SIZE * 2.0_f64.powf(zoom.0);
// Get x value // Get x value
let x = (lat_lon.1 + 180.0) * (tile_size / 360.0); let x = (lat_lon.longitude + 180.0) * (tile_size / 360.0);
// Convert from degrees to radians // Convert from degrees to radians
let lat_rad = (lat_lon.0 * PI) / 180.0; let lat_rad = (lat_lon.latitude * PI) / 180.0;
// get y value // get y value
let merc_n = f64::ln(f64::tan((PI / 4.0) + (lat_rad / 2.0))); let merc_n = f64::ln(f64::tan((PI / 4.0) + (lat_rad / 2.0)));

View File

@ -13,7 +13,7 @@ use wgpu::{BufferAsyncError, BufferSlice};
use crate::{ use crate::{
context::{MapContext, ViewState}, context::{MapContext, ViewState},
coords::{LatLon, ViewRegion, WorldTileCoords, Zoom}, coords::{LatLon, ViewRegion, WorldCoords, WorldTileCoords, Zoom, TILE_SIZE},
error::Error, error::Error,
headless::utils::HeadlessPipelineProcessor, headless::utils::HeadlessPipelineProcessor,
io::{ io::{
@ -114,12 +114,9 @@ where
) -> Self { ) -> Self {
let view_state = ViewState::new( let view_state = ViewState::new(
&window_size, &window_size,
style.zoom.map(|zoom| Zoom::new(zoom)).unwrap_or_default(), WorldCoords::from((TILE_SIZE / 2., TILE_SIZE / 2.)),
style Zoom::default(),
.center 0.0,
.map(|center| LatLon::new(center[0], center[1]))
.unwrap_or_default(),
style.pitch.unwrap_or_default(),
cgmath::Deg(110.0), cgmath::Deg(110.0),
); );
let tile_repository = TileRepository::new(); let tile_repository = TileRepository::new();
@ -210,6 +207,8 @@ where
pool.clear(); pool.clear();
} }
self.map_context.tile_repository.clear();
while let Some(layer) = processor.layers.pop() { while let Some(layer) = processor.layers.pop() {
self.map_context self.map_context
.tile_repository .tile_repository

View File

@ -67,6 +67,10 @@ impl TileRepository {
} }
} }
pub fn clear(&mut self) {
self.tree.clear();
}
/// Inserts a tessellated layer into the quad tree at its world tile coords. /// 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 /// If the space is vacant, the tessellated layer is inserted into a new
/// [crate::io::tile_repository::CachedTile]. /// [crate::io::tile_repository::CachedTile].

View File

@ -2,7 +2,7 @@ use std::{marker::PhantomData, mem};
use crate::{ use crate::{
context::{MapContext, ViewState}, context::{MapContext, ViewState},
coords::{LatLon, Zoom}, coords::{LatLon, WorldCoords, Zoom, TILE_SIZE},
error::Error, error::Error,
io::{ io::{
scheduler::Scheduler, scheduler::Scheduler,
@ -52,16 +52,14 @@ where
wgpu_settings: WgpuSettings, wgpu_settings: WgpuSettings,
renderer_settings: RendererSettings, renderer_settings: RendererSettings,
) -> Self { ) -> Self {
let view_state = ViewState::new( let zoom = style.zoom.map(|zoom| Zoom::new(zoom)).unwrap_or_default();
&window_size, let position = style
style.zoom.map(|zoom| Zoom::new(zoom)).unwrap_or_default(), .center
style .map(|center| WorldCoords::from_lat_lon(LatLon::new(center[0], center[1]), zoom))
.center .unwrap_or_default();
.map(|center| LatLon::new(center[0], center[1])) let pitch = style.pitch.unwrap_or_default();
.unwrap_or_default(), let view_state = ViewState::new(&window_size, position, zoom, pitch, cgmath::Deg(110.0));
style.pitch.unwrap_or_default(),
cgmath::Deg(110.0),
);
let tile_repository = TileRepository::new(); let tile_repository = TileRepository::new();
let mut schedule = Schedule::default(); let mut schedule = Schedule::default();

View File

@ -18,7 +18,7 @@ pub struct Style {
pub metadata: HashMap<String, String>, pub metadata: HashMap<String, String>,
pub sources: HashMap<String, Source>, pub sources: HashMap<String, Source>,
pub layers: Vec<StyleLayer>, pub layers: Vec<StyleLayer>,
pub center: Option<[f64; 2]>, pub center: Option<[f64; 2]>, // TODO: Use LatLon type here
pub zoom: Option<f64>, pub zoom: Option<f64>,
pub pitch: Option<f64>, pub pitch: Option<f64>,
} }