Angle support on Windows

Also remove the macros module in favor of generic functions.
This commit is contained in:
Dzmitry Malyshau 2021-12-05 02:24:10 -05:00 committed by Dzmitry Malyshau
parent c8d572a001
commit ab28009d80
9 changed files with 180 additions and 315 deletions

View File

@ -67,14 +67,24 @@ 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: | |
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

View File

@ -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

View File

@ -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
)
},

View File

@ -94,7 +94,7 @@ pub struct Instance {
impl Instance {
pub fn new(name: &str, backends: Backends) -> Self {
fn init<A: HalApi>(mask: Backends) -> Option<A::Instance> {
fn init<A: HalApi>(_: A, mask: Backends) -> Option<A::Instance> {
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::<hal::api::Vulkan>(backends),
vulkan: init(hal::api::Vulkan, backends),
#[cfg(metal)]
metal: init::<hal::api::Metal>(backends),
metal: init(hal::api::Metal, backends),
#[cfg(dx12)]
dx12: init::<hal::api::Dx12>(backends),
dx12: init(hal::api::Dx12, backends),
#[cfg(dx11)]
dx11: init::<hal::api::Dx11>(backends),
dx11: init(hal::api::Dx11, backends),
#[cfg(gl)]
gl: init::<hal::api::Gles>(backends),
gl: init(hal::api::Gles, backends),
}
}
pub(crate) fn destroy_surface(&self, surface: Surface) {
backends_map! {
let map = |(surface_backend, self_backend)| {
fn destroy<A: HalApi>(
_: A,
instance: &Option<A::Instance>,
surface: Option<HalSurface<A>>,
) {
unsafe {
if let Some(suf) = surface_backend {
self_backend.as_ref().unwrap().destroy_surface(suf.raw);
if let Some(suf) = surface {
instance.as_ref().unwrap().destroy_surface(suf.raw);
}
}
}
};
#[cfg(vulkan)]
map((surface.vulkan, &self.vulkan)),
destroy(hal::api::Vulkan, &self.vulkan, surface.vulkan);
#[cfg(metal)]
map((surface.metal, &self.metal)),
destroy(hal::api::Metal, &self.metal, surface.metal);
#[cfg(dx12)]
map((surface.dx12, &self.dx12)),
destroy(hal::api::Dx12, &self.dx12, surface.dx12);
#[cfg(dx11)]
map((surface.dx11, &self.dx11)),
destroy(hal::api::Dx11, &self.dx11, surface.dx11);
#[cfg(gl)]
map((surface.gl, &self.gl)),
}
destroy(hal::api::Gles, &self.gl, surface.gl);
}
}
@ -437,7 +438,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
) -> 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: hal::Api>(
_: A,
@ -509,46 +510,91 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
self.instance.destroy_surface(surface.unwrap());
}
fn enumerate<A: HalApi>(
&self,
_: A,
instance: &Option<A::Instance>,
inputs: &AdapterInputs<Input<G, AdapterId>>,
list: &mut Vec<AdapterId>,
) {
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<Input<G, AdapterId>>) -> Vec<AdapterId> {
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")),
self.enumerate(
hal::api::Vulkan,
&self.instance.vulkan,
&inputs,
&mut adapters,
);
#[cfg(metal)]
map((&instance.metal, Backend::Metal, "Metal")),
self.enumerate(
hal::api::Metal,
&self.instance.metal,
&inputs,
&mut adapters,
);
#[cfg(dx12)]
map((&instance.dx12, Backend::Dx12, "Dx12")),
self.enumerate(hal::api::Dx12, &self.instance.dx12, &inputs, &mut adapters);
#[cfg(dx11)]
map((&instance.dx11, Backend::Dx11, "Dx11")),
self.enumerate(hal::api::Dx11, &self.instance.dx11, &inputs, &mut adapters);
#[cfg(gl)]
map((&instance.gl, Backend::Gl, "GL")),
}
self.enumerate(hal::api::Gles, &self.instance.gl, &inputs, &mut adapters);
adapters
}
fn select<A: HalApi>(
&self,
selected: &mut usize,
new_id: Option<Input<G, AdapterId>>,
mut list: Vec<hal::ExposedAdapter<A>>,
) -> Option<AdapterId> {
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<G: GlobalIdentityHandlerFactory> Global<G> {
}
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<G: GlobalIdentityHandlerFactory> Global<G> {
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<G: GlobalIdentityHandlerFactory> Global<G> {
&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<G: GlobalIdentityHandlerFactory> Global<G> {
&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<G: GlobalIdentityHandlerFactory> Global<G> {
&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<G: GlobalIdentityHandlerFactory> Global<G> {
&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<G: GlobalIdentityHandlerFactory> Global<G> {
&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<G: GlobalIdentityHandlerFactory> Global<G> {
};
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)),
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)
}

View File

@ -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),+ ),

View File

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

View File

@ -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<super::Api> for Instance {
unsafe fn init(desc: &crate::InstanceDescriptor) -> Result<Self, crate::InstanceError> {
let egl = match egl::DynamicInstance::<egl::EGL1_4>::load_required() {
let egl_result = if cfg!(windows) {
egl::DynamicInstance::<egl::EGL1_4>::load_required_from_filename("libEGL.dll")
} else {
egl::DynamicInstance::<egl::EGL1_4>::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<super::Api> 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<super::Api> 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<super::Api> 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

View File

@ -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;

View File

@ -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]