bevy_core_pipeline/oit/resolve/
mod.rs1use crate::{
2 fullscreen_vertex_shader::fullscreen_shader_vertex_state,
3 oit::OrderIndependentTransparencySettings,
4};
5use bevy_app::Plugin;
6use bevy_asset::{load_internal_asset, Handle};
7use bevy_derive::Deref;
8use bevy_ecs::{
9 entity::{EntityHashMap, EntityHashSet},
10 prelude::*,
11};
12use bevy_image::BevyDefault as _;
13use bevy_render::{
14 render_resource::{
15 binding_types::{storage_buffer_sized, texture_depth_2d, uniform_buffer},
16 BindGroup, BindGroupEntries, BindGroupLayout, BindGroupLayoutEntries, BlendComponent,
17 BlendState, CachedRenderPipelineId, ColorTargetState, ColorWrites, DownlevelFlags,
18 FragmentState, MultisampleState, PipelineCache, PrimitiveState, RenderPipelineDescriptor,
19 Shader, ShaderDefVal, ShaderStages, TextureFormat,
20 },
21 renderer::{RenderAdapter, RenderDevice},
22 view::{ExtractedView, ViewTarget, ViewUniform, ViewUniforms},
23 Render, RenderApp, RenderSet,
24};
25use bevy_utils::tracing::warn;
26
27use super::OitBuffers;
28
29pub const OIT_RESOLVE_SHADER_HANDLE: Handle<Shader> = Handle::weak_from_u128(7698420424769536);
31
32pub mod node;
34
35pub struct OitResolvePlugin;
37impl Plugin for OitResolvePlugin {
38 fn build(&self, app: &mut bevy_app::App) {
39 load_internal_asset!(
40 app,
41 OIT_RESOLVE_SHADER_HANDLE,
42 "oit_resolve.wgsl",
43 Shader::from_wgsl
44 );
45 }
46
47 fn finish(&self, app: &mut bevy_app::App) {
48 let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
49 return;
50 };
51
52 if !render_app
53 .world()
54 .resource::<RenderAdapter>()
55 .get_downlevel_capabilities()
56 .flags
57 .contains(DownlevelFlags::FRAGMENT_WRITABLE_STORAGE)
58 {
59 warn!("OrderIndependentTransparencyPlugin not loaded. GPU lacks support: DownlevelFlags::FRAGMENT_WRITABLE_STORAGE.");
60 return;
61 }
62
63 render_app
64 .add_systems(
65 Render,
66 (
67 queue_oit_resolve_pipeline.in_set(RenderSet::Queue),
68 prepare_oit_resolve_bind_group.in_set(RenderSet::PrepareBindGroups),
69 ),
70 )
71 .init_resource::<OitResolvePipeline>();
72 }
73}
74
75#[derive(Resource, Deref)]
77pub struct OitResolveBindGroup(pub BindGroup);
78
79#[derive(Resource)]
81pub struct OitResolvePipeline {
82 pub view_bind_group_layout: BindGroupLayout,
84 pub oit_depth_bind_group_layout: BindGroupLayout,
86}
87
88impl FromWorld for OitResolvePipeline {
89 fn from_world(world: &mut World) -> Self {
90 let render_device = world.resource::<RenderDevice>();
91
92 let view_bind_group_layout = render_device.create_bind_group_layout(
93 "oit_resolve_bind_group_layout",
94 &BindGroupLayoutEntries::sequential(
95 ShaderStages::FRAGMENT,
96 (
97 uniform_buffer::<ViewUniform>(true),
98 storage_buffer_sized(false, None),
100 storage_buffer_sized(false, None),
102 ),
103 ),
104 );
105
106 let oit_depth_bind_group_layout = render_device.create_bind_group_layout(
107 "oit_depth_bind_group_layout",
108 &BindGroupLayoutEntries::single(ShaderStages::FRAGMENT, texture_depth_2d()),
109 );
110 OitResolvePipeline {
111 view_bind_group_layout,
112 oit_depth_bind_group_layout,
113 }
114 }
115}
116
117#[derive(Component, Deref, Clone, Copy)]
118pub struct OitResolvePipelineId(pub CachedRenderPipelineId);
119
120#[derive(Debug, PartialEq, Eq, Clone, Copy)]
122pub struct OitResolvePipelineKey {
123 hdr: bool,
124 layer_count: i32,
125}
126
127#[allow(clippy::too_many_arguments)]
128pub fn queue_oit_resolve_pipeline(
129 mut commands: Commands,
130 pipeline_cache: Res<PipelineCache>,
131 resolve_pipeline: Res<OitResolvePipeline>,
132 views: Query<
133 (
134 Entity,
135 &ExtractedView,
136 &OrderIndependentTransparencySettings,
137 ),
138 With<OrderIndependentTransparencySettings>,
139 >,
140 mut cached_pipeline_id: Local<EntityHashMap<(OitResolvePipelineKey, CachedRenderPipelineId)>>,
143) {
144 let mut current_view_entities = EntityHashSet::default();
145 for (e, view, oit_settings) in &views {
146 current_view_entities.insert(e);
147 let key = OitResolvePipelineKey {
148 hdr: view.hdr,
149 layer_count: oit_settings.layer_count,
150 };
151
152 if let Some((cached_key, id)) = cached_pipeline_id.get(&e) {
153 if *cached_key == key {
154 commands.entity(e).insert(OitResolvePipelineId(*id));
155 continue;
156 }
157 }
158
159 let desc = specialize_oit_resolve_pipeline(key, &resolve_pipeline);
160
161 let pipeline_id = pipeline_cache.queue_render_pipeline(desc);
162 commands.entity(e).insert(OitResolvePipelineId(pipeline_id));
163 cached_pipeline_id.insert(e, (key, pipeline_id));
164 }
165
166 for e in cached_pipeline_id.keys().copied().collect::<Vec<_>>() {
168 if !current_view_entities.contains(&e) {
169 cached_pipeline_id.remove(&e);
170 }
171 }
172}
173
174fn specialize_oit_resolve_pipeline(
175 key: OitResolvePipelineKey,
176 resolve_pipeline: &OitResolvePipeline,
177) -> RenderPipelineDescriptor {
178 let format = if key.hdr {
179 ViewTarget::TEXTURE_FORMAT_HDR
180 } else {
181 TextureFormat::bevy_default()
182 };
183
184 RenderPipelineDescriptor {
185 label: Some("oit_resolve_pipeline".into()),
186 layout: vec![
187 resolve_pipeline.view_bind_group_layout.clone(),
188 resolve_pipeline.oit_depth_bind_group_layout.clone(),
189 ],
190 fragment: Some(FragmentState {
191 entry_point: "fragment".into(),
192 shader: OIT_RESOLVE_SHADER_HANDLE,
193 shader_defs: vec![ShaderDefVal::UInt(
194 "LAYER_COUNT".into(),
195 key.layer_count as u32,
196 )],
197 targets: vec![Some(ColorTargetState {
198 format,
199 blend: Some(BlendState {
200 color: BlendComponent::OVER,
201 alpha: BlendComponent::OVER,
202 }),
203 write_mask: ColorWrites::ALL,
204 })],
205 }),
206 vertex: fullscreen_shader_vertex_state(),
207 primitive: PrimitiveState::default(),
208 depth_stencil: None,
209 multisample: MultisampleState::default(),
210 push_constant_ranges: vec![],
211 zero_initialize_workgroup_memory: false,
212 }
213}
214
215pub fn prepare_oit_resolve_bind_group(
216 mut commands: Commands,
217 resolve_pipeline: Res<OitResolvePipeline>,
218 render_device: Res<RenderDevice>,
219 view_uniforms: Res<ViewUniforms>,
220 buffers: Res<OitBuffers>,
221) {
222 if let (Some(binding), Some(layers_binding), Some(layer_ids_binding)) = (
223 view_uniforms.uniforms.binding(),
224 buffers.layers.binding(),
225 buffers.layer_ids.binding(),
226 ) {
227 let bind_group = render_device.create_bind_group(
228 "oit_resolve_bind_group",
229 &resolve_pipeline.view_bind_group_layout,
230 &BindGroupEntries::sequential((binding.clone(), layers_binding, layer_ids_binding)),
231 );
232 commands.insert_resource(OitResolveBindGroup(bind_group));
233 }
234}