diff --git a/crates/bevy_ecs/src/schedule/schedule.rs b/crates/bevy_ecs/src/schedule/schedule.rs index ed40f21fbd..a9067b75b8 100644 --- a/crates/bevy_ecs/src/schedule/schedule.rs +++ b/crates/bevy_ecs/src/schedule/schedule.rs @@ -15,6 +15,7 @@ use bevy_utils::{default, prelude::DebugName, TypeIdMap}; use core::{ any::{Any, TypeId}, fmt::{Debug, Write}, + ops::Range, }; use fixedbitset::FixedBitSet; use log::{error, info, warn}; @@ -752,11 +753,31 @@ new_key_type! { pub struct SystemSetKey; } +/// A node in a [`ScheduleGraph`] with a system or conditions that have not been +/// initialized yet. +/// +/// We have to defer initialization of nodes in the graph until we have +/// `&mut World` access, so we store these in a list ([`ScheduleGraph::uninit`]) +/// until then. In most cases, initialization occurs upon the first run of the +/// schedule. enum UninitializedId { + /// A system and its conditions that have not been initialized yet. System(SystemKey), + /// A system set's conditions that have not been initialized yet. Set { key: SystemSetKey, - first_uninit_condition: usize, + /// The range of indices in [`SystemSets::conditions`] that correspond + /// to conditions that have not been initialized yet. + /// + /// [`SystemSets::conditions`] for a given set may be appended to + /// multiple times (e.g. when `configure_sets` is called multiple with + /// the same set), so we need to track which conditions in that list + /// are newly added and not yet initialized. + /// + /// Systems don't need this tracking because each `add_systems` call + /// creates separate nodes in the graph with their own conditions, + /// so all conditions are initialized together. + uninitialized_conditions: Range, }, } @@ -793,8 +814,8 @@ pub struct ScheduleGraph { pub system_conditions: SecondaryMap>, /// Data about system sets in the schedule system_sets: SystemSets, - /// Systems that have not been initialized yet; for system sets, we store the index of the first uninitialized condition - /// (all the conditions after that index still need to be initialized) + /// Systems, their conditions, and system set conditions that need to be + /// initialized before the schedule can be run. uninit: Vec, /// Directed acyclic graph of the hierarchy (which systems/sets are children of which sets) hierarchy: Dag, @@ -807,7 +828,6 @@ pub struct ScheduleGraph { anonymous_sets: usize, changed: bool, settings: ScheduleBuildSettings, - passes: BTreeMap>, } @@ -1101,9 +1121,10 @@ impl ScheduleGraph { // system init has to be deferred (need `&mut World`) let system_set_conditions = self.system_sets.conditions.entry(key).unwrap().or_default(); + let start = system_set_conditions.len(); self.uninit.push(UninitializedId::Set { key, - first_uninit_condition: system_set_conditions.len(), + uninitialized_conditions: start..(start + conditions.len()), }); system_set_conditions.extend(conditions.into_iter().map(ConditionWithAccess::new)); @@ -1189,11 +1210,9 @@ impl ScheduleGraph { } UninitializedId::Set { key, - first_uninit_condition, + uninitialized_conditions, } => { - for condition in self.system_sets.conditions[key] - .iter_mut() - .skip(first_uninit_condition) + for condition in &mut self.system_sets.conditions[key][uninitialized_conditions] { condition.access = condition.condition.initialize(world); } diff --git a/crates/bevy_pbr/src/render/shadow_sampling.wgsl b/crates/bevy_pbr/src/render/shadow_sampling.wgsl index c7f7253a63..2b35e57037 100644 --- a/crates/bevy_pbr/src/render/shadow_sampling.wgsl +++ b/crates/bevy_pbr/src/render/shadow_sampling.wgsl @@ -422,11 +422,7 @@ fn sample_shadow_cubemap_gaussian( ) -> f32 { // Create an orthonormal basis so we can apply a 2D sampling pattern to a // cubemap. - var up = vec3(0.0, 1.0, 0.0); - if (dot(up, normalize(light_local)) > 0.99) { - up = vec3(1.0, 0.0, 0.0); // Avoid creating a degenerate basis. - } - let basis = orthonormalize(light_local, up) * scale * distance_to_light; + let basis = orthonormalize(normalize(light_local)) * scale * distance_to_light; var sum: f32 = 0.0; sum += sample_shadow_cubemap_at_offset( @@ -469,11 +465,7 @@ fn sample_shadow_cubemap_jittered( ) -> f32 { // Create an orthonormal basis so we can apply a 2D sampling pattern to a // cubemap. - var up = vec3(0.0, 1.0, 0.0); - if (dot(up, normalize(light_local)) > 0.99) { - up = vec3(1.0, 0.0, 0.0); // Avoid creating a degenerate basis. - } - let basis = orthonormalize(light_local, up) * scale * distance_to_light; + let basis = orthonormalize(normalize(light_local)) * scale * distance_to_light; let rotation_matrix = random_rotation_matrix(vec2(1.0), temporal); @@ -553,11 +545,7 @@ fn search_for_blockers_in_shadow_cubemap( ) -> f32 { // Create an orthonormal basis so we can apply a 2D sampling pattern to a // cubemap. - var up = vec3(0.0, 1.0, 0.0); - if (dot(up, normalize(light_local)) > 0.99) { - up = vec3(1.0, 0.0, 0.0); // Avoid creating a degenerate basis. - } - let basis = orthonormalize(light_local, up) * scale * distance_to_light; + let basis = orthonormalize(normalize(light_local)) * scale * distance_to_light; var sum: vec2 = vec2(0.0); sum += search_for_blockers_in_shadow_cubemap_at_offset( diff --git a/crates/bevy_render/src/maths.wgsl b/crates/bevy_render/src/maths.wgsl index d1e35523dc..0f9a11076f 100644 --- a/crates/bevy_render/src/maths.wgsl +++ b/crates/bevy_render/src/maths.wgsl @@ -63,17 +63,19 @@ fn mat4x4_to_mat3x3(m: mat4x4) -> mat3x3 { return mat3x3(m[0].xyz, m[1].xyz, m[2].xyz); } -// Creates an orthonormal basis given a Z vector and an up vector (which becomes -// Y after orthonormalization). +// Creates an orthonormal basis given a normalized Z vector. // // The results are equivalent to the Gram-Schmidt process [1]. // // [1]: https://math.stackexchange.com/a/1849294 -fn orthonormalize(z_unnormalized: vec3, up: vec3) -> mat3x3 { - let z_basis = normalize(z_unnormalized); - let x_basis = normalize(cross(z_basis, up)); - let y_basis = cross(z_basis, x_basis); - return mat3x3(x_basis, y_basis, z_basis); +fn orthonormalize(z_normalized: vec3) -> mat3x3 { + var up = vec3(0.0, 1.0, 0.0); + if (abs(dot(up, z_normalized)) > 0.99) { + up = vec3(1.0, 0.0, 0.0); // Avoid creating a degenerate basis. + } + let x_basis = normalize(cross(z_normalized, up)); + let y_basis = cross(z_normalized, x_basis); + return mat3x3(x_basis, y_basis, z_normalized); } // Returns true if any part of a sphere is on the positive side of a plane. diff --git a/crates/bevy_render/src/renderer/mod.rs b/crates/bevy_render/src/renderer/mod.rs index 7cb8023de1..52679002fa 100644 --- a/crates/bevy_render/src/renderer/mod.rs +++ b/crates/bevy_render/src/renderer/mod.rs @@ -7,7 +7,7 @@ use bevy_tasks::ComputeTaskPool; use bevy_utils::WgpuWrapper; pub use graph_runner::*; pub use render_device::*; -use tracing::{debug, error, info, info_span, trace, warn}; +use tracing::{debug, error, info, info_span, warn}; use crate::{ diagnostic::{internal::DiagnosticsRecorder, RecordDiagnostics}, @@ -145,6 +145,33 @@ const GPU_NOT_FOUND_ERROR_MESSAGE: &str = if cfg!(target_os = "linux") { "Unable to find a GPU! Make sure you have installed required drivers!" }; +#[cfg(not(target_family = "wasm"))] +fn find_adapter_by_name( + instance: &Instance, + options: &WgpuSettings, + compatible_surface: Option<&wgpu::Surface<'_>>, + adapter_name: &str, +) -> Option { + for adapter in + instance.enumerate_adapters(options.backends.expect( + "The `backends` field of `WgpuSettings` must be set to use a specific adapter.", + )) + { + tracing::trace!("Checking adapter: {:?}", adapter.get_info()); + let info = adapter.get_info(); + if let Some(surface) = compatible_surface { + if !adapter.is_surface_supported(surface) { + continue; + } + } + + if info.name.eq_ignore_ascii_case(adapter_name) { + return Some(adapter); + } + } + None +} + /// Initializes the renderer by retrieving and preparing the GPU instance, device and queue /// for the specified backend. pub async fn initialize_renderer( @@ -153,36 +180,30 @@ pub async fn initialize_renderer( request_adapter_options: &RequestAdapterOptions<'_, '_>, desired_adapter_name: Option, ) -> (RenderDevice, RenderQueue, RenderAdapterInfo, RenderAdapter) { + #[cfg(not(target_family = "wasm"))] + let mut selected_adapter = desired_adapter_name.and_then(|adapter_name| { + find_adapter_by_name( + instance, + options, + request_adapter_options.compatible_surface, + &adapter_name, + ) + }); + #[cfg(target_family = "wasm")] let mut selected_adapter = None; - if let Some(adapter_name) = &desired_adapter_name { - debug!("Searching for adapter with name: {}", adapter_name); - for adapter in instance.enumerate_adapters(options.backends.expect( - "The `backends` field of `WgpuSettings` must be set to use a specific adapter.", - )) { - trace!("Checking adapter: {:?}", adapter.get_info()); - let info = adapter.get_info(); - if let Some(surface) = request_adapter_options.compatible_surface { - if !adapter.is_surface_supported(surface) { - continue; - } - } - if info - .name - .to_lowercase() - .contains(&adapter_name.to_lowercase()) - { - selected_adapter = Some(adapter); - break; - } - } - } else { + #[cfg(target_family = "wasm")] + if desired_adapter_name.is_some() { + warn!("Choosing an adapter is not supported on wasm."); + } + + if selected_adapter.is_none() { debug!( "Searching for adapter with options: {:?}", request_adapter_options ); selected_adapter = instance.request_adapter(request_adapter_options).await.ok(); - }; + } let adapter = selected_adapter.expect(GPU_NOT_FOUND_ERROR_MESSAGE); let adapter_info = adapter.get_info();