From 9d420b435ac3eae933926cc3086f9ab0983db2cb Mon Sep 17 00:00:00 2001 From: Waridley Date: Sat, 24 Feb 2024 01:46:00 -0600 Subject: [PATCH 01/11] Pad SkyUniforms to 16 bytes for WASM (#12078) # Objective Fixes Skyboxes on WebGL, which broke in Bevy 0.13 due to the addition of the `brightness` uniform, when previously the skybox pipeline only had view and global uniforms. ```ignore panicked at ~/.cargo/registry/src/index.crates.io-6f17d22bba15001f/wgpu-0.19.1/src/backend/wgpu_core.rs:3009:5: wgpu error: Validation Error Caused by: In Device::create_render_pipeline note: label = `skybox_pipeline` In the provided shader, the type given for group 0 binding 3 has a size of 4. As the device does not support `DownlevelFlags::BUFFER_BINDINGS_NOT_16_BYTE_ALIGNED`, the type must have a size that is a multiple of 16 bytes. ``` It would be nice if this could be backported to a 0.13.1 patch as well if possible. I'm needing to rely on my own fork for now. ## Solution Similar to the Globals uniform solution here: https://github.com/bevyengine/bevy/blob/d31de3f1398080661a83a04dcbdd31a7ee9fa76e/crates/bevy_render/src/globals.rs#L59-L60 I've added 3 conditional fields to `SkyboxUniforms`. --- crates/bevy_core_pipeline/src/skybox/mod.rs | 12 ++++++++++++ crates/bevy_core_pipeline/src/skybox/skybox.wgsl | 13 +++++++++++-- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/crates/bevy_core_pipeline/src/skybox/mod.rs b/crates/bevy_core_pipeline/src/skybox/mod.rs index c8c128325fea1..0144a988db9f0 100644 --- a/crates/bevy_core_pipeline/src/skybox/mod.rs +++ b/crates/bevy_core_pipeline/src/skybox/mod.rs @@ -93,6 +93,12 @@ impl ExtractComponent for Skybox { skybox.clone(), SkyboxUniforms { brightness: skybox.brightness * exposure, + #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))] + _wasm_padding_8b: 0, + #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))] + _wasm_padding_12b: 0, + #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))] + _wasm_padding_16b: 0, }, )) } @@ -102,6 +108,12 @@ impl ExtractComponent for Skybox { #[derive(Component, ShaderType, Clone)] pub struct SkyboxUniforms { brightness: f32, + #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))] + _wasm_padding_8b: u32, + #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))] + _wasm_padding_12b: u32, + #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))] + _wasm_padding_16b: u32, } #[derive(Resource)] diff --git a/crates/bevy_core_pipeline/src/skybox/skybox.wgsl b/crates/bevy_core_pipeline/src/skybox/skybox.wgsl index cfbacf0e63ec5..593b440ee4680 100644 --- a/crates/bevy_core_pipeline/src/skybox/skybox.wgsl +++ b/crates/bevy_core_pipeline/src/skybox/skybox.wgsl @@ -1,10 +1,19 @@ #import bevy_render::view::View #import bevy_pbr::utils::coords_to_viewport_uv +struct SkyboxUniforms { + brightness: f32, +#ifdef SIXTEEN_BYTE_ALIGNMENT + _wasm_padding_8b: u32, + _wasm_padding_12b: u32, + _wasm_padding_16b: u32, +#endif +} + @group(0) @binding(0) var skybox: texture_cube; @group(0) @binding(1) var skybox_sampler: sampler; @group(0) @binding(2) var view: View; -@group(0) @binding(3) var brightness: f32; +@group(0) @binding(3) var uniforms: SkyboxUniforms; fn coords_to_ray_direction(position: vec2, viewport: vec4) -> vec3 { // Using world positions of the fragment and camera to calculate a ray direction @@ -63,5 +72,5 @@ fn skybox_fragment(in: VertexOutput) -> @location(0) vec4 { let ray_direction = coords_to_ray_direction(in.position.xy, view.viewport); // Cube maps are left-handed so we negate the z coordinate. - return textureSample(skybox, skybox_sampler, ray_direction * vec3(1.0, 1.0, -1.0)) * brightness; + return textureSample(skybox, skybox_sampler, ray_direction * vec3(1.0, 1.0, -1.0)) * uniforms.brightness; } From 9d13ae3113c508cb66b533641fd02127ef01dca4 Mon Sep 17 00:00:00 2001 From: SpecificProtagonist Date: Sat, 24 Feb 2024 10:52:25 +0100 Subject: [PATCH 02/11] Fix SimpleExecutor crash (#12076) # Objective Since #9822, `SimpleExecutor` panics when an automatic sync point is inserted: ```rust let mut sched = Schedule::default(); sched.set_executor_kind(ExecutorKind::Simple); sched.add_systems((|_: Commands| (), || ()).chain()); sched.run(&mut World::new()); ``` ``` System's param_state was not found. Did you forget to initialize this system before running it? note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace Encountered a panic in system `bevy_ecs::schedule::executor::apply_deferred`! ``` ## Solution Don't try to run the `apply_deferred` system. --- .../bevy_ecs/src/schedule/executor/simple.rs | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/crates/bevy_ecs/src/schedule/executor/simple.rs b/crates/bevy_ecs/src/schedule/executor/simple.rs index 002ba532ad74b..6c1b7437aca8b 100644 --- a/crates/bevy_ecs/src/schedule/executor/simple.rs +++ b/crates/bevy_ecs/src/schedule/executor/simple.rs @@ -4,7 +4,9 @@ use fixedbitset::FixedBitSet; use std::panic::AssertUnwindSafe; use crate::{ - schedule::{BoxedCondition, ExecutorKind, SystemExecutor, SystemSchedule}, + schedule::{ + executor::is_apply_deferred, BoxedCondition, ExecutorKind, SystemExecutor, SystemSchedule, + }, world::World, }; @@ -86,6 +88,10 @@ impl SystemExecutor for SimpleExecutor { } let system = &mut schedule.systems[system_index]; + if is_apply_deferred(system) { + continue; + } + let res = std::panic::catch_unwind(AssertUnwindSafe(|| { system.run((), world); })); @@ -125,3 +131,16 @@ fn evaluate_and_fold_conditions(conditions: &mut [BoxedCondition], world: &mut W .map(|condition| condition.run((), world)) .fold(true, |acc, res| acc && res) } + +#[cfg(test)] +#[test] +fn skip_automatic_sync_points() { + // Schedules automatically insert appy_deferred systems, but these should + // not be executed as they only serve as markers and are not initialized + use crate::prelude::*; + let mut sched = Schedule::default(); + sched.set_executor_kind(ExecutorKind::Simple); + sched.add_systems((|_: Commands| (), || ()).chain()); + let mut world = World::new(); + sched.run(&mut world); +} From 42e61557f8b7d1957ff684fb836cbcfaa39c565d Mon Sep 17 00:00:00 2001 From: James Liu Date: Sat, 24 Feb 2024 06:01:06 -0800 Subject: [PATCH 03/11] Immediately apply deferred system params in System::run (#11823) # Objective Fixes #11821. ## Solution * Run `System::apply_deferred` in `System::run` after executing the system. * Switch to using `System::run_unsafe` in `SingleThreadedExecutor` to preserve the current behavior. * Remove the `System::apply_deferred` in `SimpleExecutor` as it's now redundant. * Remove the `System::apply_deferred` when running one-shot systems, as it's now redundant. --- ## Changelog Changed: `System::run` will now immediately apply deferred system params after running the system. ## Migration Guide `System::run` will now always run `System::apply_deferred` immediately after running the system now. If you were running systems and then applying their deferred buffers at a later point in time, you can eliminate the latter. ```rust // in 0.13 system.run(world); // .. sometime later ... system.apply_deferred(world); // in 0.14 system.run(world); ``` --------- Co-authored-by: Alice Cecile --- .../bevy_ecs/src/schedule/executor/simple.rs | 2 -- .../src/schedule/executor/single_threaded.rs | 24 +++++++++++++------ crates/bevy_ecs/src/system/system.rs | 17 ++++++++----- crates/bevy_ecs/src/system/system_registry.rs | 1 - 4 files changed, 28 insertions(+), 16 deletions(-) diff --git a/crates/bevy_ecs/src/schedule/executor/simple.rs b/crates/bevy_ecs/src/schedule/executor/simple.rs index 6c1b7437aca8b..882a7736f67b8 100644 --- a/crates/bevy_ecs/src/schedule/executor/simple.rs +++ b/crates/bevy_ecs/src/schedule/executor/simple.rs @@ -99,8 +99,6 @@ impl SystemExecutor for SimpleExecutor { eprintln!("Encountered a panic in system `{}`!", &*system.name()); std::panic::resume_unwind(payload); } - - system.apply_deferred(world); } self.evaluated_sets.clear(); diff --git a/crates/bevy_ecs/src/schedule/executor/single_threaded.rs b/crates/bevy_ecs/src/schedule/executor/single_threaded.rs index 502bed8af50e8..3c88b463df43f 100644 --- a/crates/bevy_ecs/src/schedule/executor/single_threaded.rs +++ b/crates/bevy_ecs/src/schedule/executor/single_threaded.rs @@ -96,16 +96,26 @@ impl SystemExecutor for SingleThreadedExecutor { let system = &mut schedule.systems[system_index]; if is_apply_deferred(system) { self.apply_deferred(schedule, world); - } else { - let res = std::panic::catch_unwind(AssertUnwindSafe(|| { + continue; + } + + let res = std::panic::catch_unwind(AssertUnwindSafe(|| { + if system.is_exclusive() { system.run((), world); - })); - if let Err(payload) = res { - eprintln!("Encountered a panic in system `{}`!", &*system.name()); - std::panic::resume_unwind(payload); + } else { + // Use run_unsafe to avoid immediately applying deferred buffers + let world = world.as_unsafe_world_cell(); + system.update_archetype_component_access(world); + // SAFETY: We have exclusive, single-threaded access to the world and + // update_archetype_component_access is being called immediately before this. + unsafe { system.run_unsafe((), world) }; } - self.unapplied_systems.insert(system_index); + })); + if let Err(payload) = res { + eprintln!("Encountered a panic in system `{}`!", &*system.name()); + std::panic::resume_unwind(payload); } + self.unapplied_systems.insert(system_index); } if self.apply_final_deferred { diff --git a/crates/bevy_ecs/src/system/system.rs b/crates/bevy_ecs/src/system/system.rs index 3cceb36ef8553..1896327263ed4 100644 --- a/crates/bevy_ecs/src/system/system.rs +++ b/crates/bevy_ecs/src/system/system.rs @@ -52,6 +52,9 @@ pub trait System: Send + Sync + 'static { /// can be called in parallel with other systems and may break Rust's aliasing rules /// if used incorrectly, making it unsafe to call. /// + /// Unlike [`System::run`], this will not apply deferred parameters, which must be independently + /// applied by calling [`System::apply_deferred`] at later point in time. + /// /// # Safety /// /// - The caller must ensure that `world` has permission to access any world data @@ -66,14 +69,18 @@ pub trait System: Send + Sync + 'static { /// /// For [read-only](ReadOnlySystem) systems, see [`run_readonly`], which can be called using `&World`. /// + /// Unlike [`System::run_unsafe`], this will apply deferred parameters *immediately*. + /// /// [`run_readonly`]: ReadOnlySystem::run_readonly fn run(&mut self, input: Self::In, world: &mut World) -> Self::Out { - let world = world.as_unsafe_world_cell(); - self.update_archetype_component_access(world); + let world_cell = world.as_unsafe_world_cell(); + self.update_archetype_component_access(world_cell); // SAFETY: // - We have exclusive access to the entire world. // - `update_archetype_component_access` has been called. - unsafe { self.run_unsafe(input, world) } + let ret = unsafe { self.run_unsafe(input, world_cell) }; + self.apply_deferred(world); + ret } /// Applies any [`Deferred`](crate::system::Deferred) system parameters (or other system buffers) of this system to the world. @@ -283,9 +290,7 @@ impl RunSystemOnce for &mut World { ) -> Out { let mut system: T::System = IntoSystem::into_system(system); system.initialize(self); - let out = system.run(input, self); - system.apply_deferred(self); - out + system.run(input, self) } } diff --git a/crates/bevy_ecs/src/system/system_registry.rs b/crates/bevy_ecs/src/system/system_registry.rs index 7057b6fcae328..61a458e20adbf 100644 --- a/crates/bevy_ecs/src/system/system_registry.rs +++ b/crates/bevy_ecs/src/system/system_registry.rs @@ -284,7 +284,6 @@ impl World { initialized = true; } let result = system.run(input, self); - system.apply_deferred(self); // return ownership of system trait object (if entity still exists) if let Some(mut entity) = self.get_entity_mut(id.0) { From 65267dd1f9c5417d902160f938b643369a952f20 Mon Sep 17 00:00:00 2001 From: Dimitri Belopopsky Date: Sat, 24 Feb 2024 19:41:17 +0100 Subject: [PATCH 04/11] Fix missing renaming of Input -> ButtonInput (#12096) # Objective - Fix a wrongly named type ## Solution - Rename it properly --------- Co-authored-by: Alice Cecile Co-authored-by: Nicola Papale --- crates/bevy_input/src/mouse.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_input/src/mouse.rs b/crates/bevy_input/src/mouse.rs index 4eb34c6187032..2e9b98602c526 100644 --- a/crates/bevy_input/src/mouse.rs +++ b/crates/bevy_input/src/mouse.rs @@ -20,7 +20,7 @@ use bevy_reflect::{ReflectDeserialize, ReflectSerialize}; /// ## Usage /// /// The event is read inside of the [`mouse_button_input_system`] -/// to update the [`Input`](ButtonInput) resource. +/// to update the [`ButtonInput`] resource. #[derive(Event, Debug, Clone, Copy, PartialEq, Eq, Reflect)] #[reflect(Debug, PartialEq)] #[cfg_attr( From 972ca6283181dbb6d8c1457c39136b26f0a6ee04 Mon Sep 17 00:00:00 2001 From: Zachary Harrold Date: Sun, 25 Feb 2024 05:49:51 +1100 Subject: [PATCH 05/11] `bevy_color`: Added `Xyza` Colour Space (#12079) # Objective Add XYZ colour space. This will be most useful as a conversion step when working with other (more common) colour spaces. See [Wikipedia](https://en.wikipedia.org/wiki/CIE_1931_color_space) for details on this space. ## Solution - Added `Xyza` to `Color` and as its own type. --- ## Changelog - Added `Xyza` type. - Added `Color::Xyza` variant. ## Migration Guide - `Color` enum now has an additional member, `Xyza`. Convert it to any other type to handle this case in match statements. --- .../bevy_color/crates/gen_tests/src/main.rs | 10 +- crates/bevy_color/src/color.rs | 32 ++- crates/bevy_color/src/lib.rs | 8 + crates/bevy_color/src/test_colors.rs | 21 +- crates/bevy_color/src/xyza.rs | 245 ++++++++++++++++++ 5 files changed, 312 insertions(+), 4 deletions(-) create mode 100644 crates/bevy_color/src/xyza.rs diff --git a/crates/bevy_color/crates/gen_tests/src/main.rs b/crates/bevy_color/crates/gen_tests/src/main.rs index d7c0dc5bb7135..b4d765f4a1801 100644 --- a/crates/bevy_color/crates/gen_tests/src/main.rs +++ b/crates/bevy_color/crates/gen_tests/src/main.rs @@ -1,4 +1,4 @@ -use palette::{Hsl, IntoColor, Lch, LinSrgb, Oklab, Srgb}; +use palette::{Hsl, IntoColor, Lch, LinSrgb, Oklab, Srgb, Xyz}; const TEST_COLORS: &[(f32, f32, f32, &str)] = &[ (0., 0., 0., "black"), @@ -25,7 +25,7 @@ fn main() { println!( "// Generated by gen_tests. Do not edit. #[cfg(test)] -use crate::{{Hsla, Srgba, LinearRgba, Oklaba, Lcha}}; +use crate::{{Hsla, Srgba, LinearRgba, Oklaba, Lcha, Xyza}}; #[cfg(test)] pub struct TestColor {{ @@ -35,6 +35,7 @@ pub struct TestColor {{ pub hsl: Hsla, pub lch: Lcha, pub oklab: Oklaba, + pub xyz: Xyza, }} " ); @@ -48,6 +49,7 @@ pub struct TestColor {{ let hsl: Hsl = srgb.into_color(); let lch: Lch = srgb.into_color(); let oklab: Oklab = srgb.into_color(); + let xyz: Xyz = srgb.into_color(); println!(" // {name}"); println!( " TestColor {{ @@ -57,6 +59,7 @@ pub struct TestColor {{ hsl: Hsla::new({}, {}, {}, 1.0), lch: Lcha::new({}, {}, {}, 1.0), oklab: Oklaba::new({}, {}, {}, 1.0), + xyz: Xyza::new({}, {}, {}, 1.0), }},", VariablePrecision(srgb.red), VariablePrecision(srgb.green), @@ -73,6 +76,9 @@ pub struct TestColor {{ VariablePrecision(oklab.l), VariablePrecision(oklab.a), VariablePrecision(oklab.b), + VariablePrecision(xyz.x), + VariablePrecision(xyz.y), + VariablePrecision(xyz.z), ); } println!("];"); diff --git a/crates/bevy_color/src/color.rs b/crates/bevy_color/src/color.rs index 3186c0b5d11af..df997b44a9a13 100644 --- a/crates/bevy_color/src/color.rs +++ b/crates/bevy_color/src/color.rs @@ -1,4 +1,4 @@ -use crate::{Alpha, Hsla, Lcha, LinearRgba, Oklaba, Srgba, StandardColor}; +use crate::{Alpha, Hsla, Lcha, LinearRgba, Oklaba, Srgba, StandardColor, Xyza}; use bevy_reflect::{Reflect, ReflectDeserialize, ReflectSerialize}; use bevy_render::color::Color as LegacyColor; use serde::{Deserialize, Serialize}; @@ -20,6 +20,8 @@ pub enum Color { Lcha(Lcha), /// A color in the Oklaba color space with alpha. Oklaba(Oklaba), + /// A color in the XYZ color space with alpha. + Xyza(Xyza), } impl StandardColor for Color {} @@ -33,6 +35,7 @@ impl Color { Color::Hsla(hsla) => (*hsla).into(), Color::Lcha(lcha) => (*lcha).into(), Color::Oklaba(oklab) => (*oklab).into(), + Color::Xyza(xyza) => (*xyza).into(), } } } @@ -53,6 +56,7 @@ impl Alpha for Color { Color::Hsla(x) => *x = x.with_alpha(alpha), Color::Lcha(x) => *x = x.with_alpha(alpha), Color::Oklaba(x) => *x = x.with_alpha(alpha), + Color::Xyza(x) => *x = x.with_alpha(alpha), } new @@ -65,6 +69,7 @@ impl Alpha for Color { Color::Hsla(x) => x.alpha(), Color::Lcha(x) => x.alpha(), Color::Oklaba(x) => x.alpha(), + Color::Xyza(x) => x.alpha(), } } } @@ -99,6 +104,12 @@ impl From for Color { } } +impl From for Color { + fn from(value: Xyza) -> Self { + Self::Xyza(value) + } +} + impl From for Srgba { fn from(value: Color) -> Self { match value { @@ -107,6 +118,7 @@ impl From for Srgba { Color::Hsla(hsla) => hsla.into(), Color::Lcha(lcha) => lcha.into(), Color::Oklaba(oklab) => oklab.into(), + Color::Xyza(xyza) => xyza.into(), } } } @@ -119,6 +131,7 @@ impl From for LinearRgba { Color::Hsla(hsla) => hsla.into(), Color::Lcha(lcha) => lcha.into(), Color::Oklaba(oklab) => oklab.into(), + Color::Xyza(xyza) => xyza.into(), } } } @@ -131,6 +144,7 @@ impl From for Hsla { Color::Hsla(hsla) => hsla, Color::Lcha(lcha) => LinearRgba::from(lcha).into(), Color::Oklaba(oklab) => LinearRgba::from(oklab).into(), + Color::Xyza(xyza) => LinearRgba::from(xyza).into(), } } } @@ -143,6 +157,7 @@ impl From for Lcha { Color::Hsla(hsla) => Srgba::from(hsla).into(), Color::Lcha(lcha) => lcha, Color::Oklaba(oklab) => LinearRgba::from(oklab).into(), + Color::Xyza(xyza) => LinearRgba::from(xyza).into(), } } } @@ -155,6 +170,20 @@ impl From for Oklaba { Color::Hsla(hsla) => Srgba::from(hsla).into(), Color::Lcha(lcha) => LinearRgba::from(lcha).into(), Color::Oklaba(oklab) => oklab, + Color::Xyza(xyza) => LinearRgba::from(xyza).into(), + } + } +} + +impl From for Xyza { + fn from(value: Color) -> Self { + match value { + Color::Srgba(x) => x.into(), + Color::LinearRgba(x) => x.into(), + Color::Hsla(x) => x.into(), + Color::Lcha(x) => x.into(), + Color::Oklaba(x) => x.into(), + Color::Xyza(xyza) => xyza, } } } @@ -178,6 +207,7 @@ impl From for LegacyColor { Color::Hsla(x) => x.into(), Color::Lcha(x) => x.into(), Color::Oklaba(x) => x.into(), + Color::Xyza(x) => x.into(), } } } diff --git a/crates/bevy_color/src/lib.rs b/crates/bevy_color/src/lib.rs index 1864743bc62c6..3030440c7d5ed 100644 --- a/crates/bevy_color/src/lib.rs +++ b/crates/bevy_color/src/lib.rs @@ -7,6 +7,7 @@ //! - [`Hsla`] (hue, saturation, lightness, alpha) //! - [`Lcha`] (lightness, chroma, hue, alpha) //! - [`Oklaba`] (lightness, a-axis, b-axis, alpha) +//! - [`Xyza`] (x-axis, y-axis, z-axis, alpha) //! //! Each of these color spaces is represented as a distinct Rust type. //! @@ -35,6 +36,10 @@ //! for tasks such as color correction and image analysis, where it is important to be able //! to do things like change color saturation without causing hue shifts. //! +//! XYZ is a foundational space commonly used in the definition of other more modern color +//! spaces. The space is more formally known as CIE 1931, where the `x` and `z` axes represent +//! a form of chromaticity, while `y` defines an illuminance level. +//! //! See also the [Wikipedia article on color spaces](https://en.wikipedia.org/wiki/Color_space). //! //! # Conversions @@ -78,6 +83,7 @@ mod srgba; mod test_colors; #[cfg(test)] mod testing; +mod xyza; pub use color::*; pub use color_ops::*; @@ -87,6 +93,7 @@ pub use lcha::*; pub use linear_rgba::*; pub use oklaba::*; pub use srgba::*; +pub use xyza::*; use bevy_render::color::Color as LegacyColor; @@ -106,6 +113,7 @@ where Self: From + Into, Self: From + Into, Self: From + Into, + Self: From + Into, Self: Alpha, { } diff --git a/crates/bevy_color/src/test_colors.rs b/crates/bevy_color/src/test_colors.rs index 6d0e95a29e67f..52e4deb9931af 100644 --- a/crates/bevy_color/src/test_colors.rs +++ b/crates/bevy_color/src/test_colors.rs @@ -1,6 +1,6 @@ // Generated by gen_tests. Do not edit. #[cfg(test)] -use crate::{Hsla, Lcha, LinearRgba, Oklaba, Srgba}; +use crate::{Hsla, Lcha, LinearRgba, Oklaba, Srgba, Xyza}; #[cfg(test)] pub struct TestColor { @@ -10,6 +10,7 @@ pub struct TestColor { pub hsl: Hsla, pub lch: Lcha, pub oklab: Oklaba, + pub xyz: Xyza, } // Table of equivalent colors in various color spaces @@ -23,6 +24,7 @@ pub const TEST_COLORS: &[TestColor] = &[ hsl: Hsla::new(0.0, 0.0, 0.0, 1.0), lch: Lcha::new(0.0, 0.0, 0.0000136603785, 1.0), oklab: Oklaba::new(0.0, 0.0, 0.0, 1.0), + xyz: Xyza::new(0.0, 0.0, 0.0, 1.0), }, // white TestColor { @@ -32,6 +34,7 @@ pub const TEST_COLORS: &[TestColor] = &[ hsl: Hsla::new(0.0, 0.0, 1.0, 1.0), lch: Lcha::new(1.0, 0.0, 0.0000136603785, 1.0), oklab: Oklaba::new(1.0, 0.0, 0.000000059604645, 1.0), + xyz: Xyza::new(0.95047, 1.0, 1.08883, 1.0), }, // red TestColor { @@ -41,6 +44,7 @@ pub const TEST_COLORS: &[TestColor] = &[ hsl: Hsla::new(0.0, 1.0, 0.5, 1.0), lch: Lcha::new(0.53240794, 1.0455177, 39.99901, 1.0), oklab: Oklaba::new(0.6279554, 0.22486295, 0.1258463, 1.0), + xyz: Xyza::new(0.4124564, 0.2126729, 0.0193339, 1.0), }, // green TestColor { @@ -50,6 +54,7 @@ pub const TEST_COLORS: &[TestColor] = &[ hsl: Hsla::new(120.0, 1.0, 0.5, 1.0), lch: Lcha::new(0.87734723, 1.1977587, 136.01595, 1.0), oklab: Oklaba::new(0.8664396, -0.2338874, 0.1794985, 1.0), + xyz: Xyza::new(0.3575761, 0.7151522, 0.119192, 1.0), }, // blue TestColor { @@ -59,6 +64,7 @@ pub const TEST_COLORS: &[TestColor] = &[ hsl: Hsla::new(240.0, 1.0, 0.5, 1.0), lch: Lcha::new(0.32297012, 1.3380761, 306.28494, 1.0), oklab: Oklaba::new(0.4520137, -0.032456964, -0.31152815, 1.0), + xyz: Xyza::new(0.1804375, 0.072175, 0.9503041, 1.0), }, // yellow TestColor { @@ -68,6 +74,7 @@ pub const TEST_COLORS: &[TestColor] = &[ hsl: Hsla::new(60.0, 1.0, 0.5, 1.0), lch: Lcha::new(0.9713927, 0.96905375, 102.85126, 1.0), oklab: Oklaba::new(0.9679827, -0.07136908, 0.19856972, 1.0), + xyz: Xyza::new(0.7700325, 0.9278251, 0.1385259, 1.0), }, // magenta TestColor { @@ -77,6 +84,7 @@ pub const TEST_COLORS: &[TestColor] = &[ hsl: Hsla::new(300.0, 1.0, 0.5, 1.0), lch: Lcha::new(0.6032421, 1.1554068, 328.23495, 1.0), oklab: Oklaba::new(0.7016738, 0.27456632, -0.16915613, 1.0), + xyz: Xyza::new(0.5928939, 0.28484792, 0.969638, 1.0), }, // cyan TestColor { @@ -86,6 +94,7 @@ pub const TEST_COLORS: &[TestColor] = &[ hsl: Hsla::new(180.0, 1.0, 0.5, 1.0), lch: Lcha::new(0.9111322, 0.50120866, 196.37614, 1.0), oklab: Oklaba::new(0.90539926, -0.1494439, -0.039398134, 1.0), + xyz: Xyza::new(0.5380136, 0.78732723, 1.069496, 1.0), }, // gray TestColor { @@ -95,6 +104,7 @@ pub const TEST_COLORS: &[TestColor] = &[ hsl: Hsla::new(0.0, 0.0, 0.5, 1.0), lch: Lcha::new(0.5338897, 0.00000011920929, 90.0, 1.0), oklab: Oklaba::new(0.5981807, 0.00000011920929, 0.0, 1.0), + xyz: Xyza::new(0.2034397, 0.21404117, 0.23305441, 1.0), }, // olive TestColor { @@ -104,6 +114,7 @@ pub const TEST_COLORS: &[TestColor] = &[ hsl: Hsla::new(60.0, 1.0, 0.25, 1.0), lch: Lcha::new(0.51677734, 0.57966936, 102.851265, 1.0), oklab: Oklaba::new(0.57902855, -0.042691574, 0.11878061, 1.0), + xyz: Xyza::new(0.16481864, 0.19859275, 0.029650241, 1.0), }, // purple TestColor { @@ -113,6 +124,7 @@ pub const TEST_COLORS: &[TestColor] = &[ hsl: Hsla::new(300.0, 1.0, 0.25, 1.0), lch: Lcha::new(0.29655674, 0.69114214, 328.23495, 1.0), oklab: Oklaba::new(0.41972777, 0.1642403, -0.10118592, 1.0), + xyz: Xyza::new(0.12690368, 0.060969174, 0.20754242, 1.0), }, // teal TestColor { @@ -122,6 +134,7 @@ pub const TEST_COLORS: &[TestColor] = &[ hsl: Hsla::new(180.0, 1.0, 0.25, 1.0), lch: Lcha::new(0.48073065, 0.29981336, 196.37614, 1.0), oklab: Oklaba::new(0.54159236, -0.08939436, -0.02356726, 1.0), + xyz: Xyza::new(0.11515705, 0.16852042, 0.22891617, 1.0), }, // maroon TestColor { @@ -131,6 +144,7 @@ pub const TEST_COLORS: &[TestColor] = &[ hsl: Hsla::new(0.0, 1.0, 0.25, 1.0), lch: Lcha::new(0.2541851, 0.61091745, 38.350803, 1.0), oklab: Oklaba::new(0.3756308, 0.13450874, 0.07527886, 1.0), + xyz: Xyza::new(0.08828264, 0.045520753, 0.0041382504, 1.0), }, // lime TestColor { @@ -140,6 +154,7 @@ pub const TEST_COLORS: &[TestColor] = &[ hsl: Hsla::new(120.0, 1.0, 0.25, 1.0), lch: Lcha::new(0.46052113, 0.71647626, 136.01596, 1.0), oklab: Oklaba::new(0.5182875, -0.13990697, 0.10737252, 1.0), + xyz: Xyza::new(0.076536, 0.153072, 0.025511991, 1.0), }, // navy TestColor { @@ -149,6 +164,7 @@ pub const TEST_COLORS: &[TestColor] = &[ hsl: Hsla::new(240.0, 1.0, 0.25, 1.0), lch: Lcha::new(0.12890343, 0.8004114, 306.28494, 1.0), oklab: Oklaba::new(0.27038592, -0.01941514, -0.18635012, 1.0), + xyz: Xyza::new(0.03862105, 0.01544842, 0.20340417, 1.0), }, // orange TestColor { @@ -158,6 +174,7 @@ pub const TEST_COLORS: &[TestColor] = &[ hsl: Hsla::new(60.0, 1.0, 0.25, 1.0), lch: Lcha::new(0.51677734, 0.57966936, 102.851265, 1.0), oklab: Oklaba::new(0.57902855, -0.042691574, 0.11878061, 1.0), + xyz: Xyza::new(0.16481864, 0.19859275, 0.029650241, 1.0), }, // fuchsia TestColor { @@ -167,6 +184,7 @@ pub const TEST_COLORS: &[TestColor] = &[ hsl: Hsla::new(300.0, 1.0, 0.25, 1.0), lch: Lcha::new(0.29655674, 0.69114214, 328.23495, 1.0), oklab: Oklaba::new(0.41972777, 0.1642403, -0.10118592, 1.0), + xyz: Xyza::new(0.12690368, 0.060969174, 0.20754242, 1.0), }, // aqua TestColor { @@ -176,5 +194,6 @@ pub const TEST_COLORS: &[TestColor] = &[ hsl: Hsla::new(180.0, 1.0, 0.25, 1.0), lch: Lcha::new(0.48073065, 0.29981336, 196.37614, 1.0), oklab: Oklaba::new(0.54159236, -0.08939436, -0.02356726, 1.0), + xyz: Xyza::new(0.11515705, 0.16852042, 0.22891617, 1.0), }, ]; diff --git a/crates/bevy_color/src/xyza.rs b/crates/bevy_color/src/xyza.rs new file mode 100644 index 0000000000000..3d6420365bd62 --- /dev/null +++ b/crates/bevy_color/src/xyza.rs @@ -0,0 +1,245 @@ +use crate::{Alpha, Hsla, Lcha, LinearRgba, Luminance, Mix, Oklaba, Srgba, StandardColor}; +use bevy_reflect::{Reflect, ReflectDeserialize, ReflectSerialize}; +use bevy_render::color::Color; +use serde::{Deserialize, Serialize}; + +/// [CIE 1931](https://en.wikipedia.org/wiki/CIE_1931_color_space) color space, also known as XYZ, with an alpha channel. +#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, Reflect)] +#[reflect(PartialEq, Serialize, Deserialize)] +pub struct Xyza { + /// The x-axis. [0.0, 1.0] + pub x: f32, + /// The y-axis, intended to represent luminance. [0.0, 1.0] + pub y: f32, + /// The z-axis. [0.0, 1.0] + pub z: f32, + /// The alpha channel. [0.0, 1.0] + pub alpha: f32, +} + +impl StandardColor for Xyza {} + +impl Xyza { + /// Construct a new [`Xyza`] color from components. + /// + /// # Arguments + /// + /// * `x` - x-axis. [0.0, 1.0] + /// * `y` - y-axis. [0.0, 1.0] + /// * `z` - z-axis. [0.0, 1.0] + /// * `alpha` - Alpha channel. [0.0, 1.0] + pub const fn new(x: f32, y: f32, z: f32, alpha: f32) -> Self { + Self { x, y, z, alpha } + } + + /// Construct a new [`Xyza`] color from (x, y, z) components, with the default alpha (1.0). + /// + /// # Arguments + /// + /// * `x` - x-axis. [0.0, 1.0] + /// * `y` - y-axis. [0.0, 1.0] + /// * `z` - z-axis. [0.0, 1.0] + pub const fn rgb(x: f32, y: f32, z: f32) -> Self { + Self { + x, + y, + z, + alpha: 1.0, + } + } +} + +impl Default for Xyza { + fn default() -> Self { + Self::new(0., 0., 0., 1.) + } +} + +impl Alpha for Xyza { + #[inline] + fn with_alpha(&self, alpha: f32) -> Self { + Self { alpha, ..*self } + } + + #[inline] + fn alpha(&self) -> f32 { + self.alpha + } +} + +impl Luminance for Xyza { + #[inline] + fn with_luminance(&self, lightness: f32) -> Self { + Self { + y: lightness, + ..*self + } + } + + fn luminance(&self) -> f32 { + self.y + } + + fn darker(&self, amount: f32) -> Self { + Self { + y: (self.y - amount).clamp(0., 1.), + ..*self + } + } + + fn lighter(&self, amount: f32) -> Self { + Self { + y: (self.y + amount).min(1.), + ..*self + } + } +} + +impl Mix for Xyza { + #[inline] + fn mix(&self, other: &Self, factor: f32) -> Self { + let n_factor = 1.0 - factor; + Self { + x: self.x * n_factor + other.x * factor, + y: self.y * n_factor + other.y * factor, + z: self.z * n_factor + other.z * factor, + alpha: self.alpha * n_factor + other.alpha * factor, + } + } +} + +impl From for Xyza { + fn from( + LinearRgba { + red, + green, + blue, + alpha, + }: LinearRgba, + ) -> Self { + // Linear sRGB to XYZ + // http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_RGB.html + // http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html (sRGB, RGB to XYZ [M]) + let r = red; + let g = green; + let b = blue; + + let x = r * 0.4124564 + g * 0.3575761 + b * 0.1804375; + let y = r * 0.2126729 + g * 0.7151522 + b * 0.072175; + let z = r * 0.0193339 + g * 0.119192 + b * 0.9503041; + + Xyza::new(x, y, z, alpha) + } +} + +impl From for LinearRgba { + fn from(Xyza { x, y, z, alpha }: Xyza) -> Self { + // XYZ to Linear sRGB + // http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_RGB.html + // http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html (sRGB, XYZ to RGB [M]-1) + let r = x * 3.2404542 + y * -1.5371385 + z * -0.4985314; + let g = x * -0.969266 + y * 1.8760108 + z * 0.041556; + let b = x * 0.0556434 + y * -0.2040259 + z * 1.0572252; + + LinearRgba::new(r, g, b, alpha) + } +} + +impl From for Xyza { + fn from(value: Srgba) -> Self { + LinearRgba::from(value).into() + } +} + +impl From for Srgba { + fn from(value: Xyza) -> Self { + LinearRgba::from(value).into() + } +} + +impl From for Xyza { + fn from(value: Hsla) -> Self { + LinearRgba::from(value).into() + } +} + +impl From for Hsla { + fn from(value: Xyza) -> Self { + LinearRgba::from(value).into() + } +} + +impl From for Xyza { + fn from(value: Lcha) -> Self { + LinearRgba::from(value).into() + } +} + +impl From for Lcha { + fn from(value: Xyza) -> Self { + LinearRgba::from(value).into() + } +} + +impl From for Xyza { + fn from(value: Oklaba) -> Self { + LinearRgba::from(value).into() + } +} + +impl From for Oklaba { + fn from(value: Xyza) -> Self { + LinearRgba::from(value).into() + } +} + +impl From for Xyza { + fn from(value: Color) -> Self { + LinearRgba::from(value).into() + } +} + +impl From for Color { + fn from(value: Xyza) -> Self { + LinearRgba::from(value).into() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + color_difference::EuclideanDistance, test_colors::TEST_COLORS, testing::assert_approx_eq, + Srgba, + }; + + #[test] + fn test_to_from_srgba() { + let xyza = Xyza::new(0.5, 0.5, 0.5, 1.0); + let srgba: Srgba = xyza.into(); + let xyza2: Xyza = srgba.into(); + assert_approx_eq!(xyza.x, xyza2.x, 0.001); + assert_approx_eq!(xyza.y, xyza2.y, 0.001); + assert_approx_eq!(xyza.z, xyza2.z, 0.001); + assert_approx_eq!(xyza.alpha, xyza2.alpha, 0.001); + } + + #[test] + fn test_to_from_srgba_2() { + for color in TEST_COLORS.iter() { + let rgb2: Srgba = (color.xyz).into(); + let xyz2: Xyza = (color.rgb).into(); + assert!( + color.rgb.distance(&rgb2) < 0.00001, + "{}: {:?} != {:?}", + color.name, + color.rgb, + rgb2 + ); + assert_approx_eq!(color.xyz.x, xyz2.x, 0.001); + assert_approx_eq!(color.xyz.y, xyz2.y, 0.001); + assert_approx_eq!(color.xyz.z, xyz2.z, 0.001); + assert_approx_eq!(color.xyz.alpha, xyz2.alpha, 0.001); + } + } +} From e689d4601546675bf486afa0960881eb60844ba3 Mon Sep 17 00:00:00 2001 From: rmsthebest <11652273+rmsthebest@users.noreply.github.com> Date: Sat, 24 Feb 2024 21:57:16 +0100 Subject: [PATCH 06/11] remove unnecessary mut in query in ui_texture_slice example (#12101) # Objective Keep the examples as correct as possible: Remove mutability from a query that is unused ui_texture_slice ## Solution removed "mut" --- --- examples/ui/ui_texture_slice.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/ui/ui_texture_slice.rs b/examples/ui/ui_texture_slice.rs index c9278e6cf1403..d486d79a163b0 100644 --- a/examples/ui/ui_texture_slice.rs +++ b/examples/ui/ui_texture_slice.rs @@ -14,10 +14,10 @@ fn main() { } fn button_system( - mut interaction_query: Query<(&Interaction, &Children), (Changed, With