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_COUNT = 1 << 1;
24
25 const PUSH_CONSTANTS = 1 << 2;
28
29 const TEXTURE_COMPRESSION_BC = 1 << 3;
32
33 const DEPTH_CLIP_CONTROL = 1 << 4;
36
37 const SHADER_F16 = 1 << 5;
40
41 const INDIRECT_FIRST_VERTEX = 1 << 6;
44
45 const POLYGON_MODE_LINE = 1 << 7;
47
48 const POLYGON_MODE_POINT = 1 << 8;
50
51 const CONSERVATIVE_RASTERIZATION = 1 << 9;
53
54 const TEXTURE_BINDING_ARRAY = 1 << 10;
57
58 const BUFFER_BINDING_ARRAY = 1 << 11;
60
61 const STORAGE_RESOURCE_BINDING_ARRAY = 1 << 12;
63
64 const PARTIALLY_BOUND_BINDING_ARRAY = 1 << 13;
67
68 const FLOAT32_FILTERABLE = 1 << 14;
70
71 const RG11B10UFLOAT_RENDERABLE = 1 << 15;
73
74 const BGRA8UNORM_STORAGE = 1 << 16;
76
77 const TIMESTAMP_QUERY = 1 << 17;
82
83 const TIMESTAMP_QUERY_INSIDE_ENCODERS = 1 << 19;
87
88 const TIMESTAMP_QUERY_INSIDE_PASSES = 1 << 20;
92
93 const SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING = 1 << 18;
97 }
98}
99
100impl GpuFeatures {
101 pub fn to_wgpu(self) -> wgpu::Features {
103 let mut features = wgpu::Features::empty();
104
105 if self.contains(GpuFeatures::INDIRECT_FIRST_INSTANCE) {
106 features |= wgpu::Features::INDIRECT_FIRST_INSTANCE;
107 }
108 if self.contains(GpuFeatures::MULTI_DRAW_INDIRECT_COUNT) {
109 features |= wgpu::Features::MULTI_DRAW_INDIRECT_COUNT;
110 }
111 if self.contains(GpuFeatures::PUSH_CONSTANTS) {
112 features |= wgpu::Features::PUSH_CONSTANTS;
113 }
114 if self.contains(GpuFeatures::TEXTURE_COMPRESSION_BC) {
115 features |= wgpu::Features::TEXTURE_COMPRESSION_BC;
116 }
117 if self.contains(GpuFeatures::DEPTH_CLIP_CONTROL) {
118 features |= wgpu::Features::DEPTH_CLIP_CONTROL;
119 }
120 if self.contains(GpuFeatures::SHADER_F16) {
121 features |= wgpu::Features::SHADER_F16;
122 }
123 if self.contains(GpuFeatures::POLYGON_MODE_LINE) {
124 features |= wgpu::Features::POLYGON_MODE_LINE;
125 }
126 if self.contains(GpuFeatures::POLYGON_MODE_POINT) {
127 features |= wgpu::Features::POLYGON_MODE_POINT;
128 }
129 if self.contains(GpuFeatures::CONSERVATIVE_RASTERIZATION) {
130 features |= wgpu::Features::CONSERVATIVE_RASTERIZATION;
131 }
132 if self.contains(GpuFeatures::TEXTURE_BINDING_ARRAY) {
133 features |= wgpu::Features::TEXTURE_BINDING_ARRAY;
134 }
135 if self.contains(GpuFeatures::BUFFER_BINDING_ARRAY) {
136 features |= wgpu::Features::BUFFER_BINDING_ARRAY;
137 }
138 if self.contains(GpuFeatures::STORAGE_RESOURCE_BINDING_ARRAY) {
139 features |= wgpu::Features::STORAGE_RESOURCE_BINDING_ARRAY;
140 }
141 if self.contains(GpuFeatures::PARTIALLY_BOUND_BINDING_ARRAY) {
142 features |= wgpu::Features::PARTIALLY_BOUND_BINDING_ARRAY;
143 }
144 if self.contains(GpuFeatures::FLOAT32_FILTERABLE) {
145 features |= wgpu::Features::FLOAT32_FILTERABLE;
146 }
147 if self.contains(GpuFeatures::RG11B10UFLOAT_RENDERABLE) {
148 features |= wgpu::Features::RG11B10UFLOAT_RENDERABLE;
149 }
150 if self.contains(GpuFeatures::BGRA8UNORM_STORAGE) {
151 features |= wgpu::Features::BGRA8UNORM_STORAGE;
152 }
153 if self.contains(GpuFeatures::TIMESTAMP_QUERY) {
154 features |= wgpu::Features::TIMESTAMP_QUERY;
155 }
156 if self.contains(GpuFeatures::TIMESTAMP_QUERY_INSIDE_ENCODERS) {
157 features |= wgpu::Features::TIMESTAMP_QUERY_INSIDE_ENCODERS;
158 }
159 if self.contains(GpuFeatures::TIMESTAMP_QUERY_INSIDE_PASSES) {
160 features |= wgpu::Features::TIMESTAMP_QUERY_INSIDE_PASSES;
161 }
162 if self.contains(GpuFeatures::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING)
163 {
164 features |=
165 wgpu::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING;
166 }
167
168 features
169 }
170
171 pub fn from_wgpu(features: wgpu::Features) -> Self {
175 let mut gpu_features = GpuFeatures::empty();
176
177 if features.contains(wgpu::Features::INDIRECT_FIRST_INSTANCE) {
178 gpu_features |= GpuFeatures::INDIRECT_FIRST_INSTANCE;
179 }
180 if features.contains(wgpu::Features::MULTI_DRAW_INDIRECT_COUNT) {
181 gpu_features |= GpuFeatures::MULTI_DRAW_INDIRECT_COUNT;
182 }
183 if features.contains(wgpu::Features::PUSH_CONSTANTS) {
184 gpu_features |= GpuFeatures::PUSH_CONSTANTS;
185 }
186 if features.contains(wgpu::Features::TEXTURE_COMPRESSION_BC) {
187 gpu_features |= GpuFeatures::TEXTURE_COMPRESSION_BC;
188 }
189 if features.contains(wgpu::Features::DEPTH_CLIP_CONTROL) {
190 gpu_features |= GpuFeatures::DEPTH_CLIP_CONTROL;
191 }
192 if features.contains(wgpu::Features::SHADER_F16) {
193 gpu_features |= GpuFeatures::SHADER_F16;
194 }
195 if features.contains(wgpu::Features::POLYGON_MODE_LINE) {
196 gpu_features |= GpuFeatures::POLYGON_MODE_LINE;
197 }
198 if features.contains(wgpu::Features::POLYGON_MODE_POINT) {
199 gpu_features |= GpuFeatures::POLYGON_MODE_POINT;
200 }
201 if features.contains(wgpu::Features::CONSERVATIVE_RASTERIZATION) {
202 gpu_features |= GpuFeatures::CONSERVATIVE_RASTERIZATION;
203 }
204 if features.contains(wgpu::Features::TEXTURE_BINDING_ARRAY) {
205 gpu_features |= GpuFeatures::TEXTURE_BINDING_ARRAY;
206 }
207 if features.contains(wgpu::Features::BUFFER_BINDING_ARRAY) {
208 gpu_features |= GpuFeatures::BUFFER_BINDING_ARRAY;
209 }
210 if features.contains(wgpu::Features::STORAGE_RESOURCE_BINDING_ARRAY) {
211 gpu_features |= GpuFeatures::STORAGE_RESOURCE_BINDING_ARRAY;
212 }
213 if features.contains(wgpu::Features::PARTIALLY_BOUND_BINDING_ARRAY) {
214 gpu_features |= GpuFeatures::PARTIALLY_BOUND_BINDING_ARRAY;
215 }
216 if features.contains(wgpu::Features::FLOAT32_FILTERABLE) {
217 gpu_features |= GpuFeatures::FLOAT32_FILTERABLE;
218 }
219 if features.contains(wgpu::Features::RG11B10UFLOAT_RENDERABLE) {
220 gpu_features |= GpuFeatures::RG11B10UFLOAT_RENDERABLE;
221 }
222 if features.contains(wgpu::Features::BGRA8UNORM_STORAGE) {
223 gpu_features |= GpuFeatures::BGRA8UNORM_STORAGE;
224 }
225 if features.contains(wgpu::Features::TIMESTAMP_QUERY) {
226 gpu_features |= GpuFeatures::TIMESTAMP_QUERY;
227 }
228 if features.contains(wgpu::Features::TIMESTAMP_QUERY_INSIDE_ENCODERS) {
229 gpu_features |= GpuFeatures::TIMESTAMP_QUERY_INSIDE_ENCODERS;
230 }
231 if features.contains(wgpu::Features::TIMESTAMP_QUERY_INSIDE_PASSES) {
232 gpu_features |= GpuFeatures::TIMESTAMP_QUERY_INSIDE_PASSES;
233 }
234 if features
235 .contains(wgpu::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING)
236 {
237 gpu_features |=
238 GpuFeatures::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING;
239 }
240
241 gpu_features
242 }
243
244 pub fn check_support(self, adapter: &wgpu::Adapter) -> FeatureSupportResult {
246 let adapter_features = GpuFeatures::from_wgpu(adapter.features());
247 let missing = self - (self & adapter_features);
248
249 if missing.is_empty() {
250 FeatureSupportResult::Supported
251 } else {
252 FeatureSupportResult::Missing(missing)
253 }
254 }
255}
256
257impl Default for GpuFeatures {
258 fn default() -> Self {
259 GpuFeatures::empty()
260 }
261}
262
263#[derive(Debug, Clone, Copy, PartialEq, Eq)]
265pub enum FeatureSupportResult {
266 Supported,
268 Missing(GpuFeatures),
270}
271
272impl FeatureSupportResult {
273 pub fn is_supported(&self) -> bool {
275 matches!(self, FeatureSupportResult::Supported)
276 }
277
278 pub fn missing(&self) -> Option<GpuFeatures> {
280 match self {
281 FeatureSupportResult::Supported => None,
282 FeatureSupportResult::Missing(features) => Some(*features),
283 }
284 }
285}
286
287#[cfg(test)]
288mod tests {
289 use super::*;
290
291 #[test]
292 fn test_gpu_features_empty() {
293 let features = GpuFeatures::empty();
294 assert!(features.is_empty());
295 assert_eq!(features.to_wgpu(), wgpu::Features::empty());
296 }
297
298 #[test]
299 fn test_gpu_features_roundtrip() {
300 let features = GpuFeatures::INDIRECT_FIRST_INSTANCE
301 | GpuFeatures::MULTI_DRAW_INDIRECT_COUNT
302 | GpuFeatures::PUSH_CONSTANTS
303 | GpuFeatures::TIMESTAMP_QUERY;
304
305 let wgpu_features = features.to_wgpu();
306 let back = GpuFeatures::from_wgpu(wgpu_features);
307
308 assert_eq!(features, back);
309 }
310
311 #[test]
312 fn test_gpu_features_contains() {
313 let features = GpuFeatures::INDIRECT_FIRST_INSTANCE | GpuFeatures::PUSH_CONSTANTS;
314
315 assert!(features.contains(GpuFeatures::INDIRECT_FIRST_INSTANCE));
316 assert!(features.contains(GpuFeatures::PUSH_CONSTANTS));
317 assert!(!features.contains(GpuFeatures::MULTI_DRAW_INDIRECT_COUNT));
318 }
319}