bevy_core_pipeline/auto_exposure/
mod.rs1use bevy_app::prelude::*;
2use bevy_asset::{load_internal_asset, AssetApp, Assets, Handle};
3use bevy_ecs::prelude::*;
4use bevy_render::{
5 extract_component::ExtractComponentPlugin,
6 render_asset::RenderAssetPlugin,
7 render_graph::RenderGraphApp,
8 render_resource::{
9 Buffer, BufferDescriptor, BufferUsages, PipelineCache, Shader, SpecializedComputePipelines,
10 },
11 renderer::RenderDevice,
12 ExtractSchedule, Render, RenderApp, RenderSet,
13};
14
15mod buffers;
16mod compensation_curve;
17mod node;
18mod pipeline;
19mod settings;
20
21use buffers::{extract_buffers, prepare_buffers, AutoExposureBuffers};
22pub use compensation_curve::{AutoExposureCompensationCurve, AutoExposureCompensationCurveError};
23use node::AutoExposureNode;
24use pipeline::{
25 AutoExposurePass, AutoExposurePipeline, ViewAutoExposurePipeline, METERING_SHADER_HANDLE,
26};
27pub use settings::AutoExposure;
28
29use crate::{
30 auto_exposure::compensation_curve::GpuAutoExposureCompensationCurve,
31 core_3d::graph::{Core3d, Node3d},
32};
33
34pub struct AutoExposurePlugin;
38
39#[derive(Resource)]
40struct AutoExposureResources {
41 histogram: Buffer,
42}
43
44impl Plugin for AutoExposurePlugin {
45 fn build(&self, app: &mut App) {
46 load_internal_asset!(
47 app,
48 METERING_SHADER_HANDLE,
49 "auto_exposure.wgsl",
50 Shader::from_wgsl
51 );
52
53 app.add_plugins(RenderAssetPlugin::<GpuAutoExposureCompensationCurve>::default())
54 .register_type::<AutoExposureCompensationCurve>()
55 .init_asset::<AutoExposureCompensationCurve>()
56 .register_asset_reflect::<AutoExposureCompensationCurve>();
57 app.world_mut()
58 .resource_mut::<Assets<AutoExposureCompensationCurve>>()
59 .insert(&Handle::default(), AutoExposureCompensationCurve::default());
60
61 app.register_type::<AutoExposure>();
62 app.add_plugins(ExtractComponentPlugin::<AutoExposure>::default());
63
64 let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
65 return;
66 };
67
68 render_app
69 .init_resource::<SpecializedComputePipelines<AutoExposurePipeline>>()
70 .init_resource::<AutoExposureBuffers>()
71 .add_systems(ExtractSchedule, extract_buffers)
72 .add_systems(
73 Render,
74 (
75 prepare_buffers.in_set(RenderSet::Prepare),
76 queue_view_auto_exposure_pipelines.in_set(RenderSet::Queue),
77 ),
78 )
79 .add_render_graph_node::<AutoExposureNode>(Core3d, node::AutoExposure)
80 .add_render_graph_edges(
81 Core3d,
82 (Node3d::EndMainPass, node::AutoExposure, Node3d::Tonemapping),
83 );
84 }
85
86 fn finish(&self, app: &mut App) {
87 let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
88 return;
89 };
90
91 render_app.init_resource::<AutoExposurePipeline>();
92 render_app.init_resource::<AutoExposureResources>();
93 }
94}
95
96impl FromWorld for AutoExposureResources {
97 fn from_world(world: &mut World) -> Self {
98 Self {
99 histogram: world
100 .resource::<RenderDevice>()
101 .create_buffer(&BufferDescriptor {
102 label: Some("histogram buffer"),
103 size: pipeline::HISTOGRAM_BIN_COUNT * 4,
104 usage: BufferUsages::STORAGE,
105 mapped_at_creation: false,
106 }),
107 }
108 }
109}
110
111fn queue_view_auto_exposure_pipelines(
112 mut commands: Commands,
113 pipeline_cache: Res<PipelineCache>,
114 mut compute_pipelines: ResMut<SpecializedComputePipelines<AutoExposurePipeline>>,
115 pipeline: Res<AutoExposurePipeline>,
116 view_targets: Query<(Entity, &AutoExposure)>,
117) {
118 for (entity, auto_exposure) in view_targets.iter() {
119 let histogram_pipeline =
120 compute_pipelines.specialize(&pipeline_cache, &pipeline, AutoExposurePass::Histogram);
121 let average_pipeline =
122 compute_pipelines.specialize(&pipeline_cache, &pipeline, AutoExposurePass::Average);
123
124 commands.entity(entity).insert(ViewAutoExposurePipeline {
125 histogram_pipeline,
126 mean_luminance_pipeline: average_pipeline,
127 compensation_curve: auto_exposure.compensation_curve.clone(),
128 metering_mask: auto_exposure.metering_mask.clone(),
129 });
130 }
131}