use glow::HasContext; use glutin_wgl_sys::wgl_extra::{
Wgl, CONTEXT_CORE_PROFILE_BIT_ARB, CONTEXT_DEBUG_BIT_ARB, CONTEXT_FLAGS_ARB,
CONTEXT_PROFILE_MASK_ARB,
}; use once_cell::sync::Lazy; use parking_lot::{Mutex, MutexGuard, RwLock}; use raw_window_handle::{RawDisplayHandle, RawWindowHandle}; use wgt::InstanceFlags; use windows::{
core::{Error, PCSTR},
Win32::{
Foundation,
Graphics::{Gdi, OpenGL},
System::LibraryLoader,
UI::WindowsAndMessaging,
},
};
/// The amount of time to wait while trying to obtain a lock to the adapter context const CONTEXT_LOCK_TIMEOUT_SECS: u64 = 1;
/// A wrapper around a `[`glow::Context`]` and the required WGL context that uses locking to /// guarantee exclusive access when shared with multiple threads. pubstruct AdapterContext {
inner: Arc<Mutex<Inner>>,
}
unsafeimpl Sync for AdapterContext {} unsafeimpl Send for AdapterContext {}
/// Obtain a lock to the WGL context and get handle to the [`glow::Context`] that can be used to /// do rendering. #[track_caller] pubfn lock(&self) -> AdapterContextLock<'_> { let inner = self
.inner // Don't lock forever. If it takes longer than 1 second to get the lock we've got a // deadlock and should panic to show where we got stuck
.try_lock_for(Duration::from_secs(CONTEXT_LOCK_TIMEOUT_SECS))
.expect("Could not lock adapter context. This is most-likely a deadlock.");
/// Obtain a lock to the WGL context and get handle to the [`glow::Context`] that can be used to /// do rendering. /// /// Unlike [`lock`](Self::lock), this accepts a device to pass to `make_current` and exposes the error /// when `make_current` fails. #[track_caller] fn lock_with_dc(&self, device: Gdi::HDC) -> windows::core::Result<AdapterContextLock<'_>> { let inner = self
.inner
.try_lock_for(Duration::from_secs(CONTEXT_LOCK_TIMEOUT_SECS))
.expect("Could not lock adapter context. This is most-likely a deadlock.");
/// A guard containing a lock to an [`AdapterContext`], while the GL context is kept current. pubstruct AdapterContextLock<'a> {
inner: MutexGuard<'a, Inner>,
}
impl<'a> std::ops::Deref for AdapterContextLock<'a> { type Target = glow::Context;
impl Drop for Inner { fn drop(&mutself) { struct CurrentGuard<'a>(&'a WglContext); impl Drop for CurrentGuard<'_> { fn drop(&mutself) { self.0.unmake_current().unwrap();
}
}
// Context must be current when dropped. See safety docs on // `glow::HasContext`. // // NOTE: This is only set to `None` by `Adapter::new_external` which // requires the context to be current when anything that may be holding // the `Arc<AdapterShared>` is dropped. let _guard = self.context.as_ref().map(|wgl| {
wgl.make_current(self.device.dc).unwrap();
CurrentGuard(wgl)
}); // SAFETY: Field not used after this. unsafe { ManuallyDrop::drop(&mutself.gl) };
}
}
unsafeimpl Send for Inner {} unsafeimpl Sync for Inner {}
let index = unsafe { OpenGL::ChoosePixelFormat(dc, &format) }; if index == 0 { return Err(crate::InstanceError::with_source(
String::from("unable to choose pixel format"),
Error::from_win32(),
));
}
let current = unsafe { OpenGL::GetPixelFormat(dc) };
if index != current { unsafe { OpenGL::SetPixelFormat(dc, index, &format) }.map_err(|e| { crate::InstanceError::with_source(String::from("unable to set pixel format"), e)
})?;
}
}
{ let index = unsafe { OpenGL::GetPixelFormat(dc) }; if index == 0 { return Err(crate::InstanceError::with_source(
String::from("unable to get pixel format index"),
Error::from_win32(),
));
} letmut format = Default::default(); ifunsafe {
OpenGL::DescribePixelFormat(dc, index, size_of_val(&format) as u32, Some(&mut format))
} == 0
{ return Err(crate::InstanceError::with_source(
String::from("unable to read pixel format"),
Error::from_win32(),
));
}
fn create_global_window_class() -> Result<CString, crate::InstanceError> { let instance = unsafe { LibraryLoader::GetModuleHandleA(None) }.map_err(|e| { crate::InstanceError::with_source(String::from("unable to get executable instance"), e)
})?;
// Use the address of `UNIQUE` as part of the window class name to ensure different // `wgpu` versions use different names. static UNIQUE: Mutex<u8> = Mutex::new(0); let class_addr: *const _ = &UNIQUE; let name = format!("wgpu Device Class {:x}\0", class_addr as usize); let name = CString::from_vec_with_nul(name.into_bytes()).unwrap();
// Use a wrapper function for compatibility with `windows-rs`. unsafeextern"system"fn wnd_proc(
window: Foundation::HWND,
msg: u32,
wparam: Foundation::WPARAM,
lparam: Foundation::LPARAM,
) -> Foundation::LRESULT { unsafe { WindowsAndMessaging::DefWindowProcA(window, msg, wparam, lparam) }
}
/// This is used to keep the thread owning `dc` alive until this struct is dropped.
_tx: SyncSender<()>,
}
fn create_instance_device() -> Result<InstanceDevice, crate::InstanceError> { #[derive(Clone, Copy)] // TODO: We can get these SendSync definitions in the upstream metadata if this is the case struct SendDc(Gdi::HDC); unsafeimpl Sync for SendDc {} unsafeimpl Send for SendDc {}
struct Window {
window: Foundation::HWND,
} impl Drop for Window { fn drop(&mutself) { iflet Err(e) = unsafe { WindowsAndMessaging::DestroyWindow(self.window) } {
log::error!("failed to destroy window: {e}");
}
}
}
let window_class = get_global_window_class()?;
let (drop_tx, drop_rx) = sync_channel(0); let (setup_tx, setup_rx) = sync_channel(0);
// We spawn a thread which owns the hidden window for this instance.
thread::Builder::new()
.stack_size(256 * 1024)
.name("wgpu-hal WGL Instance Thread".to_owned())
.spawn(move || { let setup = (|| { let instance = unsafe { LibraryLoader::GetModuleHandleA(None) }.map_err(|e| { crate::InstanceError::with_source(
String::from("unable to get executable instance"),
e,
)
})?;
// Create a hidden window since we don't pass `WS_VISIBLE`. let window = unsafe {
WindowsAndMessaging::CreateWindowExA(
WindowsAndMessaging::WINDOW_EX_STYLE::default(),
PCSTR(window_class.as_ptr().cast()),
PCSTR(window_class.as_ptr().cast()),
WindowsAndMessaging::WINDOW_STYLE::default(), 0, 0, 1, 1,
None,
None,
instance,
None,
)
}
.map_err(|e| { crate::InstanceError::with_source(
String::from("unable to create hidden instance window"),
e,
)
})?; let window = Window { window };
let dc = unsafe { Gdi::GetDC(window.window) }; if dc.is_invalid() { return Err(crate::InstanceError::with_source(
String::from("unable to create memory device"),
Error::from_win32(),
));
} let dc = DeviceContextHandle {
device: dc,
window: window.window,
}; unsafe { setup_pixel_format(dc.device)? };
Ok((window, dc))
})();
match setup {
Ok((_window, dc)) => {
setup_tx.send(Ok(SendDc(dc.device))).unwrap(); // Wait for the shutdown event to free the window and device context handle.
drop_rx.recv().ok();
}
Err(err) => {
setup_tx.send(Err(err)).unwrap();
}
}
})
.map_err(|e| { crate::InstanceError::with_source(String::from("unable to create instance thread"), e)
})?;
let dc = setup_rx.recv().unwrap()?.0;
Ok(InstanceDevice { dc, _tx: drop_tx })
}
implcrate::Instance for Instance { type A = super::Api;
unsafefn init(desc: &crate::InstanceDescriptor) -> Result<Self, crate::InstanceError> {
profiling::scope!("Init OpenGL (WGL) Backend"); let opengl_module = unsafe { LibraryLoader::LoadLibraryA(PCSTR("opengl32.dll\0".as_ptr())) }.map_err(
|e| { crate::InstanceError::with_source(
String::from("unable to load the OpenGL library"),
e,
)
},
)?;
let device = create_instance_device()?; let dc = device.dc;
let context = unsafe { OpenGL::wglCreateContext(dc) }.map_err(|e| { crate::InstanceError::with_source(
String::from("unable to create initial OpenGL context"),
e,
)
})?; let context = WglContext { context };
context.make_current(dc).map_err(|e| { crate::InstanceError::with_source(
String::from("unable to set initial OpenGL context as current"),
e,
)
})?;
let extra = Wgl::load_with(|name| load_gl_func(name, None)); let extensions = get_extensions(&extra, dc);
let can_use_profile = extensions.contains("WGL_ARB_create_context_profile")
&& extra.CreateContextAttribsARB.is_loaded();
let context = if can_use_profile { let attributes = [
CONTEXT_PROFILE_MASK_ARB as c_int,
CONTEXT_CORE_PROFILE_BIT_ARB as c_int,
CONTEXT_FLAGS_ARB as c_int, if desc.flags.contains(InstanceFlags::DEBUG) {
CONTEXT_DEBUG_BIT_ARB as c_int
} else { 0
}, 0, // End of list
]; let context = unsafe { extra.CreateContextAttribsARB(dc.0, ptr::null(), attributes.as_ptr()) }; if context.is_null() { return Err(crate::InstanceError::with_source(
String::from("unable to create OpenGL context"),
Error::from_win32(),
));
}
WglContext {
context: OpenGL::HGLRC(context.cast_mut()),
}
} else {
context
};
context.make_current(dc).map_err(|e| { crate::InstanceError::with_source(
String::from("unable to set OpenGL context as current"),
e,
)
})?;
let extra = Wgl::load_with(|name| load_gl_func(name, None)); let extensions = get_extensions(&extra, dc);
let srgb_capable = extensions.contains("WGL_EXT_framebuffer_sRGB")
|| extensions.contains("WGL_ARB_framebuffer_sRGB")
|| gl
.supported_extensions()
.contains("GL_ARB_framebuffer_sRGB");
// In contrast to OpenGL ES, OpenGL requires explicitly enabling sRGB conversions, // as otherwise the user has to do the sRGB conversion. if srgb_capable { unsafe { gl.enable(glow::FRAMEBUFFER_SRGB) };
}
// Wrap in ManuallyDrop to make it easier to "current" the GL context before dropping this // GLOW context, which could also happen if a panic occurs after we uncurrent the context // below but before Inner is constructed. let gl = ManuallyDrop::new(gl);
context.unmake_current().map_err(|e| { crate::InstanceError::with_source(
String::from("unable to unset the current WGL context"),
e,
)
})?;
implsuper::Adapter { /// Creates a new external adapter using the specified loader function. /// /// # Safety /// /// - The underlying OpenGL ES context must be current. /// - The underlying OpenGL ES context must be current when interfacing with any objects returned by /// wgpu-hal from this adapter. /// - The underlying OpenGL ES context must be current when dropping this adapter and when /// dropping any objects returned from this adapter. pubunsafefn new_external(
fun: impl FnMut(&str) -> *const c_void,
) -> Option<crate::ExposedAdapter<super::Api>> { let context = unsafe { glow::Context::from_loader_function(fun) }; unsafe { Self::expose(AdapterContext {
inner: Arc::new(Mutex::new(Inner {
gl: ManuallyDrop::new(context),
device: create_instance_device().ok()?,
context: None,
})),
})
}
}
unsafeimpl Send for Surface {} unsafeimpl Sync for Surface {}
impl Surface { pub(super) unsafefn present(
&self,
_suf_texture: super::Texture,
context: &AdapterContext,
) -> Result<(), crate::SurfaceError> { let swapchain = self.swapchain.read(); let sc = swapchain.as_ref().unwrap(); let dc = unsafe { Gdi::GetDC(self.window) }; if dc.is_invalid() {
log::error!( "unable to get the device context from window: {}",
Error::from_win32()
); return Err(crate::SurfaceError::Other( "unable to get the device context from window",
));
} let dc = DeviceContextHandle {
device: dc,
window: self.window,
};
let gl = context.lock_with_dc(dc.device).map_err(|e| {
log::error!("unable to make the OpenGL context current for surface: {e}",); crate::SurfaceError::Other("unable to make the OpenGL context current for surface")
})?;
ifself.srgb_capable { // Disable sRGB conversions for `glBlitFramebuffer` as behavior does diverge between // drivers and formats otherwise and we want to ensure no sRGB conversions happen. unsafe { gl.disable(glow::FRAMEBUFFER_SRGB) };
}
// Note the Y-flipping here. GL's presentation is not flipped, // but main rendering is. Therefore, we Y-flip the output positions // in the shader, and also this blit. unsafe {
gl.blit_framebuffer( 0,
sc.extent.height as i32,
sc.extent.width as i32, 0, 0, 0,
sc.extent.width as i32,
sc.extent.height as i32,
glow::COLOR_BUFFER_BIT,
glow::NEAREST,
)
};
let dc = unsafe { Gdi::GetDC(self.window) }; if dc.is_invalid() {
log::error!( "unable to get the device context from window: {}",
Error::from_win32()
); return Err(crate::SurfaceError::Other( "unable to get the device context from window",
));
} let dc = DeviceContextHandle {
device: dc,
window: self.window,
};
let format_desc = device.shared.describe_texture_format(config.format); let gl = &device.shared.context.lock_with_dc(dc.device).map_err(|e| {
log::error!("unable to make the OpenGL context current for surface: {e}",); crate::SurfaceError::Other("unable to make the OpenGL context current for surface")
})?;
// Setup presentation mode let extra = Wgl::load_with(|name| load_gl_func(name, None)); let extensions = get_extensions(&extra, dc.device); if !(extensions.contains("WGL_EXT_swap_control") && extra.SwapIntervalEXT.is_loaded()) {
log::error!("WGL_EXT_swap_control is unsupported"); return Err(crate::SurfaceError::Other( "WGL_EXT_swap_control is unsupported",
));
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.