From 4ad527d81b601f2934309a3a384c48eb9f284263 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mariusz=20Kry=C5=84ski?= Date: Fri, 29 Oct 2021 00:19:48 +0000 Subject: [PATCH] WebGL2 support (#3039) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Objective Make possible to use wgpu gles backend on in the browser (wasm32 + WebGL2). ## Solution It is built on top of old @cart patch initializing windows before wgpu. Also: - initializes wgpu with `Backends::GL` and proper `wgpu::Limits` on wasm32 - changes default texture format to `wgpu::TextureFormat::Rgba8UnormSrgb` Co-authored-by: Mariusz KryƄski --- crates/bevy_internal/src/default_plugins.rs | 6 +-- crates/bevy_winit/src/lib.rs | 49 +++++++++++++++------ pipelined/bevy_render2/Cargo.toml | 3 ++ pipelined/bevy_render2/src/lib.rs | 41 +++++++++++++---- pipelined/bevy_render2/src/renderer/mod.rs | 8 ++-- pipelined/bevy_render2/src/texture/mod.rs | 2 +- 6 files changed, 79 insertions(+), 30 deletions(-) diff --git a/crates/bevy_internal/src/default_plugins.rs b/crates/bevy_internal/src/default_plugins.rs index 27e206d657af8..0a97da009ac62 100644 --- a/crates/bevy_internal/src/default_plugins.rs +++ b/crates/bevy_internal/src/default_plugins.rs @@ -119,6 +119,9 @@ impl PluginGroup for PipelinedDefaultPlugins { group.add(bevy_asset::AssetPlugin::default()); group.add(bevy_scene::ScenePlugin::default()); + #[cfg(feature = "bevy_winit")] + group.add(bevy_winit::WinitPlugin::default()); + #[cfg(feature = "bevy_render2")] { group.add(bevy_render2::RenderPlugin::default()); @@ -137,8 +140,5 @@ impl PluginGroup for PipelinedDefaultPlugins { #[cfg(feature = "bevy_gltf2")] group.add(bevy_gltf2::GltfPlugin::default()); } - - #[cfg(feature = "bevy_winit")] - group.add(bevy_winit::WinitPlugin::default()); } } diff --git a/crates/bevy_winit/src/lib.rs b/crates/bevy_winit/src/lib.rs index af072b7a60f9f..298f9fb0f6a40 100644 --- a/crates/bevy_winit/src/lib.rs +++ b/crates/bevy_winit/src/lib.rs @@ -43,6 +43,9 @@ impl Plugin for WinitPlugin { app.init_resource::() .set_runner(winit_runner) .add_system_to_stage(CoreStage::PostUpdate, change_window.exclusive_system()); + let mut event_loop = EventLoop::new(); + handle_initial_window_events(&mut app.world, &mut event_loop); + app.insert_non_send_resource(event_loop); } } @@ -207,21 +210,22 @@ where } pub fn winit_runner(app: App) { - winit_runner_with(app, EventLoop::new()); + winit_runner_with(app); } -#[cfg(any( - target_os = "linux", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd" -))] -pub fn winit_runner_any_thread(app: App) { - winit_runner_with(app, EventLoop::new_any_thread()); -} - -pub fn winit_runner_with(mut app: App, mut event_loop: EventLoop<()>) { +// #[cfg(any( +// target_os = "linux", +// target_os = "dragonfly", +// target_os = "freebsd", +// target_os = "netbsd", +// target_os = "openbsd" +// ))] +// pub fn winit_runner_any_thread(app: App) { +// winit_runner_with(app, EventLoop::new_any_thread()); +// } + +pub fn winit_runner_with(mut app: App) { + let mut event_loop = app.world.remove_non_send::>().unwrap(); let mut create_window_event_reader = ManualEventReader::::default(); let mut app_exit_event_reader = ManualEventReader::::default(); app.world.insert_non_send(event_loop.create_proxy()); @@ -525,3 +529,22 @@ fn handle_create_window_events( }); } } + +fn handle_initial_window_events(world: &mut World, event_loop: &EventLoop<()>) { + let world = world.cell(); + let mut winit_windows = world.get_resource_mut::().unwrap(); + let mut windows = world.get_resource_mut::().unwrap(); + let mut create_window_events = world.get_resource_mut::>().unwrap(); + let mut window_created_events = world.get_resource_mut::>().unwrap(); + for create_window_event in create_window_events.drain() { + let window = winit_windows.create_window( + event_loop, + create_window_event.id, + &create_window_event.descriptor, + ); + windows.add(window); + window_created_events.send(WindowCreated { + id: create_window_event.id, + }); + } +} diff --git a/pipelined/bevy_render2/Cargo.toml b/pipelined/bevy_render2/Cargo.toml index db947d62f394b..1544990fd2310 100644 --- a/pipelined/bevy_render2/Cargo.toml +++ b/pipelined/bevy_render2/Cargo.toml @@ -45,6 +45,9 @@ parking_lot = "0.11.0" regex = "1.5" crevice = { path = "../../crates/crevice", version = "0.6.0" } +[target.'cfg(target_arch = "wasm32")'.dependencies] +wgpu = { version = "0.11.0", features = ["spirv", "webgl"] } + [features] png = ["image/png"] hdr = ["image/hdr"] diff --git a/pipelined/bevy_render2/src/lib.rs b/pipelined/bevy_render2/src/lib.rs index 526e41b997004..023efdcd46aeb 100644 --- a/pipelined/bevy_render2/src/lib.rs +++ b/pipelined/bevy_render2/src/lib.rs @@ -85,15 +85,40 @@ struct ScratchRenderWorld(World); impl Plugin for RenderPlugin { fn build(&self, app: &mut App) { - let (instance, device, queue) = - futures_lite::future::block_on(renderer::initialize_renderer( - wgpu::util::backend_bits_from_env().unwrap_or(Backends::PRIMARY), - &wgpu::RequestAdapterOptions { - power_preference: wgpu::PowerPreference::HighPerformance, - ..Default::default() + let default_backend = if cfg!(not(target_arch = "wasm32")) { + Backends::PRIMARY + } else { + Backends::GL + }; + let backends = wgpu::util::backend_bits_from_env().unwrap_or(default_backend); + let instance = wgpu::Instance::new(backends); + let surface = { + let world = app.world.cell(); + let windows = world.get_resource_mut::().unwrap(); + let raw_handle = windows.get_primary().map(|window| unsafe { + let handle = window.raw_window_handle().get_handle(); + instance.create_surface(&handle) + }); + raw_handle + }; + let (device, queue) = futures_lite::future::block_on(renderer::initialize_renderer( + &instance, + &wgpu::RequestAdapterOptions { + power_preference: wgpu::PowerPreference::HighPerformance, + compatible_surface: surface.as_ref(), + ..Default::default() + }, + &wgpu::DeviceDescriptor { + features: wgpu::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES, + #[cfg(not(target_arch = "wasm32"))] + limits: wgpu::Limits::default(), + #[cfg(target_arch = "wasm32")] + limits: wgpu::Limits { + ..wgpu::Limits::downlevel_webgl2_defaults() }, - &wgpu::DeviceDescriptor::default(), - )); + ..Default::default() + }, + )); app.insert_resource(device.clone()) .insert_resource(queue.clone()) .add_asset::() diff --git a/pipelined/bevy_render2/src/renderer/mod.rs b/pipelined/bevy_render2/src/renderer/mod.rs index 840faed624036..e3ef3635c3214 100644 --- a/pipelined/bevy_render2/src/renderer/mod.rs +++ b/pipelined/bevy_render2/src/renderer/mod.rs @@ -42,12 +42,10 @@ pub type RenderQueue = Arc; pub type RenderInstance = Instance; pub async fn initialize_renderer( - backends: Backends, + instance: &Instance, request_adapter_options: &RequestAdapterOptions<'_>, device_descriptor: &DeviceDescriptor<'_>, -) -> (RenderInstance, RenderDevice, RenderQueue) { - let instance = wgpu::Instance::new(backends); - +) -> (RenderDevice, RenderQueue) { let adapter = instance .request_adapter(request_adapter_options) .await @@ -72,7 +70,7 @@ pub async fn initialize_renderer( .unwrap(); let device = Arc::new(device); let queue = Arc::new(queue); - (instance, RenderDevice::from(device), queue) + (RenderDevice::from(device), queue) } pub struct RenderContext { diff --git a/pipelined/bevy_render2/src/texture/mod.rs b/pipelined/bevy_render2/src/texture/mod.rs index cd25dd57387ae..59eb1f5dc11d0 100644 --- a/pipelined/bevy_render2/src/texture/mod.rs +++ b/pipelined/bevy_render2/src/texture/mod.rs @@ -42,7 +42,7 @@ pub trait BevyDefault { impl BevyDefault for wgpu::TextureFormat { fn bevy_default() -> Self { - if cfg!(target_os = "android") { + if cfg!(target_os = "android") || cfg!(target_arch = "wasm32") { // Bgra8UnormSrgb texture missing on some Android devices wgpu::TextureFormat::Rgba8UnormSrgb } else {