1use bevy_app::prelude::*;
4use bevy_camera::{Camera, Camera3d};
5use bevy_ecs::{component::*, lifecycle::ComponentHook, prelude::*};
6use bevy_math::UVec2;
7use bevy_platform::collections::HashSet;
8use bevy_platform::time::Instant;
9use bevy_reflect::{std_traits::ReflectDefault, Reflect};
10use bevy_render::{
11 camera::ExtractedCamera,
12 extract_component::{ExtractComponent, ExtractComponentPlugin},
13 render_graph::{RenderGraphExt, ViewNodeRunner},
14 render_resource::{BufferUsages, BufferVec, DynamicUniformBuffer, ShaderType, TextureUsages},
15 renderer::{RenderDevice, RenderQueue},
16 view::Msaa,
17 Render, RenderApp, RenderStartup, RenderSystems,
18};
19use bevy_shader::load_shader_library;
20use bevy_window::PrimaryWindow;
21use resolve::{
22 node::{OitResolveNode, OitResolvePass},
23 OitResolvePlugin,
24};
25use tracing::{trace, warn};
26
27use crate::core_3d::graph::{Core3d, Node3d};
28
29pub mod resolve;
31
32#[derive(Clone, Copy, ExtractComponent, Reflect, ShaderType)]
39#[reflect(Clone, Default)]
40pub struct OrderIndependentTransparencySettings {
41 pub layer_count: i32,
45 pub alpha_threshold: f32,
49}
50
51impl Default for OrderIndependentTransparencySettings {
52 fn default() -> Self {
53 Self {
54 layer_count: 8,
55 alpha_threshold: 0.0,
56 }
57 }
58}
59
60impl Component for OrderIndependentTransparencySettings {
63 const STORAGE_TYPE: StorageType = StorageType::SparseSet;
64 type Mutability = Mutable;
65
66 fn on_add() -> Option<ComponentHook> {
67 Some(|world, context| {
68 if let Some(value) = world.get::<OrderIndependentTransparencySettings>(context.entity)
69 && value.layer_count > 32
70 {
71 warn!("{}OrderIndependentTransparencySettings layer_count set to {} might be too high.",
72 context.caller.map(|location|format!("{location}: ")).unwrap_or_default(),
73 value.layer_count
74 );
75 }
76 })
77 }
78}
79
80pub struct OrderIndependentTransparencyPlugin;
98impl Plugin for OrderIndependentTransparencyPlugin {
99 fn build(&self, app: &mut App) {
100 load_shader_library!(app, "oit_draw.wgsl");
101
102 app.add_plugins((
103 ExtractComponentPlugin::<OrderIndependentTransparencySettings>::default(),
104 OitResolvePlugin,
105 ))
106 .add_systems(Update, check_msaa)
107 .add_systems(Last, configure_depth_texture_usages);
108
109 let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
110 return;
111 };
112
113 render_app
114 .add_systems(RenderStartup, init_oit_buffers)
115 .add_systems(
116 Render,
117 prepare_oit_buffers.in_set(RenderSystems::PrepareResources),
118 );
119
120 render_app
121 .add_render_graph_node::<ViewNodeRunner<OitResolveNode>>(Core3d, OitResolvePass)
122 .add_render_graph_edges(
123 Core3d,
124 (
125 Node3d::MainTransparentPass,
126 OitResolvePass,
127 Node3d::EndMainPass,
128 ),
129 );
130 }
131}
132
133fn configure_depth_texture_usages(
137 p: Query<Entity, With<PrimaryWindow>>,
138 cameras: Query<(&Camera, Has<OrderIndependentTransparencySettings>)>,
139 mut new_cameras: Query<(&mut Camera3d, &Camera), Added<Camera3d>>,
140) {
141 if new_cameras.is_empty() {
142 return;
143 }
144
145 let primary_window = p.single().ok();
147 let mut render_target_has_oit = <HashSet<_>>::default();
148 for (camera, has_oit) in &cameras {
149 if has_oit {
150 render_target_has_oit.insert(camera.target.normalize(primary_window));
151 }
152 }
153
154 for (mut camera_3d, camera) in &mut new_cameras {
156 if render_target_has_oit.contains(&camera.target.normalize(primary_window)) {
157 let mut usages = TextureUsages::from(camera_3d.depth_texture_usages);
158 usages |= TextureUsages::RENDER_ATTACHMENT | TextureUsages::TEXTURE_BINDING;
159 camera_3d.depth_texture_usages = usages.into();
160 }
161 }
162}
163
164fn check_msaa(cameras: Query<&Msaa, With<OrderIndependentTransparencySettings>>) {
165 for msaa in &cameras {
166 if msaa.samples() > 1 {
167 panic!("MSAA is not supported when using OrderIndependentTransparency");
168 }
169 }
170}
171
172#[derive(Resource)]
176pub struct OitBuffers {
177 pub layers: BufferVec<UVec2>,
181 pub layer_ids: BufferVec<i32>,
183 pub settings: DynamicUniformBuffer<OrderIndependentTransparencySettings>,
184}
185
186pub fn init_oit_buffers(
187 mut commands: Commands,
188 render_device: Res<RenderDevice>,
189 render_queue: Res<RenderQueue>,
190) {
191 let mut layers = BufferVec::new(BufferUsages::COPY_DST | BufferUsages::STORAGE);
194 layers.set_label(Some("oit_layers"));
195 layers.reserve(1, &render_device);
196 layers.write_buffer(&render_device, &render_queue);
197
198 let mut layer_ids = BufferVec::new(BufferUsages::COPY_DST | BufferUsages::STORAGE);
199 layer_ids.set_label(Some("oit_layer_ids"));
200 layer_ids.reserve(1, &render_device);
201 layer_ids.write_buffer(&render_device, &render_queue);
202
203 let mut settings = DynamicUniformBuffer::default();
204 settings.set_label(Some("oit_settings"));
205
206 commands.insert_resource(OitBuffers {
207 layers,
208 layer_ids,
209 settings,
210 });
211}
212
213#[derive(Component)]
214pub struct OrderIndependentTransparencySettingsOffset {
215 pub offset: u32,
216}
217
218pub fn prepare_oit_buffers(
222 mut commands: Commands,
223 render_device: Res<RenderDevice>,
224 render_queue: Res<RenderQueue>,
225 cameras: Query<
226 (&ExtractedCamera, &OrderIndependentTransparencySettings),
227 (
228 Changed<ExtractedCamera>,
229 Changed<OrderIndependentTransparencySettings>,
230 ),
231 >,
232 camera_oit_uniforms: Query<(Entity, &OrderIndependentTransparencySettings)>,
233 mut buffers: ResMut<OitBuffers>,
234) {
235 let mut max_layer_ids_size = usize::MIN;
237 let mut max_layers_size = usize::MIN;
238 for (camera, settings) in &cameras {
239 let Some(size) = camera.physical_target_size else {
240 continue;
241 };
242
243 let layer_count = settings.layer_count as usize;
244 let size = (size.x * size.y) as usize;
245 max_layer_ids_size = max_layer_ids_size.max(size);
246 max_layers_size = max_layers_size.max(size * layer_count);
247 }
248
249 if buffers.layers.capacity() < max_layers_size {
251 let start = Instant::now();
252 buffers.layers.reserve(max_layers_size, &render_device);
253 let remaining = max_layers_size - buffers.layers.capacity();
254 for _ in 0..remaining {
255 buffers.layers.push(UVec2::ZERO);
256 }
257 buffers.layers.write_buffer(&render_device, &render_queue);
258 trace!(
259 "OIT layers buffer updated in {:.01}ms with total size {} MiB",
260 start.elapsed().as_millis(),
261 buffers.layers.capacity() * size_of::<UVec2>() / 1024 / 1024,
262 );
263 }
264
265 if buffers.layer_ids.capacity() < max_layer_ids_size {
267 let start = Instant::now();
268 buffers
269 .layer_ids
270 .reserve(max_layer_ids_size, &render_device);
271 let remaining = max_layer_ids_size - buffers.layer_ids.capacity();
272 for _ in 0..remaining {
273 buffers.layer_ids.push(0);
274 }
275 buffers
276 .layer_ids
277 .write_buffer(&render_device, &render_queue);
278 trace!(
279 "OIT layer ids buffer updated in {:.01}ms with total size {} MiB",
280 start.elapsed().as_millis(),
281 buffers.layer_ids.capacity() * size_of::<UVec2>() / 1024 / 1024,
282 );
283 }
284
285 if let Some(mut writer) = buffers.settings.get_writer(
286 camera_oit_uniforms.iter().len(),
287 &render_device,
288 &render_queue,
289 ) {
290 for (entity, settings) in &camera_oit_uniforms {
291 let offset = writer.write(settings);
292 commands
293 .entity(entity)
294 .insert(OrderIndependentTransparencySettingsOffset { offset });
295 }
296 }
297}