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