feature: emscripten

This commit is contained in:
Alexander Guryanov 2022-01-25 17:35:45 +07:00 committed by Dzmitry Malyshau
parent 77b2c25684
commit b19000367c
12 changed files with 202 additions and 74 deletions

View File

@ -14,6 +14,7 @@ env:
# - local: build for the same target as we compile on, and do local tests
# - other: build without testing, e.g. cross-build
# - web: build for the Web
# - em: build for the Emscripten
jobs:
build:
@ -94,6 +95,12 @@ jobs:
tool: clippy
kind: web
- name: Emscripten
os: ubuntu-20.04
target: wasm32-unknown-emscripten
tool: clippy
kind: em
name: Check ${{ matrix.name }}
runs-on: ${{ matrix.os }}
@ -168,6 +175,15 @@ jobs:
# build docs
cargo doc --target ${{ matrix.target }} -p wgpu --no-deps
- name: check em
if: matrix.kind == 'em'
run: |
# build for Emscripten/WebGL
cargo ${{matrix.tool}} --target ${{ matrix.target }} -p wgpu -p wgpu-hal --features webgl,emscripten
# build raw-gles example
cargo ${{matrix.tool}} --target ${{ matrix.target }} --example raw-gles --features webgl,emscripten
- name: check native
if: matrix.kind == 'local' || matrix.kind == 'other'
run: |

1
Cargo.lock generated
View File

@ -900,6 +900,7 @@ checksum = "8c2352bd1d0bceb871cb9d40f24360c8133c11d7486b68b5381c1dd1a32015e3"
dependencies = [
"libc",
"libloading",
"pkg-config",
]
[[package]]

View File

@ -65,5 +65,8 @@ hal = { path = "../wgpu-hal", package = "wgpu-hal", version = "0.12", features =
[target.'cfg(all(not(target_arch = "wasm32"), windows))'.dependencies]
hal = { path = "../wgpu-hal", package = "wgpu-hal", version = "0.12", features = ["vulkan", "dx12", "renderdoc"] }
[target.'cfg(target_os = "emscripten")'.dependencies]
hal = { path = "../wgpu-hal", package = "wgpu-hal", version = "0.12", features = ["emscripten"] }
[build-dependencies]
cfg_aliases = "0.1"

View File

@ -18,6 +18,7 @@ vulkan = ["naga/spv-out", "ash", "gpu-alloc", "gpu-descriptor", "libloading", "i
gles = ["naga/glsl-out", "glow", "egl", "libloading"]
dx12 = ["naga/hlsl-out", "native", "bit-set", "range-alloc", "winapi/d3d12", "winapi/d3d12shader", "winapi/d3d12sdklayers", "winapi/dxgi1_6"]
renderdoc = ["libloading", "renderdoc-sys"]
emscripten = ["gles"]
[[example]]
name = "halmark"
@ -66,6 +67,11 @@ egl = { package = "khronos-egl", version = "4.1", features = ["dynamic"], option
#Note: it's only unused on Apple platforms
libloading = { version = "0.7", optional = true }
[target.'cfg(target_os = "emscripten")'.dependencies]
egl = { package = "khronos-egl", version = "4.1", features = ["static", "no-pkg-config"] }
#Note: it's unused by emscripten, but we keep it to have single code base in egl.rs
libloading = { version = "0.7", optional = true }
[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3", features = ["libloaderapi", "windef", "winuser"] }
native = { package = "d3d12", version = "0.4.1", features = ["libloading"], optional = true }
@ -75,7 +81,7 @@ mtl = { package = "metal", git = "https://github.com/gfx-rs/metal-rs", rev = "14
objc = "0.2.5"
core-graphics-types = "0.1"
[target.'cfg(target_arch = "wasm32")'.dependencies]
[target.'cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))'.dependencies]
wasm-bindgen = { version = "0.2" }
web-sys = { version = "0.3", features = ["Window", "HtmlCanvasElement", "WebGl2RenderingContext"] }
js-sys = { version = "0.3" }

View File

@ -0,0 +1,16 @@
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head>
<body>
<canvas id="canvas" width="640" height="400"></canvas>
<script>
var Module = {
canvas: document.getElementById("canvas"),
preRun: [function() {ENV.RUST_LOG = "debug"}]
};
</script>
<script src="raw-gles.js"></script>
</body>
</html>

View File

@ -1,15 +1,16 @@
//! This example shows interop with raw GLES contexts -
//! the ability to hook up wgpu-hal to an existing context and draw into it.
//!
//! Emscripten build:
//! 1. install emsdk
//! 2. build this example with cargo:
//! EMMAKEN_CFLAGS="-g -s ERROR_ON_UNDEFINED_SYMBOLS=0 --no-entry -s FULL_ES3=1" cargo build --example raw-gles --target wasm32-unknown-emscripten --features emscripten,webgl
//! 3. copy raw-gles.em.html into target/wasm32-unknown-emscripten/debug/examples/ and open it in browser
extern crate wgpu_hal as hal;
#[cfg(target_arch = "wasm32")]
fn main() {}
#[cfg(not(target_arch = "wasm32"))]
fn main() {
use hal::{Adapter as _, CommandEncoder as _, Device as _, Queue as _};
env_logger::init();
println!("Initializing external GL context");
@ -32,6 +33,94 @@ fn main() {
})
}
.expect("GL adapter can't be initialized");
fill_screen(&exposed, inner_size.width, inner_size.height);
println!("Showing the window");
gl_context.swap_buffers().unwrap();
event_loop.run(move |event, _, control_flow| {
use glutin::{
event::{Event, KeyboardInput, VirtualKeyCode, WindowEvent},
event_loop::ControlFlow,
};
*control_flow = ControlFlow::Wait;
match event {
Event::LoopDestroyed => return,
Event::WindowEvent { event, .. } => match event {
WindowEvent::CloseRequested
| WindowEvent::KeyboardInput {
input:
KeyboardInput {
virtual_keycode: Some(VirtualKeyCode::Escape),
..
},
..
} => *control_flow = ControlFlow::Exit,
_ => (),
},
_ => (),
}
});
}
#[cfg(feature = "emscripten")]
fn main() {
env_logger::init();
println!("Initializing external GL context");
let egl = egl::Instance::new(egl::Static);
let display = egl.get_display(egl::DEFAULT_DISPLAY).unwrap();
egl.initialize(display)
.expect("unable to initialize display");
let attributes = [
egl::RED_SIZE,
8,
egl::GREEN_SIZE,
8,
egl::BLUE_SIZE,
8,
egl::NONE,
];
let config = egl
.choose_first_config(display, &attributes)
.unwrap()
.expect("unable to choose config");
let surface = unsafe {
let window = std::ptr::null_mut::<std::ffi::c_void>();
egl.create_window_surface(display, config, window, None)
}
.expect("unable to create surface");
let context_attributes = [egl::CONTEXT_CLIENT_VERSION, 3, egl::NONE];
let gl_context = egl
.create_context(display, config, None, &context_attributes)
.expect("unable to create context");
egl.make_current(display, Some(surface), Some(surface), Some(gl_context))
.expect("can't make context current");
println!("Hooking up to wgpu-hal");
let exposed = unsafe {
<hal::api::Gles as hal::Api>::Adapter::new_external(|name| {
egl.get_proc_address(name)
.map_or(std::ptr::null(), |p| p as *const _)
})
}
.expect("GL adapter can't be initialized");
fill_screen(&exposed, 640, 400);
}
#[cfg(all(target_arch = "wasm32", not(feature = "emscripten")))]
fn main() {}
fn fill_screen(exposed: &hal::ExposedAdapter<hal::api::Gles>, width: u32, height: u32) {
use hal::{Adapter as _, CommandEncoder as _, Device as _, Queue as _};
let mut od = unsafe {
exposed
.adapter
@ -68,8 +157,8 @@ fn main() {
let rp_desc = hal::RenderPassDescriptor {
label: None,
extent: wgt::Extent3d {
width: inner_size.width,
height: inner_size.height,
width,
height,
depth_or_array_layers: 1,
},
sample_count: 1,
@ -92,32 +181,4 @@ fn main() {
let cmd_buf = encoder.end_encoding().unwrap();
od.queue.submit(&[&cmd_buf], None).unwrap();
}
println!("Showing the window");
gl_context.swap_buffers().unwrap();
event_loop.run(move |event, _, control_flow| {
use glutin::{
event::{Event, KeyboardInput, VirtualKeyCode, WindowEvent},
event_loop::ControlFlow,
};
*control_flow = ControlFlow::Wait;
match event {
Event::LoopDestroyed => return,
Event::WindowEvent { event, .. } => match event {
WindowEvent::CloseRequested
| WindowEvent::KeyboardInput {
input:
KeyboardInput {
virtual_keycode: Some(VirtualKeyCode::Escape),
..
},
..
} => *control_flow = ControlFlow::Exit,
_ => (),
},
_ => (),
}
});
}

View File

@ -26,6 +26,12 @@ type WlDisplayConnectFun =
type WlDisplayDisconnectFun = unsafe extern "system" fn(display: *const raw::c_void);
#[cfg(not(feature = "emscripten"))]
type EglInstance = egl::DynamicInstance<egl::EGL1_4>;
#[cfg(feature = "emscripten")]
type EglInstance = egl::Instance<egl::Static>;
type WlEglWindowCreateFun = unsafe extern "system" fn(
surface: *const raw::c_void,
width: raw::c_int,
@ -154,7 +160,7 @@ enum SrgbFrameBufferKind {
/// Choose GLES framebuffer configuration.
fn choose_config(
egl: &egl::DynamicInstance<egl::EGL1_4>,
egl: &EglInstance,
display: egl::Display,
srgb_kind: SrgbFrameBufferKind,
) -> Result<(egl::Config, bool), crate::InstanceError> {
@ -263,7 +269,7 @@ fn gl_debug_message_callback(source: u32, gltype: u32, id: u32, severity: u32, m
#[derive(Clone, Debug)]
struct EglContext {
instance: Arc<egl::DynamicInstance<egl::EGL1_4>>,
instance: Arc<EglInstance>,
display: egl::Display,
raw: egl::Context,
pbuffer: Option<egl::Surface>,
@ -307,7 +313,7 @@ impl AdapterContext {
}
struct EglContextLock<'a> {
instance: &'a Arc<egl::DynamicInstance<egl::EGL1_4>>,
instance: &'a Arc<EglInstance>,
display: egl::Display,
}
@ -385,6 +391,7 @@ struct Inner {
version: (i32, i32),
supports_native_window: bool,
config: egl::Config,
#[cfg_attr(feature = "emscripten", allow(dead_code))]
wl_display: Option<*mut raw::c_void>,
/// Method by which the framebuffer should support srgb
srgb_kind: SrgbFrameBufferKind,
@ -393,7 +400,7 @@ struct Inner {
impl Inner {
fn create(
flags: crate::InstanceFlags,
egl: Arc<egl::DynamicInstance<egl::EGL1_4>>,
egl: Arc<EglInstance>,
display: egl::Display,
) -> Result<Self, crate::InstanceError> {
let version = egl.initialize(display).map_err(|_| crate::InstanceError)?;
@ -564,6 +571,10 @@ unsafe impl Sync for Instance {}
impl crate::Instance<super::Api> for Instance {
unsafe fn init(desc: &crate::InstanceDescriptor) -> Result<Self, crate::InstanceError> {
#[cfg(feature = "emscripten")]
let egl_result: Result<EglInstance, egl::Error> = Ok(egl::Instance::new(egl::Static));
#[cfg(not(feature = "emscripten"))]
let egl_result = if cfg!(windows) {
egl::DynamicInstance::<egl::EGL1_4>::load_required_from_filename("libEGL.dll")
} else if cfg!(any(target_os = "macos", target_os = "ios")) {
@ -606,8 +617,14 @@ impl crate::Instance<super::Api> for Instance {
None
};
#[cfg(not(feature = "emscripten"))]
let egl1_5 = egl.upcast::<egl::EGL1_5>();
#[cfg(feature = "emscripten")]
let egl1_5: Option<&Arc<EglInstance>> = Some(&egl);
let (display, wsi_library, wsi_kind) = if let (Some(library), Some(egl)) =
(wayland_library, egl.upcast::<egl::EGL1_5>())
(wayland_library, egl1_5)
{
log::info!("Using Wayland platform");
let display_attributes = [egl::ATTRIB_NONE];
@ -619,18 +636,14 @@ impl crate::Instance<super::Api> for Instance {
)
.unwrap();
(display, Some(Arc::new(library)), WindowKind::Wayland)
} else if let (Some((display, library)), Some(egl)) =
(x11_display_library, egl.upcast::<egl::EGL1_5>())
{
} else if let (Some((display, library)), Some(egl)) = (x11_display_library, egl1_5) {
log::info!("Using X11 platform");
let display_attributes = [egl::ATTRIB_NONE];
let display = egl
.get_platform_display(EGL_PLATFORM_X11_KHR, display.as_ptr(), &display_attributes)
.unwrap();
(display, Some(Arc::new(library)), WindowKind::X11)
} else if let (Some((display, library)), Some(egl)) =
(angle_x11_display_library, egl.upcast::<egl::EGL1_5>())
{
} else if let (Some((display, library)), Some(egl)) = (angle_x11_display_library, egl1_5) {
log::info!("Using Angle platform with X11");
let display_attributes = [
EGL_PLATFORM_ANGLE_NATIVE_PLATFORM_TYPE_ANGLE as egl::Attrib,
@ -698,7 +711,7 @@ impl crate::Instance<super::Api> for Instance {
let raw_window_handle = has_handle.raw_window_handle();
#[cfg_attr(target_os = "android", allow(unused_mut))]
#[cfg_attr(any(target_os = "android", feature = "emscripten"), allow(unused_mut))]
let mut inner = self.inner.lock();
match raw_window_handle {
@ -721,6 +734,7 @@ impl crate::Instance<super::Api> for Instance {
return Err(crate::InstanceError);
}
}
#[cfg(not(feature = "emscripten"))]
Rwh::Wayland(handle) => {
/* Wayland displays are not sharable between surfaces so if the
* surface we receive from this handle is from a different
@ -757,6 +771,8 @@ impl crate::Instance<super::Api> for Instance {
drop(old_inner);
}
}
#[cfg(feature = "emscripten")]
Rwh::Web(_) => {}
other => {
log::error!("Unsupported window: {:?}", other);
return Err(crate::InstanceError);
@ -975,6 +991,8 @@ impl crate::Surface<super::Api> for Surface {
wl_window = Some(window);
window
}
#[cfg(feature = "emscripten")]
(WindowKind::Unknown, Rwh::Web(handle)) => handle.id as *mut std::ffi::c_void,
(WindowKind::Unknown, Rwh::Win32(handle)) => handle.hwnd,
(WindowKind::Unknown, Rwh::AppKit(handle)) => handle.ns_view,
_ => {
@ -1014,8 +1032,14 @@ impl crate::Surface<super::Api> for Surface {
}
attributes.push(egl::ATTRIB_NONE as i32);
#[cfg(not(feature = "emscripten"))]
let egl1_5 = self.egl.instance.upcast::<egl::EGL1_5>();
#[cfg(feature = "emscripten")]
let egl1_5: Option<&Arc<EglInstance>> = Some(&self.egl.instance);
// Careful, we can still be in 1.4 version even if `upcast` succeeds
let raw_result = match self.egl.instance.upcast::<egl::EGL1_5>() {
let raw_result = match egl1_5 {
Some(egl) if self.wsi.kind != WindowKind::Unknown => {
let attributes_usize = attributes
.into_iter()

View File

@ -56,9 +56,9 @@ To address this, we invalidate the vertex buffers based on:
*/
#[cfg(not(target_arch = "wasm32"))]
#[cfg(any(not(target_arch = "wasm32"), feature = "emscripten"))]
mod egl;
#[cfg(target_arch = "wasm32")]
#[cfg(all(target_arch = "wasm32", not(feature = "emscripten")))]
mod web;
mod adapter;
@ -67,10 +67,10 @@ mod conv;
mod device;
mod queue;
#[cfg(not(target_arch = "wasm32"))]
#[cfg(any(not(target_arch = "wasm32"), feature = "emscripten"))]
use self::egl::{AdapterContext, Instance, Surface};
#[cfg(target_arch = "wasm32")]
#[cfg(all(target_arch = "wasm32", not(feature = "emscripten")))]
use self::web::{AdapterContext, Instance, Surface};
use arrayvec::ArrayVec;

View File

@ -1176,10 +1176,10 @@ impl crate::Queue<super::Api> for super::Queue {
surface: &mut super::Surface,
texture: super::Texture,
) -> Result<(), crate::SurfaceError> {
#[cfg(not(target_arch = "wasm32"))]
#[cfg(any(not(target_arch = "wasm32"), feature = "emscripten"))]
let gl = &self.shared.context.get_without_egl_lock();
#[cfg(target_arch = "wasm32")]
#[cfg(all(target_arch = "wasm32", not(feature = "emscripten")))]
let gl = &self.shared.context.glow_context;
surface.present(texture, gl)

View File

@ -83,6 +83,7 @@ trace = ["serde", "wgc/trace"]
replay = ["serde", "wgc/replay"]
angle = ["wgc/angle"]
webgl = ["wgc"]
emscripten = ["webgl"]
[target.'cfg(not(target_arch = "wasm32"))'.dependencies.wgc]
package = "wgpu-core"
@ -102,7 +103,7 @@ package = "wgpu-types"
path = "../wgpu-types"
version = "0.12"
[target.'cfg(not(target_arch = "wasm32"))'.dependencies.hal]
[target.'cfg(any(not(target_arch = "wasm32"), target_os = "emscripten"))'.dependencies.hal]
package = "wgpu-hal"
path = "../wgpu-hal"
version = "0.12"

View File

@ -38,7 +38,7 @@ impl fmt::Debug for Context {
}
impl Context {
#[cfg(not(target_arch = "wasm32"))]
#[cfg(any(not(target_arch = "wasm32"), feature = "emscripten"))]
pub unsafe fn from_hal_instance<A: wgc::hub::HalApi>(hal_instance: A::Instance) -> Self {
Self(wgc::hub::Global::from_hal_instance::<A>(
"wgpu",
@ -51,7 +51,7 @@ impl Context {
&self.0
}
#[cfg(not(target_arch = "wasm32"))]
#[cfg(any(not(target_arch = "wasm32"), feature = "emscripten"))]
pub fn enumerate_adapters(&self, backends: wgt::Backends) -> Vec<wgc::id::AdapterId> {
self.0
.enumerate_adapters(wgc::instance::AdapterInputs::Mask(backends, |_| {
@ -59,7 +59,7 @@ impl Context {
}))
}
#[cfg(not(target_arch = "wasm32"))]
#[cfg(any(not(target_arch = "wasm32"), feature = "emscripten"))]
pub unsafe fn create_adapter_from_hal<A: wgc::hub::HalApi>(
&self,
hal_adapter: hal::ExposedAdapter<A>,
@ -67,7 +67,7 @@ impl Context {
self.0.create_adapter_from_hal(hal_adapter, PhantomData)
}
#[cfg(not(target_arch = "wasm32"))]
#[cfg(any(not(target_arch = "wasm32"), feature = "emscripten"))]
pub unsafe fn create_device_from_hal<A: wgc::hub::HalApi>(
&self,
adapter: &wgc::id::AdapterId,
@ -94,7 +94,7 @@ impl Context {
Ok((device, device_id))
}
#[cfg(not(target_arch = "wasm32"))]
#[cfg(any(not(target_arch = "wasm32"), feature = "emscripten"))]
pub unsafe fn create_texture_from_hal<A: wgc::hub::HalApi>(
&self,
hal_texture: A::Texture,
@ -123,7 +123,7 @@ impl Context {
}
}
#[cfg(not(target_arch = "wasm32"))]
#[cfg(any(not(target_arch = "wasm32"), feature = "emscripten"))]
pub unsafe fn device_as_hal<A: wgc::hub::HalApi, F: FnOnce(Option<&A::Device>) -> R, R>(
&self,
device: &Device,
@ -133,7 +133,7 @@ impl Context {
.device_as_hal::<A, F, R>(device.id, hal_device_callback)
}
#[cfg(not(target_arch = "wasm32"))]
#[cfg(any(not(target_arch = "wasm32"), feature = "emscripten"))]
pub unsafe fn texture_as_hal<A: wgc::hub::HalApi, F: FnOnce(Option<&A::Texture>)>(
&self,
texture: &Texture,
@ -143,7 +143,7 @@ impl Context {
.texture_as_hal::<A, F>(texture.id, hal_texture_callback)
}
#[cfg(not(target_arch = "wasm32"))]
#[cfg(any(not(target_arch = "wasm32"), feature = "emscripten"))]
pub fn generate_report(&self) -> wgc::hub::GlobalReport {
self.0.generate_report()
}
@ -1528,7 +1528,7 @@ impl crate::Context for Context {
#[cfg_attr(target_arch = "wasm32", allow(unused))]
fn device_drop(&self, device: &Self::DeviceId) {
#[cfg(not(target_arch = "wasm32"))]
#[cfg(any(not(target_arch = "wasm32"), feature = "emscripten"))]
{
let global = &self.0;
match wgc::gfx_select!(device.id => global.device_poll(device.id, true)) {
@ -1537,7 +1537,7 @@ impl crate::Context for Context {
}
}
//TODO: make this work in general
#[cfg(not(target_arch = "wasm32"))]
#[cfg(any(not(target_arch = "wasm32"), feature = "emscripten"))]
#[cfg(feature = "metal-auto-capture")]
{
let global = &self.0;

View File

@ -1431,7 +1431,7 @@ impl Instance {
/// # Safety
///
/// Refer to the creation of wgpu-hal Instance for every backend.
#[cfg(not(target_arch = "wasm32"))]
#[cfg(any(not(target_arch = "wasm32"), feature = "emscripten"))]
pub unsafe fn from_hal<A: wgc::hub::HalApi>(hal_instance: A::Instance) -> Self {
Self {
context: Arc::new(C::from_hal_instance::<A>(hal_instance)),
@ -1443,7 +1443,7 @@ impl Instance {
/// # Arguments
///
/// - `backends` - Backends from which to enumerate adapters.
#[cfg(not(target_arch = "wasm32"))]
#[cfg(any(not(target_arch = "wasm32"), feature = "emscripten"))]
pub fn enumerate_adapters(&self, backends: Backends) -> impl Iterator<Item = Adapter> {
let context = Arc::clone(&self.context);
self.context
@ -1474,7 +1474,7 @@ impl Instance {
/// # Safety
///
/// `hal_adapter` must be created from this instance internal handle.
#[cfg(not(target_arch = "wasm32"))]
#[cfg(any(not(target_arch = "wasm32"), feature = "emscripten"))]
pub unsafe fn create_adapter_from_hal<A: wgc::hub::HalApi>(
&self,
hal_adapter: hal::ExposedAdapter<A>,
@ -1555,7 +1555,7 @@ impl Instance {
}
/// Generates memory report.
#[cfg(not(target_arch = "wasm32"))]
#[cfg(any(not(target_arch = "wasm32"), feature = "emscripten"))]
pub fn generate_report(&self) -> wgc::hub::GlobalReport {
self.context.generate_report()
}
@ -1607,7 +1607,7 @@ impl Adapter {
///
/// - `hal_device` must be created from this adapter internal handle.
/// - `desc.features` must be a subset of `hal_device` features.
#[cfg(not(target_arch = "wasm32"))]
#[cfg(any(not(target_arch = "wasm32"), feature = "emscripten"))]
pub unsafe fn create_device_from_hal<A: wgc::hub::HalApi>(
&self,
hal_device: hal::OpenDevice<A>,
@ -1846,7 +1846,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(not(target_arch = "wasm32"))]
#[cfg(any(not(target_arch = "wasm32"), feature = "emscripten"))]
pub unsafe fn create_texture_from_hal<A: wgc::hub::HalApi>(
&self,
hal_texture: A::Texture,
@ -1910,7 +1910,7 @@ impl Device {
/// # Safety
///
/// - The raw handle obtained from the hal Device must not be manually destroyed
#[cfg(not(target_arch = "wasm32"))]
#[cfg(any(not(target_arch = "wasm32"), feature = "emscripten"))]
pub unsafe fn as_hal<A: wgc::hub::HalApi, F: FnOnce(Option<&A::Device>) -> R, R>(
&self,
hal_device_callback: F,
@ -2203,7 +2203,7 @@ impl Texture {
/// # Safety
///
/// - The raw handle obtained from the hal Texture must not be manually destroyed
#[cfg(not(target_arch = "wasm32"))]
#[cfg(any(not(target_arch = "wasm32"), feature = "emscripten"))]
pub unsafe fn as_hal<A: wgc::hub::HalApi, F: FnOnce(Option<&A::Texture>)>(
&self,
hal_texture_callback: F,