diff --git a/Cargo.lock b/Cargo.lock
index dfc2f1a901c..fff14bdbe06 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1840,6 +1840,7 @@ dependencies = [
"gpu-alloc",
"gpu-descriptor",
"inplace_it",
+ "js-sys",
"khronos-egl",
"libloading 0.7.0",
"log",
@@ -1851,6 +1852,8 @@ dependencies = [
"raw-window-handle",
"renderdoc-sys",
"thiserror",
+ "wasm-bindgen",
+ "web-sys",
"wgpu-types",
"winapi",
"winit",
diff --git a/run-wasm-example.sh b/run-wasm-example.sh
new file mode 100755
index 00000000000..4032616353a
--- /dev/null
+++ b/run-wasm-example.sh
@@ -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
\ No newline at end of file
diff --git a/wasm-resources/README.md b/wasm-resources/README.md
new file mode 100644
index 00000000000..8b2b328fb74
--- /dev/null
+++ b/wasm-resources/README.md
@@ -0,0 +1,3 @@
+# WASM Resources
+
+This directory contains resources used when building the WGPU examples for web.
diff --git a/wasm-resources/index.template.html b/wasm-resources/index.template.html
new file mode 100644
index 00000000000..155072d10e2
--- /dev/null
+++ b/wasm-resources/index.template.html
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/wgpu-core/Cargo.toml b/wgpu-core/Cargo.toml
index 6810e90d1e3..05b8ef0b64d 100644
--- a/wgpu-core/Cargo.toml
+++ b/wgpu-core/Cargo.toml
@@ -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"
diff --git a/wgpu-core/build.rs b/wgpu-core/build.rs
index 38dad2eac7f..2dabdafc11a 100644
--- a/wgpu-core/build.rs
+++ b/wgpu-core/build.rs
@@ -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")
+ )
+ },
}
}
diff --git a/wgpu-core/src/device/mod.rs b/wgpu-core/src/device/mod.rs
index 2d3a6f159a9..2123883f3e7 100644
--- a/wgpu-core/src/device/mod.rs
+++ b/wgpu-core/src/device/mod.rs
@@ -4486,6 +4486,10 @@ impl Global {
{
self.poll_devices::(force_wait, &mut callbacks)?;
}
+ #[cfg(gl)]
+ {
+ self.poll_devices::(force_wait, &mut callbacks)?;
+ }
fire_map_callbacks(callbacks);
diff --git a/wgpu-core/src/lib.rs b/wgpu-core/src/lib.rs
index 968eff3e667..5c085ec4472 100644
--- a/wgpu-core/src/lib.rs
+++ b/wgpu-core/src/lib.rs
@@ -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),
diff --git a/wgpu-hal/Cargo.toml b/wgpu-hal/Cargo.toml
index 876499d0d10..db6156c511b 100644
--- a/wgpu-hal/Cargo.toml
+++ b/wgpu-hal/Cargo.toml
@@ -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"
diff --git a/wgpu-hal/src/gles/adapter.rs b/wgpu-hal/src/gles/adapter.rs
index 4f731fef8da..2872e262d55 100644
--- a/wgpu-hal/src/gles/adapter.rs
+++ b/wgpu-hal/src/gles/adapter.rs
@@ -184,7 +184,7 @@ impl super::Adapter {
log::info!("Vendor: {}", vendor);
log::info!("Renderer: {}", renderer);
- log::info!("Version: {}", version);
+ log::info!("Version: {}", &version);
log::debug!("Extensions: {:#?}", extensions);
@@ -192,25 +192,42 @@ 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)
};
- 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;
@@ -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;
@@ -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;
@@ -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,
@@ -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,
};
@@ -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
@@ -344,6 +390,7 @@ impl super::Adapter {
shared: Arc::new(super::AdapterShared {
context,
private_caps,
+ downlevel_flags,
workarounds,
shading_language_version,
}),
@@ -448,6 +495,7 @@ impl crate::Adapter for super::Adapter {
zero_buffer,
temp_query_results: Vec::new(),
draw_buffer_count: 1,
+ current_index_buffer: None,
},
})
}
@@ -547,11 +595,13 @@ impl crate::Adapter 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,
]
},
@@ -576,6 +626,12 @@ impl crate::Adapter 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;
diff --git a/wgpu-hal/src/gles/command.rs b/wgpu-hal/src/gles/command.rs
index b5020554b45..85f2480218e 100644
--- a/wgpu-hal/src/gles/command.rs
+++ b/wgpu-hal/src/gles/command.rs
@@ -251,14 +251,12 @@ impl crate::CommandEncoder for super::CommandEncoder {
) where
T: Iterator- ,
{
- //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,
})
}
diff --git a/wgpu-hal/src/gles/device.rs b/wgpu-hal/src/gles/device.rs
index bf47814ded9..5b4da35f256 100644
--- a/wgpu-hal/src/gles/device.rs
+++ b/wgpu-hal/src/gles/device.rs
@@ -76,7 +76,7 @@ impl super::Device {
gl: &glow::Context,
shader: &str,
naga_stage: naga::ShaderStage,
- label: Option<&str>,
+ #[cfg_attr(target_arch = "wasm32", allow(unused))] label: Option<&str>,
) -> Result {
let target = match naga_stage {
naga::ShaderStage::Vertex => glow::VERTEX_SHADER,
@@ -85,6 +85,7 @@ impl super::Device {
};
let raw = gl.create_shader(target).unwrap();
+ #[cfg(not(target_arch = "wasm32"))]
if gl.supports_debug() {
gl.object_label(glow::SHADER, raw, label);
}
@@ -163,9 +164,10 @@ impl super::Device {
gl: &glow::Context,
shaders: I,
layout: &super::PipelineLayout,
- label: crate::Label,
+ #[cfg_attr(target_arch = "wasm32", allow(unused))] label: Option<&str>,
) -> Result {
let program = gl.create_program().unwrap();
+ #[cfg(not(target_arch = "wasm32"))]
if let Some(label) = label {
if gl.supports_debug() {
gl.object_label(glow::PROGRAM, program, Some(label));
@@ -318,26 +320,46 @@ impl crate::Device for super::Device {
.contains(crate::MemoryFlags::PREFER_COHERENT);
let mut map_flags = 0;
- if is_host_visible {
- map_flags |= glow::MAP_PERSISTENT_BIT;
- if is_coherent {
- map_flags |= glow::MAP_COHERENT_BIT;
- }
- }
- if desc.usage.contains(crate::BufferUses::MAP_READ) {
- map_flags |= glow::MAP_READ_BIT;
- }
- if desc.usage.contains(crate::BufferUses::MAP_WRITE) {
- map_flags |= glow::MAP_WRITE_BIT;
- }
-
let raw = gl.create_buffer().unwrap();
gl.bind_buffer(target, Some(raw));
let raw_size = desc
.size
.try_into()
.map_err(|_| crate::DeviceError::OutOfMemory)?;
- gl.buffer_storage(target, raw_size, None, map_flags);
+
+ if self
+ .shared
+ .downlevel_flags
+ .contains(wgt::DownlevelFlags::STORAGE_RESOURCES)
+ {
+ if is_host_visible {
+ map_flags |= glow::MAP_PERSISTENT_BIT;
+ if is_coherent {
+ map_flags |= glow::MAP_COHERENT_BIT;
+ }
+ }
+ if desc.usage.contains(crate::BufferUses::MAP_READ) {
+ map_flags |= glow::MAP_READ_BIT;
+ }
+ if desc.usage.contains(crate::BufferUses::MAP_WRITE) {
+ map_flags |= glow::MAP_WRITE_BIT;
+ }
+
+ gl.buffer_storage(target, raw_size, None, map_flags);
+ } else {
+ assert!(!is_coherent);
+ let usage = if is_host_visible {
+ if desc.usage.contains(crate::BufferUses::MAP_READ) {
+ glow::STREAM_READ
+ } else {
+ glow::DYNAMIC_DRAW
+ }
+ } else {
+ glow::STATIC_DRAW
+ };
+ gl.buffer_data_size(target, raw_size, usage);
+ }
+
gl.bind_buffer(target, None);
if !is_coherent && desc.usage.contains(crate::BufferUses::MAP_WRITE) {
@@ -345,6 +367,7 @@ impl crate::Device for super::Device {
}
//TODO: do we need `glow::MAP_UNSYNCHRONIZED_BIT`?
+ #[cfg(not(target_arch = "wasm32"))]
if let Some(label) = desc.label {
if gl.supports_debug() {
gl.object_label(glow::BUFFER, raw, Some(label));
@@ -356,6 +379,8 @@ impl crate::Device for super::Device {
target,
size: desc.size,
map_flags,
+ #[cfg(target_arch = "wasm32")]
+ emulate_map_allocation: Default::default(),
})
}
unsafe fn destroy_buffer(&self, buffer: super::Buffer) {
@@ -372,14 +397,41 @@ impl crate::Device for super::Device {
let is_coherent = buffer.map_flags & glow::MAP_COHERENT_BIT != 0;
- gl.bind_buffer(buffer.target, Some(buffer.raw));
- let ptr = gl.map_buffer_range(
- buffer.target,
- range.start as i32,
- (range.end - range.start) as i32,
- buffer.map_flags,
- );
- gl.bind_buffer(buffer.target, None);
+ let ptr = if self
+ .shared
+ .private_caps
+ .contains(super::PrivateCapabilities::EMULATE_BUFFER_MAP)
+ {
+ #[cfg(target_arch = "wasm32")]
+ {
+ let ptr: *mut u8 = if let Some(ptr) = buffer.emulate_map_allocation.get().as_ref() {
+ *ptr
+ } else {
+ let ptr =
+ Box::into_raw(vec![0; buffer.size as usize].into_boxed_slice()) as *mut u8;
+ buffer.emulate_map_allocation.set(Some(ptr));
+ ptr
+ };
+
+ ptr.offset(range.start as isize)
+ }
+
+ #[cfg(not(target_arch = "wasm32"))]
+ unimplemented!(
+ "map_buffer() is not implemented for native platforms that do not support it"
+ );
+ } else {
+ gl.bind_buffer(buffer.target, Some(buffer.raw));
+ let ptr = gl.map_buffer_range(
+ buffer.target,
+ range.start as i32,
+ (range.end - range.start) as i32,
+ buffer.map_flags,
+ );
+ gl.bind_buffer(buffer.target, None);
+
+ ptr
+ };
Ok(crate::BufferMapping {
ptr: ptr::NonNull::new(ptr).ok_or(crate::DeviceError::Lost)?,
@@ -389,7 +441,26 @@ impl crate::Device for super::Device {
unsafe fn unmap_buffer(&self, buffer: &super::Buffer) -> Result<(), crate::DeviceError> {
let gl = &self.shared.context.lock();
gl.bind_buffer(buffer.target, Some(buffer.raw));
- gl.unmap_buffer(buffer.target);
+
+ if self
+ .shared
+ .private_caps
+ .contains(super::PrivateCapabilities::EMULATE_BUFFER_MAP)
+ {
+ #[cfg(target_arch = "wasm32")]
+ {
+ let ptr = buffer.emulate_map_allocation.take().unwrap();
+ let _ = Box::from_raw(std::slice::from_raw_parts_mut(ptr, buffer.size as usize));
+ }
+
+ #[cfg(not(target_arch = "wasm32"))]
+ unimplemented!(
+ "unmap_buffer() is not implemented for native plaforms that do not support it"
+ );
+ } else {
+ gl.unmap_buffer(buffer.target);
+ }
+
gl.bind_buffer(buffer.target, None);
Ok(())
}
@@ -400,11 +471,35 @@ impl crate::Device for super::Device {
let gl = &self.shared.context.lock();
gl.bind_buffer(buffer.target, Some(buffer.raw));
for range in ranges {
- gl.flush_mapped_buffer_range(
- buffer.target,
- range.start as i32,
- (range.end - range.start) as i32,
- );
+ if self
+ .shared
+ .private_caps
+ .contains(super::PrivateCapabilities::EMULATE_BUFFER_MAP)
+ {
+ #[cfg(target_arch = "wasm32")]
+ {
+ let ptr = buffer
+ .emulate_map_allocation
+ .get()
+ .expect("Buffer not mapped");
+ let slice = std::slice::from_raw_parts_mut(
+ ptr.offset(range.start as isize),
+ buffer.size as usize,
+ );
+ gl.buffer_sub_data_u8_slice(buffer.target, range.start as i32, slice);
+ }
+
+ #[cfg(not(target_arch = "wasm32"))]
+ unimplemented!(
+ "flush_mapped_ranges() not implemented for native targets that do not support it"
+ );
+ } else {
+ gl.flush_mapped_buffer_range(
+ buffer.target,
+ range.start as i32,
+ (range.end - range.start) as i32,
+ );
+ }
}
}
unsafe fn invalidate_mapped_ranges(&self, _buffer: &super::Buffer, _ranges: I) {
@@ -445,6 +540,7 @@ impl crate::Device for super::Device {
);
}
+ #[cfg(not(target_arch = "wasm32"))]
if let Some(label) = desc.label {
if gl.supports_debug() {
gl.object_label(glow::RENDERBUFFER, raw, Some(label));
@@ -523,6 +619,7 @@ impl crate::Device for super::Device {
}
};
+ #[cfg(not(target_arch = "wasm32"))]
if let Some(label) = desc.label {
if gl.supports_debug() {
gl.object_label(glow::TEXTURE, raw, Some(label));
@@ -657,6 +754,7 @@ impl crate::Device for super::Device {
);
}
+ #[cfg(not(target_arch = "wasm32"))]
if let Some(label) = desc.label {
if gl.supports_debug() {
gl.object_label(glow::SAMPLER, raw, Some(label));
@@ -944,11 +1042,11 @@ impl crate::Device for super::Device {
gl.delete_program(pipeline.inner.program);
}
+ #[cfg_attr(target_arch = "wasm32", allow(unused))]
unsafe fn create_query_set(
&self,
desc: &wgt::QuerySetDescriptor,
) -> Result {
- use std::fmt::Write;
let gl = &self.shared.context.lock();
let mut temp_string = String::new();
@@ -957,7 +1055,10 @@ impl crate::Device for super::Device {
let query = gl
.create_query()
.map_err(|_| crate::DeviceError::OutOfMemory)?;
+ #[cfg(not(target_arch = "wasm32"))]
if gl.supports_debug() {
+ use std::fmt::Write;
+
if let Some(label) = desc.label {
temp_string.clear();
let _ = write!(temp_string, "{}[{}]", label, i);
@@ -1039,3 +1140,9 @@ impl crate::Device for super::Device {
.end_frame_capture(ptr::null_mut(), ptr::null_mut())
}
}
+
+// SAFE: WASM doesn't have threads
+#[cfg(target_arch = "wasm32")]
+unsafe impl Sync for super::Device {}
+#[cfg(target_arch = "wasm32")]
+unsafe impl Send for super::Device {}
diff --git a/wgpu-hal/src/gles/mod.rs b/wgpu-hal/src/gles/mod.rs
index 144e9bd725e..35905bbd994 100644
--- a/wgpu-hal/src/gles/mod.rs
+++ b/wgpu-hal/src/gles/mod.rs
@@ -58,6 +58,8 @@ To address this, we invalidate the vertex buffers based on:
#[cfg(not(target_arch = "wasm32"))]
mod egl;
+#[cfg(target_arch = "wasm32")]
+mod web;
mod adapter;
mod command;
@@ -68,6 +70,9 @@ mod queue;
#[cfg(not(target_arch = "wasm32"))]
use self::egl::{AdapterContext, Instance, Surface};
+#[cfg(target_arch = "wasm32")]
+use self::web::{AdapterContext, Instance, Surface};
+
use arrayvec::ArrayVec;
use glow::HasContext;
@@ -122,6 +127,11 @@ bitflags::bitflags! {
const MEMORY_BARRIERS = 1 << 2;
/// Vertex buffer layouts separate from the data.
const VERTEX_BUFFER_LAYOUT = 1 << 3;
+ /// Buffer map must emulated becuase it is not supported natively.
+ const EMULATE_BUFFER_MAP = 1 << 4;
+ /// Indicates that buffers used as ELEMENT_ARRAY_BUFFER may be created / initialized / used
+ /// as other targets, if not present they must not be mixed with other targets.
+ const INDEX_BUFFER_ROLE_CHANGE = 1 << 5;
}
}
@@ -163,6 +173,7 @@ struct TextureFormatDesc {
struct AdapterShared {
context: AdapterContext,
private_caps: PrivateCapabilities,
+ downlevel_flags: wgt::DownlevelFlags,
workarounds: Workarounds,
shading_language_version: naga::back::glsl::Version,
}
@@ -193,6 +204,7 @@ pub struct Queue {
zero_buffer: glow::Buffer,
temp_query_results: Vec,
draw_buffer_count: u8,
+ current_index_buffer: Option,
}
#[derive(Debug)]
@@ -201,8 +213,16 @@ pub struct Buffer {
target: BindTarget,
size: wgt::BufferAddress,
map_flags: u32,
+ #[cfg(target_arch = "wasm32")]
+ emulate_map_allocation: std::cell::Cell