Improve headless feature structure (#128)

* Collect headless related stuff under feature flag

* Run clippy
This commit is contained in:
Max Ammann 2022-06-04 10:53:54 +02:00 committed by GitHub
parent 4a57d052d8
commit 1c46a77ce0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
38 changed files with 435 additions and 482 deletions

View File

@ -6,7 +6,7 @@ use maplibre::platform::run_multithreaded;
use maplibre::platform::schedule_method::TokioScheduleMethod;
use maplibre::render::settings::{Backends, WgpuSettings};
use maplibre::MapBuilder;
use maplibre_winit::winit::{WinitEventLoop, WinitMapWindow, WinitMapWindowConfig, WinitWindow};
use maplibre_winit::winit::WinitMapWindowConfig;
use std::ffi::CString;
#[cfg(not(target_os = "android"))]
@ -33,7 +33,10 @@ pub fn android_main() {
}
#[no_mangle]
pub extern "system" fn Java_org_maplibre_1rs_MapLibreRs_android_1main(env: JNIEnv, class: JClass) {
pub extern "system" fn Java_org_maplibre_1rs_MapLibreRs_android_1main(
_env: JNIEnv,
_class: JClass,
) {
let tag = CString::new("maplibre").unwrap();
let message = CString::new("maplibre WOORKING").unwrap();
ndk_glue::android_log(Level::Warn, &tag, &message);

View File

@ -14,6 +14,18 @@ install-clippy:
install-nightly-clippy:
rustup component add clippy --toolchain $NIGHTLY_TOOLCHAIN
fixup:
cargo clippy --no-deps -p maplibre --fix
cargo clippy --allow-dirty --no-deps -p maplibre-winit --fix
cargo clippy --allow-dirty --no-deps -p maplibre-demo --fix
# Web
cargo clippy --allow-dirty --no-deps -p web --target wasm32-unknown-unknown --fix
cargo clippy --allow-dirty --no-deps -p maplibre --target wasm32-unknown-unknown --fix
cargo clippy --allow-dirty --no-deps -p maplibre-winit --target wasm32-unknown-unknown --fix
# Android
cargo clippy --allow-dirty --no-deps -p maplibre-winit --target x86_64-linux-android --fix
cargo clippy --allow-dirty --no-deps -p maplibre-android --target x86_64-linux-android --fix
check PROJECT ARCH: install-clippy
cargo clippy --no-deps -p {{PROJECT}} --target {{ARCH}}

View File

@ -1,23 +1,24 @@
use maplibre::benchmarking::tessellation::{IndexDataType, OverAlignedVertexBuffer};
use maplibre::coords::{WorldTileCoords, ZoomLevel};
use maplibre::error::Error;
use maplibre::headless::HeadlessMapWindowConfig;
use maplibre::io::pipeline::Processable;
use maplibre::io::pipeline::{PipelineContext, PipelineProcessor};
use maplibre::io::scheduler::ScheduleMethod;
use maplibre::io::source_client::{HttpClient, HttpSourceClient};
use maplibre::io::tile_pipelines::build_vector_tile_pipeline;
use maplibre::io::tile_repository::StoredLayer;
use maplibre::io::{RawLayer, TileRequest, TileRequestID};
use maplibre::map_schedule::{EventuallyMapContext, InteractiveMapSchedule};
use maplibre::io::{RawLayer, TileRequest};
use maplibre::platform::http_client::ReqwestHttpClient;
use maplibre::platform::run_multithreaded;
use maplibre::platform::schedule_method::TokioScheduleMethod;
use maplibre::render::settings::{RendererSettings, TextureFormat};
use maplibre::render::ShaderVertex;
use maplibre::window::{EventLoop, MapWindow, MapWindowConfig, WindowSize};
use maplibre::window::{EventLoop, WindowSize};
use maplibre::MapBuilder;
use maplibre_winit::winit::{WinitEventLoop, WinitMapWindow, WinitMapWindowConfig, WinitWindow};
use std::any::Any;
use maplibre_winit::winit::WinitMapWindowConfig;
use std::collections::HashSet;
#[cfg(feature = "trace")]
@ -29,27 +30,6 @@ fn enable_tracing() {
tracing::subscriber::set_global_default(subscriber).expect("setting default subscriber failed");
}
pub struct HeadlessMapWindowConfig {
size: WindowSize,
}
impl MapWindowConfig for HeadlessMapWindowConfig {
type MapWindow = HeadlessMapWindow;
fn create(&self) -> Self::MapWindow {
Self::MapWindow { size: self.size }
}
}
pub struct HeadlessMapWindow {
size: WindowSize,
}
impl MapWindow for HeadlessMapWindow {
fn size(&self) -> WindowSize {
self.size
}
}
fn run_in_window() {
run_multithreaded(async {
@ -157,6 +137,6 @@ fn main() {
#[cfg(feature = "trace")]
enable_tracing();
//run_headless();
run_headless();
run_in_window();
}

View File

@ -1,16 +1,14 @@
use crate::input::{InputController, UpdateState};
use instant::Instant;
use maplibre::error::Error;
use maplibre::io::scheduler::ScheduleMethod;
use maplibre::io::source_client::HttpClient;
use std::borrow::BorrowMut;
use maplibre::map_schedule::InteractiveMapSchedule;
use maplibre::window::{EventLoop, HeadedMapWindow, MapWindowConfig};
use winit::event::Event;
use winit::event::{ElementState, KeyboardInput, VirtualKeyCode, WindowEvent};
use winit::event_loop::ControlFlow;
use crate::input::{InputController, UpdateState};
use maplibre::map_schedule::InteractiveMapSchedule;
use maplibre::window::{EventLoop, HeadedMapWindow, MapWindow, MapWindowConfig};
use winit::event::Event;
#[cfg(target_arch = "wasm32")]
mod web;
@ -89,7 +87,7 @@ where
use tokio::runtime::Handle;
use tokio::task;
let state = task::block_in_place(|| {
task::block_in_place(|| {
Handle::current().block_on(async {
map_schedule.late_init().await;
})

View File

@ -3,9 +3,6 @@
//! This script is built and executed just before building the package.
//! It will validate the WGSL (WebGPU Shading Language) shaders and embed static files.
use std::path::{Path, PathBuf};
use std::{env, fs};
#[cfg(feature = "embed-static-tiles")]
use maplibre_build_tools::mbtiles::extract;
use maplibre_build_tools::wgsl::validate_project_wgsl;
@ -16,13 +13,13 @@ const MUNICH_Z: u8 = 15;
/// Tiles which can be used by StaticTileFetcher.
#[cfg(feature = "embed-static-tiles")]
fn clean_static_tiles() -> PathBuf {
let out_dir = env::var("OUT_DIR").unwrap();
fn clean_static_tiles() -> std::path::PathBuf {
let out_dir = std::env::var("OUT_DIR").unwrap();
let out = Path::new(&out_dir).join("extracted-tiles");
let out = std::path::Path::new(&out_dir).join("extracted-tiles");
if out.exists() && out.is_dir() {
fs::remove_dir_all(&out).unwrap()
std::fs::remove_dir_all(&out).unwrap()
}
out
@ -52,6 +49,8 @@ fn generate_type_def() -> Option<u32> {
#[cfg(feature = "embed-static-tiles")]
fn embed_tiles_statically() {
use std::env;
use std::path::Path;
let out = clean_static_tiles();
let root_dir = env::var("CARGO_MANIFEST_DIR").unwrap();

View File

@ -2,8 +2,7 @@ use crate::coords::{Zoom, ZoomLevel, TILE_SIZE};
use crate::io::tile_repository::TileRepository;
use crate::render::camera::{Camera, Perspective, ViewProjection};
use crate::util::ChangeObserver;
use crate::{Renderer, ScheduleMethod, Style, WindowSize};
use std::sync::mpsc;
use crate::{Renderer, Style, WindowSize};
/// Stores the camera configuration.
pub struct ViewState {

View File

@ -63,7 +63,7 @@ pub struct ZoomLevel(u8);
impl ZoomLevel {
pub fn is_root(self) -> bool {
return self.0 == 0;
self.0 == 0
}
}

244
maplibre/src/headless.rs Normal file
View File

@ -0,0 +1,244 @@
use crate::context::{MapContext, ViewState};
use crate::coords::{ViewRegion, Zoom};
use crate::error::Error;
use crate::io::tile_repository::TileRepository;
use crate::render::camera::ViewProjection;
use crate::render::graph::{Node, NodeRunError, RenderContext, RenderGraphContext, SlotInfo};
use crate::render::resource::{BufferDimensions, BufferedTextureHead, IndexEntry};
use crate::render::resource::{Head, TrackedRenderPass};
use crate::render::stages::RenderStageLabel;
use crate::render::{
create_default_render_graph, draw_graph, register_default_render_stages, RenderState,
};
use crate::schedule::{Schedule, Stage};
use crate::{
HttpClient, MapWindow, MapWindowConfig, Renderer, ScheduleMethod, Scheduler, Style, WindowSize,
};
use std::fs::File;
use std::future::Future;
use std::io::Write;
use std::iter;
use std::ops::{Deref, Range};
use std::sync::Arc;
use tokio::runtime::Handle;
use tokio::task;
use wgpu::{BufferAsyncError, BufferSlice};
pub struct HeadlessMapWindowConfig {
pub size: WindowSize,
}
impl MapWindowConfig for HeadlessMapWindowConfig {
type MapWindow = HeadlessMapWindow;
fn create(&self) -> Self::MapWindow {
Self::MapWindow { size: self.size }
}
}
pub struct HeadlessMapWindow {
size: WindowSize,
}
impl MapWindow for HeadlessMapWindow {
fn size(&self) -> WindowSize {
self.size
}
}
pub struct HeadlessMap<MWC, SM, HC>
where
MWC: MapWindowConfig,
SM: ScheduleMethod,
HC: HttpClient,
{
pub map_schedule: HeadlessMapSchedule<MWC, SM, HC>,
pub window: MWC::MapWindow,
}
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> {
&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 map_context: MapContext,
schedule: Schedule,
scheduler: Scheduler<SM>,
http_client: HC,
}
impl<MWC, SM, HC> HeadlessMapSchedule<MWC, SM, HC>
where
MWC: MapWindowConfig,
SM: ScheduleMethod,
HC: HttpClient,
{
pub fn new(
map_window_config: MWC,
window_size: WindowSize,
renderer: Renderer,
scheduler: Scheduler<SM>,
http_client: HC,
style: Style,
) -> Self {
let view_state = ViewState::new(&window_size);
let tile_repository = TileRepository::new();
let mut schedule = Schedule::default();
let mut graph = create_default_render_graph().unwrap();
let draw_graph = graph.get_sub_graph_mut(draw_graph::NAME).unwrap();
draw_graph.add_node(draw_graph::node::COPY, CopySurfaceBufferNode::default());
draw_graph
.add_node_edge(draw_graph::node::MAIN_PASS, draw_graph::node::COPY)
.unwrap();
register_default_render_stages(graph, &mut schedule);
schedule.add_stage(
RenderStageLabel::Cleanup,
WriteSurfaceBufferStage::default(),
);
Self {
map_window_config,
map_context: MapContext {
view_state,
style,
tile_repository,
renderer,
},
schedule,
scheduler,
http_client,
}
}
#[tracing::instrument(name = "update_and_redraw", skip_all)]
pub fn update_and_redraw(&mut self) -> Result<(), Error> {
self.schedule.run(&mut self.map_context);
Ok(())
}
pub fn schedule(&self) -> &Schedule {
&self.schedule
}
pub fn scheduler(&self) -> &Scheduler<SM> {
&self.scheduler
}
pub fn http_client(&self) -> &HC {
&self.http_client
}
}
/// Node which copies the contents of the GPU-side texture in [`BufferedTextureHead`] to an
/// unmapped GPU-side buffer. This buffer will be mapped in
/// [`crate::render::stages::write_surface_buffer_stage::WriteSurfaceBufferStage`].
#[derive(Default)]
pub struct CopySurfaceBufferNode {}
impl CopySurfaceBufferNode {
pub fn new() -> Self {
Self {}
}
}
impl Node for CopySurfaceBufferNode {
fn input(&self) -> Vec<SlotInfo> {
vec![]
}
fn update(&mut self, _state: &mut RenderState) {}
fn run(
&self,
_graph: &mut RenderGraphContext,
RenderContext {
command_encoder, ..
}: &mut RenderContext,
state: &RenderState,
) -> Result<(), NodeRunError> {
match state.surface.head() {
Head::Headed(_) => {}
Head::Headless(buffered_texture) => {
let size = state.surface.size();
command_encoder.copy_texture_to_buffer(
buffered_texture.texture.as_image_copy(),
wgpu::ImageCopyBuffer {
buffer: &buffered_texture.output_buffer,
layout: wgpu::ImageDataLayout {
offset: 0,
bytes_per_row: Some(
std::num::NonZeroU32::new(
buffered_texture.buffer_dimensions.padded_bytes_per_row as u32,
)
.unwrap(),
),
rows_per_image: None,
},
},
wgpu::Extent3d {
width: size.width() as u32,
height: size.height() as u32,
depth_or_array_layers: 1,
},
);
}
}
Ok(())
}
}
/// Stage which writes the current contents of the GPU/CPU buffer in [`BufferedTextureHead`]
/// to disk as PNG.
#[derive(Default)]
pub struct WriteSurfaceBufferStage {
frame: u64,
}
impl Stage for WriteSurfaceBufferStage {
fn run(
&mut self,
MapContext {
renderer: Renderer { state, device, .. },
..
}: &mut MapContext,
) {
match state.surface.head() {
Head::Headed(_) => {}
Head::Headless(buffered_texture) => {
let buffered_texture: Arc<BufferedTextureHead> = buffered_texture.clone();
let device = device.clone();
let current_frame = self.frame;
task::block_in_place(|| {
Handle::current().block_on(async {
buffered_texture
.create_png(&device, format!("frame_{}.png", current_frame).as_str())
.await;
})
});
self.frame += 1;
}
}
}
}

View File

@ -1,8 +1,7 @@
//! Handles IO related processing as well as multithreading.
use crate::coords::WorldTileCoords;
use crate::render::ShaderVertex;
use crate::tessellation::{IndexDataType, OverAlignedVertexBuffer};
use std::collections::HashSet;
use std::fmt;
pub mod scheduler;

View File

@ -5,27 +5,25 @@ use crate::render::ShaderVertex;
use crate::tessellation::{IndexDataType, OverAlignedVertexBuffer};
use downcast_rs::{impl_downcast, Downcast};
use geozero::mvt::tile;
use std::any::Any;
use std::marker::PhantomData;
use std::process::Output;
use std::sync::mpsc;
/// Processes events which happen during the pipeline execution
pub trait PipelineProcessor: Downcast {
fn tile_finished(&mut self, request_id: TileRequestID, coords: &WorldTileCoords) {}
fn layer_unavailable(&mut self, coords: &WorldTileCoords, layer_name: &str) {}
fn tile_finished(&mut self, _request_id: TileRequestID, _coords: &WorldTileCoords) {}
fn layer_unavailable(&mut self, _coords: &WorldTileCoords, _layer_name: &str) {}
fn layer_tesselation_finished(
&mut self,
coords: &WorldTileCoords,
buffer: OverAlignedVertexBuffer<ShaderVertex, IndexDataType>,
feature_indices: Vec<u32>,
layer_data: tile::Layer,
_coords: &WorldTileCoords,
_buffer: OverAlignedVertexBuffer<ShaderVertex, IndexDataType>,
_feature_indices: Vec<u32>,
_layer_data: tile::Layer,
) {
}
fn layer_indexing_finished(
&mut self,
coords: &WorldTileCoords,
geometries: Vec<IndexedGeometry<f64>>,
_coords: &WorldTileCoords,
_geometries: Vec<IndexedGeometry<f64>>,
) {
}
}
@ -181,17 +179,16 @@ mod tests {
ClosureProcessable, DataPipeline, PipelineContext, PipelineEnd, PipelineProcessor,
Processable,
};
use std::sync::mpsc;
pub struct DummyPipelineProcessor;
impl PipelineProcessor for DummyPipelineProcessor {}
fn add_one(input: u32, context: &mut PipelineContext) -> u8 {
fn add_one(input: u32, _context: &mut PipelineContext) -> u8 {
input as u8 + 1
}
fn add_two(input: u8, context: &mut PipelineContext) -> u32 {
fn add_two(input: u8, _context: &mut PipelineContext) -> u32 {
input as u32 + 2
}
@ -219,22 +216,21 @@ mod tests {
#[test]
fn test_closure() {
let mut context = PipelineContext::new(DummyPipelineProcessor);
let mut outer_value = 3;
let outer_value = 3;
// using from()
let closure = ClosureProcessable::from(|input: u8, context: &mut PipelineContext| -> u32 {
return input as u32 + 2 + outer_value;
});
let closure =
ClosureProcessable::from(|input: u8, _context: &mut PipelineContext| -> u32 {
input as u32 + 2 + outer_value
});
let output: u32 =
DataPipeline::new(closure, PipelineEnd::default()).process(5u8, &mut context);
assert_eq!(output, 10);
// with into()
let output: u32 = DataPipeline::<ClosureProcessable<_, u8, u32>, _>::new(
(|input: u8, context: &mut PipelineContext| -> u32 {
return input as u32 + 2 + outer_value;
})
.into(),
(|input: u8, _context: &mut PipelineContext| -> u32 { input as u32 + 2 + outer_value })
.into(),
PipelineEnd::<u32>::default(),
)
.process(5u8, &mut context);

View File

@ -1,7 +1,6 @@
//! Scheduling.
use std::future::Future;
use std::pin::Pin;
use crate::error::Error;

View File

@ -55,11 +55,6 @@ impl StaticTileFetcher {
#[cfg(test)]
mod tests {
use crate::style::source::TileAddressingScheme;
use crate::coords::WorldTileCoords;
use super::StaticTileFetcher;
#[cfg(all(static_tiles, not(target_arch = "wasm32")))]
#[tokio::test]

View File

@ -141,7 +141,7 @@ mod tests {
let mut context = PipelineContext::new(DummyPipelineProcessor);
let pipeline = build_vector_tile_pipeline();
let output = pipeline.process(
let _output = pipeline.process(
(
TileRequest {
coords: (0, 0, ZoomLevel::default()).into(),

View File

@ -18,7 +18,7 @@
use crate::io::scheduler::{ScheduleMethod, Scheduler};
use crate::io::source_client::HttpClient;
use crate::map_schedule::{InteractiveMapSchedule, SimpleMapSchedule};
use crate::map_schedule::InteractiveMapSchedule;
use crate::render::settings::{RendererSettings, WgpuSettings};
use crate::render::{RenderState, Renderer};
use crate::style::Style;
@ -27,6 +27,8 @@ use crate::window::{EventLoop, HeadedMapWindow, MapWindow, MapWindowConfig, Wind
pub mod context;
pub mod coords;
pub mod error;
#[cfg(feature = "headless")]
pub mod headless;
pub mod io;
// Exposed because of input handlers in maplibre-winit
pub mod map_schedule;
@ -34,6 +36,7 @@ pub mod platform;
// Exposed because of camera
pub mod render;
pub mod style;
pub mod window;
// Exposed because of doc-strings
pub mod schedule;
@ -106,27 +109,6 @@ where
}
}
pub struct HeadlessMap<MWC, SM, HC>
where
MWC: MapWindowConfig,
SM: ScheduleMethod,
HC: HttpClient,
{
map_schedule: SimpleMapSchedule<MWC, SM, HC>,
window: MWC::MapWindow,
}
impl<MWC, SM, HC> HeadlessMap<MWC, SM, HC>
where
MWC: MapWindowConfig,
SM: ScheduleMethod,
HC: HttpClient,
{
pub fn map_schedule_mut(&mut self) -> &mut SimpleMapSchedule<MWC, SM, HC> {
&mut self.map_schedule
}
}
/// Stores the map configuration before the map's state has been fully initialized.
pub struct UninitializedMap<MWC, SM, HC>
where
@ -184,7 +166,8 @@ where
}
}
pub async fn initialize_headless(self) -> HeadlessMap<MWC, SM, HC> {
#[cfg(feature = "headless")]
pub async fn initialize_headless(self) -> headless::HeadlessMap<MWC, SM, HC> {
let window = self.map_window_config.create();
let window_size = window.size();
@ -195,8 +178,8 @@ where
)
.await
.expect("Failed to initialize renderer");
HeadlessMap {
map_schedule: SimpleMapSchedule::new(
headless::HeadlessMap {
map_schedule: headless::HeadlessMapSchedule::new(
self.map_window_config,
window_size,
renderer,

View File

@ -1,61 +1,20 @@
use crate::context::{MapContext, ViewState};
use crate::error::Error;
use crate::io::geometry_index::GeometryIndex;
use crate::io::scheduler::Scheduler;
use crate::io::source_client::{HttpClient, HttpSourceClient, SourceClient};
use crate::io::source_client::{HttpClient, HttpSourceClient};
use crate::io::tile_repository::TileRepository;
use crate::io::tile_request_state::TileRequestState;
use crate::render::register_render_stages;
use crate::render::{create_default_render_graph, register_default_render_stages};
use crate::schedule::{Schedule, Stage};
use crate::stages::register_stages;
use crate::style::Style;
use crate::{
HeadedMapWindow, MapWindow, MapWindowConfig, Renderer, RendererSettings, ScheduleMethod,
WgpuSettings, WindowSize,
HeadedMapWindow, MapWindowConfig, Renderer, RendererSettings, ScheduleMethod, WgpuSettings,
WindowSize,
};
use std::marker::PhantomData;
use std::mem;
use std::sync::{mpsc, Arc, Mutex};
pub struct PrematureMapContext {
view_state: ViewState,
style: Style,
tile_repository: TileRepository,
wgpu_settings: WgpuSettings,
renderer_settings: RendererSettings,
}
pub enum EventuallyMapContext {
Full(MapContext),
Premature(PrematureMapContext),
_Uninitialized,
}
impl EventuallyMapContext {
pub fn make_full(&mut self, renderer: Renderer) {
let context = mem::replace(self, EventuallyMapContext::_Uninitialized);
match context {
EventuallyMapContext::Full(_) => {}
EventuallyMapContext::Premature(PrematureMapContext {
view_state,
style,
tile_repository,
..
}) => {
*self = EventuallyMapContext::Full(MapContext {
view_state,
style,
tile_repository,
renderer,
});
}
EventuallyMapContext::_Uninitialized => {}
}
}
}
/// Stores the state of the map, dispatches tile fetching and caching, tessellation and drawing.
pub struct InteractiveMapSchedule<MWC, SM, HC>
@ -66,7 +25,7 @@ where
{
map_window_config: MWC,
pub map_context: EventuallyMapContext,
map_context: EventuallyMapContext,
schedule: Schedule,
@ -99,7 +58,8 @@ where
let http_source_client: HttpSourceClient<HC> = HttpSourceClient::new(http_client);
register_stages(&mut schedule, http_source_client, Box::new(scheduler));
register_render_stages(&mut schedule, false).unwrap();
let graph = create_default_render_graph().unwrap();
register_default_render_stages(graph, &mut schedule);
Self {
map_window_config,
@ -157,7 +117,7 @@ where
<MWC as MapWindowConfig>::MapWindow: HeadedMapWindow,
{
if let EventuallyMapContext::Full(map_context) = &mut self.map_context {
let mut renderer = &mut map_context.renderer;
let renderer = &mut map_context.renderer;
renderer
.state
.recreate_surface::<MWC::MapWindow>(window, &renderer.instance);
@ -204,70 +164,42 @@ where
}
}
/// Stores the state of the map, dispatches tile fetching and caching, tessellation and drawing.
pub struct SimpleMapSchedule<MWC, SM, HC>
where
MWC: MapWindowConfig,
SM: ScheduleMethod,
HC: HttpClient,
{
map_window_config: MWC,
pub struct PrematureMapContext {
view_state: ViewState,
style: Style,
pub map_context: MapContext,
tile_repository: TileRepository,
schedule: Schedule,
scheduler: Scheduler<SM>,
http_client: HC,
wgpu_settings: WgpuSettings,
renderer_settings: RendererSettings,
}
impl<MWC, SM, HC> SimpleMapSchedule<MWC, SM, HC>
where
MWC: MapWindowConfig,
SM: ScheduleMethod,
HC: HttpClient,
{
pub fn new(
map_window_config: MWC,
window_size: WindowSize,
renderer: Renderer,
scheduler: Scheduler<SM>,
http_client: HC,
style: Style,
) -> Self {
let view_state = ViewState::new(&window_size);
let tile_repository = TileRepository::new();
let mut schedule = Schedule::default();
pub enum EventuallyMapContext {
Full(MapContext),
Premature(PrematureMapContext),
_Uninitialized,
}
register_render_stages(&mut schedule, true).unwrap();
impl EventuallyMapContext {
pub fn make_full(&mut self, renderer: Renderer) {
let context = mem::replace(self, EventuallyMapContext::_Uninitialized);
Self {
map_window_config,
map_context: MapContext {
match context {
EventuallyMapContext::Full(_) => {}
EventuallyMapContext::Premature(PrematureMapContext {
view_state,
style,
tile_repository,
renderer,
},
schedule,
scheduler,
http_client,
..
}) => {
*self = EventuallyMapContext::Full(MapContext {
view_state,
style,
tile_repository,
renderer,
});
}
EventuallyMapContext::_Uninitialized => {}
}
}
#[tracing::instrument(name = "update_and_redraw", skip_all)]
pub fn update_and_redraw(&mut self) -> Result<(), Error> {
self.schedule.run(&mut self.map_context);
Ok(())
}
pub fn schedule(&self) -> &Schedule {
&self.schedule
}
pub fn scheduler(&self) -> &Scheduler<SM> {
&self.scheduler
}
pub fn http_client(&self) -> &HC {
&self.http_client
}
}

View File

@ -1,7 +1,6 @@
use crate::error::Error;
use crate::ScheduleMethod;
use std::future::Future;
use std::pin::Pin;
/// Multi-threading with Tokio.
pub struct TokioScheduleMethod;

View File

@ -1,68 +0,0 @@
//! Node which copies the contents of the GPU-side texture in [`BufferedTextureHead`] to an
//! unmapped GPU-side buffer. This buffer will be mapped in
//! [`crate::render::stages::write_surface_buffer_stage::WriteSurfaceBufferStage`].
use crate::render::graph::{Node, NodeRunError, RenderContext, RenderGraphContext, SlotInfo};
use crate::render::render_commands::{DrawMasks, DrawTiles};
use crate::render::render_phase::{PhaseItem, RenderCommand};
use crate::render::resource::{Head, TrackedRenderPass};
use crate::render::util::FloatOrd;
use crate::render::Eventually::Initialized;
use crate::render::RenderState;
use std::ops::{Deref, Range};
#[derive(Default)]
pub struct CopySurfaceBufferNode {}
impl CopySurfaceBufferNode {
pub fn new() -> Self {
Self {}
}
}
impl Node for CopySurfaceBufferNode {
fn input(&self) -> Vec<SlotInfo> {
vec![]
}
fn update(&mut self, _state: &mut RenderState) {}
fn run(
&self,
_graph: &mut RenderGraphContext,
RenderContext {
command_encoder, ..
}: &mut RenderContext,
state: &RenderState,
) -> Result<(), NodeRunError> {
match state.surface.head() {
Head::Headed(_) => {}
Head::Headless(buffered_texture) => {
let size = state.surface.size();
command_encoder.copy_texture_to_buffer(
buffered_texture.texture.as_image_copy(),
wgpu::ImageCopyBuffer {
buffer: &buffered_texture.output_buffer,
layout: wgpu::ImageDataLayout {
offset: 0,
bytes_per_row: Some(
std::num::NonZeroU32::new(
buffered_texture.buffer_dimensions.padded_bytes_per_row as u32,
)
.unwrap(),
),
rows_per_image: None,
},
},
wgpu::Extent3d {
width: size.width() as u32,
height: size.height() as u32,
depth_or_array_layers: 1,
},
);
}
}
Ok(())
}
}

View File

@ -609,9 +609,9 @@ mod tests {
fn run(
&self,
graph: &mut RenderGraphContext,
render_context: &mut RenderContext,
state: &RenderState,
_graph: &mut RenderGraphContext,
_render_context: &mut RenderContext,
_state: &RenderState,
) -> Result<(), NodeRunError> {
Ok(())
}
@ -682,9 +682,9 @@ mod tests {
impl Node for MyNode {
fn run(
&self,
graph: &mut RenderGraphContext,
render_context: &mut RenderContext,
state: &RenderState,
_graph: &mut RenderGraphContext,
_render_context: &mut RenderContext,
_state: &RenderState,
) -> Result<(), NodeRunError> {
Ok(())
}

View File

@ -5,12 +5,12 @@
use crate::render::graph::{Node, NodeRunError, RenderContext, RenderGraphContext, SlotInfo};
use crate::render::render_commands::{DrawMasks, DrawTiles};
use crate::render::render_phase::{PhaseItem, RenderCommand};
use crate::render::render_phase::RenderCommand;
use crate::render::resource::TrackedRenderPass;
use crate::render::util::FloatOrd;
use crate::render::Eventually::Initialized;
use crate::render::{draw_graph, main_graph, RenderState};
use std::ops::{Deref, Range};
use crate::render::{draw_graph, RenderState};
use std::ops::Deref;
pub mod graph {
// Labels for input nodes

View File

@ -22,18 +22,15 @@ use crate::render::render_phase::RenderPhase;
use crate::render::resource::{BufferPool, Globals, IndexEntry};
use crate::render::resource::{Head, Surface};
use crate::render::resource::{Texture, TextureView};
use crate::render::settings::{RendererSettings, SurfaceType, WgpuSettings};
use crate::render::settings::{RendererSettings, WgpuSettings};
use crate::render::shaders::{ShaderFeatureStyle, ShaderLayerMetadata};
use crate::render::tile_view_pattern::{TileInView, TileShape, TileViewPattern};
use crate::render::util::Eventually;
use crate::tessellation::IndexDataType;
use crate::{HeadedMapWindow, MapWindow, MapWindowConfig};
use crate::{HeadedMapWindow, MapWindow};
use log::info;
use std::sync::Arc;
#[cfg(feature = "headless")]
// Exposed because it should be addable conditionally
pub mod copy_surface_to_buffer_node;
pub mod graph;
pub mod resource;
pub mod stages;
@ -52,8 +49,10 @@ mod util;
pub mod camera;
pub mod settings;
use crate::render::graph::{EmptyNode, RenderGraph, RenderGraphError};
use crate::render::main_pass::{MainPassDriverNode, MainPassNode};
pub use shaders::ShaderVertex;
pub use stages::register_render_stages;
pub use stages::register_default_render_stages;
pub const INDEX_FORMAT: wgpu::IndexFormat = wgpu::IndexFormat::Uint32; // Must match IndexDataType
@ -80,7 +79,7 @@ pub struct RenderState {
depth_texture: Eventually<Texture>,
multisampling_texture: Eventually<Option<Texture>>,
surface: Surface,
pub surface: Surface,
mask_phase: RenderPhase<TileInView>,
tile_phase: RenderPhase<(IndexEntry, TileShape)>,
@ -380,11 +379,7 @@ impl Renderer {
#[cfg(test)]
mod tests {
use crate::render::graph::RenderGraph;
use crate::render::graph_runner::RenderGraphRunner;
use crate::render::resource::Surface;
use crate::{MapWindow, MapWindowConfig, RenderState, Renderer, RendererSettings, WindowSize};
use log::LevelFilter;
use crate::{MapWindow, MapWindowConfig, WindowSize};
pub struct HeadlessMapWindowConfig {
size: WindowSize,
@ -411,6 +406,12 @@ mod tests {
#[cfg(not(target_arch = "wasm32"))]
#[tokio::test]
async fn test_render() {
use crate::render::graph::RenderGraph;
use crate::render::graph_runner::RenderGraphRunner;
use crate::render::resource::Surface;
use crate::{RenderState, RendererSettings};
use log::LevelFilter;
let _ = env_logger::builder()
.filter_level(LevelFilter::Trace)
.is_test(true)
@ -474,3 +475,22 @@ pub mod draw_graph {
pub const COPY: &str = "copy";
}
}
pub fn create_default_render_graph() -> Result<RenderGraph, RenderGraphError> {
let mut graph = RenderGraph::default();
let mut draw_graph = RenderGraph::default();
draw_graph.add_node(draw_graph::node::MAIN_PASS, MainPassNode::new());
let input_node_id = draw_graph.set_input(vec![]);
draw_graph.add_node_edge(input_node_id, draw_graph::node::MAIN_PASS)?;
graph.add_sub_graph(draw_graph::NAME, draw_graph);
graph.add_node(main_graph::node::MAIN_PASS_DEPENDENCIES, EmptyNode);
graph.add_node(main_graph::node::MAIN_PASS_DRIVER, MainPassDriverNode);
graph.add_node_edge(
main_graph::node::MAIN_PASS_DEPENDENCIES,
main_graph::node::MAIN_PASS_DRIVER,
)?;
Ok(graph)
}

View File

@ -1,7 +1,5 @@
use crate::render::resource::TrackedRenderPass;
use crate::RenderState;
use std::collections::HashMap;
use std::{any::TypeId, fmt::Debug, hash::Hash};
/// A draw function which is used to draw a specific [`PhaseItem`].
///

View File

@ -5,9 +5,8 @@ use crate::render::resource::texture::TextureView;
use crate::render::settings::RendererSettings;
use crate::render::util::HasChanged;
use crate::window::HeadedMapWindow;
use crate::{MapWindow, MapWindowConfig, WindowSize};
use std::fs::File;
use std::io::Write;
use crate::{MapWindow, WindowSize};
use std::mem::size_of;
use std::sync::Arc;
@ -69,6 +68,8 @@ impl BufferedTextureHead {
png_output_path: &str,
// device: &wgpu::Device,
) {
use std::fs::File;
use std::io::Write;
// Note that we're not calling `.await` here.
let buffer_slice = self.output_buffer.slice(..);
let buffer_future = buffer_slice.map_async(wgpu::MapMode::Read, |_| ());

View File

@ -1,19 +1,11 @@
//! Extracts data from the current state.
use crate::context::MapContext;
use crate::coords::{ViewRegion, Zoom};
use crate::io::tile_repository::TileRepository;
use crate::render::camera::ViewProjection;
use crate::render::render_phase::RenderPhase;
use crate::render::resource::IndexEntry;
use crate::render::shaders::{
ShaderCamera, ShaderFeatureStyle, ShaderGlobals, ShaderLayerMetadata, Vec4f32,
};
use crate::render::tile_view_pattern::TileInView;
use crate::coords::ViewRegion;
use crate::render::util::Eventually::Initialized;
use crate::schedule::Stage;
use crate::{RenderState, Renderer, Style};
use std::iter;
use crate::{RenderState, Renderer};
#[derive(Default)]
pub struct ExtractStage;
@ -27,8 +19,8 @@ impl Stage for ExtractStage {
Renderer {
state:
RenderState {
mask_phase,
tile_phase,
mask_phase: _,
tile_phase: _,
tile_view_pattern,
buffer_pool,
..

View File

@ -1,9 +1,9 @@
//! Executes the [`RenderGraph`] current render graph.
use crate::context::MapContext;
use crate::render::graph::{EmptyNode, RenderGraph};
use crate::render::graph::RenderGraph;
use crate::render::graph_runner::RenderGraphRunner;
use crate::render::main_pass::{MainPassDriverNode, MainPassNode};
use crate::render::util::Eventually::Initialized;
use crate::schedule::Stage;
use crate::Renderer;

View File

@ -2,13 +2,13 @@
use crate::context::MapContext;
use crate::multi_stage;
use crate::render::graph::{EmptyNode, RenderGraph, RenderGraphError};
use crate::render::main_pass::{MainPassDriverNode, MainPassNode};
use crate::render::graph::RenderGraph;
use crate::render::stages::extract_stage::ExtractStage;
use crate::render::stages::phase_sort_stage::PhaseSortStage;
use crate::render::stages::queue_stage::QueueStage;
use crate::render::{draw_graph, main_graph};
use crate::schedule::{MultiStage, Schedule, Stage, StageLabel};
use crate::schedule::{Schedule, Stage, StageLabel};
use graph_runner_stage::GraphRunnerStage;
use resource_stage::ResourceStage;
use upload_stage::UploadStage;
@ -20,10 +20,6 @@ mod queue_stage;
mod resource_stage;
mod upload_stage;
#[cfg(feature = "headless")]
// Exposed because it should be addable conditionally
pub mod write_surface_buffer_stage;
/// The labels of the default App rendering stages.
#[derive(Debug, Hash, PartialEq, Eq, Clone)]
pub enum RenderStageLabel {
@ -60,45 +56,9 @@ multi_stage!(
upload: UploadStage
);
pub fn register_render_stages(
schedule: &mut Schedule,
headless: bool,
) -> Result<(), RenderGraphError> {
let mut graph = RenderGraph::default();
let mut draw_graph = RenderGraph::default();
draw_graph.add_node(draw_graph::node::MAIN_PASS, MainPassNode::new());
let input_node_id = draw_graph.set_input(vec![]);
draw_graph.add_node_edge(input_node_id, draw_graph::node::MAIN_PASS)?;
#[cfg(feature = "headless")]
if headless {
use crate::render::copy_surface_to_buffer_node::CopySurfaceBufferNode;
draw_graph.add_node(draw_graph::node::COPY, CopySurfaceBufferNode::default());
draw_graph.add_node_edge(draw_graph::node::MAIN_PASS, draw_graph::node::COPY)?;
}
graph.add_sub_graph(draw_graph::NAME, draw_graph);
graph.add_node(main_graph::node::MAIN_PASS_DEPENDENCIES, EmptyNode);
graph.add_node(main_graph::node::MAIN_PASS_DRIVER, MainPassDriverNode);
graph.add_node_edge(
main_graph::node::MAIN_PASS_DEPENDENCIES,
main_graph::node::MAIN_PASS_DRIVER,
)?;
pub fn register_default_render_stages(graph: RenderGraph, schedule: &mut Schedule) {
schedule.add_stage(RenderStageLabel::Prepare, PrepareStage::default());
schedule.add_stage(RenderStageLabel::Queue, QueueStage::default());
schedule.add_stage(RenderStageLabel::PhaseSort, PhaseSortStage::default());
schedule.add_stage(RenderStageLabel::Render, GraphRunnerStage::new(graph));
#[cfg(feature = "headless")]
if headless {
use crate::render::stages::write_surface_buffer_stage::WriteSurfaceBufferStage;
schedule.add_stage(
RenderStageLabel::Cleanup,
WriteSurfaceBufferStage::default(),
);
}
Ok(())
}

View File

@ -1,19 +1,11 @@
//! Sorts items of the [RenderPhases](RenderPhase).
use crate::context::MapContext;
use crate::coords::{ViewRegion, Zoom};
use crate::io::tile_repository::TileRepository;
use crate::render::camera::ViewProjection;
use crate::render::render_phase::RenderPhase;
use crate::render::resource::IndexEntry;
use crate::render::shaders::{
ShaderCamera, ShaderFeatureStyle, ShaderGlobals, ShaderLayerMetadata, Vec4f32,
};
use crate::render::tile_view_pattern::TileInView;
use crate::render::util::Eventually::Initialized;
use crate::schedule::Stage;
use crate::{RenderState, Renderer, Style};
use std::iter;
use crate::Renderer;
#[derive(Default)]
pub struct PhaseSortStage;

View File

@ -1,18 +1,13 @@
//! Queues [PhaseItems](crate::render::render_phase::PhaseItem) for rendering.
use crate::context::MapContext;
use crate::coords::{ViewRegion, Zoom};
use crate::io::tile_repository::TileRepository;
use crate::render::camera::ViewProjection;
use crate::render::resource::IndexEntry;
use crate::render::shaders::{
ShaderCamera, ShaderFeatureStyle, ShaderGlobals, ShaderLayerMetadata, Vec4f32,
};
use crate::render::tile_view_pattern::TileInView;
use crate::render::util::Eventually::Initialized;
use crate::schedule::Stage;
use crate::{RenderState, Renderer, Style};
use std::iter;
use crate::{RenderState, Renderer};
#[derive(Default)]
pub struct QueueStage;
@ -22,7 +17,7 @@ impl Stage for QueueStage {
fn run(
&mut self,
MapContext {
view_state,
view_state: _,
renderer:
Renderer {
state:

View File

@ -1,17 +1,17 @@
//! Prepares GPU-owned resources by initializing them if they are uninitialized or out-of-date.
use crate::context::MapContext;
use crate::platform::MIN_BUFFER_SIZE;
use crate::render::resource::Texture;
use crate::render::resource::{BackingBufferDescriptor, BufferPool};
use crate::render::resource::{Globals, RenderPipeline};
use crate::render::shaders;
use crate::render::shaders::{Shader, ShaderGlobals, ShaderTileMetadata};
use crate::render::shaders::{Shader, ShaderTileMetadata};
use crate::render::tile_pipeline::TilePipeline;
use crate::render::tile_view_pattern::TileViewPattern;
use crate::schedule::Stage;
use crate::Renderer;
use std::cmp;
use std::mem::size_of;
pub const TILE_VIEW_SIZE: wgpu::BufferAddress = 32;

View File

@ -1,14 +1,14 @@
//! Uploads data to the GPU which is needed for rendering.
use crate::context::MapContext;
use crate::coords::{ViewRegion, Zoom};
use crate::coords::ViewRegion;
use crate::io::tile_repository::{StoredLayer, TileRepository};
use crate::render::camera::ViewProjection;
use crate::render::resource::IndexEntry;
use crate::render::shaders::{
ShaderCamera, ShaderFeatureStyle, ShaderGlobals, ShaderLayerMetadata, Vec4f32,
};
use crate::render::tile_view_pattern::TileInView;
use crate::render::util::Eventually::Initialized;
use crate::schedule::Stage;
use crate::{RenderState, Renderer, Style};

View File

@ -1,60 +0,0 @@
//! Stage which writes the current contents of the GPU/CPU buffer in [`BufferedTextureHead`]
//! to disk as PNG.
use crate::context::MapContext;
use crate::coords::{ViewRegion, Zoom};
use crate::io::tile_repository::TileRepository;
use crate::render::camera::ViewProjection;
use crate::render::render_phase::RenderPhase;
use crate::render::resource::{BufferDimensions, BufferedTextureHead, Head, IndexEntry};
use crate::render::shaders::{
ShaderCamera, ShaderFeatureStyle, ShaderGlobals, ShaderLayerMetadata, Vec4f32,
};
use crate::render::tile_view_pattern::TileInView;
use crate::render::util::Eventually::Initialized;
use crate::schedule::Stage;
use crate::{RenderState, Renderer, Style};
use std::fs::File;
use std::future::Future;
use std::io::Write;
use std::iter;
use std::ops::Deref;
use std::sync::Arc;
use tokio::runtime::Handle;
use tokio::task;
use wgpu::{BufferAsyncError, BufferSlice};
#[derive(Default)]
pub struct WriteSurfaceBufferStage {
frame: u64,
}
impl Stage for WriteSurfaceBufferStage {
fn run(
&mut self,
MapContext {
renderer: Renderer { state, device, .. },
..
}: &mut MapContext,
) {
match state.surface.head() {
Head::Headed(_) => {}
Head::Headless(buffered_texture) => {
let buffered_texture: Arc<BufferedTextureHead> = buffered_texture.clone();
let device = device.clone();
let current_frame = self.frame;
task::block_in_place(|| {
Handle::current().block_on(async {
buffered_texture
.create_png(&device, format!("frame_{}.png", current_frame).as_str())
.await;
})
});
self.frame += 1;
}
}
}
}

View File

@ -68,13 +68,7 @@ where
#[tracing::instrument(name = "reinitialize", skip_all)]
pub fn reinitialize(&mut self, f: impl FnOnce() -> T, criteria: &T::Criteria) {
let should_replace = match &self {
Eventually::Initialized(current) => {
if current.has_changed(criteria) {
true
} else {
false
}
}
Eventually::Initialized(current) => current.has_changed(criteria),
Eventually::Uninitialized => true,
};

View File

@ -1,10 +1,8 @@
use crate::context::MapContext;
use crate::define_label;
use downcast_rs::{impl_downcast, Downcast};
use std::any::Any;
use std::collections::HashMap;
use std::fmt::Debug;
use std::rc::Rc;
pub struct NopStage;

View File

@ -1,18 +1,14 @@
use crate::coords::{WorldCoords, WorldTileCoords, Zoom};
use crate::error::Error;
use crate::io::geometry_index::{GeometryIndex, IndexedGeometry};
use crate::io::pipeline::PipelineContext;
use crate::io::pipeline::Processable;
use crate::io::tile_pipelines::build_vector_tile_pipeline;
use crate::coords::WorldTileCoords;
use crate::io::tile_repository::StoredLayer;
use crate::io::tile_request_state::TileRequestState;
use crate::io::{TileRequest, TileRequestID};
use crate::io::TileRequestID;
use crate::render::ShaderVertex;
use crate::stages::HeadedPipelineProcessor;
use crate::tessellation::{IndexDataType, OverAlignedVertexBuffer};
use geozero::mvt::tile;
use std::fmt;
use std::sync::{mpsc, Arc, Mutex};
use std::sync::mpsc;
pub type MessageSender = mpsc::Sender<TessellateMessage>;
pub type MessageReceiver = mpsc::Receiver<TessellateMessage>;

View File

@ -4,12 +4,12 @@ use crate::coords::ZoomLevel;
use crate::coords::{WorldCoords, WorldTileCoords, Zoom};
use crate::error::Error;
use crate::io::geometry_index::GeometryIndex;
use crate::io::geometry_index::{IndexProcessor, IndexedGeometry, TileIndex};
use crate::io::geometry_index::{IndexedGeometry, TileIndex};
use crate::io::pipeline::Processable;
use crate::io::pipeline::{PipelineContext, PipelineProcessor};
use crate::io::source_client::{HttpSourceClient, SourceClient};
use crate::io::source_client::HttpSourceClient;
use crate::io::tile_pipelines::build_vector_tile_pipeline;
use crate::io::tile_repository::StoredLayer;
use crate::io::tile_request_state::TileRequestState;
use crate::io::{TileRequest, TileRequestID};
use crate::render::ShaderVertex;
@ -19,15 +19,14 @@ use crate::stages::message::{
TileTessellateMessage,
};
use crate::stages::populate_tile_store_stage::PopulateTileStore;
use crate::tessellation::zero_tessellator::ZeroTessellator;
use crate::tessellation::{IndexDataType, OverAlignedVertexBuffer};
use crate::{HttpClient, ScheduleMethod, Scheduler};
use geozero::mvt::tile;
use geozero::GeozeroDatasource;
use prost::Message;
use request_stage::RequestStage;
use std::collections::HashSet;
use std::fmt;
use std::sync::{mpsc, Arc, Mutex};
mod message;
@ -110,7 +109,7 @@ impl PipelineProcessor for HeadedPipelineProcessor {
geometries: Vec<IndexedGeometry<f64>>,
) {
if let Ok(mut geometry_index) = self.state.geometry_index.lock() {
geometry_index.index_tile(&coords, TileIndex::Linear { list: geometries })
geometry_index.index_tile(coords, TileIndex::Linear { list: geometries })
}
}
}

View File

@ -4,7 +4,6 @@ use super::{MessageReceiver, SharedThreadState, TessellateMessage, TileTessellat
use crate::context::MapContext;
use crate::io::tile_repository::StoredLayer;
use crate::schedule::Stage;
use std::sync::mpsc;
pub struct PopulateTileStore {
shared_thread_state: SharedThreadState,

View File

@ -1,7 +1,7 @@
//! Utilities for the window system.
use crate::{HttpClient, InteractiveMapSchedule, ScheduleMethod};
use raw_window_handle::{HasRawWindowHandle, RawWindowHandle};
use raw_window_handle::HasRawWindowHandle;
/// Window of a certain [`WindowSize`]. This can either be a proper window or a headless one.
pub trait MapWindow {

View File

@ -4,7 +4,7 @@ use crate::platform::schedule_method::WebWorkerPoolScheduleMethod;
use maplibre::io::scheduler::Scheduler;
use maplibre::MapBuilder;
use maplibre_winit::winit::{WinitMapWindow, WinitMapWindowConfig};
use maplibre_winit::winit::WinitMapWindowConfig;
use std::panic;
use wasm_bindgen::prelude::*;

View File

@ -1,5 +1,4 @@
use std::future::Future;
use std::pin::Pin;
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;