bevy_core_pipeline/tonemapping/
node.rs1use crate::tonemapping::{TonemappingLuts, TonemappingPipeline, ViewTonemappingPipeline};
2
3use bevy_ecs::prelude::*;
4use bevy_render::{
5 camera::ExtractedCamera,
6 diagnostic::RecordDiagnostics,
7 render_asset::RenderAssets,
8 render_resource::{
9 BindGroup, BindGroupEntries, BufferId, LoadOp, Operations, PipelineCache,
10 RenderPassColorAttachment, RenderPassDescriptor, StoreOp, TextureViewId,
11 },
12 renderer::{RenderContext, ViewQuery},
13 texture::{FallbackImage, GpuImage},
14 view::{ViewTarget, ViewUniformOffset, ViewUniforms},
15};
16
17use super::{get_lut_bindings, Tonemapping};
18
19#[derive(Default)]
21pub struct TonemappingBindGroupCache {
22 cached: Option<(BufferId, TextureViewId, TextureViewId, BindGroup)>,
23 last_tonemapping: Option<Tonemapping>,
24}
25
26pub fn tonemapping(
27 view: ViewQuery<(
28 &ExtractedCamera,
29 &ViewUniformOffset,
30 &ViewTarget,
31 &ViewTonemappingPipeline,
32 &Tonemapping,
33 )>,
34 pipeline_cache: Res<PipelineCache>,
35 tonemapping_pipeline: Res<TonemappingPipeline>,
36 gpu_images: Res<RenderAssets<GpuImage>>,
37 fallback_image: Res<FallbackImage>,
38 view_uniforms: Res<ViewUniforms>,
39 tonemapping_luts: Res<TonemappingLuts>,
40 mut cache: Local<TonemappingBindGroupCache>,
41 mut ctx: RenderContext,
42) {
43 let (camera, view_uniform_offset, target, view_tonemapping_pipeline, tonemapping) =
44 view.into_inner();
45
46 if *tonemapping == Tonemapping::None {
47 return;
48 }
49
50 if !camera.hdr {
51 return;
52 }
53
54 let Some(pipeline) = pipeline_cache.get_render_pipeline(view_tonemapping_pipeline.0) else {
55 return;
56 };
57
58 let view_uniforms_buffer = &view_uniforms.uniforms;
59 let view_uniforms_id = view_uniforms_buffer.buffer().unwrap().id();
60
61 let post_process = target.post_process_write();
62 let source = post_process.source;
63 let destination = post_process.destination;
64
65 let tonemapping_changed = cache.last_tonemapping != Some(*tonemapping);
66 if tonemapping_changed {
67 cache.last_tonemapping = Some(*tonemapping);
68 }
69
70 let bind_group = match &mut cache.cached {
71 Some((buffer_id, texture_id, lut_id, bind_group))
72 if view_uniforms_id == *buffer_id
73 && source.id() == *texture_id
74 && *lut_id != fallback_image.d3.texture_view.id()
75 && !tonemapping_changed =>
76 {
77 bind_group
78 }
79 cached => {
80 let lut_bindings =
81 get_lut_bindings(&gpu_images, &tonemapping_luts, tonemapping, &fallback_image);
82
83 let bind_group = ctx.render_device().create_bind_group(
84 None,
85 &pipeline_cache.get_bind_group_layout(&tonemapping_pipeline.texture_bind_group),
86 &BindGroupEntries::sequential((
87 view_uniforms_buffer,
88 source,
89 &tonemapping_pipeline.sampler,
90 lut_bindings.0,
91 lut_bindings.1,
92 )),
93 );
94
95 let (_, _, _, bind_group) = cached.insert((
96 view_uniforms_id,
97 source.id(),
98 lut_bindings.0.id(),
99 bind_group,
100 ));
101 bind_group
102 }
103 };
104
105 let pass_descriptor = RenderPassDescriptor {
106 label: Some("tonemapping"),
107 color_attachments: &[Some(RenderPassColorAttachment {
108 view: destination,
109 depth_slice: None,
110 resolve_target: None,
111 ops: Operations {
112 load: LoadOp::Clear(Default::default()), store: StoreOp::Store,
114 },
115 })],
116 depth_stencil_attachment: None,
117 timestamp_writes: None,
118 occlusion_query_set: None,
119 multiview_mask: None,
120 };
121
122 let diagnostics = ctx.diagnostic_recorder();
123 let diagnostics = diagnostics.as_deref();
124 let time_span = diagnostics.time_span(ctx.command_encoder(), "tonemapping");
125
126 {
127 let mut render_pass = ctx.command_encoder().begin_render_pass(&pass_descriptor);
128
129 render_pass.set_pipeline(pipeline);
130 render_pass.set_bind_group(0, bind_group, &[view_uniform_offset.offset]);
131 render_pass.draw(0..3, 0..1);
132 }
133
134 time_span.end(ctx.command_encoder());
135}