Skip to main content

freya_core/style/
shader.rs

1use std::sync::Arc;
2
3use freya_engine::prelude::*;
4use torin::prelude::Area;
5
6pub trait ShaderProvider: Send + Sync {
7    fn prepare_shader(&self, effect: &RuntimeEffect, bounds: Area) -> Option<Shader>;
8}
9
10impl<F> ShaderProvider for F
11where
12    F: Fn(&RuntimeEffect, Area) -> Option<Shader> + Send + Sync,
13{
14    fn prepare_shader(&self, effect: &RuntimeEffect, bounds: Area) -> Option<Shader> {
15        self(effect, bounds)
16    }
17}
18
19#[derive(Clone)]
20struct SharedRuntimeEffect(RuntimeEffect);
21
22// SAFETY: `RuntimeEffect` is immutable.
23unsafe impl Send for SharedRuntimeEffect {}
24unsafe impl Sync for SharedRuntimeEffect {}
25
26/// A custom paint source backed by an SkSL shader.
27///
28/// Build it with [`ShaderFill::new`], passing the SkSL source, a compiled
29/// [`RuntimeEffect`] and a provider closure that supplies the shader's uniforms
30/// for a given bounds. Use it as a [`Fill`](crate::style::fill::Fill) for
31/// backgrounds or text.
32#[derive(Clone)]
33pub struct ShaderFill {
34    sksl: Arc<str>,
35    effect: Arc<SharedRuntimeEffect>,
36    provider: Arc<dyn ShaderProvider>,
37}
38
39impl ShaderFill {
40    pub fn new<F>(sksl: impl Into<Arc<str>>, effect: RuntimeEffect, provider: F) -> Self
41    where
42        F: Fn(&RuntimeEffect, Area) -> Option<Shader> + Send + Sync + 'static,
43    {
44        Self::from_provider(sksl, effect, provider)
45    }
46
47    pub fn from_provider<S>(sksl: impl Into<Arc<str>>, effect: RuntimeEffect, provider: S) -> Self
48    where
49        S: ShaderProvider + 'static,
50    {
51        Self {
52            sksl: sksl.into(),
53            effect: Arc::new(SharedRuntimeEffect(effect)),
54            provider: Arc::new(provider),
55        }
56    }
57
58    /// Prepare the shader for use by providing the necessary uniforms.
59    /// Returns [None] if the provider could not produce a [Shader], in which case the renderer will fallback to no fill.
60    pub fn prepare_shader(&self, bounds: Area) -> Option<Shader> {
61        self.provider.prepare_shader(&self.effect.0, bounds)
62    }
63}
64
65impl std::fmt::Display for ShaderFill {
66    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
67        write!(f, "shader({:p})", Arc::as_ptr(&self.provider))
68    }
69}
70
71impl std::fmt::Debug for ShaderFill {
72    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
73        f.debug_struct("FillShader")
74            .field("sksl", &self.sksl)
75            .finish()
76    }
77}
78
79impl PartialEq for ShaderFill {
80    fn eq(&self, other: &Self) -> bool {
81        *self.sksl == *other.sksl
82            && Arc::ptr_eq(&self.effect, &other.effect)
83            && Arc::ptr_eq(&self.provider, &other.provider)
84    }
85}
86
87impl std::hash::Hash for ShaderFill {
88    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
89        // Mirrors PartialEq: SKSL by bytes, effect/provider by Arc pointer.
90        // The provider cast strips the vtable to match `Arc::ptr_eq` semantics.
91        (*self.sksl).hash(state);
92        Arc::as_ptr(&self.effect).hash(state);
93        Arc::as_ptr(&self.provider).cast::<()>().hash(state);
94    }
95}
96
97#[cfg(feature = "serde")]
98impl serde::Serialize for ShaderFill {
99    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
100    where
101        S: serde::Serializer,
102    {
103        serializer.serialize_str(&self.sksl)
104    }
105}
106
107#[cfg(feature = "serde")]
108impl<'de> serde::Deserialize<'de> for ShaderFill {
109    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
110    where
111        D: serde::Deserializer<'de>,
112    {
113        let sksl = String::deserialize(deserializer)?;
114        let effect =
115            RuntimeEffect::make_for_shader(&sksl, None).map_err(serde::de::Error::custom)?;
116
117        Ok(Self {
118            sksl: sksl.into(),
119            effect: Arc::new(SharedRuntimeEffect(effect)),
120            provider: Arc::new(|_: &RuntimeEffect, _: Area| None),
121        })
122    }
123}