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 = concat!(
80 framework_wgsl!("fullscreen_quad_vs.wgsl"),
81 framework_wgsl!("runtime_shader_bindings.wgsl"),
82);
83
84#[derive(Clone, Debug)]
124pub struct RuntimeShader {
125 source: Arc<str>,
126 source_hash: u64,
127 uniforms: RuntimeShaderUniforms,
128 specialization: Option<Arc<ShaderSpecialization>>,
129 input_padding: f32,
130 output_padding: f32,
131 batched_source: bool,
132 position_independent: bool,
133 preserves_transparency: bool,
134 domains: Option<Box<ShaderDomains>>,
135 placeholder: Option<ShaderPlaceholder>,
136}
137
138#[derive(Clone, Copy, Debug, PartialEq)]
141pub struct ShaderPlaceholder {
142 pub color: Color,
144 pub shape: Option<PlaceholderShape>,
147}
148
149#[derive(Clone, Copy, Debug, PartialEq)]
151pub struct PlaceholderShape {
152 pub bounds: Rect,
154 pub corner_radius: f32,
157}
158
159#[derive(Clone, Debug, Default)]
160struct ShaderSpecialization {
161 overrides: Vec<(&'static str, f64)>,
162 overrides_hash: OnceLock<u64>,
163 substrates: ArrayVec<SubstrateSpec, MAX_SUBSTRATES>,
164 draw_split: Option<&'static str>,
165 exact: bool,
166 large_draws: Option<LargeDrawSpecialization>,
167}
168
169impl ShaderSpecialization {
170 fn overrides_hash(&self) -> u64 {
171 if self.overrides.is_empty() {
172 return 0;
173 }
174 *self.overrides_hash.get_or_init(|| {
175 #[cfg(test)]
176 OVERRIDE_HASH_COMPUTATIONS.with(|count| count.set(count.get() + 1));
177 runtime_shader_overrides_hash(self.overrides.iter().copied())
178 })
179 }
180}
181
182#[derive(Clone, Debug)]
185struct LargeDrawSpecialization {
186 min_pixels: u64,
187 specialization: Arc<ShaderSpecialization>,
188}
189
190#[derive(Clone, Copy, Debug)]
193pub struct DrawSpecialization<'a> {
194 specialization: &'a ShaderSpecialization,
195}
196
197impl<'a> DrawSpecialization<'a> {
198 pub fn overrides(self) -> &'a [(&'static str, f64)] {
200 &self.specialization.overrides
201 }
202
203 pub fn overrides_hash(self) -> u64 {
205 self.specialization.overrides_hash()
206 }
207
208 pub fn draw_split(self) -> Option<&'static str> {
210 self.specialization.draw_split
211 }
212
213 pub fn exact(self) -> bool {
216 self.specialization.exact
217 }
218}
219
220pub(crate) struct ShaderSpecializationCache<K, const N: usize> {
221 entries: ArrayVec<CachedShaderSpecialization<K>, N>,
222}
223
224struct CachedShaderSpecialization<K> {
225 source: Option<Arc<ShaderSpecialization>>,
226 key: K,
227 result: Option<Arc<ShaderSpecialization>>,
228}
229
230impl<K: PartialEq, const N: usize> ShaderSpecializationCache<K, N> {
231 pub(crate) const fn new() -> Self {
232 assert!(N > 0);
233 Self {
234 entries: ArrayVec::new_const(),
235 }
236 }
237
238 pub(crate) fn apply(
239 &mut self,
240 shader: &mut RuntimeShader,
241 key: K,
242 specialize: impl FnOnce(&mut RuntimeShader, &K),
243 ) {
244 let hit = self.entries.iter().rposition(|entry| {
245 entry.key == key
246 && match (&entry.source, &shader.specialization) {
247 (Some(source), Some(current)) => Arc::ptr_eq(source, current),
248 (None, None) => true,
249 _ => false,
250 }
251 });
252 if let Some(index) = hit {
253 let entry = self.entries.remove(index);
254 shader.specialization.clone_from(&entry.result);
255 self.entries.push(entry);
256 return;
257 }
258 if shader
259 .specialization
260 .as_ref()
261 .is_some_and(|source| Arc::strong_count(source) == 1)
262 {
263 specialize(shader, &key);
264 return;
265 }
266 let source = shader.specialization.clone();
267 specialize(shader, &key);
268 if self.entries.is_full() {
269 self.entries.remove(0);
270 }
271 self.entries.push(CachedShaderSpecialization {
272 source,
273 key,
274 result: shader.specialization.clone(),
275 });
276 }
277}
278
279static DEFAULT_SHADER_SPECIALIZATION: ShaderSpecialization = ShaderSpecialization {
280 overrides: Vec::new(),
281 overrides_hash: OnceLock::new(),
282 substrates: ArrayVec::new_const(),
283 draw_split: None,
284 exact: false,
285 large_draws: None,
286};
287
288#[derive(Clone, Copy, Debug, Default, PartialEq)]
289struct ShaderDomains {
290 output_support: Option<Rect>,
291 sample_domain: Option<Rect>,
292}
293
294fn finite_rect(rect: Option<Rect>) -> Option<Rect> {
295 rect.filter(|rect| {
296 rect.x.is_finite()
297 && rect.y.is_finite()
298 && rect.width.is_finite()
299 && rect.height.is_finite()
300 })
301}
302
303pub const MAX_SUBSTRATES: usize = 3;
305
306#[derive(Clone, Copy, Debug, PartialEq)]
309pub enum SubstrateSpec {
310 Mean,
315 Average { block: u32 },
318 Blur { radius_px: f32 },
321}
322
323impl SubstrateSpec {
324 fn same_bits(&self, other: &Self) -> bool {
325 match (self, other) {
326 (Self::Mean, Self::Mean) => true,
327 (Self::Average { block: a }, Self::Average { block: b }) => a == b,
328 (Self::Blur { radius_px: a }, Self::Blur { radius_px: b }) => {
329 a.to_bits() == b.to_bits()
330 }
331 _ => false,
332 }
333 }
334
335 fn hash_bits<H: std::hash::Hasher>(&self, state: &mut H) {
336 use std::hash::Hash;
337 match self {
338 Self::Mean => 2u8.hash(state),
339 Self::Average { block } => {
340 0u8.hash(state);
341 block.hash(state);
342 }
343 Self::Blur { radius_px } => {
344 1u8.hash(state);
345 radius_px.to_bits().hash(state);
346 }
347 }
348 }
349}
350
351#[derive(Clone, Debug, PartialEq)]
352struct RuntimeShaderUniforms {
353 len: usize,
354 inline: [f32; RUNTIME_SHADER_INLINE_UNIFORMS],
355 heap: Option<Vec<f32>>,
356}
357
358impl RuntimeShaderUniforms {
359 fn new() -> Self {
360 Self {
361 len: 0,
362 inline: [0.0; RUNTIME_SHADER_INLINE_UNIFORMS],
363 heap: None,
364 }
365 }
366
367 fn as_slice(&self) -> &[f32] {
368 if let Some(heap) = &self.heap {
369 heap.as_slice()
370 } else {
371 &self.inline[..self.len]
372 }
373 }
374
375 fn len(&self) -> usize {
376 self.as_slice().len()
377 }
378
379 fn ensure_len(&mut self, min_len: usize) {
380 if let Some(heap) = &mut self.heap {
381 if heap.len() < min_len {
382 heap.resize(min_len, 0.0);
383 }
384 return;
385 }
386
387 if min_len <= RUNTIME_SHADER_INLINE_UNIFORMS {
388 self.len = self.len.max(min_len);
389 return;
390 }
391
392 let mut heap = Vec::with_capacity(min_len);
393 heap.extend_from_slice(&self.inline[..self.len]);
394 heap.resize(min_len, 0.0);
395 self.heap = Some(heap);
396 }
397
398 fn set(&mut self, index: usize, value: f32) {
399 if let Some(heap) = &mut self.heap {
400 heap[index] = value;
401 } else {
402 self.inline[index] = value;
403 }
404 }
405
406 #[cfg(test)]
407 fn is_inline(&self) -> bool {
408 self.heap.is_none()
409 }
410}
411
412#[derive(Clone, Copy, Debug, Eq, PartialEq, thiserror::Error)]
414pub enum RuntimeShaderUniformError {
415 #[error(
416 "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"
417 )]
418 OutOfUserRange {
419 index: usize,
420 width: usize,
421 max_user_uniforms: usize,
422 reserved_start: usize,
423 max_uniforms: usize,
424 },
425}
426
427impl RuntimeShader {
428 pub const MAX_UNIFORMS: usize = 256;
432 pub const RESERVED_UNIFORM_START: usize = 224;
434 pub const SUBSTRATE_REGION_UNIFORMS: [usize; MAX_SUBSTRATES] = [232, 228, 224];
437 pub const SOURCE_REGION_UNIFORM: usize = 236;
439 pub const MASK_RECT_UNIFORM: usize = 240;
441 pub const MASK_RADII_UNIFORM: usize = 244;
443 pub const EFFECT_RECT_UNIFORM: usize = 248;
445 pub const LOGICAL_SIZE_UNIFORM: usize = 252;
447 pub const ALPHA_UNIFORM: usize = 254;
449 pub const MAX_USER_UNIFORMS: usize = Self::RESERVED_UNIFORM_START;
451
452 #[track_caller]
454 pub fn new(wgsl_source: &str) -> Self {
455 let (source, source_hash) =
456 cached_shader_source(std::panic::Location::caller(), wgsl_source);
457 Self::with_source(source, source_hash)
458 }
459
460 pub fn from_shared_source(source: Arc<str>) -> Self {
465 let source_hash = cached_shared_shader_source_hash(&source);
466 Self::with_source(source, source_hash)
467 }
468
469 fn with_source(source: Arc<str>, source_hash: u64) -> Self {
470 Self {
471 source,
472 source_hash,
473 uniforms: RuntimeShaderUniforms::new(),
474 specialization: None,
475 input_padding: 0.0,
476 output_padding: 0.0,
477 batched_source: false,
478 position_independent: false,
479 preserves_transparency: false,
480 domains: None,
481 placeholder: None,
482 }
483 }
484
485 fn specialization(&self) -> &ShaderSpecialization {
486 self.specialization
487 .as_deref()
488 .unwrap_or(&DEFAULT_SHADER_SPECIALIZATION)
489 }
490
491 fn specialization_mut(&mut self) -> &mut ShaderSpecialization {
492 Arc::make_mut(self.specialization.get_or_insert_with(Arc::default))
493 }
494
495 pub fn set_override(&mut self, name: &'static str, value: f64) {
509 self.clear_large_draws();
510 let position = self
511 .overrides()
512 .binary_search_by(|(existing, _)| existing.cmp(&name));
513 if position.is_ok_and(|index| self.overrides()[index].1.to_bits() == value.to_bits()) {
514 return;
515 }
516 let specialization = self.specialization_mut();
517 specialization.overrides_hash.take();
518 let overrides = &mut specialization.overrides;
519 match position {
520 Ok(index) => overrides[index].1 = value,
521 Err(index) => overrides.insert(index, (name, value)),
522 }
523 }
524
525 pub fn clear_override(&mut self, name: &str) -> bool {
527 self.clear_large_draws();
528 let Ok(index) = self
529 .overrides()
530 .binary_search_by(|(existing, _)| (*existing).cmp(name))
531 else {
532 return false;
533 };
534 let specialization = self.specialization_mut();
535 specialization.overrides_hash.take();
536 specialization.overrides.remove(index);
537 true
538 }
539
540 pub fn overrides(&self) -> &[(&'static str, f64)] {
543 &self.specialization().overrides
544 }
545
546 pub fn overrides_hash(&self) -> u64 {
548 self.specialization().overrides_hash()
549 }
550
551 pub fn draw_specialization(&self, pixels: u64) -> DrawSpecialization<'_> {
555 let own = self.specialization();
556 let specialization = match &own.large_draws {
557 Some(large) if pixels >= large.min_pixels => &large.specialization,
558 _ => own,
559 };
560 DrawSpecialization { specialization }
561 }
562
563 pub(crate) fn specialize_with_large_draws(
567 &mut self,
568 min_pixels: u64,
569 large: impl FnOnce(&mut RuntimeShader),
570 own: impl FnOnce(&mut RuntimeShader),
571 ) {
572 let source = self.specialization.clone();
573 large(self);
574 let large = std::mem::replace(&mut self.specialization, source);
575 own(self);
576 self.specialization_mut().large_draws =
577 large.map(|specialization| LargeDrawSpecialization {
578 min_pixels,
579 specialization,
580 });
581 }
582
583 pub(crate) fn clear_large_draws(&mut self) {
588 if self.specialization().large_draws.is_some() {
589 self.specialization_mut().large_draws = None;
590 }
591 }
592
593 pub fn set_input_padding(&mut self, padding: f32) {
597 self.input_padding = if padding.is_finite() {
598 padding.max(0.0)
599 } else {
600 0.0
601 };
602 }
603
604 pub fn input_padding(&self) -> f32 {
606 self.input_padding
607 }
608
609 pub fn set_output_padding(&mut self, padding: f32) {
614 self.output_padding = if padding.is_finite() {
615 padding.max(0.0)
616 } else {
617 0.0
618 };
619 }
620
621 pub fn output_padding(&self) -> f32 {
623 self.output_padding
624 }
625
626 pub fn set_output_support(&mut self, support: Option<Rect>) {
637 self.set_domains(ShaderDomains {
638 output_support: finite_rect(support),
639 sample_domain: self.sample_domain(),
640 });
641 }
642
643 pub fn output_support(&self) -> Option<Rect> {
645 self.domains
646 .as_ref()
647 .and_then(|domains| domains.output_support)
648 }
649
650 fn set_domains(&mut self, domains: ShaderDomains) {
651 self.domains = (domains != ShaderDomains::default()).then(|| Box::new(domains));
652 }
653
654 pub fn set_sample_domain(&mut self, domain: Option<Rect>) {
663 self.set_domains(ShaderDomains {
664 output_support: self.output_support(),
665 sample_domain: finite_rect(domain),
666 });
667 }
668
669 pub fn sample_domain(&self) -> Option<Rect> {
671 self.domains
672 .as_ref()
673 .and_then(|domains| domains.sample_domain)
674 }
675
676 pub fn set_float(&mut self, index: usize, value: f32) {
681 let _ = self.try_set_float(index, value);
682 }
683
684 pub fn try_set_float(
686 &mut self,
687 index: usize,
688 value: f32,
689 ) -> Result<(), RuntimeShaderUniformError> {
690 self.try_ensure_capacity(index, 1)?;
691 self.uniforms.set(index, value);
692 Ok(())
693 }
694
695 pub fn set_float2(&mut self, index: usize, x: f32, y: f32) {
700 let _ = self.try_set_float2(index, x, y);
701 }
702
703 pub fn try_set_float2(
705 &mut self,
706 index: usize,
707 x: f32,
708 y: f32,
709 ) -> Result<(), RuntimeShaderUniformError> {
710 self.try_ensure_capacity(index, 2)?;
711 self.uniforms.set(index, x);
712 self.uniforms.set(index + 1, y);
713 Ok(())
714 }
715
716 pub fn set_float4(&mut self, index: usize, x: f32, y: f32, z: f32, w: f32) {
721 let _ = self.try_set_float4(index, x, y, z, w);
722 }
723
724 pub fn try_set_float4(
726 &mut self,
727 index: usize,
728 x: f32,
729 y: f32,
730 z: f32,
731 w: f32,
732 ) -> Result<(), RuntimeShaderUniformError> {
733 self.try_ensure_capacity(index, 4)?;
734 self.uniforms.set(index, x);
735 self.uniforms.set(index + 1, y);
736 self.uniforms.set(index + 2, z);
737 self.uniforms.set(index + 3, w);
738 Ok(())
739 }
740
741 pub fn set_batched_source(&mut self, batched: bool) {
746 self.batched_source = batched;
747 }
748
749 pub fn batched_source(&self) -> bool {
752 self.batched_source
753 }
754
755 pub fn set_position_independent(&mut self, independent: bool) {
762 self.position_independent = independent;
763 }
764
765 pub fn position_independent(&self) -> bool {
767 self.position_independent
768 }
769
770 pub fn set_preserves_transparency(&mut self, preserves: bool) {
775 self.preserves_transparency = preserves;
776 }
777
778 pub fn preserves_transparency(&self) -> bool {
780 self.preserves_transparency
781 }
782
783 pub fn set_placeholder(&mut self, placeholder: Option<ShaderPlaceholder>) {
789 self.placeholder = placeholder;
790 }
791
792 pub fn placeholder(&self) -> Option<ShaderPlaceholder> {
794 self.placeholder
795 }
796
797 pub fn set_substrates(&mut self, substrates: &[SubstrateSpec]) {
806 assert!(
807 substrates.len() <= MAX_SUBSTRATES,
808 "a runtime shader declares at most {MAX_SUBSTRATES} substrates"
809 );
810 self.clear_large_draws();
811 if self.substrates().len() == substrates.len()
812 && self
813 .substrates()
814 .iter()
815 .zip(substrates)
816 .all(|(existing, incoming)| existing.same_bits(incoming))
817 {
818 return;
819 }
820 self.specialization_mut().substrates = substrates.iter().copied().collect();
821 }
822
823 pub fn substrates(&self) -> &[SubstrateSpec] {
825 &self.specialization().substrates
826 }
827
828 pub fn hash_substrates<H: std::hash::Hasher>(&self, state: &mut H) {
830 use std::hash::Hash;
831 self.substrates().len().hash(state);
832 for substrate in self.substrates() {
833 substrate.hash_bits(state);
834 }
835 self.draw_split().hash(state);
836 }
837
838 pub fn set_draw_split(&mut self, override_name: Option<&'static str>) {
845 self.clear_large_draws();
846 if self.draw_split() == override_name {
847 return;
848 }
849 self.specialization_mut().draw_split = override_name;
850 }
851
852 pub fn draw_split(&self) -> Option<&'static str> {
854 self.specialization().draw_split
855 }
856
857 pub fn set_specialization_exact(&mut self, exact: bool) {
867 self.clear_large_draws();
868 if self.specialization_exact() == exact {
869 return;
870 }
871 self.specialization_mut().exact = exact;
872 }
873
874 pub fn specialization_exact(&self) -> bool {
876 self.specialization().exact
877 }
878
879 pub fn source(&self) -> &str {
881 &self.source
882 }
883
884 pub fn uniforms(&self) -> &[f32] {
886 self.uniforms.as_slice()
887 }
888
889 pub fn uniforms_padded(&self) -> [f32; Self::MAX_UNIFORMS] {
891 let mut padded = [0.0f32; Self::MAX_UNIFORMS];
892 let len = self.uniforms.len().min(Self::MAX_UNIFORMS);
893 padded[..len].copy_from_slice(&self.uniforms.as_slice()[..len]);
894 padded
895 }
896
897 pub fn source_hash(&self) -> u64 {
899 self.source_hash
900 }
901
902 fn try_ensure_capacity(
903 &mut self,
904 index: usize,
905 width: usize,
906 ) -> Result<(), RuntimeShaderUniformError> {
907 let min_len = index
908 .checked_add(width)
909 .ok_or_else(|| Self::uniform_range_error(index, width))?;
910 if min_len > Self::MAX_USER_UNIFORMS {
911 return Err(Self::uniform_range_error(index, width));
912 }
913 self.uniforms.ensure_len(min_len);
914 Ok(())
915 }
916
917 fn uniform_range_error(index: usize, width: usize) -> RuntimeShaderUniformError {
918 RuntimeShaderUniformError::OutOfUserRange {
919 index,
920 width,
921 max_user_uniforms: Self::MAX_USER_UNIFORMS,
922 reserved_start: Self::RESERVED_UNIFORM_START,
923 max_uniforms: Self::MAX_UNIFORMS,
924 }
925 }
926}
927
928#[cfg(test)]
929thread_local! {
930 static OVERRIDE_HASH_COMPUTATIONS: std::cell::Cell<usize> = const { std::cell::Cell::new(0) };
931}
932
933impl PartialEq for RuntimeShader {
934 fn eq(&self, other: &Self) -> bool {
935 self.source_hash == other.source_hash
936 && (Arc::ptr_eq(&self.source, &other.source)
937 || self.source.as_ref() == other.source.as_ref())
938 && self.uniforms == other.uniforms
939 && self.overrides().len() == other.overrides().len()
940 && self
941 .overrides()
942 .iter()
943 .zip(other.overrides())
944 .all(|(a, b)| a.0 == b.0 && a.1.to_bits() == b.1.to_bits())
945 && self.input_padding.to_bits() == other.input_padding.to_bits()
946 && self.output_padding.to_bits() == other.output_padding.to_bits()
947 && self.batched_source == other.batched_source
948 && self.position_independent == other.position_independent
949 && self.preserves_transparency == other.preserves_transparency
950 && self.substrates() == other.substrates()
951 && self.draw_split() == other.draw_split()
952 && self.domains == other.domains
953 && self.placeholder == other.placeholder
954 }
955}
956
957pub fn runtime_shader_source_hash(source: &str) -> u64 {
960 hash_shader_bytes(source.bytes())
961}
962
963pub fn runtime_shader_overrides_hash<'a>(
966 overrides: impl IntoIterator<Item = (&'a str, f64)>,
967) -> u64 {
968 hash_shader_bytes(
969 overrides
970 .into_iter()
971 .flat_map(|(name, value)| name.bytes().chain([0]).chain(value.to_bits().to_le_bytes())),
972 )
973}
974
975fn hash_shader_bytes(bytes: impl IntoIterator<Item = u8>) -> u64 {
976 const FNV_OFFSET_BASIS: u64 = 0xcbf2_9ce4_8422_2325;
977 const FNV_PRIME: u64 = 0x0000_0100_0000_01b3;
978
979 bytes.into_iter().fold(FNV_OFFSET_BASIS, |hash, byte| {
980 (hash ^ u64::from(byte)).wrapping_mul(FNV_PRIME)
981 })
982}
983
984#[derive(Clone, Copy, Debug, PartialEq, Eq)]
985struct ShaderSourceCallsite {
986 file: &'static str,
987 line: u32,
988 column: u32,
989}
990
991struct CachedShaderSource {
992 callsite: ShaderSourceCallsite,
993 source_hash: u64,
994 source: Arc<str>,
995}
996
997struct CachedSharedShaderSourceHash {
998 byte_ptr: usize,
999 len: usize,
1000 source_hash: u64,
1001 source: Weak<str>,
1002}
1003
1004fn cached_shared_shader_source_hash(source: &Arc<str>) -> u64 {
1005 static CACHE: OnceLock<Mutex<Vec<CachedSharedShaderSourceHash>>> = OnceLock::new();
1006 let byte_ptr = source.as_ptr() as usize;
1007 let len = source.len();
1008 let mut cache = CACHE
1009 .get_or_init(|| Mutex::new(Vec::new()))
1010 .lock()
1011 .unwrap_or_else(PoisonError::into_inner);
1012
1013 cache.retain(|entry| entry.source.strong_count() > 0);
1014 if let Some(entry) = cache.iter().find(|entry| {
1015 entry.byte_ptr == byte_ptr
1016 && entry.len == len
1017 && entry
1018 .source
1019 .upgrade()
1020 .is_some_and(|cached| Arc::ptr_eq(&cached, source))
1021 }) {
1022 return entry.source_hash;
1023 }
1024
1025 let source_hash = runtime_shader_source_hash(source);
1026 cache.push(CachedSharedShaderSourceHash {
1027 byte_ptr,
1028 len,
1029 source_hash,
1030 source: Arc::downgrade(source),
1031 });
1032 source_hash
1033}
1034
1035fn cached_shader_source(
1036 location: &'static std::panic::Location<'static>,
1037 source: &str,
1038) -> (Arc<str>, u64) {
1039 static CACHE: OnceLock<Mutex<Vec<CachedShaderSource>>> = OnceLock::new();
1040 let callsite = ShaderSourceCallsite {
1041 file: location.file(),
1042 line: location.line(),
1043 column: location.column(),
1044 };
1045 let mut cache = CACHE
1046 .get_or_init(|| Mutex::new(Vec::new()))
1047 .lock()
1048 .unwrap_or_else(PoisonError::into_inner);
1049
1050 if let Some(entry) = cache.iter_mut().find(|entry| entry.callsite == callsite) {
1051 if entry.source.as_ref() == source {
1052 return (entry.source.clone(), entry.source_hash);
1053 }
1054 let source_hash = runtime_shader_source_hash(source);
1055 entry.source_hash = source_hash;
1056 entry.source = Arc::<str>::from(source);
1057 return (entry.source.clone(), entry.source_hash);
1058 }
1059
1060 let source_hash = runtime_shader_source_hash(source);
1061 let shared = Arc::<str>::from(source);
1062 cache.push(CachedShaderSource {
1063 callsite,
1064 source_hash,
1065 source: shared.clone(),
1066 });
1067 (shared, source_hash)
1068}
1069
1070#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
1077pub enum ShaderTarget {
1078 Page,
1079 Layer,
1080}
1081
1082#[derive(Clone, Debug, PartialEq)]
1086pub struct ShaderWarmUp {
1087 pub shader: RuntimeShader,
1088 pub target: ShaderTarget,
1089}
1090
1091#[derive(Clone, Debug, PartialEq)]
1096pub enum RenderEffect {
1097 Blur {
1099 radius_x: f32,
1100 radius_y: f32,
1101 edge_treatment: TileMode,
1102 },
1103 Offset { offset_x: f32, offset_y: f32 },
1105 Shader {
1107 shader: Arc<RuntimeShader>,
1109 },
1110 Chain {
1114 first: Arc<RenderEffect>,
1115 second: Arc<RenderEffect>,
1116 },
1117}
1118
1119impl RenderEffect {
1120 pub fn placeholder(&self) -> Option<ShaderPlaceholder> {
1123 match self {
1124 RenderEffect::Shader { shader } => shader.placeholder(),
1125 RenderEffect::Chain { second, .. } => second.placeholder(),
1126 _ => None,
1127 }
1128 }
1129
1130 pub fn blur(radius: f32) -> Self {
1132 Self::blur_with_edge_treatment(radius, TileMode::default())
1133 }
1134
1135 pub fn blur_with_edge_treatment(radius: f32, edge_treatment: TileMode) -> Self {
1138 Self::Blur {
1139 radius_x: radius,
1140 radius_y: radius,
1141 edge_treatment,
1142 }
1143 }
1144
1145 pub fn blur_xy(radius_x: f32, radius_y: f32, edge_treatment: TileMode) -> Self {
1147 Self::Blur {
1148 radius_x,
1149 radius_y,
1150 edge_treatment,
1151 }
1152 }
1153
1154 pub fn offset(offset_x: f32, offset_y: f32) -> Self {
1156 Self::Offset { offset_x, offset_y }
1157 }
1158
1159 pub fn runtime_shader(shader: RuntimeShader) -> Self {
1161 Self::Shader {
1162 shader: Arc::new(shader),
1163 }
1164 }
1165
1166 pub fn then(self, other: RenderEffect) -> Self {
1168 Self::Chain {
1169 first: Arc::new(self),
1170 second: Arc::new(other),
1171 }
1172 }
1173
1174 pub fn contains_runtime_shader(&self) -> bool {
1178 match self {
1179 RenderEffect::Shader { .. } => true,
1180 RenderEffect::Chain { first, second } => {
1181 first.contains_runtime_shader() || second.contains_runtime_shader()
1182 }
1183 _ => false,
1184 }
1185 }
1186
1187 pub fn preserves_transparency(&self) -> bool {
1191 match self {
1192 RenderEffect::Blur { .. } | RenderEffect::Offset { .. } => true,
1193 RenderEffect::Shader { shader } => shader.preserves_transparency(),
1194 RenderEffect::Chain { first, second } => {
1195 first.preserves_transparency() && second.preserves_transparency()
1196 }
1197 }
1198 }
1199
1200 pub fn input_padding(&self) -> f32 {
1202 match self {
1203 RenderEffect::Blur {
1204 radius_x, radius_y, ..
1205 } => radius_x.abs().max(radius_y.abs()),
1206 RenderEffect::Offset { offset_x, offset_y } => offset_x.abs().max(offset_y.abs()),
1207 RenderEffect::Shader { shader } => shader.input_padding(),
1208 RenderEffect::Chain { first, second } => first.input_padding() + second.input_padding(),
1209 }
1210 }
1211
1212 pub fn output_padding(&self) -> f32 {
1216 match self {
1217 RenderEffect::Blur { .. } | RenderEffect::Offset { .. } => 0.0,
1218 RenderEffect::Shader { shader } => shader.output_padding(),
1219 RenderEffect::Chain { first, second } => {
1220 first.output_padding() + second.output_padding()
1221 }
1222 }
1223 }
1224
1225 pub fn output_support(&self) -> Option<Rect> {
1230 match self {
1231 RenderEffect::Blur { .. } | RenderEffect::Offset { .. } => None,
1232 RenderEffect::Shader { shader } => shader.output_support(),
1233 RenderEffect::Chain { second, .. } => second.output_support(),
1234 }
1235 }
1236
1237 pub fn sample_domain(&self) -> Option<Rect> {
1241 match self {
1242 RenderEffect::Blur { .. } | RenderEffect::Offset { .. } => None,
1243 RenderEffect::Shader { shader } => shader.sample_domain(),
1244 RenderEffect::Chain { second, .. } => second.sample_domain(),
1245 }
1246 }
1247}
1248
1249#[cfg(test)]
1250#[path = "tests/render_effect_tests.rs"]
1251mod tests;