naga/back/spv/
f16_polyfill.rs

1/*!
2This module provides functionality for polyfilling `f16` input/output variables
3when the `StorageInputOutput16` capability is not available or disabled.
4
5It works by:
6
71. Declaring `f16` I/O variables as `f32` in SPIR-V
82. Converting between `f16` and `f32` at runtime using `OpFConvert`
93. Maintaining mappings to track which variables need conversion
10*/
11
12use crate::back::spv::{Instruction, LocalType, NumericType, Word};
13use alloc::vec::Vec;
14
15/// Manages `f16` I/O polyfill state and operations.
16#[derive(Default)]
17pub(in crate::back::spv) struct F16IoPolyfill {
18    use_native: bool,
19    io_var_to_f32_type: crate::FastHashMap<Word, Word>,
20}
21
22impl F16IoPolyfill {
23    pub fn new(use_storage_input_output_16: bool) -> Self {
24        Self {
25            use_native: use_storage_input_output_16,
26            io_var_to_f32_type: crate::FastHashMap::default(),
27        }
28    }
29
30    pub fn needs_polyfill(&self, ty_inner: &crate::TypeInner) -> bool {
31        use crate::{ScalarKind as Sk, TypeInner};
32
33        !self.use_native
34            && match *ty_inner {
35                TypeInner::Scalar(ref s) if s.kind == Sk::Float && s.width == 2 => true,
36                TypeInner::Vector { scalar, .. }
37                    if scalar.kind == Sk::Float && scalar.width == 2 =>
38                {
39                    true
40                }
41                _ => false,
42            }
43    }
44
45    pub fn register_io_var(&mut self, variable_id: Word, f32_type_id: Word) {
46        self.io_var_to_f32_type.insert(variable_id, f32_type_id);
47    }
48
49    pub fn get_f32_io_type(&self, variable_id: Word) -> Option<Word> {
50        self.io_var_to_f32_type.get(&variable_id).copied()
51    }
52
53    pub fn emit_f16_to_f32_conversion(
54        f16_value_id: Word,
55        f32_type_id: Word,
56        converted_id: Word,
57        body: &mut Vec<Instruction>,
58    ) {
59        body.push(Instruction::unary(
60            spirv::Op::FConvert,
61            f32_type_id,
62            converted_id,
63            f16_value_id,
64        ));
65    }
66
67    pub fn emit_f32_to_f16_conversion(
68        f32_value_id: Word,
69        f16_type_id: Word,
70        converted_id: Word,
71        body: &mut Vec<Instruction>,
72    ) {
73        body.push(Instruction::unary(
74            spirv::Op::FConvert,
75            f16_type_id,
76            converted_id,
77            f32_value_id,
78        ));
79    }
80
81    pub fn create_polyfill_type(ty_inner: &crate::TypeInner) -> Option<LocalType> {
82        use crate::{ScalarKind as Sk, TypeInner};
83
84        match *ty_inner {
85            TypeInner::Scalar(ref s) if s.kind == Sk::Float && s.width == 2 => {
86                Some(LocalType::Numeric(NumericType::Scalar(crate::Scalar::F32)))
87            }
88            TypeInner::Vector { size, scalar } if scalar.kind == Sk::Float && scalar.width == 2 => {
89                Some(LocalType::Numeric(NumericType::Vector {
90                    size,
91                    scalar: crate::Scalar::F32,
92                }))
93            }
94            _ => None,
95        }
96    }
97}
98
99impl crate::back::spv::recyclable::Recyclable for F16IoPolyfill {
100    fn recycle(mut self) -> Self {
101        self.io_var_to_f32_type = self.io_var_to_f32_type.recycle();
102        self
103    }
104}