1use bevy_app::prelude::*;
4use bevy_asset::{load_internal_asset, weak_handle, Handle};
5use bevy_ecs::{component::*, 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::{Camera, ExtractedCamera},
12 extract_component::{ExtractComponent, ExtractComponentPlugin},
13 render_graph::{RenderGraphApp, ViewNodeRunner},
14 render_resource::{
15 BufferUsages, BufferVec, DynamicUniformBuffer, Shader, ShaderType, TextureUsages,
16 },
17 renderer::{RenderDevice, RenderQueue},
18 view::Msaa,
19 Render, RenderApp, RenderSet,
20};
21use bevy_window::PrimaryWindow;
22use resolve::{
23 node::{OitResolveNode, OitResolvePass},
24 OitResolvePlugin,
25};
26use tracing::{trace, warn};
27
28use crate::core_3d::{
29 graph::{Core3d, Node3d},
30 Camera3d,
31};
32
33pub mod resolve;
35
36pub const OIT_DRAW_SHADER_HANDLE: Handle<Shader> =
38 weak_handle!("0cd3c764-39b8-437b-86b4-4e45635fc03d");
39
40#[derive(Clone, Copy, ExtractComponent, Reflect, ShaderType)]
47#[reflect(Clone, Default)]
48pub struct OrderIndependentTransparencySettings {
49 pub layer_count: i32,
53 pub alpha_threshold: f32,
57}
58
59impl Default for OrderIndependentTransparencySettings {
60 fn default() -> Self {
61 Self {
62 layer_count: 8,
63 alpha_threshold: 0.0,
64 }
65 }
66}
67
68impl Component for OrderIndependentTransparencySettings {
71 const STORAGE_TYPE: StorageType = StorageType::SparseSet;
72 type Mutability = Mutable;
73
74 fn on_add() -> Option<ComponentHook> {
75 Some(|world, context| {
76 if let Some(value) = world.get::<OrderIndependentTransparencySettings>(context.entity) {
77 if value.layer_count > 32 {
78 warn!("{}OrderIndependentTransparencySettings layer_count set to {} might be too high.",
79 context.caller.map(|location|format!("{location}: ")).unwrap_or_default(),
80 value.layer_count
81 );
82 }
83 }
84 })
85 }
86}
87
88pub struct OrderIndependentTransparencyPlugin;
106impl Plugin for OrderIndependentTransparencyPlugin {
107 fn build(&self, app: &mut App) {
108 load_internal_asset!(
109 app,
110 OIT_DRAW_SHADER_HANDLE,
111 "oit_draw.wgsl",
112 Shader::from_wgsl
113 );
114
115 app.add_plugins((
116 ExtractComponentPlugin::<OrderIndependentTransparencySettings>::default(),
117 OitResolvePlugin,
118 ))
119 .add_systems(Update, check_msaa)
120 .add_systems(Last, configure_depth_texture_usages)
121 .register_type::<OrderIndependentTransparencySettings>();
122
123 let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
124 return;
125 };
126
127 render_app.add_systems(
128 Render,
129 prepare_oit_buffers.in_set(RenderSet::PrepareResources),
130 );
131
132 render_app
133 .add_render_graph_node::<ViewNodeRunner<OitResolveNode>>(Core3d, OitResolvePass)
134 .add_render_graph_edges(
135 Core3d,
136 (
137 Node3d::MainTransparentPass,
138 OitResolvePass,
139 Node3d::EndMainPass,
140 ),
141 );
142 }
143
144 fn finish(&self, app: &mut App) {
145 let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
146 return;
147 };
148
149 render_app.init_resource::<OitBuffers>();
150 }
151}
152
153fn configure_depth_texture_usages(
157 p: Query<Entity, With<PrimaryWindow>>,
158 cameras: Query<(&Camera, Has<OrderIndependentTransparencySettings>)>,
159 mut new_cameras: Query<(&mut Camera3d, &Camera), Added<Camera3d>>,
160) {
161 if new_cameras.is_empty() {
162 return;
163 }
164
165 let primary_window = p.single().ok();
167 let mut render_target_has_oit = <HashSet<_>>::default();
168 for (camera, has_oit) in &cameras {
169 if has_oit {
170 render_target_has_oit.insert(camera.target.normalize(primary_window));
171 }
172 }
173
174 for (mut camera_3d, camera) in &mut new_cameras {
176 if render_target_has_oit.contains(&camera.target.normalize(primary_window)) {
177 let mut usages = TextureUsages::from(camera_3d.depth_texture_usages);
178 usages |= TextureUsages::RENDER_ATTACHMENT | TextureUsages::TEXTURE_BINDING;
179 camera_3d.depth_texture_usages = usages.into();
180 }
181 }
182}
183
184fn check_msaa(cameras: Query<&Msaa, With<OrderIndependentTransparencySettings>>) {
185 for msaa in &cameras {
186 if msaa.samples() > 1 {
187 panic!("MSAA is not supported when using OrderIndependentTransparency");
188 }
189 }
190}
191
192#[derive(Resource)]
196pub struct OitBuffers {
197 pub layers: BufferVec<UVec2>,
201 pub layer_ids: BufferVec<i32>,
203 pub settings: DynamicUniformBuffer<OrderIndependentTransparencySettings>,
204}
205
206impl FromWorld for OitBuffers {
207 fn from_world(world: &mut World) -> Self {
208 let render_device = world.resource::<RenderDevice>();
209 let render_queue = world.resource::<RenderQueue>();
210
211 let mut layers = BufferVec::new(BufferUsages::COPY_DST | BufferUsages::STORAGE);
214 layers.set_label(Some("oit_layers"));
215 layers.reserve(1, render_device);
216 layers.write_buffer(render_device, render_queue);
217
218 let mut layer_ids = BufferVec::new(BufferUsages::COPY_DST | BufferUsages::STORAGE);
219 layer_ids.set_label(Some("oit_layer_ids"));
220 layer_ids.reserve(1, render_device);
221 layer_ids.write_buffer(render_device, render_queue);
222
223 let mut settings = DynamicUniformBuffer::default();
224 settings.set_label(Some("oit_settings"));
225
226 Self {
227 layers,
228 layer_ids,
229 settings,
230 }
231 }
232}
233
234#[derive(Component)]
235pub struct OrderIndependentTransparencySettingsOffset {
236 pub offset: u32,
237}
238
239pub fn prepare_oit_buffers(
243 mut commands: Commands,
244 render_device: Res<RenderDevice>,
245 render_queue: Res<RenderQueue>,
246 cameras: Query<
247 (&ExtractedCamera, &OrderIndependentTransparencySettings),
248 (
249 Changed<ExtractedCamera>,
250 Changed<OrderIndependentTransparencySettings>,
251 ),
252 >,
253 camera_oit_uniforms: Query<(Entity, &OrderIndependentTransparencySettings)>,
254 mut buffers: ResMut<OitBuffers>,
255) {
256 let mut max_layer_ids_size = usize::MIN;
258 let mut max_layers_size = usize::MIN;
259 for (camera, settings) in &cameras {
260 let Some(size) = camera.physical_target_size else {
261 continue;
262 };
263
264 let layer_count = settings.layer_count as usize;
265 let size = (size.x * size.y) as usize;
266 max_layer_ids_size = max_layer_ids_size.max(size);
267 max_layers_size = max_layers_size.max(size * layer_count);
268 }
269
270 if buffers.layers.capacity() < max_layers_size {
272 let start = Instant::now();
273 buffers.layers.reserve(max_layers_size, &render_device);
274 let remaining = max_layers_size - buffers.layers.capacity();
275 for _ in 0..remaining {
276 buffers.layers.push(UVec2::ZERO);
277 }
278 buffers.layers.write_buffer(&render_device, &render_queue);
279 trace!(
280 "OIT layers buffer updated in {:.01}ms with total size {} MiB",
281 start.elapsed().as_millis(),
282 buffers.layers.capacity() * size_of::<UVec2>() / 1024 / 1024,
283 );
284 }
285
286 if buffers.layer_ids.capacity() < max_layer_ids_size {
288 let start = Instant::now();
289 buffers
290 .layer_ids
291 .reserve(max_layer_ids_size, &render_device);
292 let remaining = max_layer_ids_size - buffers.layer_ids.capacity();
293 for _ in 0..remaining {
294 buffers.layer_ids.push(0);
295 }
296 buffers
297 .layer_ids
298 .write_buffer(&render_device, &render_queue);
299 trace!(
300 "OIT layer ids buffer updated in {:.01}ms with total size {} MiB",
301 start.elapsed().as_millis(),
302 buffers.layer_ids.capacity() * size_of::<UVec2>() / 1024 / 1024,
303 );
304 }
305
306 if let Some(mut writer) = buffers.settings.get_writer(
307 camera_oit_uniforms.iter().len(),
308 &render_device,
309 &render_queue,
310 ) {
311 for (entity, settings) in &camera_oit_uniforms {
312 let offset = writer.write(settings);
313 commands
314 .entity(entity)
315 .insert(OrderIndependentTransparencySettingsOffset { offset });
316 }
317 }
318}