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