wgpu_hal/gles/
wgl.rs

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