Skip to content

Commit

Permalink
Add reuse of auto exposure settings uniform buffer between frames
Browse files Browse the repository at this point in the history
  • Loading branch information
Kurble committed Apr 9, 2024
1 parent 0ce95e9 commit 30e930b
Show file tree
Hide file tree
Showing 7 changed files with 73 additions and 73 deletions.
1 change: 0 additions & 1 deletion crates/bevy_core_pipeline/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ bevy_derive = { path = "../bevy_derive", version = "0.14.0-dev" }
bevy_ecs = { path = "../bevy_ecs", version = "0.14.0-dev" }
bevy_reflect = { path = "../bevy_reflect", version = "0.14.0-dev" }
bevy_render = { path = "../bevy_render", version = "0.14.0-dev" }
bevy_time = { path = "../bevy_time", version = "0.14.0-dev" }
bevy_transform = { path = "../bevy_transform", version = "0.14.0-dev" }
bevy_math = { path = "../bevy_math", version = "0.14.0-dev" }
bevy_utils = { path = "../bevy_utils", version = "0.14.0-dev" }
Expand Down
30 changes: 18 additions & 12 deletions crates/bevy_core_pipeline/src/auto_exposure/auto_exposure.wgsl
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#import bevy_render::view::View
#import bevy_render::globals::Globals

// Constant to convert RGB to luminance, taken from RTR vol 4 pg. 278
const RGB_TO_LUM = vec3<f32>(0.2125, 0.7154, 0.0721);
Expand All @@ -11,8 +12,7 @@ struct AutoExposure {
high_percent: f32,
speed_up: f32,
speed_down: f32,
exp_up: f32,
exp_down: f32,
exponential_transition_distance: f32,
}

struct CompensationCurve {
Expand All @@ -22,21 +22,23 @@ struct CompensationCurve {
compensation_range: f32,
}

@group(0) @binding(0) var<uniform> settings: AutoExposure;
@group(0) @binding(0) var<uniform> globals: Globals;

@group(0) @binding(1) var tex_color: texture_2d<f32>;
@group(0) @binding(1) var<uniform> settings: AutoExposure;

@group(0) @binding(2) var tex_mask: texture_2d<f32>;
@group(0) @binding(2) var tex_color: texture_2d<f32>;

@group(0) @binding(3) var tex_compensation: texture_1d<f32>;
@group(0) @binding(3) var tex_mask: texture_2d<f32>;

@group(0) @binding(4) var<uniform> compensation_curve: CompensationCurve;
@group(0) @binding(4) var tex_compensation: texture_1d<f32>;

@group(0) @binding(5) var<storage, read_write> histogram: array<atomic<u32>, 64>;
@group(0) @binding(5) var<uniform> compensation_curve: CompensationCurve;

@group(0) @binding(6) var<storage, read_write> exposure: f32;
@group(0) @binding(6) var<storage, read_write> histogram: array<atomic<u32>, 64>;

@group(0) @binding(7) var<storage, read_write> view: View;
@group(0) @binding(7) var<storage, read_write> exposure: f32;

@group(0) @binding(8) var<storage, read_write> view: View;

var<workgroup> histogram_shared: array<atomic<u32>, 64>;

Expand Down Expand Up @@ -155,9 +157,13 @@ fn compute_average(@builtin(local_invocation_index) local_index: u32) {
// Smoothly adjust the `exposure` towards the `target_exposure`
let delta = target_exposure - exposure;
if target_exposure > exposure {
exposure = exposure + min(settings.speed_down, delta * settings.exp_down);
let speed_down = settings.speed_down * globals.delta_time;
let exp_down = speed_down / settings.exponential_transition_distance;
exposure = exposure + min(speed_down, delta * exp_down);
} else {
exposure = exposure + max(-settings.speed_up, delta * settings.exp_up);
let speed_up = settings.speed_up * globals.delta_time;
let exp_up = speed_up / settings.exponential_transition_distance;
exposure = exposure + max(-speed_up, delta * exp_up);
}

// Apply the exposure to the color grading settings, from where it will be used for the color
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,6 @@ const MAX_ERROR: f32 = 1e-5;
const MAX_ITERS: u8 = 8;

/// Find the `y` value of the curve at the given `x` value using the Newton-Raphson method.
#[inline]
fn find_y_given_x(segment: &CubicSegment<Vec2>, x: f32) -> f32 {
let mut t_guess = x;
let mut pos_guess = Vec2::ZERO;
Expand Down
25 changes: 2 additions & 23 deletions crates/bevy_core_pipeline/src/auto_exposure/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ use bevy_render::{
renderer::RenderDevice,
Render, RenderApp, RenderSet,
};
use bevy_time::Time;

mod compensation_curve;
mod node;
Expand All @@ -23,10 +22,7 @@ mod state;

pub use compensation_curve::AutoExposureCompensationCurve;
use node::AutoExposureNode;
use pipeline::{
AutoExposurePipeline, AutoExposureUniform, Pass, ViewAutoExposurePipeline,
METERING_SHADER_HANDLE,
};
use pipeline::{AutoExposurePipeline, Pass, ViewAutoExposurePipeline, METERING_SHADER_HANDLE};
pub use settings::AutoExposureSettings;
use state::{extract_state_buffers, prepare_state_buffers, AutoExposureStateBuffers};

Expand Down Expand Up @@ -115,7 +111,6 @@ fn queue_view_auto_exposure_pipelines(
mut compute_pipelines: ResMut<SpecializedComputePipelines<AutoExposurePipeline>>,
pipeline: Res<AutoExposurePipeline>,
buffers: Res<AutoExposureStateBuffers>,
time: Res<Time>,
view_targets: Query<(Entity, &AutoExposureSettings)>,
) {
for (entity, settings) in view_targets.iter() {
Expand All @@ -128,28 +123,12 @@ fn queue_view_auto_exposure_pipelines(
continue;
};

let (min_log_lum, max_log_lum) = settings.range.clone().into_inner();
let (low_percent, high_percent) = settings.filter.clone().into_inner();

let brighten = settings.speed_brighten * time.delta_seconds();
let darken = settings.speed_darken * time.delta_seconds();

commands.entity(entity).insert(ViewAutoExposurePipeline {
histogram_pipeline,
mean_luminance_pipeline: average_pipeline,
state: buffer.state.clone(),
settings: buffer.settings.clone(),
compensation_curve: settings.compensation_curve.clone(),
uniform: AutoExposureUniform {
min_log_lum,
inv_log_lum_range: 1.0 / (max_log_lum - min_log_lum),
log_lum_range: max_log_lum - min_log_lum,
low_percent,
high_percent,
speed_up: brighten,
speed_down: darken,
exp_up: brighten / settings.exponential_transition_distance,
exp_down: darken / settings.exponential_transition_distance,
},
metering_mask: settings.metering_mask.clone(),
});
}
Expand Down
21 changes: 7 additions & 14 deletions crates/bevy_core_pipeline/src/auto_exposure/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use bevy_ecs::{
world::{FromWorld, World},
};
use bevy_render::{
globals::GlobalsBuffer,
render_asset::RenderAssets,
render_graph::*,
render_resource::*,
Expand Down Expand Up @@ -57,6 +58,8 @@ impl Node for AutoExposureNode {
let view_uniforms = &view_uniforms_resource.uniforms;
let view_uniforms_buffer = view_uniforms.buffer().unwrap();

let globals_buffer = world.resource::<GlobalsBuffer>();

let Ok((view_uniform_offset, view_target, auto_exposure, view)) =
self.query.get_manual(world, view_entity)
else {
Expand Down Expand Up @@ -87,22 +90,12 @@ impl Node for AutoExposureNode {
return Ok(());
};

let mut uniform = encase::UniformBuffer::new(Vec::new());
uniform.write(&auto_exposure.uniform).unwrap();
let uniform =
render_context
.render_device()
.create_buffer_with_data(&BufferInitDescriptor {
label: None,
contents: uniform.as_ref(),
usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST,
});

let compute_bind_group = render_context.render_device().create_bind_group(
None,
&pipeline.histogram_layout,
&BindGroupEntries::sequential((
uniform.as_entire_buffer_binding(),
&globals_buffer.buffer,
auto_exposure.settings.as_entire_buffer_binding(),
source,
mask,
&compensation_curve.texture_view,
Expand All @@ -128,8 +121,8 @@ impl Node for AutoExposureNode {
compute_pass.set_bind_group(0, &compute_bind_group, &[view_uniform_offset.offset]);
compute_pass.set_pipeline(histogram_pipeline);
compute_pass.dispatch_workgroups(
(view.viewport.z + 15) / 16,
(view.viewport.w + 15) / 16,
view.viewport.z.div_ceil(16),
view.viewport.w.div_ceil(16),
1,
);
compute_pass.set_pipeline(average_pipeline);
Expand Down
16 changes: 7 additions & 9 deletions crates/bevy_core_pipeline/src/auto_exposure/pipeline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use super::compensation_curve::{
use bevy_asset::prelude::*;
use bevy_ecs::prelude::*;
use bevy_render::{
globals::GlobalsUniform,
render_resource::{binding_types::*, *},
renderer::RenderDevice,
texture::Image,
Expand All @@ -22,8 +23,8 @@ pub struct ViewAutoExposurePipeline {
pub histogram_pipeline: CachedComputePipelineId,
pub mean_luminance_pipeline: CachedComputePipelineId,
pub state: Buffer,
pub settings: Buffer,
pub compensation_curve: Handle<AutoExposureCompensationCurve>,
pub uniform: AutoExposureUniform,
pub metering_mask: Handle<Image>,
}

Expand All @@ -36,8 +37,7 @@ pub struct AutoExposureUniform {
pub(super) high_percent: f32,
pub(super) speed_up: f32,
pub(super) speed_down: f32,
pub(super) exp_up: f32,
pub(super) exp_down: f32,
pub(super) exponential_transition_distance: f32,
}

#[derive(PartialEq, Eq, Hash, Clone)]
Expand All @@ -60,17 +60,15 @@ impl FromWorld for AutoExposurePipeline {
&BindGroupLayoutEntries::sequential(
ShaderStages::COMPUTE,
(
uniform_buffer_sized(false, Some(AutoExposureUniform::min_size())),
uniform_buffer::<GlobalsUniform>(false),
uniform_buffer::<AutoExposureUniform>(false),
texture_2d(TextureSampleType::Float { filterable: false }),
texture_2d(TextureSampleType::Float { filterable: false }),
texture_1d(TextureSampleType::Float { filterable: false }),
uniform_buffer_sized(
false,
Some(AutoExposureCompensationCurveUniform::min_size()),
),
uniform_buffer::<AutoExposureCompensationCurveUniform>(false),
storage_buffer_sized(false, NonZeroU64::new(HISTOGRAM_BIN_COUNT * 4)),
storage_buffer_sized(false, NonZeroU64::new(4)),
storage_buffer_sized(true, Some(ViewUniform::min_size())),
storage_buffer::<ViewUniform>(true),
),
),
),
Expand Down
52 changes: 39 additions & 13 deletions crates/bevy_core_pipeline/src/auto_exposure/state.rs
Original file line number Diff line number Diff line change
@@ -1,40 +1,40 @@
use bevy_ecs::prelude::*;
use bevy_render::{
render_resource::{Buffer, BufferInitDescriptor, BufferUsages},
render_resource::{encase::UniformBuffer, Buffer, BufferInitDescriptor, BufferUsages},
renderer::RenderDevice,
Extract,
};
use bevy_utils::HashMap;

use crate::auto_exposure::AutoExposureSettings;

use super::pipeline::AutoExposureUniform;

#[derive(Resource, Default)]
pub(super) struct AutoExposureStateBuffers {
pub(super) buffers: HashMap<Entity, AutoExposureStateBuffer>,
}

pub(super) struct AutoExposureStateBuffer {
pub(super) state: Buffer,
pub(super) settings: Buffer,
}

#[derive(Resource)]
pub(super) struct ExtractedStateBuffers {
changed: Vec<(Entity, f32)>,
changed: Vec<(Entity, AutoExposureSettings)>,
removed: Vec<Entity>,
}

pub(super) fn extract_state_buffers(
mut commands: Commands,
changed: Extract<Query<(Entity, &AutoExposureSettings), Added<AutoExposureSettings>>>,
changed: Extract<Query<(Entity, &AutoExposureSettings), Changed<AutoExposureSettings>>>,
mut removed: Extract<RemovedComponents<AutoExposureSettings>>,
) {
commands.insert_resource(ExtractedStateBuffers {
changed: changed
.iter()
.map(|(entity, settings)| {
let (min, max) = settings.range.clone().into_inner();
(entity, 0.0f32.clamp(min, max))
})
.map(|(entity, settings)| (entity, settings.clone()))
.collect(),
removed: removed.read().collect(),
});
Expand All @@ -45,15 +45,41 @@ pub(super) fn prepare_state_buffers(
mut extracted: ResMut<ExtractedStateBuffers>,
mut buffers: ResMut<AutoExposureStateBuffers>,
) {
for (entity, initial_state) in extracted.changed.drain(..) {
for (entity, settings) in extracted.changed.drain(..) {
let (min_log_lum, max_log_lum) = settings.range.into_inner();
let (low_percent, high_percent) = settings.filter.into_inner();
let initial_state = 0.0f32.clamp(min_log_lum, max_log_lum);

let state_buffer = device.create_buffer_with_data(&BufferInitDescriptor {
label: Some("auto exposure state buffer"),
contents: &initial_state.to_ne_bytes(),
usage: BufferUsages::STORAGE | BufferUsages::COPY_DST,
});

let mut settings_buffer = UniformBuffer::new(Vec::new());
settings_buffer
.write(&AutoExposureUniform {
min_log_lum,
inv_log_lum_range: 1.0 / (max_log_lum - min_log_lum),
log_lum_range: max_log_lum - min_log_lum,
low_percent,
high_percent,
speed_up: settings.speed_brighten,
speed_down: settings.speed_darken,
exponential_transition_distance: settings.exponential_transition_distance,
})
.unwrap();
let settings_buffer = device.create_buffer_with_data(&BufferInitDescriptor {
label: None,
contents: settings_buffer.as_ref(),
usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST,
});

buffers.buffers.insert(
entity,
AutoExposureStateBuffer {
state: device.create_buffer_with_data(&BufferInitDescriptor {
label: Some("auto exposure state buffer"),
contents: &initial_state.to_ne_bytes(),
usage: BufferUsages::STORAGE | BufferUsages::COPY_DST,
}),
state: state_buffer,
settings: settings_buffer,
},
);
}
Expand Down

0 comments on commit 30e930b

Please sign in to comment.