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 features |= wgpu::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING;
164 }
165
166 features
167 }
168
169 pub fn from_wgpu(features: wgpu::Features) -> Self {
173 let mut gpu_features = GpuFeatures::empty();
174
175 if features.contains(wgpu::Features::INDIRECT_FIRST_INSTANCE) {
176 gpu_features |= GpuFeatures::INDIRECT_FIRST_INSTANCE;
177 }
178 if features.contains(wgpu::Features::MULTI_DRAW_INDIRECT_COUNT) {
179 gpu_features |= GpuFeatures::MULTI_DRAW_INDIRECT_COUNT;
180 }
181 if features.contains(wgpu::Features::PUSH_CONSTANTS) {
182 gpu_features |= GpuFeatures::PUSH_CONSTANTS;
183 }
184 if features.contains(wgpu::Features::TEXTURE_COMPRESSION_BC) {
185 gpu_features |= GpuFeatures::TEXTURE_COMPRESSION_BC;
186 }
187 if features.contains(wgpu::Features::DEPTH_CLIP_CONTROL) {
188 gpu_features |= GpuFeatures::DEPTH_CLIP_CONTROL;
189 }
190 if features.contains(wgpu::Features::SHADER_F16) {
191 gpu_features |= GpuFeatures::SHADER_F16;
192 }
193 if features.contains(wgpu::Features::POLYGON_MODE_LINE) {
194 gpu_features |= GpuFeatures::POLYGON_MODE_LINE;
195 }
196 if features.contains(wgpu::Features::POLYGON_MODE_POINT) {
197 gpu_features |= GpuFeatures::POLYGON_MODE_POINT;
198 }
199 if features.contains(wgpu::Features::CONSERVATIVE_RASTERIZATION) {
200 gpu_features |= GpuFeatures::CONSERVATIVE_RASTERIZATION;
201 }
202 if features.contains(wgpu::Features::TEXTURE_BINDING_ARRAY) {
203 gpu_features |= GpuFeatures::TEXTURE_BINDING_ARRAY;
204 }
205 if features.contains(wgpu::Features::BUFFER_BINDING_ARRAY) {
206 gpu_features |= GpuFeatures::BUFFER_BINDING_ARRAY;
207 }
208 if features.contains(wgpu::Features::STORAGE_RESOURCE_BINDING_ARRAY) {
209 gpu_features |= GpuFeatures::STORAGE_RESOURCE_BINDING_ARRAY;
210 }
211 if features.contains(wgpu::Features::PARTIALLY_BOUND_BINDING_ARRAY) {
212 gpu_features |= GpuFeatures::PARTIALLY_BOUND_BINDING_ARRAY;
213 }
214 if features.contains(wgpu::Features::FLOAT32_FILTERABLE) {
215 gpu_features |= GpuFeatures::FLOAT32_FILTERABLE;
216 }
217 if features.contains(wgpu::Features::RG11B10UFLOAT_RENDERABLE) {
218 gpu_features |= GpuFeatures::RG11B10UFLOAT_RENDERABLE;
219 }
220 if features.contains(wgpu::Features::BGRA8UNORM_STORAGE) {
221 gpu_features |= GpuFeatures::BGRA8UNORM_STORAGE;
222 }
223 if features.contains(wgpu::Features::TIMESTAMP_QUERY) {
224 gpu_features |= GpuFeatures::TIMESTAMP_QUERY;
225 }
226 if features.contains(wgpu::Features::TIMESTAMP_QUERY_INSIDE_ENCODERS) {
227 gpu_features |= GpuFeatures::TIMESTAMP_QUERY_INSIDE_ENCODERS;
228 }
229 if features.contains(wgpu::Features::TIMESTAMP_QUERY_INSIDE_PASSES) {
230 gpu_features |= GpuFeatures::TIMESTAMP_QUERY_INSIDE_PASSES;
231 }
232 if features.contains(wgpu::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING) {
233 gpu_features |= GpuFeatures::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING;
234 }
235
236 gpu_features
237 }
238
239 pub fn check_support(self, adapter: &wgpu::Adapter) -> FeatureSupportResult {
241 let adapter_features = GpuFeatures::from_wgpu(adapter.features());
242 let missing = self - (self & adapter_features);
243
244 if missing.is_empty() {
245 FeatureSupportResult::Supported
246 } else {
247 FeatureSupportResult::Missing(missing)
248 }
249 }
250}
251
252impl Default for GpuFeatures {
253 fn default() -> Self {
254 GpuFeatures::empty()
255 }
256}
257
258#[derive(Debug, Clone, Copy, PartialEq, Eq)]
260pub enum FeatureSupportResult {
261 Supported,
263 Missing(GpuFeatures),
265}
266
267impl FeatureSupportResult {
268 pub fn is_supported(&self) -> bool {
270 matches!(self, FeatureSupportResult::Supported)
271 }
272
273 pub fn missing(&self) -> Option<GpuFeatures> {
275 match self {
276 FeatureSupportResult::Supported => None,
277 FeatureSupportResult::Missing(features) => Some(*features),
278 }
279 }
280}
281
282#[cfg(test)]
283mod tests {
284 use super::*;
285
286 #[test]
287 fn test_gpu_features_empty() {
288 let features = GpuFeatures::empty();
289 assert!(features.is_empty());
290 assert_eq!(features.to_wgpu(), wgpu::Features::empty());
291 }
292
293 #[test]
294 fn test_gpu_features_roundtrip() {
295 let features = GpuFeatures::INDIRECT_FIRST_INSTANCE
296 | GpuFeatures::MULTI_DRAW_INDIRECT_COUNT
297 | GpuFeatures::PUSH_CONSTANTS
298 | GpuFeatures::TIMESTAMP_QUERY;
299
300 let wgpu_features = features.to_wgpu();
301 let back = GpuFeatures::from_wgpu(wgpu_features);
302
303 assert_eq!(features, back);
304 }
305
306 #[test]
307 fn test_gpu_features_contains() {
308 let features = GpuFeatures::INDIRECT_FIRST_INSTANCE | GpuFeatures::PUSH_CONSTANTS;
309
310 assert!(features.contains(GpuFeatures::INDIRECT_FIRST_INSTANCE));
311 assert!(features.contains(GpuFeatures::PUSH_CONSTANTS));
312 assert!(!features.contains(GpuFeatures::MULTI_DRAW_INDIRECT_COUNT));
313 }
314}