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_math::{vec4, Vec4};
15use bevy_platform::collections::HashMap;
16use bevy_utils::prelude::default;
17use nonmax::NonMaxU16;
18use wgpu::{BufferBindingType, BufferUsages};
19
20use crate::{
21 render_resource::BufferVec,
22 renderer::{RenderDevice, RenderQueue},
23 sync_world::{MainEntity, MainEntityHashMap},
24 Extract, ExtractSchedule, Render, RenderApp, RenderSystems,
25};
26
27pub const VISIBILITY_RANGES_STORAGE_BUFFER_COUNT: u32 = 4;
34
35const VISIBILITY_RANGE_UNIFORM_BUFFER_SIZE: usize = 64;
39
40pub struct RenderVisibilityRangePlugin;
43
44impl Plugin for RenderVisibilityRangePlugin {
45 fn build(&self, app: &mut App) {
46 let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
47 return;
48 };
49
50 render_app
51 .init_resource::<RenderVisibilityRanges>()
52 .add_systems(ExtractSchedule, extract_visibility_ranges)
53 .add_systems(
54 Render,
55 write_render_visibility_ranges.in_set(RenderSystems::PrepareResourcesFlush),
56 );
57 }
58}
59
60#[derive(Resource)]
62pub struct RenderVisibilityRanges {
63 entities: MainEntityHashMap<RenderVisibilityEntityInfo>,
65
66 range_to_index: HashMap<VisibilityRange, NonMaxU16>,
71
72 buffer: BufferVec<Vec4>,
77
78 buffer_dirty: bool,
81}
82
83struct RenderVisibilityEntityInfo {
85 buffer_index: NonMaxU16,
87 is_abrupt: bool,
89}
90
91impl Default for RenderVisibilityRanges {
92 fn default() -> Self {
93 Self {
94 entities: default(),
95 range_to_index: default(),
96 buffer: BufferVec::new(
97 BufferUsages::STORAGE | BufferUsages::UNIFORM | BufferUsages::VERTEX,
98 ),
99 buffer_dirty: true,
100 }
101 }
102}
103
104impl RenderVisibilityRanges {
105 fn clear(&mut self) {
108 self.entities.clear();
109 self.range_to_index.clear();
110 self.buffer.clear();
111 self.buffer_dirty = true;
112 }
113
114 fn insert(&mut self, entity: MainEntity, visibility_range: &VisibilityRange) {
116 let buffer_index = *self
119 .range_to_index
120 .entry(visibility_range.clone())
121 .or_insert_with(|| {
122 NonMaxU16::try_from(self.buffer.push(vec4(
123 visibility_range.start_margin.start,
124 visibility_range.start_margin.end,
125 visibility_range.end_margin.start,
126 visibility_range.end_margin.end,
127 )) as u16)
128 .unwrap_or_default()
129 });
130
131 self.entities.insert(
132 entity,
133 RenderVisibilityEntityInfo {
134 buffer_index,
135 is_abrupt: visibility_range.is_abrupt(),
136 },
137 );
138 }
139
140 #[inline]
145 pub fn lod_index_for_entity(&self, entity: MainEntity) -> Option<NonMaxU16> {
146 self.entities.get(&entity).map(|info| info.buffer_index)
147 }
148
149 #[inline]
152 pub fn entity_has_crossfading_visibility_ranges(&self, entity: MainEntity) -> bool {
153 self.entities
154 .get(&entity)
155 .is_some_and(|info| !info.is_abrupt)
156 }
157
158 #[inline]
160 pub fn buffer(&self) -> &BufferVec<Vec4> {
161 &self.buffer
162 }
163}
164
165pub fn extract_visibility_ranges(
168 mut render_visibility_ranges: ResMut<RenderVisibilityRanges>,
169 visibility_ranges_query: Extract<Query<(Entity, &VisibilityRange)>>,
170 changed_ranges_query: Extract<Query<Entity, Changed<VisibilityRange>>>,
171 mut removed_visibility_ranges: Extract<RemovedComponents<VisibilityRange>>,
172) {
173 if changed_ranges_query.is_empty() && removed_visibility_ranges.read().next().is_none() {
174 return;
175 }
176
177 render_visibility_ranges.clear();
178 for (entity, visibility_range) in visibility_ranges_query.iter() {
179 render_visibility_ranges.insert(entity.into(), visibility_range);
180 }
181}
182
183pub fn write_render_visibility_ranges(
185 render_device: Res<RenderDevice>,
186 render_queue: Res<RenderQueue>,
187 mut render_visibility_ranges: ResMut<RenderVisibilityRanges>,
188) {
189 if !render_visibility_ranges.buffer_dirty {
191 return;
192 }
193
194 match render_device.get_supported_read_only_binding_type(VISIBILITY_RANGES_STORAGE_BUFFER_COUNT)
196 {
197 BufferBindingType::Uniform
200 if render_visibility_ranges.buffer.len() > VISIBILITY_RANGE_UNIFORM_BUFFER_SIZE =>
201 {
202 render_visibility_ranges
203 .buffer
204 .truncate(VISIBILITY_RANGE_UNIFORM_BUFFER_SIZE);
205 }
206 BufferBindingType::Uniform
207 if render_visibility_ranges.buffer.len() < VISIBILITY_RANGE_UNIFORM_BUFFER_SIZE =>
208 {
209 while render_visibility_ranges.buffer.len() < VISIBILITY_RANGE_UNIFORM_BUFFER_SIZE {
210 render_visibility_ranges.buffer.push(default());
211 }
212 }
213
214 BufferBindingType::Storage { .. } if render_visibility_ranges.buffer.is_empty() => {
217 render_visibility_ranges.buffer.push(default());
218 }
219
220 _ => {}
221 }
222
223 render_visibility_ranges
225 .buffer
226 .write_buffer(&render_device, &render_queue);
227 render_visibility_ranges.buffer_dirty = false;
228}