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
//! Post-processing configuration and updates.
use crate::{error::Result, AwsmRenderer};
/// Post-processing settings for the renderer.
#[derive(Clone, Debug, PartialEq)]
pub struct PostProcessing {
pub tonemapping: ToneMapping,
pub bloom: bool,
pub dof: bool,
/// Pre-tonemap scene exposure in EV (stops). 0 = unity, +1 = 2x as
/// bright, -1 = half as bright. Lets the user pull authored
/// photometric intensities (candela-scale gltf lights) into a range
/// the tonemapper can resolve without saturating. The renderer
/// doesn't try to convert lumens→watts; this is the user-facing
/// knob that compensates.
pub exposure: f32,
}
impl Eq for PostProcessing {}
/// Tonemapping operator selection.
#[derive(Clone, Debug, PartialEq, Eq, Copy, Hash)]
pub enum ToneMapping {
None,
KhronosNeutralPbr,
Aces,
}
impl Default for PostProcessing {
fn default() -> Self {
Self {
tonemapping: ToneMapping::KhronosNeutralPbr,
bloom: false,
dof: false,
exposure: 0.0,
}
}
}
impl AwsmRenderer {
/// Applies post-processing configuration and recompiles the
/// dependent pipelines for the new config.
///
/// **Lazy-pool model:** cold-boot only compiled the effects
/// pass's `BloomPhase::None` shader (the default-disabled bloom
/// case). Toggling `bloom: true` here triggers a transactional
/// recompile that compiles the 4 missing bloom phases inside
/// one batched `Shaders::ensure_keys` + one batched
/// `ComputePipelines::ensure_keys`. The display pipeline (which
/// depends on the live tonemapper) is recompiled the same way.
///
/// Returns only after every newly-required variant is GPU-
/// resident, so the next render frame can dispatch without
/// further awaits.
pub async fn set_post_processing(&mut self, pp: PostProcessing) -> Result<()> {
// Race policy per https://github.com/dakom/awsm-renderer/pull/99: config-change
// APIs return NotReady when called before build() finishes its
// eager batch.
if !self.build_complete {
return Err(crate::error::AwsmError::NotReady);
}
// No-op fast path — caller asked for the state we're
// already in. Saves a redundant batched ensure on UI
// double-fire (common when sliders re-emit on every focus
// event).
if self.post_processing == pp {
return Ok(());
}
self.post_processing = pp;
self.render_passes
.effects
.pipelines
.set_render_pipeline_keys(
&self.anti_aliasing,
&self.post_processing,
&self.gpu,
&mut self.shaders,
&mut self.pipelines,
&self.pipeline_layouts,
&self.render_textures.formats,
)
.await?;
self.render_passes
.display
.pipelines
.set_render_pipeline_key(
&self.post_processing,
&self.gpu,
&mut self.shaders,
&mut self.pipelines,
&self.pipeline_layouts,
&self.render_textures.formats,
)
.await?;
Ok(())
}
}