1use self::{
2 belt::Belt,
3 catcher::Catcher,
4 surface::{ISurface, Surface},
5};
6use crate::{label, DeviceStorage, Frame};
7use colorful::Colorful;
8use std::sync::Arc;
9use wgpu::{
10 util::power_preference_from_env, Adapter, Device, DeviceDescriptor, Features, Instance, Limits,
11 PowerPreference, Queue, RequestAdapterOptionsBase, TextureFormat,
12};
13use winit::window::Window;
14
15pub mod prelude;
18pub mod surface;
19
20mod belt;
23mod catcher;
24
25pub struct Target {
28 pub(crate) device: Arc<Device>,
29 pub(crate) queue: Arc<Queue>,
30
31 pub(crate) surface: Option<Surface>,
32 pub(crate) belt: Belt,
33 catcher: Catcher,
34
35 active: bool,
36 init: bool,
37}
38
39impl Target {
42 pub async fn new(
43 instance: Arc<Instance>,
44 window: Arc<Window>,
45 device_storage: DeviceStorage,
46 ) -> Self {
47 let surface = ISurface::new(window, instance.clone());
49
50 let (adapter, device, queue) =
52 Self::new_with_opt(instance, Some(&surface), device_storage).await;
53
54 let surface = Some(surface.complete(&adapter, device.clone()));
56
57 let belt = Belt::new(device.clone());
59
60 let catcher = Catcher::new(&device);
63
64 Self {
65 device,
66 queue,
67
68 surface,
69 belt,
70 catcher,
71
72 active: false,
73 init: true,
74 }
75 }
76
77 pub async fn new_headless(instance: Arc<Instance>, device_storage: DeviceStorage) -> Self {
78 let (_, device, queue) = Self::new_with_opt(instance, None, device_storage).await;
79
80 let belt = Belt::new(device.clone());
82
83 let catcher = Catcher::new(&device);
86
87 Self {
88 device,
89 queue,
90
91 surface: None,
92 belt,
93 catcher,
94
95 active: false,
96 init: true,
97 }
98 }
99
100 async fn new_with_opt(
101 instance: Arc<Instance>,
102 surface: Option<&wgpu::Surface>,
103 device_storage: DeviceStorage,
104 ) -> (Arc<Adapter>, Arc<Device>, Arc<Queue>) {
105 if let Some(pre_existing) = Self::try_borrow_device(surface, device_storage.clone()) {
108 pre_existing
110 } else {
111 let adapter = Self::make_adapter(surface, &instance).await;
114
115 Self::debug_report(&adapter);
117
118 let (device, queue) = Self::make_device(&adapter).await;
120
121 if let Ok(mut write) = device_storage.write() {
123 write.push((adapter.clone(), device.clone(), queue.clone()));
124 }
125
126 (adapter, device, queue)
127 }
128 }
129
130 pub fn compatible_with(&self, other: &Target) -> bool {
133 Arc::ptr_eq(&self.device, &other.device) && Arc::ptr_eq(&self.queue, &other.queue)
134 }
135
136 #[must_use]
137 pub fn get_frame(&mut self) -> Frame {
138 if self.active {
139 panic!("Earlier frame was not finished before starting a new one");
140 }
141
142 if self.init {
143 self.init = false;
144 self.get_window().unwrap().set_visible(true);
145 }
146
147 Frame::new(
148 &self.device,
149 self.queue.clone(),
150 self.surface.as_mut().expect("TODO: Draw in headless mode"),
151 self.belt.get(),
152 )
153 }
154
155 pub fn finish_frame(&mut self, frame: Frame) {
156 self.belt.set(frame.finish())
157 }
158
159 pub fn set_vsync(&mut self, on: bool) {
160 if let Some(s) = self.surface.as_mut() {
161 s.set_vsync(on);
162 }
163 }
164
165 pub fn get_vsync(&self) -> Option<bool> {
166 self.surface.as_ref().map(|s| s.get_vsync())
167 }
168
169 pub fn get_window(&self) -> Option<Arc<Window>> {
170 self.surface.as_ref().map(|surface| surface.get_window())
171 }
172
173 pub fn get_format(&self) -> TextureFormat {
174 self.surface
175 .as_ref()
176 .map(|surface| surface.format())
177 .unwrap_or(TextureFormat::Rgba8Unorm)
178 }
179
180 pub fn get_device(&self) -> Arc<Device> {
181 self.device.clone()
182 }
183
184 pub fn catch_error<T, F: FnOnce(&Self) -> T>(&self, f: F) -> Result<T, String> {
185 Catcher::catch_error(self, f)
186 }
187
188 fn try_borrow_device(
189 compatible_surface: Option<&wgpu::Surface>,
190 device_storage: DeviceStorage,
191 ) -> Option<(Arc<Adapter>, Arc<Device>, Arc<Queue>)> {
192 device_storage
193 .read()
194 .ok()?
195 .iter()
196 .find(|(adapter, _, _)| {
197 if let Some(surface) = compatible_surface {
198 adapter.is_surface_supported(surface)
199 } else {
200 true
201 }
202 })
203 .cloned()
204 }
205
206 async fn make_adapter(
207 compatible_surface: Option<&wgpu::Surface>,
208 instance: &Instance,
209 ) -> Arc<Adapter> {
210 Arc::new(
211 instance
212 .request_adapter(&RequestAdapterOptionsBase {
213 power_preference: power_preference_from_env()
214 .unwrap_or(PowerPreference::HighPerformance),
215 force_fallback_adapter: false,
216 compatible_surface,
217 })
218 .await
219 .expect("No suitable GPUs"),
220 )
221 }
222
223 fn debug_report(adapter: &Adapter) {
224 if log::log_enabled!(log::Level::Debug) {
225 let gpu_info = adapter.get_info();
226 let api = format!("{:?}", gpu_info.backend).red();
227 let name = gpu_info.name.blue();
228 let ty = format!("{:?}", gpu_info.device_type).green();
229
230 log::debug!("GPU API: {api}");
231 log::debug!("GPU: {name} ({ty})");
232 }
233 }
234
235 async fn make_device(adapter: &Adapter) -> (Arc<Device>, Arc<Queue>) {
236 let (device, queue) = adapter
237 .request_device(
238 &DeviceDescriptor {
239 label: label!(),
240 features: Features::empty(),
241 limits: Limits {
242 ..Limits::downlevel_webgl2_defaults()
244 },
245 },
246 None,
247 )
248 .await
249 .unwrap();
250 (Arc::new(device), Arc::new(queue))
251 }
252}