 206c7ce219
			
		
	
	
		206c7ce219
		
	
	
	
	
		
			
			Huge thanks to @maniwani, @devil-ira, @hymm, @cart, @superdump and @jakobhellermann for the help with this PR. # Objective - Followup #6587. - Minimal integration for the Stageless Scheduling RFC: https://github.com/bevyengine/rfcs/pull/45 ## Solution - [x] Remove old scheduling module - [x] Migrate new methods to no longer use extension methods - [x] Fix compiler errors - [x] Fix benchmarks - [x] Fix examples - [x] Fix docs - [x] Fix tests ## Changelog ### Added - a large number of methods on `App` to work with schedules ergonomically - the `CoreSchedule` enum - `App::add_extract_system` via the `RenderingAppExtension` trait extension method - the private `prepare_view_uniforms` system now has a public system set for scheduling purposes, called `ViewSet::PrepareUniforms` ### Removed - stages, and all code that mentions stages - states have been dramatically simplified, and no longer use a stack - `RunCriteriaLabel` - `AsSystemLabel` trait - `on_hierarchy_reports_enabled` run criteria (now just uses an ad hoc resource checking run condition) - systems in `RenderSet/Stage::Extract` no longer warn when they do not read data from the main world - `RunCriteriaLabel` - `transform_propagate_system_set`: this was a nonstandard pattern that didn't actually provide enough control. The systems are already `pub`: the docs have been updated to ensure that the third-party usage is clear. ### Changed - `System::default_labels` is now `System::default_system_sets`. - `App::add_default_labels` is now `App::add_default_sets` - `CoreStage` and `StartupStage` enums are now `CoreSet` and `StartupSet` - `App::add_system_set` was renamed to `App::add_systems` - The `StartupSchedule` label is now defined as part of the `CoreSchedules` enum - `.label(SystemLabel)` is now referred to as `.in_set(SystemSet)` - `SystemLabel` trait was replaced by `SystemSet` - `SystemTypeIdLabel<T>` was replaced by `SystemSetType<T>` - The `ReportHierarchyIssue` resource now has a public constructor (`new`), and implements `PartialEq` - Fixed time steps now use a schedule (`CoreSchedule::FixedTimeStep`) rather than a run criteria. - Adding rendering extraction systems now panics rather than silently failing if no subapp with the `RenderApp` label is found. - the `calculate_bounds` system, with the `CalculateBounds` label, is now in `CoreSet::Update`, rather than in `CoreSet::PostUpdate` before commands are applied. - `SceneSpawnerSystem` now runs under `CoreSet::Update`, rather than `CoreStage::PreUpdate.at_end()`. - `bevy_pbr::add_clusters` is no longer an exclusive system - the top level `bevy_ecs::schedule` module was replaced with `bevy_ecs::scheduling` - `tick_global_task_pools_on_main_thread` is no longer run as an exclusive system. Instead, it has been replaced by `tick_global_task_pools`, which uses a `NonSend` resource to force running on the main thread. ## Migration Guide - Calls to `.label(MyLabel)` should be replaced with `.in_set(MySet)` - Stages have been removed. Replace these with system sets, and then add command flushes using the `apply_system_buffers` exclusive system where needed. - The `CoreStage`, `StartupStage, `RenderStage` and `AssetStage` enums have been replaced with `CoreSet`, `StartupSet, `RenderSet` and `AssetSet`. The same scheduling guarantees have been preserved. - Systems are no longer added to `CoreSet::Update` by default. Add systems manually if this behavior is needed, although you should consider adding your game logic systems to `CoreSchedule::FixedTimestep` instead for more reliable framerate-independent behavior. - Similarly, startup systems are no longer part of `StartupSet::Startup` by default. In most cases, this won't matter to you. - For example, `add_system_to_stage(CoreStage::PostUpdate, my_system)` should be replaced with - `add_system(my_system.in_set(CoreSet::PostUpdate)` - When testing systems or otherwise running them in a headless fashion, simply construct and run a schedule using `Schedule::new()` and `World::run_schedule` rather than constructing stages - Run criteria have been renamed to run conditions. These can now be combined with each other and with states. - Looping run criteria and state stacks have been removed. Use an exclusive system that runs a schedule if you need this level of control over system control flow. - For app-level control flow over which schedules get run when (such as for rollback networking), create your own schedule and insert it under the `CoreSchedule::Outer` label. - Fixed timesteps are now evaluated in a schedule, rather than controlled via run criteria. The `run_fixed_timestep` system runs this schedule between `CoreSet::First` and `CoreSet::PreUpdate` by default. - Command flush points introduced by `AssetStage` have been removed. If you were relying on these, add them back manually. - Adding extract systems is now typically done directly on the main app. Make sure the `RenderingAppExtension` trait is in scope, then call `app.add_extract_system(my_system)`. - the `calculate_bounds` system, with the `CalculateBounds` label, is now in `CoreSet::Update`, rather than in `CoreSet::PostUpdate` before commands are applied. You may need to order your movement systems to occur before this system in order to avoid system order ambiguities in culling behavior. - the `RenderLabel` `AppLabel` was renamed to `RenderApp` for clarity - `App::add_state` now takes 0 arguments: the starting state is set based on the `Default` impl. - Instead of creating `SystemSet` containers for systems that run in stages, simply use `.on_enter::<State::Variant>()` or its `on_exit` or `on_update` siblings. - `SystemLabel` derives should be replaced with `SystemSet`. You will also need to add the `Debug`, `PartialEq`, `Eq`, and `Hash` traits to satisfy the new trait bounds. - `with_run_criteria` has been renamed to `run_if`. Run criteria have been renamed to run conditions for clarity, and should now simply return a bool. - States have been dramatically simplified: there is no longer a "state stack". To queue a transition to the next state, call `NextState::set` ## TODO - [x] remove dead methods on App and World - [x] add `App::add_system_to_schedule` and `App::add_systems_to_schedule` - [x] avoid adding the default system set at inappropriate times - [x] remove any accidental cycles in the default plugins schedule - [x] migrate benchmarks - [x] expose explicit labels for the built-in command flush points - [x] migrate engine code - [x] remove all mentions of stages from the docs - [x] verify docs for States - [x] fix uses of exclusive systems that use .end / .at_start / .before_commands - [x] migrate RenderStage and AssetStage - [x] migrate examples - [x] ensure that transform propagation is exported in a sufficiently public way (the systems are already pub) - [x] ensure that on_enter schedules are run at least once before the main app - [x] re-enable opt-in to execution order ambiguities - [x] revert change to `update_bounds` to ensure it runs in `PostUpdate` - [x] test all examples - [x] unbreak directional lights - [x] unbreak shadows (see 3d_scene, 3d_shape, lighting, transparaency_3d examples) - [x] game menu example shows loading screen and menu simultaneously - [x] display settings menu is a blank screen - [x] `without_winit` example panics - [x] ensure all tests pass - [x] SubApp doc test fails - [x] runs_spawn_local tasks fails - [x] [Fix panic_when_hierachy_cycle test hanging](https://github.com/alice-i-cecile/bevy/pull/120) ## Points of Difficulty and Controversy **Reviewers, please give feedback on these and look closely** 1. Default sets, from the RFC, have been removed. These added a tremendous amount of implicit complexity and result in hard to debug scheduling errors. They're going to be tackled in the form of "base sets" by @cart in a followup. 2. The outer schedule controls which schedule is run when `App::update` is called. 3. I implemented `Label for `Box<dyn Label>` for our label types. This enables us to store schedule labels in concrete form, and then later run them. I ran into the same set of problems when working with one-shot systems. We've previously investigated this pattern in depth, and it does not appear to lead to extra indirection with nested boxes. 4. `SubApp::update` simply runs the default schedule once. This sucks, but this whole API is incomplete and this was the minimal changeset. 5. `time_system` and `tick_global_task_pools_on_main_thread` no longer use exclusive systems to attempt to force scheduling order 6. Implemetnation strategy for fixed timesteps 7. `AssetStage` was migrated to `AssetSet` without reintroducing command flush points. These did not appear to be used, and it's nice to remove these bottlenecks. 8. Migration of `bevy_render/lib.rs` and pipelined rendering. The logic here is unusually tricky, as we have complex scheduling requirements. ## Future Work (ideally before 0.10) - Rename schedule_v3 module to schedule or scheduling - Add a derive macro to states, and likely a `EnumIter` trait of some form - Figure out what exactly to do with the "systems added should basically work by default" problem - Improve ergonomics for working with fixed timesteps and states - Polish FixedTime API to match Time - Rebase and merge #7415 - Resolve all internal ambiguities (blocked on better tools, especially #7442) - Add "base sets" to replace the removed default sets.
		
			
				
	
	
		
			363 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			363 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| //! This example shows how to manually render 2d items using "mid level render apis" with a custom
 | |
| //! pipeline for 2d meshes.
 | |
| //! It doesn't use the [`Material2d`] abstraction, but changes the vertex buffer to include vertex color.
 | |
| //! Check out the "mesh2d" example for simpler / higher level 2d meshes.
 | |
| 
 | |
| use std::f32::consts::PI;
 | |
| 
 | |
| use bevy::{
 | |
|     core_pipeline::core_2d::Transparent2d,
 | |
|     prelude::*,
 | |
|     reflect::TypeUuid,
 | |
|     render::{
 | |
|         mesh::{Indices, MeshVertexAttribute},
 | |
|         render_asset::RenderAssets,
 | |
|         render_phase::{AddRenderCommand, DrawFunctions, RenderPhase, SetItemPipeline},
 | |
|         render_resource::{
 | |
|             BlendState, ColorTargetState, ColorWrites, Face, FragmentState, FrontFace,
 | |
|             MultisampleState, PipelineCache, PolygonMode, PrimitiveState, PrimitiveTopology,
 | |
|             RenderPipelineDescriptor, SpecializedRenderPipeline, SpecializedRenderPipelines,
 | |
|             TextureFormat, VertexBufferLayout, VertexFormat, VertexState, VertexStepMode,
 | |
|         },
 | |
|         texture::BevyDefault,
 | |
|         view::{ExtractedView, ViewTarget, VisibleEntities},
 | |
|         Extract, RenderApp, RenderSet,
 | |
|     },
 | |
|     sprite::{
 | |
|         DrawMesh2d, Mesh2dHandle, Mesh2dPipeline, Mesh2dPipelineKey, Mesh2dUniform,
 | |
|         SetMesh2dBindGroup, SetMesh2dViewBindGroup,
 | |
|     },
 | |
|     utils::FloatOrd,
 | |
| };
 | |
| 
 | |
| fn main() {
 | |
|     App::new()
 | |
|         .add_plugins(DefaultPlugins)
 | |
|         .add_plugin(ColoredMesh2dPlugin)
 | |
|         .add_startup_system(star)
 | |
|         .run();
 | |
| }
 | |
| 
 | |
| fn star(
 | |
|     mut commands: Commands,
 | |
|     // We will add a new Mesh for the star being created
 | |
|     mut meshes: ResMut<Assets<Mesh>>,
 | |
| ) {
 | |
|     // Let's define the mesh for the object we want to draw: a nice star.
 | |
|     // We will specify here what kind of topology is used to define the mesh,
 | |
|     // that is, how triangles are built from the vertices. We will use a
 | |
|     // triangle list, meaning that each vertex of the triangle has to be
 | |
|     // specified.
 | |
|     let mut star = Mesh::new(PrimitiveTopology::TriangleList);
 | |
| 
 | |
|     // Vertices need to have a position attribute. We will use the following
 | |
|     // vertices (I hope you can spot the star in the schema).
 | |
|     //
 | |
|     //        1
 | |
|     //
 | |
|     //     10   2
 | |
|     // 9      0      3
 | |
|     //     8     4
 | |
|     //        6
 | |
|     //   7        5
 | |
|     //
 | |
|     // These vertices are specified in 3D space.
 | |
|     let mut v_pos = vec![[0.0, 0.0, 0.0]];
 | |
|     for i in 0..10 {
 | |
|         // The angle between each vertex is 1/10 of a full rotation.
 | |
|         let a = i as f32 * PI / 5.0;
 | |
|         // The radius of inner vertices (even indices) is 100. For outer vertices (odd indices) it's 200.
 | |
|         let r = (1 - i % 2) as f32 * 100.0 + 100.0;
 | |
|         // Add the vertex position.
 | |
|         v_pos.push([r * a.sin(), r * a.cos(), 0.0]);
 | |
|     }
 | |
|     // Set the position attribute
 | |
|     star.insert_attribute(Mesh::ATTRIBUTE_POSITION, v_pos);
 | |
|     // And a RGB color attribute as well
 | |
|     let mut v_color: Vec<u32> = vec![Color::BLACK.as_linear_rgba_u32()];
 | |
|     v_color.extend_from_slice(&[Color::YELLOW.as_linear_rgba_u32(); 10]);
 | |
|     star.insert_attribute(
 | |
|         MeshVertexAttribute::new("Vertex_Color", 1, VertexFormat::Uint32),
 | |
|         v_color,
 | |
|     );
 | |
| 
 | |
|     // Now, we specify the indices of the vertex that are going to compose the
 | |
|     // triangles in our star. Vertices in triangles have to be specified in CCW
 | |
|     // winding (that will be the front face, colored). Since we are using
 | |
|     // triangle list, we will specify each triangle as 3 vertices
 | |
|     //   First triangle: 0, 2, 1
 | |
|     //   Second triangle: 0, 3, 2
 | |
|     //   Third triangle: 0, 4, 3
 | |
|     //   etc
 | |
|     //   Last triangle: 0, 1, 10
 | |
|     let mut indices = vec![0, 1, 10];
 | |
|     for i in 2..=10 {
 | |
|         indices.extend_from_slice(&[0, i, i - 1]);
 | |
|     }
 | |
|     star.set_indices(Some(Indices::U32(indices)));
 | |
| 
 | |
|     // We can now spawn the entities for the star and the camera
 | |
|     commands.spawn((
 | |
|         // We use a marker component to identify the custom colored meshes
 | |
|         ColoredMesh2d::default(),
 | |
|         // The `Handle<Mesh>` needs to be wrapped in a `Mesh2dHandle` to use 2d rendering instead of 3d
 | |
|         Mesh2dHandle(meshes.add(star)),
 | |
|         // This bundle's components are needed for something to be rendered
 | |
|         SpatialBundle::INHERITED_IDENTITY,
 | |
|     ));
 | |
| 
 | |
|     // Spawn the camera
 | |
|     commands.spawn(Camera2dBundle::default());
 | |
| }
 | |
| 
 | |
| /// A marker component for colored 2d meshes
 | |
| #[derive(Component, Default)]
 | |
| pub struct ColoredMesh2d;
 | |
| 
 | |
| /// Custom pipeline for 2d meshes with vertex colors
 | |
| #[derive(Resource)]
 | |
| pub struct ColoredMesh2dPipeline {
 | |
|     /// this pipeline wraps the standard [`Mesh2dPipeline`]
 | |
|     mesh2d_pipeline: Mesh2dPipeline,
 | |
| }
 | |
| 
 | |
| impl FromWorld for ColoredMesh2dPipeline {
 | |
|     fn from_world(world: &mut World) -> Self {
 | |
|         Self {
 | |
|             mesh2d_pipeline: Mesh2dPipeline::from_world(world),
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| // We implement `SpecializedPipeline` to customize the default rendering from `Mesh2dPipeline`
 | |
| impl SpecializedRenderPipeline for ColoredMesh2dPipeline {
 | |
|     type Key = Mesh2dPipelineKey;
 | |
| 
 | |
|     fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor {
 | |
|         // Customize how to store the meshes' vertex attributes in the vertex buffer
 | |
|         // Our meshes only have position and color
 | |
|         let formats = vec![
 | |
|             // Position
 | |
|             VertexFormat::Float32x3,
 | |
|             // Color
 | |
|             VertexFormat::Uint32,
 | |
|         ];
 | |
| 
 | |
|         let vertex_layout =
 | |
|             VertexBufferLayout::from_vertex_formats(VertexStepMode::Vertex, formats);
 | |
| 
 | |
|         let format = match key.contains(Mesh2dPipelineKey::HDR) {
 | |
|             true => ViewTarget::TEXTURE_FORMAT_HDR,
 | |
|             false => TextureFormat::bevy_default(),
 | |
|         };
 | |
| 
 | |
|         RenderPipelineDescriptor {
 | |
|             vertex: VertexState {
 | |
|                 // Use our custom shader
 | |
|                 shader: COLORED_MESH2D_SHADER_HANDLE.typed::<Shader>(),
 | |
|                 entry_point: "vertex".into(),
 | |
|                 shader_defs: Vec::new(),
 | |
|                 // Use our custom vertex buffer
 | |
|                 buffers: vec![vertex_layout],
 | |
|             },
 | |
|             fragment: Some(FragmentState {
 | |
|                 // Use our custom shader
 | |
|                 shader: COLORED_MESH2D_SHADER_HANDLE.typed::<Shader>(),
 | |
|                 shader_defs: Vec::new(),
 | |
|                 entry_point: "fragment".into(),
 | |
|                 targets: vec![Some(ColorTargetState {
 | |
|                     format,
 | |
|                     blend: Some(BlendState::ALPHA_BLENDING),
 | |
|                     write_mask: ColorWrites::ALL,
 | |
|                 })],
 | |
|             }),
 | |
|             // Use the two standard uniforms for 2d meshes
 | |
|             layout: Some(vec![
 | |
|                 // Bind group 0 is the view uniform
 | |
|                 self.mesh2d_pipeline.view_layout.clone(),
 | |
|                 // Bind group 1 is the mesh uniform
 | |
|                 self.mesh2d_pipeline.mesh_layout.clone(),
 | |
|             ]),
 | |
|             primitive: PrimitiveState {
 | |
|                 front_face: FrontFace::Ccw,
 | |
|                 cull_mode: Some(Face::Back),
 | |
|                 unclipped_depth: false,
 | |
|                 polygon_mode: PolygonMode::Fill,
 | |
|                 conservative: false,
 | |
|                 topology: key.primitive_topology(),
 | |
|                 strip_index_format: None,
 | |
|             },
 | |
|             depth_stencil: None,
 | |
|             multisample: MultisampleState {
 | |
|                 count: key.msaa_samples(),
 | |
|                 mask: !0,
 | |
|                 alpha_to_coverage_enabled: false,
 | |
|             },
 | |
|             label: Some("colored_mesh2d_pipeline".into()),
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| // This specifies how to render a colored 2d mesh
 | |
| type DrawColoredMesh2d = (
 | |
|     // Set the pipeline
 | |
|     SetItemPipeline,
 | |
|     // Set the view uniform as bind group 0
 | |
|     SetMesh2dViewBindGroup<0>,
 | |
|     // Set the mesh uniform as bind group 1
 | |
|     SetMesh2dBindGroup<1>,
 | |
|     // Draw the mesh
 | |
|     DrawMesh2d,
 | |
| );
 | |
| 
 | |
| // The custom shader can be inline like here, included from another file at build time
 | |
| // using `include_str!()`, or loaded like any other asset with `asset_server.load()`.
 | |
| const COLORED_MESH2D_SHADER: &str = r"
 | |
| // Import the standard 2d mesh uniforms and set their bind groups
 | |
| #import bevy_sprite::mesh2d_types
 | |
| #import bevy_sprite::mesh2d_view_bindings
 | |
| 
 | |
| @group(1) @binding(0)
 | |
| var<uniform> mesh: Mesh2d;
 | |
| 
 | |
| // NOTE: Bindings must come before functions that use them!
 | |
| #import bevy_sprite::mesh2d_functions
 | |
| 
 | |
| // The structure of the vertex buffer is as specified in `specialize()`
 | |
| struct Vertex {
 | |
|     @location(0) position: vec3<f32>,
 | |
|     @location(1) color: u32,
 | |
| };
 | |
| 
 | |
| struct VertexOutput {
 | |
|     // The vertex shader must set the on-screen position of the vertex
 | |
|     @builtin(position) clip_position: vec4<f32>,
 | |
|     // We pass the vertex color to the fragment shader in location 0
 | |
|     @location(0) color: vec4<f32>,
 | |
| };
 | |
| 
 | |
| /// Entry point for the vertex shader
 | |
| @vertex
 | |
| fn vertex(vertex: Vertex) -> VertexOutput {
 | |
|     var out: VertexOutput;
 | |
|     // Project the world position of the mesh into screen position
 | |
|     out.clip_position = mesh2d_position_local_to_clip(mesh.model, vec4<f32>(vertex.position, 1.0));
 | |
|     // Unpack the `u32` from the vertex buffer into the `vec4<f32>` used by the fragment shader
 | |
|     out.color = vec4<f32>((vec4<u32>(vertex.color) >> vec4<u32>(0u, 8u, 16u, 24u)) & vec4<u32>(255u)) / 255.0;
 | |
|     return out;
 | |
| }
 | |
| 
 | |
| // The input of the fragment shader must correspond to the output of the vertex shader for all `location`s
 | |
| struct FragmentInput {
 | |
|     // The color is interpolated between vertices by default
 | |
|     @location(0) color: vec4<f32>,
 | |
| };
 | |
| 
 | |
| /// Entry point for the fragment shader
 | |
| @fragment
 | |
| fn fragment(in: FragmentInput) -> @location(0) vec4<f32> {
 | |
|     return in.color;
 | |
| }
 | |
| ";
 | |
| 
 | |
| /// Plugin that renders [`ColoredMesh2d`]s
 | |
| pub struct ColoredMesh2dPlugin;
 | |
| 
 | |
| /// Handle to the custom shader with a unique random ID
 | |
| pub const COLORED_MESH2D_SHADER_HANDLE: HandleUntyped =
 | |
|     HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 13828845428412094821);
 | |
| 
 | |
| impl Plugin for ColoredMesh2dPlugin {
 | |
|     fn build(&self, app: &mut App) {
 | |
|         // Load our custom shader
 | |
|         let mut shaders = app.world.resource_mut::<Assets<Shader>>();
 | |
|         shaders.set_untracked(
 | |
|             COLORED_MESH2D_SHADER_HANDLE,
 | |
|             Shader::from_wgsl(COLORED_MESH2D_SHADER),
 | |
|         );
 | |
| 
 | |
|         // Register our custom draw function and pipeline, and add our render systems
 | |
|         app.get_sub_app_mut(RenderApp)
 | |
|             .unwrap()
 | |
|             .add_render_command::<Transparent2d, DrawColoredMesh2d>()
 | |
|             .init_resource::<ColoredMesh2dPipeline>()
 | |
|             .init_resource::<SpecializedRenderPipelines<ColoredMesh2dPipeline>>()
 | |
|             .add_system_to_schedule(ExtractSchedule, extract_colored_mesh2d)
 | |
|             .add_system(queue_colored_mesh2d.in_set(RenderSet::Queue));
 | |
|     }
 | |
| }
 | |
| 
 | |
| /// Extract the [`ColoredMesh2d`] marker component into the render app
 | |
| pub fn extract_colored_mesh2d(
 | |
|     mut commands: Commands,
 | |
|     mut previous_len: Local<usize>,
 | |
|     // When extracting, you must use `Extract` to mark the `SystemParam`s
 | |
|     // which should be taken from the main world.
 | |
|     query: Extract<Query<(Entity, &ComputedVisibility), With<ColoredMesh2d>>>,
 | |
| ) {
 | |
|     let mut values = Vec::with_capacity(*previous_len);
 | |
|     for (entity, computed_visibility) in &query {
 | |
|         if !computed_visibility.is_visible() {
 | |
|             continue;
 | |
|         }
 | |
|         values.push((entity, ColoredMesh2d));
 | |
|     }
 | |
|     *previous_len = values.len();
 | |
|     commands.insert_or_spawn_batch(values);
 | |
| }
 | |
| 
 | |
| /// Queue the 2d meshes marked with [`ColoredMesh2d`] using our custom pipeline and draw function
 | |
| #[allow(clippy::too_many_arguments)]
 | |
| pub fn queue_colored_mesh2d(
 | |
|     transparent_draw_functions: Res<DrawFunctions<Transparent2d>>,
 | |
|     colored_mesh2d_pipeline: Res<ColoredMesh2dPipeline>,
 | |
|     mut pipelines: ResMut<SpecializedRenderPipelines<ColoredMesh2dPipeline>>,
 | |
|     pipeline_cache: Res<PipelineCache>,
 | |
|     msaa: Res<Msaa>,
 | |
|     render_meshes: Res<RenderAssets<Mesh>>,
 | |
|     colored_mesh2d: Query<(&Mesh2dHandle, &Mesh2dUniform), With<ColoredMesh2d>>,
 | |
|     mut views: Query<(
 | |
|         &VisibleEntities,
 | |
|         &mut RenderPhase<Transparent2d>,
 | |
|         &ExtractedView,
 | |
|     )>,
 | |
| ) {
 | |
|     if colored_mesh2d.is_empty() {
 | |
|         return;
 | |
|     }
 | |
|     // Iterate each view (a camera is a view)
 | |
|     for (visible_entities, mut transparent_phase, view) in &mut views {
 | |
|         let draw_colored_mesh2d = transparent_draw_functions.read().id::<DrawColoredMesh2d>();
 | |
| 
 | |
|         let mesh_key = Mesh2dPipelineKey::from_msaa_samples(msaa.samples())
 | |
|             | Mesh2dPipelineKey::from_hdr(view.hdr);
 | |
| 
 | |
|         // Queue all entities visible to that view
 | |
|         for visible_entity in &visible_entities.entities {
 | |
|             if let Ok((mesh2d_handle, mesh2d_uniform)) = colored_mesh2d.get(*visible_entity) {
 | |
|                 // Get our specialized pipeline
 | |
|                 let mut mesh2d_key = mesh_key;
 | |
|                 if let Some(mesh) = render_meshes.get(&mesh2d_handle.0) {
 | |
|                     mesh2d_key |=
 | |
|                         Mesh2dPipelineKey::from_primitive_topology(mesh.primitive_topology);
 | |
|                 }
 | |
| 
 | |
|                 let pipeline_id =
 | |
|                     pipelines.specialize(&pipeline_cache, &colored_mesh2d_pipeline, mesh2d_key);
 | |
| 
 | |
|                 let mesh_z = mesh2d_uniform.transform.w_axis.z;
 | |
|                 transparent_phase.add(Transparent2d {
 | |
|                     entity: *visible_entity,
 | |
|                     draw_function: draw_colored_mesh2d,
 | |
|                     pipeline: pipeline_id,
 | |
|                     // The 2d render items are sorted according to their z value before rendering,
 | |
|                     // in order to get correct transparency
 | |
|                     sort_key: FloatOrd(mesh_z),
 | |
|                     // This material is not batched
 | |
|                     batch_range: None,
 | |
|                 });
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| }
 |