wgpu_hal/gles/
wgl.rs

1use std::{
2    collections::HashSet,
3    ffi::{c_void, CStr, CString},
4    mem::{self, size_of, size_of_val, ManuallyDrop},
5    os::raw::c_int,
6    ptr,
7    sync::{
8        mpsc::{sync_channel, SyncSender},
9        Arc,
10    },
11    thread,
12    time::Duration,
13};
14
15use glow::HasContext;
16use glutin_wgl_sys::wgl_extra::{
17    Wgl, CONTEXT_CORE_PROFILE_BIT_ARB, CONTEXT_DEBUG_BIT_ARB, CONTEXT_FLAGS_ARB,
18    CONTEXT_PROFILE_MASK_ARB,
19};
20use once_cell::sync::Lazy;
21use parking_lot::{Mutex, MutexGuard, RwLock};
22use raw_window_handle::{RawDisplayHandle, RawWindowHandle};
23use wgt::InstanceFlags;
24use windows::{
25    core::{Error, PCSTR},
26    Win32::{
27        Foundation,
28        Graphics::{Gdi, OpenGL},
29        System::LibraryLoader,
30        UI::WindowsAndMessaging,
31    },
32};
33
34/// The amount of time to wait while trying to obtain a lock to the adapter context
35const CONTEXT_LOCK_TIMEOUT_SECS: u64 = 1;
36
37/// A wrapper around a `[`glow::Context`]` and the required WGL context that uses locking to
38/// guarantee exclusive access when shared with multiple threads.
39pub struct AdapterContext {
40    inner: Arc<Mutex<Inner>>,
41}
42
43unsafe impl Sync for AdapterContext {}
44unsafe impl Send for AdapterContext {}
45
46impl AdapterContext {
47    pub fn is_owned(&self) -> bool {
48        true
49    }
50
51    pub fn raw_context(&self) -> *mut c_void {
52        match self.inner.lock().context {
53            Some(ref wgl) => wgl.context.0,
54            None => ptr::null_mut(),
55        }
56    }
57
58    /// Obtain a lock to the WGL context and get handle to the [`glow::Context`] that can be used to
59    /// do rendering.
60    #[track_caller]
61    pub fn lock(&self) -> AdapterContextLock<'_> {
62        let inner = self
63            .inner
64            // Don't lock forever. If it takes longer than 1 second to get the lock we've got a
65            // deadlock and should panic to show where we got stuck
66            .try_lock_for(Duration::from_secs(CONTEXT_LOCK_TIMEOUT_SECS))
67            .expect("Could not lock adapter context. This is most-likely a deadlock.");
68
69        if let Some(wgl) = &inner.context {
70            wgl.make_current(inner.device.dc).unwrap()
71        };
72
73        AdapterContextLock { inner }
74    }
75
76    /// Obtain a lock to the WGL context and get handle to the [`glow::Context`] that can be used to
77    /// do rendering.
78    ///
79    /// Unlike [`lock`](Self::lock), this accepts a device to pass to `make_current` and exposes the error
80    /// when `make_current` fails.
81    #[track_caller]
82    fn lock_with_dc(&self, device: Gdi::HDC) -> windows::core::Result<AdapterContextLock<'_>> {
83        let inner = self
84            .inner
85            .try_lock_for(Duration::from_secs(CONTEXT_LOCK_TIMEOUT_SECS))
86            .expect("Could not lock adapter context. This is most-likely a deadlock.");
87
88        if let Some(wgl) = &inner.context {
89            wgl.make_current(device)?;
90        }
91
92        Ok(AdapterContextLock { inner })
93    }
94}
95
96/// A guard containing a lock to an [`AdapterContext`], while the GL context is kept current.
97pub struct AdapterContextLock<'a> {
98    inner: MutexGuard<'a, Inner>,
99}
100
101impl<'a> std::ops::Deref for AdapterContextLock<'a> {
102    type Target = glow::Context;
103
104    fn deref(&self) -> &Self::Target {
105        &self.inner.gl
106    }
107}
108
109impl<'a> Drop for AdapterContextLock<'a> {
110    fn drop(&mut self) {
111        if let Some(wgl) = &self.inner.context {
112            wgl.unmake_current().unwrap()
113        }
114    }
115}
116
117struct WglContext {
118    context: OpenGL::HGLRC,
119}
120
121impl WglContext {
122    fn make_current(&self, device: Gdi::HDC) -> windows::core::Result<()> {
123        unsafe { OpenGL::wglMakeCurrent(device, self.context) }
124    }
125
126    fn unmake_current(&self) -> windows::core::Result<()> {
127        if unsafe { OpenGL::wglGetCurrentContext() }.is_invalid() {
128            return Ok(());
129        }
130        unsafe { OpenGL::wglMakeCurrent(None, None) }
131    }
132}
133
134impl Drop for WglContext {
135    fn drop(&mut self) {
136        if let Err(e) = unsafe { OpenGL::wglDeleteContext(self.context) } {
137            log::error!("failed to delete WGL context: {e}");
138        }
139    }
140}
141
142unsafe impl Send for WglContext {}
143unsafe impl Sync for WglContext {}
144
145struct Inner {
146    gl: ManuallyDrop<glow::Context>,
147    device: InstanceDevice,
148    context: Option<WglContext>,
149}
150
151impl Drop for Inner {
152    fn drop(&mut self) {
153        struct CurrentGuard<'a>(&'a WglContext);
154        impl Drop for CurrentGuard<'_> {
155            fn drop(&mut self) {
156                self.0.unmake_current().unwrap();
157            }
158        }
159
160        // Context must be current when dropped. See safety docs on
161        // `glow::HasContext`.
162        //
163        // NOTE: This is only set to `None` by `Adapter::new_external` which
164        // requires the context to be current when anything that may be holding
165        // the `Arc<AdapterShared>` is dropped.
166        let _guard = self.context.as_ref().map(|wgl| {
167            wgl.make_current(self.device.dc).unwrap();
168            CurrentGuard(wgl)
169        });
170        // SAFETY: Field not used after this.
171        unsafe { ManuallyDrop::drop(&mut self.gl) };
172    }
173}
174
175unsafe impl Send for Inner {}
176unsafe impl Sync for Inner {}
177
178pub struct Instance {
179    srgb_capable: bool,
180    inner: Arc<Mutex<Inner>>,
181}
182
183unsafe impl Send for Instance {}
184unsafe impl Sync for Instance {}
185
186fn load_gl_func(name: &str, module: Option<Foundation::HMODULE>) -> *const c_void {
187    let addr = CString::new(name.as_bytes()).unwrap();
188    let mut ptr = unsafe { OpenGL::wglGetProcAddress(PCSTR(addr.as_ptr().cast())) };
189    if ptr.is_none() {
190        if let Some(module) = module {
191            ptr = unsafe { LibraryLoader::GetProcAddress(module, PCSTR(addr.as_ptr().cast())) };
192        }
193    }
194    ptr.map_or_else(ptr::null_mut, |p| p as *mut c_void)
195}
196
197fn get_extensions(extra: &Wgl, dc: Gdi::HDC) -> HashSet<String> {
198    if extra.GetExtensionsStringARB.is_loaded() {
199        unsafe { CStr::from_ptr(extra.GetExtensionsStringARB(dc.0)) }
200            .to_str()
201            .unwrap_or("")
202    } else {
203        ""
204    }
205    .split(' ')
206    .map(|s| s.to_owned())
207    .collect()
208}
209
210unsafe fn setup_pixel_format(dc: Gdi::HDC) -> Result<(), crate::InstanceError> {
211    {
212        let format = OpenGL::PIXELFORMATDESCRIPTOR {
213            nVersion: 1,
214            nSize: size_of::<OpenGL::PIXELFORMATDESCRIPTOR>() as u16,
215            dwFlags: OpenGL::PFD_DRAW_TO_WINDOW
216                | OpenGL::PFD_SUPPORT_OPENGL
217                | OpenGL::PFD_DOUBLEBUFFER,
218            iPixelType: OpenGL::PFD_TYPE_RGBA,
219            cColorBits: 8,
220            ..unsafe { mem::zeroed() }
221        };
222
223        let index = unsafe { OpenGL::ChoosePixelFormat(dc, &format) };
224        if index == 0 {
225            return Err(crate::InstanceError::with_source(
226                String::from("unable to choose pixel format"),
227                Error::from_win32(),
228            ));
229        }
230
231        let current = unsafe { OpenGL::GetPixelFormat(dc) };
232
233        if index != current {
234            unsafe { OpenGL::SetPixelFormat(dc, index, &format) }.map_err(|e| {
235                crate::InstanceError::with_source(String::from("unable to set pixel format"), e)
236            })?;
237        }
238    }
239
240    {
241        let index = unsafe { OpenGL::GetPixelFormat(dc) };
242        if index == 0 {
243            return Err(crate::InstanceError::with_source(
244                String::from("unable to get pixel format index"),
245                Error::from_win32(),
246            ));
247        }
248        let mut format = Default::default();
249        if unsafe {
250            OpenGL::DescribePixelFormat(dc, index, size_of_val(&format) as u32, Some(&mut format))
251        } == 0
252        {
253            return Err(crate::InstanceError::with_source(
254                String::from("unable to read pixel format"),
255                Error::from_win32(),
256            ));
257        }
258
259        if !format.dwFlags.contains(OpenGL::PFD_SUPPORT_OPENGL)
260            || format.iPixelType != OpenGL::PFD_TYPE_RGBA
261        {
262            return Err(crate::InstanceError::new(String::from(
263                "unsuitable pixel format",
264            )));
265        }
266    }
267    Ok(())
268}
269
270fn create_global_window_class() -> Result<CString, crate::InstanceError> {
271    let instance = unsafe { LibraryLoader::GetModuleHandleA(None) }.map_err(|e| {
272        crate::InstanceError::with_source(String::from("unable to get executable instance"), e)
273    })?;
274
275    // Use the address of `UNIQUE` as part of the window class name to ensure different
276    // `wgpu` versions use different names.
277    static UNIQUE: Mutex<u8> = Mutex::new(0);
278    let class_addr: *const _ = &UNIQUE;
279    let name = format!("wgpu Device Class {:x}\0", class_addr as usize);
280    let name = CString::from_vec_with_nul(name.into_bytes()).unwrap();
281
282    // Use a wrapper function for compatibility with `windows-rs`.
283    unsafe extern "system" fn wnd_proc(
284        window: Foundation::HWND,
285        msg: u32,
286        wparam: Foundation::WPARAM,
287        lparam: Foundation::LPARAM,
288    ) -> Foundation::LRESULT {
289        unsafe { WindowsAndMessaging::DefWindowProcA(window, msg, wparam, lparam) }
290    }
291
292    let window_class = WindowsAndMessaging::WNDCLASSEXA {
293        cbSize: size_of::<WindowsAndMessaging::WNDCLASSEXA>() as u32,
294        style: WindowsAndMessaging::CS_OWNDC,
295        lpfnWndProc: Some(wnd_proc),
296        cbClsExtra: 0,
297        cbWndExtra: 0,
298        hInstance: instance.into(),
299        hIcon: WindowsAndMessaging::HICON::default(),
300        hCursor: WindowsAndMessaging::HCURSOR::default(),
301        hbrBackground: Gdi::HBRUSH::default(),
302        lpszMenuName: PCSTR::null(),
303        lpszClassName: PCSTR(name.as_ptr().cast()),
304        hIconSm: WindowsAndMessaging::HICON::default(),
305    };
306
307    let atom = unsafe { WindowsAndMessaging::RegisterClassExA(&window_class) };
308
309    if atom == 0 {
310        return Err(crate::InstanceError::with_source(
311            String::from("unable to register window class"),
312            Error::from_win32(),
313        ));
314    }
315
316    // We intentionally leak the window class as we only need one per process.
317
318    Ok(name)
319}
320
321fn get_global_window_class() -> Result<CString, crate::InstanceError> {
322    static GLOBAL: Lazy<Result<CString, crate::InstanceError>> =
323        Lazy::new(create_global_window_class);
324    GLOBAL.clone()
325}
326
327struct InstanceDevice {
328    dc: Gdi::HDC,
329
330    /// This is used to keep the thread owning `dc` alive until this struct is dropped.
331    _tx: SyncSender<()>,
332}
333
334fn create_instance_device() -> Result<InstanceDevice, crate::InstanceError> {
335    #[derive(Clone, Copy)]
336    // TODO: We can get these SendSync definitions in the upstream metadata if this is the case
337    struct SendDc(Gdi::HDC);
338    unsafe impl Sync for SendDc {}
339    unsafe impl Send for SendDc {}
340
341    struct Window {
342        window: Foundation::HWND,
343    }
344    impl Drop for Window {
345        fn drop(&mut self) {
346            if let Err(e) = unsafe { WindowsAndMessaging::DestroyWindow(self.window) } {
347                log::error!("failed to destroy window: {e}");
348            }
349        }
350    }
351
352    let window_class = get_global_window_class()?;
353
354    let (drop_tx, drop_rx) = sync_channel(0);
355    let (setup_tx, setup_rx) = sync_channel(0);
356
357    // We spawn a thread which owns the hidden window for this instance.
358    thread::Builder::new()
359        .stack_size(256 * 1024)
360        .name("wgpu-hal WGL Instance Thread".to_owned())
361        .spawn(move || {
362            let setup = (|| {
363                let instance = unsafe { LibraryLoader::GetModuleHandleA(None) }.map_err(|e| {
364                    crate::InstanceError::with_source(
365                        String::from("unable to get executable instance"),
366                        e,
367                    )
368                })?;
369
370                // Create a hidden window since we don't pass `WS_VISIBLE`.
371                let window = unsafe {
372                    WindowsAndMessaging::CreateWindowExA(
373                        WindowsAndMessaging::WINDOW_EX_STYLE::default(),
374                        PCSTR(window_class.as_ptr().cast()),
375                        PCSTR(window_class.as_ptr().cast()),
376                        WindowsAndMessaging::WINDOW_STYLE::default(),
377                        0,
378                        0,
379                        1,
380                        1,
381                        None,
382                        None,
383                        instance,
384                        None,
385                    )
386                }
387                .map_err(|e| {
388                    crate::InstanceError::with_source(
389                        String::from("unable to create hidden instance window"),
390                        e,
391                    )
392                })?;
393                let window = Window { window };
394
395                let dc = unsafe { Gdi::GetDC(window.window) };
396                if dc.is_invalid() {
397                    return Err(crate::InstanceError::with_source(
398                        String::from("unable to create memory device"),
399                        Error::from_win32(),
400                    ));
401                }
402                let dc = DeviceContextHandle {
403                    device: dc,
404                    window: window.window,
405                };
406                unsafe { setup_pixel_format(dc.device)? };
407
408                Ok((window, dc))
409            })();
410
411            match setup {
412                Ok((_window, dc)) => {
413                    setup_tx.send(Ok(SendDc(dc.device))).unwrap();
414                    // Wait for the shutdown event to free the window and device context handle.
415                    drop_rx.recv().ok();
416                }
417                Err(err) => {
418                    setup_tx.send(Err(err)).unwrap();
419                }
420            }
421        })
422        .map_err(|e| {
423            crate::InstanceError::with_source(String::from("unable to create instance thread"), e)
424        })?;
425
426    let dc = setup_rx.recv().unwrap()?.0;
427
428    Ok(InstanceDevice { dc, _tx: drop_tx })
429}
430
431impl crate::Instance for Instance {
432    type A = super::Api;
433
434    unsafe fn init(desc: &crate::InstanceDescriptor) -> Result<Self, crate::InstanceError> {
435        profiling::scope!("Init OpenGL (WGL) Backend");
436        let opengl_module =
437            unsafe { LibraryLoader::LoadLibraryA(PCSTR("opengl32.dll\0".as_ptr())) }.map_err(
438                |e| {
439                    crate::InstanceError::with_source(
440                        String::from("unable to load the OpenGL library"),
441                        e,
442                    )
443                },
444            )?;
445
446        let device = create_instance_device()?;
447        let dc = device.dc;
448
449        let context = unsafe { OpenGL::wglCreateContext(dc) }.map_err(|e| {
450            crate::InstanceError::with_source(
451                String::from("unable to create initial OpenGL context"),
452                e,
453            )
454        })?;
455        let context = WglContext { context };
456        context.make_current(dc).map_err(|e| {
457            crate::InstanceError::with_source(
458                String::from("unable to set initial OpenGL context as current"),
459                e,
460            )
461        })?;
462
463        let extra = Wgl::load_with(|name| load_gl_func(name, None));
464        let extensions = get_extensions(&extra, dc);
465
466        let can_use_profile = extensions.contains("WGL_ARB_create_context_profile")
467            && extra.CreateContextAttribsARB.is_loaded();
468
469        let context = if can_use_profile {
470            let attributes = [
471                CONTEXT_PROFILE_MASK_ARB as c_int,
472                CONTEXT_CORE_PROFILE_BIT_ARB as c_int,
473                CONTEXT_FLAGS_ARB as c_int,
474                if desc.flags.contains(InstanceFlags::DEBUG) {
475                    CONTEXT_DEBUG_BIT_ARB as c_int
476                } else {
477                    0
478                },
479                0, // End of list
480            ];
481            let context =
482                unsafe { extra.CreateContextAttribsARB(dc.0, ptr::null(), attributes.as_ptr()) };
483            if context.is_null() {
484                return Err(crate::InstanceError::with_source(
485                    String::from("unable to create OpenGL context"),
486                    Error::from_win32(),
487                ));
488            }
489            WglContext {
490                context: OpenGL::HGLRC(context.cast_mut()),
491            }
492        } else {
493            context
494        };
495
496        context.make_current(dc).map_err(|e| {
497            crate::InstanceError::with_source(
498                String::from("unable to set OpenGL context as current"),
499                e,
500            )
501        })?;
502
503        let mut gl = unsafe {
504            glow::Context::from_loader_function(|name| load_gl_func(name, Some(opengl_module)))
505        };
506
507        let extra = Wgl::load_with(|name| load_gl_func(name, None));
508        let extensions = get_extensions(&extra, dc);
509
510        let srgb_capable = extensions.contains("WGL_EXT_framebuffer_sRGB")
511            || extensions.contains("WGL_ARB_framebuffer_sRGB")
512            || gl
513                .supported_extensions()
514                .contains("GL_ARB_framebuffer_sRGB");
515
516        // In contrast to OpenGL ES, OpenGL requires explicitly enabling sRGB conversions,
517        // as otherwise the user has to do the sRGB conversion.
518        if srgb_capable {
519            unsafe { gl.enable(glow::FRAMEBUFFER_SRGB) };
520        }
521
522        if desc.flags.contains(InstanceFlags::VALIDATION) && gl.supports_debug() {
523            log::debug!("Enabling GL debug output");
524            unsafe { gl.enable(glow::DEBUG_OUTPUT) };
525            unsafe { gl.debug_message_callback(super::gl_debug_message_callback) };
526        }
527
528        // Wrap in ManuallyDrop to make it easier to "current" the GL context before dropping this
529        // GLOW context, which could also happen if a panic occurs after we uncurrent the context
530        // below but before Inner is constructed.
531        let gl = ManuallyDrop::new(gl);
532        context.unmake_current().map_err(|e| {
533            crate::InstanceError::with_source(
534                String::from("unable to unset the current WGL context"),
535                e,
536            )
537        })?;
538
539        Ok(Instance {
540            inner: Arc::new(Mutex::new(Inner {
541                device,
542                gl,
543                context: Some(context),
544            })),
545            srgb_capable,
546        })
547    }
548
549    #[cfg_attr(target_os = "macos", allow(unused, unused_mut, unreachable_code))]
550    unsafe fn create_surface(
551        &self,
552        _display_handle: RawDisplayHandle,
553        window_handle: RawWindowHandle,
554    ) -> Result<Surface, crate::InstanceError> {
555        let window = if let RawWindowHandle::Win32(handle) = window_handle {
556            handle
557        } else {
558            return Err(crate::InstanceError::new(format!(
559                "unsupported window: {window_handle:?}"
560            )));
561        };
562        Ok(Surface {
563            // This cast exists because of https://github.com/rust-windowing/raw-window-handle/issues/171
564            window: Foundation::HWND(window.hwnd.get() as *mut _),
565            presentable: true,
566            swapchain: RwLock::new(None),
567            srgb_capable: self.srgb_capable,
568        })
569    }
570
571    unsafe fn enumerate_adapters(
572        &self,
573        _surface_hint: Option<&Surface>,
574    ) -> Vec<crate::ExposedAdapter<super::Api>> {
575        unsafe {
576            super::Adapter::expose(AdapterContext {
577                inner: self.inner.clone(),
578            })
579        }
580        .into_iter()
581        .collect()
582    }
583}
584
585impl super::Adapter {
586    /// Creates a new external adapter using the specified loader function.
587    ///
588    /// # Safety
589    ///
590    /// - The underlying OpenGL ES context must be current.
591    /// - The underlying OpenGL ES context must be current when interfacing with any objects returned by
592    ///   wgpu-hal from this adapter.
593    /// - The underlying OpenGL ES context must be current when dropping this adapter and when
594    ///   dropping any objects returned from this adapter.
595    pub unsafe fn new_external(
596        fun: impl FnMut(&str) -> *const c_void,
597    ) -> Option<crate::ExposedAdapter<super::Api>> {
598        let context = unsafe { glow::Context::from_loader_function(fun) };
599        unsafe {
600            Self::expose(AdapterContext {
601                inner: Arc::new(Mutex::new(Inner {
602                    gl: ManuallyDrop::new(context),
603                    device: create_instance_device().ok()?,
604                    context: None,
605                })),
606            })
607        }
608    }
609
610    pub fn adapter_context(&self) -> &AdapterContext {
611        &self.shared.context
612    }
613}
614
615impl super::Device {
616    /// Returns the underlying WGL context.
617    pub fn context(&self) -> &AdapterContext {
618        &self.shared.context
619    }
620}
621
622struct DeviceContextHandle {
623    device: Gdi::HDC,
624    window: Foundation::HWND,
625}
626
627impl Drop for DeviceContextHandle {
628    fn drop(&mut self) {
629        unsafe {
630            Gdi::ReleaseDC(self.window, self.device);
631        };
632    }
633}
634
635pub struct Swapchain {
636    framebuffer: glow::Framebuffer,
637    renderbuffer: glow::Renderbuffer,
638
639    /// Extent because the window lies
640    extent: wgt::Extent3d,
641
642    format: wgt::TextureFormat,
643    format_desc: super::TextureFormatDesc,
644    #[allow(unused)]
645    sample_type: wgt::TextureSampleType,
646}
647
648pub struct Surface {
649    window: Foundation::HWND,
650    pub(super) presentable: bool,
651    swapchain: RwLock<Option<Swapchain>>,
652    srgb_capable: bool,
653}
654
655unsafe impl Send for Surface {}
656unsafe impl Sync for Surface {}
657
658impl Surface {
659    pub(super) unsafe fn present(
660        &self,
661        _suf_texture: super::Texture,
662        context: &AdapterContext,
663    ) -> Result<(), crate::SurfaceError> {
664        let swapchain = self.swapchain.read();
665        let sc = swapchain.as_ref().unwrap();
666        let dc = unsafe { Gdi::GetDC(self.window) };
667        if dc.is_invalid() {
668            log::error!(
669                "unable to get the device context from window: {}",
670                Error::from_win32()
671            );
672            return Err(crate::SurfaceError::Other(
673                "unable to get the device context from window",
674            ));
675        }
676        let dc = DeviceContextHandle {
677            device: dc,
678            window: self.window,
679        };
680
681        let gl = context.lock_with_dc(dc.device).map_err(|e| {
682            log::error!("unable to make the OpenGL context current for surface: {e}",);
683            crate::SurfaceError::Other("unable to make the OpenGL context current for surface")
684        })?;
685
686        unsafe { gl.bind_framebuffer(glow::DRAW_FRAMEBUFFER, None) };
687        unsafe { gl.bind_framebuffer(glow::READ_FRAMEBUFFER, Some(sc.framebuffer)) };
688
689        if self.srgb_capable {
690            // Disable sRGB conversions for `glBlitFramebuffer` as behavior does diverge between
691            // drivers and formats otherwise and we want to ensure no sRGB conversions happen.
692            unsafe { gl.disable(glow::FRAMEBUFFER_SRGB) };
693        }
694
695        // Note the Y-flipping here. GL's presentation is not flipped,
696        // but main rendering is. Therefore, we Y-flip the output positions
697        // in the shader, and also this blit.
698        unsafe {
699            gl.blit_framebuffer(
700                0,
701                sc.extent.height as i32,
702                sc.extent.width as i32,
703                0,
704                0,
705                0,
706                sc.extent.width as i32,
707                sc.extent.height as i32,
708                glow::COLOR_BUFFER_BIT,
709                glow::NEAREST,
710            )
711        };
712
713        if self.srgb_capable {
714            unsafe { gl.enable(glow::FRAMEBUFFER_SRGB) };
715        }
716
717        unsafe { gl.bind_renderbuffer(glow::RENDERBUFFER, None) };
718        unsafe { gl.bind_framebuffer(glow::READ_FRAMEBUFFER, None) };
719
720        if let Err(e) = unsafe { OpenGL::SwapBuffers(dc.device) } {
721            log::error!("unable to swap buffers: {e}");
722            return Err(crate::SurfaceError::Other("unable to swap buffers"));
723        }
724
725        Ok(())
726    }
727
728    pub fn supports_srgb(&self) -> bool {
729        self.srgb_capable
730    }
731}
732
733impl crate::Surface for Surface {
734    type A = super::Api;
735
736    unsafe fn configure(
737        &self,
738        device: &super::Device,
739        config: &crate::SurfaceConfiguration,
740    ) -> Result<(), crate::SurfaceError> {
741        // Remove the old configuration.
742        unsafe { self.unconfigure(device) };
743
744        let dc = unsafe { Gdi::GetDC(self.window) };
745        if dc.is_invalid() {
746            log::error!(
747                "unable to get the device context from window: {}",
748                Error::from_win32()
749            );
750            return Err(crate::SurfaceError::Other(
751                "unable to get the device context from window",
752            ));
753        }
754        let dc = DeviceContextHandle {
755            device: dc,
756            window: self.window,
757        };
758
759        if let Err(e) = unsafe { setup_pixel_format(dc.device) } {
760            log::error!("unable to setup surface pixel format: {e}",);
761            return Err(crate::SurfaceError::Other(
762                "unable to setup surface pixel format",
763            ));
764        }
765
766        let format_desc = device.shared.describe_texture_format(config.format);
767        let gl = &device.shared.context.lock_with_dc(dc.device).map_err(|e| {
768            log::error!("unable to make the OpenGL context current for surface: {e}",);
769            crate::SurfaceError::Other("unable to make the OpenGL context current for surface")
770        })?;
771
772        let renderbuffer = unsafe { gl.create_renderbuffer() }.map_err(|error| {
773            log::error!("Internal swapchain renderbuffer creation failed: {error}");
774            crate::DeviceError::OutOfMemory
775        })?;
776        unsafe { gl.bind_renderbuffer(glow::RENDERBUFFER, Some(renderbuffer)) };
777        unsafe {
778            gl.renderbuffer_storage(
779                glow::RENDERBUFFER,
780                format_desc.internal,
781                config.extent.width as _,
782                config.extent.height as _,
783            )
784        };
785
786        let framebuffer = unsafe { gl.create_framebuffer() }.map_err(|error| {
787            log::error!("Internal swapchain framebuffer creation failed: {error}");
788            crate::DeviceError::OutOfMemory
789        })?;
790        unsafe { gl.bind_framebuffer(glow::READ_FRAMEBUFFER, Some(framebuffer)) };
791        unsafe {
792            gl.framebuffer_renderbuffer(
793                glow::READ_FRAMEBUFFER,
794                glow::COLOR_ATTACHMENT0,
795                glow::RENDERBUFFER,
796                Some(renderbuffer),
797            )
798        };
799        unsafe { gl.bind_renderbuffer(glow::RENDERBUFFER, None) };
800        unsafe { gl.bind_framebuffer(glow::READ_FRAMEBUFFER, None) };
801
802        // Setup presentation mode
803        let extra = Wgl::load_with(|name| load_gl_func(name, None));
804        let extensions = get_extensions(&extra, dc.device);
805        if !(extensions.contains("WGL_EXT_swap_control") && extra.SwapIntervalEXT.is_loaded()) {
806            log::error!("WGL_EXT_swap_control is unsupported");
807            return Err(crate::SurfaceError::Other(
808                "WGL_EXT_swap_control is unsupported",
809            ));
810        }
811
812        let vsync = match config.present_mode {
813            wgt::PresentMode::Immediate => false,
814            wgt::PresentMode::Fifo => true,
815            _ => {
816                log::error!("unsupported present mode: {:?}", config.present_mode);
817                return Err(crate::SurfaceError::Other("unsupported present mode"));
818            }
819        };
820
821        if unsafe { extra.SwapIntervalEXT(if vsync { 1 } else { 0 }) } == Foundation::FALSE.0 {
822            log::error!("unable to set swap interval: {}", Error::from_win32());
823            return Err(crate::SurfaceError::Other("unable to set swap interval"));
824        }
825
826        self.swapchain.write().replace(Swapchain {
827            renderbuffer,
828            framebuffer,
829            extent: config.extent,
830            format: config.format,
831            format_desc,
832            sample_type: wgt::TextureSampleType::Float { filterable: false },
833        });
834
835        Ok(())
836    }
837
838    unsafe fn unconfigure(&self, device: &super::Device) {
839        let gl = &device.shared.context.lock();
840        if let Some(sc) = self.swapchain.write().take() {
841            unsafe {
842                gl.delete_renderbuffer(sc.renderbuffer);
843                gl.delete_framebuffer(sc.framebuffer)
844            };
845        }
846    }
847
848    unsafe fn acquire_texture(
849        &self,
850        _timeout_ms: Option<Duration>,
851        _fence: &super::Fence,
852    ) -> Result<Option<crate::AcquiredSurfaceTexture<super::Api>>, crate::SurfaceError> {
853        let swapchain = self.swapchain.read();
854        let sc = swapchain.as_ref().unwrap();
855        let texture = super::Texture {
856            inner: super::TextureInner::Renderbuffer {
857                raw: sc.renderbuffer,
858            },
859            drop_guard: None,
860            array_layer_count: 1,
861            mip_level_count: 1,
862            format: sc.format,
863            format_desc: sc.format_desc.clone(),
864            copy_size: crate::CopyExtent {
865                width: sc.extent.width,
866                height: sc.extent.height,
867                depth: 1,
868            },
869        };
870        Ok(Some(crate::AcquiredSurfaceTexture {
871            texture,
872            suboptimal: false,
873        }))
874    }
875    unsafe fn discard_texture(&self, _texture: super::Texture) {}
876}