librashader_reflect/reflect/
semantics.rs

1use bitflags::bitflags;
2use librashader_common::map::{FastHashMap, ShortString};
3use std::fmt::{Display, Formatter};
4use std::str::FromStr;
5
6/// The maximum number of bindings allowed in a shader.
7pub const MAX_BINDINGS_COUNT: u32 = 16;
8/// The maximum size of the push constant range.
9pub const MAX_PUSH_BUFFER_SIZE: u32 = 128;
10
11/// The type of a uniform.
12#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Copy, Clone, Hash)]
13#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
14pub enum UniformType {
15    /// A matrix of 4x4 floats (`mat4`).
16    Mat4,
17    /// A vector of 4 floats (`vec4`).
18    Vec4,
19    /// An unsigned integer (`uint`).
20    Unsigned,
21    /// A signed integer (`int`).
22    Signed,
23    /// A floating point number (`float`).
24    Float,
25}
26
27/// Unique semantics are builtin uniforms passed by the shader runtime
28/// that are always available.
29#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Copy, Clone, Hash)]
30#[repr(i32)]
31#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
32pub enum UniqueSemantics {
33    // mat4, MVP
34    /// The Model View Projection matrix for the frame.
35    MVP = 0,
36    // vec4, viewport size of current pass
37    /// The viewport size of the current pass.
38    Output = 1,
39    // vec4, viewport size of final pass
40    /// The viewport size of the final pass.
41    FinalViewport = 2,
42    // uint, frame count with modulo
43    /// The frame count, possibly with shader-defined modulo.
44    FrameCount = 3,
45    // int, frame direction
46    /// The direction in time where frames are rendered
47    FrameDirection = 4,
48    // uint, frame time delta
49    /// The time delta between the previous frame and the current frame.
50    FrameTimeDelta = 5,
51    // uint, original FPS
52    /// The original frames per second for the underlying content
53    OriginalFPS = 6,
54    //int, rotation (glUniform1i(uni->rotation, retroarch_get_rotation());)
55    /// The rotation index (0 = 0deg, 1 = 90deg, 2 = 180deg, 3 = 270deg)
56    Rotation = 7,
57    // float
58    /// The original aspect ratio
59    OriginalAspect = 8,
60    /// The original aspect ratio, if rotated.
61    OriginalAspectRotated = 9,
62    /// Total number of subframes.
63    TotalSubFrames = 10,
64    /// The current subframe (default 1)
65    CurrentSubFrame = 11,
66    /// A user defined float parameter.
67    // float, user defined parameter, array
68    FloatParameter = 12,
69}
70
71impl UniqueSemantics {
72    /// Produce a `Semantic` for this `UniqueSemantics`.
73    pub const fn semantics(self) -> Semantic<UniqueSemantics, ()> {
74        Semantic {
75            semantics: self,
76            index: (),
77        }
78    }
79
80    /// Get the type of the uniform when bound.
81    pub const fn binding_type(&self) -> UniformType {
82        match self {
83            UniqueSemantics::MVP => UniformType::Mat4,
84            UniqueSemantics::Output => UniformType::Vec4,
85            UniqueSemantics::FinalViewport => UniformType::Vec4,
86            UniqueSemantics::FrameCount => UniformType::Unsigned,
87            UniqueSemantics::FrameDirection => UniformType::Signed,
88            UniqueSemantics::Rotation => UniformType::Unsigned,
89            UniqueSemantics::TotalSubFrames => UniformType::Unsigned,
90            UniqueSemantics::CurrentSubFrame => UniformType::Unsigned,
91            UniqueSemantics::FloatParameter => UniformType::Float,
92            UniqueSemantics::FrameTimeDelta => UniformType::Unsigned,
93            UniqueSemantics::OriginalFPS => UniformType::Float,
94            UniqueSemantics::OriginalAspect => UniformType::Float,
95            UniqueSemantics::OriginalAspectRotated => UniformType::Float,
96        }
97    }
98
99    /// Get the name of the semantic as a string.
100    pub const fn as_str(&self) -> &'static str {
101        match self {
102            UniqueSemantics::MVP => "MVP",
103            UniqueSemantics::Output => "Output",
104            UniqueSemantics::FinalViewport => "FinalViewport",
105            UniqueSemantics::FrameCount => "FrameCount",
106            UniqueSemantics::FrameDirection => "FrameDirection",
107            UniqueSemantics::Rotation => "Rotation",
108            UniqueSemantics::TotalSubFrames => "TotalSubFrames",
109            UniqueSemantics::CurrentSubFrame => "CurrentSubFrame",
110            UniqueSemantics::FloatParameter => "FloatParameter",
111            UniqueSemantics::FrameTimeDelta => "FrameTimeDelta",
112            UniqueSemantics::OriginalFPS => "OriginalFPS",
113            UniqueSemantics::OriginalAspect => "OriginalAspect",
114            UniqueSemantics::OriginalAspectRotated => "OriginalAspectRotated",
115        }
116    }
117}
118
119impl Display for UniqueSemantics {
120    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
121        write!(f, "{}", self.as_str())
122    }
123}
124
125/// Texture semantics relate to input or output textures.
126///
127/// Texture semantics are used to relate both texture samplers and `*Size` uniforms.
128#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Copy, Clone, Hash)]
129#[repr(i32)]
130#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
131pub enum TextureSemantics {
132    /// The original input of the filter chain.
133    Original = 0,
134    /// The input from the previous shader pass, or the input on the first shader pass.
135    Source = 1,
136    /// The input frames from previous frames.
137    OriginalHistory = 2,
138    /// The output from previous shader passes in the same frame.
139    PassOutput = 3,
140    /// The output from previous shader passes in the previous frame.
141    PassFeedback = 4,
142    /// A user provided lookup texture.
143    User = 5,
144}
145
146impl TextureSemantics {
147    pub(crate) const TEXTURE_SEMANTICS: [TextureSemantics; 6] = [
148        TextureSemantics::Source,
149        // originalhistory needs to come first, otherwise
150        // the name lookup implementation will prioritize Original
151        // when reflecting semantics.
152        TextureSemantics::OriginalHistory,
153        TextureSemantics::Original,
154        TextureSemantics::PassOutput,
155        TextureSemantics::PassFeedback,
156        TextureSemantics::User,
157    ];
158
159    /// Get the name of the size uniform for this semantics when bound.
160    pub fn size_uniform_name(&self) -> &'static str {
161        match self {
162            TextureSemantics::Original => "OriginalSize",
163            TextureSemantics::Source => "SourceSize",
164            TextureSemantics::OriginalHistory => "OriginalHistorySize",
165            TextureSemantics::PassOutput => "PassOutputSize",
166            TextureSemantics::PassFeedback => "PassFeedbackSize",
167            TextureSemantics::User => "UserSize",
168        }
169    }
170
171    /// Get the name of the texture sampler for this semantics when bound.
172    pub fn texture_name(&self) -> &'static str {
173        match self {
174            TextureSemantics::Original => "Original",
175            TextureSemantics::Source => "Source",
176            TextureSemantics::OriginalHistory => "OriginalHistory",
177            TextureSemantics::PassOutput => "PassOutput",
178            TextureSemantics::PassFeedback => "PassFeedback",
179            TextureSemantics::User => "User",
180        }
181    }
182
183    /// Returns whether or not textures of this semantics are indexed or unique.
184    ///
185    /// Only Original and Source are unique, all other textures can be indexed.
186    pub fn is_indexed(&self) -> bool {
187        !matches!(self, TextureSemantics::Original | TextureSemantics::Source)
188    }
189
190    /// Produce a `Semantic` for this `TextureSemantics` of the given index.
191    pub const fn semantics(self, index: usize) -> Semantic<TextureSemantics> {
192        Semantic {
193            semantics: self,
194            index,
195        }
196    }
197}
198
199pub(crate) struct TypeInfo {
200    pub size: u32,
201    pub columns: u32,
202}
203
204pub(crate) trait ValidateTypeSemantics<T> {
205    fn validate_type(&self, ty: &T) -> Option<TypeInfo>;
206}
207
208/// A unit of unique or indexed semantic.
209#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
210pub struct Semantic<T, I = usize> {
211    /// The semantics of this unit.
212    pub semantics: T,
213    /// The index of the semantic if not unique.
214    pub index: I,
215}
216
217bitflags! {
218    /// The pipeline stage for which a uniform is bound.
219    #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
220    #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
221    #[cfg_attr(feature = "serde", serde(transparent))]
222    pub struct BindingStage: u8 {
223        const NONE = 0b00000000;
224        const VERTEX = 0b00000001;
225        const FRAGMENT = 0b00000010;
226    }
227}
228
229/// Reflection information for the Uniform Buffer or Push Constant Block
230#[derive(Clone, Debug)]
231#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
232pub struct BufferReflection<T> {
233    /// The binding point for this buffer, if applicable
234    pub binding: T,
235    /// The size of the buffer. Buffer sizes returned by reflection is always aligned to a 16 byte boundary.
236    pub size: u32,
237    /// The mask indicating for which stages the UBO should be bound.
238    pub stage_mask: BindingStage,
239}
240
241/// The offset of a uniform member.
242///
243/// A uniform can be bound to both the UBO, or as a Push Constant.
244#[derive(Debug, Copy, Clone, PartialEq, Eq)]
245#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
246pub struct MemberOffset {
247    /// The offset of the uniform member within the UBO.
248    pub ubo: Option<usize>,
249    /// The offset of the uniform member within the Push Constant range.
250    pub push: Option<usize>,
251}
252
253#[derive(Debug, Clone, Copy, PartialEq, Eq)]
254#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
255/// The block where a uniform member is located.
256pub enum UniformMemberBlock {
257    /// The offset is for a UBO.
258    Ubo,
259    /// The offset is for a push constant block.
260    PushConstant,
261}
262
263impl UniformMemberBlock {
264    /// A list of valid member block types.
265    pub const TYPES: [UniformMemberBlock; 2] =
266        [UniformMemberBlock::Ubo, UniformMemberBlock::PushConstant];
267}
268
269impl MemberOffset {
270    pub(crate) fn new(off: usize, ty: UniformMemberBlock) -> Self {
271        match ty {
272            UniformMemberBlock::Ubo => MemberOffset {
273                ubo: Some(off),
274                push: None,
275            },
276            UniformMemberBlock::PushConstant => MemberOffset {
277                ubo: None,
278                push: Some(off),
279            },
280        }
281    }
282
283    pub fn offset(&self, ty: UniformMemberBlock) -> Option<usize> {
284        match ty {
285            UniformMemberBlock::Ubo => self.ubo,
286            UniformMemberBlock::PushConstant => self.push,
287        }
288    }
289
290    pub(crate) fn offset_mut(&mut self, ty: UniformMemberBlock) -> &mut Option<usize> {
291        match ty {
292            UniformMemberBlock::Ubo => &mut self.ubo,
293            UniformMemberBlock::PushConstant => &mut self.push,
294        }
295    }
296}
297
298/// Reflection information about a non-texture related uniform variable.
299#[derive(Clone, Debug)]
300#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
301pub struct VariableMeta {
302    /// The offset of this variable uniform.
303    pub offset: MemberOffset,
304    /// The size of the uniform.
305    pub size: u32,
306    /// The name of the uniform.
307    pub id: ShortString,
308}
309
310/// Reflection information about a texture size uniform variable.
311#[derive(Clone, Debug)]
312#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
313pub struct TextureSizeMeta {
314    // this might bite us in the back because retroarch keeps separate UBO/push offsets..
315    /// The offset of this size uniform.
316    pub offset: MemberOffset,
317    /// The mask indicating for which stages the texture size uniform should be bound.
318    pub stage_mask: BindingStage,
319    /// The name of the uniform.
320    pub id: ShortString,
321}
322
323/// Reflection information about texture samplers.
324#[derive(Clone, Debug)]
325#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
326pub struct TextureBinding {
327    /// The binding index of the texture.
328    pub binding: u32,
329}
330
331/// Reflection information about a shader.
332#[derive(Clone, Debug)]
333#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
334pub struct ShaderReflection {
335    /// Reflection information about the UBO for this shader.
336    pub ubo: Option<BufferReflection<u32>>,
337    /// Reflection information about the Push Constant range for this shader.
338    pub push_constant: Option<BufferReflection<Option<u32>>>,
339    /// Metadata about the bindings required for this shader.
340    pub meta: BindingMeta,
341}
342
343/// Metadata about a uniform variable.
344pub trait UniformMeta {
345    /// The offset of this uniform.
346    fn offset(&self) -> MemberOffset;
347    /// The name of this uniform in the shader.
348    fn id(&self) -> &str;
349}
350
351impl UniformMeta for VariableMeta {
352    fn offset(&self) -> MemberOffset {
353        self.offset
354    }
355
356    fn id(&self) -> &str {
357        &self.id
358    }
359}
360
361impl UniformMeta for TextureSizeMeta {
362    fn offset(&self) -> MemberOffset {
363        self.offset
364    }
365    fn id(&self) -> &str {
366        &self.id
367    }
368}
369
370/// A trait for maps that can return texture semantic units.
371pub trait TextureSemanticMap {
372    /// Get the texture semantic for the given variable name.
373    fn texture_semantic(&self, name: &str) -> Option<Semantic<TextureSemantics>>;
374}
375
376impl TextureSemanticMap for FastHashMap<ShortString, UniformSemantic> {
377    fn texture_semantic(&self, name: &str) -> Option<Semantic<TextureSemantics>> {
378        match self.get(name) {
379            None => {
380                if let Some(semantics) = TextureSemantics::TEXTURE_SEMANTICS
381                    .iter()
382                    .find(|f| name.starts_with(f.size_uniform_name()))
383                {
384                    if semantics.is_indexed() {
385                        let index = &name[semantics.size_uniform_name().len()..];
386                        let Ok(index) = usize::from_str(index) else {
387                            return None;
388                        };
389                        return Some(Semantic {
390                            semantics: *semantics,
391                            index,
392                        });
393                    } else if name == semantics.size_uniform_name() {
394                        return Some(Semantic {
395                            semantics: *semantics,
396                            index: 0,
397                        });
398                    }
399                }
400                None
401            }
402            Some(UniformSemantic::Unique(_)) => None,
403            Some(UniformSemantic::Texture(texture)) => Some(*texture),
404        }
405    }
406}
407
408impl TextureSemanticMap for FastHashMap<ShortString, Semantic<TextureSemantics>> {
409    fn texture_semantic(&self, name: &str) -> Option<Semantic<TextureSemantics>> {
410        match self.get(name) {
411            None => {
412                if let Some(semantics) = TextureSemantics::TEXTURE_SEMANTICS
413                    .iter()
414                    .find(|f| name.starts_with(f.texture_name()))
415                {
416                    if semantics.is_indexed() {
417                        let index = &name[semantics.texture_name().len()..];
418                        let Ok(index) = usize::from_str(index) else {
419                            return None;
420                        };
421                        return Some(Semantic {
422                            semantics: *semantics,
423                            index,
424                        });
425                    } else if name == semantics.texture_name() {
426                        return Some(Semantic {
427                            semantics: *semantics,
428                            index: 0,
429                        });
430                    }
431                }
432                None
433            }
434            Some(texture) => Some(*texture),
435        }
436    }
437}
438
439/// A trait for maps that can return unique semantic units.
440pub trait UniqueSemanticMap {
441    /// Get the unique semantic for the given variable name.
442    fn unique_semantic(&self, name: &str) -> Option<Semantic<UniqueSemantics, ()>>;
443}
444
445impl UniqueSemanticMap for FastHashMap<ShortString, UniformSemantic> {
446    fn unique_semantic(&self, name: &str) -> Option<Semantic<UniqueSemantics, ()>> {
447        match self.get(name) {
448            // existing uniforms in the semantic map have priority
449            None => match name {
450                "MVP" => Some(Semantic {
451                    semantics: UniqueSemantics::MVP,
452                    index: (),
453                }),
454                "OutputSize" => Some(Semantic {
455                    semantics: UniqueSemantics::Output,
456                    index: (),
457                }),
458                "FinalViewportSize" => Some(Semantic {
459                    semantics: UniqueSemantics::FinalViewport,
460                    index: (),
461                }),
462                "FrameCount" => Some(Semantic {
463                    semantics: UniqueSemantics::FrameCount,
464                    index: (),
465                }),
466                "FrameDirection" => Some(Semantic {
467                    semantics: UniqueSemantics::FrameDirection,
468                    index: (),
469                }),
470                "Rotation" => Some(Semantic {
471                    semantics: UniqueSemantics::Rotation,
472                    index: (),
473                }),
474                "TotalSubFrames" => Some(Semantic {
475                    semantics: UniqueSemantics::TotalSubFrames,
476                    index: (),
477                }),
478                "CurrentSubFrame" => Some(Semantic {
479                    semantics: UniqueSemantics::CurrentSubFrame,
480                    index: (),
481                }),
482                "OriginalAspect" => Some(Semantic {
483                    semantics: UniqueSemantics::OriginalAspect,
484                    index: (),
485                }),
486                "OriginalAspectRotated" => Some(Semantic {
487                    semantics: UniqueSemantics::OriginalAspectRotated,
488                    index: (),
489                }),
490                "OriginalFPS" => Some(Semantic {
491                    semantics: UniqueSemantics::OriginalFPS,
492                    index: (),
493                }),
494                "FrameTimeDelta" => Some(Semantic {
495                    semantics: UniqueSemantics::FrameTimeDelta,
496                    index: (),
497                }),
498                _ => None,
499            },
500            Some(UniformSemantic::Unique(variable)) => Some(*variable),
501            Some(UniformSemantic::Texture(_)) => None,
502        }
503    }
504}
505
506/// Semantic assignment of a shader uniform to filter chain semantics.
507#[derive(Debug, Clone)]
508#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
509pub enum UniformSemantic {
510    /// A unique semantic.
511    Unique(Semantic<UniqueSemantics, ()>),
512    /// A texture related semantic.
513    Texture(Semantic<TextureSemantics>),
514}
515
516/// The runtime provided maps of uniform and texture variables to filter chain semantics.
517#[derive(Debug, Clone)]
518#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
519pub struct ShaderSemantics {
520    /// A map of uniform names to filter chain semantics.
521    pub uniform_semantics: FastHashMap<ShortString, UniformSemantic>,
522    /// A map of texture names to filter chain semantics.
523    pub texture_semantics: FastHashMap<ShortString, Semantic<TextureSemantics>>,
524}
525
526/// The binding of a uniform after the shader has been linked.
527///
528/// Used in combination with [`MemberOffset`] to keep track
529/// of semantics at each frame pass.
530#[derive(Debug, Clone, Eq, Hash, PartialEq)]
531#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
532pub enum UniformBinding {
533    /// A user parameter (`float`) binding.
534    Parameter(ShortString),
535    /// A known semantic binding.
536    SemanticVariable(UniqueSemantics),
537    /// A texture size (`float4`) binding.
538    TextureSize(Semantic<TextureSemantics>),
539}
540
541impl From<UniqueSemantics> for UniformBinding {
542    fn from(value: UniqueSemantics) -> Self {
543        UniformBinding::SemanticVariable(value)
544    }
545}
546
547impl From<Semantic<TextureSemantics>> for UniformBinding {
548    fn from(value: Semantic<TextureSemantics>) -> Self {
549        UniformBinding::TextureSize(value)
550    }
551}
552
553/// Reflection metadata about the various bindings for this shader.
554#[derive(Debug, Default, Clone)]
555#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
556pub struct BindingMeta {
557    #[cfg_attr(feature = "serde", serde(rename = "param"))]
558    /// A map of parameter names to uniform binding metadata.
559    pub parameter_meta: FastHashMap<ShortString, VariableMeta>,
560    #[cfg_attr(feature = "serde", serde(rename = "unique"))]
561    /// A map of unique semantics to uniform binding metadata.
562    pub unique_meta: FastHashMap<UniqueSemantics, VariableMeta>,
563    #[cfg_attr(feature = "serde", serde(rename = "texture"))]
564    /// A map of texture semantics to texture binding points.
565    pub texture_meta: FastHashMap<Semantic<TextureSemantics>, TextureBinding>,
566    #[cfg_attr(feature = "serde", serde(rename = "texture_size"))]
567    /// A map of texture semantics to texture size uniform binding metadata.
568    pub texture_size_meta: FastHashMap<Semantic<TextureSemantics>, TextureSizeMeta>,
569}
570
571#[cfg(feature = "serde")]
572mod serde_impl {
573    use super::*;
574    use serde::de::{Deserialize, Visitor};
575    use serde::ser::Serialize;
576    use serde::{Deserializer, Serializer};
577
578    struct TextureSemanticVisitor;
579
580    impl<'de> Visitor<'de> for TextureSemanticVisitor {
581        type Value = Semantic<TextureSemantics>;
582
583        fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
584            formatter.write_str("a string of the form (Semantic)N?")
585        }
586
587        fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
588        where
589            E: serde::de::Error,
590        {
591            match v {
592                "Original" => Ok(TextureSemantics::Original.semantics(0)),
593                "Source" => Ok(TextureSemantics::Source.semantics(0)),
594                other => {
595                    let Some(index) = other.find(|c: char| c.is_digit(10)) else {
596                        return Err(E::custom(format!(
597                            "expected index for indexed texture semantic {v}"
598                        )));
599                    };
600
601                    let (semantic, index) = other.split_at(index);
602                    let Ok(index) = index.parse::<usize>() else {
603                        return Err(E::custom(format!(
604                            "could not parse index {index} of texture semantic {v}"
605                        )));
606                    };
607
608                    match semantic {
609                        "OriginalHistory" => Ok(TextureSemantics::OriginalHistory.semantics(index)),
610                        "PassOutput" => Ok(TextureSemantics::PassOutput.semantics(index)),
611                        "PassFeedback" => Ok(TextureSemantics::PassFeedback.semantics(index)),
612                        // everything else (including "User") is a user semantic.
613                        _ => Ok(TextureSemantics::User.semantics(index)),
614                    }
615                }
616            }
617        }
618    }
619    impl<'de> Deserialize<'de> for Semantic<TextureSemantics> {
620        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
621        where
622            D: Deserializer<'de>,
623        {
624            deserializer.deserialize_str(TextureSemanticVisitor)
625        }
626    }
627
628    impl Serialize for Semantic<TextureSemantics> {
629        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
630        where
631            S: Serializer,
632        {
633            if self.semantics.is_indexed() {
634                serializer.serialize_str(&format!(
635                    "{}{}",
636                    self.semantics.texture_name(),
637                    self.index
638                ))
639            } else {
640                serializer.serialize_str(&format!("{}", self.semantics.texture_name()))
641            }
642        }
643    }
644
645    struct UniqueSemanticsVisitor;
646    impl<'de> Visitor<'de> for UniqueSemanticsVisitor {
647        type Value = Semantic<UniqueSemantics, ()>;
648
649        fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
650            formatter.write_str("a valid uniform semantic name")
651        }
652
653        fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
654        where
655            E: serde::de::Error,
656        {
657            Ok(match v {
658                "MVP" => Semantic {
659                    semantics: UniqueSemantics::MVP,
660                    index: (),
661                },
662                "OutputSize" => Semantic {
663                    semantics: UniqueSemantics::Output,
664                    index: (),
665                },
666                "FinalViewportSize" => Semantic {
667                    semantics: UniqueSemantics::FinalViewport,
668                    index: (),
669                },
670                "FrameCount" => Semantic {
671                    semantics: UniqueSemantics::FrameCount,
672                    index: (),
673                },
674                "FrameDirection" => Semantic {
675                    semantics: UniqueSemantics::FrameDirection,
676                    index: (),
677                },
678                "Rotation" => Semantic {
679                    semantics: UniqueSemantics::Rotation,
680                    index: (),
681                },
682                "TotalSubFrames" => Semantic {
683                    semantics: UniqueSemantics::TotalSubFrames,
684                    index: (),
685                },
686                "CurrentSubFrame" => Semantic {
687                    semantics: UniqueSemantics::CurrentSubFrame,
688                    index: (),
689                },
690                _ => return Err(E::custom(format!("unknown unique semantic {v}"))),
691            })
692        }
693    }
694
695    impl Serialize for Semantic<UniqueSemantics, ()> {
696        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
697        where
698            S: Serializer,
699        {
700            serializer.serialize_str(self.semantics.as_str())
701        }
702    }
703
704    impl<'de> Deserialize<'de> for Semantic<UniqueSemantics, ()> {
705        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
706        where
707            D: Deserializer<'de>,
708        {
709            deserializer.deserialize_str(UniqueSemanticsVisitor)
710        }
711    }
712}