1use crate::{Context, FullOutput, RawInput};
2use ahash::HashMap;
3use epaint::mutex::{Mutex, MutexGuard};
4use std::sync::Arc;
5
6#[expect(unused_variables)]
13pub trait Plugin: Send + Sync + std::any::Any + 'static {
14 fn debug_name(&self) -> &'static str;
18
19 fn setup(&mut self, ctx: &Context) {}
23
24 fn on_begin_pass(&mut self, ctx: &Context) {}
28
29 fn on_end_pass(&mut self, ctx: &Context) {}
33
34 fn input_hook(&mut self, input: &mut RawInput) {}
39
40 fn output_hook(&mut self, output: &mut FullOutput) {}
45}
46
47pub(crate) struct PluginHandle {
48 plugin: Box<dyn Plugin>,
49}
50
51pub struct TypedPluginHandle<P: Plugin> {
52 handle: Arc<Mutex<PluginHandle>>,
53 _type: std::marker::PhantomData<P>,
54}
55
56impl<P: Plugin> TypedPluginHandle<P> {
57 pub(crate) fn new(handle: Arc<Mutex<PluginHandle>>) -> Self {
58 Self {
59 handle,
60 _type: std::marker::PhantomData,
61 }
62 }
63
64 pub fn lock(&self) -> TypedPluginGuard<'_, P> {
65 TypedPluginGuard {
66 guard: self.handle.lock(),
67 _type: std::marker::PhantomData,
68 }
69 }
70}
71
72pub struct TypedPluginGuard<'a, P: Plugin> {
73 guard: MutexGuard<'a, PluginHandle>,
74 _type: std::marker::PhantomData<P>,
75}
76
77impl<P: Plugin> TypedPluginGuard<'_, P> {}
78
79impl<P: Plugin> std::ops::Deref for TypedPluginGuard<'_, P> {
80 type Target = P;
81
82 fn deref(&self) -> &Self::Target {
83 self.guard.typed_plugin()
84 }
85}
86
87impl<P: Plugin> std::ops::DerefMut for TypedPluginGuard<'_, P> {
88 fn deref_mut(&mut self) -> &mut Self::Target {
89 self.guard.typed_plugin_mut()
90 }
91}
92
93impl PluginHandle {
94 pub fn new<P: Plugin>(plugin: P) -> Arc<Mutex<Self>> {
95 Arc::new(Mutex::new(Self {
96 plugin: Box::new(plugin),
97 }))
98 }
99
100 fn plugin_type_id(&self) -> std::any::TypeId {
101 (*self.plugin).type_id()
102 }
103
104 pub fn dyn_plugin_mut(&mut self) -> &mut dyn Plugin {
105 &mut *self.plugin
106 }
107
108 fn typed_plugin<P: Plugin + 'static>(&self) -> &P {
109 (&*self.plugin as &dyn std::any::Any)
110 .downcast_ref::<P>()
111 .expect("PluginHandle: plugin is not of the expected type")
112 }
113
114 pub fn typed_plugin_mut<P: Plugin + 'static>(&mut self) -> &mut P {
115 (&mut *self.plugin as &mut dyn std::any::Any)
116 .downcast_mut::<P>()
117 .expect("PluginHandle: plugin is not of the expected type")
118 }
119}
120
121#[derive(Clone, Default)]
123pub(crate) struct Plugins {
124 plugins: HashMap<std::any::TypeId, Arc<Mutex<PluginHandle>>>,
125 plugins_ordered: PluginsOrdered,
126}
127
128#[derive(Clone, Default)]
129pub(crate) struct PluginsOrdered(Vec<Arc<Mutex<PluginHandle>>>);
130
131impl PluginsOrdered {
132 fn for_each_dyn<F>(&self, mut f: F)
133 where
134 F: FnMut(&mut dyn Plugin),
135 {
136 for plugin in &self.0 {
137 let mut plugin = plugin.lock();
138 profiling::scope!("plugin", plugin.dyn_plugin_mut().debug_name());
139 f(plugin.dyn_plugin_mut());
140 }
141 }
142
143 pub fn on_begin_pass(&self, ctx: &Context) {
144 profiling::scope!("plugins", "on_begin_pass");
145 self.for_each_dyn(|p| {
146 p.on_begin_pass(ctx);
147 });
148 }
149
150 pub fn on_end_pass(&self, ctx: &Context) {
151 profiling::scope!("plugins", "on_end_pass");
152 self.for_each_dyn(|p| {
153 p.on_end_pass(ctx);
154 });
155 }
156
157 pub fn on_input(&self, input: &mut RawInput) {
158 profiling::scope!("plugins", "on_input");
159 self.for_each_dyn(|plugin| {
160 plugin.input_hook(input);
161 });
162 }
163
164 pub fn on_output(&self, output: &mut FullOutput) {
165 profiling::scope!("plugins", "on_output");
166 self.for_each_dyn(|plugin| {
167 plugin.output_hook(output);
168 });
169 }
170}
171
172impl Plugins {
173 pub fn ordered_plugins(&self) -> PluginsOrdered {
174 self.plugins_ordered.clone()
175 }
176
177 pub fn add(&mut self, handle: Arc<Mutex<PluginHandle>>) -> bool {
182 profiling::scope!("plugins", "add");
183
184 let type_id = handle.lock().plugin_type_id();
185
186 if self.plugins.contains_key(&type_id) {
187 return false;
188 }
189
190 self.plugins.insert(type_id, handle.clone());
191 self.plugins_ordered.0.push(handle);
192
193 true
194 }
195
196 pub fn get(&self, type_id: std::any::TypeId) -> Option<Arc<Mutex<PluginHandle>>> {
197 self.plugins.get(&type_id).cloned()
198 }
199}
200
201pub type ContextCallback = Arc<dyn Fn(&Context) + Send + Sync>;
203
204#[derive(Default)]
205pub(crate) struct CallbackPlugin {
206 pub on_begin_plugins: Vec<(&'static str, ContextCallback)>,
207 pub on_end_plugins: Vec<(&'static str, ContextCallback)>,
208}
209
210impl Plugin for CallbackPlugin {
211 fn debug_name(&self) -> &'static str {
212 "CallbackPlugins"
213 }
214
215 fn on_begin_pass(&mut self, ctx: &Context) {
216 profiling::function_scope!();
217
218 for (_debug_name, cb) in &self.on_begin_plugins {
219 profiling::scope!(*_debug_name);
220 (cb)(ctx);
221 }
222 }
223
224 fn on_end_pass(&mut self, ctx: &Context) {
225 profiling::function_scope!();
226
227 for (_debug_name, cb) in &self.on_end_plugins {
228 profiling::scope!(*_debug_name);
229 (cb)(ctx);
230 }
231 }
232}