From c039a74884a00ba744d9ebccb80e45a2e9e8f80e Mon Sep 17 00:00:00 2001 From: Ashley Date: Tue, 24 Jan 2023 19:47:03 +0100 Subject: [PATCH] [WebGL] Allow creating a texture from an external `web_sys::WebGlFramebuffer` and writing to it (#2609) --- wgpu-hal/src/gles/command.rs | 24 +++++++++++++++++++---- wgpu-hal/src/gles/device.rs | 2 ++ wgpu-hal/src/gles/mod.rs | 38 ++++++++++++++++++++++++------------ wgpu-hal/src/gles/queue.rs | 4 ++++ wgpu-hal/src/lib.rs | 18 +++++++++++------ wgpu/src/backend/direct.rs | 2 +- wgpu/src/lib.rs | 2 +- 7 files changed, 65 insertions(+), 25 deletions(-) diff --git a/wgpu-hal/src/gles/command.rs b/wgpu-hal/src/gles/command.rs index 0756637c66..7c1eb1c841 100644 --- a/wgpu-hal/src/gles/command.rs +++ b/wgpu-hal/src/gles/command.rs @@ -452,6 +452,20 @@ impl crate::CommandEncoder for super::CommandEncoder { self.state.has_pass_label = true; } + let rendering_to_external_framebuffer = desc + .color_attachments + .iter() + .filter_map(|at| at.as_ref()) + .any(|at| match at.target.view.inner { + #[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] + super::TextureInner::ExternalFramebuffer { .. } => true, + _ => false, + }); + + if rendering_to_external_framebuffer && desc.color_attachments.len() != 1 { + panic!("Multiple render attachments with external framebuffers are not supported."); + } + match desc .color_attachments .first() @@ -514,10 +528,12 @@ impl crate::CommandEncoder for super::CommandEncoder { } } - // set the draw buffers and states - self.cmd_buffer - .commands - .push(C::SetDrawColorBuffers(desc.color_attachments.len() as u8)); + if !rendering_to_external_framebuffer { + // set the draw buffers and states + self.cmd_buffer + .commands + .push(C::SetDrawColorBuffers(desc.color_attachments.len() as u8)); + } } } diff --git a/wgpu-hal/src/gles/device.rs b/wgpu-hal/src/gles/device.rs index a212cce3f4..ed8717df97 100644 --- a/wgpu-hal/src/gles/device.rs +++ b/wgpu-hal/src/gles/device.rs @@ -813,6 +813,8 @@ impl crate::Device for super::Device { super::TextureInner::Texture { raw, .. } => { unsafe { gl.delete_texture(raw) }; } + #[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] + super::TextureInner::ExternalFramebuffer { .. } => {} } } diff --git a/wgpu-hal/src/gles/mod.rs b/wgpu-hal/src/gles/mod.rs index 4ff293b54b..5b0e447c1b 100644 --- a/wgpu-hal/src/gles/mod.rs +++ b/wgpu-hal/src/gles/mod.rs @@ -187,10 +187,10 @@ impl Default for VertexAttribKind { } #[derive(Clone, Debug)] -struct TextureFormatDesc { - internal: u32, - external: u32, - data_type: u32, +pub struct TextureFormatDesc { + pub internal: u32, + pub external: u32, + pub data_type: u32, } struct AdapterShared { @@ -249,7 +249,7 @@ unsafe impl Sync for Buffer {} unsafe impl Send for Buffer {} #[derive(Clone, Debug)] -enum TextureInner { +pub enum TextureInner { Renderbuffer { raw: glow::Renderbuffer, }, @@ -258,8 +258,18 @@ enum TextureInner { raw: glow::Texture, target: BindTarget, }, + #[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] + ExternalFramebuffer { + inner: web_sys::WebGlFramebuffer, + }, } +// SAFE: WASM doesn't have threads +#[cfg(target_arch = "wasm32")] +unsafe impl Send for TextureInner {} +#[cfg(target_arch = "wasm32")] +unsafe impl Sync for TextureInner {} + impl TextureInner { fn as_native(&self) -> (glow::Texture, BindTarget) { match *self { @@ -267,21 +277,23 @@ impl TextureInner { panic!("Unexpected renderbuffer"); } Self::Texture { raw, target } => (raw, target), + #[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] + Self::ExternalFramebuffer { .. } => panic!("Unexpected external framebuffer"), } } } #[derive(Debug)] pub struct Texture { - inner: TextureInner, - drop_guard: Option, - mip_level_count: u32, - array_layer_count: u32, - format: wgt::TextureFormat, + pub inner: TextureInner, + pub drop_guard: Option, + pub mip_level_count: u32, + pub array_layer_count: u32, + pub format: wgt::TextureFormat, #[allow(unused)] - format_desc: TextureFormatDesc, - copy_size: CopyExtent, - is_cubemap: bool, + pub format_desc: TextureFormatDesc, + pub copy_size: CopyExtent, + pub is_cubemap: bool, } impl Texture { diff --git a/wgpu-hal/src/gles/queue.rs b/wgpu-hal/src/gles/queue.rs index e57fecabbe..add1daeb74 100644 --- a/wgpu-hal/src/gles/queue.rs +++ b/wgpu-hal/src/gles/queue.rs @@ -143,6 +143,10 @@ impl super::Queue { }; } } + #[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] + super::TextureInner::ExternalFramebuffer { ref inner } => unsafe { + gl.bind_external_framebuffer(glow::FRAMEBUFFER, inner); + }, } } diff --git a/wgpu-hal/src/lib.rs b/wgpu-hal/src/lib.rs index 710891fac6..8ca7a22e7a 100644 --- a/wgpu-hal/src/lib.rs +++ b/wgpu-hal/src/lib.rs @@ -63,17 +63,23 @@ compile_error!("Metal API enabled on non-Apple OS. If your project is not using #[cfg(all(feature = "dx12", not(windows)))] compile_error!("DX12 API enabled on non-Windows OS. If your project is not using resolver=\"2\" in Cargo.toml, it should."); +/// DirectX11 API internals. #[cfg(all(feature = "dx11", windows))] -mod dx11; +pub mod dx11; +/// DirectX12 API internals. #[cfg(all(feature = "dx12", windows))] -mod dx12; -mod empty; +pub mod dx12; +/// A dummy API implementation. +pub mod empty; +/// GLES API internals. #[cfg(all(feature = "gles"))] -mod gles; +pub mod gles; +/// Metal API internals. #[cfg(all(feature = "metal"))] -mod metal; +pub mod metal; +/// Vulkan API internals. #[cfg(feature = "vulkan")] -mod vulkan; +pub mod vulkan; pub mod auxil; pub mod api { diff --git a/wgpu/src/backend/direct.rs b/wgpu/src/backend/direct.rs index df3eb4c3a7..3ddf7a2446 100644 --- a/wgpu/src/backend/direct.rs +++ b/wgpu/src/backend/direct.rs @@ -128,7 +128,7 @@ impl Context { Ok((device, queue)) } - #[cfg(any(not(target_arch = "wasm32"), feature = "emscripten"))] + #[cfg(any(not(target_arch = "wasm32"), feature = "emscripten", feature = "webgl"))] pub unsafe fn create_texture_from_hal( &self, hal_texture: A::Texture, diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index 512b70eafd..b1e246c9a5 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -2118,7 +2118,7 @@ impl Device { /// - `hal_texture` must be created from this device internal handle /// - `hal_texture` must be created respecting `desc` /// - `hal_texture` must be initialized - #[cfg(any(not(target_arch = "wasm32"), feature = "emscripten"))] + #[cfg(any(not(target_arch = "wasm32"), feature = "emscripten", feature = "webgl"))] pub unsafe fn create_texture_from_hal( &self, hal_texture: A::Texture,