bevy_post_process/auto_exposure/
mod.rs1use bevy_app::prelude::*;
2use bevy_asset::{embedded_asset, AssetApp, Assets, Handle};
3use bevy_ecs::prelude::*;
4use bevy_render::{
5 extract_component::ExtractComponentPlugin,
6 render_asset::RenderAssetPlugin,
7 render_graph::RenderGraphExt,
8 render_resource::{
9 Buffer, BufferDescriptor, BufferUsages, PipelineCache, SpecializedComputePipelines,
10 },
11 renderer::RenderDevice,
12 ExtractSchedule, Render, RenderApp, RenderStartup, RenderSystems,
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::{AutoExposurePass, AutoExposurePipeline, ViewAutoExposurePipeline};
25pub use settings::AutoExposure;
26
27use crate::auto_exposure::{
28 compensation_curve::GpuAutoExposureCompensationCurve, pipeline::init_auto_exposure_pipeline,
29};
30use bevy_core_pipeline::core_3d::graph::{Core3d, Node3d};
31
32pub struct AutoExposurePlugin;
36
37#[derive(Resource)]
38struct AutoExposureResources {
39 histogram: Buffer,
40}
41
42impl Plugin for AutoExposurePlugin {
43 fn build(&self, app: &mut App) {
44 embedded_asset!(app, "auto_exposure.wgsl");
45
46 app.add_plugins(RenderAssetPlugin::<GpuAutoExposureCompensationCurve>::default())
47 .init_asset::<AutoExposureCompensationCurve>()
48 .register_asset_reflect::<AutoExposureCompensationCurve>();
49 app.world_mut()
50 .resource_mut::<Assets<AutoExposureCompensationCurve>>()
51 .insert(&Handle::default(), AutoExposureCompensationCurve::default())
52 .unwrap();
53
54 app.add_plugins(ExtractComponentPlugin::<AutoExposure>::default());
55
56 let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
57 return;
58 };
59
60 render_app
61 .init_resource::<SpecializedComputePipelines<AutoExposurePipeline>>()
62 .init_resource::<AutoExposureBuffers>()
63 .add_systems(
64 RenderStartup,
65 (init_auto_exposure_pipeline, init_auto_exposure_resources),
66 )
67 .add_systems(ExtractSchedule, extract_buffers)
68 .add_systems(
69 Render,
70 (
71 prepare_buffers.in_set(RenderSystems::Prepare),
72 queue_view_auto_exposure_pipelines.in_set(RenderSystems::Queue),
73 ),
74 )
75 .add_render_graph_node::<AutoExposureNode>(Core3d, node::AutoExposure)
76 .add_render_graph_edges(
77 Core3d,
78 (
79 Node3d::StartMainPassPostProcessing,
80 node::AutoExposure,
81 Node3d::Tonemapping,
82 ),
83 );
84 }
85}
86
87pub fn init_auto_exposure_resources(mut commands: Commands, render_device: Res<RenderDevice>) {
88 commands.insert_resource(AutoExposureResources {
89 histogram: render_device.create_buffer(&BufferDescriptor {
90 label: Some("histogram buffer"),
91 size: pipeline::HISTOGRAM_BIN_COUNT * 4,
92 usage: BufferUsages::STORAGE,
93 mapped_at_creation: false,
94 }),
95 });
96}
97
98fn queue_view_auto_exposure_pipelines(
99 mut commands: Commands,
100 pipeline_cache: Res<PipelineCache>,
101 mut compute_pipelines: ResMut<SpecializedComputePipelines<AutoExposurePipeline>>,
102 pipeline: Res<AutoExposurePipeline>,
103 view_targets: Query<(Entity, &AutoExposure)>,
104) {
105 for (entity, auto_exposure) in view_targets.iter() {
106 let histogram_pipeline =
107 compute_pipelines.specialize(&pipeline_cache, &pipeline, AutoExposurePass::Histogram);
108 let average_pipeline =
109 compute_pipelines.specialize(&pipeline_cache, &pipeline, AutoExposurePass::Average);
110
111 commands.entity(entity).insert(ViewAutoExposurePipeline {
112 histogram_pipeline,
113 mean_luminance_pipeline: average_pipeline,
114 compensation_curve: auto_exposure.compensation_curve.clone(),
115 metering_mask: auto_exposure.metering_mask.clone(),
116 });
117 }
118}