mewgpu 3.7.3

Maybe Easier Wgpu (mew), a thin abstraction over wgpu's chaos
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
//! Utilities for working with windows & surfaces.
//!
//! To bundle a
//! [`winit::window::Window`](https://docs.rs/winit/latest/winit/window/struct.Window.html)
//! with its
//! [`wgpu::Surface`](https://docs.rs/wgpu/latest/wgpu/struct.Surface.html),
//! you're looking for [`SurfaceWindow`].
//!
//! For an abstraction over WebGL's retardedness having to provide a surface
//! before you can get an adapter, see [`DeferredContext`].


#[allow(unused_imports)]
use std::{
    cell::{
        BorrowMutError,
        Cell,
        RefCell,
        RefMut,
        UnsafeCell,
    },
    sync::{
        Arc,
        mpsc::{
            Receiver,
        },
    },
};
use thiserror::Error;
use winit::{
    dpi::PhysicalSize,
    error::OsError,
    window::{ Window, WindowAttributes, },
    event_loop::ActiveEventLoop,
};
use crate::{
    RenderContext,
    RenderContextBuilder,
    BuildContextError,
    wgpu,
};


/// Clamps surface size to device limits, preserving aspect ratio.
fn clamp_config_size(config: &mut wgpu::SurfaceConfiguration, device: &wgpu::Device, mut size: PhysicalSize<u32>) {
    let max_allowed_size = device.limits().max_texture_dimension_2d;
    let max_config_size = size.width.max(size.height);
    if max_config_size > max_allowed_size {
        let ratio = max_allowed_size as f32 / max_config_size.max(1) as f32;
        size.width = (size.width as f32 * ratio) as u32;
        size.height = (size.height as f32 * ratio) as u32;
    }

    config.width = size.width.max(1);
    config.height = size.height.max(1);
}


/// Get the WebGL canvas from the page DOM, by its `id`.
///
/// ```
/// use winit::platform::web::WindowAttributesExtWebSys;
/// let canvas = get_gl_canvas("canvas").expect("Couldn't get GL canvas");
/// let attributes = winit::WindowAttributes::default().with_canvas(Some(canvas));
/// ```
#[cfg(any(target_family = "wasm", doc))]
pub fn get_gl_canvas(id: &'static str) -> Result<web_sys::HtmlCanvasElement, GetGlCanvasError> {
    use web_sys::wasm_bindgen::prelude::*;
    let canvas = web_sys::window().ok_or(GetGlCanvasError::WindowDocument)?
        .document().ok_or(GetGlCanvasError::WindowDocument)?
        .get_element_by_id(id).ok_or(GetGlCanvasError::Element(id))?
        .dyn_into::<web_sys::HtmlCanvasElement>().or(Err(GetGlCanvasError::DynInto))?;
    Ok(canvas)
}

/// Error returned when trying to get a WebGL canvas with [`get_gl_canvas`].
#[derive(Clone, Debug, Error)]
pub enum GetGlCanvasError {
    /// `web_sys` couldn't get the page's window or document.
    #[error("Couldn't get page window or document")]
    WindowDocument,
    /// Couldn't find an element with the provided `id` on the page.
    #[error("Couldn't find canvas element by id \"{0}\"")]
    Element(&'static str),
    /// An element was found, but it's not a [`web_sys::HtmlCanvasElement`] object.
    #[error("Couldn't convert element to canvas object")]
    DynInto,
}


/// Error returned when waiting for a [`RenderContext`] to be built.
#[derive(Clone, Debug, Error)]
pub enum BuildDeferredContextError {
    /// Something went wrong with the actual building of the [`RenderContext`].
    #[error(transparent)]
    BuildContext(#[from] BuildContextError),
    /// The thread or async task dropped (only on web).
    #[error("Context builder thread dropped before finishing")]
    BuilderThreadDied,
}

/// Error returned trying to get the [`RenderContext`] from a [`DeferredContext`].
#[derive(Clone, Debug, Error)]
pub enum GetDeferredContextError {
    /// Something went wrong actually building the [`RenderContext`].
    /// This is a fatal error, if it fails you should probably just `panic!`.
    #[error(transparent)]
    Build(#[from] BuildDeferredContextError),
    /// As of _wgpu_ 29.0, you need to manually provide a display handle to create
    /// an [`wgpu::Instance`](https://docs.rs/wgpu/latest/wgpu/struct.Instance.html)
    /// that can configure surfaces. This can be done manually by calling
    /// [`RenderContextBuilder::with_display_handle`], or
    /// [`DeferredContext::create_window`] will do this automatically using the
    /// passed-in
    /// [`winit::ActiveEventLoop`](https://docs.rs/winit/latest/winit/event_loop/struct.ActiveEventLoop.html).
    ///
    /// If you get this error, just create a window first.
    #[error("No display handle has been provided yet to construct an Instance")]
    RequiresDisplayHandle,
    /// On web, you need to provide a canvas surface to create a
    /// [`wgpu::Adapter`](https://docs.rs/wgpu/latest/wgpu/struct.Adapter.html)
    /// before you can even get to the [`RenderContext`]. This is done automatically
    /// when you create a new window through [`DeferredContext::create_window`].
    ///
    /// If you get this error, just create a window first.
    #[error("No compatible surface has been provided yet to construct an Adapter")]
    RequiresSurface,
    /// On web, the async builder task hasn't returned a result yet. Building a
    /// [`RenderContext`] should be quick though, so if you see this something might be
    /// wrong.
    #[error("No context available yet, waiting on builder thread to finish")]
    StillBuilding,
}


/// _wgpu_ 29.0 removed `SurfaceError` and replaced the `Result` entirely with a
/// single enum,
/// [`wgpu::CurrentSurfaceTexture`](https://docs.rs/wgpu/latest/wgpu/enum.CurrentSurfaceTexture.html).
/// Some variants are recoverable, some aren't, but it's still nice to have a proper
/// `Result` to work with.
#[derive(Clone, Debug, Error)]
pub enum SurfaceError {
    /// The surface returned but should be reconfigured. This should be handled by
    /// the [`DeferredContext`] so you shouldn't see this.
    #[error("Surface is suboptimal")]
    Suboptimal,
    /// Getting the frame timed out. Non-fatal, just skip this frame.
    #[error("Getting surface timed out")]
    Timeout,
    /// Window isn't visible. Non-fatal, just skip this frame.
    #[error("Surface is occluded")]
    Occluded,
    /// The surface needs to be reconfigured. This error should be handled by
    /// [`DeferredContext`] so you shouldn't see this, unless it failed immediately
    /// after being reconfigured in which case something is very wrong.
    #[error("Surface is outdated")]
    Outdated,
    /// The surface needs to be reconfigured. This error should be handled by
    /// [`DeferredContext`] so you shouldn't see this, unless it failed immediately
    /// after being recreated in which case something is very wrong.
    #[error("Surface has been lost")]
    Lost,
    /// _wgpu_ threw an error. Fatal, something is very wrong.
    #[error("Validation error raised by wgpu")]
    Validation,
}

impl SurfaceError {
    fn from(texture: wgpu::CurrentSurfaceTexture) -> Result<wgpu::SurfaceTexture, Self> {
        match texture {
            wgpu::CurrentSurfaceTexture::Success(t) => Ok(t),
            wgpu::CurrentSurfaceTexture::Suboptimal(_) => Err(Self::Suboptimal),
            wgpu::CurrentSurfaceTexture::Timeout => Err(Self::Timeout),
            wgpu::CurrentSurfaceTexture::Occluded => Err(Self::Occluded),
            wgpu::CurrentSurfaceTexture::Outdated => Err(Self::Outdated),
            wgpu::CurrentSurfaceTexture::Lost => Err(Self::Lost),
            wgpu::CurrentSurfaceTexture::Validation => Err(Self::Validation),
        }
    }
}


/// Error returned when configuring a surface or making a texture view for the
/// surface.
#[derive(Clone, Debug, Error)]
pub enum ConfigSurfaceError {
    /// _wgpu_ could not create a surface, see
    /// [`wgpu::Instance::create_surface`](https://docs.rs/wgpu/latest/wgpu/struct.Instance.html#method.create_surface).
    #[error(transparent)]
    CreateSurface(#[from] wgpu::CreateSurfaceError),
    /// The surface already has an active view - a new one can't be created, nor can
    /// the current one be reconfigured.
    #[error("Surface is already active")]
    InUse,
    /// Can't reconfigure the surface because it hasn't been created or configured
    /// yet. Non-fatal, but run [`SurfaceWindow::init_with_context`] first.
    #[error("Surface hasn't been created or configured yet")]
    NotInitialized,
    /// Trying to use the surface failed. Non-fatal, but the surface will be dropped
    /// & recreated. Also see
    /// [`wgpu::CurrentSurfaceTexture`](https://docs.rs/wgpu/latest/wgpu/enum.CurrentSurfaceTexture.html).
    #[error(transparent)]
    SurfaceError(#[from] SurfaceError),
    /// The surface that tried to be configure wasn't supported by this adapter. Don't
    /// mix & match resources between [`RenderContext`]s!
    #[error("Surface not supported by adapter - don't mix & match between RenderContexts!")]
    Unsupported,
}

impl From<BorrowMutError> for ConfigSurfaceError {
    fn from(_e: BorrowMutError) -> Self {
        Self::InUse
    }
}


/// Error returned when trying to create a window.
#[derive(Debug, Error)]
pub enum CreateWindowError {
    /// The _winit_ event loop couldn't create a window for whatever reason, see
    /// [`winit::ActiveEventLoop::create_window`](https://docs.rs/winit/latest/x86_64-unknown-linux-gnu/winit/event_loop/struct.ActiveEventLoop.html#method.create_window).
    #[error(transparent)]
    Window(#[from] OsError),
    /// The window was created but _wgpu_ couldn't configure its surface.
    #[error(transparent)]
    ConfigSurface(#[from] ConfigSurfaceError),
}

impl From<wgpu::CreateSurfaceError> for CreateWindowError {
    fn from(e: wgpu::CreateSurfaceError) -> Self {
        Self::ConfigSurface(e.into())
    }
}


/// Persistent configuration for surfaces, which may be destroyed & re-created.
/// Overrides defaults of [`wgpu::SurfaceConfiguration`](https://docs.rs/wgpu/latest/wgpu/type.SurfaceConfiguration.html).
#[derive(Debug, Default)]
pub struct SurfaceConfigOptions {
    /// The surface's [`wgpu::PresentMode`](https://docs.rs/wgpu/latest/wgpu/enum.PresentMode.html).
    pub present_mode: Option<wgpu::PresentMode>,
    /// The surface's max frame latency.
    pub frame_latency: Option<u32>,
    /// The surface's [`wgpu::CompositeAlphaMode`](https://docs.rs/wgpu/latest/wgpu/enum.CompositeAlphaMode.html).
    pub alpha_mode: Option<wgpu::CompositeAlphaMode>,
}


/// A combined
/// [`winit::window::Window`](https://docs.rs/winit/latest/winit/window/struct.Window.html)
/// &
/// [`wgpu::Surface`](https://docs.rs/wgpu/latest/wgpu/struct.Surface.html).
#[derive(Debug)]
pub struct SurfaceWindow {
    config_opts: SurfaceConfigOptions,
    surface: RefCell<Option<Arc<wgpu::Surface<'static>>>>,
    window: Arc<Window>,
    target_size: Cell<Option<PhysicalSize<u32>>>,
    reconfigure: Cell<bool>,
}

impl SurfaceWindow {
    /// Create a new window under an
    /// [`winit::ActiveEventLoop`](https://docs.rs/winit/latest/winit/event_loop/struct.ActiveEventLoop.html).
    ///
    /// Also requires a
    /// [`winit::window::WindowAttributes`](https://docs.rs/winit/latest/winit/window/struct.WindowAttributes.html)
    /// (though [`Default`] should work fine for most cases) and a [`SurfaceConfigOptions`]
    /// ([`Default`] should be fine here too).
    pub fn new(event_loop: &ActiveEventLoop, attributes: WindowAttributes, config_opts: SurfaceConfigOptions) -> Result<Self, OsError> {
        let window = Arc::new(event_loop.create_window(attributes)?);
        Ok(Self {
            config_opts,
            //config: OnceCell::new(),
            surface: RefCell::new(None),
            window,
            target_size: Cell::new(None),
            reconfigure: Cell::new(false),
            //_config_lock: RefCell::new(()),
        })
    }

    /*
    /// Try to get the [`wgpu::Surface`](https://docs.rs/wgpu/latest/wgpu/struct.Surface.html),
    /// if it's been built.
    ///
    /// This is marked `unsafe` because reconfiguring this surface while it has an
    /// active view will cause _wgpu_ to panic. _wgpu_ doesn't enforce this so I
    /// gotta.
    pub unsafe fn try_get_surface(&self) -> Option<&wgpu::Surface<'static>> {
        self.surface.get().map(|s| &**s)
    }
    */

    /// Try to get the
    /// [`wgpu::SurfaceConfiguration`](https://docs.rs/wgpu/latest/wgpu/type.SurfaceConfiguration.html),
    /// if the surface has been created.
    pub fn try_get_config(&self) -> Option<wgpu::SurfaceConfiguration> {
        unsafe { &*self.surface.as_ptr() }
            .as_ref().and_then(|s| s.get_configuration())
    }

    /// Get a reference to the window.
    pub fn window(&self) -> &Window {
        &self.window
    }

    /*
    /// Get an [`Arc`] to the window.
    pub fn window_arc(&self) -> Arc<Window> {
        self.window.clone()
    }
    */

    /*
    fn get_surface(&self, instance: &wgpu::Instance) -> Result<(&Arc<wgpu::Surface<'static>>, bool), wgpu::CreateSurfaceError> {
        match self.surface.get() {
            Some(s) => Ok((s, false)),
            None => {
                let surface = instance.create_surface(self.window.clone())
                    .map(|s| s.into())?;
                Ok((self.surface.get_or_init(|| surface), true))
            },
        }
    }

    fn get_config(&self, surface: &wgpu::Surface<'static>, adapter: &wgpu::Adapter, device: &wgpu::Device) -> Option<(&wgpu::SurfaceConfiguration, bool)> {
        match self.config.get() {
            Some(c) => Some((c, false)),
            None => {
                let size = self.get_size();

                let mut config = surface.get_default_config(adapter, 0, 0)?;
                clamp_config_size(&mut config, device, size);

                self.config_opts.present_mode.map(|pm| config.present_mode = pm);
                self.config_opts.frame_latency.map(|fl| config.desired_maximum_frame_latency = fl);
                self.config_opts.alpha_mode.map(|am| config.alpha_mode = am);

                Some((self.config.get_or_init(|| config), true))
            },
        }
    }
    */

    /*
    fn get_surface(&self, instance: &wgpu::Instance) -> Result<&Arc<wgpu::Surface<'static>>, ConfigSurfaceError> {
        let mut lock = self.surface.try_borrow_mut()?;
        match &*lock {
            Some(s) => Ok(s),
            None => {
                let surface = self.make_surface(instance)?;
                Ok(lock.insert(Arc::new(surface)))
            },
        }
    }
    */

    fn make_surface(&self, instance: &wgpu::Instance) -> Result<wgpu::Surface<'static>, wgpu::CreateSurfaceError> {
        instance.create_surface(wgpu::SurfaceTarget::DisplayAndWindow(Box::new(self.window.clone())))
    }

    fn make_config(&self, surface: &wgpu::Surface<'static>, adapter: &wgpu::Adapter, device: &wgpu::Device) -> Option<wgpu::SurfaceConfiguration> {
        let mut config = surface.get_default_config(adapter, 0, 0)?;

        if let Some(pm) = self.config_opts.present_mode { config.present_mode = pm; }
        if let Some(fl) = self.config_opts.frame_latency { config.desired_maximum_frame_latency = fl; }
        if let Some(am) = self.config_opts.alpha_mode { config.alpha_mode = am; }

        let size = self.target_size.get()
            .unwrap_or_else(|| self.get_size());
        clamp_config_size(&mut config, device, size);

        Some(config)
    }

    /// Opaquely create, configure, and get the
    /// [`wgpu::SurfaceTexture`](https://docs.rs/wgpu/latest/wgpu/struct.SurfaceTexture.html)
    /// &
    /// [`wgpu::TextureView`](https://docs.rs/wgpu/latest/wgpu/struct.TextureView.html)
    /// of the underlying
    /// [`winit::window::Window`](https://docs.rs/winit/latest/winit/window/struct.Window.html).
    ///
    /// If the window's
    /// [`wgpu::Surface`](https://docs.rs/wgpu/latest/wgpu/struct.Surface.html)
    /// hasn't been created yet, it will do so now. If needed it'll make a new
    /// [`wgpu::SurfaceConfiguration`](https://docs.rs/wgpu/latest/wgpu/type.SurfaceConfiguration.html)
    /// using either the window's inner size or a manually provided one from
    /// the last [`Self::resize`].
    ///
    /// Next, it'll try getting its
    /// [`wgpu::SurfaceTexture`](https://docs.rs/wgpu/latest/wgpu/struct.SurfaceTexture.html)
    /// with
    /// [`wgpu::Surface::get_current_texture`](https://docs.rs/wgpu/latest/wgpu/struct.Surface.html#method.get_current_texture).
    /// This may fail if the surface was old and needed to be refreshed, in which
    /// case it'll need to fully recreate the surface. If getting the surface
    /// texture fails a 2nd time, the error will be raised to the caller.
    ///
    /// Only one [`ActiveSurfaceWindow`] is allowed at any one time, this is
    /// enforced during runtime.
    pub fn init_surface(&self, instance: &wgpu::Instance, adapter: &wgpu::Adapter, device: &wgpu::Device, queue: &wgpu::Queue) -> Result<ActiveSurfaceWindow<'_>, ConfigSurfaceError> {
        let mut surface_lock = self.surface.try_borrow_mut()?;

        let reconfigure = self.reconfigure.replace(false);
        let old_size = surface_lock.as_ref().and_then(|s| s.get_configuration()).map(|c| (c.width, c.height));
        let mut resized = false;

        // (Re)configure if surface exists & configuration is needed
        if let Some(surface) = &*surface_lock && (surface.get_configuration().is_none() || reconfigure) {
            let config = self.make_config(surface, adapter, device)
                .ok_or(ConfigSurfaceError::Unsupported)?;
            surface.configure(device, &config);
            resized = old_size != Some((config.width, config.height));
        }

        let texture = match surface_lock.as_ref().map(|s| (s, s.get_current_texture())) {
            Some((_, wgpu::CurrentSurfaceTexture::Success(t))) => t,
            Some((_, wgpu::CurrentSurfaceTexture::Timeout)) => return Err(SurfaceError::Timeout.into()),
            Some((_, wgpu::CurrentSurfaceTexture::Occluded)) => return Err(SurfaceError::Occluded.into()),

            Some((surface, wgpu::CurrentSurfaceTexture::Suboptimal(_) | wgpu::CurrentSurfaceTexture::Outdated)) => {
                // Only reconfigure
                let config = self.make_config(surface, adapter, device)
                    .ok_or(ConfigSurfaceError::Unsupported)?;
                surface.configure(device, &config);

                resized = old_size != Some((config.width, config.height));

                // Get texture of new surface, if this errors something is really borked
                SurfaceError::from(surface.get_current_texture())?
            },

            None | Some((_, wgpu::CurrentSurfaceTexture::Lost | wgpu::CurrentSurfaceTexture::Validation)) => {
                // Surface borked, recreate everything
                let new_surface = Arc::new(self.make_surface(instance)?);
                let surface = surface_lock.insert(new_surface);

                let config = self.make_config(surface, adapter, device)
                    .ok_or(ConfigSurfaceError::Unsupported)?;
                surface.configure(device, &config);

                resized = old_size != Some((config.width, config.height));

                // Get texture of new surface, if this errors something is really borked
                SurfaceError::from(surface.get_current_texture())?
            },
        };

        let view = texture.texture.create_view(&wgpu::TextureViewDescriptor {
            format: Some(texture.texture.format()),
            ..Default::default()
        });

        Ok(ActiveSurfaceWindow {
            #[cfg(feature = "wgpu-30")]
            queue: queue.clone(),
            surface: RefMut::map(surface_lock, |sl| sl.as_mut().expect("Surface should have been created")),
            window: &self.window,
            texture,
            view,
            resized,
        })
    }

    /// Get a usable [`ActiveSurfaceWindow`], which guarantees that the window
    /// surface exists & is configured.
    ///
    /// See [`Self::init_surface`] for more details.
    pub fn init_with_context(&self, context: &RenderContext) -> Result<ActiveSurfaceWindow<'_>, ConfigSurfaceError> {
        self.init_surface(&context.instance, &context.adapter, &context.device, &context.queue)
    }

    /// Try to drop the surface, if it's not in use.
    pub fn try_drop_surface(&self) -> Result<(), BorrowMutError> {
        let _ = self.surface.try_borrow_mut()?.take();
        Ok(())
    }

    /// Drop the surface now. Requires mutable access.
    pub fn drop_surface(&mut self) {
        let _ = self.surface.get_mut().take();
    }

    /// Set the target window size and trigger a reconfiguration.
    ///
    /// If you pass `None`, the size will be queried from the window, or you can
    /// specify it from a _winit_ resize event. The surface will be reconfigured
    /// when it's next initialized.
    pub fn resize(&self, size: Option<PhysicalSize<u32>>) {
        self.target_size.set(size);
        self.reconfigure();
    }

    /// Mark the surface to be reconfigured when it's next initialized.
    pub fn reconfigure(&self) {
        self.reconfigure.set(true);
    }

    /// Convenience function to get the window's inner size.
    pub fn get_size(&self) -> PhysicalSize<u32> {
        self.window.inner_size()
    }
}


/// A borrowed form of [`SurfaceWindow`] which guarantees that its surface
/// exists & is configured, ready to be rendered to.
///
/// Only one instance is allowed at any time (per [`SurfaceWindow`]). _wgpu_
/// panics if multiple
/// [`wgpu::SurfaceTexture`](https://docs.rs/wgpu/latest/wgpu/struct.SurfaceTexture.html)s
/// exist in specific conditions, so the safest thing to do is make sure only
/// one can exist.
#[must_use]
pub struct ActiveSurfaceWindow<'w> {
    #[cfg(feature = "wgpu-30")]
    queue: wgpu::Queue,
    //surface: &'w wgpu::Surface<'static>,
    //config: &'w wgpu::SurfaceConfiguration,
    surface: RefMut<'w, Arc<wgpu::Surface<'static>>>,
    window: &'w Window,
    texture: wgpu::SurfaceTexture,
    view: wgpu::TextureView,
    resized: bool,
}

impl ActiveSurfaceWindow<'_> {
    /// Get the window's [`wgpu::Surface`](https://docs.rs/wgpu/latest/wgpu/struct.Surface.html).
    ///
    /// # Safety
    ///
    /// This is `unsafe` because reconfiguring this surface while it has an
    /// active view will cause _wgpu_ to panic. _wgpu_ doesn't enforce this so I
    /// gotta. In other words:  
    /// ***ABSOLUTELY DO NOT CALL
    /// [`wgpu::Surface::configure`](https://docs.rs/wgpu/latest/wgpu/struct.Surface.html#method.configure)
    /// ON THIS SURFACE***
    pub unsafe fn surface(&self) -> &wgpu::Surface<'static> {
        &self.surface
    }

    /// Get the surface's [`wgpu::SurfaceConfiguration`](https://docs.rs/wgpu/latest/wgpu/type.SurfaceConfiguration.html).
    ///
    /// Under the hood, `self.surface().get_configuration().unwrap()` - the surface
    /// is guaranteed to be configured if it made it into this struct.
    pub fn config(&self) -> wgpu::SurfaceConfiguration {
        self.surface.get_configuration()
            .expect("Surface should have been configured before constructing ActiveSurfaceWindow")
    }

    /// Get the [`winit::window::Window`](https://docs.rs/winit/latest/winit/window/struct.Window.html).
    pub fn window(&self) -> &Window {
        self.window
    }

    /// Convenience function to get the window's inner size.
    pub fn get_size(&self) -> PhysicalSize<u32> {
        self.window.inner_size()
    }

    /// Convenience function to get the configured size of the surface.
    ///
    /// Resize events may not be sent in time before a frame runs, leading to
    /// desyncs. This may cause a panic on _wgpu_'s side if you have a depth, which
    /// needs to always be kept the same size as the configured surface.  
    /// See also [`Self::resized`].
    pub fn get_config_size(&self) -> PhysicalSize<u32> {
        let config = self.config();
        PhysicalSize {
            width: config.width,
            height: config.height,
        }
    }

    /// Get the surface's
    /// [`wgpu::SurfaceTexture`](https://docs.rs/wgpu/latest/wgpu/struct.SurfaceTexture.html).
    pub fn texture(&self) -> &wgpu::SurfaceTexture {
        &self.texture
    }

    /// Get the surface texture's
    /// [`wgpu::TextureView`](https://docs.rs/wgpu/latest/wgpu/struct.TextureView.html)
    /// with default settings.
    pub fn view(&self) -> &wgpu::TextureView {
        &self.view
    }

    /// Returns `true` if the surface has just been resized (reconfigured or rebuilt
    /// with a new size since last frame).
    ///
    /// Useful to keep a depth stencil buffer in-sync.
    pub fn resized(&self) -> bool {
        self.resized
    }

    /// Get a [`wgpu::RenderPassColorAttachment`](https://docs.rs/wgpu/latest/wgpu/struct.RenderPassColorAttachment.html)
    /// to attach this surface's texture to a render pass, to draw to it using a
    /// specified clear colour.
    pub fn as_colour_attachment(&self, clear: Option<wgpu::Color>) -> wgpu::RenderPassColorAttachment<'_> {
        wgpu::RenderPassColorAttachment {
            view: &self.view,
            depth_slice: None,
            resolve_target: None,  // ???
            ops: wgpu::Operations {
                load: clear.map_or(wgpu::LoadOp::Load, wgpu::LoadOp::Clear),
                store: wgpu::StoreOp::Store,
            },
        }
    }

    /// Present the graphics operations to the surface. Consumes this struct, init a
    /// new one for next frame.
    pub fn present_texture(self) {
        self.window.pre_present_notify();

        #[cfg(feature = "wgpu-29")]
        self.texture.present();
        #[cfg(feature = "wgpu-30")]
        self.queue.present(self.texture);
    }
}


/// Unsafe state of a [`DeferredContext`]. Waits for display handles & surfaces,
/// then schedules or polls an `async` task to build a [`RenderContext`].
#[derive(Debug)]
#[allow(dead_code)]
enum ContextState {
    /// Instance not built yet, waiting on a window to be built to grab a display
    /// handle. Will wait for a surface to be provided in [`WaitingForSurface`].
    WaitingForDisplayHandle {
        builder: RenderContextBuilder<crate::InstanceSettings>,
    },
    /// Compatible surface not provided yet, which is required on web. Once
    /// provided, spawns a JS async task and waits in [`Building`], or blocks/polls
    /// and goes directly to [`Resolved`].
    WaitingForSurface {
        builder: RenderContextBuilder<wgpu::Instance>,
    },
    /// Context building has started, waiting for it to return. Will go to
    /// [`Resolved`] when queried once the task has finished. Only waits in this
    /// stage on web.
    Building {
        instance: wgpu::Instance,
        receiver: Receiver<Result<RenderContext, BuildContextError>>,
    },
    /// Finished result from async task or poll. Has either the successful context,
    /// or an error. Also holds an [`wgpu::Instance`], but only in error state as
    /// it's also stored in the context.
    Resolved {
        context: Result<RenderContext, (wgpu::Instance, BuildDeferredContextError)>,
    },
    /// Invalid state to temporarily take ownership of the enum when switching
    /// between variants. Should never be constructed.
    Switching,
}

impl ContextState {
    /// Given a builder with an instance, wait for a surface to be provided, or if
    /// it already is, start the building task.
    fn build(builder: RenderContextBuilder<wgpu::Instance>) -> Self {
        #[cfg(target_family = "wasm")]
        {
            // Still need compatible surface 
            if builder.compatible_surface.is_none() {
                return Self::WaitingForSurface { builder, };
            }

            use std::sync::mpsc::sync_channel;
            let (send, recv) = sync_channel::<Result<RenderContext, BuildContextError>>(1);

            let instance = builder.get_instance();
            web_sys::js_sys::futures::spawn_local(async move {
                let context = builder.build().await;
                let _ = send.send(context);
            });

            Self::Building { instance, receiver: recv, }
        }

        #[cfg(not(target_family = "wasm"))]
        {
            // If not on web, can skip compatible surface entirely
            let instance = builder.get_instance();
            let context = pollster::block_on(builder.build())
                .map_err(|e| (instance, e.into()));
            Self::Resolved { context, }
        }
    }

    /// Provide a display handle to make an Instance, if necessary. Returns the
    /// Instance that is guaranteed to exist now that there is a display handle.
    fn provide_display_handle_with(&mut self, get_display_handle: impl FnOnce() -> Box<dyn wgpu::wgt::WgpuHasDisplayHandle>) -> wgpu::Instance {
        let mut switching = std::mem::replace(self, Self::Switching);

        let instance = match switching {
            Self::WaitingForDisplayHandle { mut builder, } => {
                builder.set_display_handle(get_display_handle());
                let builder = builder.build_instance();
                let instance = builder.get_instance();
                // Polls immediately on native, or waits for surface on web
                switching = Self::build(builder);

                instance
            },

            Self::WaitingForSurface { ref builder, } => builder.get_instance(),
            Self::Building { ref instance, .. } => instance.clone(),
            Self::Resolved { ref context, } => context.as_ref().map(|c| c.instance.clone())
                .unwrap_or_else(|e| e.0.clone()),
            Self::Switching => unreachable!(),
        };

        let _ = std::mem::replace(self, switching);
        instance
    }

    /// Provide a surface to make an [`wgpu::Adapter`]. Not necessary on native.
    fn provide_surface_with(&mut self, get_surface: impl FnOnce() -> Arc<wgpu::Surface<'static>>) {
        let mut switching = std::mem::replace(self, Self::Switching);

        match switching {
            Self::WaitingForDisplayHandle { .. } => {
                unreachable!("Display handle should have been provided before a surface");
            },
            Self::WaitingForSurface { mut builder, } => {
                builder.set_compatible_surface(Some(get_surface()));
                switching = Self::build(builder);
            },
            Self::Switching => unreachable!(),
            _ => {},
        }

        let _ = std::mem::replace(self, switching);
    }

    /// Query the builder channel whether the thread finished building it. If so,
    /// return it.
    fn poll_resolve_context(&mut self) -> Result<&RenderContext, GetDeferredContextError> {
        match self {
            Self::WaitingForDisplayHandle { .. } => Err(GetDeferredContextError::RequiresDisplayHandle),
            Self::WaitingForSurface { .. } => Err(GetDeferredContextError::RequiresSurface),
            Self::Building { instance, receiver, } => {
                use std::sync::mpsc::TryRecvError;

                match receiver.try_recv() {
                    Ok(context) => {
                        let context = context.map_err(|e| (instance.clone(), e.into()));
                        *self = Self::Resolved { context, };
                        let Self::Resolved { context, .. } = self else { unreachable!() };
                        Ok(context.as_ref().map_err(|e| e.1.clone())?)
                    },
                    Err(TryRecvError::Disconnected) => {
                        *self = Self::Resolved {
                            context: Err((instance.clone(), BuildDeferredContextError::BuilderThreadDied)),
                        };
                        Err(BuildDeferredContextError::BuilderThreadDied.into())
                    },
                    Err(TryRecvError::Empty) => Err(GetDeferredContextError::StillBuilding),
                }
            },
            Self::Resolved { context, .. } => Ok(context.as_ref().map_err(|e| e.1.clone())?),
            Self::Switching => unreachable!(),
        }
    }
}


/// Defer the construction of the [`RenderContext`] until a window (and surface)
/// has been made.
///
/// WebGL really screws up the normal workflow of "first create your _wgpu_
/// handles, then make windows and stuff", because creating the
/// [`wgpu::Adapter`](https://docs.rs/wgpu/latest/wgpu/struct.Adapter.html)
/// requires a
/// [`wgpu::Surface`](https://docs.rs/wgpu/latest/wgpu/struct.Surface.html)
/// to be passed in after creating it with a
/// [`wgpu::Instance`](https://docs.rs/wgpu/latest/wgpu/struct.Instance.html)
/// using the configuration of a
/// [`winit::window::Window`](https://docs.rs/winit/latest/winit/window/struct.Window.html).
/// To top it all off, this painful back-and-forth involves `async`.
///
/// On web, this struct caches a builder and constructs the [`RenderContext`]
/// in a JS thread opportunistically when it is used to construct a
/// [`SurfaceWindow`].  
/// Native platforms block the thread when constructing, it's not an operation
/// that should take very long. _pollster_ doesn't seem to get along with JS
/// however.
///
/// Also, as of _wgpu_ 29.0, you need to provide a
/// [`dyn wgpu::wgt::WgpuHasDisplayHandle`](https://docs.rs/wgpu-types/latest/wgpu_types/instance/trait.WgpuHasDisplayHandle.html)
/// manually, whereas this was automatic before. The [`DeferredContext`] also
/// handles this for you, by grabbing the
/// [`winit::event_loop::OwnedDisplayHandle`](https://docs.rs/winit/latest/winit/event_loop/struct.OwnedDisplayHandle.html)
/// when creating a window.
///
/// See example usage below. There's a bit of jumping through hoops, but most of
/// the annoying stuff is handled behind the scenes for you.
/// ```rust
/// impl ApplicationHandler for YourApp {
///     fn resume(&mut self, event_loop: &ActiveEventLoop) {
///         // Create a window and provide the display handle & surface to the deferred
///         // context to start building.
///         let window = self.window.get_or_init(|| {
///             let mut attributes = WindowAttributes::default();
///             #[cfg(target_family = "wasm")]
///             attributes.with_canvas(get_gl_canvas());
///             // Passing in the event loop here gives the render context access to a
///             // display handle, which needs to be manually provided since wgpu 29.0.
///             // On web, will also provide the surface to the context builder. The
///             // surface isn't configured until it's needed.
///             self.deferred_context.create_window(event_loop, attributes, Default::default())
///                 .expect("Couldn't create window");
///         });
///
///         // Can't do much else until we have a valid context, which may take until
///         // the next function call to finish building. Move on to main loop.
///         self.suspended = false;
///     }
///
///     fn window_event(&mut self, event_loop: &ActiveEventLoop, window_id: _, event: _) {
///         if self.suspended { return; }
///
///         let context = match self.defered_context.get_context() {
///             Ok(c) => c,
///             // Something went wrong building the context, abort.
///             Err(GetDeferredContextError::Build(e)) => panic!("Error building context: {e}"),
///             // resume() didn't run for some reason, RenderContext has no display
///             // handle to use to initialize with.
///             Err(GetDeferredContextError::RequiresDisplayHandle) =>
///                 unreachable!("Display handle should have been provided in resume()"),
///             // resume() didn't run for some reason, RenderContext has no surface
///             // to use to initialize with (on web).
///             Err(GetDeferredContextError::RequiresSurface) =>
///                 unreachable!("Surface should have been provided in resume()"),
///             // Still building (on web), will try again next window_event().
///             Err(GetDeferredContextError::StillBuilding) => return,
///         };
///
///         // Now that the context is ready, we can finish configuring the window surface.
///         // init_with_context() will initialize everything lazily.
///         let active_window = self.window.get().expect("Window should have created in resume()")
///             .init_with_context(&context).expect("Couldn't init window");
///
///         // Side-note: Vertex/shader buffers, pipelines, etc. are also dependent on the
///         // RenderContext being configured (with a display handle & surface on web).
///         // Put the buffers in an Option/OnceCell and generate them ASAP.
///         let buffers_and_shit = self.buffers_and_shit.get_or_insert_with(|| init_buffers_and_shit(&context, active_window));
///
///         Self::finally_render(context, active_window);
///     }
///
///     fn suspended(&mut self, event_loop: &ActiveEventLoop) {
///         // Android requires surfaces be dropped on suspend. init_with_context() above
///         // will re-create and configure the window surface automatically when unsuspended.
///         self.window.get_mut().map(|w| w.drop_surface());
///         self.suspended = true;
///     }
/// }
/// ```
#[derive(Debug)]
pub struct DeferredContext {
    //instance: wgpu::Instance,
    context: UnsafeCell<ContextState>,
}

impl DeferredContext {
    /// Defer building a [`RenderContext`] until all required resources are
    /// available.
    ///
    /// As of _wgpu_ 29.0, a 
    /// [`dyn wgpu::wgt::WgpuHasDisplayHandle`](https://docs.rs/wgpu-types/latest/wgpu_types/instance/trait.WgpuHasDisplayHandle.html)
    /// is required to properly get an
    /// [`wgpu::Instance`](https://docs.rs/wgpu/latest/wgpu/struct.Instance.html),
    /// so the context waits until a [`SurfaceWindow`] is created to fully
    /// initialize.
    /// 
    /// On WebGL, it also needs to wait for a window to be created so it can pass
    /// in a compatible
    /// [`wgpu::Surface`](https://docs.rs/wgpu/latest/wgpu/struct.Surface.html).
    /// This step is skipped on native platforms.
    pub fn new(builder: RenderContextBuilder<crate::InstanceSettings>) -> Self {
        if builder.instance.display_handle.is_some() {
            Self {
                context: ContextState::build(builder.build_instance()).into(),
            }
        } else {
            Self {
                context: ContextState::WaitingForDisplayHandle { builder, }.into(),
            }
        }
    }


    /// The same as [`Self::new`], but takes a [`RenderContextBuilder`] with an
    /// already-initialized
    /// [`wgpu::Instance`](https://docs.rs/wgpu/latest/wgpu/struct.Instance.html).
    ///
    /// On WebGL, this will still wait for a window to be created, so it can
    /// validate the
    /// [`wgpu::Surface`](https://docs.rs/wgpu/latest/wgpu/struct.Surface.html).
    pub fn new_with_instance(builder: RenderContextBuilder<wgpu::Instance>) -> Self {
        Self {
            context: ContextState::build(builder).into(),
        }
    }

    /// Create a [`SurfaceWindow`], and at the same time, provide the builder with
    /// a display handle to create a valid 
    /// [`wgpu::Instance`](https://docs.rs/wgpu/latest/wgpu/struct.Instance.html),
    /// and on web a WebGL surface so it can create a valid
    /// [`wgpu::Adapter`](https://docs.rs/wgpu/latest/wgpu/struct.Adapter.html).
    ///
    /// In effect, the caller can almost ignore the whole initialization workflow -
    /// this method makes a window in the event loop, and then gets the context to
    /// draw to it.
    pub fn create_window(&self, event_loop: &ActiveEventLoop, attributes: WindowAttributes, config_opts: SurfaceConfigOptions) -> Result<SurfaceWindow, CreateWindowError> {
        let context_state = unsafe { &mut *self.context.get() };
        let instance = context_state.provide_display_handle_with(|| Box::new(event_loop.owned_display_handle()));

        let mut window = SurfaceWindow::new(event_loop, attributes, config_opts)?;
        let surface = Arc::new(window.make_surface(&instance)?);
        context_state.provide_surface_with(|| surface.clone());
        let _ = window.surface.get_mut().insert(surface);

        Ok(window)
    }

    /// Get a clone of the
    /// [`wgpu::Instance`](https://docs.rs/wgpu/latest/wgpu/struct.Instance.html),
    /// if it has been provided manually, or after a window has been created.
    pub fn get_instance(&self) -> Option<wgpu::Instance> {
        match unsafe { &*self.context.get() } {
            ContextState::WaitingForDisplayHandle { .. } => None,
            ContextState::WaitingForSurface { builder, } => Some(builder.get_instance()),
            ContextState::Building { instance, .. } => Some(instance.clone()),
            ContextState::Resolved { context: Ok(c), } => Some(c.instance.clone()),
            ContextState::Resolved { context: Err((i, _)), } => Some(i.clone()),
            ContextState::Switching => unreachable!(),
        }
    }

    /// Try to get the [`RenderContext`].
    ///
    /// If it hasn't been built yet, or if it ran into an error while building,
    /// you'll get an `Err(_)` - try again next frame.
    ///
    /// Make sure to first create a window with [`Self::create_window`], as you
    /// can't make a valid 
    /// [`wgpu::Instance`](https://docs.rs/wgpu/latest/wgpu/struct.Instance.html)
    /// without passing in a
    /// [`winit::ActiveEventLoop`](https://docs.rs/winit/latest/winit/event_loop/struct.ActiveEventLoop.html),
    /// and then you can't get a
    /// [`wgpu::Adapter`](https://docs.rs/wgpu/latest/wgpu/struct.Adapter.html)
    /// before providing a
    /// [`wgpu::Surface`](https://docs.rs/wgpu/latest/wgpu/struct.Surface.html),
    /// which needs to be configured with an instance.  
    /// Makes sense, right? Totally not backwards? That's why I made this library.
    pub fn get_context(&self) -> Result<&RenderContext, GetDeferredContextError> {
        let context_state = unsafe { &mut *self.context.get() };
        context_state.poll_resolve_context()
    }
}