1use bevy_app::{App, Plugin};
18use bevy_asset::{embedded_asset, load_embedded_asset, AssetServer, Handle};
19use bevy_camera::{Camera3d, PhysicalCameraParameters, Projection};
20use bevy_derive::{Deref, DerefMut};
21use bevy_ecs::{
22 component::Component,
23 entity::Entity,
24 query::With,
25 reflect::ReflectComponent,
26 resource::Resource,
27 schedule::IntoScheduleConfigs as _,
28 system::{Commands, Query, Res, ResMut},
29};
30use bevy_math::ops;
31use bevy_reflect::{prelude::ReflectDefault, Reflect};
32use bevy_render::{
33 camera::ExtractedCamera,
34 diagnostic::RecordDiagnostics,
35 extract_component::{ComponentUniforms, DynamicUniformIndex, UniformComponentPlugin},
36 render_resource::{
37 binding_types::{
38 sampler, texture_2d, texture_depth_2d, texture_depth_2d_multisampled, uniform_buffer,
39 },
40 BindGroup, BindGroupEntries, BindGroupLayoutDescriptor, BindGroupLayoutEntries,
41 CachedRenderPipelineId, ColorTargetState, ColorWrites, FilterMode, FragmentState, LoadOp,
42 Operations, PipelineCache, RenderPassColorAttachment, RenderPassDescriptor,
43 RenderPipelineDescriptor, Sampler, SamplerBindingType, SamplerDescriptor, ShaderStages,
44 ShaderType, SpecializedRenderPipeline, SpecializedRenderPipelines, StoreOp,
45 TextureDescriptor, TextureDimension, TextureFormat, TextureSampleType, TextureUsages,
46 },
47 renderer::{RenderContext, RenderDevice, ViewQuery},
48 sync_component::{SyncComponent, SyncComponentPlugin},
49 sync_world::RenderEntity,
50 texture::{CachedTexture, TextureCache},
51 view::{
52 prepare_view_targets, ExtractedView, Msaa, ViewDepthStencilTexture, ViewTarget,
53 ViewUniform, ViewUniformOffset, ViewUniforms,
54 },
55 Extract, ExtractSchedule, GpuResourceAppExt, Render, RenderApp, RenderStartup, RenderSystems,
56};
57use bevy_shader::Shader;
58use bevy_utils::{default, once};
59use smallvec::SmallVec;
60use tracing::{info, warn};
61
62use crate::bloom::bloom;
63use bevy_core_pipeline::{
64 core_3d::DEPTH_PREPASS_TEXTURE_SUPPORTED, schedule::Core3d, tonemapping::tonemapping,
65 FullscreenShader,
66};
67
68#[derive(Default)]
70pub struct DepthOfFieldPlugin;
71
72#[derive(Component, Clone, Copy, Reflect)]
77#[reflect(Component, Clone, Default)]
78pub struct DepthOfField {
79 pub mode: DepthOfFieldMode,
81
82 pub focal_distance: f32,
84
85 pub sensor_height: f32,
94
95 pub aperture_f_stops: f32,
98
99 pub max_circle_of_confusion_diameter: f32,
106
107 pub max_depth: f32,
117}
118
119#[derive(Clone, Copy, Default, PartialEq, Debug, Reflect)]
121#[reflect(Default, Clone, PartialEq)]
122pub enum DepthOfFieldMode {
123 Bokeh,
130
131 #[default]
138 Gaussian,
139}
140
141#[derive(Clone, Copy, Component, ShaderType)]
143pub struct DepthOfFieldUniform {
144 focal_distance: f32,
146
147 focal_length: f32,
150
151 coc_scale_factor: f32,
156
157 max_circle_of_confusion_diameter: f32,
160
161 max_depth: f32,
164
165 pad_a: u32,
167 pad_b: u32,
169 pad_c: u32,
171}
172
173#[derive(Clone, Copy, PartialEq, Eq, Hash)]
175pub struct DepthOfFieldPipelineKey {
176 pass: DofPass,
178 target_format: TextureFormat,
179 multisample: bool,
181}
182
183#[derive(Clone, Copy, PartialEq, Eq, Hash)]
185enum DofPass {
186 GaussianHorizontal,
188 GaussianVertical,
190 BokehPass0,
192 BokehPass1,
194}
195
196impl Plugin for DepthOfFieldPlugin {
197 fn build(&self, app: &mut App) {
198 embedded_asset!(app, "dof.wesl");
199
200 app.add_plugins(UniformComponentPlugin::<DepthOfFieldUniform>::default());
201
202 app.add_plugins(SyncComponentPlugin::<DepthOfField>::default());
203
204 let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
205 return;
206 };
207
208 render_app
209 .init_gpu_resource::<SpecializedRenderPipelines<DepthOfFieldPipeline>>()
210 .init_resource::<DepthOfFieldGlobalBindGroup>()
211 .add_systems(RenderStartup, init_dof_global_bind_group_layout)
212 .add_systems(ExtractSchedule, extract_depth_of_field_settings)
213 .add_systems(
214 Render,
215 (
216 configure_depth_of_field_view_targets
217 .ambiguous_with(RenderSystems::PrepareViews),
218 prepare_auxiliary_depth_of_field_textures,
219 )
220 .after(prepare_view_targets)
221 .in_set(RenderSystems::PrepareViews),
222 )
223 .add_systems(
224 Render,
225 (
226 prepare_depth_of_field_view_bind_group_layouts,
227 prepare_depth_of_field_pipelines,
228 )
229 .chain()
230 .in_set(RenderSystems::Prepare),
231 )
232 .add_systems(
233 Render,
234 prepare_depth_of_field_global_bind_group.in_set(RenderSystems::PrepareBindGroups),
235 )
236 .add_systems(Core3d, depth_of_field.after(bloom).before(tonemapping));
237 }
238}
239
240#[derive(Resource, Clone)]
243pub struct DepthOfFieldGlobalBindGroupLayout {
244 layout: BindGroupLayoutDescriptor,
246 color_texture_sampler: Sampler,
248}
249
250#[derive(Resource, Default, Deref, DerefMut)]
253pub struct DepthOfFieldGlobalBindGroup(Option<BindGroup>);
254
255#[derive(Component)]
256pub enum DepthOfFieldPipelines {
257 Gaussian {
258 horizontal: CachedRenderPipelineId,
259 vertical: CachedRenderPipelineId,
260 },
261 Bokeh {
262 pass_0: CachedRenderPipelineId,
263 pass_1: CachedRenderPipelineId,
264 },
265}
266
267struct DepthOfFieldPipelineRenderInfo {
268 pass_label: &'static str,
269 view_bind_group_label: &'static str,
270 pipeline: CachedRenderPipelineId,
271 is_dual_input: bool,
272 is_dual_output: bool,
273}
274
275#[derive(Component, Deref, DerefMut)]
281pub struct AuxiliaryDepthOfFieldTexture(CachedTexture);
282
283#[derive(Component, Clone)]
285pub struct ViewDepthOfFieldBindGroupLayouts {
286 single_input: BindGroupLayoutDescriptor,
288
289 dual_input: Option<BindGroupLayoutDescriptor>,
293}
294
295pub struct DepthOfFieldPipeline {
298 view_bind_group_layouts: ViewDepthOfFieldBindGroupLayouts,
300 global_bind_group_layout: BindGroupLayoutDescriptor,
303 fullscreen_shader: FullscreenShader,
305 fragment_shader: Handle<Shader>,
307}
308
309impl Default for DepthOfField {
310 fn default() -> Self {
311 let physical_camera_default = PhysicalCameraParameters::default();
312 Self {
313 focal_distance: 10.0,
314 aperture_f_stops: physical_camera_default.aperture_f_stops,
315 sensor_height: physical_camera_default.sensor_height,
316 max_circle_of_confusion_diameter: 64.0,
317 max_depth: f32::INFINITY,
318 mode: DepthOfFieldMode::default(),
319 }
320 }
321}
322
323impl DepthOfField {
324 pub fn from_physical_camera(camera: &PhysicalCameraParameters) -> DepthOfField {
335 DepthOfField {
336 sensor_height: camera.sensor_height,
337 aperture_f_stops: camera.aperture_f_stops,
338 ..default()
339 }
340 }
341}
342
343pub fn init_dof_global_bind_group_layout(mut commands: Commands, render_device: Res<RenderDevice>) {
344 let layout = BindGroupLayoutDescriptor::new(
347 "depth of field global bind group layout",
348 &BindGroupLayoutEntries::sequential(
349 ShaderStages::FRAGMENT,
350 (
351 uniform_buffer::<DepthOfFieldUniform>(true),
353 sampler(SamplerBindingType::Filtering),
355 ),
356 ),
357 );
358
359 let sampler = render_device.create_sampler(&SamplerDescriptor {
361 label: Some("depth of field sampler"),
362 mag_filter: FilterMode::Linear,
363 min_filter: FilterMode::Linear,
364 ..default()
365 });
366
367 commands.insert_resource(DepthOfFieldGlobalBindGroupLayout {
368 color_texture_sampler: sampler,
369 layout,
370 });
371}
372
373pub fn prepare_depth_of_field_view_bind_group_layouts(
376 mut commands: Commands,
377 view_targets: Query<(Entity, &DepthOfField, &Msaa)>,
378) {
379 for (view, depth_of_field, msaa) in view_targets.iter() {
380 let single_input = BindGroupLayoutDescriptor::new(
382 "depth of field bind group layout (single input)",
383 &BindGroupLayoutEntries::sequential(
384 ShaderStages::FRAGMENT,
385 (
386 uniform_buffer::<ViewUniform>(true),
387 if *msaa != Msaa::Off {
388 texture_depth_2d_multisampled()
389 } else {
390 texture_depth_2d()
391 },
392 texture_2d(TextureSampleType::Float { filterable: true }),
393 ),
394 ),
395 );
396
397 let dual_input = match depth_of_field.mode {
400 DepthOfFieldMode::Gaussian => None,
401 DepthOfFieldMode::Bokeh => Some(BindGroupLayoutDescriptor::new(
402 "depth of field bind group layout (dual input)",
403 &BindGroupLayoutEntries::sequential(
404 ShaderStages::FRAGMENT,
405 (
406 uniform_buffer::<ViewUniform>(true),
407 if *msaa != Msaa::Off {
408 texture_depth_2d_multisampled()
409 } else {
410 texture_depth_2d()
411 },
412 texture_2d(TextureSampleType::Float { filterable: true }),
413 texture_2d(TextureSampleType::Float { filterable: true }),
414 ),
415 ),
416 )),
417 };
418
419 commands
420 .entity(view)
421 .insert(ViewDepthOfFieldBindGroupLayouts {
422 single_input,
423 dual_input,
424 });
425 }
426}
427
428pub fn configure_depth_of_field_view_targets(
436 mut view_targets: Query<&mut Camera3d, With<DepthOfField>>,
437) {
438 for mut camera_3d in view_targets.iter_mut() {
439 let mut depth_texture_usages = TextureUsages::from(camera_3d.depth_texture_usages);
440 depth_texture_usages |= TextureUsages::TEXTURE_BINDING;
441 camera_3d.depth_texture_usages = depth_texture_usages.into();
442 }
443}
444
445pub fn prepare_depth_of_field_global_bind_group(
448 global_bind_group_layout: Res<DepthOfFieldGlobalBindGroupLayout>,
449 mut dof_bind_group: ResMut<DepthOfFieldGlobalBindGroup>,
450 depth_of_field_uniforms: Res<ComponentUniforms<DepthOfFieldUniform>>,
451 render_device: Res<RenderDevice>,
452 pipeline_cache: Res<PipelineCache>,
453) {
454 let Some(depth_of_field_uniforms) = depth_of_field_uniforms.binding() else {
455 return;
456 };
457
458 **dof_bind_group = Some(render_device.create_bind_group(
459 Some("depth of field global bind group"),
460 &pipeline_cache.get_bind_group_layout(&global_bind_group_layout.layout),
461 &BindGroupEntries::sequential((
462 depth_of_field_uniforms, &global_bind_group_layout.color_texture_sampler, )),
465 ));
466}
467
468pub fn prepare_auxiliary_depth_of_field_textures(
471 mut commands: Commands,
472 render_device: Res<RenderDevice>,
473 mut texture_cache: ResMut<TextureCache>,
474 mut view_targets: Query<(Entity, &ViewTarget, &DepthOfField)>,
475) {
476 for (entity, view_target, depth_of_field) in view_targets.iter_mut() {
477 if depth_of_field.mode != DepthOfFieldMode::Bokeh {
479 continue;
480 }
481
482 let texture_descriptor = TextureDescriptor {
484 label: Some("depth of field auxiliary texture"),
485 size: view_target.main_texture().size(),
486 mip_level_count: 1,
487 sample_count: view_target.main_texture().sample_count(),
488 dimension: TextureDimension::D2,
489 format: view_target.main_texture_format(),
490 usage: TextureUsages::RENDER_ATTACHMENT | TextureUsages::TEXTURE_BINDING,
491 view_formats: &[],
492 };
493
494 let texture = texture_cache.get(&render_device, texture_descriptor);
495
496 commands
497 .entity(entity)
498 .insert(AuxiliaryDepthOfFieldTexture(texture));
499 }
500}
501
502pub fn prepare_depth_of_field_pipelines(
504 mut commands: Commands,
505 pipeline_cache: Res<PipelineCache>,
506 mut pipelines: ResMut<SpecializedRenderPipelines<DepthOfFieldPipeline>>,
507 global_bind_group_layout: Res<DepthOfFieldGlobalBindGroupLayout>,
508 view_targets: Query<
509 (
510 Entity,
511 &ExtractedView,
512 &DepthOfField,
513 &ViewDepthOfFieldBindGroupLayouts,
514 &Msaa,
515 ),
516 With<ExtractedCamera>,
517 >,
518 fullscreen_shader: Res<FullscreenShader>,
519 asset_server: Res<AssetServer>,
520) {
521 for (entity, view, depth_of_field, view_bind_group_layouts, msaa) in view_targets.iter() {
522 let dof_pipeline = DepthOfFieldPipeline {
523 view_bind_group_layouts: view_bind_group_layouts.clone(),
524 global_bind_group_layout: global_bind_group_layout.layout.clone(),
525 fullscreen_shader: fullscreen_shader.clone(),
526 fragment_shader: load_embedded_asset!(asset_server.as_ref(), "dof.wesl"),
527 };
528
529 let (target_format, multisample) = (view.target_format, *msaa != Msaa::Off);
531
532 match depth_of_field.mode {
534 DepthOfFieldMode::Gaussian => {
535 commands
536 .entity(entity)
537 .insert(DepthOfFieldPipelines::Gaussian {
538 horizontal: pipelines.specialize(
539 &pipeline_cache,
540 &dof_pipeline,
541 DepthOfFieldPipelineKey {
542 target_format,
543 multisample,
544 pass: DofPass::GaussianHorizontal,
545 },
546 ),
547 vertical: pipelines.specialize(
548 &pipeline_cache,
549 &dof_pipeline,
550 DepthOfFieldPipelineKey {
551 target_format,
552 multisample,
553 pass: DofPass::GaussianVertical,
554 },
555 ),
556 });
557 }
558
559 DepthOfFieldMode::Bokeh => {
560 commands
561 .entity(entity)
562 .insert(DepthOfFieldPipelines::Bokeh {
563 pass_0: pipelines.specialize(
564 &pipeline_cache,
565 &dof_pipeline,
566 DepthOfFieldPipelineKey {
567 target_format,
568 multisample,
569 pass: DofPass::BokehPass0,
570 },
571 ),
572 pass_1: pipelines.specialize(
573 &pipeline_cache,
574 &dof_pipeline,
575 DepthOfFieldPipelineKey {
576 target_format,
577 multisample,
578 pass: DofPass::BokehPass1,
579 },
580 ),
581 });
582 }
583 }
584 }
585}
586
587impl SpecializedRenderPipeline for DepthOfFieldPipeline {
588 type Key = DepthOfFieldPipelineKey;
589
590 fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor {
591 let (mut layout, mut shader_defs) = (vec![], vec![]);
593 let mut targets = vec![Some(ColorTargetState {
594 format: key.target_format,
595 blend: None,
596 write_mask: ColorWrites::ALL,
597 })];
598
599 match key.pass {
601 DofPass::GaussianHorizontal | DofPass::GaussianVertical => {
602 layout.push(self.view_bind_group_layouts.single_input.clone());
604 }
605 DofPass::BokehPass0 => {
606 layout.push(self.view_bind_group_layouts.single_input.clone());
608 targets.push(targets[0].clone());
609 }
610 DofPass::BokehPass1 => {
611 let dual_input_bind_group_layout = self
614 .view_bind_group_layouts
615 .dual_input
616 .as_ref()
617 .expect("Dual-input depth of field bind group should have been created by now")
618 .clone();
619 layout.push(dual_input_bind_group_layout);
620 shader_defs.push("DUAL_INPUT".into());
621 }
622 }
623
624 layout.push(self.global_bind_group_layout.clone());
626
627 if key.multisample {
628 shader_defs.push("MULTISAMPLED".into());
629 }
630
631 RenderPipelineDescriptor {
632 label: Some("depth of field pipeline".into()),
633 layout,
634 vertex: self.fullscreen_shader.to_vertex_state(),
635 fragment: Some(FragmentState {
636 shader: self.fragment_shader.clone(),
637 shader_defs,
638 entry_point: Some(match key.pass {
639 DofPass::GaussianHorizontal => "gaussian_horizontal".into(),
640 DofPass::GaussianVertical => "gaussian_vertical".into(),
641 DofPass::BokehPass0 => "bokeh_pass_a".into(),
644 DofPass::BokehPass1 => "bokeh_pass_b".into(),
645 }),
646 targets,
647 constants: vec![],
648 }),
649 ..default()
650 }
651 }
652}
653
654impl SyncComponent<RenderApp> for DepthOfField {
655 type Target = (
656 DepthOfField,
657 DepthOfFieldUniform,
658 DepthOfFieldPipelines,
659 AuxiliaryDepthOfFieldTexture,
660 ViewDepthOfFieldBindGroupLayouts,
661 );
662}
663
664fn extract_depth_of_field_settings(
666 mut commands: Commands,
667 mut query: Extract<Query<(RenderEntity, &DepthOfField, &Projection)>>,
668) {
669 if !DEPTH_PREPASS_TEXTURE_SUPPORTED {
670 once!(info!(
671 "Disabling depth of field on this platform because depth textures aren't supported correctly"
672 ));
673 return;
674 }
675
676 for (entity, depth_of_field, projection) in query.iter_mut() {
677 let mut entity_commands = commands
678 .get_entity(entity)
679 .expect("Depth of field entity wasn't synced.");
680
681 let Projection::Perspective(ref perspective_projection) = *projection else {
683 entity_commands.remove::<<DepthOfField as SyncComponent<RenderApp>>::Target>();
684
685 continue;
686 };
687
688 let focal_length =
689 calculate_focal_length(depth_of_field.sensor_height, perspective_projection.fov);
690
691 entity_commands.insert((
693 *depth_of_field,
694 DepthOfFieldUniform {
695 focal_distance: depth_of_field.focal_distance,
696 focal_length,
697 coc_scale_factor: focal_length * focal_length
698 / (depth_of_field.sensor_height * depth_of_field.aperture_f_stops),
699 max_circle_of_confusion_diameter: depth_of_field.max_circle_of_confusion_diameter,
700 max_depth: depth_of_field.max_depth,
701 pad_a: 0,
702 pad_b: 0,
703 pad_c: 0,
704 },
705 ));
706 }
707}
708
709pub fn calculate_focal_length(sensor_height: f32, fov: f32) -> f32 {
713 0.5 * sensor_height / ops::tan(0.5 * fov)
714}
715
716impl DepthOfFieldPipelines {
717 fn pipeline_render_info(&self) -> [DepthOfFieldPipelineRenderInfo; 2] {
720 match *self {
721 DepthOfFieldPipelines::Gaussian {
722 horizontal: horizontal_pipeline,
723 vertical: vertical_pipeline,
724 } => [
725 DepthOfFieldPipelineRenderInfo {
726 pass_label: "depth of field pass (horizontal Gaussian)",
727 view_bind_group_label: "depth of field view bind group (horizontal Gaussian)",
728 pipeline: horizontal_pipeline,
729 is_dual_input: false,
730 is_dual_output: false,
731 },
732 DepthOfFieldPipelineRenderInfo {
733 pass_label: "depth of field pass (vertical Gaussian)",
734 view_bind_group_label: "depth of field view bind group (vertical Gaussian)",
735 pipeline: vertical_pipeline,
736 is_dual_input: false,
737 is_dual_output: false,
738 },
739 ],
740
741 DepthOfFieldPipelines::Bokeh {
742 pass_0: pass_0_pipeline,
743 pass_1: pass_1_pipeline,
744 } => [
745 DepthOfFieldPipelineRenderInfo {
746 pass_label: "depth of field pass (bokeh pass 0)",
747 view_bind_group_label: "depth of field view bind group (bokeh pass 0)",
748 pipeline: pass_0_pipeline,
749 is_dual_input: false,
750 is_dual_output: true,
751 },
752 DepthOfFieldPipelineRenderInfo {
753 pass_label: "depth of field pass (bokeh pass 1)",
754 view_bind_group_label: "depth of field view bind group (bokeh pass 1)",
755 pipeline: pass_1_pipeline,
756 is_dual_input: true,
757 is_dual_output: false,
758 },
759 ],
760 }
761 }
762}
763
764pub(crate) fn depth_of_field(
765 view: ViewQuery<(
766 &ViewUniformOffset,
767 &ViewTarget,
768 &ViewDepthStencilTexture,
769 &DepthOfFieldPipelines,
770 &ViewDepthOfFieldBindGroupLayouts,
771 &DynamicUniformIndex<DepthOfFieldUniform>,
772 Option<&AuxiliaryDepthOfFieldTexture>,
773 )>,
774 pipeline_cache: Res<PipelineCache>,
775 view_uniforms: Res<ViewUniforms>,
776 global_bind_group: Res<DepthOfFieldGlobalBindGroup>,
777 mut ctx: RenderContext,
778) {
779 let (
780 view_uniform_offset,
781 view_target,
782 view_depth_texture,
783 view_pipelines,
784 view_bind_group_layouts,
785 depth_of_field_uniform_index,
786 auxiliary_dof_texture,
787 ) = view.into_inner();
788 let Some(depth_view) = view_depth_texture
789 .attachment
790 .depth_stencil_views()
791 .depth_only_view()
792 else {
793 return;
794 };
795 let diagnostics = ctx.diagnostic_recorder();
796 let diagnostics = diagnostics.as_deref();
797 let time_span = diagnostics.time_span(ctx.command_encoder(), "depth_of_field");
798
799 for pipeline_render_info in view_pipelines.pipeline_render_info().iter() {
802 let (Some(render_pipeline), Some(view_uniforms_binding), Some(global_bind_group)) = (
803 pipeline_cache.get_render_pipeline(pipeline_render_info.pipeline),
804 view_uniforms.uniforms.binding(),
805 &**global_bind_group,
806 ) else {
807 time_span.end(ctx.command_encoder());
808 return;
809 };
810
811 let postprocess = view_target.post_process_write();
816
817 let view_bind_group = if pipeline_render_info.is_dual_input {
818 let (Some(auxiliary_dof_texture), Some(dual_input_bind_group_layout)) = (
819 auxiliary_dof_texture,
820 view_bind_group_layouts.dual_input.as_ref(),
821 ) else {
822 once!(warn!(
823 "Should have created the auxiliary depth of field texture by now"
824 ));
825 continue;
826 };
827 ctx.render_device().create_bind_group(
828 Some(pipeline_render_info.view_bind_group_label),
829 &pipeline_cache.get_bind_group_layout(dual_input_bind_group_layout),
830 &BindGroupEntries::sequential((
831 view_uniforms_binding,
832 depth_view,
833 postprocess.source,
834 &auxiliary_dof_texture.default_view,
835 )),
836 )
837 } else {
838 ctx.render_device().create_bind_group(
839 Some(pipeline_render_info.view_bind_group_label),
840 &pipeline_cache.get_bind_group_layout(&view_bind_group_layouts.single_input),
841 &BindGroupEntries::sequential((
842 view_uniforms_binding,
843 depth_view,
844 postprocess.source,
845 )),
846 )
847 };
848
849 let mut color_attachments: SmallVec<[_; 2]> = SmallVec::new();
851 color_attachments.push(Some(RenderPassColorAttachment {
852 view: postprocess.destination,
853 depth_slice: None,
854 resolve_target: None,
855 ops: Operations {
856 load: LoadOp::Clear(default()),
857 store: StoreOp::Store,
858 },
859 }));
860
861 if pipeline_render_info.is_dual_output {
866 let Some(auxiliary_dof_texture) = auxiliary_dof_texture else {
867 once!(warn!(
868 "Should have created the auxiliary depth of field texture by now"
869 ));
870 continue;
871 };
872 color_attachments.push(Some(RenderPassColorAttachment {
873 view: &auxiliary_dof_texture.default_view,
874 depth_slice: None,
875 resolve_target: None,
876 ops: Operations {
877 load: LoadOp::Clear(default()),
878 store: StoreOp::Store,
879 },
880 }));
881 }
882
883 let render_pass_descriptor = RenderPassDescriptor {
884 label: Some(pipeline_render_info.pass_label),
885 color_attachments: &color_attachments,
886 ..default()
887 };
888
889 let mut render_pass = ctx
890 .command_encoder()
891 .begin_render_pass(&render_pass_descriptor);
892
893 render_pass.set_pipeline(render_pipeline);
894 render_pass.set_bind_group(0, &view_bind_group, &[view_uniform_offset.offset]);
896 render_pass.set_bind_group(
898 1,
899 global_bind_group,
900 &[depth_of_field_uniform_index.index()],
901 );
902 render_pass.draw(0..3, 0..1);
904 }
905 time_span.end(ctx.command_encoder());
906}