WebGL2 support (#3039)

# 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 <mrk@sed.pl>
This commit is contained in:
Mariusz Kryński 2021-10-29 00:46:18 +00:00
parent a2ea9279b2
commit 7d932ac1d8
6 changed files with 79 additions and 38 deletions

View file

@ -119,6 +119,9 @@ impl PluginGroup for PipelinedDefaultPlugins {
group.add(bevy_asset::AssetPlugin::default()); group.add(bevy_asset::AssetPlugin::default());
group.add(bevy_scene::ScenePlugin::default()); group.add(bevy_scene::ScenePlugin::default());
#[cfg(feature = "bevy_winit")]
group.add(bevy_winit::WinitPlugin::default());
#[cfg(feature = "bevy_render2")] #[cfg(feature = "bevy_render2")]
{ {
group.add(bevy_render2::RenderPlugin::default()); group.add(bevy_render2::RenderPlugin::default());
@ -137,8 +140,5 @@ impl PluginGroup for PipelinedDefaultPlugins {
#[cfg(feature = "bevy_gltf2")] #[cfg(feature = "bevy_gltf2")]
group.add(bevy_gltf2::GltfPlugin::default()); group.add(bevy_gltf2::GltfPlugin::default());
} }
#[cfg(feature = "bevy_winit")]
group.add(bevy_winit::WinitPlugin::default());
} }
} }

View file

@ -26,14 +26,6 @@ use winit::{
}; };
use winit::dpi::LogicalSize; use winit::dpi::LogicalSize;
#[cfg(any(
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd"
))]
use winit::platform::unix::EventLoopExtUnix;
#[derive(Default)] #[derive(Default)]
pub struct WinitPlugin; pub struct WinitPlugin;
@ -43,6 +35,9 @@ impl Plugin for WinitPlugin {
app.init_resource::<WinitWindows>() app.init_resource::<WinitWindows>()
.set_runner(winit_runner) .set_runner(winit_runner)
.add_system_to_stage(CoreStage::PostUpdate, change_window.exclusive_system()); .add_system_to_stage(CoreStage::PostUpdate, change_window.exclusive_system());
let event_loop = EventLoop::new();
handle_initial_window_events(&mut app.world, &event_loop);
app.insert_non_send_resource(event_loop);
} }
} }
@ -207,21 +202,22 @@ where
} }
pub fn winit_runner(app: App) { pub fn winit_runner(app: App) {
winit_runner_with(app, EventLoop::new()); winit_runner_with(app);
} }
#[cfg(any( // #[cfg(any(
target_os = "linux", // target_os = "linux",
target_os = "dragonfly", // target_os = "dragonfly",
target_os = "freebsd", // target_os = "freebsd",
target_os = "netbsd", // target_os = "netbsd",
target_os = "openbsd" // target_os = "openbsd"
))] // ))]
pub fn winit_runner_any_thread(app: App) { // pub fn winit_runner_any_thread(app: App) {
winit_runner_with(app, EventLoop::new_any_thread()); // winit_runner_with(app, EventLoop::new_any_thread());
} // }
pub fn winit_runner_with(mut app: App, mut event_loop: EventLoop<()>) { pub fn winit_runner_with(mut app: App) {
let mut event_loop = app.world.remove_non_send::<EventLoop<()>>().unwrap();
let mut create_window_event_reader = ManualEventReader::<CreateWindow>::default(); let mut create_window_event_reader = ManualEventReader::<CreateWindow>::default();
let mut app_exit_event_reader = ManualEventReader::<AppExit>::default(); let mut app_exit_event_reader = ManualEventReader::<AppExit>::default();
app.world.insert_non_send(event_loop.create_proxy()); app.world.insert_non_send(event_loop.create_proxy());
@ -525,3 +521,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::<WinitWindows>().unwrap();
let mut windows = world.get_resource_mut::<Windows>().unwrap();
let mut create_window_events = world.get_resource_mut::<Events<CreateWindow>>().unwrap();
let mut window_created_events = world.get_resource_mut::<Events<WindowCreated>>().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,
});
}
}

View file

@ -45,6 +45,9 @@ parking_lot = "0.11.0"
regex = "1.5" regex = "1.5"
crevice = { path = "../../crates/crevice", version = "0.6.0" } crevice = { path = "../../crates/crevice", version = "0.6.0" }
[target.'cfg(target_arch = "wasm32")'.dependencies]
wgpu = { version = "0.11.0", features = ["spirv", "webgl"] }
[features] [features]
png = ["image/png"] png = ["image/png"]
hdr = ["image/hdr"] hdr = ["image/hdr"]

View file

@ -85,15 +85,40 @@ struct ScratchRenderWorld(World);
impl Plugin for RenderPlugin { impl Plugin for RenderPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
let (instance, device, queue) = let default_backend = if cfg!(not(target_arch = "wasm32")) {
futures_lite::future::block_on(renderer::initialize_renderer( Backends::PRIMARY
wgpu::util::backend_bits_from_env().unwrap_or(Backends::PRIMARY), } else {
&wgpu::RequestAdapterOptions { Backends::GL
power_preference: wgpu::PowerPreference::HighPerformance, };
..Default::default() 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::<bevy_window::Windows>().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()) app.insert_resource(device.clone())
.insert_resource(queue.clone()) .insert_resource(queue.clone())
.add_asset::<Shader>() .add_asset::<Shader>()

View file

@ -8,7 +8,7 @@ pub use render_device::*;
use crate::{render_graph::RenderGraph, view::ExtractedWindows}; use crate::{render_graph::RenderGraph, view::ExtractedWindows};
use bevy_ecs::prelude::*; use bevy_ecs::prelude::*;
use std::sync::Arc; use std::sync::Arc;
use wgpu::{Backends, CommandEncoder, DeviceDescriptor, Instance, Queue, RequestAdapterOptions}; use wgpu::{CommandEncoder, DeviceDescriptor, Instance, Queue, RequestAdapterOptions};
pub fn render_system(world: &mut World) { pub fn render_system(world: &mut World) {
world.resource_scope(|world, mut graph: Mut<RenderGraph>| { world.resource_scope(|world, mut graph: Mut<RenderGraph>| {
@ -42,12 +42,10 @@ pub type RenderQueue = Arc<Queue>;
pub type RenderInstance = Instance; pub type RenderInstance = Instance;
pub async fn initialize_renderer( pub async fn initialize_renderer(
backends: Backends, instance: &Instance,
request_adapter_options: &RequestAdapterOptions<'_>, request_adapter_options: &RequestAdapterOptions<'_>,
device_descriptor: &DeviceDescriptor<'_>, device_descriptor: &DeviceDescriptor<'_>,
) -> (RenderInstance, RenderDevice, RenderQueue) { ) -> (RenderDevice, RenderQueue) {
let instance = wgpu::Instance::new(backends);
let adapter = instance let adapter = instance
.request_adapter(request_adapter_options) .request_adapter(request_adapter_options)
.await .await
@ -72,7 +70,7 @@ pub async fn initialize_renderer(
.unwrap(); .unwrap();
let device = Arc::new(device); let device = Arc::new(device);
let queue = Arc::new(queue); let queue = Arc::new(queue);
(instance, RenderDevice::from(device), queue) (RenderDevice::from(device), queue)
} }
pub struct RenderContext { pub struct RenderContext {

View file

@ -42,7 +42,7 @@ pub trait BevyDefault {
impl BevyDefault for wgpu::TextureFormat { impl BevyDefault for wgpu::TextureFormat {
fn bevy_default() -> Self { 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 // Bgra8UnormSrgb texture missing on some Android devices
wgpu::TextureFormat::Rgba8UnormSrgb wgpu::TextureFormat::Rgba8UnormSrgb
} else { } else {