bevy_core_pipeline/auto_exposure/
mod.rs

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