astrelis_render/
context.rs1use crate::features::GpuFeatures;
2
3pub struct GraphicsContext {
5 pub instance: wgpu::Instance,
6 pub adapter: wgpu::Adapter,
7 pub device: wgpu::Device,
8 pub queue: wgpu::Queue,
9 enabled_features: GpuFeatures,
11}
12
13impl GraphicsContext {
14 pub fn new_sync() -> &'static Self {
18 pollster::block_on(Self::new())
19 }
20
21 pub async fn new() -> &'static Self {
25 Self::new_with_descriptor(GraphicsContextDescriptor::default()).await
26 }
27
28 pub async fn new_with_descriptor(descriptor: GraphicsContextDescriptor) -> &'static Self {
34 let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor {
35 backends: descriptor.backends,
36 ..Default::default()
37 });
38
39 let adapter = instance
40 .request_adapter(&wgpu::RequestAdapterOptions {
41 power_preference: descriptor.power_preference,
42 compatible_surface: None,
43 force_fallback_adapter: descriptor.force_fallback_adapter,
44 })
45 .await
46 .expect("Failed to find a suitable GPU adapter");
47
48 let required_result = descriptor.required_gpu_features.check_support(&adapter);
50 if let Some(missing) = required_result.missing() {
51 panic!(
52 "Required GPU features are not supported by the adapter: {:?}\n\
53 Adapter: {:?}\n\
54 Supported features: {:?}",
55 missing,
56 adapter.get_info().name,
57 GpuFeatures::from_wgpu(adapter.features())
58 );
59 }
60
61 let available_requested = descriptor.requested_gpu_features
63 & GpuFeatures::from_wgpu(adapter.features());
64
65 let unavailable_requested =
67 descriptor.requested_gpu_features - available_requested;
68 if !unavailable_requested.is_empty() {
69 tracing::warn!(
70 "Some requested GPU features are not available: {:?}",
71 unavailable_requested
72 );
73 }
74
75 let enabled_features = descriptor.required_gpu_features | available_requested;
77 let wgpu_features = enabled_features.to_wgpu() | descriptor.additional_wgpu_features;
78
79 let (device, queue) = adapter
80 .request_device(&wgpu::DeviceDescriptor {
81 required_features: wgpu_features,
82 required_limits: descriptor.limits.clone(),
83 label: descriptor.label,
84 ..Default::default()
85 })
86 .await
87 .expect("Failed to create device");
88
89 tracing::info!(
90 "Created graphics context with features: {:?}",
91 enabled_features
92 );
93
94 Box::leak(Box::new(Self {
95 instance,
96 adapter,
97 device,
98 queue,
99 enabled_features,
100 }))
101 }
102
103 pub fn info(&self) -> wgpu::AdapterInfo {
105 self.adapter.get_info()
106 }
107
108 pub fn limits(&self) -> wgpu::Limits {
110 self.device.limits()
111 }
112
113 pub fn wgpu_features(&self) -> wgpu::Features {
115 self.device.features()
116 }
117
118 pub fn gpu_features(&self) -> GpuFeatures {
120 self.enabled_features
121 }
122
123 pub fn has_feature(&self, feature: GpuFeatures) -> bool {
125 self.enabled_features.contains(feature)
126 }
127
128 pub fn has_all_features(&self, features: GpuFeatures) -> bool {
130 self.enabled_features.contains(features)
131 }
132
133 pub fn require_feature(&self, feature: GpuFeatures) {
137 if !self.has_feature(feature) {
138 panic!(
139 "GPU feature {:?} is required but not enabled.\n\
140 Enabled features: {:?}\n\
141 To use this feature, add it to `required_gpu_features` in GraphicsContextDescriptor.",
142 feature, self.enabled_features
143 );
144 }
145 }
146}
147
148pub struct GraphicsContextDescriptor {
150 pub backends: wgpu::Backends,
152 pub power_preference: wgpu::PowerPreference,
154 pub force_fallback_adapter: bool,
156 pub required_gpu_features: GpuFeatures,
160 pub requested_gpu_features: GpuFeatures,
164 pub additional_wgpu_features: wgpu::Features,
166 pub limits: wgpu::Limits,
168 pub label: Option<&'static str>,
170}
171
172impl Default for GraphicsContextDescriptor {
173 fn default() -> Self {
174 Self {
175 backends: wgpu::Backends::all(),
176 power_preference: wgpu::PowerPreference::HighPerformance,
177 force_fallback_adapter: false,
178 required_gpu_features: GpuFeatures::empty(),
179 requested_gpu_features: GpuFeatures::empty(),
180 additional_wgpu_features: wgpu::Features::empty(),
181 limits: wgpu::Limits::default(),
182 label: None,
183 }
184 }
185}
186
187impl GraphicsContextDescriptor {
188 pub fn new() -> Self {
190 Self::default()
191 }
192
193 pub fn require_features(mut self, features: GpuFeatures) -> Self {
195 self.required_gpu_features = features;
196 self
197 }
198
199 pub fn request_features(mut self, features: GpuFeatures) -> Self {
201 self.requested_gpu_features = features;
202 self
203 }
204
205 pub fn with_required_features(mut self, features: GpuFeatures) -> Self {
207 self.required_gpu_features |= features;
208 self
209 }
210
211 pub fn with_requested_features(mut self, features: GpuFeatures) -> Self {
213 self.requested_gpu_features |= features;
214 self
215 }
216
217 pub fn with_wgpu_features(mut self, features: wgpu::Features) -> Self {
219 self.additional_wgpu_features = features;
220 self
221 }
222
223 pub fn power_preference(mut self, preference: wgpu::PowerPreference) -> Self {
225 self.power_preference = preference;
226 self
227 }
228
229 pub fn backends(mut self, backends: wgpu::Backends) -> Self {
231 self.backends = backends;
232 self
233 }
234
235 pub fn limits(mut self, limits: wgpu::Limits) -> Self {
237 self.limits = limits;
238 self
239 }
240
241 pub fn label(mut self, label: &'static str) -> Self {
243 self.label = Some(label);
244 self
245 }
246}