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 const TIMESTAMP_QUERY = 1 << 17;
78 }
79}
80
81impl GpuFeatures {
82 pub fn to_wgpu(self) -> wgpu::Features {
84 let mut features = wgpu::Features::empty();
85
86 if self.contains(GpuFeatures::INDIRECT_FIRST_INSTANCE) {
87 features |= wgpu::Features::INDIRECT_FIRST_INSTANCE;
88 }
89 if self.contains(GpuFeatures::MULTI_DRAW_INDIRECT) {
90 features |= wgpu::Features::MULTI_DRAW_INDIRECT_COUNT;
91 }
92 if self.contains(GpuFeatures::PUSH_CONSTANTS) {
93 features |= wgpu::Features::PUSH_CONSTANTS;
94 }
95 if self.contains(GpuFeatures::TEXTURE_COMPRESSION_BC) {
96 features |= wgpu::Features::TEXTURE_COMPRESSION_BC;
97 }
98 if self.contains(GpuFeatures::DEPTH_CLIP_CONTROL) {
99 features |= wgpu::Features::DEPTH_CLIP_CONTROL;
100 }
101 if self.contains(GpuFeatures::SHADER_F16) {
102 features |= wgpu::Features::SHADER_F16;
103 }
104 if self.contains(GpuFeatures::POLYGON_MODE_LINE) {
105 features |= wgpu::Features::POLYGON_MODE_LINE;
106 }
107 if self.contains(GpuFeatures::POLYGON_MODE_POINT) {
108 features |= wgpu::Features::POLYGON_MODE_POINT;
109 }
110 if self.contains(GpuFeatures::CONSERVATIVE_RASTERIZATION) {
111 features |= wgpu::Features::CONSERVATIVE_RASTERIZATION;
112 }
113 if self.contains(GpuFeatures::TEXTURE_BINDING_ARRAY) {
114 features |= wgpu::Features::TEXTURE_BINDING_ARRAY;
115 }
116 if self.contains(GpuFeatures::BUFFER_BINDING_ARRAY) {
117 features |= wgpu::Features::BUFFER_BINDING_ARRAY;
118 }
119 if self.contains(GpuFeatures::STORAGE_RESOURCE_BINDING_ARRAY) {
120 features |= wgpu::Features::STORAGE_RESOURCE_BINDING_ARRAY;
121 }
122 if self.contains(GpuFeatures::PARTIALLY_BOUND_BINDING_ARRAY) {
123 features |= wgpu::Features::PARTIALLY_BOUND_BINDING_ARRAY;
124 }
125 if self.contains(GpuFeatures::FLOAT32_FILTERABLE) {
126 features |= wgpu::Features::FLOAT32_FILTERABLE;
127 }
128 if self.contains(GpuFeatures::RG11B10UFLOAT_RENDERABLE) {
129 features |= wgpu::Features::RG11B10UFLOAT_RENDERABLE;
130 }
131 if self.contains(GpuFeatures::BGRA8UNORM_STORAGE) {
132 features |= wgpu::Features::BGRA8UNORM_STORAGE;
133 }
134 if self.contains(GpuFeatures::TIMESTAMP_QUERY) {
135 features |= wgpu::Features::TIMESTAMP_QUERY;
136 }
137
138 features
139 }
140
141 pub fn from_wgpu(features: wgpu::Features) -> Self {
145 let mut gpu_features = GpuFeatures::empty();
146
147 if features.contains(wgpu::Features::INDIRECT_FIRST_INSTANCE) {
148 gpu_features |= GpuFeatures::INDIRECT_FIRST_INSTANCE;
149 }
150 if features.contains(wgpu::Features::MULTI_DRAW_INDIRECT_COUNT) {
151 gpu_features |= GpuFeatures::MULTI_DRAW_INDIRECT;
152 }
153 if features.contains(wgpu::Features::PUSH_CONSTANTS) {
154 gpu_features |= GpuFeatures::PUSH_CONSTANTS;
155 }
156 if features.contains(wgpu::Features::TEXTURE_COMPRESSION_BC) {
157 gpu_features |= GpuFeatures::TEXTURE_COMPRESSION_BC;
158 }
159 if features.contains(wgpu::Features::DEPTH_CLIP_CONTROL) {
160 gpu_features |= GpuFeatures::DEPTH_CLIP_CONTROL;
161 }
162 if features.contains(wgpu::Features::SHADER_F16) {
163 gpu_features |= GpuFeatures::SHADER_F16;
164 }
165 if features.contains(wgpu::Features::POLYGON_MODE_LINE) {
166 gpu_features |= GpuFeatures::POLYGON_MODE_LINE;
167 }
168 if features.contains(wgpu::Features::POLYGON_MODE_POINT) {
169 gpu_features |= GpuFeatures::POLYGON_MODE_POINT;
170 }
171 if features.contains(wgpu::Features::CONSERVATIVE_RASTERIZATION) {
172 gpu_features |= GpuFeatures::CONSERVATIVE_RASTERIZATION;
173 }
174 if features.contains(wgpu::Features::TEXTURE_BINDING_ARRAY) {
175 gpu_features |= GpuFeatures::TEXTURE_BINDING_ARRAY;
176 }
177 if features.contains(wgpu::Features::BUFFER_BINDING_ARRAY) {
178 gpu_features |= GpuFeatures::BUFFER_BINDING_ARRAY;
179 }
180 if features.contains(wgpu::Features::STORAGE_RESOURCE_BINDING_ARRAY) {
181 gpu_features |= GpuFeatures::STORAGE_RESOURCE_BINDING_ARRAY;
182 }
183 if features.contains(wgpu::Features::PARTIALLY_BOUND_BINDING_ARRAY) {
184 gpu_features |= GpuFeatures::PARTIALLY_BOUND_BINDING_ARRAY;
185 }
186 if features.contains(wgpu::Features::FLOAT32_FILTERABLE) {
187 gpu_features |= GpuFeatures::FLOAT32_FILTERABLE;
188 }
189 if features.contains(wgpu::Features::RG11B10UFLOAT_RENDERABLE) {
190 gpu_features |= GpuFeatures::RG11B10UFLOAT_RENDERABLE;
191 }
192 if features.contains(wgpu::Features::BGRA8UNORM_STORAGE) {
193 gpu_features |= GpuFeatures::BGRA8UNORM_STORAGE;
194 }
195 if features.contains(wgpu::Features::TIMESTAMP_QUERY) {
196 gpu_features |= GpuFeatures::TIMESTAMP_QUERY;
197 }
198
199 gpu_features
200 }
201
202 pub fn check_support(self, adapter: &wgpu::Adapter) -> FeatureSupportResult {
204 let adapter_features = GpuFeatures::from_wgpu(adapter.features());
205 let missing = self - (self & adapter_features);
206
207 if missing.is_empty() {
208 FeatureSupportResult::Supported
209 } else {
210 FeatureSupportResult::Missing(missing)
211 }
212 }
213}
214
215impl Default for GpuFeatures {
216 fn default() -> Self {
217 GpuFeatures::empty()
218 }
219}
220
221#[derive(Debug, Clone, Copy, PartialEq, Eq)]
223pub enum FeatureSupportResult {
224 Supported,
226 Missing(GpuFeatures),
228}
229
230impl FeatureSupportResult {
231 pub fn is_supported(&self) -> bool {
233 matches!(self, FeatureSupportResult::Supported)
234 }
235
236 pub fn missing(&self) -> Option<GpuFeatures> {
238 match self {
239 FeatureSupportResult::Supported => None,
240 FeatureSupportResult::Missing(features) => Some(*features),
241 }
242 }
243}
244
245#[cfg(test)]
246mod tests {
247 use super::*;
248
249 #[test]
250 fn test_gpu_features_empty() {
251 let features = GpuFeatures::empty();
252 assert!(features.is_empty());
253 assert_eq!(features.to_wgpu(), wgpu::Features::empty());
254 }
255
256 #[test]
257 fn test_gpu_features_roundtrip() {
258 let features = GpuFeatures::INDIRECT_FIRST_INSTANCE
259 | GpuFeatures::MULTI_DRAW_INDIRECT
260 | GpuFeatures::PUSH_CONSTANTS
261 | GpuFeatures::TIMESTAMP_QUERY;
262
263 let wgpu_features = features.to_wgpu();
264 let back = GpuFeatures::from_wgpu(wgpu_features);
265
266 assert_eq!(features, back);
267 }
268
269 #[test]
270 fn test_gpu_features_contains() {
271 let features = GpuFeatures::INDIRECT_FIRST_INSTANCE | GpuFeatures::PUSH_CONSTANTS;
272
273 assert!(features.contains(GpuFeatures::INDIRECT_FIRST_INSTANCE));
274 assert!(features.contains(GpuFeatures::PUSH_CONSTANTS));
275 assert!(!features.contains(GpuFeatures::MULTI_DRAW_INDIRECT));
276 }
277}