Skip to content

Commit

Permalink
Support for --target wasm32-unknown-emscripten
Browse files Browse the repository at this point in the history
  • Loading branch information
caiiiycuk committed Dec 3, 2021
1 parent ed020ce commit e86e194
Show file tree
Hide file tree
Showing 14 changed files with 266 additions and 64 deletions.
7 changes: 7 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,13 @@ jobs:
target: wasm32-unknown-unknown
kind: webgl

# Emscripten WebGL
- name: WebAssembly emscripten
os: ubuntu-20.04
channel: stable
target: wasm32-unknown-emscripten
kind: compile

name: Check ${{ matrix.name }}
runs-on: ${{ matrix.os }}

Expand Down
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ default-members = ["wgpu", "wgpu-hal", "wgpu-info"]
[patch."https://github.com/zakarumych/gpu-alloc"]
#gpu-alloc = { path = "../gpu-alloc/gpu-alloc" }

[patch."https://github.com/grovesNL/glow"]
[patch."https://github.com/caiiiycuk/glow"]
#glow = { path = "../glow" }

[patch.crates-io]
Expand Down
33 changes: 33 additions & 0 deletions run-wasm-emscripten-example.sh
Original file line number Diff line number Diff line change
@@ -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
15 changes: 15 additions & 0 deletions wasm-resources/index.em.template.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head>
<body>
<canvas id="canvas" width="640" height="400"></canvas>
<script>
var Module = {
canvas: document.getElementById("canvas"),
};
</script>
<script src="{{example}}.js"></script>
</body>
</html>
12 changes: 10 additions & 2 deletions wgpu-hal/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,9 @@ gpu-descriptor = { version = "0.2", optional = true }
inplace_it = { version ="0.3.3", optional = true }

# backend: Gles
glow = { version = "0.11", optional = true }
# TODO: remove caiiiycuk/glow when
# https://github.com/grovesNL/glow/pull/195 will be merged
glow = { git = "https://github.com/caiiiycuk/glow", rev = "5abe786", optional = true }

# backend: Dx12
bit-set = { version = "0.5", optional = true }
Expand All @@ -59,6 +61,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 }
Expand All @@ -68,7 +74,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" }
Expand All @@ -86,4 +92,6 @@ features = ["wgsl-in"]

[dev-dependencies]
env_logger = "0.8"

[target.'cfg(not(target_os = "emscripten"))'.dev-dependencies]
winit = "0.26"
8 changes: 8 additions & 0 deletions wgpu-hal/src/gles/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1096,6 +1096,8 @@ impl crate::Device<super::Api> 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,
Expand All @@ -1114,6 +1116,12 @@ impl crate::Device<super::Api> 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")]
Expand Down
55 changes: 48 additions & 7 deletions wgpu-hal/src/gles/egl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ 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;
const EGL_GL_COLORSPACE_KHR: u32 = 0x309D;
const EGL_GL_COLORSPACE_SRGB_KHR: u32 = 0x3089;
Expand All @@ -23,12 +25,20 @@ type WlDisplayConnectFun =

type WlDisplayDisconnectFun = unsafe extern "system" fn(display: *const raw::c_void);

#[cfg(not(target_os = "emscripten"))]
type EglInstance = egl::DynamicInstance<egl::EGL1_4>;

#[cfg(target_os = "emscripten")]
type EglInstance = egl::Instance<egl::Static>;

#[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,
Expand Down Expand Up @@ -141,7 +151,7 @@ enum SrgbFrameBufferKind {

/// Choose GLES framebuffer configuration.
fn choose_config(
egl: &egl::DynamicInstance<egl::EGL1_4>,
egl: &EglInstance,
display: egl::Display,
srgb_kind: SrgbFrameBufferKind,
) -> Result<(egl::Config, bool), crate::InstanceError> {
Expand Down Expand Up @@ -249,7 +259,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<glow::Context>,
egl: Arc<egl::DynamicInstance<egl::EGL1_4>>,
egl: Arc<EglInstance>,
egl_display: egl::Display,
pub(super) egl_context: egl::Context,
egl_pbuffer: Option<egl::Surface>,
Expand All @@ -261,7 +271,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::DynamicInstance<egl::EGL1_4>>,
egl: &'a Arc<EglInstance>,
egl_display: egl::Display,
}

Expand Down Expand Up @@ -331,7 +341,7 @@ impl AdapterContext {

#[derive(Debug)]
struct Inner {
egl: Arc<egl::DynamicInstance<egl::EGL1_4>>,
egl: Arc<EglInstance>,
#[allow(unused)]
version: (i32, i32),
supports_native_window: bool,
Expand All @@ -341,6 +351,7 @@ struct Inner {
/// Dummy pbuffer (1x1).
/// Required for `eglMakeCurrent` on platforms that doesn't supports `EGL_KHR_surfaceless_context`.
pbuffer: Option<egl::Surface>,
#[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,
Expand All @@ -349,7 +360,7 @@ struct Inner {
impl Inner {
fn create(
flags: crate::InstanceFlags,
egl: Arc<egl::DynamicInstance<egl::EGL1_4>>,
egl: Arc<EglInstance>,
display: egl::Display,
) -> Result<Self, crate::InstanceError> {
let version = egl.initialize(display).map_err(|_| crate::InstanceError)?;
Expand Down Expand Up @@ -443,8 +454,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");
Expand Down Expand Up @@ -494,7 +509,12 @@ unsafe impl Send for Instance {}
unsafe impl Sync for Instance {}

impl crate::Instance<super::Api> for Instance {
#[cfg_attr(target_os = "emscripten", allow(unused_variables, unused_mut))]
unsafe fn init(desc: &crate::InstanceDescriptor) -> Result<Self, crate::InstanceError> {
#[cfg(target_os = "emscripten")]
let egl = Arc::new(egl::Instance::new(egl::Static));

#[cfg(not(target_os = "emscripten"))]
let egl = match egl::DynamicInstance::<egl::EGL1_4>::load_required() {
Ok(egl) => Arc::new(egl),
Err(e) => {
Expand Down Expand Up @@ -528,6 +548,7 @@ impl crate::Instance<super::Api> for Instance {
None
};

#[cfg(not(target_os = "emscripten"))]
let display = if let (Some(library), Some(egl)) =
(wayland_library, egl.upcast::<egl::EGL1_5>())
{
Expand All @@ -553,6 +574,9 @@ impl crate::Instance<super::Api> for Instance {
egl.get_display(egl::DEFAULT_DISPLAY).unwrap()
};

#[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")
{
Expand Down Expand Up @@ -582,7 +606,10 @@ impl crate::Instance<super::Api> 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,
Expand Down Expand Up @@ -611,6 +638,7 @@ impl crate::Instance<super::Api> 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
Expand Down Expand Up @@ -645,6 +673,8 @@ impl crate::Instance<super::Api> for Instance {
drop(old_inner);
}
}
#[cfg(target_os = "emscripten")]
Rwh::Web(_) => {}
other => {
log::error!("Unsupported window: {:?}", other);
return Err(crate::InstanceError);
Expand Down Expand Up @@ -736,7 +766,7 @@ pub struct Swapchain {

#[derive(Debug)]
pub struct Surface {
egl: Arc<egl::DynamicInstance<egl::EGL1_4>>,
egl: Arc<EglInstance>,
wsi_library: Option<Arc<libloading::Library>>,
config: egl::Config,
display: egl::Display,
Expand Down Expand Up @@ -875,6 +905,8 @@ impl crate::Surface<super::Api> for Surface {
wl_window = Some(window);
window
}
#[cfg(target_os = "emscripten")]
Rwh::Web(handle) => handle.id as *mut std::ffi::c_void,
_ => unreachable!(),
};

Expand All @@ -900,6 +932,7 @@ impl crate::Surface<super::Api> for Surface {
attributes.push(egl::ATTRIB_NONE as i32);

// Careful, we can still be in 1.4 version even if `upcast` succeeds
#[cfg(not(target_os = "emscripten"))]
let raw_result = if let Some(egl) = self.egl.upcast::<egl::EGL1_5>() {
let attributes_usize = attributes
.into_iter()
Expand All @@ -920,6 +953,14 @@ impl crate::Surface<super::Api> for Surface {
)
};

#[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) => {
Expand Down
8 changes: 4 additions & 4 deletions wgpu-hal/src/gles/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down
4 changes: 2 additions & 2 deletions wgpu-hal/src/gles/queue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1102,10 +1102,10 @@ impl crate::Queue<super::Api> 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)
Expand Down
Loading

0 comments on commit e86e194

Please sign in to comment.