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