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#[derive(Clone)]
27pub struct ShaderFill {
28    sksl: Arc<str>,
29    effect: Arc<SharedRuntimeEffect>,
30    provider: Arc<dyn ShaderProvider>,
31}
32
33impl ShaderFill {
34    pub fn new<F>(sksl: impl Into<Arc<str>>, effect: RuntimeEffect, provider: F) -> Self
35    where
36        F: Fn(&RuntimeEffect, Area) -> Option<Shader> + Send + Sync + 'static,
37    {
38        Self::from_provider(sksl, effect, provider)
39    }
40
41    pub fn from_provider<S>(sksl: impl Into<Arc<str>>, effect: RuntimeEffect, provider: S) -> Self
42    where
43        S: ShaderProvider + 'static,
44    {
45        Self {
46            sksl: sksl.into(),
47            effect: Arc::new(SharedRuntimeEffect(effect)),
48            provider: Arc::new(provider),
49        }
50    }
51
52    /// Prepare the shader for use by providing the necessary uniforms.
53    /// Returns [None] if the provider could not produce a [Shader], in which case the renderer will fallback to no fill.
54    pub fn prepare_shader(&self, bounds: Area) -> Option<Shader> {
55        self.provider.prepare_shader(&self.effect.0, bounds)
56    }
57}
58
59impl std::fmt::Display for ShaderFill {
60    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
61        write!(f, "shader({:p})", Arc::as_ptr(&self.provider))
62    }
63}
64
65impl std::fmt::Debug for ShaderFill {
66    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
67        f.debug_struct("FillShader")
68            .field("sksl", &self.sksl)
69            .finish()
70    }
71}
72
73impl PartialEq for ShaderFill {
74    fn eq(&self, other: &Self) -> bool {
75        *self.sksl == *other.sksl
76            && Arc::ptr_eq(&self.effect, &other.effect)
77            && Arc::ptr_eq(&self.provider, &other.provider)
78    }
79}
80
81#[cfg(feature = "serde")]
82impl serde::Serialize for ShaderFill {
83    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
84    where
85        S: serde::Serializer,
86    {
87        serializer.serialize_str(&self.sksl)
88    }
89}
90
91#[cfg(feature = "serde")]
92impl<'de> serde::Deserialize<'de> for ShaderFill {
93    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
94    where
95        D: serde::Deserializer<'de>,
96    {
97        let sksl = String::deserialize(deserializer)?;
98        let effect =
99            RuntimeEffect::make_for_shader(&sksl, None).map_err(serde::de::Error::custom)?;
100
101        Ok(Self {
102            sksl: sksl.into(),
103            effect: Arc::new(SharedRuntimeEffect(effect)),
104            provider: Arc::new(|_: &RuntimeEffect, _: Area| None),
105        })
106    }
107}