bevy_core_pipeline/auto_exposure/
buffers.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
use bevy_ecs::prelude::*;
use bevy_render::{
    render_resource::{StorageBuffer, UniformBuffer},
    renderer::{RenderDevice, RenderQueue},
    sync_world::RenderEntity,
    Extract,
};
use bevy_utils::{Entry, HashMap};

use super::{pipeline::AutoExposureUniform, AutoExposure};

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

pub(super) struct AutoExposureBuffer {
    pub(super) state: StorageBuffer<f32>,
    pub(super) settings: UniformBuffer<AutoExposureUniform>,
}

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

pub(super) fn extract_buffers(
    mut commands: Commands,
    changed: Extract<Query<(RenderEntity, &AutoExposure), Changed<AutoExposure>>>,
    mut removed: Extract<RemovedComponents<AutoExposure>>,
) {
    commands.insert_resource(ExtractedStateBuffers {
        changed: changed
            .iter()
            .map(|(entity, settings)| (entity, settings.clone()))
            .collect(),
        removed: removed.read().collect(),
    });
}

pub(super) fn prepare_buffers(
    device: Res<RenderDevice>,
    queue: Res<RenderQueue>,
    mut extracted: ResMut<ExtractedStateBuffers>,
    mut buffers: ResMut<AutoExposureBuffers>,
) {
    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 settings = 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,
        };

        match buffers.buffers.entry(entity) {
            Entry::Occupied(mut entry) => {
                // Update the settings buffer, but skip updating the state buffer.
                // The state buffer is skipped so that the animation stays continuous.
                let value = entry.get_mut();
                value.settings.set(settings);
                value.settings.write_buffer(&device, &queue);
            }
            Entry::Vacant(entry) => {
                let value = entry.insert(AutoExposureBuffer {
                    state: StorageBuffer::from(initial_state),
                    settings: UniformBuffer::from(settings),
                });

                value.state.write_buffer(&device, &queue);
                value.settings.write_buffer(&device, &queue);
            }
        }
    }

    for entity in extracted.removed.drain(..) {
        buffers.buffers.remove(&entity);
    }
}