mirror of
https://github.com/maplibre/maplibre-rs.git
synced 2025-12-08 19:05:57 +00:00
Add script which works for webgpu and webgl
This commit is contained in:
parent
bf52ad1d8e
commit
4a90df93fb
23
README.md
23
README.md
@ -57,13 +57,34 @@ TODO
|
||||
|
||||
## Building
|
||||
|
||||
Now, to build the project:
|
||||
Now, to clone the project:
|
||||
|
||||
```bash
|
||||
git clone git@github.com/maxammann/mapr
|
||||
```
|
||||
|
||||
and then build it for running on a desktop:
|
||||
|
||||
```bash
|
||||
cargo build
|
||||
```
|
||||
|
||||
### Target: WebGPU
|
||||
|
||||
```bash
|
||||
tools/build-web
|
||||
cd web
|
||||
python3 -m http.server
|
||||
```
|
||||
|
||||
### Target: WebGL
|
||||
|
||||
```bash
|
||||
tools/build-web -g
|
||||
cd web
|
||||
python3 -m http.server
|
||||
```
|
||||
|
||||
## Running on Linux
|
||||
|
||||
Fuzz using three clients:
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
[toolchain]
|
||||
channel = "1.56.1"
|
||||
channel = "1.57"
|
||||
targets = [ "wasm32-unknown-unknown", "x86_64-unknown-linux-gnu" ]
|
||||
|
||||
38
src/main.rs
38
src/main.rs
@ -1,7 +1,7 @@
|
||||
use log::info;
|
||||
use winit::event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent};
|
||||
use winit::event_loop::{ControlFlow, EventLoop};
|
||||
use winit::window::{WindowBuilder};
|
||||
use winit::window::{Window, WindowBuilder};
|
||||
|
||||
use crate::state::State;
|
||||
|
||||
@ -14,43 +14,19 @@ mod state;
|
||||
mod tesselation;
|
||||
mod texture;
|
||||
|
||||
mod platform_constants;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
mod web;
|
||||
mod platform_constants;
|
||||
|
||||
async fn setup() {
|
||||
async fn setup(window: Window, event_loop: EventLoop<()>) {
|
||||
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 = 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();
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
match event {
|
||||
Event::WindowEvent {
|
||||
@ -105,5 +81,11 @@ async fn setup() {
|
||||
fn main() {
|
||||
env_logger::init_from_env(env_logger::Env::default().default_filter_or("info"));
|
||||
|
||||
pollster::block_on(setup());
|
||||
let event_loop = EventLoop::new();
|
||||
let window = WindowBuilder::new()
|
||||
.with_title("A fantastic window!")
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
pollster::block_on(setup(window, event_loop));
|
||||
}
|
||||
|
||||
@ -21,7 +21,6 @@ pub fn create_map_render_pipeline_description<'a>(
|
||||
clamp_depth: false,
|
||||
conservative: false,
|
||||
},
|
||||
/*depth_stencil: None,*/
|
||||
depth_stencil: Some(wgpu::DepthStencilState {
|
||||
format: wgpu::TextureFormat::Depth32Float,
|
||||
depth_write_enabled: true,
|
||||
|
||||
@ -1,7 +1,13 @@
|
||||
|
||||
// WebGPU
|
||||
#[cfg(all(target_arch = "wasm32", not(feature = "web-webgl")))]
|
||||
pub const COLOR_TEXTURE_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Bgra8Unorm;
|
||||
// WebGL
|
||||
#[cfg(all(target_arch = "wasm32", feature = "web-webgl"))]
|
||||
pub const COLOR_TEXTURE_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Rgba8UnormSrgb;
|
||||
// Vulkan/Metal/OpenGL
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub const COLOR_TEXTURE_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Bgra8UnormSrgb;
|
||||
|
||||
// FIXME: This limit is enforced by WebGL. Actually this makes sense!
|
||||
// This is usually achieved by _pad attributes in shader_ffi.rs
|
||||
pub const MIN_BUFFER_SIZE: u64 = 32;
|
||||
|
||||
@ -1,10 +1,25 @@
|
||||
type Vec2f32 = [f32; 2];
|
||||
type Vec4f32 = [f32; 4];
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Globals {
|
||||
pub resolution: [f32; 2],
|
||||
pub scroll_offset: [f32; 2],
|
||||
pub resolution: Vec2f32,
|
||||
pub scroll_offset: Vec2f32,
|
||||
pub zoom: f32,
|
||||
pub _pad: f32,
|
||||
_pad1: u32, // _padX aligns it to 8 bytes = AlignOf(Vec2f32=vec2<f32>):
|
||||
// https://gpuweb.github.io/gpuweb/wgsl/#alignment-and-size
|
||||
}
|
||||
|
||||
impl Globals {
|
||||
pub fn new(resolution: Vec2f32, scroll_offset: Vec2f32, zoom: f32) -> Self {
|
||||
Self {
|
||||
resolution,
|
||||
scroll_offset,
|
||||
zoom,
|
||||
_pad1: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl bytemuck::Pod for Globals {}
|
||||
@ -13,37 +28,65 @@ unsafe impl bytemuck::Zeroable for Globals {}
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct GpuVertex {
|
||||
pub position: [f32; 2],
|
||||
pub normal: [f32; 2],
|
||||
pub position: Vec2f32,
|
||||
pub normal: Vec2f32,
|
||||
pub prim_id: u32,
|
||||
_pad1: u32, // _padX aligns it to 8 bytes = AlignOf(Vec2f32=vec2<f32>):
|
||||
// https://gpuweb.github.io/gpuweb/wgsl/#alignment-and-size
|
||||
}
|
||||
|
||||
impl GpuVertex {
|
||||
pub fn new(position: Vec2f32, normal: Vec2f32, prim_id: u32) -> Self {
|
||||
Self {
|
||||
position,
|
||||
normal,
|
||||
prim_id,
|
||||
_pad1: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl bytemuck::Pod for GpuVertex {}
|
||||
unsafe impl bytemuck::Zeroable for GpuVertex {}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Primitive {
|
||||
pub color: [f32; 4],
|
||||
pub translate: [f32; 2],
|
||||
pub color: Vec4f32,
|
||||
pub translate: Vec2f32,
|
||||
pub z_index: i32,
|
||||
pub width: f32,
|
||||
pub angle: f32,
|
||||
pub scale: f32,
|
||||
pub _pad1: i32,
|
||||
pub _pad2: i32,
|
||||
_pad1: u32, // _padX aligns it to 16 bytes = AlignOf(Vec4f32/vec4<f32>):
|
||||
_pad2: u32, // https://gpuweb.github.io/gpuweb/wgsl/#alignment-and-size
|
||||
}
|
||||
|
||||
impl Default for Primitive {
|
||||
fn default() -> Self {
|
||||
Primitive::new([0.0; 4], [0.0; 2], 0, 0.0, 0.0, 1.0)
|
||||
}
|
||||
}
|
||||
impl Primitive {
|
||||
pub const DEFAULT: Self = Primitive {
|
||||
color: [0.0; 4],
|
||||
translate: [0.0; 2],
|
||||
z_index: 0,
|
||||
width: 0.0,
|
||||
angle: 0.0,
|
||||
scale: 1.0,
|
||||
_pad1: 0,
|
||||
_pad2: 0,
|
||||
};
|
||||
pub fn new(
|
||||
color: Vec4f32,
|
||||
translate: Vec2f32,
|
||||
z_index: i32,
|
||||
width: f32,
|
||||
angle: f32,
|
||||
scale: f32,
|
||||
) -> Self {
|
||||
Self {
|
||||
color,
|
||||
translate,
|
||||
z_index,
|
||||
width,
|
||||
angle,
|
||||
scale,
|
||||
_pad1: Default::default(),
|
||||
_pad2: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl bytemuck::Pod for Primitive {}
|
||||
@ -54,5 +97,6 @@ unsafe impl bytemuck::Zeroable for Primitive {}
|
||||
pub struct BgPoint {
|
||||
pub point: [f32; 2],
|
||||
}
|
||||
|
||||
unsafe impl bytemuck::Pod for BgPoint {}
|
||||
unsafe impl bytemuck::Zeroable for BgPoint {}
|
||||
|
||||
57
src/state.rs
57
src/state.rs
@ -1,3 +1,4 @@
|
||||
use std::cmp;
|
||||
use std::io::Cursor;
|
||||
use std::ops::Range;
|
||||
|
||||
@ -13,7 +14,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::platform_constants::{COLOR_TEXTURE_FORMAT, MIN_BUFFER_SIZE};
|
||||
use crate::shader::*;
|
||||
use crate::shader_ffi::*;
|
||||
use crate::tesselation::{RustLogo, Tesselated};
|
||||
@ -104,29 +105,22 @@ impl State {
|
||||
|
||||
let mut cpu_primitives = Vec::with_capacity(PRIM_BUFFER_LEN);
|
||||
for _ in 0..PRIM_BUFFER_LEN {
|
||||
cpu_primitives.push(Primitive {
|
||||
color: [1.0, 0.0, 0.0, 1.0],
|
||||
z_index: 0,
|
||||
width: 0.0,
|
||||
translate: [0.0, 0.0],
|
||||
angle: 0.0,
|
||||
..Primitive::DEFAULT
|
||||
});
|
||||
cpu_primitives.push(Primitive::new(
|
||||
[1.0, 0.0, 0.0, 1.0],
|
||||
[0.0, 0.0],
|
||||
0,
|
||||
0.0,
|
||||
0.0,
|
||||
1.0,
|
||||
));
|
||||
}
|
||||
|
||||
// Stroke primitive
|
||||
cpu_primitives[stroke_prim_id as usize] = Primitive {
|
||||
color: [0.0, 0.0, 0.0, 1.0],
|
||||
z_index: 3,
|
||||
width: 1.0,
|
||||
..Primitive::DEFAULT
|
||||
};
|
||||
cpu_primitives[stroke_prim_id as usize] =
|
||||
Primitive::new([0.0, 0.0, 0.0, 1.0], [0.0, 0.0], 3, 1.0, 0.0, 1.0);
|
||||
// Main fill primitive
|
||||
cpu_primitives[fill_prim_id as usize] = Primitive {
|
||||
color: [1.0, 1.0, 1.0, 1.0],
|
||||
z_index: 1,
|
||||
..Primitive::DEFAULT
|
||||
};
|
||||
cpu_primitives[fill_prim_id as usize] =
|
||||
Primitive::new([1.0, 1.0, 1.0, 1.0], [0.0, 0.0], 1, 0.0, 0.0, 1.0);
|
||||
|
||||
// create an instance
|
||||
let instance = wgpu::Instance::new(wgpu::Backends::all());
|
||||
@ -155,7 +149,7 @@ impl State {
|
||||
&wgpu::DeviceDescriptor {
|
||||
label: None,
|
||||
features: wgpu::Features::default(),
|
||||
limits
|
||||
limits,
|
||||
},
|
||||
None,
|
||||
)
|
||||
@ -174,8 +168,12 @@ impl State {
|
||||
usage: wgpu::BufferUsages::INDEX,
|
||||
});
|
||||
|
||||
let prim_buffer_byte_size = (PRIM_BUFFER_LEN * std::mem::size_of::<Primitive>()) as u64;
|
||||
let globals_buffer_byte_size = std::mem::size_of::<Globals>() as u64;
|
||||
let prim_buffer_byte_size = cmp::max(
|
||||
MIN_BUFFER_SIZE,
|
||||
(PRIM_BUFFER_LEN * std::mem::size_of::<Primitive>()) as u64,
|
||||
);
|
||||
let globals_buffer_byte_size =
|
||||
cmp::max(MIN_BUFFER_SIZE, std::mem::size_of::<Globals>() as u64);
|
||||
|
||||
let prims_uniform_buffer = device.create_buffer(&wgpu::BufferDescriptor {
|
||||
label: Some("Prims ubo"),
|
||||
@ -243,7 +241,7 @@ impl State {
|
||||
|
||||
let vertex_module = device.create_shader_module(&create_vertex_module_descriptor());
|
||||
let fragment_module = device.create_shader_module(&create_fragment_module_descriptor());
|
||||
let mut render_pipeline_descriptor = create_map_render_pipeline_description(
|
||||
let render_pipeline_descriptor = create_map_render_pipeline_description(
|
||||
&pipeline_layout,
|
||||
create_map_vertex_state(&vertex_module),
|
||||
create_map_fragment_state(&fragment_module),
|
||||
@ -406,12 +404,11 @@ impl State {
|
||||
self.queue.write_buffer(
|
||||
&self.globals_uniform_buffer,
|
||||
0,
|
||||
bytemuck::cast_slice(&[Globals {
|
||||
resolution: [self.size.width as f32, self.size.height as f32],
|
||||
zoom: scene.zoom,
|
||||
scroll_offset: scene.scroll.to_array(),
|
||||
_pad: 0.0,
|
||||
}]),
|
||||
bytemuck::cast_slice(&[Globals::new(
|
||||
[self.size.width as f32, self.size.height as f32],
|
||||
scene.scroll.to_array(),
|
||||
scene.zoom,
|
||||
)]),
|
||||
);
|
||||
|
||||
self.queue.write_buffer(
|
||||
|
||||
@ -25,21 +25,17 @@ pub struct WithId(pub u32);
|
||||
|
||||
impl FillVertexConstructor<GpuVertex> for WithId {
|
||||
fn new_vertex(&mut self, vertex: tessellation::FillVertex) -> GpuVertex {
|
||||
GpuVertex {
|
||||
position: vertex.position().to_array(),
|
||||
normal: [0.0, 0.0],
|
||||
prim_id: self.0,
|
||||
}
|
||||
GpuVertex::new(vertex.position().to_array(), [0.0, 0.0], self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl StrokeVertexConstructor<GpuVertex> for WithId {
|
||||
fn new_vertex(&mut self, vertex: tessellation::StrokeVertex) -> GpuVertex {
|
||||
GpuVertex {
|
||||
position: vertex.position_on_path().to_array(),
|
||||
normal: vertex.normal().to_array(),
|
||||
prim_id: self.0,
|
||||
}
|
||||
GpuVertex::new(
|
||||
vertex.position_on_path().to_array(),
|
||||
vertex.normal().to_array(),
|
||||
self.0,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +0,0 @@
|
||||
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));
|
||||
}
|
||||
@ -1,14 +1,43 @@
|
||||
use wasm_bindgen::prelude::wasm_bindgen;
|
||||
use log::{Level, warn};
|
||||
extern crate console_error_panic_hook;
|
||||
|
||||
mod console;
|
||||
use std::panic;
|
||||
|
||||
use log::{warn, Level};
|
||||
use wasm_bindgen::prelude::wasm_bindgen;
|
||||
use wasm_bindgen::JsCast;
|
||||
use web_sys::Window as WebWindow;
|
||||
use winit::dpi::{LogicalSize, Size};
|
||||
use winit::event_loop::EventLoop;
|
||||
use winit::platform::web::WindowBuilderExtWebSys;
|
||||
use winit::window::{Window, WindowBuilder};
|
||||
|
||||
#[wasm_bindgen(start)]
|
||||
pub fn run() {
|
||||
console_log::init_with_level(Level::Info).expect("error initializing log");
|
||||
console::init_console_error_panic_hook();
|
||||
panic::set_hook(Box::new(console_error_panic_hook::hook));
|
||||
|
||||
wasm_bindgen_futures::spawn_local(async {
|
||||
super::setup().await;
|
||||
let event_loop = EventLoop::new();
|
||||
let web_window: WebWindow = web_sys::window().unwrap();
|
||||
let document = web_window.document().unwrap();
|
||||
let body = document.body().unwrap();
|
||||
let builder = WindowBuilder::new();
|
||||
let canvas: web_sys::HtmlCanvasElement = document
|
||||
.get_element_by_id("mapr")
|
||||
.unwrap()
|
||||
.dyn_into::<web_sys::HtmlCanvasElement>()
|
||||
.unwrap();
|
||||
|
||||
let window: Window = builder
|
||||
.with_canvas(Some(canvas))
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
window.set_inner_size(LogicalSize {
|
||||
width: body.client_width(),
|
||||
height: body.client_height(),
|
||||
});
|
||||
|
||||
super::setup(window, event_loop).await;
|
||||
});
|
||||
}
|
||||
|
||||
21
tools/build-web
Executable file
21
tools/build-web
Executable file
@ -0,0 +1,21 @@
|
||||
#!/bin/bash
|
||||
|
||||
webgl_flag=""
|
||||
|
||||
TEMP=$(getopt --long -o "g" "$@")
|
||||
eval set -- "$TEMP"
|
||||
while true; do
|
||||
case "$1" in
|
||||
-g)
|
||||
webgl_flag="--features web-webgl"
|
||||
shift 2
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# shellcheck disable=SC2086
|
||||
RUSTFLAGS=--cfg=web_sys_unstable_apis cargo build $webgl_flag --target wasm32-unknown-unknown --bin mapr
|
||||
wasm-bindgen --out-dir web --web target/wasm32-unknown-unknown/debug/mapr.wasm
|
||||
@ -2,11 +2,12 @@
|
||||
<head>
|
||||
<title>mapr Demo</title>
|
||||
</head>
|
||||
<body>
|
||||
<body style="margin: 0; padding: 0;">
|
||||
<script type="module">
|
||||
import init from "./mapr.js";
|
||||
|
||||
init();
|
||||
</script>
|
||||
<canvas id="mapr"></canvas>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user