1use wgpu::{
7 Adapter, Device, Features, Instance, Limits, MemoryHints, Queue, Surface, SurfaceTarget,
8};
9
10mod buffer_renderer;
11mod error;
12mod surface_renderer;
13mod util;
14
15pub use buffer_renderer::{BufferRenderer, BufferRendererConfig};
16pub use error::WgpuContextError;
17pub use surface_renderer::{SurfaceRenderer, SurfaceRendererConfiguration, TextureConfiguration};
18pub use util::block_on_wgpu;
19
20#[derive(Clone, Debug)]
22pub struct DeviceHandle {
23 pub instance: Instance,
24 pub adapter: Adapter,
25 pub device: Device,
26 pub queue: Queue,
27}
28
29impl DeviceHandle {
30 pub async fn new_from_compatible_surface(
32 instance: Instance,
33 compatible_surface: Option<&Surface<'_>>,
34 extra_features: Option<Features>,
35 override_limits: Option<Limits>,
36 ) -> Result<Self, WgpuContextError> {
37 let adapter =
38 wgpu::util::initialize_adapter_from_env_or_default(&instance, compatible_surface)
39 .await?;
40
41 let requested_features = extra_features.unwrap_or(Features::empty());
44 let available_features = adapter.features();
45 let required_features = requested_features & available_features;
46
47 let required_limits = override_limits.clone().unwrap_or_else(|| Limits {
50 max_inter_stage_shader_variables: 15,
52 ..Limits::default()
53 });
54
55 let descripter = wgpu::DeviceDescriptor {
57 label: None,
58 required_features,
59 required_limits,
60 memory_hints: MemoryHints::MemoryUsage,
61 trace: wgpu::Trace::default(),
62 experimental_features: wgpu::ExperimentalFeatures::default(),
63 };
64 let (device, queue) = adapter.request_device(&descripter).await?;
65
66 Ok(DeviceHandle {
68 instance,
69 adapter,
70 device,
71 queue,
72 })
73 }
74
75 pub async fn create_surface<'w>(
77 &mut self,
78 window: impl Into<SurfaceTarget<'w>>,
79 surface_config: SurfaceRendererConfiguration,
80 intermediate_texture_config: Option<TextureConfiguration>,
81 ) -> Result<SurfaceRenderer<'w>, WgpuContextError> {
82 let surface = self.instance.create_surface(window.into())?;
84 SurfaceRenderer::new(
85 surface,
86 surface_config,
87 intermediate_texture_config,
88 self.clone(),
89 )
90 }
91}
92
93pub struct WGPUContext {
95 pub instance: Instance,
97 pub device_pool: Vec<DeviceHandle>,
100
101 extra_features: Option<Features>,
103 override_limits: Option<Limits>,
104}
105
106impl Default for WGPUContext {
107 fn default() -> Self {
108 Self::new()
109 }
110}
111
112impl WGPUContext {
113 pub fn new() -> Self {
114 Self::with_features_and_limits(None, None)
115 }
116
117 pub fn with_features_and_limits(
118 extra_features: Option<Features>,
119 override_limits: Option<Limits>,
120 ) -> Self {
121 Self {
122 instance: Instance::new(wgpu::InstanceDescriptor {
123 backends: wgpu::Backends::from_env().unwrap_or_default(),
124 flags: wgpu::InstanceFlags::from_build_config().with_env(),
125 backend_options: wgpu::BackendOptions::from_env_or_default(),
126 memory_budget_thresholds: wgpu::MemoryBudgetThresholds::default(),
127
128 display: None,
131 }),
132 device_pool: Vec::new(),
133 extra_features,
134 override_limits,
135 }
136 }
137
138 pub fn extra_features(&self) -> Option<Features> {
139 self.extra_features
140 }
141
142 pub fn override_limits(&self) -> Option<Limits> {
143 self.override_limits.clone()
144 }
145
146 pub fn create_surface<'w>(
148 &self,
149 window: impl Into<SurfaceTarget<'w>>,
150 ) -> Result<Surface<'w>, WgpuContextError> {
151 Ok(self.instance.create_surface(window.into())?)
152 }
153
154 pub async fn create_surface_renderer<'w>(
156 &mut self,
157 window: impl Into<SurfaceTarget<'w>>,
158 surface_config: SurfaceRendererConfiguration,
159 intermediate_texture_config: Option<TextureConfiguration>,
160 ) -> Result<SurfaceRenderer<'w>, WgpuContextError> {
161 let surface = self.create_surface(window.into())?;
163
164 let dev_id = self
166 .find_or_create_device(Some(&surface))
167 .await
168 .or(Err(WgpuContextError::NoCompatibleDevice))?;
169 let device_handle = self.device_pool[dev_id].clone();
170
171 SurfaceRenderer::new(
172 surface,
173 surface_config,
174 intermediate_texture_config,
175 device_handle,
176 )
177 }
178
179 pub async fn create_buffer_renderer(
181 &mut self,
182 config: BufferRendererConfig,
183 ) -> Result<BufferRenderer, WgpuContextError> {
184 let dev_id = self
186 .find_or_create_device(None)
187 .await
188 .or(Err(WgpuContextError::NoCompatibleDevice))?;
189 let device_handle = self.device_pool[dev_id].clone();
190
191 Ok(BufferRenderer::new(config, device_handle, dev_id))
192 }
193
194 pub async fn find_or_create_device(
196 &mut self,
197 compatible_surface: Option<&Surface<'_>>,
198 ) -> Result<usize, WgpuContextError> {
199 match self.find_existing_device_idx(compatible_surface) {
200 Some(device_id) => Ok(device_id),
201 None => self.create_device(compatible_surface).await,
202 }
203 }
204
205 pub fn find_compatible_device_handle(
207 &mut self,
208 compatible_surface: Option<&Surface<'_>>,
209 ) -> Option<DeviceHandle> {
210 self.find_existing_device_idx(compatible_surface)
211 .map(|idx| self.device_pool[idx].clone())
212 }
213
214 fn find_existing_device_idx(&self, compatible_surface: Option<&Surface<'_>>) -> Option<usize> {
216 match compatible_surface {
217 Some(s) => self
218 .device_pool
219 .iter()
220 .enumerate()
221 .find(|(_, d)| d.adapter.is_surface_supported(s))
222 .map(|(i, _)| i),
223 None => (!self.device_pool.is_empty()).then_some(0),
224 }
225 }
226
227 pub fn create_device_handle(
228 &self,
229 compatible_surface: Option<&Surface<'_>>,
230 ) -> impl Future<Output = Result<DeviceHandle, WgpuContextError>> {
231 DeviceHandle::new_from_compatible_surface(
232 self.instance.clone(),
233 compatible_surface,
234 self.extra_features,
235 self.override_limits.clone(),
236 )
237 }
238
239 async fn create_device(
241 &mut self,
242 compatible_surface: Option<&Surface<'_>>,
243 ) -> Result<usize, WgpuContextError> {
244 let device_handle = self.create_device_handle(compatible_surface).await?;
245 self.device_pool.push(device_handle);
246 Ok(self.device_pool.len() - 1)
247 }
248}