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
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
use crate::{transform::GlobalTransformQueue, HikariSettings};
use bevy::{
    ecs::query::QueryItem,
    prelude::*,
    render::{
        extract_component::{ExtractComponent, ExtractComponentPlugin, UniformComponentPlugin},
        render_resource::*,
        renderer::{RenderDevice, RenderQueue},
        view::ExtractedView,
        RenderApp, RenderStage,
    },
};

pub struct ViewPlugin;
impl Plugin for ViewPlugin {
    fn build(&self, app: &mut App) {
        app.register_type::<FrameCounter>()
            .add_plugin(ExtractComponentPlugin::<FrameCounter>::default())
            .add_plugin(ExtractComponentPlugin::<FrameUniform>::default())
            .add_plugin(UniformComponentPlugin::<FrameUniform>::default())
            .add_system_to_stage(CoreStage::PostUpdate, frame_counter_system);

        if let Ok(render_app) = app.get_sub_app_mut(RenderApp) {
            render_app
                .init_resource::<PreviousViewUniforms>()
                .add_system_to_stage(RenderStage::Prepare, prepare_view_uniforms);
        }
    }
}

#[derive(Clone, ShaderType)]
pub struct PreviousViewUniform {
    view_proj: Mat4,
    inverse_view_proj: Mat4,
}

#[derive(Default, Resource)]
pub struct PreviousViewUniforms {
    pub uniforms: DynamicUniformBuffer<PreviousViewUniform>,
}

#[derive(Component)]
pub struct PreviousViewUniformOffset {
    pub offset: u32,
}

fn prepare_view_uniforms(
    mut commands: Commands,
    render_device: Res<RenderDevice>,
    render_queue: Res<RenderQueue>,
    mut view_uniforms: ResMut<PreviousViewUniforms>,
    views: Query<(Entity, &ExtractedView, &GlobalTransformQueue)>,
) {
    view_uniforms.uniforms.clear();
    for (entity, camera, queue) in &views {
        let projection = camera.projection;
        let inverse_projection = projection.inverse();
        let view = queue[1];
        let inverse_view = view.inverse();
        let view_uniforms = PreviousViewUniformOffset {
            offset: view_uniforms.uniforms.push(PreviousViewUniform {
                view_proj: projection * inverse_view,
                inverse_view_proj: view * inverse_projection,
            }),
        };

        commands.entity(entity).insert(view_uniforms);
    }

    view_uniforms
        .uniforms
        .write_buffer(&render_device, &render_queue);
}

#[derive(Default, Clone, Copy, Component, Reflect, Deref, DerefMut)]
#[reflect(Component)]
pub struct FrameCounter(pub usize);

impl ExtractComponent for FrameCounter {
    type Query = &'static Self;
    type Filter = ();

    fn extract_component(item: QueryItem<Self::Query>) -> Self {
        *item
    }
}

#[allow(clippy::type_complexity)]
fn frame_counter_system(
    mut commands: Commands,
    mut queries: ParamSet<(
        Query<Entity, (With<Camera>, Without<FrameCounter>)>,
        Query<&mut FrameCounter>,
    )>,
) {
    for entity in &queries.p0() {
        commands.entity(entity).insert(FrameCounter::default());
    }

    for mut counter in queries.p1().iter_mut() {
        **counter += 1;
    }
}

#[derive(Debug, Default, Clone, Copy, Component, ShaderType)]
pub struct FrameUniform {
    pub kernel: Mat3,
    pub clear_color: Vec4,
    pub number: u32,
    pub direct_validate_interval: u32,
    pub emissive_validate_interval: u32,
    pub indirect_bounces: u32,
    pub enable_temporal_reuse: u32,
    pub enable_spatial_reuse: u32,
    pub max_temporal_reuse_count: u32,
    pub max_spatial_reuse_count: u32,
    pub max_reservoir_lifetime: f32,
    pub solar_angle: f32,
    pub max_indirect_luminance: f32,
    pub upscale_ratio: f32,
}

impl ExtractComponent for FrameUniform {
    type Query = (&'static HikariSettings, &'static FrameCounter);
    type Filter = ();

    fn extract_component((settings, counter): QueryItem<Self::Query>) -> Self {
        let HikariSettings {
            direct_validate_interval,
            emissive_validate_interval,
            max_temporal_reuse_count,
            max_spatial_reuse_count,
            max_reservoir_lifetime,
            solar_angle,
            indirect_bounces,
            max_indirect_luminance,
            clear_color,
            temporal_reuse,
            spatial_reuse,
            ..
        } = settings.clone();

        let kernel = Mat3 {
            x_axis: Vec3::new(0.0625, 0.125, 0.0625),
            y_axis: Vec3::new(0.125, 0.25, 0.125),
            z_axis: Vec3::new(0.0625, 0.125, 0.0625),
        };
        let number = counter.0 as u32;
        let direct_validate_interval = direct_validate_interval as u32;
        let emissive_validate_interval = emissive_validate_interval as u32;
        let indirect_bounces = indirect_bounces as u32;
        let clear_color = clear_color.into();
        let max_temporal_reuse_count = max_temporal_reuse_count as u32;
        let max_spatial_reuse_count = max_spatial_reuse_count as u32;
        let enable_temporal_reuse = temporal_reuse.into();
        let enable_spatial_reuse = spatial_reuse.into();
        let upscale_ratio = settings.upscale.ratio();

        Self {
            kernel,
            clear_color,
            number,
            direct_validate_interval,
            emissive_validate_interval,
            indirect_bounces,
            enable_temporal_reuse,
            enable_spatial_reuse,
            max_temporal_reuse_count,
            max_spatial_reuse_count,
            max_reservoir_lifetime,
            solar_angle,
            max_indirect_luminance,
            upscale_ratio,
        }
    }
}