Skip to content

Commit

Permalink
Implement WebGL2 Backend (#1686)
Browse files Browse the repository at this point in the history
* Implement WebGL Backend

* Add WebGL Fixes by @mrk-its

* Update Limits for WASM and Examples

* Address Review Points
  • Loading branch information
zicklag authored Oct 7, 2021
1 parent c36e080 commit 312828f
Show file tree
Hide file tree
Showing 32 changed files with 853 additions and 120 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,9 @@ jobs:
run: |
cargo clippy --target ${{ matrix.target }} -p wgpu
# Build for WebGL
cargo clippy --target ${{ matrix.target }} -p wgpu --features webgl -- -D warnings
# build docs
cargo doc --target ${{ matrix.target }} -p wgpu --no-deps
Expand Down
34 changes: 34 additions & 0 deletions run-wasm-example.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#!/bin/env bash

set -e

echo "Compiling..."
cargo build --example $1 --target wasm32-unknown-unknown --features webgl

echo "Generating bindings..."
mkdir -p target/wasm-examples/$1
wasm-bindgen --target web --out-dir target/wasm-examples/$1 target/wasm32-unknown-unknown/debug/examples/$1.wasm
cp wasm-resources/index.template.html target/wasm-examples/$1/index.html
sed -i "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
3 changes: 3 additions & 0 deletions wasm-resources/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# WASM Resources

This directory contains resources used when building the WGPU examples for web.
14 changes: 14 additions & 0 deletions wasm-resources/index.template.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head>
<body>
<script type="module">
import init from "./{{example}}.js";
window.addEventListener("load", () => {
init();
});
</script>
</body>
</html>
3 changes: 3 additions & 0 deletions wgpu-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ path = "../wgpu-hal"
package = "wgpu-hal"
version = "0.10.1"

[target.'cfg(target_arch = "wasm32")'.dependencies]
hal = { path = "../wgpu-hal", package = "wgpu-hal", version = "0.10", features = ["gles"] }

[target.'cfg(all(not(target_arch = "wasm32"), any(target_os = "ios", target_os = "macos")))'.dependencies]
hal = { path = "../wgpu-hal", package = "wgpu-hal", version = "0.10", features = ["metal"] }
#Note: could also enable "vulkan" for Vulkan Portability
Expand Down
7 changes: 6 additions & 1 deletion wgpu-core/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ fn main() {
metal: { all(not(wasm), apple) },
dx12: { all(not(wasm), windows) },
dx11: { all(false, not(wasm), windows) },
gl: { all(not(wasm), unix_wo_apple) },
gl: {
any(
all(not(wasm), unix_wo_apple),
wasm
)
},
}
}
4 changes: 4 additions & 0 deletions wgpu-core/src/device/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4584,6 +4584,10 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
{
self.poll_devices::<hal::api::Dx11>(force_wait, &mut closures)?;
}
#[cfg(gl)]
{
self.poll_devices::<hal::api::Gles>(force_wait, &mut closures)?;
}

unsafe {
closures.fire();
Expand Down
1 change: 1 addition & 0 deletions wgpu-core/src/hub.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1022,6 +1022,7 @@ impl HalApi for hal::api::Dx11 {
impl HalApi for hal::api::Gles {
const VARIANT: Backend = Backend::Gl;
fn create_instance_from_hal(name: &str, hal_instance: Self::Instance) -> Instance {
#[allow(clippy::needless_update)]
Instance {
name: name.to_owned(),
gl: Some(hal_instance),
Expand Down
7 changes: 5 additions & 2 deletions wgpu-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ pub mod resource;
mod track;
mod validation;

pub use hal::api;
pub use hal::{api, MAX_BIND_GROUPS, MAX_COLOR_TARGETS, MAX_VERTEX_BUFFERS};

use atomic::{AtomicUsize, Ordering};

Expand Down Expand Up @@ -211,7 +211,10 @@ macro_rules! gfx_select {
wgt::Backend::Dx12 => $global.$method::<$crate::api::Dx12>( $($param),* ),
//#[cfg(all(not(target_arch = "wasm32"), windows))]
//wgt::Backend::Dx11 => $global.$method::<$crate::api::Dx11>( $($param),* ),
#[cfg(all(not(target_arch = "wasm32"), unix, not(any(target_os = "ios", target_os = "macos"))))]
#[cfg(any(
all(unix, not(target_os = "macos"), not(target_os = "ios")),
target_arch = "wasm32"
))]
wgt::Backend::Gl => $global.$method::<$crate::api::Gles>( $($param),+ ),
other => panic!("Unexpected backend {:?}", other),

Expand Down
5 changes: 5 additions & 0 deletions wgpu-hal/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,11 @@ mtl = { package = "metal", version = "0.23.1" }
objc = "0.2.5"
core-graphics-types = "0.1"

[target.'cfg(target_arch = "wasm32")'.dependencies]
wasm-bindgen = { version = "0.2" }
web-sys = { version = "0.3", features = ["Window", "HtmlCanvasElement", "WebGl2RenderingContext"] }
js-sys = { version = "0.3" }

[dependencies.naga]
git = "https://github.com/gfx-rs/naga"
rev = "2e7d629"
Expand Down
38 changes: 34 additions & 4 deletions wgpu-hal/src/gles/adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ impl super::Adapter {

let shading_language_version = {
let sl_version = gl.get_parameter_string(glow::SHADING_LANGUAGE_VERSION);
log::info!("SL version: {}", sl_version);
log::info!("SL version: {}", &sl_version);
let (sl_major, sl_minor) = Self::parse_version(&sl_version).ok()?;
let value = sl_major as u16 * 100 + sl_minor as u16 * 10;
naga::back::glsl::Version::Embedded(value)
Expand All @@ -209,9 +209,11 @@ impl super::Adapter {
let max_storage_block_size =
gl.get_parameter_i32(glow::MAX_SHADER_STORAGE_BLOCK_SIZE) as u32;

// WORKAROUND:
// In order to work around an issue with GL on RPI4 and similar, we ignore a zero vertex ssbo count if there are vertex sstos. (more info: https://github.com/gfx-rs/wgpu/pull/1607#issuecomment-874938961)
// The hardware does not want us to write to these SSBOs, but GLES cannot express that. We detect this case and disable writing to SSBOs.
// WORKAROUND: In order to work around an issue with GL on RPI4 and similar, we ignore a
// zero vertex ssbo count if there are vertex sstos. (more info:
// https://github.com/gfx-rs/wgpu/pull/1607#issuecomment-874938961) The hardware does not
// want us to write to these SSBOs, but GLES cannot express that. We detect this case and
// disable writing to SSBOs.
let vertex_ssbo_false_zero =
vertex_shader_storage_blocks == 0 && vertex_shader_storage_textures != 0;
if vertex_ssbo_false_zero {
Expand Down Expand Up @@ -254,6 +256,7 @@ impl super::Adapter {
&& max_storage_block_size != 0
&& (vertex_shader_storage_blocks != 0 || vertex_ssbo_false_zero),
);
downlevel_flags.set(wgt::DownlevelFlags::FRAGMENT_STORAGE, ver >= (3, 1));

let mut features = wgt::Features::empty()
| wgt::Features::TEXTURE_COMPRESSION_ETC2
Expand Down Expand Up @@ -283,6 +286,14 @@ impl super::Adapter {
super::PrivateCapabilities::VERTEX_BUFFER_LAYOUT,
ver >= (3, 1),
);
private_caps.set(
super::PrivateCapabilities::INDEX_BUFFER_ROLE_CHANGE,
cfg!(not(target_arch = "wasm32")),
);
private_caps.set(
super::PrivateCapabilities::CAN_DISABLE_DRAW_BUFFER,
cfg!(not(target_arch = "wasm32")),
);

let max_texture_size = gl.get_parameter_i32(glow::MAX_TEXTURE_SIZE) as u32;
let max_texture_3d_size = gl.get_parameter_i32(glow::MAX_3D_TEXTURE_SIZE) as u32;
Expand Down Expand Up @@ -340,6 +351,12 @@ impl super::Adapter {
};

let mut workarounds = super::Workarounds::empty();

workarounds.set(
super::Workarounds::EMULATE_BUFFER_MAP,
cfg!(target_arch = "wasm32"),
);

let r = renderer.to_lowercase();
// Check for Mesa sRGB clear bug. See
// [`super::PrivateCapabilities::MESA_I915_SRGB_SHADER_CLEAR`].
Expand All @@ -358,13 +375,17 @@ impl super::Adapter {
let downlevel_defaults = wgt::DownlevelLimits {};

// Drop the GL guard so we can move the context into AdapterShared
// ( on WASM the gl handle is just a ref so we tell clippy to allow
// dropping the ref )
#[allow(clippy::drop_ref)]
drop(gl);

Some(crate::ExposedAdapter {
adapter: super::Adapter {
shared: Arc::new(super::AdapterShared {
context,
private_caps,
downlevel_flags,
workarounds,
shading_language_version,
}),
Expand Down Expand Up @@ -462,6 +483,7 @@ impl crate::Adapter<super::Api> for super::Adapter {
zero_buffer,
temp_query_results: Vec::new(),
draw_buffer_count: 1,
current_index_buffer: None,
},
})
}
Expand Down Expand Up @@ -561,11 +583,13 @@ impl crate::Adapter<super::Api> for super::Adapter {
formats: if surface.enable_srgb {
vec![
wgt::TextureFormat::Rgba8UnormSrgb,
#[cfg(not(target_arch = "wasm32"))]
wgt::TextureFormat::Bgra8UnormSrgb,
]
} else {
vec![
wgt::TextureFormat::Rgba8Unorm,
#[cfg(not(target_arch = "wasm32"))]
wgt::TextureFormat::Bgra8Unorm,
]
},
Expand All @@ -590,6 +614,12 @@ impl crate::Adapter<super::Api> for super::Adapter {
}
}

// SAFE: WASM doesn't have threads
#[cfg(target_arch = "wasm32")]
unsafe impl Sync for super::Adapter {}
#[cfg(target_arch = "wasm32")]
unsafe impl Send for super::Adapter {}

#[cfg(test)]
mod tests {
use super::super::Adapter;
Expand Down
11 changes: 7 additions & 4 deletions wgpu-hal/src/gles/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,14 +266,17 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
) where
T: Iterator<Item = crate::BufferCopy>,
{
//TODO: preserve `src.target` and `dst.target`
// at least for the buffers that require it.
let (src_target, dst_target) = if src.target == dst.target {
(glow::COPY_READ_BUFFER, glow::COPY_WRITE_BUFFER)
} else {
(src.target, dst.target)
};
for copy in regions {
self.cmd_buffer.commands.push(C::CopyBufferToBuffer {
src: src.raw,
src_target: glow::COPY_READ_BUFFER,
src_target,
dst: dst.raw,
dst_target: glow::COPY_WRITE_BUFFER,
dst_target,
copy,
})
}
Expand Down
Loading

0 comments on commit 312828f

Please sign in to comment.