From 8eb6fa81dd03e4adae12621e27ad90af6a59ead1 Mon Sep 17 00:00:00 2001 From: Alexander Guryanov Date: Mon, 29 Nov 2021 13:14:02 +0700 Subject: [PATCH] Support for --target wasm32-unknown-emscripten --- .github/workflows/ci.yml | 17 ++++ Cargo.lock | 4 +- run-wasm-emscripten-example.sh | 33 +++++++ wasm-resources/index.em.template.html | 15 +++ wgpu-hal/Cargo.toml | 10 +- wgpu-hal/src/gles/device.rs | 8 ++ wgpu-hal/src/gles/egl.rs | 103 +++++++++++++++------ wgpu-hal/src/gles/mod.rs | 8 +- wgpu-hal/src/gles/queue.rs | 4 +- wgpu/Cargo.toml | 14 +-- wgpu/examples/hello-triangle/main.rs | 126 +++++++++++++++++++------- wgpu/src/backend/mod.rs | 30 +++++- wgpu/src/lib.rs | 12 ++- 13 files changed, 298 insertions(+), 86 deletions(-) create mode 100755 run-wasm-emscripten-example.sh create mode 100644 wasm-resources/index.em.template.html diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f3179d8734..5711c70e4c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -94,6 +94,14 @@ jobs: tool: clippy kind: web + # Emscripten WebGL + - name: WebAssembly emscripten + os: ubuntu-20.04 + channel: stable + target: wasm32-unknown-emscripten + tool: clippy + kind: webgl-em + name: Check ${{ matrix.name }} runs-on: ${{ matrix.os }} @@ -164,6 +172,15 @@ jobs: # build docs cargo doc --target ${{ matrix.target }} -p wgpu --no-deps + - name: check web-em + if: matrix.kind == 'webgl-em' + run: | + # check with no features + cargo ${{matrix.tool}} --target ${{ matrix.target }} -p wgpu -p wgpu-core + + # build docs + cargo doc --target ${{ matrix.target }} -p wgpu -p wgpu-core --no-deps + - name: check native if: matrix.kind == 'local' || matrix.kind == 'other' run: | diff --git a/Cargo.lock b/Cargo.lock index 0e37bfc12a..e71b61eca4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -601,8 +601,7 @@ dependencies = [ [[package]] name = "glow" version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f04649123493bc2483cbef4daddb45d40bbdae5adb221a63a23efdb0cc99520" +source = "git+https://github.com/grovesNL/glow?rev=b6eb0ba#b6eb0bafa402e23db26086b82ab2c635fa9266bd" dependencies = [ "js-sys", "slotmap", @@ -743,6 +742,7 @@ checksum = "8c2352bd1d0bceb871cb9d40f24360c8133c11d7486b68b5381c1dd1a32015e3" dependencies = [ "libc", "libloading", + "pkg-config", ] [[package]] diff --git a/run-wasm-emscripten-example.sh b/run-wasm-emscripten-example.sh new file mode 100755 index 0000000000..0ed99a09d7 --- /dev/null +++ b/run-wasm-emscripten-example.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash + +set -e + +set -- hello-triangle +echo "Compiling..." +EMMAKEN_CFLAGS="-g -s ERROR_ON_UNDEFINED_SYMBOLS=0 --no-entry -s FULL_ES3=1" cargo build --example $1 --target wasm32-unknown-emscripten + +mkdir -p target/wasm-examples/$1/ +cp target/wasm32-unknown-emscripten/debug/examples/* target/wasm-examples/$1 +cat wasm-resources/index.em.template.html | sed "s/{{example}}/$1/g" > target/wasm-examples/$1/index.html + +# Find a serving tool to host the example +SERVE_CMD="" +SERVE_ARGS="" +if which basic-http-server; then + SERVE_CMD="basic-http-server" + SERVE_ARGS="target/wasm-examples/$1 -a 127.0.0.1:1234" +elif which miniserve && python3 -m http.server --help > /dev/null; then + SERVE_CMD="miniserve" + SERVE_ARGS="target/wasm-examples/$1 -p 1234 --index index.html" +elif python3 -m http.server --help > /dev/null; then + SERVE_CMD="python3" + SERVE_ARGS="-m http.server --directory target/wasm-examples/$1 1234" +fi + +# Exit if we couldn't find a tool to serve the example with +if [ "$SERVE_CMD" = "" ]; then + echo "Couldn't find a utility to use to serve the example web page. You can serve the `target/wasm-examples/$1` folder yourself using any simple static http file server." +fi + +echo "Serving example with $SERVE_CMD at http://localhost:1234" +$SERVE_CMD $SERVE_ARGS diff --git a/wasm-resources/index.em.template.html b/wasm-resources/index.em.template.html new file mode 100644 index 0000000000..216b8d7c94 --- /dev/null +++ b/wasm-resources/index.em.template.html @@ -0,0 +1,15 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/wgpu-hal/Cargo.toml b/wgpu-hal/Cargo.toml index 9c2640a63e..89ce6d2599 100644 --- a/wgpu-hal/Cargo.toml +++ b/wgpu-hal/Cargo.toml @@ -43,7 +43,7 @@ gpu-descriptor = { version = "0.2", optional = true } inplace_it = { version ="0.3.3", optional = true } # backend: Gles -glow = { version = "0.11", optional = true } +glow = { git = "https://github.com/grovesNL/glow", rev = "b6eb0ba", optional = true } # backend: Dx12 bit-set = { version = "0.5", optional = true } @@ -59,6 +59,10 @@ egl = { package = "khronos-egl", version = "4.1", features = ["dynamic"], option #Note: it's only unused on Apple platforms libloading = { version = "0.7", optional = true } +[target.'cfg(target_os = "emscripten")'.dependencies] +egl = { package = "khronos-egl", version = "4.1", features = ["static", "no-pkg-config"], optional = true } +libloading = { version = "0.7", optional = true } + [target.'cfg(windows)'.dependencies] winapi = { version = "0.3", features = ["libloaderapi", "windef", "winuser"] } native = { package = "d3d12", version = "0.4.1", features = ["libloading"], optional = true } @@ -68,7 +72,7 @@ mtl = { package = "metal", version = "0.23.1" } objc = "0.2.5" core-graphics-types = "0.1" -[target.'cfg(target_arch = "wasm32")'.dependencies] +[target.'cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))'.dependencies] wasm-bindgen = { version = "0.2" } web-sys = { version = "0.3", features = ["Window", "HtmlCanvasElement", "WebGl2RenderingContext"] } js-sys = { version = "0.3" } @@ -86,4 +90,6 @@ features = ["wgsl-in"] [dev-dependencies] env_logger = "0.8" + +[target.'cfg(not(target_os = "emscripten"))'.dev-dependencies] winit = "0.26" diff --git a/wgpu-hal/src/gles/device.rs b/wgpu-hal/src/gles/device.rs index 722041048a..8003246ad1 100644 --- a/wgpu-hal/src/gles/device.rs +++ b/wgpu-hal/src/gles/device.rs @@ -1096,6 +1096,8 @@ impl crate::Device for super::Device { #[cfg_attr(target_arch = "wasm32", allow(clippy::needless_borrow))] Ok(fence.get_latest(&self.shared.context.lock())) } + + #[cfg_attr(target_os = "emscripten", allow(unreachable_code, unused_variables))] unsafe fn wait( &self, fence: &super::Fence, @@ -1114,6 +1116,12 @@ impl crate::Device for super::Device { .iter() .find(|&&(value, _)| value >= wait_value) .unwrap(); + + // for some reason signature of glow's client_wait_sync didn't match to emscripten's client_wait_sync + // however it should be noop in emscripten + #[cfg(target_os = "emscripten")] + return Ok(true); + match gl.client_wait_sync(sync, glow::SYNC_FLUSH_COMMANDS_BIT, timeout_ns as i32) { // for some reason firefox returns WAIT_FAILED, to investigate #[cfg(target_arch = "wasm32")] diff --git a/wgpu-hal/src/gles/egl.rs b/wgpu-hal/src/gles/egl.rs index 082318c245..63d7feae3e 100644 --- a/wgpu-hal/src/gles/egl.rs +++ b/wgpu-hal/src/gles/egl.rs @@ -10,10 +10,15 @@ const CONTEXT_LOCK_TIMEOUT_SECS: u64 = 1; const EGL_CONTEXT_FLAGS_KHR: i32 = 0x30FC; const EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR: i32 = 0x0001; const EGL_CONTEXT_OPENGL_ROBUST_ACCESS_EXT: i32 = 0x30BF; +#[cfg_attr(target_os = "emscripten", allow(dead_code))] const EGL_PLATFORM_WAYLAND_KHR: u32 = 0x31D8; +#[cfg_attr(target_os = "emscripten", allow(dead_code))] const EGL_PLATFORM_X11_KHR: u32 = 0x31D5; +#[cfg_attr(target_os = "emscripten", allow(dead_code))] const EGL_PLATFORM_ANGLE_ANGLE: u32 = 0x3202; +#[cfg_attr(target_os = "emscripten", allow(dead_code))] const EGL_PLATFORM_ANGLE_NATIVE_PLATFORM_TYPE_ANGLE: u32 = 0x348F; +#[cfg_attr(target_os = "emscripten", allow(dead_code))] const EGL_PLATFORM_ANGLE_DEBUG_LAYERS_ENABLED: u32 = 0x3451; const EGL_GL_COLORSPACE_KHR: u32 = 0x309D; const EGL_GL_COLORSPACE_SRGB_KHR: u32 = 0x3089; @@ -26,12 +31,20 @@ type WlDisplayConnectFun = type WlDisplayDisconnectFun = unsafe extern "system" fn(display: *const raw::c_void); +#[cfg(not(target_os = "emscripten"))] +type EglInstance = egl::DynamicInstance; + +#[cfg(target_os = "emscripten")] +type EglInstance = egl::Instance; + +#[cfg_attr(target_os = "emscripten", allow(dead_code))] type WlEglWindowCreateFun = unsafe extern "system" fn( surface: *const raw::c_void, width: raw::c_int, height: raw::c_int, ) -> *mut raw::c_void; +#[cfg_attr(target_os = "emscripten", allow(dead_code))] type WlEglWindowResizeFun = unsafe extern "system" fn( window: *const raw::c_void, width: raw::c_int, @@ -144,7 +157,7 @@ enum SrgbFrameBufferKind { /// Choose GLES framebuffer configuration. fn choose_config( - egl: &egl::DynamicInstance, + egl: &EglInstance, display: egl::Display, srgb_kind: SrgbFrameBufferKind, ) -> Result<(egl::Config, bool), crate::InstanceError> { @@ -255,7 +268,7 @@ fn gl_debug_message_callback(source: u32, gltype: u32, id: u32, severity: u32, m /// exclusive access when shared with multiple threads. pub struct AdapterContext { glow_context: Mutex, - egl: Arc>, + egl: Arc, egl_display: egl::Display, pub(super) egl_context: egl::Context, egl_pbuffer: Option, @@ -267,7 +280,7 @@ unsafe impl Send for AdapterContext {} /// A guard containing a lock to an [`AdapterContext`] pub struct AdapterContextLock<'a> { glow_context: MutexGuard<'a, glow::Context>, - egl: &'a Arc>, + egl: &'a Arc, egl_display: egl::Display, } @@ -337,7 +350,7 @@ impl AdapterContext { #[derive(Debug)] struct Inner { - egl: Arc>, + egl: Arc, #[allow(unused)] version: (i32, i32), supports_native_window: bool, @@ -347,6 +360,7 @@ struct Inner { /// Dummy pbuffer (1x1). /// Required for `eglMakeCurrent` on platforms that doesn't supports `EGL_KHR_surfaceless_context`. pbuffer: Option, + #[cfg_attr(target_os = "emscripten", allow(dead_code))] wl_display: Option<*mut raw::c_void>, /// Method by which the framebuffer should support srgb srgb_kind: SrgbFrameBufferKind, @@ -355,7 +369,7 @@ struct Inner { impl Inner { fn create( flags: crate::InstanceFlags, - egl: Arc>, + egl: Arc, display: egl::Display, ) -> Result { let version = egl.initialize(display).map_err(|_| crate::InstanceError)?; @@ -454,8 +468,12 @@ impl Inner { } }; + #[cfg(target_os = "emscripten")] + let pbuffer = None; + // Testing if context can be binded without surface // and creating dummy pbuffer surface if not. + #[cfg(not(target_os = "emscripten"))] let pbuffer = if version >= (1, 5) || display_extensions.contains("EGL_KHR_surfaceless_context") { log::info!("\tEGL context: +surfaceless"); @@ -495,6 +513,7 @@ impl Drop for Inner { } } +#[cfg_attr(target_os = "emscripten", allow(dead_code))] #[derive(Clone, Copy, Debug, PartialEq)] enum WindowKind { Wayland, @@ -519,15 +538,13 @@ unsafe impl Send for Instance {} unsafe impl Sync for Instance {} impl crate::Instance for Instance { + #[cfg_attr(target_os = "emscripten", allow(unused_variables, unused_mut))] unsafe fn init(desc: &crate::InstanceDescriptor) -> Result { - let egl_result = if cfg!(windows) { - egl::DynamicInstance::::load_required_from_filename("libEGL.dll") - } else if cfg!(any(target_os = "macos", target_os = "ios")) { - egl::DynamicInstance::::load_required_from_filename("libEGL.dylib") - } else { - egl::DynamicInstance::::load_required() - }; - let egl = match egl_result { + #[cfg(target_os = "emscripten")] + let egl = Arc::new(egl::Instance::new(egl::Static)); + + #[cfg(not(target_os = "emscripten"))] + let egl = match egl::DynamicInstance::::load_required() { Ok(egl) => Arc::new(egl), Err(e) => { log::info!("Unable to open libEGL: {:?}", e); @@ -562,6 +579,13 @@ impl crate::Instance for Instance { None }; + #[cfg(target_os = "emscripten")] + let wsi_library = None; + + #[cfg(target_os = "emscripten")] + let wsi_kind = WindowKind::Unknown; + + #[cfg(not(target_os = "emscripten"))] let (display, wsi_library, wsi_kind) = if let (Some(library), Some(egl)) = (wayland_library, egl.upcast::()) { @@ -613,6 +637,9 @@ impl crate::Instance for Instance { (display, None, WindowKind::Unknown) }; + #[cfg(target_os = "emscripten")] + let display = egl.get_display(egl::DEFAULT_DISPLAY).unwrap(); + if desc.flags.contains(crate::InstanceFlags::VALIDATION) && client_ext_str.contains(&"EGL_KHR_debug") { @@ -645,7 +672,10 @@ impl crate::Instance for Instance { }) } - #[cfg_attr(target_os = "macos", allow(unused, unused_mut, unreachable_code))] + #[cfg_attr( + any(target_os = "macos", target_os = "emscripten"), + allow(unused, unused_mut, unreachable_code) + )] unsafe fn create_surface( &self, has_handle: &impl HasRawWindowHandle, @@ -676,6 +706,7 @@ impl crate::Instance for Instance { return Err(crate::InstanceError); } } + #[cfg(not(target_os = "emscripten"))] Rwh::Wayland(handle) => { /* Wayland displays are not sharable between surfaces so if the * surface we receive from this handle is from a different @@ -710,6 +741,8 @@ impl crate::Instance for Instance { drop(old_inner); } } + #[cfg(target_os = "emscripten")] + Rwh::Web(_) => {} other => { log::error!("Unsupported window: {:?}", other); return Err(crate::InstanceError); @@ -801,7 +834,7 @@ pub struct Swapchain { #[derive(Debug)] pub struct Surface { - egl: Arc>, + egl: Arc, wsi: WindowSystemInterface, config: egl::Config, display: egl::Display, @@ -946,6 +979,8 @@ impl crate::Surface for Surface { wl_window = Some(window); window } + #[cfg(target_os = "emscripten")] + (WindowKind::Unknown, Rwh::Web(handle)) => handle.id as *mut std::ffi::c_void, (WindowKind::Unknown, Rwh::Win32(handle)) => handle.hwnd, (WindowKind::Unknown, Rwh::AppKit(handle)) => handle.ns_view, _ => { @@ -986,27 +1021,35 @@ impl crate::Surface for Surface { attributes.push(egl::ATTRIB_NONE as i32); // Careful, we can still be in 1.4 version even if `upcast` succeeds - let raw_result = match self.egl.upcast::() { - Some(egl) if self.wsi.kind != WindowKind::Unknown => { - let attributes_usize = attributes - .into_iter() - .map(|v| v as usize) - .collect::>(); - egl.create_platform_window_surface( - self.display, - self.config, - native_window_ptr, - &attributes_usize, - ) - } - _ => self.egl.create_window_surface( + #[cfg(not(target_os = "emscripten"))] + let raw_result = if let Some(egl) = self.egl.upcast::() { + let attributes_usize = attributes + .into_iter() + .map(|v| v as usize) + .collect::>(); + egl.create_platform_window_surface( + self.display, + self.config, + native_window_ptr, + &attributes_usize, + ) + } else { + self.egl.create_window_surface( self.display, self.config, native_window_ptr, Some(&attributes), - ), + ) }; + #[cfg(target_os = "emscripten")] + let raw_result = self.egl.create_window_surface( + self.display, + self.config, + native_window_ptr, + Some(&attributes), + ); + match raw_result { Ok(raw) => (raw, wl_window), Err(e) => { diff --git a/wgpu-hal/src/gles/mod.rs b/wgpu-hal/src/gles/mod.rs index 1d59b7f743..26534a8053 100644 --- a/wgpu-hal/src/gles/mod.rs +++ b/wgpu-hal/src/gles/mod.rs @@ -56,9 +56,9 @@ To address this, we invalidate the vertex buffers based on: */ -#[cfg(not(target_arch = "wasm32"))] +#[cfg(any(not(target_arch = "wasm32"), target_os = "emscripten"))] mod egl; -#[cfg(target_arch = "wasm32")] +#[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] mod web; mod adapter; @@ -67,10 +67,10 @@ mod conv; mod device; mod queue; -#[cfg(not(target_arch = "wasm32"))] +#[cfg(any(not(target_arch = "wasm32"), target_os = "emscripten"))] use self::egl::{AdapterContext, Instance, Surface}; -#[cfg(target_arch = "wasm32")] +#[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] use self::web::{AdapterContext, Instance, Surface}; use arrayvec::ArrayVec; diff --git a/wgpu-hal/src/gles/queue.rs b/wgpu-hal/src/gles/queue.rs index 91631f4913..f2a517af0e 100644 --- a/wgpu-hal/src/gles/queue.rs +++ b/wgpu-hal/src/gles/queue.rs @@ -1102,10 +1102,10 @@ impl crate::Queue for super::Queue { surface: &mut super::Surface, texture: super::Texture, ) -> Result<(), crate::SurfaceError> { - #[cfg(not(target_arch = "wasm32"))] + #[cfg(any(not(target_arch = "wasm32"), target_os = "emscripten"))] let gl = &self.shared.context.get_without_egl_lock(); - #[cfg(target_arch = "wasm32")] + #[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] let gl = &self.shared.context.glow_context; surface.present(texture, gl) diff --git a/wgpu/Cargo.toml b/wgpu/Cargo.toml index d36b4eb6e2..b6807cd917 100644 --- a/wgpu/Cargo.toml +++ b/wgpu/Cargo.toml @@ -84,13 +84,13 @@ replay = ["serde", "wgc/replay"] angle = ["wgc/angle"] webgl = ["wgc"] -[target.'cfg(not(target_arch = "wasm32"))'.dependencies.wgc] +[target.'cfg(any(not(target_arch = "wasm32"), target_os = "emscripten"))'.dependencies.wgc] package = "wgpu-core" path = "../wgpu-core" version = "0.11" features = ["raw-window-handle"] -[target.'cfg(target_arch = "wasm32")'.dependencies.wgc] +[target.'cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))'.dependencies.wgc] package = "wgpu-core" path = "../wgpu-core" version = "0.11" @@ -102,7 +102,7 @@ package = "wgpu-types" path = "../wgpu-types" version = "0.11" -[target.'cfg(not(target_arch = "wasm32"))'.dependencies.hal] +[target.'cfg(any(not(target_arch = "wasm32"), target_os = "emscripten"))'.dependencies.hal] package = "wgpu-hal" path = "../wgpu-hal" version = "0.11" @@ -127,9 +127,11 @@ noise = { version = "0.7", default-features = false } obj = "0.10" png = "0.16" rand = "0.7.2" + +[target.'cfg(not(target_os = "emscripten"))'.dev-dependencies] winit = "0.26" -[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies] +[target.'cfg(any(not(target_arch = "wasm32"), target_os = "emscripten"))'.dev-dependencies] async-executor = "1.0" pollster = "0.2" env_logger = "0.8" @@ -153,7 +155,7 @@ rev = "c69f676" #version = "0.7" features = ["wgsl-out"] -[target.'cfg(target_arch = "wasm32")'.dependencies] +[target.'cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))'.dependencies] wasm-bindgen = "0.2.76" # remember to change version in wiki as well web-sys = { version = "0.3.53", features = [ "Document", @@ -284,7 +286,7 @@ wasm-bindgen-futures = "0.4.23" # enable parking_lot's wasm-bindgen feature so that it, in turn, enables that of crate `instant` parking_lot = { version = "0.11", features = ["wasm-bindgen"] } -[target.'cfg(target_arch = "wasm32")'.dev-dependencies] +[target.'cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))'.dev-dependencies] console_error_panic_hook = "0.1.6" console_log = "0.1.2" # We need the Location feature in the framework examples diff --git a/wgpu/examples/hello-triangle/main.rs b/wgpu/examples/hello-triangle/main.rs index 885ca20127..b049cae814 100644 --- a/wgpu/examples/hello-triangle/main.rs +++ b/wgpu/examples/hello-triangle/main.rs @@ -1,11 +1,40 @@ +#[cfg(target_os = "emscripten")] +use raw_window_handle::{HasRawWindowHandle, RawWindowHandle}; use std::borrow::Cow; +#[cfg(not(target_os = "emscripten"))] use winit::{ event::{Event, WindowEvent}, event_loop::{ControlFlow, EventLoop}, window::Window, }; -async fn run(event_loop: EventLoop<()>, window: Window) { +#[cfg(target_os = "emscripten")] +struct Window {} + +#[cfg(target_os = "emscripten")] +struct Size { + width: u32, + height: u32, +} + +#[cfg(target_os = "emscripten")] +impl Window { + fn inner_size(self: &Window) -> Size { + Size { + width: 640, + height: 400, + } + } +} + +#[cfg(target_os = "emscripten")] +unsafe impl HasRawWindowHandle for Window { + fn raw_window_handle(&self) -> RawWindowHandle { + RawWindowHandle::Web(raw_window_handle::WebHandle::empty()) + } +} + +async fn run(#[cfg(not(target_os = "emscripten"))] event_loop: EventLoop<()>, window: Window) { let size = window.inner_size(); let instance = wgpu::Instance::new(wgpu::Backends::all()); let surface = unsafe { instance.create_surface(&window) }; @@ -67,6 +96,7 @@ async fn run(event_loop: EventLoop<()>, window: Window) { multiview: None, }); + #[cfg_attr(target_os = "emscripten", allow(unused_mut))] let mut config = wgpu::SurfaceConfiguration { usage: wgpu::TextureUsages::RENDER_ATTACHMENT, format: swapchain_format, @@ -77,6 +107,45 @@ async fn run(event_loop: EventLoop<()>, window: Window) { surface.configure(&device, &config); + fn redraw( + surface: &wgpu::Surface, + device: &wgpu::Device, + render_pipeline: &wgpu::RenderPipeline, + queue: &wgpu::Queue, + ) { + let frame = surface + .get_current_texture() + .expect("Failed to acquire next swap chain texture"); + let view = frame + .texture + .create_view(&wgpu::TextureViewDescriptor::default()); + let mut encoder = + device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); + { + let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: None, + color_attachments: &[wgpu::RenderPassColorAttachment { + view: &view, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Clear(wgpu::Color::GREEN), + store: true, + }, + }], + depth_stencil_attachment: None, + }); + rpass.set_pipeline(render_pipeline); + rpass.draw(0..3, 0..1); + } + + queue.submit(Some(encoder.finish())); + frame.present(); + } + + #[cfg(target_os = "emscripten")] + redraw(&surface, &device, &render_pipeline, &queue); + + #[cfg(not(target_os = "emscripten"))] event_loop.run(move |event, _, control_flow| { // Have the closure take ownership of the resources. // `event_loop.run` never returns, therefore we must do this to ensure @@ -94,35 +163,7 @@ async fn run(event_loop: EventLoop<()>, window: Window) { config.height = size.height; surface.configure(&device, &config); } - Event::RedrawRequested(_) => { - let frame = surface - .get_current_texture() - .expect("Failed to acquire next swap chain texture"); - let view = frame - .texture - .create_view(&wgpu::TextureViewDescriptor::default()); - let mut encoder = - device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); - { - let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { - label: None, - color_attachments: &[wgpu::RenderPassColorAttachment { - view: &view, - resolve_target: None, - ops: wgpu::Operations { - load: wgpu::LoadOp::Clear(wgpu::Color::GREEN), - store: true, - }, - }], - depth_stencil_attachment: None, - }); - rpass.set_pipeline(&render_pipeline); - rpass.draw(0..3, 0..1); - } - - queue.submit(Some(encoder.finish())); - frame.present(); - } + Event::RedrawRequested(_) => redraw(&surface, &device, &render_pipeline, &queue), Event::WindowEvent { event: WindowEvent::CloseRequested, .. @@ -133,16 +174,18 @@ async fn run(event_loop: EventLoop<()>, window: Window) { } fn main() { - let event_loop = EventLoop::new(); - let window = winit::window::Window::new(&event_loop).unwrap(); #[cfg(not(target_arch = "wasm32"))] { + let event_loop = EventLoop::new(); + let window = winit::window::Window::new(&event_loop).unwrap(); env_logger::init(); // Temporarily avoid srgb formats for the swapchain on the web pollster::block_on(run(event_loop, window)); } - #[cfg(target_arch = "wasm32")] + #[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] { + let event_loop = EventLoop::new(); + let window = winit::window::Window::new(&event_loop).unwrap(); std::panic::set_hook(Box::new(console_error_panic_hook::hook)); console_log::init().expect("could not initialize logger"); use winit::platform::web::WindowExtWebSys; @@ -157,4 +200,21 @@ fn main() { .expect("couldn't append canvas to document body"); wasm_bindgen_futures::spawn_local(run(event_loop, window)); } + #[cfg(target_os = "emscripten")] + { + use env_logger::Builder; + use log::LevelFilter; + + let window = Window {}; + Builder::new() + .format(|_buf, record| { + let message = format!("{}: {}", record.level(), record.args()); + println!("{}", &message); + Ok(()) + }) + .filter(None, LevelFilter::Info) + .init(); + + pollster::block_on(run(window)); + } } diff --git a/wgpu/src/backend/mod.rs b/wgpu/src/backend/mod.rs index e73d66ad89..bf08cb5f33 100644 --- a/wgpu/src/backend/mod.rs +++ b/wgpu/src/backend/mod.rs @@ -1,12 +1,32 @@ -#[cfg(all(target_arch = "wasm32", not(feature = "webgl")))] +#[cfg(all( + target_arch = "wasm32", + not(target_os = "emscripten"), + not(feature = "webgl") +))] mod web; -#[cfg(all(target_arch = "wasm32", not(feature = "webgl")))] +#[cfg(all( + target_arch = "wasm32", + not(target_os = "emscripten"), + not(feature = "webgl") +))] pub(crate) use web::{BufferMappedRange, Context}; -#[cfg(any(not(target_arch = "wasm32"), feature = "webgl"))] +#[cfg(any( + not(target_arch = "wasm32"), + target_os = "emscripten", + feature = "webgl" +))] mod direct; -#[cfg(any(not(target_arch = "wasm32"), feature = "webgl"))] +#[cfg(any( + not(target_arch = "wasm32"), + target_os = "emscripten", + feature = "webgl" +))] pub(crate) use direct::{BufferMappedRange, Context}; -#[cfg(any(not(target_arch = "wasm32"), feature = "webgl"))] +#[cfg(any( + not(target_arch = "wasm32"), + target_os = "emscripten", + feature = "webgl" +))] mod native_gpu_future; diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index 3558cdc284..2428399575 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -1511,7 +1511,11 @@ impl Instance { /// # Safety /// /// - canvas must be a valid element to create a surface upon. - #[cfg(all(target_arch = "wasm32", not(feature = "webgl")))] + #[cfg(all( + target_arch = "wasm32", + not(target_os = "emscripten"), + not(feature = "webgl") + ))] pub unsafe fn create_surface_from_canvas( &self, canvas: &web_sys::HtmlCanvasElement, @@ -1527,7 +1531,11 @@ impl Instance { /// # Safety /// /// - canvas must be a valid OffscreenCanvas to create a surface upon. - #[cfg(all(target_arch = "wasm32", not(feature = "webgl")))] + #[cfg(all( + target_arch = "wasm32", + not(target_os = "emscripten"), + not(feature = "webgl") + ))] pub unsafe fn create_surface_from_offscreen_canvas( &self, canvas: &web_sys::OffscreenCanvas,