Add support for wasm

This commit is contained in:
Maximilian Ammann 2021-12-02 18:37:32 +01:00
parent 2de2203ded
commit 7c70ab0ce5
12 changed files with 267 additions and 1125 deletions

1199
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -7,25 +7,30 @@ resolver = "2"
[features]
[target.'cfg(target_arch = "wasm32")'.dependencies]
console_error_panic_hook = "0.1"
winit = { version = "0.25", features = ["web-sys"] }
web-sys = "0.3"
js-sys = "0.3"
wasm-bindgen = "0.2"
wasm-bindgen-futures = "0.4"
console_log = { version = "0.2", features = ["color"] }
[dependencies]
# Futures
tokio = { version = "1", features = ["full"] }
futures = "0.3.5"
pollster = "0.2"
# Wanted by lyon experiment
# Vector riles
vector-tile = { path = "libs/vector_tile" }
geo = "0.18.0"
geo-types = "0.7"
proj = "0.24"
mbutiles = "0.1.1"
# proj = "0.24" FIXME: Incompatible with wasm
# mbutiles = "0.1.1" FIXME: Incompatible with wasm
# Rendering
winit = "0.25"
winit = { version = "0.25" }
wgpu = "0.11"
lyon = { version = "0.17", features = ["extra"] }
lyon_path = "0.17"
@ -40,7 +45,7 @@ hexdump = "0.1"
bytemuck = "1.2.0"
# Network
reqwest = { version = "0.11", features = ["gzip"] }
# reqwest = { version = "0.11", features = ["gzip"] } FIXME: Incompatible with wasm
# Serialization
serde = { version = "1.0", features = ["derive"] }

View File

@ -1,3 +1,3 @@
[toolchain]
channel = "1.56.1"
targets = [ "x86_64-unknown-linux-gnu" ]
targets = [ "wasm32-unknown-unknown", "x86_64-unknown-linux-gnu" ]

View File

@ -1,5 +1,43 @@
use std::time::{Duration, Instant};
#[cfg(target_arch = "wasm32")]
use js_sys;
use log::{info, trace};
#[cfg(target_arch = "wasm32")]
pub struct FPSMeter {
start: f64,
next_report: f64,
frame_count: u32,
pub time_secs: f64,
}
#[cfg(target_arch = "wasm32")]
impl FPSMeter {
pub fn new() -> Self {
let start = (js_sys::Date::now() / 1000.0) as f64;
Self {
start,
next_report: start + 1.0,
frame_count: 0,
time_secs: 0.0,
}
}
pub fn update_and_print(&mut self) {
self.frame_count += 1;
let now = (js_sys::Date::now() / 1000.0) as f64;
self.time_secs = now - self.start;
if now >= self.next_report {
info!("{} FPS", self.frame_count);
self.frame_count = 0;
self.next_report = now + 1.0;
}
}
}
#[cfg(not(target_arch = "wasm32"))]
pub struct FPSMeter {
start: Instant,
next_report: Instant,
@ -7,6 +45,7 @@ pub struct FPSMeter {
pub time_secs: f32,
}
#[cfg(not(target_arch = "wasm32"))]
impl FPSMeter {
pub fn new() -> Self {
let start = Instant::now();
@ -23,7 +62,7 @@ impl FPSMeter {
let now = Instant::now();
self.time_secs = (now - self.start).as_secs_f32();
if now >= self.next_report {
println!("{} FPS", self.frame_count);
info!("{} FPS", self.frame_count);
self.frame_count = 0;
self.next_report = now + Duration::from_secs(1);
}

View File

@ -1,6 +1,7 @@
use log::info;
use winit::event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent};
use winit::event_loop::{ControlFlow, EventLoop};
use winit::window::Window;
use winit::window::{WindowBuilder};
use crate::state::State;
@ -11,20 +12,42 @@ mod shader;
mod shader_ffi;
mod state;
mod tesselation;
mod tile_downloader;
mod texture;
fn main() {
env_logger::init();
println!("== wgpu example ==");
println!("Controls:");
println!(" Arrow keys: scrolling");
println!(" PgUp/PgDown: zoom in/out");
println!(" a/z: increase/decrease the stroke width");
#[cfg(target_arch = "wasm32")]
mod web;
mod platform_constants;
async fn setup() {
info!("== mapr ==");
info!("Controls:");
info!(" Arrow keys: scrolling");
info!(" PgUp/PgDown: zoom in/out");
info!(" a/z: increase/decrease the stroke width");
let event_loop = EventLoop::new();
let window = Window::new(&event_loop).unwrap();
let mut state = pollster::block_on(State::new(&window));
let window = WindowBuilder::new()
.with_title("A fantastic window!")
.build(&event_loop)
.unwrap();
#[cfg(target_arch = "wasm32")]
{
use winit::platform::web::WindowExtWebSys;
let canvas = window.canvas();
let window = web_sys::window().unwrap();
let document = window.document().unwrap();
let body = document.body().unwrap();
body.append_child(&canvas)
.expect("Append canvas to HTML body");
}
//let mut state = pollster::block_on(State::new(&window));
let mut state = State::new(&window).await;
window.request_redraw();
@ -78,3 +101,9 @@ fn main() {
}
});
}
fn main() {
env_logger::init_from_env(env_logger::Env::default().default_filter_or("info"));
pollster::block_on(setup());
}

View File

@ -0,0 +1,5 @@
#[cfg(target_arch = "wasm32")]
pub const COLOR_TEXTURE_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Bgra8Unorm;
#[cfg(not(target_arch = "wasm32"))]
pub const COLOR_TEXTURE_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Bgra8UnormSrgb;

View File

@ -1,4 +1,5 @@
use wgpu::{ColorTargetState, FragmentState, ShaderModule, ShaderModuleDescriptor, VertexAttribute, VertexBufferLayout, VertexState};
use crate::platform_constants::COLOR_TEXTURE_FORMAT;
use crate::shader_ffi::GpuVertex;
@ -27,7 +28,7 @@ const MAP_VERTEX_SHADER_BUFFERS: [VertexBufferLayout; 1] = [wgpu::VertexBufferLa
}];
const MAP_VERTEX_COLOR_TARGETS: [ColorTargetState; 1] = [wgpu::ColorTargetState {
format: wgpu::TextureFormat::Bgra8UnormSrgb,
format: COLOR_TEXTURE_FORMAT,
blend: None,
write_mask: wgpu::ColorWrites::ALL,
}];

View File

@ -1,8 +1,9 @@
use std::io::Cursor;
use std::ops::Range;
use lyon::math::Vector;
use lyon::tessellation::VertexBuffers;
use vector_tile::parse_tile;
use vector_tile::{parse_tile, parse_tile_reader};
use wgpu::util::DeviceExt;
use winit::dpi::PhysicalSize;
use winit::event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent};
@ -12,6 +13,7 @@ use winit::window::Window;
use crate::fps_meter::FPSMeter;
use crate::multisampling::create_multisampled_framebuffer;
use crate::piplines::*;
use crate::platform_constants::COLOR_TEXTURE_FORMAT;
use crate::shader::*;
use crate::shader_ffi::*;
use crate::tesselation::{RustLogo, Tesselated};
@ -72,6 +74,8 @@ pub struct State {
scene: SceneParams,
}
const TEST_TILES: &[u8] = include_bytes!("../test-data/12-2176-1425.pbf");
impl State {
pub async fn new(window: &Window) -> Self {
let sample_count = 4;
@ -83,7 +87,8 @@ impl State {
let mut geometry: VertexBuffers<GpuVertex, u16> = VertexBuffers::new();
let (stroke_range, fill_range) = if true {
let tile = parse_tile("test-data/12-2176-1425.pbf").expect("failed loading tile");
//let tile = parse_tile("test-data/12-2176-1425.pbf").expect("failed loading tile");
let tile = parse_tile_reader(&mut Cursor::new(TEST_TILES));
(
0..tile.tesselate_stroke(&mut geometry, stroke_prim_id),
0..0,
@ -247,7 +252,7 @@ impl State {
let surface_config = wgpu::SurfaceConfiguration {
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
format: wgpu::TextureFormat::Bgra8UnormSrgb,
format: COLOR_TEXTURE_FORMAT,
width: size.width,
height: size.height,
present_mode: wgpu::PresentMode::Mailbox,
@ -255,7 +260,8 @@ impl State {
surface.configure(&device, &surface_config);
let depth_texture = Texture::create_depth_texture(&device, &surface_config, "depth_texture", sample_count);
let depth_texture =
Texture::create_depth_texture(&device, &surface_config, "depth_texture", sample_count);
let multisampled_render_target = if sample_count > 1 {
Some(create_multisampled_framebuffer(
@ -289,7 +295,7 @@ impl State {
indices_uniform_buffer,
fps_meter: FPSMeter::new(),
stroke_range,
cpu_primitives
cpu_primitives,
}
}
@ -301,7 +307,12 @@ impl State {
self.surface.configure(&self.device, &self.surface_config);
// Re-configure depth buffer
self.depth_texture = Texture::create_depth_texture(&self.device, &self.surface_config, "depth_texture", self.sample_count);
self.depth_texture = Texture::create_depth_texture(
&self.device,
&self.surface_config,
"depth_texture",
self.sample_count,
);
// Re-configure multi-sampling buffer
self.multisampled_render_target = if self.sample_count > 1 {
@ -364,9 +375,9 @@ impl State {
scene.target_stroke_width *= 0.8;
true
}
_key => false
_key => false,
},
_evt => false
_evt => false,
};
found
@ -390,10 +401,7 @@ impl State {
&self.globals_uniform_buffer,
0,
bytemuck::cast_slice(&[Globals {
resolution: [
self.size.width as f32,
self.size.height as f32,
],
resolution: [self.size.width as f32, self.size.height as f32],
zoom: scene.zoom,
scroll_offset: scene.scroll.to_array(),
_pad: 0.0,
@ -465,8 +473,7 @@ impl State {
pub fn update(&mut self) {
let scene = &mut self.scene;
let time_secs = self.fps_meter.time_secs;
let time_secs = self.fps_meter.time_secs as f32;
scene.zoom += (scene.target_zoom - scene.zoom) / 3.0;
scene.scroll = scene.scroll + (scene.target_scroll - scene.scroll) / 3.0;

6
src/web/console.rs Normal file
View File

@ -0,0 +1,6 @@
extern crate console_error_panic_hook;
use std::panic;
pub fn init_console_error_panic_hook() {
panic::set_hook(Box::new(console_error_panic_hook::hook));
}

14
src/web/mod.rs Normal file
View File

@ -0,0 +1,14 @@
use wasm_bindgen::prelude::wasm_bindgen;
use log::{Level, warn};
mod console;
#[wasm_bindgen(start)]
pub fn run() {
console_log::init_with_level(Level::Info).expect("error initializing log");
console::init_console_error_panic_hook();
wasm_bindgen_futures::spawn_local(async {
super::setup().await;
});
}

3
web/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
*.ts
*.js
*.wasm

12
web/mapr.html Normal file
View File

@ -0,0 +1,12 @@
<html lang="en-US">
<head>
<title>mapr Demo</title>
</head>
<body>
<script type="module">
import init from "./mapr.js";
init();
</script>
</body>
</html>