1use std::sync::{Arc, Mutex, OnceLock, PoisonError, Weak};
7
8use arrayvec::ArrayVec;
9
10use crate::{Color, LayerShape, Rect};
11
12const RUNTIME_SHADER_INLINE_UNIFORMS: usize = 16;
13
14#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
16pub enum TileMode {
17 #[default]
19 Clamp,
20 Repeated,
22 Mirror,
24 Decal,
26}
27
28#[derive(Clone, Copy, Debug, PartialEq)]
34pub struct BlurredEdgeTreatment {
35 shape: Option<LayerShape>,
36}
37
38impl BlurredEdgeTreatment {
39 pub const RECTANGLE: Self = Self {
41 shape: Some(LayerShape::Rectangle),
42 };
43
44 pub const UNBOUNDED: Self = Self { shape: None };
46
47 pub const fn with_shape(shape: LayerShape) -> Self {
49 Self { shape: Some(shape) }
50 }
51
52 pub fn shape(self) -> Option<LayerShape> {
53 self.shape
54 }
55
56 pub fn clip(self) -> bool {
57 self.shape.is_some()
58 }
59
60 pub fn tile_mode(self) -> TileMode {
61 if self.clip() {
62 TileMode::Clamp
63 } else {
64 TileMode::Decal
65 }
66 }
67}
68
69impl Default for BlurredEdgeTreatment {
70 fn default() -> Self {
71 Self::RECTANGLE
72 }
73}
74
75pub const RUNTIME_SHADER_PRELUDE_WGSL: &str = framework_wgsl!("runtime_shader_prelude.wgsl");
81
82#[derive(Clone, Debug)]
129pub struct RuntimeShader {
130 source: Arc<str>,
131 source_hash: u64,
132 uniforms: RuntimeShaderUniforms,
133 specialization: Option<Arc<ShaderSpecialization>>,
134 input_padding: f32,
135 output_padding: f32,
136 batched_source: bool,
137 position_independent: bool,
138 preserves_transparency: bool,
139 domains: Option<Box<ShaderDomains>>,
140 placeholder: Option<ShaderPlaceholder>,
141}
142
143#[derive(Clone, Copy, Debug, PartialEq)]
146pub struct ShaderPlaceholder {
147 pub color: Color,
149 pub shape: Option<PlaceholderShape>,
152}
153
154#[derive(Clone, Copy, Debug, PartialEq)]
156pub struct PlaceholderShape {
157 pub bounds: Rect,
159 pub corner_radius: f32,
162}
163
164#[derive(Clone, Debug, Default)]
165struct ShaderSpecialization {
166 overrides: Vec<(&'static str, f64)>,
167 overrides_hash: OnceLock<u64>,
168 substrates: ArrayVec<SubstrateSpec, MAX_SUBSTRATES>,
169 draw_split: Option<&'static str>,
170 exact: bool,
171 large_draws: Option<LargeDrawSpecialization>,
172}
173
174impl ShaderSpecialization {
175 fn overrides_hash(&self) -> u64 {
176 if self.overrides.is_empty() {
177 return 0;
178 }
179 *self.overrides_hash.get_or_init(|| {
180 #[cfg(test)]
181 OVERRIDE_HASH_COMPUTATIONS.with(|count| count.set(count.get() + 1));
182 runtime_shader_overrides_hash(self.overrides.iter().copied())
183 })
184 }
185}
186
187#[derive(Clone, Debug)]
190struct LargeDrawSpecialization {
191 min_pixels: u64,
192 specialization: Arc<ShaderSpecialization>,
193}
194
195#[derive(Clone, Copy, Debug)]
198pub struct DrawSpecialization<'a> {
199 specialization: &'a ShaderSpecialization,
200}
201
202impl<'a> DrawSpecialization<'a> {
203 pub fn overrides(self) -> &'a [(&'static str, f64)] {
205 &self.specialization.overrides
206 }
207
208 pub fn overrides_hash(self) -> u64 {
210 self.specialization.overrides_hash()
211 }
212
213 pub fn draw_split(self) -> Option<&'static str> {
215 self.specialization.draw_split
216 }
217
218 pub fn exact(self) -> bool {
221 self.specialization.exact
222 }
223}
224
225pub(crate) struct ShaderSpecializationCache<K, const N: usize> {
226 entries: ArrayVec<CachedShaderSpecialization<K>, N>,
227}
228
229struct CachedShaderSpecialization<K> {
230 source: Option<Arc<ShaderSpecialization>>,
231 key: K,
232 result: Option<Arc<ShaderSpecialization>>,
233}
234
235impl<K: PartialEq, const N: usize> ShaderSpecializationCache<K, N> {
236 pub(crate) const fn new() -> Self {
237 assert!(N > 0);
238 Self {
239 entries: ArrayVec::new_const(),
240 }
241 }
242
243 pub(crate) fn apply(
244 &mut self,
245 shader: &mut RuntimeShader,
246 key: K,
247 specialize: impl FnOnce(&mut RuntimeShader, &K),
248 ) {
249 let hit = self.entries.iter().rposition(|entry| {
250 entry.key == key
251 && match (&entry.source, &shader.specialization) {
252 (Some(source), Some(current)) => Arc::ptr_eq(source, current),
253 (None, None) => true,
254 _ => false,
255 }
256 });
257 if let Some(index) = hit {
258 let entry = self.entries.remove(index);
259 shader.specialization.clone_from(&entry.result);
260 self.entries.push(entry);
261 return;
262 }
263 if shader
264 .specialization
265 .as_ref()
266 .is_some_and(|source| Arc::strong_count(source) == 1)
267 {
268 specialize(shader, &key);
269 return;
270 }
271 let source = shader.specialization.clone();
272 specialize(shader, &key);
273 if self.entries.is_full() {
274 self.entries.remove(0);
275 }
276 self.entries.push(CachedShaderSpecialization {
277 source,
278 key,
279 result: shader.specialization.clone(),
280 });
281 }
282}
283
284static DEFAULT_SHADER_SPECIALIZATION: ShaderSpecialization = ShaderSpecialization {
285 overrides: Vec::new(),
286 overrides_hash: OnceLock::new(),
287 substrates: ArrayVec::new_const(),
288 draw_split: None,
289 exact: false,
290 large_draws: None,
291};
292
293#[derive(Clone, Copy, Debug, Default, PartialEq)]
294struct ShaderDomains {
295 output_support: Option<Rect>,
296 sample_domain: Option<Rect>,
297}
298
299fn finite_rect(rect: Option<Rect>) -> Option<Rect> {
300 rect.filter(|rect| {
301 rect.x.is_finite()
302 && rect.y.is_finite()
303 && rect.width.is_finite()
304 && rect.height.is_finite()
305 })
306}
307
308pub const MAX_SUBSTRATES: usize = 3;
310
311#[derive(Clone, Copy, Debug, PartialEq)]
314pub enum SubstrateSpec {
315 Mean,
320 Average { block: u32 },
323 Blur { radius_px: f32 },
326}
327
328impl SubstrateSpec {
329 fn same_bits(&self, other: &Self) -> bool {
330 match (self, other) {
331 (Self::Mean, Self::Mean) => true,
332 (Self::Average { block: a }, Self::Average { block: b }) => a == b,
333 (Self::Blur { radius_px: a }, Self::Blur { radius_px: b }) => {
334 a.to_bits() == b.to_bits()
335 }
336 _ => false,
337 }
338 }
339
340 fn hash_bits<H: std::hash::Hasher>(&self, state: &mut H) {
341 use std::hash::Hash;
342 match self {
343 Self::Mean => 2u8.hash(state),
344 Self::Average { block } => {
345 0u8.hash(state);
346 block.hash(state);
347 }
348 Self::Blur { radius_px } => {
349 1u8.hash(state);
350 radius_px.to_bits().hash(state);
351 }
352 }
353 }
354}
355
356#[derive(Clone, Debug, PartialEq)]
357struct RuntimeShaderUniforms {
358 len: usize,
359 inline: [f32; RUNTIME_SHADER_INLINE_UNIFORMS],
360 heap: Option<Vec<f32>>,
361}
362
363impl RuntimeShaderUniforms {
364 fn new() -> Self {
365 Self {
366 len: 0,
367 inline: [0.0; RUNTIME_SHADER_INLINE_UNIFORMS],
368 heap: None,
369 }
370 }
371
372 fn as_slice(&self) -> &[f32] {
373 if let Some(heap) = &self.heap {
374 heap.as_slice()
375 } else {
376 &self.inline[..self.len]
377 }
378 }
379
380 fn len(&self) -> usize {
381 self.as_slice().len()
382 }
383
384 fn ensure_len(&mut self, min_len: usize) {
385 if let Some(heap) = &mut self.heap {
386 if heap.len() < min_len {
387 heap.resize(min_len, 0.0);
388 }
389 return;
390 }
391
392 if min_len <= RUNTIME_SHADER_INLINE_UNIFORMS {
393 self.len = self.len.max(min_len);
394 return;
395 }
396
397 let mut heap = Vec::with_capacity(min_len);
398 heap.extend_from_slice(&self.inline[..self.len]);
399 heap.resize(min_len, 0.0);
400 self.heap = Some(heap);
401 }
402
403 fn set(&mut self, index: usize, value: f32) {
404 if let Some(heap) = &mut self.heap {
405 heap[index] = value;
406 } else {
407 self.inline[index] = value;
408 }
409 }
410
411 #[cfg(test)]
412 fn is_inline(&self) -> bool {
413 self.heap.is_none()
414 }
415}
416
417#[derive(Clone, Copy, Debug, Eq, PartialEq, thiserror::Error)]
419pub enum RuntimeShaderUniformError {
420 #[error(
421 "uniform range starting at {index} with width {width} exceeds user uniform range 0..{max_user_uniforms}; slots {reserved_start}..{max_uniforms} are reserved for renderer data"
422 )]
423 OutOfUserRange {
424 index: usize,
425 width: usize,
426 max_user_uniforms: usize,
427 reserved_start: usize,
428 max_uniforms: usize,
429 },
430}
431
432impl RuntimeShader {
433 pub const MAX_UNIFORMS: usize = 256;
437 pub const RESERVED_UNIFORM_START: usize = 220;
439 pub const QUAD_PLACEMENT_UNIFORM: usize = 220;
445 pub const SUBSTRATE_REGION_UNIFORMS: [usize; MAX_SUBSTRATES] = [232, 228, 224];
448 pub const SOURCE_REGION_UNIFORM: usize = 236;
450 pub const MASK_RECT_UNIFORM: usize = 240;
452 pub const MASK_RADII_UNIFORM: usize = 244;
454 pub const EFFECT_RECT_UNIFORM: usize = 248;
456 pub const LOGICAL_SIZE_UNIFORM: usize = 252;
458 pub const ALPHA_UNIFORM: usize = 254;
460 pub const MAX_USER_UNIFORMS: usize = Self::RESERVED_UNIFORM_START;
462
463 #[track_caller]
465 pub fn new(wgsl_source: &str) -> Self {
466 let (source, source_hash) =
467 cached_shader_source(std::panic::Location::caller(), wgsl_source);
468 Self::with_source(source, source_hash)
469 }
470
471 pub fn from_shared_source(source: Arc<str>) -> Self {
476 let source_hash = cached_shared_shader_source_hash(&source);
477 Self::with_source(source, source_hash)
478 }
479
480 fn with_source(source: Arc<str>, source_hash: u64) -> Self {
481 Self {
482 source,
483 source_hash,
484 uniforms: RuntimeShaderUniforms::new(),
485 specialization: None,
486 input_padding: 0.0,
487 output_padding: 0.0,
488 batched_source: false,
489 position_independent: false,
490 preserves_transparency: false,
491 domains: None,
492 placeholder: None,
493 }
494 }
495
496 fn specialization(&self) -> &ShaderSpecialization {
497 self.specialization
498 .as_deref()
499 .unwrap_or(&DEFAULT_SHADER_SPECIALIZATION)
500 }
501
502 fn specialization_mut(&mut self) -> &mut ShaderSpecialization {
503 Arc::make_mut(self.specialization.get_or_insert_with(Arc::default))
504 }
505
506 pub fn set_override(&mut self, name: &'static str, value: f64) {
520 self.clear_large_draws();
521 let position = self
522 .overrides()
523 .binary_search_by(|(existing, _)| existing.cmp(&name));
524 if position.is_ok_and(|index| self.overrides()[index].1.to_bits() == value.to_bits()) {
525 return;
526 }
527 let specialization = self.specialization_mut();
528 specialization.overrides_hash.take();
529 let overrides = &mut specialization.overrides;
530 match position {
531 Ok(index) => overrides[index].1 = value,
532 Err(index) => overrides.insert(index, (name, value)),
533 }
534 }
535
536 pub fn clear_override(&mut self, name: &str) -> bool {
538 self.clear_large_draws();
539 let Ok(index) = self
540 .overrides()
541 .binary_search_by(|(existing, _)| (*existing).cmp(name))
542 else {
543 return false;
544 };
545 let specialization = self.specialization_mut();
546 specialization.overrides_hash.take();
547 specialization.overrides.remove(index);
548 true
549 }
550
551 pub fn overrides(&self) -> &[(&'static str, f64)] {
554 &self.specialization().overrides
555 }
556
557 pub fn overrides_hash(&self) -> u64 {
559 self.specialization().overrides_hash()
560 }
561
562 pub fn draw_specialization(&self, pixels: u64) -> DrawSpecialization<'_> {
566 let own = self.specialization();
567 let specialization = match &own.large_draws {
568 Some(large) if pixels >= large.min_pixels => &large.specialization,
569 _ => own,
570 };
571 DrawSpecialization { specialization }
572 }
573
574 pub(crate) fn specialize_with_large_draws(
578 &mut self,
579 min_pixels: u64,
580 large: impl FnOnce(&mut RuntimeShader),
581 own: impl FnOnce(&mut RuntimeShader),
582 ) {
583 let source = self.specialization.clone();
584 large(self);
585 let large = std::mem::replace(&mut self.specialization, source);
586 own(self);
587 self.specialization_mut().large_draws =
588 large.map(|specialization| LargeDrawSpecialization {
589 min_pixels,
590 specialization,
591 });
592 }
593
594 pub(crate) fn clear_large_draws(&mut self) {
599 if self.specialization().large_draws.is_some() {
600 self.specialization_mut().large_draws = None;
601 }
602 }
603
604 pub fn set_input_padding(&mut self, padding: f32) {
608 self.input_padding = if padding.is_finite() {
609 padding.max(0.0)
610 } else {
611 0.0
612 };
613 }
614
615 pub fn input_padding(&self) -> f32 {
617 self.input_padding
618 }
619
620 pub fn set_output_padding(&mut self, padding: f32) {
625 self.output_padding = if padding.is_finite() {
626 padding.max(0.0)
627 } else {
628 0.0
629 };
630 }
631
632 pub fn output_padding(&self) -> f32 {
634 self.output_padding
635 }
636
637 pub fn set_output_support(&mut self, support: Option<Rect>) {
648 self.set_domains(ShaderDomains {
649 output_support: finite_rect(support),
650 sample_domain: self.sample_domain(),
651 });
652 }
653
654 pub fn output_support(&self) -> Option<Rect> {
656 self.domains
657 .as_ref()
658 .and_then(|domains| domains.output_support)
659 }
660
661 fn set_domains(&mut self, domains: ShaderDomains) {
662 self.domains = (domains != ShaderDomains::default()).then(|| Box::new(domains));
663 }
664
665 pub fn set_sample_domain(&mut self, domain: Option<Rect>) {
674 self.set_domains(ShaderDomains {
675 output_support: self.output_support(),
676 sample_domain: finite_rect(domain),
677 });
678 }
679
680 pub fn sample_domain(&self) -> Option<Rect> {
682 self.domains
683 .as_ref()
684 .and_then(|domains| domains.sample_domain)
685 }
686
687 pub fn set_float(&mut self, index: usize, value: f32) {
692 let _ = self.try_set_float(index, value);
693 }
694
695 pub fn try_set_float(
697 &mut self,
698 index: usize,
699 value: f32,
700 ) -> Result<(), RuntimeShaderUniformError> {
701 self.try_ensure_capacity(index, 1)?;
702 self.uniforms.set(index, value);
703 Ok(())
704 }
705
706 pub fn set_float2(&mut self, index: usize, x: f32, y: f32) {
711 let _ = self.try_set_float2(index, x, y);
712 }
713
714 pub fn try_set_float2(
716 &mut self,
717 index: usize,
718 x: f32,
719 y: f32,
720 ) -> Result<(), RuntimeShaderUniformError> {
721 self.try_ensure_capacity(index, 2)?;
722 self.uniforms.set(index, x);
723 self.uniforms.set(index + 1, y);
724 Ok(())
725 }
726
727 pub fn set_float4(&mut self, index: usize, x: f32, y: f32, z: f32, w: f32) {
732 let _ = self.try_set_float4(index, x, y, z, w);
733 }
734
735 pub fn try_set_float4(
737 &mut self,
738 index: usize,
739 x: f32,
740 y: f32,
741 z: f32,
742 w: f32,
743 ) -> Result<(), RuntimeShaderUniformError> {
744 self.try_ensure_capacity(index, 4)?;
745 self.uniforms.set(index, x);
746 self.uniforms.set(index + 1, y);
747 self.uniforms.set(index + 2, z);
748 self.uniforms.set(index + 3, w);
749 Ok(())
750 }
751
752 pub fn set_batched_source(&mut self, batched: bool) {
757 self.batched_source = batched;
758 }
759
760 pub fn batched_source(&self) -> bool {
763 self.batched_source
764 }
765
766 pub fn set_position_independent(&mut self, independent: bool) {
773 self.position_independent = independent;
774 }
775
776 pub fn position_independent(&self) -> bool {
778 self.position_independent
779 }
780
781 pub fn set_preserves_transparency(&mut self, preserves: bool) {
786 self.preserves_transparency = preserves;
787 }
788
789 pub fn preserves_transparency(&self) -> bool {
791 self.preserves_transparency
792 }
793
794 pub fn set_placeholder(&mut self, placeholder: Option<ShaderPlaceholder>) {
800 self.placeholder = placeholder;
801 }
802
803 pub fn placeholder(&self) -> Option<ShaderPlaceholder> {
805 self.placeholder
806 }
807
808 pub fn set_substrates(&mut self, substrates: &[SubstrateSpec]) {
817 assert!(
818 substrates.len() <= MAX_SUBSTRATES,
819 "a runtime shader declares at most {MAX_SUBSTRATES} substrates"
820 );
821 self.clear_large_draws();
822 if self.substrates().len() == substrates.len()
823 && self
824 .substrates()
825 .iter()
826 .zip(substrates)
827 .all(|(existing, incoming)| existing.same_bits(incoming))
828 {
829 return;
830 }
831 self.specialization_mut().substrates = substrates.iter().copied().collect();
832 }
833
834 pub fn substrates(&self) -> &[SubstrateSpec] {
836 &self.specialization().substrates
837 }
838
839 pub fn hash_substrates<H: std::hash::Hasher>(&self, state: &mut H) {
841 use std::hash::Hash;
842 self.substrates().len().hash(state);
843 for substrate in self.substrates() {
844 substrate.hash_bits(state);
845 }
846 self.draw_split().hash(state);
847 }
848
849 pub fn set_draw_split(&mut self, override_name: Option<&'static str>) {
856 self.clear_large_draws();
857 if self.draw_split() == override_name {
858 return;
859 }
860 self.specialization_mut().draw_split = override_name;
861 }
862
863 pub fn draw_split(&self) -> Option<&'static str> {
865 self.specialization().draw_split
866 }
867
868 pub fn set_specialization_exact(&mut self, exact: bool) {
878 self.clear_large_draws();
879 if self.specialization_exact() == exact {
880 return;
881 }
882 self.specialization_mut().exact = exact;
883 }
884
885 pub fn specialization_exact(&self) -> bool {
887 self.specialization().exact
888 }
889
890 pub fn source(&self) -> &str {
892 &self.source
893 }
894
895 pub fn uniforms(&self) -> &[f32] {
897 self.uniforms.as_slice()
898 }
899
900 pub fn uniforms_padded(&self) -> [f32; Self::MAX_UNIFORMS] {
902 let mut padded = [0.0f32; Self::MAX_UNIFORMS];
903 let len = self.uniforms.len().min(Self::MAX_UNIFORMS);
904 padded[..len].copy_from_slice(&self.uniforms.as_slice()[..len]);
905 padded
906 }
907
908 pub fn source_hash(&self) -> u64 {
910 self.source_hash
911 }
912
913 fn try_ensure_capacity(
914 &mut self,
915 index: usize,
916 width: usize,
917 ) -> Result<(), RuntimeShaderUniformError> {
918 let min_len = index
919 .checked_add(width)
920 .ok_or_else(|| Self::uniform_range_error(index, width))?;
921 if min_len > Self::MAX_USER_UNIFORMS {
922 return Err(Self::uniform_range_error(index, width));
923 }
924 self.uniforms.ensure_len(min_len);
925 Ok(())
926 }
927
928 fn uniform_range_error(index: usize, width: usize) -> RuntimeShaderUniformError {
929 RuntimeShaderUniformError::OutOfUserRange {
930 index,
931 width,
932 max_user_uniforms: Self::MAX_USER_UNIFORMS,
933 reserved_start: Self::RESERVED_UNIFORM_START,
934 max_uniforms: Self::MAX_UNIFORMS,
935 }
936 }
937}
938
939#[cfg(test)]
940thread_local! {
941 static OVERRIDE_HASH_COMPUTATIONS: std::cell::Cell<usize> = const { std::cell::Cell::new(0) };
942}
943
944impl PartialEq for RuntimeShader {
945 fn eq(&self, other: &Self) -> bool {
946 self.source_hash == other.source_hash
947 && (Arc::ptr_eq(&self.source, &other.source)
948 || self.source.as_ref() == other.source.as_ref())
949 && self.uniforms == other.uniforms
950 && self.overrides().len() == other.overrides().len()
951 && self
952 .overrides()
953 .iter()
954 .zip(other.overrides())
955 .all(|(a, b)| a.0 == b.0 && a.1.to_bits() == b.1.to_bits())
956 && self.input_padding.to_bits() == other.input_padding.to_bits()
957 && self.output_padding.to_bits() == other.output_padding.to_bits()
958 && self.batched_source == other.batched_source
959 && self.position_independent == other.position_independent
960 && self.preserves_transparency == other.preserves_transparency
961 && self.substrates() == other.substrates()
962 && self.draw_split() == other.draw_split()
963 && self.domains == other.domains
964 && self.placeholder == other.placeholder
965 }
966}
967
968pub fn runtime_shader_source_hash(source: &str) -> u64 {
971 hash_shader_bytes(source.bytes())
972}
973
974pub fn runtime_shader_overrides_hash<'a>(
977 overrides: impl IntoIterator<Item = (&'a str, f64)>,
978) -> u64 {
979 hash_shader_bytes(
980 overrides
981 .into_iter()
982 .flat_map(|(name, value)| name.bytes().chain([0]).chain(value.to_bits().to_le_bytes())),
983 )
984}
985
986fn hash_shader_bytes(bytes: impl IntoIterator<Item = u8>) -> u64 {
987 const FNV_OFFSET_BASIS: u64 = 0xcbf2_9ce4_8422_2325;
988 const FNV_PRIME: u64 = 0x0000_0100_0000_01b3;
989
990 bytes.into_iter().fold(FNV_OFFSET_BASIS, |hash, byte| {
991 (hash ^ u64::from(byte)).wrapping_mul(FNV_PRIME)
992 })
993}
994
995#[derive(Clone, Copy, Debug, PartialEq, Eq)]
996struct ShaderSourceCallsite {
997 file: &'static str,
998 line: u32,
999 column: u32,
1000}
1001
1002struct CachedShaderSource {
1003 callsite: ShaderSourceCallsite,
1004 source_hash: u64,
1005 source: Arc<str>,
1006}
1007
1008struct CachedSharedShaderSourceHash {
1009 byte_ptr: usize,
1010 len: usize,
1011 source_hash: u64,
1012 source: Weak<str>,
1013}
1014
1015fn cached_shared_shader_source_hash(source: &Arc<str>) -> u64 {
1016 static CACHE: OnceLock<Mutex<Vec<CachedSharedShaderSourceHash>>> = OnceLock::new();
1017 let byte_ptr = source.as_ptr() as usize;
1018 let len = source.len();
1019 let mut cache = CACHE
1020 .get_or_init(|| Mutex::new(Vec::new()))
1021 .lock()
1022 .unwrap_or_else(PoisonError::into_inner);
1023
1024 cache.retain(|entry| entry.source.strong_count() > 0);
1025 if let Some(entry) = cache.iter().find(|entry| {
1026 entry.byte_ptr == byte_ptr
1027 && entry.len == len
1028 && entry
1029 .source
1030 .upgrade()
1031 .is_some_and(|cached| Arc::ptr_eq(&cached, source))
1032 }) {
1033 return entry.source_hash;
1034 }
1035
1036 let source_hash = runtime_shader_source_hash(source);
1037 cache.push(CachedSharedShaderSourceHash {
1038 byte_ptr,
1039 len,
1040 source_hash,
1041 source: Arc::downgrade(source),
1042 });
1043 source_hash
1044}
1045
1046fn cached_shader_source(
1047 location: &'static std::panic::Location<'static>,
1048 source: &str,
1049) -> (Arc<str>, u64) {
1050 static CACHE: OnceLock<Mutex<Vec<CachedShaderSource>>> = OnceLock::new();
1051 let callsite = ShaderSourceCallsite {
1052 file: location.file(),
1053 line: location.line(),
1054 column: location.column(),
1055 };
1056 let mut cache = CACHE
1057 .get_or_init(|| Mutex::new(Vec::new()))
1058 .lock()
1059 .unwrap_or_else(PoisonError::into_inner);
1060
1061 if let Some(entry) = cache.iter_mut().find(|entry| entry.callsite == callsite) {
1062 if entry.source.as_ref() == source {
1063 return (entry.source.clone(), entry.source_hash);
1064 }
1065 let source_hash = runtime_shader_source_hash(source);
1066 entry.source_hash = source_hash;
1067 entry.source = Arc::<str>::from(source);
1068 return (entry.source.clone(), entry.source_hash);
1069 }
1070
1071 let source_hash = runtime_shader_source_hash(source);
1072 let shared = Arc::<str>::from(source);
1073 cache.push(CachedShaderSource {
1074 callsite,
1075 source_hash,
1076 source: shared.clone(),
1077 });
1078 (shared, source_hash)
1079}
1080
1081#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
1088pub enum ShaderTarget {
1089 Page,
1090 Layer,
1091}
1092
1093#[derive(Clone, Debug, PartialEq)]
1097pub struct ShaderWarmUp {
1098 pub shader: RuntimeShader,
1099 pub target: ShaderTarget,
1100}
1101
1102#[derive(Clone, Debug, PartialEq)]
1107pub enum RenderEffect {
1108 Blur {
1110 radius_x: f32,
1111 radius_y: f32,
1112 edge_treatment: TileMode,
1113 },
1114 Offset { offset_x: f32, offset_y: f32 },
1116 Shader {
1118 shader: Arc<RuntimeShader>,
1120 },
1121 Chain {
1125 first: Arc<RenderEffect>,
1126 second: Arc<RenderEffect>,
1127 },
1128}
1129
1130impl RenderEffect {
1131 pub fn placeholder(&self) -> Option<ShaderPlaceholder> {
1134 match self {
1135 RenderEffect::Shader { shader } => shader.placeholder(),
1136 RenderEffect::Chain { second, .. } => second.placeholder(),
1137 _ => None,
1138 }
1139 }
1140
1141 pub fn blur(radius: f32) -> Self {
1143 Self::blur_with_edge_treatment(radius, TileMode::default())
1144 }
1145
1146 pub fn blur_with_edge_treatment(radius: f32, edge_treatment: TileMode) -> Self {
1149 Self::Blur {
1150 radius_x: radius,
1151 radius_y: radius,
1152 edge_treatment,
1153 }
1154 }
1155
1156 pub fn blur_xy(radius_x: f32, radius_y: f32, edge_treatment: TileMode) -> Self {
1158 Self::Blur {
1159 radius_x,
1160 radius_y,
1161 edge_treatment,
1162 }
1163 }
1164
1165 pub fn offset(offset_x: f32, offset_y: f32) -> Self {
1167 Self::Offset { offset_x, offset_y }
1168 }
1169
1170 pub fn runtime_shader(shader: RuntimeShader) -> Self {
1172 Self::Shader {
1173 shader: Arc::new(shader),
1174 }
1175 }
1176
1177 pub fn then(self, other: RenderEffect) -> Self {
1179 Self::Chain {
1180 first: Arc::new(self),
1181 second: Arc::new(other),
1182 }
1183 }
1184
1185 pub fn contains_runtime_shader(&self) -> bool {
1189 match self {
1190 RenderEffect::Shader { .. } => true,
1191 RenderEffect::Chain { first, second } => {
1192 first.contains_runtime_shader() || second.contains_runtime_shader()
1193 }
1194 _ => false,
1195 }
1196 }
1197
1198 pub fn preserves_transparency(&self) -> bool {
1202 match self {
1203 RenderEffect::Blur { .. } | RenderEffect::Offset { .. } => true,
1204 RenderEffect::Shader { shader } => shader.preserves_transparency(),
1205 RenderEffect::Chain { first, second } => {
1206 first.preserves_transparency() && second.preserves_transparency()
1207 }
1208 }
1209 }
1210
1211 pub fn input_padding(&self) -> f32 {
1213 match self {
1214 RenderEffect::Blur {
1215 radius_x, radius_y, ..
1216 } => radius_x.abs().max(radius_y.abs()),
1217 RenderEffect::Offset { offset_x, offset_y } => offset_x.abs().max(offset_y.abs()),
1218 RenderEffect::Shader { shader } => shader.input_padding(),
1219 RenderEffect::Chain { first, second } => first.input_padding() + second.input_padding(),
1220 }
1221 }
1222
1223 pub fn output_padding(&self) -> f32 {
1227 match self {
1228 RenderEffect::Blur { .. } | RenderEffect::Offset { .. } => 0.0,
1229 RenderEffect::Shader { shader } => shader.output_padding(),
1230 RenderEffect::Chain { first, second } => {
1231 first.output_padding() + second.output_padding()
1232 }
1233 }
1234 }
1235
1236 pub fn output_support(&self) -> Option<Rect> {
1241 match self {
1242 RenderEffect::Blur { .. } | RenderEffect::Offset { .. } => None,
1243 RenderEffect::Shader { shader } => shader.output_support(),
1244 RenderEffect::Chain { second, .. } => second.output_support(),
1245 }
1246 }
1247
1248 pub fn sample_domain(&self) -> Option<Rect> {
1252 match self {
1253 RenderEffect::Blur { .. } | RenderEffect::Offset { .. } => None,
1254 RenderEffect::Shader { shader } => shader.sample_domain(),
1255 RenderEffect::Chain { second, .. } => second.sample_domain(),
1256 }
1257 }
1258}
1259
1260#[cfg(test)]
1261#[path = "tests/render_effect_tests.rs"]
1262mod tests;