astrelis_render/
features.rs1use bitflags::bitflags;
7
8bitflags! {
9 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
14 pub struct GpuFeatures: u32 {
15 const INDIRECT_FIRST_INSTANCE = 1 << 0;
18
19 const MULTI_DRAW_INDIRECT = 1 << 1;
22
23 const PUSH_CONSTANTS = 1 << 2;
26
27 const TEXTURE_COMPRESSION_BC = 1 << 3;
30
31 const DEPTH_CLIP_CONTROL = 1 << 4;
34
35 const SHADER_F16 = 1 << 5;
38
39 const INDIRECT_FIRST_VERTEX = 1 << 6;
42
43 const POLYGON_MODE_LINE = 1 << 7;
45
46 const POLYGON_MODE_POINT = 1 << 8;
48
49 const CONSERVATIVE_RASTERIZATION = 1 << 9;
51
52 const TEXTURE_BINDING_ARRAY = 1 << 10;
55
56 const BUFFER_BINDING_ARRAY = 1 << 11;
58
59 const STORAGE_RESOURCE_BINDING_ARRAY = 1 << 12;
61
62 const PARTIALLY_BOUND_BINDING_ARRAY = 1 << 13;
65
66 const FLOAT32_FILTERABLE = 1 << 14;
68
69 const RG11B10UFLOAT_RENDERABLE = 1 << 15;
71
72 const BGRA8UNORM_STORAGE = 1 << 16;
74 }
75}
76
77impl GpuFeatures {
78 pub fn to_wgpu(self) -> wgpu::Features {
80 let mut features = wgpu::Features::empty();
81
82 if self.contains(GpuFeatures::INDIRECT_FIRST_INSTANCE) {
83 features |= wgpu::Features::INDIRECT_FIRST_INSTANCE;
84 }
85 if self.contains(GpuFeatures::MULTI_DRAW_INDIRECT) {
86 features |= wgpu::Features::MULTI_DRAW_INDIRECT_COUNT;
87 }
88 if self.contains(GpuFeatures::PUSH_CONSTANTS) {
89 features |= wgpu::Features::PUSH_CONSTANTS;
90 }
91 if self.contains(GpuFeatures::TEXTURE_COMPRESSION_BC) {
92 features |= wgpu::Features::TEXTURE_COMPRESSION_BC;
93 }
94 if self.contains(GpuFeatures::DEPTH_CLIP_CONTROL) {
95 features |= wgpu::Features::DEPTH_CLIP_CONTROL;
96 }
97 if self.contains(GpuFeatures::SHADER_F16) {
98 features |= wgpu::Features::SHADER_F16;
99 }
100 if self.contains(GpuFeatures::POLYGON_MODE_LINE) {
101 features |= wgpu::Features::POLYGON_MODE_LINE;
102 }
103 if self.contains(GpuFeatures::POLYGON_MODE_POINT) {
104 features |= wgpu::Features::POLYGON_MODE_POINT;
105 }
106 if self.contains(GpuFeatures::CONSERVATIVE_RASTERIZATION) {
107 features |= wgpu::Features::CONSERVATIVE_RASTERIZATION;
108 }
109 if self.contains(GpuFeatures::TEXTURE_BINDING_ARRAY) {
110 features |= wgpu::Features::TEXTURE_BINDING_ARRAY;
111 }
112 if self.contains(GpuFeatures::BUFFER_BINDING_ARRAY) {
113 features |= wgpu::Features::BUFFER_BINDING_ARRAY;
114 }
115 if self.contains(GpuFeatures::STORAGE_RESOURCE_BINDING_ARRAY) {
116 features |= wgpu::Features::STORAGE_RESOURCE_BINDING_ARRAY;
117 }
118 if self.contains(GpuFeatures::PARTIALLY_BOUND_BINDING_ARRAY) {
119 features |= wgpu::Features::PARTIALLY_BOUND_BINDING_ARRAY;
120 }
121 if self.contains(GpuFeatures::FLOAT32_FILTERABLE) {
122 features |= wgpu::Features::FLOAT32_FILTERABLE;
123 }
124 if self.contains(GpuFeatures::RG11B10UFLOAT_RENDERABLE) {
125 features |= wgpu::Features::RG11B10UFLOAT_RENDERABLE;
126 }
127 if self.contains(GpuFeatures::BGRA8UNORM_STORAGE) {
128 features |= wgpu::Features::BGRA8UNORM_STORAGE;
129 }
130
131 features
132 }
133
134 pub fn from_wgpu(features: wgpu::Features) -> Self {
138 let mut gpu_features = GpuFeatures::empty();
139
140 if features.contains(wgpu::Features::INDIRECT_FIRST_INSTANCE) {
141 gpu_features |= GpuFeatures::INDIRECT_FIRST_INSTANCE;
142 }
143 if features.contains(wgpu::Features::MULTI_DRAW_INDIRECT_COUNT) {
144 gpu_features |= GpuFeatures::MULTI_DRAW_INDIRECT;
145 }
146 if features.contains(wgpu::Features::PUSH_CONSTANTS) {
147 gpu_features |= GpuFeatures::PUSH_CONSTANTS;
148 }
149 if features.contains(wgpu::Features::TEXTURE_COMPRESSION_BC) {
150 gpu_features |= GpuFeatures::TEXTURE_COMPRESSION_BC;
151 }
152 if features.contains(wgpu::Features::DEPTH_CLIP_CONTROL) {
153 gpu_features |= GpuFeatures::DEPTH_CLIP_CONTROL;
154 }
155 if features.contains(wgpu::Features::SHADER_F16) {
156 gpu_features |= GpuFeatures::SHADER_F16;
157 }
158 if features.contains(wgpu::Features::POLYGON_MODE_LINE) {
159 gpu_features |= GpuFeatures::POLYGON_MODE_LINE;
160 }
161 if features.contains(wgpu::Features::POLYGON_MODE_POINT) {
162 gpu_features |= GpuFeatures::POLYGON_MODE_POINT;
163 }
164 if features.contains(wgpu::Features::CONSERVATIVE_RASTERIZATION) {
165 gpu_features |= GpuFeatures::CONSERVATIVE_RASTERIZATION;
166 }
167 if features.contains(wgpu::Features::TEXTURE_BINDING_ARRAY) {
168 gpu_features |= GpuFeatures::TEXTURE_BINDING_ARRAY;
169 }
170 if features.contains(wgpu::Features::BUFFER_BINDING_ARRAY) {
171 gpu_features |= GpuFeatures::BUFFER_BINDING_ARRAY;
172 }
173 if features.contains(wgpu::Features::STORAGE_RESOURCE_BINDING_ARRAY) {
174 gpu_features |= GpuFeatures::STORAGE_RESOURCE_BINDING_ARRAY;
175 }
176 if features.contains(wgpu::Features::PARTIALLY_BOUND_BINDING_ARRAY) {
177 gpu_features |= GpuFeatures::PARTIALLY_BOUND_BINDING_ARRAY;
178 }
179 if features.contains(wgpu::Features::FLOAT32_FILTERABLE) {
180 gpu_features |= GpuFeatures::FLOAT32_FILTERABLE;
181 }
182 if features.contains(wgpu::Features::RG11B10UFLOAT_RENDERABLE) {
183 gpu_features |= GpuFeatures::RG11B10UFLOAT_RENDERABLE;
184 }
185 if features.contains(wgpu::Features::BGRA8UNORM_STORAGE) {
186 gpu_features |= GpuFeatures::BGRA8UNORM_STORAGE;
187 }
188
189 gpu_features
190 }
191
192 pub fn check_support(self, adapter: &wgpu::Adapter) -> FeatureSupportResult {
194 let adapter_features = GpuFeatures::from_wgpu(adapter.features());
195 let missing = self - (self & adapter_features);
196
197 if missing.is_empty() {
198 FeatureSupportResult::Supported
199 } else {
200 FeatureSupportResult::Missing(missing)
201 }
202 }
203}
204
205impl Default for GpuFeatures {
206 fn default() -> Self {
207 GpuFeatures::empty()
208 }
209}
210
211#[derive(Debug, Clone, Copy, PartialEq, Eq)]
213pub enum FeatureSupportResult {
214 Supported,
216 Missing(GpuFeatures),
218}
219
220impl FeatureSupportResult {
221 pub fn is_supported(&self) -> bool {
223 matches!(self, FeatureSupportResult::Supported)
224 }
225
226 pub fn missing(&self) -> Option<GpuFeatures> {
228 match self {
229 FeatureSupportResult::Supported => None,
230 FeatureSupportResult::Missing(features) => Some(*features),
231 }
232 }
233}
234
235#[cfg(test)]
236mod tests {
237 use super::*;
238
239 #[test]
240 fn test_gpu_features_empty() {
241 let features = GpuFeatures::empty();
242 assert!(features.is_empty());
243 assert_eq!(features.to_wgpu(), wgpu::Features::empty());
244 }
245
246 #[test]
247 fn test_gpu_features_roundtrip() {
248 let features = GpuFeatures::INDIRECT_FIRST_INSTANCE
249 | GpuFeatures::MULTI_DRAW_INDIRECT
250 | GpuFeatures::PUSH_CONSTANTS;
251
252 let wgpu_features = features.to_wgpu();
253 let back = GpuFeatures::from_wgpu(wgpu_features);
254
255 assert_eq!(features, back);
256 }
257
258 #[test]
259 fn test_gpu_features_contains() {
260 let features = GpuFeatures::INDIRECT_FIRST_INSTANCE | GpuFeatures::PUSH_CONSTANTS;
261
262 assert!(features.contains(GpuFeatures::INDIRECT_FIRST_INSTANCE));
263 assert!(features.contains(GpuFeatures::PUSH_CONSTANTS));
264 assert!(!features.contains(GpuFeatures::MULTI_DRAW_INDIRECT));
265 }
266}