Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -760,6 +760,7 @@ anyhow = "1"
accesskit = "0.24"
nonmax = "0.5"
gltf = "1.4"
indexmap = "2"

[target.'cfg(not(target_family = "wasm"))'.dev-dependencies]
ureq = { version = "3.0.8", features = ["json"] }
Expand Down
28 changes: 27 additions & 1 deletion crates/bevy_camera/src/visibility/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -347,13 +347,33 @@ impl VisibleEntities {
///
/// This component contains all mesh entities visible from the current light view.
/// The collection is updated automatically by `bevy_pbr::SimulationLightSystems`.
#[derive(Component, Clone, Debug, Default, Reflect, Deref, DerefMut)]
#[derive(Component, Clone, Debug, Default, Reflect)]
#[reflect(Component, Debug, Default, Clone)]
pub struct VisibleMeshEntities {
#[reflect(ignore, clone)]
pub entities: Vec<Entity>,
}

impl VisibleMeshEntities {
/// Resizes the entity vector so that its capacity isn't too much higher
/// than its size.
pub fn shrink(&mut self) {
// Check that visible entities capacity() is no more than two times greater than len()
let capacity = self.entities.capacity();
let reserved = capacity
.checked_div(self.entities.len())
.map_or(0, |reserve| {
if reserve > 2 {
capacity / (reserve / 2)
} else {
capacity
}
});
Comment on lines +363 to +371
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i don't really understand why all this logic is needed. can't we just call shrink_to(len() * 2)? it no-ops if the provided value is larger than the existing capacity. is this something about keeping stuff PoT aligned? would probably be better served by doing shrink_to(len().next_power_of_two())


self.entities.shrink_to(reserved);
}
}

#[derive(Component, Clone, Debug, Default, Reflect)]
#[reflect(Component, Debug, Default, Clone)]
pub struct CubemapVisibleEntities {
Expand Down Expand Up @@ -760,6 +780,12 @@ pub fn check_visibility(
visible_entities.get_mut(*class).append(entities);
}
}

// The list must be sorted in order for the O(n) diffing algorithm that
// visibility determination uses to work, so do that now.
for visible_entities in visible_entities.entities.values_mut() {
visible_entities.sort_unstable();
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_core_pipeline/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ bevy_platform = { path = "../bevy_platform", version = "0.19.0-dev", default-fea
] }

bitflags = "2.3"
radsort = "0.1"
nonmax = "0.5"
indexmap = "2"

[lints]
workspace = true
Expand Down
16 changes: 12 additions & 4 deletions crates/bevy_core_pipeline/src/core_2d/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use core::ops::Range;

use bevy_asset::UntypedAssetId;
use bevy_camera::{Camera, Camera2d};
use bevy_ecs::entity::EntityHash;
use bevy_image::ToExtents;
use bevy_platform::collections::{HashMap, HashSet};
use bevy_render::{
Expand All @@ -13,6 +14,7 @@ use bevy_render::{
render_phase::PhaseItemBatchSetKey,
view::{ExtractedView, RetainedViewEntity},
};
use indexmap::IndexMap;
pub use main_opaque_pass_2d_node::*;
pub use main_transparent_pass_2d_node::*;

Expand Down Expand Up @@ -365,9 +367,15 @@ impl SortedPhaseItem for Transparent2d {
}

#[inline]
fn sort(items: &mut [Self]) {
// radsort is a stable radix sort that performed better than `slice::sort_by_key` or `slice::sort_unstable_by_key`.
radsort::sort_by_key(items, |item| item.sort_key().0);
fn sort(items: &mut IndexMap<(Entity, MainEntity), Transparent2d, EntityHash>) {
items.sort_by_key(|_, item| item.sort_key());
}

fn recalculate_sort_keys(
_: &mut IndexMap<(Entity, MainEntity), Self, EntityHash>,
_: &ExtractedView,
) {
// Sort keys are precalculated for 2D phase items.
}

fn indexed(&self) -> bool {
Expand Down Expand Up @@ -399,7 +407,7 @@ pub fn extract_core_2d_camera_phases(
// This is the main 2D camera, so we use the first subview index (0).
let retained_view_entity = RetainedViewEntity::new(main_entity.into(), None, 0);

transparent_2d_phases.insert_or_clear(retained_view_entity);
transparent_2d_phases.prepare_for_new_frame(retained_view_entity);
opaque_2d_phases.prepare_for_new_frame(retained_view_entity, GpuPreprocessingMode::None);
alpha_mask_2d_phases
.prepare_for_new_frame(retained_view_entity, GpuPreprocessingMode::None);
Expand Down
59 changes: 52 additions & 7 deletions crates/bevy_core_pipeline/src/core_3d/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ pub const DEPTH_TEXTURE_SAMPLING_SUPPORTED: bool = false;
#[cfg(any(feature = "webgpu", not(target_arch = "wasm32")))]
pub const DEPTH_TEXTURE_SAMPLING_SUPPORTED: bool = true;

use core::ops::Range;
use core::{f32, ops::Range};

use bevy_camera::{Camera, Camera3d, Camera3dDepthLoadOp};
use bevy_diagnostic::FrameCount;
Expand All @@ -33,20 +33,21 @@ use bevy_render::{
camera::CameraRenderGraph,
mesh::allocator::SlabId,
occlusion_culling::OcclusionCulling,
render_phase::PhaseItemBatchSetKey,
render_phase::{PhaseItemBatchSetKey, ViewRangefinder3d},
texture::CachedTexture,
view::{prepare_view_targets, NoIndirectDrawing, RetainedViewEntity},
};
use indexmap::IndexMap;
pub use main_opaque_pass_3d_node::*;
pub use main_transparent_pass_3d_node::*;

use bevy_app::{App, Plugin, PostUpdate};
use bevy_asset::UntypedAssetId;
use bevy_color::LinearRgba;
use bevy_ecs::prelude::*;
use bevy_ecs::{entity::EntityHash, prelude::*};
use bevy_image::ToExtents;
use bevy_log::warn;
use bevy_math::FloatOrd;
use bevy_math::{FloatOrd, Vec3};
use bevy_platform::collections::{HashMap, HashSet};
use bevy_render::{
camera::ExtractedCamera,
Expand Down Expand Up @@ -371,6 +372,7 @@ impl CachedRenderPipelinePhaseItem for AlphaMask3d {
}

pub struct Transparent3d {
pub sorting_info: TransparentSortingInfo3d,
pub distance: f32,
pub pipeline: CachedRenderPipelineId,
pub entity: (Entity, MainEntity),
Expand Down Expand Up @@ -428,8 +430,19 @@ impl SortedPhaseItem for Transparent3d {
}

#[inline]
fn sort(items: &mut [Self]) {
radsort::sort_by_key(items, |item| item.distance);
fn sort(items: &mut IndexMap<(Entity, MainEntity), Transparent3d, EntityHash>) {
items.sort_by_key(|_, item| item.sort_key());
}

fn recalculate_sort_keys(
items: &mut IndexMap<(Entity, MainEntity), Self, EntityHash>,
view: &ExtractedView,
) {
// Determine the distance to the view for each phase item.
let rangefinder = view.rangefinder3d();
for item in items.values_mut() {
item.distance = item.sorting_info.sort_distance(&rangefinder);
}
}

#[inline]
Expand All @@ -445,6 +458,38 @@ impl CachedRenderPipelinePhaseItem for Transparent3d {
}
}

/// Information needed to perform a depth sort.
#[derive(Clone, Copy)]
pub enum TransparentSortingInfo3d {
/// No information is needed because this object should always appear on top
/// of other objects.
AlwaysOnTop,
/// Information needed to sort the object based on distance to the view.
Sorted {
/// The center of the mesh.
///
/// This is the point that is used to sort.
mesh_center: Vec3,
/// An additional value that's artificially added to the distance before
/// sorting.
depth_bias: f32,
},
}

impl TransparentSortingInfo3d {
/// Calculates the value used for distance sorting for an item.
/// For [`Self::AlwaysOnTop`], this is [`f32::NEG_INFINITY`].
pub fn sort_distance(&self, rangefinder: &ViewRangefinder3d) -> f32 {
match *self {
TransparentSortingInfo3d::AlwaysOnTop => f32::NEG_INFINITY,
TransparentSortingInfo3d::Sorted {
mesh_center,
depth_bias,
} => rangefinder.distance(&mesh_center) + depth_bias,
}
}
}

pub fn extract_core_3d_camera_phases(
mut opaque_3d_phases: ResMut<ViewBinnedRenderPhases<Opaque3d>>,
mut alpha_mask_3d_phases: ResMut<ViewBinnedRenderPhases<AlphaMask3d>>,
Expand Down Expand Up @@ -473,7 +518,7 @@ pub fn extract_core_3d_camera_phases(

opaque_3d_phases.prepare_for_new_frame(retained_view_entity, gpu_preprocessing_mode);
alpha_mask_3d_phases.prepare_for_new_frame(retained_view_entity, gpu_preprocessing_mode);
transparent_3d_phases.insert_or_clear(retained_view_entity);
transparent_3d_phases.prepare_for_new_frame(retained_view_entity);

live_entities.insert(retained_view_entity);
}
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_gizmos_render/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ bevy_render = { path = "../bevy_render", version = "0.19.0-dev" }
bevy_utils = { path = "../bevy_utils", version = "0.19.0-dev" }
bevy_core_pipeline = { path = "../bevy_core_pipeline", version = "0.19.0-dev" }
bevy_transform = { path = "../bevy_transform", version = "0.19.0-dev" }
bevy_reflect = { path = "../bevy_reflect", version = "0.19.0-dev" }

# other
bytemuck = "1.0"
Expand Down
44 changes: 41 additions & 3 deletions crates/bevy_gizmos_render/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,14 @@ mod pipeline_3d;

use bevy_app::{App, Plugin};
use bevy_ecs::{
name::Name,
resource::Resource,
schedule::{IntoScheduleConfigs, SystemSet},
system::Res,
world::{FromWorld, World},
};
use bevy_math::Affine3Ext;
use bevy_reflect::Reflect;

use {bevy_gizmos::config::GizmoMeshConfig, bevy_mesh::VertexBufferLayout};

Expand All @@ -40,6 +43,7 @@ use {
bevy_ecs::{
component::Component,
entity::Entity,
prelude::ReflectResource,
query::ROQueryItem,
system::{
lifetimeless::{Read, SRes},
Expand All @@ -62,8 +66,9 @@ use {
bytemuck::cast_slice,
};

use bevy_render::render_resource::{
BindGroupLayoutDescriptor, PipelineCache, VertexAttribute, VertexStepMode,
use bevy_render::{
extract_resource::{ExtractResource, ExtractResourcePlugin},
render_resource::{BindGroupLayoutDescriptor, PipelineCache, VertexAttribute, VertexStepMode},
};

use bevy_gizmos::{
Expand All @@ -86,7 +91,9 @@ impl Plugin for GizmoRenderPlugin {
}

app.add_plugins(UniformComponentPlugin::<LineGizmoUniform>::default())
.add_plugins(RenderAssetPlugin::<GpuLineGizmo>::default());
.add_plugins(RenderAssetPlugin::<GpuLineGizmo>::default())
.add_plugins(ExtractResourcePlugin::<LineGizmoEntities>::default())
.init_resource::<LineGizmoEntities>();

if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
render_app.add_systems(RenderStartup, init_line_gizmo_uniform_bind_group_layout);
Expand Down Expand Up @@ -602,3 +609,34 @@ fn line_joint_gizmo_vertex_buffer_layouts() -> Vec<VertexBufferLayout> {
color_layout.clone(),
]
}

/// Holds entities that the gizmo render phase items are associated with.
///
/// Sorted render phases require each phase item to be associated with an entity
/// in the main world. Immediate mode gizmos don't have entities normally, so we
/// need to create entities for them. There are three potential phase items that
/// can be added and therefore three potential entities.
#[derive(Clone, Reflect, Resource, ExtractResource)]
#[reflect(Clone, Resource)]
pub struct LineGizmoEntities {
/// An entity that regular line phase items are associated with.
pub line_gizmo_renderer: MainEntity,
/// An entity that line strip phase items are associated with.
pub line_strip_gizmo_renderer: MainEntity,
/// An entity that line joint phase items are associated with.
pub line_joint_gizmo_renderer: MainEntity,
}

impl FromWorld for LineGizmoEntities {
fn from_world(world: &mut World) -> LineGizmoEntities {
// Create the entities for line gizmo phase items to be associated with.
let line_gizmo_renderer = world.spawn(Name::new("LineGizmoRenderer")).id();
let line_strip_gizmo_renderer = world.spawn(Name::new("LineStripGizmoRenderer")).id();
let line_joint_gizmo_renderer = world.spawn(Name::new("LineJointGizmoRenderer")).id();
LineGizmoEntities {
line_gizmo_renderer: MainEntity::from(line_gizmo_renderer),
line_strip_gizmo_renderer: MainEntity::from(line_strip_gizmo_renderer),
line_joint_gizmo_renderer: MainEntity::from(line_joint_gizmo_renderer),
}
}
}
21 changes: 11 additions & 10 deletions crates/bevy_gizmos_render/src/pipeline_2d.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::{
init_line_gizmo_uniform_bind_group_layout, line_gizmo_vertex_buffer_layouts,
line_joint_gizmo_vertex_buffer_layouts, DrawLineGizmo, DrawLineJointGizmo, GizmoRenderSystems,
GpuLineGizmo, LineGizmoUniformBindgroupLayout, SetLineGizmoBindGroup,
GpuLineGizmo, LineGizmoEntities, LineGizmoUniformBindgroupLayout, SetLineGizmoBindGroup,
};
use bevy_app::{App, Plugin};
use bevy_asset::{load_embedded_asset, AssetServer, Handle};
Expand All @@ -17,6 +17,7 @@ use bevy_ecs::{
};
use bevy_image::BevyDefault as _;
use bevy_math::FloatOrd;
use bevy_render::RenderStartup;
use bevy_render::{
render_asset::{prepare_assets, RenderAssets},
render_phase::{
Expand All @@ -27,7 +28,6 @@ use bevy_render::{
view::{ExtractedView, Msaa, ViewTarget},
Render, RenderApp, RenderSystems,
};
use bevy_render::{sync_world::MainEntity, RenderStartup};
use bevy_shader::Shader;
use bevy_sprite_render::{
init_mesh_2d_pipeline, Mesh2dPipeline, Mesh2dPipelineKey, SetMesh2dViewBindGroup,
Expand Down Expand Up @@ -294,8 +294,9 @@ fn queue_line_and_joint_gizmos_2d(
mut line_gizmo_pipelines: ResMut<SpecializedRenderPipelines<LineGizmoPipeline>>,
mut line_joint_gizmo_pipelines: ResMut<SpecializedRenderPipelines<LineJointGizmoPipeline>>,
pipeline_cache: Res<PipelineCache>,
line_gizmos: Query<(Entity, &MainEntity, &GizmoMeshConfig)>,
line_gizmos: Query<(Entity, &GizmoMeshConfig)>,
line_gizmo_assets: Res<RenderAssets<GpuLineGizmo>>,
line_gizmo_entities: Res<LineGizmoEntities>,
mut transparent_render_phases: ResMut<ViewSortedRenderPhases<Transparent2d>>,
mut views: Query<(&ExtractedView, &Msaa, Option<&RenderLayers>)>,
) {
Expand All @@ -319,7 +320,7 @@ fn queue_line_and_joint_gizmos_2d(
| Mesh2dPipelineKey::from_hdr(view.hdr);

let render_layers = render_layers.unwrap_or_default();
for (entity, main_entity, config) in &line_gizmos {
for (entity, config) in &line_gizmos {
if !config.render_layers.intersects(render_layers) {
continue;
}
Expand All @@ -339,8 +340,8 @@ fn queue_line_and_joint_gizmos_2d(
line_style: config.line_style,
},
);
transparent_phase.add(Transparent2d {
entity: (entity, *main_entity),
transparent_phase.add_transient(Transparent2d {
entity: (entity, line_gizmo_entities.line_gizmo_renderer),
draw_function,
pipeline,
sort_key: FloatOrd(f32::INFINITY),
Expand All @@ -361,8 +362,8 @@ fn queue_line_and_joint_gizmos_2d(
line_style: config.line_style,
},
);
transparent_phase.add(Transparent2d {
entity: (entity, *main_entity),
transparent_phase.add_transient(Transparent2d {
entity: (entity, line_gizmo_entities.line_strip_gizmo_renderer),
draw_function: draw_line_function_strip,
pipeline,
sort_key: FloatOrd(f32::INFINITY),
Expand All @@ -386,8 +387,8 @@ fn queue_line_and_joint_gizmos_2d(
joints: config.line_joints,
},
);
transparent_phase.add(Transparent2d {
entity: (entity, *main_entity),
transparent_phase.add_transient(Transparent2d {
entity: (entity, line_gizmo_entities.line_joint_gizmo_renderer),
draw_function: draw_line_joint_function,
pipeline,
sort_key: FloatOrd(f32::INFINITY),
Expand Down
Loading