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 crate::thread_state::with(|state| {
182 let count = &state.override_hash_computations;
183 count.set(count.get() + 1);
184 });
185 runtime_shader_overrides_hash(self.overrides.iter().copied())
186 })
187 }
188}
189
190#[derive(Clone, Debug)]
193struct LargeDrawSpecialization {
194 min_pixels: u64,
195 specialization: Arc<ShaderSpecialization>,
196}
197
198#[derive(Clone, Copy, Debug)]
201pub struct DrawSpecialization<'a> {
202 specialization: &'a ShaderSpecialization,
203}
204
205impl<'a> DrawSpecialization<'a> {
206 pub fn overrides(self) -> &'a [(&'static str, f64)] {
208 &self.specialization.overrides
209 }
210
211 pub fn overrides_hash(self) -> u64 {
213 self.specialization.overrides_hash()
214 }
215
216 pub fn draw_split(self) -> Option<&'static str> {
218 self.specialization.draw_split
219 }
220
221 pub fn exact(self) -> bool {
224 self.specialization.exact
225 }
226}
227
228pub(crate) struct ShaderSpecializationCache<K, const N: usize> {
229 entries: ArrayVec<CachedShaderSpecialization<K>, N>,
230}
231
232struct CachedShaderSpecialization<K> {
233 source: Option<Arc<ShaderSpecialization>>,
234 key: K,
235 result: Option<Arc<ShaderSpecialization>>,
236}
237
238impl<K: PartialEq, const N: usize> ShaderSpecializationCache<K, N> {
239 pub(crate) const fn new() -> Self {
240 assert!(N > 0);
241 Self {
242 entries: ArrayVec::new_const(),
243 }
244 }
245
246 pub(crate) fn apply(
247 &mut self,
248 shader: &mut RuntimeShader,
249 key: K,
250 specialize: impl FnOnce(&mut RuntimeShader, &K),
251 ) {
252 let hit = self.entries.iter().rposition(|entry| {
253 entry.key == key
254 && match (&entry.source, &shader.specialization) {
255 (Some(source), Some(current)) => Arc::ptr_eq(source, current),
256 (None, None) => true,
257 _ => false,
258 }
259 });
260 if let Some(index) = hit {
261 let entry = self.entries.remove(index);
262 shader.specialization.clone_from(&entry.result);
263 self.entries.push(entry);
264 return;
265 }
266 if shader
267 .specialization
268 .as_ref()
269 .is_some_and(|source| Arc::strong_count(source) == 1)
270 {
271 specialize(shader, &key);
272 return;
273 }
274 let source = shader.specialization.clone();
275 specialize(shader, &key);
276 if self.entries.is_full() {
277 self.entries.remove(0);
278 }
279 self.entries.push(CachedShaderSpecialization {
280 source,
281 key,
282 result: shader.specialization.clone(),
283 });
284 }
285}
286
287static DEFAULT_SHADER_SPECIALIZATION: ShaderSpecialization = ShaderSpecialization {
288 overrides: Vec::new(),
289 overrides_hash: OnceLock::new(),
290 substrates: ArrayVec::new_const(),
291 draw_split: None,
292 exact: false,
293 large_draws: None,
294};
295
296#[derive(Clone, Copy, Debug, Default, PartialEq)]
297struct ShaderDomains {
298 output_support: Option<Rect>,
299 sample_domain: Option<Rect>,
300}
301
302fn finite_rect(rect: Option<Rect>) -> Option<Rect> {
303 rect.filter(|rect| {
304 rect.x.is_finite()
305 && rect.y.is_finite()
306 && rect.width.is_finite()
307 && rect.height.is_finite()
308 })
309}
310
311pub const MAX_SUBSTRATES: usize = 3;
313
314#[derive(Clone, Copy, Debug, PartialEq)]
317pub enum SubstrateSpec {
318 Mean,
323 Average { block: u32 },
326 Blur { radius_px: f32 },
329}
330
331impl SubstrateSpec {
332 fn same_bits(&self, other: &Self) -> bool {
333 match (self, other) {
334 (Self::Mean, Self::Mean) => true,
335 (Self::Average { block: a }, Self::Average { block: b }) => a == b,
336 (Self::Blur { radius_px: a }, Self::Blur { radius_px: b }) => {
337 a.to_bits() == b.to_bits()
338 }
339 _ => false,
340 }
341 }
342
343 fn hash_bits<H: std::hash::Hasher>(&self, state: &mut H) {
344 use std::hash::Hash;
345 match self {
346 Self::Mean => 2u8.hash(state),
347 Self::Average { block } => {
348 0u8.hash(state);
349 block.hash(state);
350 }
351 Self::Blur { radius_px } => {
352 1u8.hash(state);
353 radius_px.to_bits().hash(state);
354 }
355 }
356 }
357}
358
359#[derive(Clone, Debug, PartialEq)]
360struct RuntimeShaderUniforms {
361 len: usize,
362 inline: [f32; RUNTIME_SHADER_INLINE_UNIFORMS],
363 heap: Option<Vec<f32>>,
364}
365
366impl RuntimeShaderUniforms {
367 fn new() -> Self {
368 Self {
369 len: 0,
370 inline: [0.0; RUNTIME_SHADER_INLINE_UNIFORMS],
371 heap: None,
372 }
373 }
374
375 fn as_slice(&self) -> &[f32] {
376 if let Some(heap) = &self.heap {
377 heap.as_slice()
378 } else {
379 &self.inline[..self.len]
380 }
381 }
382
383 fn len(&self) -> usize {
384 self.as_slice().len()
385 }
386
387 fn ensure_len(&mut self, min_len: usize) {
388 if let Some(heap) = &mut self.heap {
389 if heap.len() < min_len {
390 heap.resize(min_len, 0.0);
391 }
392 return;
393 }
394
395 if min_len <= RUNTIME_SHADER_INLINE_UNIFORMS {
396 self.len = self.len.max(min_len);
397 return;
398 }
399
400 let mut heap = Vec::with_capacity(min_len);
401 heap.extend_from_slice(&self.inline[..self.len]);
402 heap.resize(min_len, 0.0);
403 self.heap = Some(heap);
404 }
405
406 fn set(&mut self, index: usize, value: f32) {
407 if let Some(heap) = &mut self.heap {
408 heap[index] = value;
409 } else {
410 self.inline[index] = value;
411 }
412 }
413
414 #[cfg(test)]
415 fn is_inline(&self) -> bool {
416 self.heap.is_none()
417 }
418}
419
420#[derive(Clone, Copy, Debug, Eq, PartialEq, thiserror::Error)]
422pub enum RuntimeShaderUniformError {
423 #[error(
424 "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"
425 )]
426 OutOfUserRange {
427 index: usize,
428 width: usize,
429 max_user_uniforms: usize,
430 reserved_start: usize,
431 max_uniforms: usize,
432 },
433}
434
435impl RuntimeShader {
436 pub const MAX_UNIFORMS: usize = 256;
440 pub const RESERVED_UNIFORM_START: usize = 220;
442 pub const QUAD_PLACEMENT_UNIFORM: usize = 220;
448 pub const SUBSTRATE_REGION_UNIFORMS: [usize; MAX_SUBSTRATES] = [232, 228, 224];
451 pub const SOURCE_REGION_UNIFORM: usize = 236;
453 pub const MASK_RECT_UNIFORM: usize = 240;
455 pub const MASK_RADII_UNIFORM: usize = 244;
457 pub const EFFECT_RECT_UNIFORM: usize = 248;
459 pub const LOGICAL_SIZE_UNIFORM: usize = 252;
461 pub const ALPHA_UNIFORM: usize = 254;
463 pub const MAX_USER_UNIFORMS: usize = Self::RESERVED_UNIFORM_START;
465
466 #[track_caller]
468 pub fn new(wgsl_source: &str) -> Self {
469 let (source, source_hash) =
470 cached_shader_source(std::panic::Location::caller(), wgsl_source);
471 Self::with_source(source, source_hash)
472 }
473
474 pub fn from_shared_source(source: Arc<str>) -> Self {
479 let source_hash = cached_shared_shader_source_hash(&source);
480 Self::with_source(source, source_hash)
481 }
482
483 fn with_source(source: Arc<str>, source_hash: u64) -> Self {
484 Self {
485 source,
486 source_hash,
487 uniforms: RuntimeShaderUniforms::new(),
488 specialization: None,
489 input_padding: 0.0,
490 output_padding: 0.0,
491 batched_source: false,
492 position_independent: false,
493 preserves_transparency: false,
494 domains: None,
495 placeholder: None,
496 }
497 }
498
499 fn specialization(&self) -> &ShaderSpecialization {
500 self.specialization
501 .as_deref()
502 .unwrap_or(&DEFAULT_SHADER_SPECIALIZATION)
503 }
504
505 fn specialization_mut(&mut self) -> &mut ShaderSpecialization {
506 Arc::make_mut(self.specialization.get_or_insert_with(Arc::default))
507 }
508
509 pub fn set_override(&mut self, name: &'static str, value: f64) {
523 self.clear_large_draws();
524 let position = self
525 .overrides()
526 .binary_search_by(|(existing, _)| existing.cmp(&name));
527 if position.is_ok_and(|index| self.overrides()[index].1.to_bits() == value.to_bits()) {
528 return;
529 }
530 let specialization = self.specialization_mut();
531 specialization.overrides_hash.take();
532 let overrides = &mut specialization.overrides;
533 match position {
534 Ok(index) => overrides[index].1 = value,
535 Err(index) => overrides.insert(index, (name, value)),
536 }
537 }
538
539 pub fn clear_override(&mut self, name: &str) -> bool {
541 self.clear_large_draws();
542 let Ok(index) = self
543 .overrides()
544 .binary_search_by(|(existing, _)| (*existing).cmp(name))
545 else {
546 return false;
547 };
548 let specialization = self.specialization_mut();
549 specialization.overrides_hash.take();
550 specialization.overrides.remove(index);
551 true
552 }
553
554 pub fn overrides(&self) -> &[(&'static str, f64)] {
557 &self.specialization().overrides
558 }
559
560 pub fn overrides_hash(&self) -> u64 {
562 self.specialization().overrides_hash()
563 }
564
565 pub fn draw_specialization(&self, pixels: u64) -> DrawSpecialization<'_> {
569 let own = self.specialization();
570 let specialization = match &own.large_draws {
571 Some(large) if pixels >= large.min_pixels => &large.specialization,
572 _ => own,
573 };
574 DrawSpecialization { specialization }
575 }
576
577 pub(crate) fn specialize_with_large_draws(
581 &mut self,
582 min_pixels: u64,
583 large: impl FnOnce(&mut RuntimeShader),
584 own: impl FnOnce(&mut RuntimeShader),
585 ) {
586 let source = self.specialization.clone();
587 large(self);
588 let large = std::mem::replace(&mut self.specialization, source);
589 own(self);
590 self.specialization_mut().large_draws =
591 large.map(|specialization| LargeDrawSpecialization {
592 min_pixels,
593 specialization,
594 });
595 }
596
597 pub(crate) fn clear_large_draws(&mut self) {
602 if self.specialization().large_draws.is_some() {
603 self.specialization_mut().large_draws = None;
604 }
605 }
606
607 pub fn set_input_padding(&mut self, padding: f32) {
611 self.input_padding = if padding.is_finite() {
612 padding.max(0.0)
613 } else {
614 0.0
615 };
616 }
617
618 pub fn input_padding(&self) -> f32 {
620 self.input_padding
621 }
622
623 pub fn set_output_padding(&mut self, padding: f32) {
628 self.output_padding = if padding.is_finite() {
629 padding.max(0.0)
630 } else {
631 0.0
632 };
633 }
634
635 pub fn output_padding(&self) -> f32 {
637 self.output_padding
638 }
639
640 pub fn set_output_support(&mut self, support: Option<Rect>) {
651 self.set_domains(ShaderDomains {
652 output_support: finite_rect(support),
653 sample_domain: self.sample_domain(),
654 });
655 }
656
657 pub fn output_support(&self) -> Option<Rect> {
659 self.domains
660 .as_ref()
661 .and_then(|domains| domains.output_support)
662 }
663
664 fn set_domains(&mut self, domains: ShaderDomains) {
665 self.domains = (domains != ShaderDomains::default()).then(|| Box::new(domains));
666 }
667
668 pub fn set_sample_domain(&mut self, domain: Option<Rect>) {
677 self.set_domains(ShaderDomains {
678 output_support: self.output_support(),
679 sample_domain: finite_rect(domain),
680 });
681 }
682
683 pub fn sample_domain(&self) -> Option<Rect> {
685 self.domains
686 .as_ref()
687 .and_then(|domains| domains.sample_domain)
688 }
689
690 pub fn set_float(&mut self, index: usize, value: f32) {
695 let _ = self.try_set_float(index, value);
696 }
697
698 pub fn try_set_float(
700 &mut self,
701 index: usize,
702 value: f32,
703 ) -> Result<(), RuntimeShaderUniformError> {
704 self.try_ensure_capacity(index, 1)?;
705 self.uniforms.set(index, value);
706 Ok(())
707 }
708
709 pub fn set_float2(&mut self, index: usize, x: f32, y: f32) {
714 let _ = self.try_set_float2(index, x, y);
715 }
716
717 pub fn try_set_float2(
719 &mut self,
720 index: usize,
721 x: f32,
722 y: f32,
723 ) -> Result<(), RuntimeShaderUniformError> {
724 self.try_ensure_capacity(index, 2)?;
725 self.uniforms.set(index, x);
726 self.uniforms.set(index + 1, y);
727 Ok(())
728 }
729
730 pub fn set_float4(&mut self, index: usize, x: f32, y: f32, z: f32, w: f32) {
735 let _ = self.try_set_float4(index, x, y, z, w);
736 }
737
738 pub fn try_set_float4(
740 &mut self,
741 index: usize,
742 x: f32,
743 y: f32,
744 z: f32,
745 w: f32,
746 ) -> Result<(), RuntimeShaderUniformError> {
747 self.try_ensure_capacity(index, 4)?;
748 self.uniforms.set(index, x);
749 self.uniforms.set(index + 1, y);
750 self.uniforms.set(index + 2, z);
751 self.uniforms.set(index + 3, w);
752 Ok(())
753 }
754
755 pub fn set_batched_source(&mut self, batched: bool) {
760 self.batched_source = batched;
761 }
762
763 pub fn batched_source(&self) -> bool {
766 self.batched_source
767 }
768
769 pub fn set_position_independent(&mut self, independent: bool) {
776 self.position_independent = independent;
777 }
778
779 pub fn position_independent(&self) -> bool {
781 self.position_independent
782 }
783
784 pub fn set_preserves_transparency(&mut self, preserves: bool) {
789 self.preserves_transparency = preserves;
790 }
791
792 pub fn preserves_transparency(&self) -> bool {
794 self.preserves_transparency
795 }
796
797 pub fn set_placeholder(&mut self, placeholder: Option<ShaderPlaceholder>) {
803 self.placeholder = placeholder;
804 }
805
806 pub fn placeholder(&self) -> Option<ShaderPlaceholder> {
808 self.placeholder
809 }
810
811 pub fn set_substrates(&mut self, substrates: &[SubstrateSpec]) {
820 assert!(
821 substrates.len() <= MAX_SUBSTRATES,
822 "a runtime shader declares at most {MAX_SUBSTRATES} substrates"
823 );
824 self.clear_large_draws();
825 if self.substrates().len() == substrates.len()
826 && self
827 .substrates()
828 .iter()
829 .zip(substrates)
830 .all(|(existing, incoming)| existing.same_bits(incoming))
831 {
832 return;
833 }
834 self.specialization_mut().substrates = substrates.iter().copied().collect();
835 }
836
837 pub fn substrates(&self) -> &[SubstrateSpec] {
839 &self.specialization().substrates
840 }
841
842 pub fn hash_substrates<H: std::hash::Hasher>(&self, state: &mut H) {
844 use std::hash::Hash;
845 self.substrates().len().hash(state);
846 for substrate in self.substrates() {
847 substrate.hash_bits(state);
848 }
849 self.draw_split().hash(state);
850 }
851
852 pub fn set_draw_split(&mut self, override_name: Option<&'static str>) {
859 self.clear_large_draws();
860 if self.draw_split() == override_name {
861 return;
862 }
863 self.specialization_mut().draw_split = override_name;
864 }
865
866 pub fn draw_split(&self) -> Option<&'static str> {
868 self.specialization().draw_split
869 }
870
871 pub fn set_specialization_exact(&mut self, exact: bool) {
881 self.clear_large_draws();
882 if self.specialization_exact() == exact {
883 return;
884 }
885 self.specialization_mut().exact = exact;
886 }
887
888 pub fn specialization_exact(&self) -> bool {
890 self.specialization().exact
891 }
892
893 pub fn source(&self) -> &str {
895 &self.source
896 }
897
898 pub fn uniforms(&self) -> &[f32] {
900 self.uniforms.as_slice()
901 }
902
903 pub fn uniforms_padded(&self) -> [f32; Self::MAX_UNIFORMS] {
905 let mut padded = [0.0f32; Self::MAX_UNIFORMS];
906 let len = self.uniforms.len().min(Self::MAX_UNIFORMS);
907 padded[..len].copy_from_slice(&self.uniforms.as_slice()[..len]);
908 padded
909 }
910
911 pub fn source_hash(&self) -> u64 {
913 self.source_hash
914 }
915
916 fn try_ensure_capacity(
917 &mut self,
918 index: usize,
919 width: usize,
920 ) -> Result<(), RuntimeShaderUniformError> {
921 let min_len = index
922 .checked_add(width)
923 .ok_or_else(|| Self::uniform_range_error(index, width))?;
924 if min_len > Self::MAX_USER_UNIFORMS {
925 return Err(Self::uniform_range_error(index, width));
926 }
927 self.uniforms.ensure_len(min_len);
928 Ok(())
929 }
930
931 fn uniform_range_error(index: usize, width: usize) -> RuntimeShaderUniformError {
932 RuntimeShaderUniformError::OutOfUserRange {
933 index,
934 width,
935 max_user_uniforms: Self::MAX_USER_UNIFORMS,
936 reserved_start: Self::RESERVED_UNIFORM_START,
937 max_uniforms: Self::MAX_UNIFORMS,
938 }
939 }
940}
941
942impl PartialEq for RuntimeShader {
943 fn eq(&self, other: &Self) -> bool {
944 self.source_hash == other.source_hash
945 && (Arc::ptr_eq(&self.source, &other.source)
946 || self.source.as_ref() == other.source.as_ref())
947 && self.uniforms == other.uniforms
948 && self.overrides().len() == other.overrides().len()
949 && self
950 .overrides()
951 .iter()
952 .zip(other.overrides())
953 .all(|(a, b)| a.0 == b.0 && a.1.to_bits() == b.1.to_bits())
954 && self.input_padding.to_bits() == other.input_padding.to_bits()
955 && self.output_padding.to_bits() == other.output_padding.to_bits()
956 && self.batched_source == other.batched_source
957 && self.position_independent == other.position_independent
958 && self.preserves_transparency == other.preserves_transparency
959 && self.substrates() == other.substrates()
960 && self.draw_split() == other.draw_split()
961 && self.domains == other.domains
962 && self.placeholder == other.placeholder
963 }
964}
965
966pub fn runtime_shader_source_hash(source: &str) -> u64 {
969 hash_shader_bytes(source.bytes())
970}
971
972pub fn runtime_shader_overrides_hash<'a>(
975 overrides: impl IntoIterator<Item = (&'a str, f64)>,
976) -> u64 {
977 hash_shader_bytes(
978 overrides
979 .into_iter()
980 .flat_map(|(name, value)| name.bytes().chain([0]).chain(value.to_bits().to_le_bytes())),
981 )
982}
983
984fn hash_shader_bytes(bytes: impl IntoIterator<Item = u8>) -> u64 {
985 const FNV_OFFSET_BASIS: u64 = 0xcbf2_9ce4_8422_2325;
986 const FNV_PRIME: u64 = 0x0000_0100_0000_01b3;
987
988 bytes.into_iter().fold(FNV_OFFSET_BASIS, |hash, byte| {
989 (hash ^ u64::from(byte)).wrapping_mul(FNV_PRIME)
990 })
991}
992
993#[derive(Clone, Copy, Debug, PartialEq, Eq)]
994struct ShaderSourceCallsite {
995 file: &'static str,
996 line: u32,
997 column: u32,
998}
999
1000struct CachedShaderSource {
1001 callsite: ShaderSourceCallsite,
1002 source_hash: u64,
1003 source: Arc<str>,
1004}
1005
1006struct CachedSharedShaderSourceHash {
1007 byte_ptr: usize,
1008 len: usize,
1009 source_hash: u64,
1010 source: Weak<str>,
1011}
1012
1013fn cached_shared_shader_source_hash(source: &Arc<str>) -> u64 {
1014 static CACHE: OnceLock<Mutex<Vec<CachedSharedShaderSourceHash>>> = OnceLock::new();
1015 let byte_ptr = source.as_ptr() as usize;
1016 let len = source.len();
1017 let mut cache = CACHE
1018 .get_or_init(|| Mutex::new(Vec::new()))
1019 .lock()
1020 .unwrap_or_else(PoisonError::into_inner);
1021
1022 cache.retain(|entry| entry.source.strong_count() > 0);
1023 if let Some(entry) = cache.iter().find(|entry| {
1024 entry.byte_ptr == byte_ptr
1025 && entry.len == len
1026 && entry
1027 .source
1028 .upgrade()
1029 .is_some_and(|cached| Arc::ptr_eq(&cached, source))
1030 }) {
1031 return entry.source_hash;
1032 }
1033
1034 let source_hash = runtime_shader_source_hash(source);
1035 cache.push(CachedSharedShaderSourceHash {
1036 byte_ptr,
1037 len,
1038 source_hash,
1039 source: Arc::downgrade(source),
1040 });
1041 source_hash
1042}
1043
1044fn cached_shader_source(
1045 location: &'static std::panic::Location<'static>,
1046 source: &str,
1047) -> (Arc<str>, u64) {
1048 static CACHE: OnceLock<Mutex<Vec<CachedShaderSource>>> = OnceLock::new();
1049 let callsite = ShaderSourceCallsite {
1050 file: location.file(),
1051 line: location.line(),
1052 column: location.column(),
1053 };
1054 let mut cache = CACHE
1055 .get_or_init(|| Mutex::new(Vec::new()))
1056 .lock()
1057 .unwrap_or_else(PoisonError::into_inner);
1058
1059 if let Some(entry) = cache.iter_mut().find(|entry| entry.callsite == callsite) {
1060 if entry.source.as_ref() == source {
1061 return (entry.source.clone(), entry.source_hash);
1062 }
1063 let source_hash = runtime_shader_source_hash(source);
1064 entry.source_hash = source_hash;
1065 entry.source = Arc::<str>::from(source);
1066 return (entry.source.clone(), entry.source_hash);
1067 }
1068
1069 let source_hash = runtime_shader_source_hash(source);
1070 let shared = Arc::<str>::from(source);
1071 cache.push(CachedShaderSource {
1072 callsite,
1073 source_hash,
1074 source: shared.clone(),
1075 });
1076 (shared, source_hash)
1077}
1078
1079#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
1086pub enum ShaderTarget {
1087 Page,
1088 Layer,
1089}
1090
1091#[derive(Clone, Debug, PartialEq)]
1095pub struct ShaderWarmUp {
1096 pub shader: RuntimeShader,
1097 pub target: ShaderTarget,
1098}
1099
1100#[derive(Clone, Debug, PartialEq)]
1105pub enum RenderEffect {
1106 Blur {
1108 radius_x: f32,
1109 radius_y: f32,
1110 edge_treatment: TileMode,
1111 },
1112 Offset { offset_x: f32, offset_y: f32 },
1114 Shader {
1116 shader: Arc<RuntimeShader>,
1118 },
1119 Chain {
1123 first: Arc<RenderEffect>,
1124 second: Arc<RenderEffect>,
1125 },
1126}
1127
1128impl RenderEffect {
1129 pub fn placeholder(&self) -> Option<ShaderPlaceholder> {
1132 match self {
1133 RenderEffect::Shader { shader } => shader.placeholder(),
1134 RenderEffect::Chain { second, .. } => second.placeholder(),
1135 _ => None,
1136 }
1137 }
1138
1139 pub fn blur(radius: f32) -> Self {
1141 Self::blur_with_edge_treatment(radius, TileMode::default())
1142 }
1143
1144 pub fn blur_with_edge_treatment(radius: f32, edge_treatment: TileMode) -> Self {
1147 Self::Blur {
1148 radius_x: radius,
1149 radius_y: radius,
1150 edge_treatment,
1151 }
1152 }
1153
1154 pub fn blur_xy(radius_x: f32, radius_y: f32, edge_treatment: TileMode) -> Self {
1156 Self::Blur {
1157 radius_x,
1158 radius_y,
1159 edge_treatment,
1160 }
1161 }
1162
1163 pub fn offset(offset_x: f32, offset_y: f32) -> Self {
1165 Self::Offset { offset_x, offset_y }
1166 }
1167
1168 pub fn runtime_shader(shader: RuntimeShader) -> Self {
1170 Self::Shader {
1171 shader: Arc::new(shader),
1172 }
1173 }
1174
1175 pub fn then(self, other: RenderEffect) -> Self {
1177 Self::Chain {
1178 first: Arc::new(self),
1179 second: Arc::new(other),
1180 }
1181 }
1182
1183 pub fn contains_runtime_shader(&self) -> bool {
1187 match self {
1188 RenderEffect::Shader { .. } => true,
1189 RenderEffect::Chain { first, second } => {
1190 first.contains_runtime_shader() || second.contains_runtime_shader()
1191 }
1192 _ => false,
1193 }
1194 }
1195
1196 pub fn preserves_transparency(&self) -> bool {
1200 match self {
1201 RenderEffect::Blur { .. } | RenderEffect::Offset { .. } => true,
1202 RenderEffect::Shader { shader } => shader.preserves_transparency(),
1203 RenderEffect::Chain { first, second } => {
1204 first.preserves_transparency() && second.preserves_transparency()
1205 }
1206 }
1207 }
1208
1209 pub fn input_padding(&self) -> f32 {
1211 match self {
1212 RenderEffect::Blur {
1213 radius_x, radius_y, ..
1214 } => radius_x.abs().max(radius_y.abs()),
1215 RenderEffect::Offset { offset_x, offset_y } => offset_x.abs().max(offset_y.abs()),
1216 RenderEffect::Shader { shader } => shader.input_padding(),
1217 RenderEffect::Chain { first, second } => first.input_padding() + second.input_padding(),
1218 }
1219 }
1220
1221 pub fn output_padding(&self) -> f32 {
1225 match self {
1226 RenderEffect::Blur { .. } | RenderEffect::Offset { .. } => 0.0,
1227 RenderEffect::Shader { shader } => shader.output_padding(),
1228 RenderEffect::Chain { first, second } => {
1229 first.output_padding() + second.output_padding()
1230 }
1231 }
1232 }
1233
1234 pub fn output_support(&self) -> Option<Rect> {
1239 match self {
1240 RenderEffect::Blur { .. } | RenderEffect::Offset { .. } => None,
1241 RenderEffect::Shader { shader } => shader.output_support(),
1242 RenderEffect::Chain { second, .. } => second.output_support(),
1243 }
1244 }
1245
1246 pub fn sample_domain(&self) -> Option<Rect> {
1250 match self {
1251 RenderEffect::Blur { .. } | RenderEffect::Offset { .. } => None,
1252 RenderEffect::Shader { shader } => shader.sample_domain(),
1253 RenderEffect::Chain { second, .. } => second.sample_domain(),
1254 }
1255 }
1256}
1257
1258#[cfg(test)]
1259#[path = "tests/render_effect_tests.rs"]
1260mod tests;