Skip to content

Commit

Permalink
Implement WebGL Backend
Browse files Browse the repository at this point in the history
  • Loading branch information
zicklag committed Jul 28, 2021
1 parent efba648 commit b6d7a87
Show file tree
Hide file tree
Showing 30 changed files with 980 additions and 137 deletions.
3 changes: 3 additions & 0 deletions Cargo.lock

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

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>
1 change: 1 addition & 0 deletions wgpu-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ trace = ["ron", "serde", "wgt/trace", "arrayvec/serde", "naga/serialize"]
replay = ["serde", "wgt/replay", "arrayvec/serde", "naga/deserialize"]
# Enable serializable compute/render passes, and bundle encoders.
serial-pass = ["serde", "wgt/serde", "arrayvec/serde"]
webgl = ["hal/gles"]

[dependencies]
arrayvec = "0.7"
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),
all(wasm, feature = "webgl")
)
},
}
}
4 changes: 4 additions & 0 deletions wgpu-core/src/device/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4486,6 +4486,10 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
{
self.poll_devices::<hal::api::Dx11>(force_wait, &mut callbacks)?;
}
#[cfg(gl)]
{
self.poll_devices::<hal::api::Gles>(force_wait, &mut callbacks)?;
}

fire_map_callbacks(callbacks);

Expand Down
5 changes: 4 additions & 1 deletion wgpu-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,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")),
all(target_arch = "wasm32", feature = "webgl")
))]
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 @@ -63,6 +63,11 @@ mtl = { package = "metal", version = "0.22", git="https://github.com/gfx-rs/meta
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 = "a7ac13a"
Expand Down
92 changes: 74 additions & 18 deletions wgpu-hal/src/gles/adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,33 +184,50 @@ impl super::Adapter {

log::info!("Vendor: {}", vendor);
log::info!("Renderer: {}", renderer);
log::info!("Version: {}", version);
log::info!("Version: {}", &version);

log::debug!("Extensions: {:#?}", extensions);

let ver = Self::parse_version(&version).ok()?;

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)
};

let vertex_shader_storage_blocks =
gl.get_parameter_i32(glow::MAX_VERTEX_SHADER_STORAGE_BLOCKS) as u32;
let fragment_shader_storage_blocks =
gl.get_parameter_i32(glow::MAX_FRAGMENT_SHADER_STORAGE_BLOCKS) as u32;
let vertex_shader_storage_blocks = if cfg!(target_arch = "wasm32") {
// TODO: find out actual value
0
} else {
gl.get_parameter_i32(glow::MAX_VERTEX_SHADER_STORAGE_BLOCKS) as u32
};
let fragment_shader_storage_blocks = if cfg!(target_arch = "wasm32") {
// TODO: find out actual value
0
} else {
gl.get_parameter_i32(glow::MAX_FRAGMENT_SHADER_STORAGE_BLOCKS) as u32
};

let vertex_shader_storage_textures =
gl.get_parameter_i32(glow::MAX_VERTEX_IMAGE_UNIFORMS) as u32;
let fragment_shader_storage_textures =
gl.get_parameter_i32(glow::MAX_FRAGMENT_IMAGE_UNIFORMS) as u32;
let vertex_shader_storage_textures = if cfg!(target_arch = "wasm32") {
// TODO: find out actual value
8
} else {
gl.get_parameter_i32(glow::MAX_VERTEX_IMAGE_UNIFORMS) as u32
};
let fragment_shader_storage_textures = if cfg!(target_arch = "wasm32") {
8
} else {
gl.get_parameter_i32(glow::MAX_FRAGMENT_IMAGE_UNIFORMS) 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;

Expand All @@ -228,6 +245,19 @@ impl super::Adapter {
vertex_shader_storage_textures.min(fragment_shader_storage_textures)
};

let max_vertex_buffer_array_stride = if cfg!(target_arch = "wasm32") {
255 // https://www.khronos.org/registry/webgl/specs/latest/1.0/index.html#VERTEX_STRIDE
} else {
gl.get_parameter_i32(glow::MAX_VERTEX_ATTRIB_STRIDE) as u32
};

let max_vertex_buffers = if cfg!(target_arch = "wasm32") {
// TODO: Not sure what the max vertex buffer count is for WebGL
8
} else {
gl.get_parameter_i32(glow::MAX_VERTEX_ATTRIB_BINDINGS) as u32
};

let mut features = wgt::Features::empty()
| wgt::Features::TEXTURE_COMPRESSION_ETC2
| wgt::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES;
Expand Down Expand Up @@ -261,6 +291,14 @@ impl super::Adapter {
ver >= (3, 2) || extensions.contains("GL_EXT_draw_buffers_indexed"),
);

if cfg!(not(target_arch = "wasm32"))
&& (ver >= (3, 1)
|| extensions.contains("GL_ARB_buffer_storage")
|| extensions.contains("GL_EXT_buffer_storage"))
{
downlevel_flags.set(wgt::DownlevelFlags::STORAGE_RESOURCES, true);
}

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 All @@ -281,8 +319,11 @@ impl super::Adapter {
max_texture_dimension_3d: max_texture_3d_size,
max_texture_array_layers: gl.get_parameter_i32(glow::MAX_ARRAY_TEXTURE_LAYERS) as u32,
max_bind_groups: crate::MAX_BIND_GROUPS as u32,
max_dynamic_uniform_buffers_per_pipeline_layout: max_uniform_buffers_per_shader_stage,
max_dynamic_storage_buffers_per_pipeline_layout: max_storage_buffers_per_shader_stage,
// FIXME: !!!
// max_dynamic_uniform_buffers_per_pipeline_layout: max_uniform_buffers_per_shader_stage,
// max_dynamic_storage_buffers_per_pipeline_layout: max_storage_buffers_per_shader_stage,
max_dynamic_uniform_buffers_per_pipeline_layout: 8,
max_dynamic_storage_buffers_per_pipeline_layout: 4,
max_sampled_textures_per_shader_stage: super::MAX_TEXTURE_SLOTS as u32,
max_samplers_per_shader_stage: super::MAX_SAMPLERS as u32,
max_storage_buffers_per_shader_stage,
Expand All @@ -295,11 +336,10 @@ impl super::Adapter {
} else {
0
} as u32,
max_vertex_buffers: gl.get_parameter_i32(glow::MAX_VERTEX_ATTRIB_BINDINGS) as u32,
max_vertex_buffers,
max_vertex_attributes: (gl.get_parameter_i32(glow::MAX_VERTEX_ATTRIBS) as u32)
.min(super::MAX_VERTEX_ATTRIBUTES as u32),
max_vertex_buffer_array_stride: gl.get_parameter_i32(glow::MAX_VERTEX_ATTRIB_STRIDE)
as u32,
max_vertex_buffer_array_stride,
max_push_constant_size: 0,
};

Expand All @@ -318,6 +358,12 @@ impl super::Adapter {
ver >= (3, 1),
);

#[cfg(not(target_arch = "wasm32"))]
private_caps.set(super::PrivateCapabilities::INDEX_BUFFER_ROLE_CHANGE, true);

#[cfg(target_arch = "wasm32")]
private_caps.set(super::PrivateCapabilities::EMULATE_BUFFER_MAP, true);

let mut workarounds = super::Workarounds::empty();
let r = renderer.to_lowercase();
// Check for Mesa sRGB clear bug. See
Expand All @@ -344,6 +390,7 @@ impl super::Adapter {
shared: Arc::new(super::AdapterShared {
context,
private_caps,
downlevel_flags,
workarounds,
shading_language_version,
}),
Expand Down Expand Up @@ -448,6 +495,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 @@ -547,11 +595,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 @@ -576,6 +626,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
6 changes: 2 additions & 4 deletions wgpu-hal/src/gles/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -251,14 +251,12 @@ 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.
for copy in regions {
self.cmd_buffer.commands.push(C::CopyBufferToBuffer {
src: src.raw,
src_target: glow::COPY_READ_BUFFER,
src_target: src.target,
dst: dst.raw,
dst_target: glow::COPY_WRITE_BUFFER,
dst_target: dst.target,
copy,
})
}
Expand Down
Loading

0 comments on commit b6d7a87

Please sign in to comment.