1use std::{
2 cell::OnceCell,
3 rc::Rc,
4 sync::{
5 Arc,
6 mpsc::{
7 Receiver,
8 },
9 },
10};
11use thiserror::Error;
12use winit::{
13 dpi::PhysicalSize,
14 error::OsError,
15 window::{ Window, WindowAttributes, },
16 event_loop::ActiveEventLoop,
17};
18use crate::{ RenderContext, RenderContextBuilder, BuildContextError, };
19
20
21#[derive(Clone, Debug, Error)]
22pub enum BuildDeferredContextError {
23 #[error(transparent)]
24 BuildContext(#[from] BuildContextError),
25 #[error("Context builder thread dropped before finishing")]
26 BuilderThreadDied,
27}
28
29#[derive(Clone, Debug, Error)]
30pub enum GetDeferredContextError {
31 #[error(transparent)]
32 Build(#[from] BuildDeferredContextError),
33 #[error("No compatible surface has been provided yet to construct an Instance")]
34 RequiresSurface,
35 #[error("No context available yet, waiting on builder thread to finish")]
36 StillBuilding,
37}
38
39
40#[derive(Clone, Debug, Error)]
41pub enum ConfigSurfaceError {
42 #[error(transparent)]
43 CreateSurface(#[from] wgpu::CreateSurfaceError),
44 #[error("Surface not supported by adapter - don't mix & match between contexts!")]
45 Unsupported,
46}
47
48
49#[derive(Debug, Error)]
50pub enum CreateWindowError {
51 #[error(transparent)]
52 Window(#[from] OsError),
53 #[error(transparent)]
54 ConfigSurface(#[from] ConfigSurfaceError),
55}
56
57impl From<wgpu::CreateSurfaceError> for CreateWindowError {
58 fn from(e: wgpu::CreateSurfaceError) -> Self {
59 Self::ConfigSurface(e.into())
60 }
61}
62
63
64#[derive(Debug, Default)]
65pub struct SurfaceConfigOptions {
66 pub present_mode: Option<wgpu::PresentMode>,
67 pub frame_latency: Option<u32>,
68 pub alpha_mode: Option<wgpu::CompositeAlphaMode>,
69}
70
71
72#[derive(Debug)]
73pub struct SurfaceWindow {
74 config_opts: SurfaceConfigOptions,
75 config: OnceCell<wgpu::SurfaceConfiguration>,
76 surface: OnceCell<Arc<wgpu::Surface<'static>>>,
77 pub window: Arc<Window>,
78}
79
80impl SurfaceWindow {
81 pub fn new(event_loop: &ActiveEventLoop, attributes: WindowAttributes, config_opts: SurfaceConfigOptions) -> Result<Self, OsError> {
82 let window = Arc::new(event_loop.create_window(attributes)?);
83 Ok(Self {
84 config_opts,
85 config: OnceCell::new(),
86 surface: OnceCell::new(),
87 window,
88 })
89 }
90
91 pub fn try_get_config(&self) -> Option<&wgpu::SurfaceConfiguration> {
92 self.config.get()
93 }
94
95 pub fn try_get_surface(&self) -> Option<&Arc<wgpu::Surface<'static>>> {
96 self.surface.get()
97 }
98
99 fn get_surface(&self, instance: &wgpu::Instance) -> Result<(&Arc<wgpu::Surface<'static>>, bool), wgpu::CreateSurfaceError> {
100 match self.surface.get() {
101 Some(s) => Ok((s, false)),
102 None => {
103 let surface = instance.create_surface(self.window.clone())
104 .map(|s| s.into())?;
105 Ok((self.surface.get_or_init(|| surface), true))
106 },
107 }
108 }
109
110 fn get_config(&self, surface: &wgpu::Surface<'static>, adapter: &wgpu::Adapter) -> Option<(&wgpu::SurfaceConfiguration, bool)> {
111 match self.config.get() {
112 Some(c) => Some((c, false)),
113 None => {
114 let mut config = surface.get_default_config(adapter, 0, 0)?;
115 self.config_opts.present_mode.map(|pm| config.present_mode = pm);
116 self.config_opts.frame_latency.map(|fl| config.desired_maximum_frame_latency = fl);
117 self.config_opts.alpha_mode.map(|am| config.alpha_mode = am);
118
119 Some((self.config.get_or_init(|| config), true))
120 },
121 }
122 }
123
124 pub fn init_surface(&self, instance: &wgpu::Instance, adapter: &wgpu::Adapter, device: &wgpu::Device) -> Result<(&Arc<wgpu::Surface<'static>>, &wgpu::SurfaceConfiguration), ConfigSurfaceError> {
125 let (surface, is_surface_new) = self.get_surface(instance)?;
126 let (config, is_config_new) = self.get_config(surface, adapter).ok_or(ConfigSurfaceError::Unsupported)?;
127
128 if is_surface_new || is_config_new {
129 let size = self.get_size();
130 let mut config = config.clone();
131 config.width = size.width;
132 config.height = size.height;
133 surface.configure(device, &config);
134 }
135
136 Ok((surface, config))
137 }
138
139 pub fn init_with_context(&self, context: &RenderContext) -> Result<(&Arc<wgpu::Surface<'static>>, &wgpu::SurfaceConfiguration), ConfigSurfaceError> {
140 self.init_surface(&context.instance, &context.adapter, &context.device)
141 }
142
143 pub fn drop_surface(&mut self) {
144 let _ = self.surface.take();
145 }
146
147 pub fn try_reconfigure(&self, device: &wgpu::Device, size: PhysicalSize<u32>) -> Result<(), ()> {
148 let surface = self.try_get_surface().ok_or(())?;
149 let mut config = self.try_get_config().ok_or(())?.clone();
150 config.width = size.width;
151 config.height = size.height;
152 surface.configure(device, &config);
153 Ok(())
154 }
155
156 pub fn get_texture_view(&self) -> Option<(wgpu::SurfaceTexture, wgpu::TextureView)> {
157 let _ = self.try_get_config()?;
159 let texture = self.try_get_surface()?.get_current_texture().unwrap();
160 let view = texture.texture.create_view(&wgpu::TextureViewDescriptor::default());
161 Some((texture, view))
162 }
163
164 pub fn get_size(&self) -> PhysicalSize<u32> {
165 self.window.inner_size()
166 }
167}
168
169
170#[derive(Debug)]
171#[allow(dead_code)]
172enum ContextState {
173 WaitingForSurface {
174 builder: RenderContextBuilder,
175 },
176 Building {
177 receiver: Receiver<Result<RenderContext, BuildContextError>>,
178 },
179 Resolved {
180 context: Result<Rc<RenderContext>, BuildDeferredContextError>,
181 },
182}
183
184impl ContextState {
185 fn provide_surface(&mut self, surface: Arc<wgpu::Surface<'static>>) {
186 match self {
187 Self::WaitingForSurface { builder, } => {
188 builder.with_compatible_surface(Some(surface));
189 let builder = builder.clone();
190
191 #[cfg(all(feature = "webgl", not(feature = "poll")))]
192 {
193 use std::sync::mpsc::sync_channel;
194 let (send, recv) = sync_channel::<Result<RenderContext, BuildContextError>>(1);
195 wasm_bindgen_futures::spawn_local(async move {
196 let context = builder.build().await;
197 let _ = send.send(context);
198 });
199
200 *self = Self::Building { receiver: recv, };
201 }
202
203 #[cfg(all(not(feature = "webgl"), feature = "poll"))]
204 {
205 let context = pollster::block_on(builder.build())
206 .map(|c| c.into()).map_err(|e| e.into());
207 *self = Self::Resolved { context, };
208 }
209
210 #[cfg(all(feature = "webgl", feature = "poll"))]
211 unreachable!("Ambiguous whether to start JS thread or poll, only one feature of `webgl` or `poll` should be enabled");
212
213 #[cfg(not(any(feature = "webgl", feature = "poll")))]
214 unreachable!("No way to poll for context, one feature of `webgl` or `poll` should be enabled");
215 },
216 _ => {},
217 }
218 }
219
220 fn poll_resolve_context(&mut self) -> Result<Rc<RenderContext>, GetDeferredContextError> {
221 match self {
222 Self::WaitingForSurface { .. } => Err(GetDeferredContextError::RequiresSurface),
223 Self::Building { receiver, } => {
224 use std::sync::mpsc::TryRecvError;
225
226 match receiver.try_recv() {
227 Ok(context) => {
228 let context = context.map(|c| c.into()).map_err(|e| e.into());
229 *self = Self::Resolved { context: context.clone(), };
230 Ok(context?)
231 },
232 Err(TryRecvError::Disconnected) => {
233 *self = Self::Resolved { context: Err(BuildDeferredContextError::BuilderThreadDied), };
234 Err(BuildDeferredContextError::BuilderThreadDied.into())
235 },
236 Err(TryRecvError::Empty) => Err(GetDeferredContextError::StillBuilding),
237 }
238 },
239 Self::Resolved { context, } => Ok(context.clone()?),
240 }
241 }
242
243 }
252
253
254#[derive(Debug)]
321pub struct DeferredContext {
322 instance: wgpu::Instance,
323 context: ContextState,
324}
325
326impl DeferredContext {
327 #[cfg(any(feature = "webgl", feature = "poll"))]
328 pub fn new(builder: RenderContextBuilder) -> Self {
329 #[cfg(feature = "webgl")]
330 return Self::new_requiring_surface(builder);
331 #[cfg(feature = "poll")]
332 return Self::new_poll(builder);
333 }
334
335 #[cfg(any(feature = "webgl", feature = "poll"))]
336 pub fn new_requiring_surface(mut builder: RenderContextBuilder) -> Self {
337 let instance = builder.get_instance();
338 Self {
339 instance,
340 context: ContextState::WaitingForSurface { builder, },
341 }
342 }
343
344 #[cfg(not(feature = "webgl"))]
345 pub async fn new_async(mut builder: RenderContextBuilder) -> Self {
346 let instance = builder.get_instance();
347 let context = builder.build().await.map(|c| c.into()).map_err(|e| e.into());
348 Self {
349 instance,
350 context: ContextState::Resolved { context, },
351 }
352 }
353
354 #[cfg(all(not(feature = "webgl"), feature = "poll"))]
355 pub fn new_poll(builder: RenderContextBuilder) -> Self {
356 pollster::block_on(Self::new_async(builder))
357 }
358
359 pub fn create_window(&mut self, event_loop: &ActiveEventLoop, attributes: WindowAttributes, config_opts: SurfaceConfigOptions) -> Result<SurfaceWindow, CreateWindowError> {
360 let window = SurfaceWindow::new(event_loop, attributes, config_opts)?;
361 let surface = window.get_surface(&self.instance)?;
362 self.context.provide_surface(surface.0.clone());
363 if let Ok(context) = self.get_context() {
364 window.init_surface(&context.instance, &context.adapter, &context.device)?;
365 }
366 Ok(window)
367 }
368
369 pub fn get_instance(&self) -> wgpu::Instance {
370 self.instance.clone()
371 }
372
373 pub fn get_context(&mut self) -> Result<Rc<RenderContext>, GetDeferredContextError> {
374 self.context.poll_resolve_context()
375 }
376}