diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c49157a7fd824..b072a500183e4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -144,7 +144,7 @@ jobs: - name: Install Cargo APK run: cargo install --force cargo-apk - name: Build APK - run: cargo apk build --example android + run: cd examples/android && cargo apk build markdownlint: runs-on: ubuntu-latest @@ -288,7 +288,7 @@ jobs: source: './examples/' targets: '[ "./Cargo.toml", "./examples/README.md" ]' file-types: '[".rs"]' - exclude-folders: '["./examples/ios"]' + exclude-folders: '["./examples/ios", "./examples/android"]' exclude-files: '[]' check-unused-dependencies: diff --git a/Cargo.toml b/Cargo.toml index 30fcaa9de24dd..1677736d8565b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ repository = "https://github.com/bevyengine/bevy" [workspace] exclude = ["benches", "crates/bevy_ecs_compile_fail_tests"] -members = ["crates/*", "examples/ios", "tools/ci", "errors"] +members = ["crates/*", "examples/ios", "tools/ci", "errors", "examples/android"] [features] default = [ @@ -542,19 +542,4 @@ path = "examples/window/transparent_window.rs" [[example]] name = "window_settings" -path = "examples/window/window_settings.rs" - -# Android -[[example]] -crate-type = ["cdylib"] -name = "android" -path = "examples/android/android.rs" - -[package.metadata.android] -apk_label = "Bevy Example" -assets = "assets" -res = "assets/android-res" -icon = "@mipmap/ic_launcher" -build_targets = ["aarch64-linux-android", "armv7-linux-androideabi"] -min_sdk_version = 16 -target_sdk_version = 29 +path = "examples/window/window_settings.rs" \ No newline at end of file diff --git a/crates/bevy_core_pipeline/src/lib.rs b/crates/bevy_core_pipeline/src/lib.rs index a46bba2789f06..75422e0dec823 100644 --- a/crates/bevy_core_pipeline/src/lib.rs +++ b/crates/bevy_core_pipeline/src/lib.rs @@ -22,6 +22,7 @@ use std::ops::Range; use bevy_app::{App, Plugin}; use bevy_core::FloatOrd; use bevy_ecs::prelude::*; +use bevy_render::texture::DEFAULT_DEPTH_FORMAT; use bevy_render::{ camera::{ActiveCameras, CameraPlugin, RenderTarget}, color::Color, @@ -414,8 +415,7 @@ pub fn prepare_core_views_system( mip_level_count: 1, sample_count: msaa.samples, dimension: TextureDimension::D2, - format: TextureFormat::Depth32Float, /* PERF: vulkan docs recommend using 24 - * bit depth for better performance */ + format: DEFAULT_DEPTH_FORMAT, usage: TextureUsages::RENDER_ATTACHMENT, }, ); diff --git a/crates/bevy_pbr/src/render/light.rs b/crates/bevy_pbr/src/render/light.rs index c0605748262ae..7820df82a2e03 100644 --- a/crates/bevy_pbr/src/render/light.rs +++ b/crates/bevy_pbr/src/render/light.rs @@ -154,7 +154,7 @@ pub const MAX_POINT_LIGHT_SHADOW_MAPS: usize = 10; pub const MAX_DIRECTIONAL_LIGHTS: usize = 1; pub const POINT_SHADOW_LAYERS: u32 = (6 * MAX_POINT_LIGHT_SHADOW_MAPS) as u32; pub const DIRECTIONAL_SHADOW_LAYERS: u32 = MAX_DIRECTIONAL_LIGHTS as u32; -pub const SHADOW_FORMAT: TextureFormat = TextureFormat::Depth32Float; +pub const SHADOW_FORMAT: TextureFormat = DEFAULT_DEPTH_FORMAT; pub struct ShadowPipeline { pub view_layout: BindGroupLayout, diff --git a/crates/bevy_pbr/src/render/mesh.rs b/crates/bevy_pbr/src/render/mesh.rs index ac0ec3acd4360..4319c7607de0e 100644 --- a/crates/bevy_pbr/src/render/mesh.rs +++ b/crates/bevy_pbr/src/render/mesh.rs @@ -10,6 +10,7 @@ use bevy_ecs::{ }; use bevy_math::{Mat4, Size}; use bevy_reflect::TypeUuid; +use bevy_render::texture::DEFAULT_DEPTH_FORMAT; use bevy_render::{ mesh::{GpuBufferInfo, Mesh, MeshVertexBufferLayout}, render_asset::RenderAssets, @@ -477,7 +478,7 @@ impl SpecializedMeshPipeline for MeshPipeline { strip_index_format: None, }, depth_stencil: Some(DepthStencilState { - format: TextureFormat::Depth32Float, + format: DEFAULT_DEPTH_FORMAT, depth_write_enabled, depth_compare: CompareFunction::Greater, stencil: StencilState { diff --git a/crates/bevy_render/src/lib.rs b/crates/bevy_render/src/lib.rs index 36f4cbc292500..8393515348961 100644 --- a/crates/bevy_render/src/lib.rs +++ b/crates/bevy_render/src/lib.rs @@ -122,25 +122,16 @@ impl Plugin for RenderPlugin { if let Some(backends) = options.backends { let instance = wgpu::Instance::new(backends); - let surface = { - let windows = app.world.resource_mut::(); - let raw_handle = windows.get_primary().map(|window| unsafe { - let handle = window.raw_window_handle().get_handle(); - instance.create_surface(&handle) - }); - raw_handle - }; - let request_adapter_options = wgpu::RequestAdapterOptions { - power_preference: options.power_preference, - compatible_surface: surface.as_ref(), - ..Default::default() - }; - let (device, queue, adapter_info) = futures_lite::future::block_on( - renderer::initialize_renderer(&instance, &options, &request_adapter_options), + let (device, adapter, queue, adapter_info) = futures_lite::future::block_on( + renderer::initialize_renderer(app, &instance, backends, &options), + ); + debug!("Configured wgpu adapter Limits: {:#?}", &adapter.limits()); + debug!( + "Configured wgpu adapter Features: {:#?}", + &adapter.features() ); - debug!("Configured wgpu adapter Limits: {:#?}", device.limits()); - debug!("Configured wgpu adapter Features: {:#?}", device.features()); app.insert_resource(device.clone()) + .insert_resource(adapter.clone()) .insert_resource(queue.clone()) .insert_resource(adapter_info.clone()) .init_resource::() @@ -169,6 +160,7 @@ impl Plugin for RenderPlugin { .add_stage(RenderStage::Cleanup, SystemStage::parallel()) .insert_resource(instance) .insert_resource(device) + .insert_resource(adapter) .insert_resource(queue) .insert_resource(adapter_info) .insert_resource(render_pipeline_cache) diff --git a/crates/bevy_render/src/renderer/mod.rs b/crates/bevy_render/src/renderer/mod.rs index 2789ef51c8194..1d5b8f7c60a72 100644 --- a/crates/bevy_render/src/renderer/mod.rs +++ b/crates/bevy_render/src/renderer/mod.rs @@ -12,7 +12,7 @@ use crate::{ }; use bevy_ecs::prelude::*; use std::sync::Arc; -use wgpu::{AdapterInfo, CommandEncoder, Instance, Queue, RequestAdapterOptions}; +use wgpu::{Adapter, AdapterInfo, CommandEncoder, Instance, Queue}; /// Updates the [`RenderGraph`] with all of its nodes and then runs it to render the entire frame. pub fn render_system(world: &mut World) { @@ -72,6 +72,8 @@ pub fn render_system(world: &mut World) { /// This queue is used to enqueue tasks for the GPU to execute asynchronously. pub type RenderQueue = Arc; +pub type RenderAdapter = Arc; + /// The GPU instance is used to initialize the [`RenderQueue`] and [`RenderDevice`], /// aswell as to create [`WindowSurfaces`](crate::view::window::WindowSurfaces). pub type RenderInstance = Instance; @@ -79,17 +81,86 @@ pub type RenderInstance = Instance; /// Initializes the renderer by retrieving and preparing the GPU instance, device and queue /// for the specified backend. pub async fn initialize_renderer( - instance: &Instance, + #[cfg_attr(not(target_arch = "wasm32"), allow(unused))] app: &mut bevy_app::App, + instance: &wgpu::Instance, + #[cfg_attr(target_arch = "wasm32", allow(unused))] backends: wgpu::Backends, options: &WgpuSettings, - request_adapter_options: &RequestAdapterOptions<'_>, -) -> (RenderDevice, RenderQueue, AdapterInfo) { - let adapter = instance - .request_adapter(request_adapter_options) - .await - .expect("Unable to find a GPU! Make sure you have installed required drivers!"); +) -> (RenderDevice, RenderAdapter, RenderQueue, AdapterInfo) { + let adapter = { + #[cfg(not(target_arch = "wasm32"))] + { + use crate::texture::{BevyDefault as _, DEFAULT_DEPTH_FORMAT}; + let mut adapters: Vec = instance.enumerate_adapters(backends).collect(); + let (mut integrated, mut discrete, mut virt, mut cpu, mut other) = + (None, None, None, None, None); - let adapter_info = adapter.get_info(); - info!("{:?}", adapter_info); + for (i, adapter) in adapters.iter().enumerate() { + let default_texture_format_features = + adapter.get_texture_format_features(wgpu::TextureFormat::bevy_default()); + let default_depth_format_features = + adapter.get_texture_format_features(DEFAULT_DEPTH_FORMAT); + if default_texture_format_features + .allowed_usages + .contains(wgpu::TextureUsages::RENDER_ATTACHMENT) + && default_depth_format_features + .allowed_usages + .contains(wgpu::TextureUsages::RENDER_ATTACHMENT) + { + let info = adapter.get_info(); + match info.device_type { + wgpu::DeviceType::IntegratedGpu => { + integrated = integrated.or(Some(i)); + } + wgpu::DeviceType::DiscreteGpu => { + discrete = discrete.or(Some(i)); + } + wgpu::DeviceType::VirtualGpu => { + virt = virt.or(Some(i)); + } + wgpu::DeviceType::Cpu => { + cpu = cpu.or(Some(i)); + } + wgpu::DeviceType::Other => { + other = other.or(Some(i)); + } + } + } + } + + let preferred_gpu_index = match options.power_preference { + wgpu::PowerPreference::LowPower => { + integrated.or(other).or(discrete).or(virt).or(cpu) + } + wgpu::PowerPreference::HighPerformance => { + discrete.or(other).or(integrated).or(virt).or(cpu) + } + } + .expect("Unable to find a GPU! Make sure you have installed required drivers!"); + + adapters.swap_remove(preferred_gpu_index) + } + + #[cfg(target_arch = "wasm32")] + { + 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 + }; + instance + .request_adapter(&wgpu::RequestAdapterOptions { + power_preference: options.power_preference, + compatible_surface: surface.as_ref(), + ..Default::default() + }) + .await + .expect("Unable to find a GPU! Make sure you have installed required drivers!") + } + }; #[cfg(feature = "wgpu_trace")] let trace_path = { @@ -104,6 +175,10 @@ pub async fn initialize_renderer( // Maybe get features and limits based on what is supported by the adapter/backend let mut features = wgpu::Features::empty(); let mut limits = options.limits.clone(); + + let adapter_info = adapter.get_info(); + info!("{:?}", adapter_info); + if matches!(options.priority, WgpuSettingsPriority::Functionality) { features = adapter.features() | wgpu::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES; if adapter_info.device_type == wgpu::DeviceType::DiscreteGpu { @@ -227,8 +302,9 @@ pub async fn initialize_renderer( .await .unwrap(); let device = Arc::new(device); + let adapter = Arc::new(adapter); let queue = Arc::new(queue); - (RenderDevice::from(device), queue, adapter_info) + (RenderDevice::from(device), adapter, queue, adapter_info) } /// The context with all information required to interact with the GPU. diff --git a/crates/bevy_render/src/texture/mod.rs b/crates/bevy_render/src/texture/mod.rs index 3f90ef17de0ec..83680a3f3519e 100644 --- a/crates/bevy_render/src/texture/mod.rs +++ b/crates/bevy_render/src/texture/mod.rs @@ -54,6 +54,9 @@ impl Plugin for ImagePlugin { } } +// PERF: vulkan docs recommend using 24 bit depth for better performance +pub const DEFAULT_DEPTH_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Depth32Float; + pub trait BevyDefault { fn bevy_default() -> Self; } diff --git a/crates/bevy_render/src/view/window.rs b/crates/bevy_render/src/view/window.rs index c06776c54a878..6f8fe2a8c8253 100644 --- a/crates/bevy_render/src/view/window.rs +++ b/crates/bevy_render/src/view/window.rs @@ -1,3 +1,4 @@ +use crate::renderer::RenderAdapter; use crate::{ render_resource::TextureView, renderer::{RenderDevice, RenderInstance}, @@ -120,6 +121,7 @@ pub fn prepare_windows( _marker: NonSend, mut windows: ResMut, mut window_surfaces: ResMut, + render_adapter: Res, render_device: Res, render_instance: Res, ) { @@ -133,8 +135,12 @@ pub fn prepare_windows( render_instance.create_surface(&window.handle.get_handle()) }); + let surface_format = surface + .get_preferred_format(&render_adapter) + .unwrap_or_else(TextureFormat::bevy_default); + let swap_chain_descriptor = wgpu::SurfaceConfiguration { - format: TextureFormat::bevy_default(), + format: surface_format, width: window.physical_width, height: window.physical_height, usage: wgpu::TextureUsages::RENDER_ATTACHMENT, diff --git a/crates/bevy_winit/src/lib.rs b/crates/bevy_winit/src/lib.rs index 41b952aa65115..6eb5744160871 100644 --- a/crates/bevy_winit/src/lib.rs +++ b/crates/bevy_winit/src/lib.rs @@ -44,6 +44,8 @@ impl Plugin for WinitPlugin { .set_runner(winit_runner) .add_system_to_stage(CoreStage::PostUpdate, change_window.exclusive_system()); let event_loop = EventLoop::new(); + // TODO: Required for WebGL. Currently breaks only android initialization. Should be a part of init system + #[cfg(not(target_os = "android"))] handle_initial_window_events(&mut app.world, &event_loop); app.insert_non_send_resource(event_loop); } @@ -628,6 +630,7 @@ fn handle_create_window_events( } } +#[cfg(not(target_os = "android"))] fn handle_initial_window_events(world: &mut World, event_loop: &EventLoop<()>) { let world = world.cell(); let mut winit_windows = world.get_non_send_mut::().unwrap(); diff --git a/examples/README.md b/examples/README.md index bbf1278529428..c543660d4d746 100644 --- a/examples/README.md +++ b/examples/README.md @@ -287,7 +287,8 @@ When using `NDK (Side by side)`, the environment variable `ANDROID_NDK_ROOT` mus To run on a device setup for Android development, run: ```sh -cargo apk run --example android +cd examples/android +cargo apk run ``` :warning: At this time Bevy does not work in Android Emulator. @@ -309,7 +310,7 @@ Bevy by default targets Android API level 29 in its examples which is the Users of older phones may want to use an older API when testing. -To use a different API, the following fields must be updated in Cargo.toml: +To use a different API, the following fields must be updated in [Cargo.toml](./android/Cargo.toml): ```toml [package.metadata.android] @@ -317,10 +318,6 @@ target_sdk_version = >>API<< min_sdk_version = >>API or less<< ``` -Example | File | Description ---- | --- | --- -`android` | [`android/android.rs`](./android/android.rs) | The `3d/3d_scene.rs` example for Android - ## iOS ### Setup diff --git a/examples/android/Cargo.toml b/examples/android/Cargo.toml new file mode 100644 index 0000000000000..f439122232fe5 --- /dev/null +++ b/examples/android/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "bevy_android_example" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["cdylib"] + +[dependencies] + +[dependencies.bevy] +path = "../../" +features = ["bevy_winit", "render"] +default-features = false + +[package.metadata.android] +assets = "../../assets" +resources = "../../assets/android-res" +build_targets = ["aarch64-linux-android", "armv7-linux-androideabi"] +min_sdk_version = 16 +target_sdk_version = 29 + + +[package.metadata.android.application] +label = "Bevy Example" +icon = "@mipmap/ic_launcher" \ No newline at end of file diff --git a/examples/android/android.rs b/examples/android/src/lib.rs similarity index 96% rename from examples/android/android.rs rename to examples/android/src/lib.rs index 2ae75a18842ee..fab427b745e38 100644 --- a/examples/android/android.rs +++ b/examples/android/src/lib.rs @@ -4,7 +4,6 @@ use bevy::prelude::*; #[bevy_main] fn main() { App::new() - .insert_resource(Msaa { samples: 2 }) .add_plugins(DefaultPlugins) .add_startup_system(setup) .run();