bevy_render/view/visibility/
range.rs1use super::VisibilityRange;
5use bevy_app::{App, Plugin};
6use bevy_ecs::{
7 entity::Entity,
8 lifecycle::RemovedComponents,
9 query::Changed,
10 resource::Resource,
11 schedule::IntoScheduleConfigs as _,
12 system::{Query, Res, ResMut},
13};
14use bevy_log::warn_once;
15use bevy_math::{vec4, Vec4};
16use bevy_platform::collections::HashMap;
17use bevy_utils::prelude::default;
18use nonmax::NonMaxU16;
19use wgpu::{BufferBindingType, BufferUsages};
20
21use crate::{
22 render_resource::BufferVec,
23 renderer::{RenderDevice, RenderQueue},
24 sync_world::{MainEntity, MainEntityHashMap},
25 Extract, ExtractSchedule, GpuResourceAppExt, Render, RenderApp, RenderSystems,
26};
27
28pub const VISIBILITY_RANGES_STORAGE_BUFFER_COUNT: u32 = 4;
35
36const VISIBILITY_RANGE_UNIFORM_BUFFER_SIZE: usize = 64;
40
41pub struct RenderVisibilityRangePlugin;
44
45impl Plugin for RenderVisibilityRangePlugin {
46 fn build(&self, app: &mut App) {
47 let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
48 return;
49 };
50
51 render_app
52 .init_gpu_resource::<RenderVisibilityRanges>()
53 .add_systems(ExtractSchedule, extract_visibility_ranges)
54 .add_systems(
55 Render,
56 write_render_visibility_ranges.in_set(RenderSystems::PrepareResourcesFlush),
57 );
58 }
59}
60
61#[derive(impl bevy_ecs::resource::Resource for RenderVisibilityRanges where
Self: ::core::marker::Send + ::core::marker::Sync + 'static {}Resource)]
63pub struct RenderVisibilityRanges {
64 entities: MainEntityHashMap<RenderVisibilityEntityInfo>,
66
67 range_to_index: HashMap<VisibilityRange, NonMaxU16>,
72
73 buffer: BufferVec<Vec4>,
78
79 buffer_dirty: bool,
82}
83
84struct RenderVisibilityEntityInfo {
86 buffer_index: NonMaxU16,
88 is_abrupt: bool,
90}
91
92impl Default for RenderVisibilityRanges {
93 fn default() -> Self {
94 Self {
95 entities: default(),
96 range_to_index: default(),
97 buffer: BufferVec::new(
98 BufferUsages::STORAGE | BufferUsages::UNIFORM | BufferUsages::VERTEX,
99 ),
100 buffer_dirty: true,
101 }
102 }
103}
104
105impl RenderVisibilityRanges {
106 fn clear(&mut self) {
114 self.entities.clear();
115 }
116
117 fn insert(&mut self, entity: MainEntity, visibility_range: &VisibilityRange) {
119 let buffer_index = match self.range_to_index.get(visibility_range) {
122 Some(index) => *index,
123 None => {
124 let index = u16::try_from(self.range_to_index.len())
128 .ok()
129 .and_then(|next| NonMaxU16::try_from(next).ok())
130 .unwrap_or_else(|| {
131 {
{
static SHOULD_FIRE: ::bevy_utils::OnceFlag =
::bevy_utils::OnceFlag::new();
if SHOULD_FIRE.set() {
{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event src/view/visibility/range.rs:131",
"bevy_render::view::visibility::range",
::tracing::Level::WARN,
::tracing_core::__macro_support::Option::Some("src/view/visibility/range.rs"),
::tracing_core::__macro_support::Option::Some(131u32),
::tracing_core::__macro_support::Option::Some("bevy_render::view::visibility::range"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::WARN <=
::tracing::level_filters::STATIC_MAX_LEVEL &&
::tracing::Level::WARN <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("More than {0} distinct `VisibilityRange`s are in use; additional ranges will share GPU slot 0 and may be culled or crossfaded incorrectly.",
u16::MAX) as &dyn ::tracing::field::Value))])
});
} else { ; }
};
}
}
};warn_once!(
132 "More than {} distinct `VisibilityRange`s are in use; \
133 additional ranges will share GPU slot 0 and may be \
134 culled or crossfaded incorrectly.",
135 u16::MAX
136 );
137 NonMaxU16::default()
138 });
139 self.range_to_index.insert(visibility_range.clone(), index);
140 self.buffer_dirty = true;
141 index
142 }
143 };
144
145 self.entities.insert(
146 entity,
147 RenderVisibilityEntityInfo {
148 buffer_index,
149 is_abrupt: visibility_range.is_abrupt(),
150 },
151 );
152 }
153
154 fn rebuild_buffer_if_dirty(&mut self) {
158 if !self.buffer_dirty {
159 return;
160 }
161 let mut ordered = ::alloc::vec::from_elem(Vec4::ZERO, self.range_to_index.len())vec![Vec4::ZERO; self.range_to_index.len()];
162 for (range, index) in &self.range_to_index {
163 ordered[index.get() as usize] = vec4(
164 range.start_margin.start,
165 range.start_margin.end,
166 range.end_margin.start,
167 range.end_margin.end,
168 );
169 }
170 self.buffer.clear();
171 for value in ordered {
172 self.buffer.push(value);
173 }
174 }
175
176 #[inline]
181 pub fn lod_index_for_entity(&self, entity: MainEntity) -> Option<NonMaxU16> {
182 self.entities.get(&entity).map(|info| info.buffer_index)
183 }
184
185 #[inline]
188 pub fn entity_has_crossfading_visibility_ranges(&self, entity: MainEntity) -> bool {
189 self.entities
190 .get(&entity)
191 .is_some_and(|info| !info.is_abrupt)
192 }
193
194 #[inline]
196 pub fn buffer(&self) -> &BufferVec<Vec4> {
197 &self.buffer
198 }
199}
200
201pub fn extract_visibility_ranges(
204 mut render_visibility_ranges: ResMut<RenderVisibilityRanges>,
205 visibility_ranges_query: Extract<Query<(Entity, &VisibilityRange)>>,
206 changed_ranges_query: Extract<Query<Entity, Changed<VisibilityRange>>>,
207 mut removed_visibility_ranges: Extract<RemovedComponents<VisibilityRange>>,
208) {
209 if changed_ranges_query.is_empty() && removed_visibility_ranges.read().next().is_none() {
210 return;
211 }
212
213 render_visibility_ranges.clear();
214 for (entity, visibility_range) in visibility_ranges_query.iter() {
215 render_visibility_ranges.insert(entity.into(), visibility_range);
216 }
217 render_visibility_ranges.rebuild_buffer_if_dirty();
218}
219
220pub fn write_render_visibility_ranges(
222 render_device: Res<RenderDevice>,
223 render_queue: Res<RenderQueue>,
224 mut render_visibility_ranges: ResMut<RenderVisibilityRanges>,
225) {
226 if !render_visibility_ranges.buffer_dirty {
228 return;
229 }
230
231 match render_device.get_supported_read_only_binding_type(VISIBILITY_RANGES_STORAGE_BUFFER_COUNT)
233 {
234 BufferBindingType::Uniform
237 if render_visibility_ranges.buffer.len() > VISIBILITY_RANGE_UNIFORM_BUFFER_SIZE =>
238 {
239 render_visibility_ranges
240 .buffer
241 .truncate(VISIBILITY_RANGE_UNIFORM_BUFFER_SIZE);
242 }
243 BufferBindingType::Uniform
244 if render_visibility_ranges.buffer.len() < VISIBILITY_RANGE_UNIFORM_BUFFER_SIZE =>
245 {
246 while render_visibility_ranges.buffer.len() < VISIBILITY_RANGE_UNIFORM_BUFFER_SIZE {
247 render_visibility_ranges.buffer.push(default());
248 }
249 }
250
251 BufferBindingType::Storage { .. } if render_visibility_ranges.buffer.is_empty() => {
254 render_visibility_ranges.buffer.push(default());
255 }
256
257 _ => {}
258 }
259
260 render_visibility_ranges
262 .buffer
263 .write_buffer(&render_device, &render_queue);
264 render_visibility_ranges.buffer_dirty = false;
265}