Skip to main content

fret_ui_kit/
custom_effects.rs

1use fret_core::{
2    CustomEffectDescriptorV1, CustomEffectDescriptorV2, CustomEffectDescriptorV3,
3    CustomEffectRegistrationError, CustomEffectService, EffectId,
4};
5
6/// Lazily registers a custom effect program (v1) and caches its `EffectId`.
7///
8/// This is intended for app/ecosystem code that wants to author a small WGSL snippet while keeping
9/// the `Scene` contract bounded (ADR 0299).
10///
11/// Notes:
12/// - The cached `EffectId` is tied to the current renderer instance. If the renderer is recreated
13///   (e.g. device loss), call `invalidate()` and re-register.
14#[derive(Debug, Clone)]
15pub struct CustomEffectProgramV1 {
16    desc: CustomEffectDescriptorV1,
17    id: Option<EffectId>,
18}
19
20impl CustomEffectProgramV1 {
21    pub fn wgsl_utf8(source: impl Into<String>) -> Self {
22        Self {
23            desc: CustomEffectDescriptorV1::wgsl_utf8(source),
24            id: None,
25        }
26    }
27
28    pub fn descriptor(&self) -> &CustomEffectDescriptorV1 {
29        &self.desc
30    }
31
32    pub fn id(&self) -> Option<EffectId> {
33        self.id
34    }
35
36    pub fn invalidate(&mut self) {
37        self.id = None;
38    }
39
40    pub fn ensure_registered(
41        &mut self,
42        effects: &mut dyn CustomEffectService,
43    ) -> Result<EffectId, CustomEffectRegistrationError> {
44        if let Some(id) = self.id {
45            return Ok(id);
46        }
47
48        let id = effects.register_custom_effect_v1(self.desc.clone())?;
49        self.id = Some(id);
50        Ok(id)
51    }
52
53    pub fn unregister(&mut self, effects: &mut dyn CustomEffectService) -> bool {
54        let Some(id) = self.id else {
55            return false;
56        };
57        self.id = None;
58        effects.unregister_custom_effect(id)
59    }
60}
61
62/// Lazily registers a custom effect program (v2) and caches its `EffectId`.
63///
64/// V2 programs may reference additional versioned bindings (e.g. a single user image input).
65///
66/// Notes:
67/// - The cached `EffectId` is tied to the current renderer instance. If the renderer is recreated
68///   (e.g. device loss), call `invalidate()` and re-register.
69#[derive(Debug, Clone)]
70pub struct CustomEffectProgramV2 {
71    desc: CustomEffectDescriptorV2,
72    id: Option<EffectId>,
73}
74
75/// Lazily registers a custom effect program (v3) and caches its `EffectId`.
76///
77/// V3 programs may request renderer-provided sources (e.g. `src_raw` + an optional bounded pyramid).
78///
79/// Notes:
80/// - The cached `EffectId` is tied to the current renderer instance. If the renderer is recreated
81///   (e.g. device loss), call `invalidate()` and re-register.
82#[derive(Debug, Clone)]
83pub struct CustomEffectProgramV3 {
84    desc: CustomEffectDescriptorV3,
85    id: Option<EffectId>,
86}
87
88impl CustomEffectProgramV3 {
89    pub fn wgsl_utf8(source: impl Into<String>) -> Self {
90        Self {
91            desc: CustomEffectDescriptorV3::wgsl_utf8(source),
92            id: None,
93        }
94    }
95
96    pub fn descriptor(&self) -> &CustomEffectDescriptorV3 {
97        &self.desc
98    }
99
100    pub fn id(&self) -> Option<EffectId> {
101        self.id
102    }
103
104    pub fn invalidate(&mut self) {
105        self.id = None;
106    }
107
108    pub fn ensure_registered(
109        &mut self,
110        effects: &mut dyn CustomEffectService,
111    ) -> Result<EffectId, CustomEffectRegistrationError> {
112        if let Some(id) = self.id {
113            return Ok(id);
114        }
115
116        let id = effects.register_custom_effect_v3(self.desc.clone())?;
117        self.id = Some(id);
118        Ok(id)
119    }
120
121    pub fn unregister(&mut self, effects: &mut dyn CustomEffectService) -> bool {
122        let Some(id) = self.id else {
123            return false;
124        };
125        self.id = None;
126        effects.unregister_custom_effect(id)
127    }
128}
129
130impl CustomEffectProgramV2 {
131    pub fn wgsl_utf8(source: impl Into<String>) -> Self {
132        Self {
133            desc: CustomEffectDescriptorV2::wgsl_utf8(source),
134            id: None,
135        }
136    }
137
138    pub fn descriptor(&self) -> &CustomEffectDescriptorV2 {
139        &self.desc
140    }
141
142    pub fn id(&self) -> Option<EffectId> {
143        self.id
144    }
145
146    pub fn invalidate(&mut self) {
147        self.id = None;
148    }
149
150    pub fn ensure_registered(
151        &mut self,
152        effects: &mut dyn CustomEffectService,
153    ) -> Result<EffectId, CustomEffectRegistrationError> {
154        if let Some(id) = self.id {
155            return Ok(id);
156        }
157
158        let id = effects.register_custom_effect_v2(self.desc.clone())?;
159        self.id = Some(id);
160        Ok(id)
161    }
162
163    pub fn unregister(&mut self, effects: &mut dyn CustomEffectService) -> bool {
164        let Some(id) = self.id else {
165            return false;
166        };
167        self.id = None;
168        effects.unregister_custom_effect(id)
169    }
170}