mirror of
https://github.com/gfx-rs/wgpu.git
synced 2025-02-18 01:43:27 +00:00
Let GLES/Angle path compile on macOS
This commit is contained in:
parent
ab28009d80
commit
e867a7434c
19
README.md
19
README.md
@ -66,19 +66,20 @@ We have a [wiki](https://github.com/gfx-rs/wgpu/wiki) that serves as a knowledge
|
|||||||
|
|
||||||
## Supported Platforms
|
## Supported Platforms
|
||||||
|
|
||||||
API | Windows | Linux & Android | macOS & iOS |
|
API | Windows | Linux & Android | macOS & iOS |
|
||||||
----- | ----------------------------- | ------------------------- | ------------------ |
|
----- | ----------------------------- | ------------------------- | ------------------- |
|
||||||
Vulkan | :white_check_mark: | :white_check_mark: | |
|
Vulkan | :white_check_mark: | :white_check_mark: | |
|
||||||
Metal | | | :white_check_mark: |
|
Metal | | | :white_check_mark: |
|
||||||
DX12 | :white_check_mark: (W10 only) | | |
|
DX12 | :white_check_mark: (W10 only) | | |
|
||||||
DX11 | :hammer_and_wrench: | | |
|
DX11 | :hammer_and_wrench: | | |
|
||||||
GLES3 | :triangular_ruler: | :ok: + :triangular_ruler: | |
|
GLES3 | | :ok: | |
|
||||||
|
Angle | :ok: | :ok: | :hammer_and_wrench: |
|
||||||
|
|
||||||
:white_check_mark: = First Class Support — :ok: = Best Effort Support — :triangular_ruler: = via [Angle](angleproject.org) — :hammer_and_wrench: = Unsupported, but support in progress
|
:white_check_mark: = First Class Support — :ok: = Best Effort Support — :hammer_and_wrench: = Unsupported, but support in progress
|
||||||
|
|
||||||
### Angle
|
### Angle
|
||||||
|
|
||||||
Angle is a translation layer from GLES to other backends, developed by Google.
|
[Angle](angleproject.org) 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.
|
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.
|
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.
|
These binaries can be downloaded from [gfbuild-angle](https://github.com/DileSoft/gfbuild-angle) artifacts.
|
||||||
|
@ -964,6 +964,7 @@ impl HalApi for hal::api::Metal {
|
|||||||
Instance {
|
Instance {
|
||||||
name: name.to_owned(),
|
name: name.to_owned(),
|
||||||
metal: Some(hal_instance),
|
metal: Some(hal_instance),
|
||||||
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn hub<G: GlobalIdentityHandlerFactory>(global: &Global<G>) -> &Hub<Self, G> {
|
fn hub<G: GlobalIdentityHandlerFactory>(global: &Global<G>) -> &Hub<Self, G> {
|
||||||
|
@ -496,6 +496,8 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
|||||||
},
|
},
|
||||||
//acquired_texture: None,
|
//acquired_texture: None,
|
||||||
}),
|
}),
|
||||||
|
#[cfg(gl)]
|
||||||
|
gl: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut token = Token::root();
|
let mut token = Token::root();
|
||||||
|
@ -14,6 +14,8 @@
|
|||||||
clippy::needless_lifetimes,
|
clippy::needless_lifetimes,
|
||||||
// No need for defaults in the internal types.
|
// No need for defaults in the internal types.
|
||||||
clippy::new_without_default,
|
clippy::new_without_default,
|
||||||
|
// Needless updates are more scaleable, easier to play with features.
|
||||||
|
clippy::needless_update,
|
||||||
// For some reason `rustc` can warn about these in const generics even
|
// For some reason `rustc` can warn about these in const generics even
|
||||||
// though they are required.
|
// though they are required.
|
||||||
unused_braces,
|
unused_braces,
|
||||||
|
@ -422,18 +422,17 @@ impl Inner {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if needs_robustness {
|
if needs_robustness {
|
||||||
//Note: we specifically check the extension first, and then the core.
|
//Note: the core version can fail if robustness is not supported
|
||||||
// This is because the core version can fail if robustness is not supported
|
|
||||||
// (regardless of whether the extension is supported!).
|
// (regardless of whether the extension is supported!).
|
||||||
// In fact, Angle does precisely that awful behavior.
|
// In fact, Angle does precisely that awful behavior, so we don't try it there.
|
||||||
if display_extensions.contains("EGL_EXT_create_context_robustness") {
|
if version >= (1, 5) && !display_extensions.contains("EGL_ANGLE_") {
|
||||||
log::info!("\tEGL context: +robust access EXT");
|
|
||||||
context_attributes.push(EGL_CONTEXT_OPENGL_ROBUST_ACCESS_EXT);
|
|
||||||
context_attributes.push(egl::TRUE as _);
|
|
||||||
} else if version >= (1, 5) {
|
|
||||||
log::info!("\tEGL context: +robust access");
|
log::info!("\tEGL context: +robust access");
|
||||||
context_attributes.push(egl::CONTEXT_OPENGL_ROBUST_ACCESS);
|
context_attributes.push(egl::CONTEXT_OPENGL_ROBUST_ACCESS);
|
||||||
context_attributes.push(egl::TRUE as _);
|
context_attributes.push(egl::TRUE as _);
|
||||||
|
} else if display_extensions.contains("EGL_EXT_create_context_robustness") {
|
||||||
|
log::info!("\tEGL context: +robust access EXT");
|
||||||
|
context_attributes.push(EGL_CONTEXT_OPENGL_ROBUST_ACCESS_EXT);
|
||||||
|
context_attributes.push(egl::TRUE as _);
|
||||||
} else {
|
} else {
|
||||||
//Note: we aren't trying `EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR`
|
//Note: we aren't trying `EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR`
|
||||||
// because it's for desktop GL only, not GLES.
|
// because it's for desktop GL only, not GLES.
|
||||||
@ -501,16 +500,17 @@ enum WindowKind {
|
|||||||
Wayland,
|
Wayland,
|
||||||
X11,
|
X11,
|
||||||
AngleX11,
|
AngleX11,
|
||||||
|
Unknown,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
struct WindowSystemInterface {
|
struct WindowSystemInterface {
|
||||||
library: Arc<libloading::Library>,
|
library: Option<Arc<libloading::Library>>,
|
||||||
kind: WindowKind,
|
kind: WindowKind,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Instance {
|
pub struct Instance {
|
||||||
wsi: Option<WindowSystemInterface>,
|
wsi: WindowSystemInterface,
|
||||||
flags: crate::InstanceFlags,
|
flags: crate::InstanceFlags,
|
||||||
inner: Mutex<Inner>,
|
inner: Mutex<Inner>,
|
||||||
}
|
}
|
||||||
@ -522,6 +522,8 @@ impl crate::Instance<super::Api> for Instance {
|
|||||||
unsafe fn init(desc: &crate::InstanceDescriptor) -> Result<Self, crate::InstanceError> {
|
unsafe fn init(desc: &crate::InstanceDescriptor) -> Result<Self, crate::InstanceError> {
|
||||||
let egl_result = if cfg!(windows) {
|
let egl_result = if cfg!(windows) {
|
||||||
egl::DynamicInstance::<egl::EGL1_4>::load_required_from_filename("libEGL.dll")
|
egl::DynamicInstance::<egl::EGL1_4>::load_required_from_filename("libEGL.dll")
|
||||||
|
} else if cfg!(any(target_os = "macos", target_os = "ios")) {
|
||||||
|
egl::DynamicInstance::<egl::EGL1_4>::load_required_from_filename("libEGL.dylib")
|
||||||
} else {
|
} else {
|
||||||
egl::DynamicInstance::<egl::EGL1_4>::load_required()
|
egl::DynamicInstance::<egl::EGL1_4>::load_required()
|
||||||
};
|
};
|
||||||
@ -554,13 +556,13 @@ impl crate::Instance<super::Api> for Instance {
|
|||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
let angle_display_library = if client_ext_str.contains(&"EGL_ANGLE_platform_angle") {
|
let angle_x11_display_library = if client_ext_str.contains(&"EGL_ANGLE_platform_angle") {
|
||||||
open_x_display()
|
open_x_display()
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let (display, wsi) = if let (Some(library), Some(egl)) =
|
let (display, wsi_library, wsi_kind) = if let (Some(library), Some(egl)) =
|
||||||
(wayland_library, egl.upcast::<egl::EGL1_5>())
|
(wayland_library, egl.upcast::<egl::EGL1_5>())
|
||||||
{
|
{
|
||||||
log::info!("Using Wayland platform");
|
log::info!("Using Wayland platform");
|
||||||
@ -572,13 +574,7 @@ impl crate::Instance<super::Api> for Instance {
|
|||||||
&display_attributes,
|
&display_attributes,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
(
|
(display, Some(Arc::new(library)), WindowKind::Wayland)
|
||||||
display,
|
|
||||||
Some(WindowSystemInterface {
|
|
||||||
library: Arc::new(library),
|
|
||||||
kind: WindowKind::Wayland,
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
} else if let (Some((display, library)), Some(egl)) =
|
} else if let (Some((display, library)), Some(egl)) =
|
||||||
(x11_display_library, egl.upcast::<egl::EGL1_5>())
|
(x11_display_library, egl.upcast::<egl::EGL1_5>())
|
||||||
{
|
{
|
||||||
@ -587,15 +583,9 @@ impl crate::Instance<super::Api> for Instance {
|
|||||||
let display = egl
|
let display = egl
|
||||||
.get_platform_display(EGL_PLATFORM_X11_KHR, display.as_ptr(), &display_attributes)
|
.get_platform_display(EGL_PLATFORM_X11_KHR, display.as_ptr(), &display_attributes)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
(
|
(display, Some(Arc::new(library)), WindowKind::X11)
|
||||||
display,
|
|
||||||
Some(WindowSystemInterface {
|
|
||||||
library: Arc::new(library),
|
|
||||||
kind: WindowKind::X11,
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
} else if let (Some((display, library)), Some(egl)) =
|
} else if let (Some((display, library)), Some(egl)) =
|
||||||
(angle_display_library, egl.upcast::<egl::EGL1_5>())
|
(angle_x11_display_library, egl.upcast::<egl::EGL1_5>())
|
||||||
{
|
{
|
||||||
log::info!("Using Angle platform with X11");
|
log::info!("Using Angle platform with X11");
|
||||||
let display_attributes = [
|
let display_attributes = [
|
||||||
@ -616,17 +606,11 @@ impl crate::Instance<super::Api> for Instance {
|
|||||||
&display_attributes,
|
&display_attributes,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
(
|
(display, Some(Arc::new(library)), WindowKind::AngleX11)
|
||||||
display,
|
|
||||||
Some(WindowSystemInterface {
|
|
||||||
library: Arc::new(library),
|
|
||||||
kind: WindowKind::AngleX11,
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
log::info!("Using default platform");
|
log::info!("Using default platform");
|
||||||
let display = egl.get_display(egl::DEFAULT_DISPLAY).unwrap();
|
let display = egl.get_display(egl::DEFAULT_DISPLAY).unwrap();
|
||||||
(display, None)
|
(display, None, WindowKind::Unknown)
|
||||||
};
|
};
|
||||||
|
|
||||||
if desc.flags.contains(crate::InstanceFlags::VALIDATION)
|
if desc.flags.contains(crate::InstanceFlags::VALIDATION)
|
||||||
@ -652,7 +636,10 @@ impl crate::Instance<super::Api> for Instance {
|
|||||||
let inner = Inner::create(desc.flags, egl, display)?;
|
let inner = Inner::create(desc.flags, egl, display)?;
|
||||||
|
|
||||||
Ok(Instance {
|
Ok(Instance {
|
||||||
wsi,
|
wsi: WindowSystemInterface {
|
||||||
|
library: wsi_library,
|
||||||
|
kind: wsi_kind,
|
||||||
|
},
|
||||||
flags: desc.flags,
|
flags: desc.flags,
|
||||||
inner: Mutex::new(inner),
|
inner: Mutex::new(inner),
|
||||||
})
|
})
|
||||||
@ -674,6 +661,7 @@ impl crate::Instance<super::Api> for Instance {
|
|||||||
Rwh::Xlib(_) => {}
|
Rwh::Xlib(_) => {}
|
||||||
Rwh::Xcb(_) => {}
|
Rwh::Xcb(_) => {}
|
||||||
Rwh::Win32(_) => {}
|
Rwh::Win32(_) => {}
|
||||||
|
Rwh::AppKit(_) => {}
|
||||||
#[cfg(target_os = "android")]
|
#[cfg(target_os = "android")]
|
||||||
Rwh::AndroidNdk(handle) => {
|
Rwh::AndroidNdk(handle) => {
|
||||||
let format = inner
|
let format = inner
|
||||||
@ -814,7 +802,7 @@ pub struct Swapchain {
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Surface {
|
pub struct Surface {
|
||||||
egl: Arc<egl::DynamicInstance<egl::EGL1_4>>,
|
egl: Arc<egl::DynamicInstance<egl::EGL1_4>>,
|
||||||
wsi: Option<WindowSystemInterface>,
|
wsi: WindowSystemInterface,
|
||||||
config: egl::Config,
|
config: egl::Config,
|
||||||
display: egl::Display,
|
display: egl::Display,
|
||||||
context: egl::Context,
|
context: egl::Context,
|
||||||
@ -921,28 +909,27 @@ impl crate::Surface<super::Api> for Surface {
|
|||||||
let (surface, wl_window) = match self.unconfigure_impl(device) {
|
let (surface, wl_window) = match self.unconfigure_impl(device) {
|
||||||
Some(pair) => pair,
|
Some(pair) => pair,
|
||||||
None => {
|
None => {
|
||||||
let wsi_kind = self.wsi.as_ref().map(|wsi| wsi.kind);
|
|
||||||
let mut wl_window = None;
|
let mut wl_window = None;
|
||||||
let (mut temp_xlib_handle, mut temp_xcb_handle);
|
let (mut temp_xlib_handle, mut temp_xcb_handle);
|
||||||
#[allow(trivial_casts)]
|
#[allow(trivial_casts)]
|
||||||
let native_window_ptr = match (wsi_kind, self.raw_window_handle) {
|
let native_window_ptr = match (self.wsi.kind, self.raw_window_handle) {
|
||||||
(None | Some(WindowKind::X11), Rwh::Xlib(handle)) => {
|
(WindowKind::Unknown | WindowKind::X11, Rwh::Xlib(handle)) => {
|
||||||
temp_xlib_handle = handle.window;
|
temp_xlib_handle = handle.window;
|
||||||
&mut temp_xlib_handle as *mut _ as *mut std::ffi::c_void
|
&mut temp_xlib_handle as *mut _ as *mut std::ffi::c_void
|
||||||
}
|
}
|
||||||
(Some(WindowKind::AngleX11), Rwh::Xlib(handle)) => {
|
(WindowKind::AngleX11, Rwh::Xlib(handle)) => {
|
||||||
handle.window as *mut std::ffi::c_void
|
handle.window as *mut std::ffi::c_void
|
||||||
}
|
}
|
||||||
(None | Some(WindowKind::X11), Rwh::Xcb(handle)) => {
|
(WindowKind::Unknown | WindowKind::X11, Rwh::Xcb(handle)) => {
|
||||||
temp_xcb_handle = handle.window;
|
temp_xcb_handle = handle.window;
|
||||||
&mut temp_xcb_handle as *mut _ as *mut std::ffi::c_void
|
&mut temp_xcb_handle as *mut _ as *mut std::ffi::c_void
|
||||||
}
|
}
|
||||||
(Some(WindowKind::AngleX11), Rwh::Xcb(handle)) => {
|
(WindowKind::AngleX11, Rwh::Xcb(handle)) => {
|
||||||
handle.window as *mut std::ffi::c_void
|
handle.window as *mut std::ffi::c_void
|
||||||
}
|
}
|
||||||
(None, Rwh::AndroidNdk(handle)) => handle.a_native_window,
|
(WindowKind::Unknown, Rwh::AndroidNdk(handle)) => handle.a_native_window,
|
||||||
(Some(WindowKind::Wayland), Rwh::Wayland(handle)) => {
|
(WindowKind::Wayland, Rwh::Wayland(handle)) => {
|
||||||
let library = &self.wsi.as_ref().unwrap().library;
|
let library = self.wsi.library.as_ref().unwrap();
|
||||||
let wl_egl_window_create: libloading::Symbol<WlEglWindowCreateFun> =
|
let wl_egl_window_create: libloading::Symbol<WlEglWindowCreateFun> =
|
||||||
library.get(b"wl_egl_window_create").unwrap();
|
library.get(b"wl_egl_window_create").unwrap();
|
||||||
let window = wl_egl_window_create(handle.surface, 640, 480) as *mut _
|
let window = wl_egl_window_create(handle.surface, 640, 480) as *mut _
|
||||||
@ -959,11 +946,12 @@ impl crate::Surface<super::Api> for Surface {
|
|||||||
wl_window = Some(window);
|
wl_window = Some(window);
|
||||||
window
|
window
|
||||||
}
|
}
|
||||||
(None, Rwh::Win32(handle)) => handle.hwnd,
|
(WindowKind::Unknown, Rwh::Win32(handle)) => handle.hwnd,
|
||||||
|
(WindowKind::Unknown, Rwh::AppKit(handle)) => handle.ns_view,
|
||||||
_ => {
|
_ => {
|
||||||
log::warn!(
|
log::warn!(
|
||||||
"Initialized platform {:?} doesn't work with window {:?}",
|
"Initialized platform {:?} doesn't work with window {:?}",
|
||||||
wsi_kind,
|
self.wsi.kind,
|
||||||
self.raw_window_handle
|
self.raw_window_handle
|
||||||
);
|
);
|
||||||
return Err(crate::SurfaceError::Other("incompatible window kind"));
|
return Err(crate::SurfaceError::Other("incompatible window kind"));
|
||||||
@ -977,7 +965,7 @@ impl crate::Surface<super::Api> for Surface {
|
|||||||
// Some drivers just fail on surface creation seeing `EGL_SINGLE_BUFFER`.
|
// Some drivers just fail on surface creation seeing `EGL_SINGLE_BUFFER`.
|
||||||
if cfg!(target_os = "android")
|
if cfg!(target_os = "android")
|
||||||
|| cfg!(windows)
|
|| cfg!(windows)
|
||||||
|| wsi_kind == Some(WindowKind::AngleX11)
|
|| self.wsi.kind == WindowKind::AngleX11
|
||||||
{
|
{
|
||||||
egl::BACK_BUFFER
|
egl::BACK_BUFFER
|
||||||
} else {
|
} else {
|
||||||
@ -998,30 +986,31 @@ impl crate::Surface<super::Api> for Surface {
|
|||||||
attributes.push(egl::ATTRIB_NONE as i32);
|
attributes.push(egl::ATTRIB_NONE as i32);
|
||||||
|
|
||||||
// Careful, we can still be in 1.4 version even if `upcast` succeeds
|
// Careful, we can still be in 1.4 version even if `upcast` succeeds
|
||||||
let raw_result = if let Some(egl) = self.egl.upcast::<egl::EGL1_5>() {
|
let raw_result = match self.egl.upcast::<egl::EGL1_5>() {
|
||||||
let attributes_usize = attributes
|
Some(egl) if self.wsi.kind != WindowKind::Unknown => {
|
||||||
.into_iter()
|
let attributes_usize = attributes
|
||||||
.map(|v| v as usize)
|
.into_iter()
|
||||||
.collect::<Vec<_>>();
|
.map(|v| v as usize)
|
||||||
egl.create_platform_window_surface(
|
.collect::<Vec<_>>();
|
||||||
self.display,
|
egl.create_platform_window_surface(
|
||||||
self.config,
|
self.display,
|
||||||
native_window_ptr,
|
self.config,
|
||||||
&attributes_usize,
|
native_window_ptr,
|
||||||
)
|
&attributes_usize,
|
||||||
} else {
|
)
|
||||||
self.egl.create_window_surface(
|
}
|
||||||
|
_ => self.egl.create_window_surface(
|
||||||
self.display,
|
self.display,
|
||||||
self.config,
|
self.config,
|
||||||
native_window_ptr,
|
native_window_ptr,
|
||||||
Some(&attributes),
|
Some(&attributes),
|
||||||
)
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
match raw_result {
|
match raw_result {
|
||||||
Ok(raw) => (raw, wl_window),
|
Ok(raw) => (raw, wl_window),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::warn!("Error in create_platform_window_surface: {:?}", e);
|
log::warn!("Error in create_window_surface: {:?}", e);
|
||||||
return Err(crate::SurfaceError::Lost);
|
return Err(crate::SurfaceError::Lost);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1069,9 +1058,9 @@ impl crate::Surface<super::Api> for Surface {
|
|||||||
if let Some(window) = wl_window {
|
if let Some(window) = wl_window {
|
||||||
let wl_egl_window_destroy: libloading::Symbol<WlEglWindowDestroyFun> = self
|
let wl_egl_window_destroy: libloading::Symbol<WlEglWindowDestroyFun> = self
|
||||||
.wsi
|
.wsi
|
||||||
|
.library
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.expect("unsupported window")
|
.expect("unsupported window")
|
||||||
.library
|
|
||||||
.get(b"wl_egl_window_destroy")
|
.get(b"wl_egl_window_destroy")
|
||||||
.unwrap();
|
.unwrap();
|
||||||
wl_egl_window_destroy(window);
|
wl_egl_window_destroy(window);
|
||||||
|
@ -51,8 +51,6 @@
|
|||||||
compile_error!("Metal API enabled on non-Apple OS. If your project is not using resolver=\"2\" in Cargo.toml, it should.");
|
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)))]
|
#[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.");
|
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))]
|
#[cfg(all(feature = "dx12", windows))]
|
||||||
mod dx12;
|
mod dx12;
|
||||||
|
Loading…
Reference in New Issue
Block a user