Introduce environment

This commit is contained in:
Maximilian Ammann 2022-09-09 12:34:06 +02:00
parent 156e63daf4
commit 056cb5c995
9 changed files with 136 additions and 179 deletions

View File

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

View File

@ -2,10 +2,10 @@ use maplibre::{
platform::{http_client::ReqwestHttpClient, schedule_method::TokioScheduleMethod},
MapBuilder,
};
use maplibre_winit::winit::WinitMapWindowConfig;
use maplibre_winit::winit::{WinitEnvironment, WinitMapWindowConfig};
pub async fn run_headed() {
MapBuilder::new()
MapBuilder::<WinitEnvironment<_, _>>::new()
.with_map_window_config(WinitMapWindowConfig::new("maplibre".to_string()))
.with_http_client(ReqwestHttpClient::new(None))
.with_schedule_method(TokioScheduleMethod::new())

View File

@ -1,3 +1,4 @@
use maplibre::headless::HeadlessEnvironment;
use maplibre::{
coords::{LatLon, WorldTileCoords},
error::Error,
@ -8,10 +9,11 @@ use maplibre::{
window::WindowSize,
MapBuilder,
};
use maplibre_winit::winit::WinitEnvironment;
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()
let mut map = MapBuilder::<HeadlessEnvironment<_, _>>::new()
.with_map_window_config(HeadlessMapWindowConfig {
size: WindowSize::new(tile_size, tile_size).unwrap(),
})

View File

@ -1,10 +1,12 @@
use instant::Instant;
use maplibre::environment::Environment;
use maplibre::{
error::Error,
io::{scheduler::ScheduleMethod, source_client::HttpClient},
map_schedule::InteractiveMapSchedule,
window::{EventLoop, HeadedMapWindow, MapWindowConfig},
};
use std::marker::PhantomData;
use winit::{
event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent},
event_loop::ControlFlow,
@ -52,30 +54,35 @@ pub struct WinitMapWindow {
event_loop: Option<WinitEventLoop>,
}
pub type WinitWindow = winit::window::Window;
pub type WinitEventLoop = winit::event_loop::EventLoop<()>;
impl WinitMapWindow {
pub fn take_event_loop(&mut self) -> Option<WinitEventLoop> {
self.event_loop.take()
}
}
pub type WinitWindow = winit::window::Window;
pub type WinitEventLoop = winit::event_loop::EventLoop<()>;
pub struct WinitEnvironment<SM: ScheduleMethod, HC: HttpClient> {
phantom_sm: PhantomData<SM>,
phantom_hc: PhantomData<HC>,
}
impl<SM: ScheduleMethod, HC: HttpClient> Environment for WinitEnvironment<SM, HC> {
type MapWindowConfig = WinitMapWindowConfig;
type ScheduleMethod = SM;
type HttpClient = HC;
}
///Main (platform-specific) main loop which handles:
///* Input (Mouse/Keyboard)
///* Platform Events like suspend/resume
///* Render a new frame
impl<MWC, SM, HC> EventLoop<MWC, SM, HC> for WinitMapWindow
impl<E: Environment> EventLoop<E> for WinitMapWindow
where
MWC: MapWindowConfig<MapWindow = WinitMapWindow>,
SM: ScheduleMethod,
HC: HttpClient,
E::MapWindowConfig: MapWindowConfig<MapWindow = WinitMapWindow>,
{
fn run(
mut self,
mut map_schedule: InteractiveMapSchedule<MWC, SM, HC>,
max_frames: Option<u64>,
) {
fn run(mut self, mut map_schedule: InteractiveMapSchedule<E>, max_frames: Option<u64>) {
let mut last_render_time = Instant::now();
let mut current_frame: u64 = 0;

View File

@ -0,0 +1,7 @@
use crate::{HttpClient, MapWindowConfig, ScheduleMethod};
pub trait Environment: 'static {
type MapWindowConfig: MapWindowConfig;
type ScheduleMethod: ScheduleMethod;
type HttpClient: HttpClient;
}

View File

@ -1,3 +1,4 @@
use std::marker::PhantomData;
use std::{
collections::HashSet,
fs::File,
@ -35,7 +36,8 @@ use crate::{
RenderState,
},
schedule::{Schedule, Stage},
HttpClient, MapWindow, MapWindowConfig, Renderer, ScheduleMethod, Scheduler, Style, WindowSize,
Environment, HttpClient, MapWindow, MapWindowConfig, Renderer, ScheduleMethod, Scheduler,
Style, WindowSize,
};
pub struct HeadlessMapWindowConfig {
@ -60,56 +62,47 @@ impl MapWindow for HeadlessMapWindow {
}
}
pub struct HeadlessMap<MWC, SM, HC>
where
MWC: MapWindowConfig,
SM: ScheduleMethod,
HC: HttpClient,
{
pub map_schedule: HeadlessMapSchedule<MWC, SM, HC>,
pub window: MWC::MapWindow,
pub struct HeadlessEnvironment<SM: ScheduleMethod, HC: HttpClient> {
phantom_sm: PhantomData<SM>,
phantom_hc: PhantomData<HC>,
}
impl<MWC, SM, HC> HeadlessMap<MWC, SM, HC>
where
MWC: MapWindowConfig,
SM: ScheduleMethod,
HC: HttpClient,
{
pub fn map_schedule_mut(&mut self) -> &mut HeadlessMapSchedule<MWC, SM, HC> {
impl<SM: ScheduleMethod, HC: HttpClient> Environment for HeadlessEnvironment<SM, HC> {
type MapWindowConfig = HeadlessMapWindowConfig;
type ScheduleMethod = SM;
type HttpClient = HC;
}
pub struct HeadlessMap<E: Environment> {
pub map_schedule: HeadlessMapSchedule<E>,
pub window: <E::MapWindowConfig as MapWindowConfig>::MapWindow,
}
impl<E: Environment> HeadlessMap<E> {
pub fn map_schedule_mut(&mut self) -> &mut HeadlessMapSchedule<E> {
&mut self.map_schedule
}
}
/// Stores the state of the map, dispatches tile fetching and caching, tessellation and drawing.
pub struct HeadlessMapSchedule<MWC, SM, HC>
where
MWC: MapWindowConfig,
SM: ScheduleMethod,
HC: HttpClient,
{
map_window_config: MWC,
pub struct HeadlessMapSchedule<E: Environment> {
map_window_config: E::MapWindowConfig,
pub map_context: MapContext,
schedule: Schedule,
scheduler: Scheduler<SM>,
http_client: HC,
scheduler: Scheduler<E::ScheduleMethod>,
http_client: E::HttpClient,
tile_request_state: TileRequestState,
}
impl<MWC, SM, HC> HeadlessMapSchedule<MWC, SM, HC>
where
MWC: MapWindowConfig,
SM: ScheduleMethod,
HC: HttpClient,
{
impl<E: Environment> HeadlessMapSchedule<E> {
pub fn new(
map_window_config: MWC,
map_window_config: E::MapWindowConfig,
window_size: WindowSize,
renderer: Renderer,
scheduler: Scheduler<SM>,
http_client: HC,
scheduler: Scheduler<E::ScheduleMethod>,
http_client: E::HttpClient,
style: Style,
) -> Self {
let view_state = ViewState::new(
@ -160,10 +153,10 @@ where
pub fn schedule(&self) -> &Schedule {
&self.schedule
}
pub fn scheduler(&self) -> &Scheduler<SM> {
pub fn scheduler(&self) -> &Scheduler<E::ScheduleMethod> {
&self.scheduler
}
pub fn http_client(&self) -> &HC {
pub fn http_client(&self) -> &E::HttpClient {
&self.http_client
}
@ -176,7 +169,7 @@ where
.filter_map(|layer| layer.source_layer.clone())
.collect();
let http_source_client: HttpSourceClient<HC> =
let http_source_client: HttpSourceClient<E::HttpClient> =
HttpSourceClient::new(self.http_client.clone());
let data = http_source_client

View File

@ -16,6 +16,7 @@
//! maplibre = "0.0.2"
//! ```
use crate::environment::Environment;
use crate::{
io::{
scheduler::{ScheduleMethod, Scheduler},
@ -56,29 +57,21 @@ pub mod benchmarking;
// Internal modules
pub(crate) mod tessellation;
pub mod environment;
/// The [`Map`] defines the public interface of the map renderer.
// DO NOT IMPLEMENT INTERNALS ON THIS STRUCT.
pub struct Map<MWC, SM, HC>
where
MWC: MapWindowConfig,
SM: ScheduleMethod,
HC: HttpClient,
{
map_schedule: InteractiveMapSchedule<MWC, SM, HC>,
window: MWC::MapWindow,
pub struct Map<E: Environment> {
map_schedule: InteractiveMapSchedule<E>,
window: <E::MapWindowConfig as MapWindowConfig>::MapWindow,
}
impl<MWC, SM, HC> Map<MWC, SM, HC>
impl<E: Environment> Map<E>
where
MWC: MapWindowConfig,
SM: ScheduleMethod,
HC: HttpClient,
<E::MapWindowConfig as MapWindowConfig>::MapWindow: EventLoop<E>,
{
/// Starts the [`crate::map_schedule::MapState`] Runnable with the configured event loop.
pub fn run(self)
where
MWC::MapWindow: EventLoop<MWC, SM, HC>,
{
pub fn run(self) {
self.run_with_optionally_max_frames(None);
}
@ -87,10 +80,7 @@ where
/// # Arguments
///
/// * `max_frames` - Maximum number of frames per second.
pub fn run_with_max_frames(self, max_frames: u64)
where
MWC::MapWindow: EventLoop<MWC, SM, HC>,
{
pub fn run_with_max_frames(self, max_frames: u64) {
self.run_with_optionally_max_frames(Some(max_frames));
}
@ -99,51 +89,37 @@ where
/// # Arguments
///
/// * `max_frames` - Optional maximum number of frames per second.
pub fn run_with_optionally_max_frames(self, max_frames: Option<u64>)
where
MWC::MapWindow: EventLoop<MWC, SM, HC>,
{
pub fn run_with_optionally_max_frames(self, max_frames: Option<u64>) {
self.window.run(self.map_schedule, max_frames);
}
pub fn map_schedule(&self) -> &InteractiveMapSchedule<MWC, SM, HC> {
pub fn map_schedule(&self) -> &InteractiveMapSchedule<E> {
&self.map_schedule
}
pub fn map_schedule_mut(&mut self) -> &mut InteractiveMapSchedule<MWC, SM, HC> {
pub fn map_schedule_mut(&mut self) -> &mut InteractiveMapSchedule<E> {
&mut self.map_schedule
}
}
/// Stores the map configuration before the map's state has been fully initialized.
pub struct UninitializedMap<MWC, SM, HC>
where
MWC: MapWindowConfig,
SM: ScheduleMethod,
HC: HttpClient,
{
scheduler: Scheduler<SM>,
http_client: HC,
pub struct UninitializedMap<E: Environment> {
scheduler: Scheduler<E::ScheduleMethod>,
http_client: E::HttpClient,
style: Style,
wgpu_settings: WgpuSettings,
renderer_settings: RendererSettings,
map_window_config: MWC,
map_window_config: E::MapWindowConfig,
}
impl<MWC, SM, HC> UninitializedMap<MWC, SM, HC>
impl<E: Environment> UninitializedMap<E>
where
MWC: MapWindowConfig,
SM: ScheduleMethod,
HC: HttpClient,
<E::MapWindowConfig as MapWindowConfig>::MapWindow: HeadedMapWindow,
{
/// Initializes the whole rendering pipeline for the given configuration.
/// Returns the initialized map, ready to be run.
pub async fn initialize(self) -> Map<MWC, SM, HC>
where
MWC: MapWindowConfig,
<MWC as MapWindowConfig>::MapWindow: HeadedMapWindow,
{
pub async fn initialize(self) -> Map<E> {
let window = self.map_window_config.create();
let window_size = window.size();
@ -171,9 +147,11 @@ where
window,
}
}
}
#[cfg(feature = "headless")]
pub async fn initialize_headless(self) -> headless::HeadlessMap<MWC, SM, HC> {
#[cfg(feature = "headless")]
impl<E: Environment> UninitializedMap<E> {
pub async fn initialize_headless(self) -> headless::HeadlessMap<E> {
let window = self.map_window_config.create();
let window_size = window.size();
@ -198,26 +176,18 @@ where
}
}
pub struct MapBuilder<MWC, SM, HC>
where
SM: ScheduleMethod,
{
schedule_method: Option<SM>,
scheduler: Option<Scheduler<SM>>,
http_client: Option<HC>,
pub struct MapBuilder<E: Environment> {
schedule_method: Option<E::ScheduleMethod>,
scheduler: Option<Scheduler<E::ScheduleMethod>>,
http_client: Option<E::HttpClient>,
style: Option<Style>,
map_window_config: Option<MWC>,
map_window_config: Option<E::MapWindowConfig>,
wgpu_settings: Option<WgpuSettings>,
renderer_settings: Option<RendererSettings>,
}
impl<MWC, SM, HC> MapBuilder<MWC, SM, HC>
where
MWC: MapWindowConfig,
SM: ScheduleMethod,
HC: HttpClient,
{
impl<E: Environment> MapBuilder<E> {
pub fn new() -> Self {
Self {
schedule_method: None,
@ -230,7 +200,7 @@ where
}
}
pub fn with_map_window_config(mut self, map_window_config: MWC) -> Self {
pub fn with_map_window_config(mut self, map_window_config: E::MapWindowConfig) -> Self {
self.map_window_config = Some(map_window_config);
self
}
@ -245,17 +215,17 @@ where
self
}
pub fn with_schedule_method(mut self, schedule_method: SM) -> Self {
pub fn with_schedule_method(mut self, schedule_method: E::ScheduleMethod) -> Self {
self.schedule_method = Some(schedule_method);
self
}
pub fn with_http_client(mut self, http_client: HC) -> Self {
pub fn with_http_client(mut self, http_client: E::HttpClient) -> Self {
self.http_client = Some(http_client);
self
}
pub fn with_existing_scheduler(mut self, scheduler: Scheduler<SM>) -> Self {
pub fn with_existing_scheduler(mut self, scheduler: Scheduler<E::ScheduleMethod>) -> Self {
self.scheduler = Some(scheduler);
self
}
@ -266,7 +236,7 @@ where
}
/// Builds the UninitializedMap with the given configuration.
pub fn build(self) -> UninitializedMap<MWC, SM, HC> {
pub fn build(self) -> UninitializedMap<E> {
let scheduler = self
.scheduler
.unwrap_or_else(|| Scheduler::new(self.schedule_method.unwrap()));

View File

@ -13,41 +13,28 @@ use crate::{
schedule::{Schedule, Stage},
stages::register_stages,
style::Style,
HeadedMapWindow, MapWindowConfig, Renderer, RendererSettings, ScheduleMethod, WgpuSettings,
WindowSize,
Environment, HeadedMapWindow, MapWindowConfig, Renderer, RendererSettings, ScheduleMethod,
WgpuSettings, WindowSize,
};
/// Stores the state of the map, dispatches tile fetching and caching, tessellation and drawing.
pub struct InteractiveMapSchedule<MWC, SM, HC>
where
MWC: MapWindowConfig,
SM: ScheduleMethod,
HC: HttpClient,
{
map_window_config: MWC,
pub struct InteractiveMapSchedule<E: Environment> {
map_window_config: E::MapWindowConfig,
map_context: EventuallyMapContext,
schedule: Schedule,
phantom_sm: PhantomData<SM>,
phantom_hc: PhantomData<HC>,
suspended: bool,
}
impl<MWC, SM, HC> InteractiveMapSchedule<MWC, SM, HC>
where
MWC: MapWindowConfig,
SM: ScheduleMethod,
HC: HttpClient,
{
impl<E: Environment> InteractiveMapSchedule<E> {
pub fn new(
map_window_config: MWC,
map_window_config: E::MapWindowConfig,
window_size: WindowSize,
renderer: Option<Renderer>,
scheduler: Scheduler<SM>,
http_client: HC,
scheduler: Scheduler<E::ScheduleMethod>,
http_client: E::HttpClient,
style: Style,
wgpu_settings: WgpuSettings,
renderer_settings: RendererSettings,
@ -63,7 +50,8 @@ where
let tile_repository = TileRepository::new();
let mut schedule = Schedule::default();
let http_source_client: HttpSourceClient<HC> = HttpSourceClient::new(http_client);
let http_source_client: HttpSourceClient<E::HttpClient> =
HttpSourceClient::new(http_client);
register_stages(&mut schedule, http_source_client, Box::new(scheduler));
let graph = create_default_render_graph().unwrap();
@ -87,8 +75,6 @@ where
}),
},
schedule,
phantom_sm: Default::default(),
phantom_hc: Default::default(),
suspended: false,
}
}
@ -116,23 +102,6 @@ where
}
}
pub fn suspend(&mut self) {
self.suspended = true;
}
pub fn resume(&mut self, window: &MWC::MapWindow)
where
<MWC as MapWindowConfig>::MapWindow: HeadedMapWindow,
{
if let EventuallyMapContext::Full(map_context) = &mut self.map_context {
let renderer = &mut map_context.renderer;
renderer
.state
.recreate_surface::<MWC::MapWindow>(window, &renderer.instance);
self.suspended = false;
}
}
pub fn is_initialized(&self) -> bool {
match &self.map_context {
EventuallyMapContext::Full(_) => true,
@ -140,10 +109,32 @@ where
}
}
pub async fn late_init(&mut self) -> bool
where
<MWC as MapWindowConfig>::MapWindow: HeadedMapWindow,
{
pub fn view_state_mut(&mut self) -> &mut ViewState {
match &mut self.map_context {
EventuallyMapContext::Full(MapContext { view_state, .. }) => view_state,
EventuallyMapContext::Premature(PrematureMapContext { view_state, .. }) => view_state,
_ => panic!("should not happen"),
}
}
}
impl<E: Environment> InteractiveMapSchedule<E>
where
<E::MapWindowConfig as MapWindowConfig>::MapWindow: HeadedMapWindow,
{
pub fn suspend(&mut self) {
self.suspended = true;
}
pub fn resume(&mut self, window: &<E::MapWindowConfig as MapWindowConfig>::MapWindow) {
if let EventuallyMapContext::Full(map_context) = &mut self.map_context {
let renderer = &mut map_context.renderer;
renderer.state.recreate_surface(window, &renderer.instance);
self.suspended = false;
}
}
pub async fn late_init(&mut self) -> bool {
match &self.map_context {
EventuallyMapContext::Full(_) => false,
EventuallyMapContext::Premature(PrematureMapContext {
@ -162,14 +153,6 @@ where
EventuallyMapContext::_Uninitialized => false,
}
}
pub fn view_state_mut(&mut self) -> &mut ViewState {
match &mut self.map_context {
EventuallyMapContext::Full(MapContext { view_state, .. }) => view_state,
EventuallyMapContext::Premature(PrematureMapContext { view_state, .. }) => view_state,
_ => panic!("should not happen"),
}
}
}
pub struct PrematureMapContext {

View File

@ -2,7 +2,7 @@
use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle};
use crate::{HttpClient, InteractiveMapSchedule, ScheduleMethod};
use crate::{Environment, HttpClient, InteractiveMapSchedule, ScheduleMethod};
/// Window of a certain [`WindowSize`]. This can either be a proper window or a headless one.
pub trait MapWindow {
@ -27,13 +27,8 @@ pub trait MapWindowConfig: 'static {
/// The event loop is responsible for processing events and propagating them to the map renderer.
/// Only non-headless windows use an [`EventLoop`].
pub trait EventLoop<MWC, SM, HC>
where
MWC: MapWindowConfig,
SM: ScheduleMethod,
HC: HttpClient,
{
fn run(self, map_schedule: InteractiveMapSchedule<MWC, SM, HC>, max_frames: Option<u64>);
pub trait EventLoop<E: Environment> {
fn run(self, map_schedule: InteractiveMapSchedule<E>, max_frames: Option<u64>);
}
/// Window size with a width and an height in pixels.