@@ -34,6 +34,8 @@ fn main() {
3434 OnEnter ( Scene :: TextureAtlasBuilder ) ,
3535 texture_atlas_builder:: setup,
3636 )
37+ . add_systems ( OnEnter ( Scene :: ColorConsistency ) , color_consistency:: setup)
38+ . add_systems ( OnExit ( Scene :: ColorConsistency ) , color_consistency:: teardown)
3739 . add_systems ( Update , switch_scene)
3840 . add_systems ( Update , gizmos:: draw_gizmos. run_if ( in_state ( Scene :: Gizmos ) ) ) ;
3941
@@ -58,6 +60,7 @@ enum Scene {
5860 SpriteSlicing ,
5961 Gizmos ,
6062 TextureAtlasBuilder ,
63+ ColorConsistency ,
6164}
6265
6366impl std:: str:: FromStr for Scene {
@@ -84,7 +87,8 @@ impl Next for Scene {
8487 Scene :: Sprite => Scene :: SpriteSlicing ,
8588 Scene :: SpriteSlicing => Scene :: Gizmos ,
8689 Scene :: Gizmos => Scene :: TextureAtlasBuilder ,
87- Scene :: TextureAtlasBuilder => Scene :: Shapes ,
90+ Scene :: TextureAtlasBuilder => Scene :: ColorConsistency ,
91+ Scene :: ColorConsistency => Scene :: Shapes ,
8892 }
8993 }
9094}
@@ -543,3 +547,78 @@ mod texture_atlas_builder {
543547 }
544548 }
545549}
550+
551+ mod color_consistency {
552+ //! Visual regression test for <https://github.com/bevyengine/bevy/issues/23577>.
553+ //!
554+ //! The clear color and shapes rendered using different pipelines (sprites,
555+ //! 2D meshes, UI background) should produce identical pixel values for the
556+ //! same sRGB input color.
557+ //!
558+ //! If the color conversion paths are consistent, the entire window will appear
559+ //! as a uniform solid color with no visible boundaries between the strips.
560+
561+ use bevy:: { core_pipeline:: tonemapping:: Tonemapping , prelude:: * } ;
562+
563+ // We've chosen the sRGB value from issue #23577, in case it can be reproduced elsewhere.
564+ const TEST_COLOR : Color = Color :: srgb ( 0.1 , 0.1 , 0.1 ) ;
565+ const DEFAULT_WIDTH : f32 = 1280.0 ;
566+ const DEFAULT_HEIGHT : f32 = 720.0 ;
567+ const STRIP_WIDTH : f32 = DEFAULT_WIDTH / 3.0 ;
568+ const STRIP_HEIGHT : f32 = DEFAULT_HEIGHT / 3.0 ;
569+
570+ pub fn setup (
571+ mut commands : Commands ,
572+ mut meshes : ResMut < Assets < Mesh > > ,
573+ mut materials : ResMut < Assets < ColorMaterial > > ,
574+ ) {
575+ // The window background is drawn with the clear color.
576+ commands. insert_resource ( ClearColor ( TEST_COLOR ) ) ;
577+
578+ // Make sure there's no tonemapping
579+ commands. spawn ( (
580+ Camera2d ,
581+ Tonemapping :: None ,
582+ DespawnOnExit ( super :: Scene :: ColorConsistency ) ,
583+ ) ) ;
584+
585+ // Top third for sprites
586+ commands. spawn ( (
587+ Sprite {
588+ color : TEST_COLOR ,
589+ custom_size : Some ( Vec2 :: new ( STRIP_WIDTH , STRIP_HEIGHT ) ) ,
590+ ..default ( )
591+ } ,
592+ Transform :: from_xyz ( 0.0 , STRIP_HEIGHT , 0.0 ) ,
593+ DespawnOnExit ( super :: Scene :: ColorConsistency ) ,
594+ ) ) ;
595+
596+ // Middle third for 2D meshes
597+ commands. spawn ( (
598+ Mesh2d ( meshes. add ( Rectangle :: new ( STRIP_WIDTH , STRIP_HEIGHT ) ) ) ,
599+ MeshMaterial2d ( materials. add ( ColorMaterial :: from_color ( TEST_COLOR ) ) ) ,
600+ Transform :: from_xyz ( 0.0 , 0.0 , 0.0 ) ,
601+ DespawnOnExit ( super :: Scene :: ColorConsistency ) ,
602+ ) ) ;
603+
604+ // Bottom third for UI nodes
605+ commands. spawn ( (
606+ Node {
607+ position_type : PositionType :: Absolute ,
608+ bottom : Val :: Px ( 0.0 ) ,
609+ left : Val :: Px ( 0.0 ) ,
610+ width : Val :: Percent ( 33.3 ) ,
611+ height : Val :: Px ( STRIP_HEIGHT ) ,
612+ ..default ( )
613+ } ,
614+ BackgroundColor ( TEST_COLOR ) ,
615+ DespawnOnExit ( super :: Scene :: ColorConsistency ) ,
616+ ) ) ;
617+ }
618+
619+ // Remember to reset the clear color
620+ // Tonemapping is per-camera, and is reset when the camera despawns
621+ pub fn teardown ( mut commands : Commands ) {
622+ commands. insert_resource ( ClearColor :: default ( ) ) ;
623+ }
624+ }
0 commit comments