From ab28009d80e17a3387a03f4040a7c780cead30b9 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Sun, 5 Dec 2021 02:24:10 -0500 Subject: [PATCH] Angle support on Windows Also remove the macros module in favor of generic functions. --- README.md | 26 +++-- wgpu-core/Cargo.toml | 1 + wgpu-core/build.rs | 3 +- wgpu-core/src/instance.rs | 218 +++++++++++++++++++++++--------------- wgpu-core/src/lib.rs | 4 +- wgpu-core/src/macros.rs | 193 --------------------------------- wgpu-hal/src/gles/egl.rs | 29 +++-- wgpu-hal/src/lib.rs | 20 +--- wgpu/Cargo.toml | 1 + 9 files changed, 180 insertions(+), 315 deletions(-) delete mode 100644 wgpu-core/src/macros.rs diff --git a/README.md b/README.md index 597c34126..fc44b576a 100644 --- a/README.md +++ b/README.md @@ -66,15 +66,25 @@ We have a [wiki](https://github.com/gfx-rs/wgpu/wiki) that serves as a knowledge ## Supported Platforms - API | Windows | Linux & Android | macOS & iOS | - ----- | ----------------------------- | ------------------ | ------------------ | - Vulkan | :white_check_mark: | :white_check_mark: | | - Metal | | | :white_check_mark: | - DX12 | :white_check_mark: (W10 only) | | | - DX11 | :construction: | | | - GLES3 | | :ok: | | + API | Windows | Linux & Android | macOS & iOS | + ----- | ----------------------------- | ------------------------- | ------------------ | + Vulkan | :white_check_mark: | :white_check_mark: | | + Metal | | | :white_check_mark: | + DX12 | :white_check_mark: (W10 only) | | | + DX11 | :hammer_and_wrench: | | | + GLES3 | :triangular_ruler: | :ok: + :triangular_ruler: | | -:white_check_mark: = First Class Support — :ok: = Best Effort Support — :construction: = Unsupported, but support in progress +:white_check_mark: = First Class Support — :ok: = Best Effort Support — :triangular_ruler: = via [Angle](angleproject.org) — :hammer_and_wrench: = Unsupported, but support in progress + +### Angle + +Angle is a translation layer from GLES to other backends, developed by Google. +We support running our GLES3 backend over it in order to reach platforms with GLES2 or DX11 support, which aren't accessible otherwise. +In order to run with Angle, "angle" feature has to be enabled, and Angle libraries placed in a location visible to the application. +These binaries can be downloaded from [gfbuild-angle](https://github.com/DileSoft/gfbuild-angle) artifacts. + +On Windows, you generally need to copy them into the working directory, or in the same directory as the executable. +On Linux, you can point to them using `LD_LIBRARY_PATH` environment. ## Environment Variables diff --git a/wgpu-core/Cargo.toml b/wgpu-core/Cargo.toml index 0de1de1ea..3e0bad5f5 100644 --- a/wgpu-core/Cargo.toml +++ b/wgpu-core/Cargo.toml @@ -13,6 +13,7 @@ license = "MIT OR Apache-2.0" [features] default = [] +angle = ["hal/gles"] # Enable API tracing trace = ["ron", "serde", "wgt/trace", "arrayvec/serde", "naga/serialize"] # Enable API replaying diff --git a/wgpu-core/build.rs b/wgpu-core/build.rs index 37cf80b79..ed0959da8 100644 --- a/wgpu-core/build.rs +++ b/wgpu-core/build.rs @@ -13,7 +13,8 @@ fn main() { dx11: { all(false, not(wasm), windows) }, gl: { any( - all(not(wasm), unix_wo_apple), + unix_wo_apple, + feature = "angle", wasm ) }, diff --git a/wgpu-core/src/instance.rs b/wgpu-core/src/instance.rs index a093fae44..cb86079f6 100644 --- a/wgpu-core/src/instance.rs +++ b/wgpu-core/src/instance.rs @@ -94,7 +94,7 @@ pub struct Instance { impl Instance { pub fn new(name: &str, backends: Backends) -> Self { - fn init(mask: Backends) -> Option { + fn init(_: A, mask: Backends) -> Option { if mask.contains(A::VARIANT.into()) { let mut flags = hal::InstanceFlags::empty(); if cfg!(debug_assertions) { @@ -114,39 +114,40 @@ impl Instance { Self { name: name.to_string(), #[cfg(vulkan)] - vulkan: init::(backends), + vulkan: init(hal::api::Vulkan, backends), #[cfg(metal)] - metal: init::(backends), + metal: init(hal::api::Metal, backends), #[cfg(dx12)] - dx12: init::(backends), + dx12: init(hal::api::Dx12, backends), #[cfg(dx11)] - dx11: init::(backends), + dx11: init(hal::api::Dx11, backends), #[cfg(gl)] - gl: init::(backends), + gl: init(hal::api::Gles, backends), } } pub(crate) fn destroy_surface(&self, surface: Surface) { - backends_map! { - let map = |(surface_backend, self_backend)| { - unsafe { - if let Some(suf) = surface_backend { - self_backend.as_ref().unwrap().destroy_surface(suf.raw); - } + fn destroy( + _: A, + instance: &Option, + surface: Option>, + ) { + unsafe { + if let Some(suf) = surface { + instance.as_ref().unwrap().destroy_surface(suf.raw); } - }; - - #[cfg(vulkan)] - map((surface.vulkan, &self.vulkan)), - #[cfg(metal)] - map((surface.metal, &self.metal)), - #[cfg(dx12)] - map((surface.dx12, &self.dx12)), - #[cfg(dx11)] - map((surface.dx11, &self.dx11)), - #[cfg(gl)] - map((surface.gl, &self.gl)), + } } + #[cfg(vulkan)] + destroy(hal::api::Vulkan, &self.vulkan, surface.vulkan); + #[cfg(metal)] + destroy(hal::api::Metal, &self.metal, surface.metal); + #[cfg(dx12)] + destroy(hal::api::Dx12, &self.dx12, surface.dx12); + #[cfg(dx11)] + destroy(hal::api::Dx11, &self.dx11, surface.dx11); + #[cfg(gl)] + destroy(hal::api::Gles, &self.gl, surface.gl); } } @@ -437,7 +438,7 @@ impl Global { ) -> SurfaceId { profiling::scope!("create_surface", "Instance"); - //Note: using adummy argument to work around the following error: + //Note: using a dummy argument to work around the following error: //> cannot provide explicit generic arguments when `impl Trait` is used in argument position fn init( _: A, @@ -509,46 +510,91 @@ impl Global { self.instance.destroy_surface(surface.unwrap()); } + fn enumerate( + &self, + _: A, + instance: &Option, + inputs: &AdapterInputs>, + list: &mut Vec, + ) { + let inst = match *instance { + Some(ref inst) => inst, + None => return, + }; + let id_backend = match inputs.find(A::VARIANT) { + Some(id) => id, + None => return, + }; + + profiling::scope!("enumerating", format!("{:?}", A::VARIANT)); + let hub = HalApi::hub(self); + let mut token = Token::root(); + + let hal_adapters = unsafe { inst.enumerate_adapters() }; + for raw in hal_adapters { + let adapter = Adapter::new(raw); + log::info!("Adapter {:?} {:?}", A::VARIANT, adapter.raw.info); + let id = hub + .adapters + .prepare(id_backend.clone()) + .assign(adapter, &mut token); + list.push(id.0); + } + } + pub fn enumerate_adapters(&self, inputs: AdapterInputs>) -> Vec { profiling::scope!("enumerate_adapters", "Instance"); - let instance = &self.instance; - let mut token = Token::root(); let mut adapters = Vec::new(); - backends_map! { - let map = |(instance_field, backend, backend_info)| { - if let Some(ref inst) = *instance_field { - let hub = HalApi::hub(self); - if let Some(id_backend) = inputs.find(backend) { - profiling::scope!("enumerating", backend_info); - for raw in unsafe {inst.enumerate_adapters()} { - let adapter = Adapter::new(raw); - log::info!("Adapter {} {:?}", backend_info, adapter.raw.info); - let id = hub.adapters - .prepare(id_backend.clone()) - .assign(adapter, &mut token); - adapters.push(id.0); - } - } - } - }; - - #[cfg(vulkan)] - map((&instance.vulkan, Backend::Vulkan, "Vulkan")), - #[cfg(metal)] - map((&instance.metal, Backend::Metal, "Metal")), - #[cfg(dx12)] - map((&instance.dx12, Backend::Dx12, "Dx12")), - #[cfg(dx11)] - map((&instance.dx11, Backend::Dx11, "Dx11")), - #[cfg(gl)] - map((&instance.gl, Backend::Gl, "GL")), - } + #[cfg(vulkan)] + self.enumerate( + hal::api::Vulkan, + &self.instance.vulkan, + &inputs, + &mut adapters, + ); + #[cfg(metal)] + self.enumerate( + hal::api::Metal, + &self.instance.metal, + &inputs, + &mut adapters, + ); + #[cfg(dx12)] + self.enumerate(hal::api::Dx12, &self.instance.dx12, &inputs, &mut adapters); + #[cfg(dx11)] + self.enumerate(hal::api::Dx11, &self.instance.dx11, &inputs, &mut adapters); + #[cfg(gl)] + self.enumerate(hal::api::Gles, &self.instance.gl, &inputs, &mut adapters); adapters } + fn select( + &self, + selected: &mut usize, + new_id: Option>, + mut list: Vec>, + ) -> Option { + match selected.checked_sub(list.len()) { + Some(left) => { + *selected = left; + None + } + None => { + let mut token = Token::root(); + let adapter = Adapter::new(list.swap_remove(*selected)); + log::info!("Adapter {:?} {:?}", A::VARIANT, adapter.raw.info); + let id = HalApi::hub(self) + .adapters + .prepare(new_id.unwrap()) + .assign(adapter, &mut token); + Some(id.0) + } + } + } + pub fn request_adapter( &self, desc: &RequestAdapterOptions, @@ -585,7 +631,7 @@ impl Global { } let mut token = Token::root(); - let (surface_guard, mut token) = self.surfaces.read(&mut token); + let (surface_guard, _) = self.surfaces.read(&mut token); let compatible_surface = desc .compatible_surface .map(|id| { @@ -597,7 +643,7 @@ impl Global { let mut device_types = Vec::new(); #[cfg(vulkan)] - let (mut id_vulkan, adapters_vk) = gather( + let (id_vulkan, adapters_vk) = gather( hal::api::Vulkan, self.instance.vulkan.as_ref(), &inputs, @@ -606,7 +652,7 @@ impl Global { &mut device_types, ); #[cfg(metal)] - let (mut id_metal, adapters_metal) = gather( + let (id_metal, adapters_metal) = gather( hal::api::Metal, self.instance.metal.as_ref(), &inputs, @@ -615,7 +661,7 @@ impl Global { &mut device_types, ); #[cfg(dx12)] - let (mut id_dx12, adapters_dx12) = gather( + let (id_dx12, adapters_dx12) = gather( hal::api::Dx12, self.instance.dx12.as_ref(), &inputs, @@ -624,7 +670,7 @@ impl Global { &mut device_types, ); #[cfg(dx11)] - let (mut id_dx11, adapters_dx11) = gather( + let (id_dx11, adapters_dx11) = gather( hal::api::Dx11, self.instance.dx11.as_ref(), &inputs, @@ -633,7 +679,7 @@ impl Global { &mut device_types, ); #[cfg(gl)] - let (mut id_gl, adapters_gl) = gather( + let (id_gl, adapters_gl) = gather( hal::api::Gles, self.instance.gl.as_ref(), &inputs, @@ -642,6 +688,9 @@ impl Global { &mut device_types, ); + // need to free the token to be used by `select` + drop(surface_guard); + drop(token); if device_types.is_empty() { return Err(RequestAdapterError::NotFound); } @@ -675,33 +724,28 @@ impl Global { }; let mut selected = preferred_gpu.unwrap_or(0); - - backends_map! { - let map = |(info_adapter, id_backend, mut adapters_backend)| { - if selected < adapters_backend.len() { - let adapter = Adapter::new(adapters_backend.swap_remove(selected)); - log::info!("Adapter {} {:?}", info_adapter, adapter.raw.info); - let id = HalApi::hub(self).adapters - .prepare(id_backend.take().unwrap()) - .assign(adapter, &mut token); - return Ok(id.0); - } - selected -= adapters_backend.len(); - }; - - #[cfg(vulkan)] - map(("Vulkan", &mut id_vulkan, adapters_vk)), - #[cfg(metal)] - map(("Metal", &mut id_metal, adapters_metal)), - #[cfg(dx12)] - map(("Dx12", &mut id_dx12, adapters_dx12)), - #[cfg(dx11)] - map(("Dx11", &mut id_dx11, adapters_dx11)), - #[cfg(gl)] - map(("GL", &mut id_gl, adapters_gl)), + #[cfg(vulkan)] + if let Some(id) = self.select(&mut selected, id_vulkan, adapters_vk) { + return Ok(id); + } + #[cfg(metal)] + if let Some(id) = self.select(&mut selected, id_metal, adapters_metal) { + return Ok(id); + } + #[cfg(dx12)] + if let Some(id) = self.select(&mut selected, id_dx12, adapters_dx12) { + return Ok(id); + } + #[cfg(dx11)] + if let Some(id) = self.select(&mut selected, id_dx11, adapters_dx11) { + return Ok(id); + } + #[cfg(gl)] + if let Some(id) = self.select(&mut selected, id_gl, adapters_gl) { + return Ok(id); } - let _ = selected; + log::warn!("Some adapters are present, but enumerating them failed!"); Err(RequestAdapterError::NotFound) } diff --git a/wgpu-core/src/lib.rs b/wgpu-core/src/lib.rs index 2e1ca6175..7293c78ce 100644 --- a/wgpu-core/src/lib.rs +++ b/wgpu-core/src/lib.rs @@ -27,9 +27,6 @@ clippy::pattern_type_mismatch, )] -#[macro_use] -mod macros; - pub mod binding_model; pub mod command; mod conv; @@ -213,6 +210,7 @@ macro_rules! gfx_select { //wgt::Backend::Dx11 => $global.$method::<$crate::api::Dx11>( $($param),* ), #[cfg(any( all(unix, not(target_os = "macos"), not(target_os = "ios")), + feature = "angle", target_arch = "wasm32" ))] wgt::Backend::Gl => $global.$method::<$crate::api::Gles>( $($param),+ ), diff --git a/wgpu-core/src/macros.rs b/wgpu-core/src/macros.rs deleted file mode 100644 index 59698d5b8..000000000 --- a/wgpu-core/src/macros.rs +++ /dev/null @@ -1,193 +0,0 @@ -macro_rules! backends_map { - // one let statement per backend with mapped data - ( - let map = |$backend:pat| $map:block; - $( - #[cfg($backend_cfg:meta)] let $pat:pat = map($expr:expr); - )* - ) => { - $( - #[cfg($backend_cfg)] - let $pat = { - let $backend = $expr; - $map - }; - )* - }; - - // one block statement per backend with mapped data - ( - let map = |$backend:pat| $map:block; - $( - #[cfg($backend_cfg:meta)] map($expr:expr), - )* - ) => { - $( - #[cfg($backend_cfg)] - { - let $backend = $expr; - $map - } - )* - }; -} - -#[test] -fn test_backend_macro() { - struct Foo { - #[cfg(any(windows, all(unix, not(target_os = "ios"), not(target_os = "macos")),))] - vulkan: u32, - - #[cfg(any(target_os = "ios", target_os = "macos"))] - metal: u32, - - #[cfg(dx12)] - dx12: u32, - - #[cfg(dx11)] - dx11: u32, - } - - // test struct construction - let test_foo: Foo = { - Foo { - #[cfg(vulkan)] - vulkan: 101, - #[cfg(metal)] - metal: 102, - #[cfg(dx12)] - dx12: 103, - #[cfg(dx11)] - dx11: 104, - } - }; - - let mut vec = Vec::new(); - - // test basic statement-per-backend - backends_map! { - let map = |(id, chr)| { - vec.push((id, chr)); - }; - - #[cfg(vulkan)] - map((test_foo.vulkan, 'a')), - - #[cfg(metal)] - map((test_foo.metal, 'b')), - - #[cfg(dx12)] - map((test_foo.dx12, 'c')), - - #[cfg(dx11)] - map((test_foo.dx11, 'd')), - } - - #[cfg(any(windows, all(unix, not(target_os = "ios"), not(target_os = "macos")),))] - assert!(vec.contains(&(101, 'a'))); - - #[cfg(any(target_os = "ios", target_os = "macos"))] - assert!(vec.contains(&(102, 'b'))); - - #[cfg(dx12)] - assert!(vec.contains(&(103, 'c'))); - - #[cfg(dx11)] - assert!(vec.contains(&(104, 'd'))); - - // test complex statement-per-backend - backends_map! { - let map = |(id, pred, code)| { - if pred(id) { - code(); - } - }; - - #[cfg(vulkan)] - map((test_foo.vulkan, |v| v == 101, || println!("vulkan"))), - - #[cfg(metal)] - map((test_foo.metal, |v| v == 102, || println!("metal"))), - - #[cfg(dx12)] - map((test_foo.dx12, |v| v == 103, || println!("dx12"))), - - #[cfg(dx11)] - map((test_foo.dx11, |v| v == 104, || println!("dx11"))), - } - - // test struct construction 2 - let test_foo_2: Foo = Foo { - #[cfg(vulkan)] - vulkan: 1, - - #[cfg(metal)] - metal: 2, - - #[cfg(dx12)] - dx12: 3, - - #[cfg(dx11)] - dx11: 4, - }; - - #[cfg(vulkan)] - let var_vulkan = test_foo_2.vulkan; - - #[cfg(metal)] - let var_metal = test_foo_2.metal; - - #[cfg(dx12)] - let var_dx12 = test_foo_2.dx12; - - #[cfg(dx11)] - let var_dx11 = test_foo_2.dx11; - - backends_map! { - let map = |(id, chr, var)| { (chr, id, var) }; - - #[cfg(vulkan)] - let var_vulkan = map((test_foo_2.vulkan, 'a', var_vulkan)); - - #[cfg(metal)] - let var_metal = map((test_foo_2.metal, 'b', var_metal)); - - #[cfg(dx12)] - let var_dx12 = map((test_foo_2.dx12, 'c', var_dx12)); - - #[cfg(dx11)] - let var_dx11 = map((test_foo_2.dx11, 'd', var_dx11)); - } - - #[cfg(vulkan)] - { - println!("backend int: {:?}", var_vulkan); - } - - #[cfg(metal)] - { - println!("backend int: {:?}", var_metal); - } - - #[cfg(dx12)] - { - println!("backend int: {:?}", var_dx12); - } - - #[cfg(dx11)] - { - println!("backend int: {:?}", var_dx11); - } - - #[cfg(any(windows, all(unix, not(target_os = "ios"), not(target_os = "macos")),))] - let _ = var_vulkan; - - #[cfg(any(target_os = "ios", target_os = "macos"))] - let _ = var_metal; - - #[cfg(dx12)] - let _ = var_dx12; - - #[cfg(dx11)] - let _ = var_dx11; -} diff --git a/wgpu-hal/src/gles/egl.rs b/wgpu-hal/src/gles/egl.rs index 689d52a67..b7662e5ab 100644 --- a/wgpu-hal/src/gles/egl.rs +++ b/wgpu-hal/src/gles/egl.rs @@ -181,13 +181,16 @@ fn choose_config( match egl.choose_first_config(display, &attributes) { Ok(Some(config)) => { if tier_max == 1 { - log::warn!( - "EGL says it can present to the window but not natively. {}.", - "This has been confirmed to malfunction on Intel+NV laptops", - ); + //Note: this has been confirmed to malfunction on Intel+NV laptops, + // but also on Angle. + log::warn!("EGL says it can present to the window but not natively",); } // Android emulator can't natively present either. - let tier_threshold = if cfg!(target_os = "android") { 1 } else { 2 }; + let tier_threshold = if cfg!(target_os = "android") || cfg!(windows) { + 1 + } else { + 2 + }; return Ok((config, tier_max >= tier_threshold)); } Ok(None) => { @@ -517,10 +520,15 @@ unsafe impl Sync for Instance {} impl crate::Instance for Instance { unsafe fn init(desc: &crate::InstanceDescriptor) -> Result { - let egl = match egl::DynamicInstance::::load_required() { + let egl_result = if cfg!(windows) { + egl::DynamicInstance::::load_required_from_filename("libEGL.dll") + } else { + egl::DynamicInstance::::load_required() + }; + let egl = match egl_result { Ok(egl) => Arc::new(egl), Err(e) => { - log::warn!("Unable to open libEGL.so: {:?}", e); + log::info!("Unable to open libEGL: {:?}", e); return Err(crate::InstanceError); } }; @@ -665,6 +673,7 @@ impl crate::Instance for Instance { match raw_window_handle { Rwh::Xlib(_) => {} Rwh::Xcb(_) => {} + Rwh::Win32(_) => {} #[cfg(target_os = "android")] Rwh::AndroidNdk(handle) => { let format = inner @@ -950,6 +959,7 @@ impl crate::Surface for Surface { wl_window = Some(window); window } + (None, Rwh::Win32(handle)) => handle.hwnd, _ => { log::warn!( "Initialized platform {:?} doesn't work with window {:?}", @@ -965,7 +975,10 @@ impl crate::Surface for Surface { // We don't want any of the buffering done by the driver, because we // manage a swapchain on our side. // Some drivers just fail on surface creation seeing `EGL_SINGLE_BUFFER`. - if cfg!(target_os = "android") || wsi_kind == Some(WindowKind::AngleX11) { + if cfg!(target_os = "android") + || cfg!(windows) + || wsi_kind == Some(WindowKind::AngleX11) + { egl::BACK_BUFFER } else { egl::SINGLE_BUFFER diff --git a/wgpu-hal/src/lib.rs b/wgpu-hal/src/lib.rs index ffc79300c..28dadb3ec 100644 --- a/wgpu-hal/src/lib.rs +++ b/wgpu-hal/src/lib.rs @@ -51,19 +51,15 @@ compile_error!("Metal API enabled on non-Apple OS. If your project is not using resolver=\"2\" in Cargo.toml, it should."); #[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."); +#[cfg(all(feature = "gles", any(target_os = "macos", target_os = "ios")))] +compile_error!("Gles API enabled on Apple OS. If your project is not using resolver=\"2\" in Cargo.toml, it should."); #[cfg(all(feature = "dx12", windows))] mod dx12; mod empty; -#[cfg(all( - feature = "gles", - any( - target_arch = "wasm32", - all(unix, not(target_os = "ios"), not(target_os = "macos")) - ) -))] +#[cfg(all(feature = "gles"))] mod gles; -#[cfg(all(feature = "metal", any(target_os = "macos", target_os = "ios")))] +#[cfg(all(feature = "metal"))] mod metal; #[cfg(feature = "vulkan")] mod vulkan; @@ -73,13 +69,7 @@ pub mod api { #[cfg(feature = "dx12")] pub use super::dx12::Api as Dx12; pub use super::empty::Api as Empty; - #[cfg(all( - feature = "gles", - any( - target_arch = "wasm32", - all(unix, not(target_os = "ios"), not(target_os = "macos")) - ) - ))] + #[cfg(feature = "gles")] pub use super::gles::Api as Gles; #[cfg(feature = "metal")] pub use super::metal::Api as Metal; diff --git a/wgpu/Cargo.toml b/wgpu/Cargo.toml index d02b17fdc..d36b4eb6e 100644 --- a/wgpu/Cargo.toml +++ b/wgpu/Cargo.toml @@ -81,6 +81,7 @@ spirv = ["naga/spv-in"] glsl = ["naga/glsl-in"] trace = ["serde", "wgc/trace"] replay = ["serde", "wgc/replay"] +angle = ["wgc/angle"] webgl = ["wgc"] [target.'cfg(not(target_arch = "wasm32"))'.dependencies.wgc]