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};
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
35pub 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}