Path: blob/main/crates/bevy_post_process/src/auto_exposure/mod.rs
9394 views
use bevy_app::prelude::*;1use bevy_asset::{embedded_asset, AssetApp, Assets, Handle};2use bevy_ecs::prelude::*;3use bevy_render::{4diagnostic::RecordDiagnostics,5extract_component::ExtractComponentPlugin,6globals::GlobalsBuffer,7render_asset::{RenderAssetPlugin, RenderAssets},8render_resource::{9BindGroupEntries, Buffer, BufferBinding, BufferDescriptor, BufferUsages,10ComputePassDescriptor, PipelineCache, ShaderType, SpecializedComputePipelines,11},12renderer::{RenderContext, RenderDevice, ViewQuery},13texture::{FallbackImage, GpuImage},14view::{ExtractedView, ViewTarget, ViewUniform, ViewUniformOffset, ViewUniforms},15ExtractSchedule, Render, RenderApp, RenderStartup, RenderSystems,16};1718mod buffers;19mod compensation_curve;20mod pipeline;21mod settings;2223use buffers::{extract_buffers, prepare_buffers, AutoExposureBuffers};24pub use compensation_curve::{AutoExposureCompensationCurve, AutoExposureCompensationCurveError};25use pipeline::{AutoExposurePass, AutoExposurePipeline, ViewAutoExposurePipeline};26pub use settings::AutoExposure;2728use crate::auto_exposure::{29compensation_curve::GpuAutoExposureCompensationCurve, pipeline::init_auto_exposure_pipeline,30};31use bevy_core_pipeline::{32schedule::{Core3d, Core3dSystems},33tonemapping::tonemapping,34};3536/// Plugin for the auto exposure feature.37///38/// See [`AutoExposure`] for more details.39pub struct AutoExposurePlugin;4041#[derive(Resource)]42struct AutoExposureResources {43histogram: Buffer,44}4546impl Plugin for AutoExposurePlugin {47fn build(&self, app: &mut App) {48embedded_asset!(app, "auto_exposure.wgsl");4950app.add_plugins(RenderAssetPlugin::<GpuAutoExposureCompensationCurve>::default())51.init_asset::<AutoExposureCompensationCurve>()52.register_asset_reflect::<AutoExposureCompensationCurve>();53app.world_mut()54.resource_mut::<Assets<AutoExposureCompensationCurve>>()55.insert(&Handle::default(), AutoExposureCompensationCurve::default())56.unwrap();5758app.add_plugins(ExtractComponentPlugin::<AutoExposure>::default());5960let Some(render_app) = app.get_sub_app_mut(RenderApp) else {61return;62};6364render_app65.init_resource::<SpecializedComputePipelines<AutoExposurePipeline>>()66.init_resource::<AutoExposureBuffers>()67.add_systems(68RenderStartup,69(init_auto_exposure_pipeline, init_auto_exposure_resources),70)71.add_systems(ExtractSchedule, extract_buffers)72.add_systems(73Render,74(75prepare_buffers.in_set(RenderSystems::Prepare),76queue_view_auto_exposure_pipelines.in_set(RenderSystems::Queue),77),78)79.add_systems(80Core3d,81auto_exposure82.before(tonemapping)83.in_set(Core3dSystems::PostProcess),84);85}86}8788pub fn init_auto_exposure_resources(mut commands: Commands, render_device: Res<RenderDevice>) {89commands.insert_resource(AutoExposureResources {90histogram: render_device.create_buffer(&BufferDescriptor {91label: Some("histogram buffer"),92size: pipeline::HISTOGRAM_BIN_COUNT * 4,93usage: BufferUsages::STORAGE,94mapped_at_creation: false,95}),96});97}9899fn queue_view_auto_exposure_pipelines(100mut commands: Commands,101pipeline_cache: Res<PipelineCache>,102mut compute_pipelines: ResMut<SpecializedComputePipelines<AutoExposurePipeline>>,103pipeline: Res<AutoExposurePipeline>,104view_targets: Query<(Entity, &AutoExposure)>,105) {106for (entity, auto_exposure) in view_targets.iter() {107let histogram_pipeline =108compute_pipelines.specialize(&pipeline_cache, &pipeline, AutoExposurePass::Histogram);109let average_pipeline =110compute_pipelines.specialize(&pipeline_cache, &pipeline, AutoExposurePass::Average);111112commands.entity(entity).insert(ViewAutoExposurePipeline {113histogram_pipeline,114mean_luminance_pipeline: average_pipeline,115compensation_curve: auto_exposure.compensation_curve.clone(),116metering_mask: auto_exposure.metering_mask.clone(),117});118}119}120121fn auto_exposure(122view: ViewQuery<(123&ViewUniformOffset,124&ViewTarget,125&ViewAutoExposurePipeline,126&ExtractedView,127)>,128pipeline_cache: Res<PipelineCache>,129pipeline: Res<AutoExposurePipeline>,130resources: Res<AutoExposureResources>,131view_uniforms: Res<ViewUniforms>,132globals_buffer: Res<GlobalsBuffer>,133auto_exposure_buffers: Res<AutoExposureBuffers>,134fallback: Res<FallbackImage>,135gpu_images: Res<RenderAssets<GpuImage>>,136compensation_curves: Res<RenderAssets<GpuAutoExposureCompensationCurve>>,137mut ctx: RenderContext,138) {139let view_entity = view.entity();140let (view_uniform_offset, view_target, auto_exposure_pipeline, extracted_view) =141view.into_inner();142143let Some(auto_exposure_buffer) = auto_exposure_buffers.buffers.get(&view_entity) else {144return;145};146147let (Some(histogram_pipeline), Some(average_pipeline)) = (148pipeline_cache.get_compute_pipeline(auto_exposure_pipeline.histogram_pipeline),149pipeline_cache.get_compute_pipeline(auto_exposure_pipeline.mean_luminance_pipeline),150) else {151return;152};153154let view_uniforms_buffer = view_uniforms.uniforms.buffer().unwrap();155let source = view_target.main_texture_view();156157let mask = gpu_images158.get(&auto_exposure_pipeline.metering_mask)159.map(|i| &i.texture_view)160.unwrap_or(&fallback.d2.texture_view);161162let Some(compensation_curve) =163compensation_curves.get(&auto_exposure_pipeline.compensation_curve)164else {165return;166};167168let compute_bind_group = ctx.render_device().create_bind_group(169None,170&pipeline_cache.get_bind_group_layout(&pipeline.histogram_layout),171&BindGroupEntries::sequential((172&globals_buffer.buffer,173&auto_exposure_buffer.settings,174source,175mask,176&compensation_curve.texture_view,177&compensation_curve.extents,178resources.histogram.as_entire_buffer_binding(),179&auto_exposure_buffer.state,180BufferBinding {181buffer: view_uniforms_buffer,182size: Some(ViewUniform::min_size()),183offset: 0,184},185)),186);187188let diagnostics = ctx.diagnostic_recorder();189let diagnostics = diagnostics.as_deref();190let time_span = diagnostics.time_span(ctx.command_encoder(), "auto_exposure");191192{193let mut compute_pass = ctx194.command_encoder()195.begin_compute_pass(&ComputePassDescriptor {196label: Some("auto_exposure"),197timestamp_writes: None,198});199200compute_pass.set_bind_group(0, &compute_bind_group, &[view_uniform_offset.offset]);201compute_pass.set_pipeline(histogram_pipeline);202compute_pass.dispatch_workgroups(203extracted_view.viewport.z.div_ceil(16),204extracted_view.viewport.w.div_ceil(16),2051,206);207compute_pass.set_pipeline(average_pipeline);208compute_pass.dispatch_workgroups(1, 1, 1);209}210211time_span.end(ctx.command_encoder());212}213214215