Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ Bottom level categories:

- Expanded documentation of `QuerySet`, `QueryType`, and `resolve_query_set()` describing how to use queries. By @kpreid in [#8776](https://github.com/gfx-rs/wgpu/pull/8776).

### Hacks (for lack of a better description)

- `DisplayHandle` is now optional in surface creation if it was passed to `InstanceDescriptor::display`. By @MarijnS95 in [#8782](https://github.com/gfx-rs/wgpu/pull/8782)

## v28.0.0 (2025-12-17)

### Major Changes
Expand Down
3 changes: 2 additions & 1 deletion deno_webgpu/byow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,8 @@ impl UnsafeWindowSurface {
// SAFETY: see above comment
let id = unsafe {
instance
.instance_create_surface(display_handle, win_handle, None)
// TODO: May need to be None in some cases
.instance_create_surface(Some(display_handle), win_handle, None)
.map_err(ByowError::CreateSurface)?
};

Expand Down
3 changes: 3 additions & 0 deletions examples/standalone/02_hello_window/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ impl State {

let size = window.inner_size();

// BREAKING CHANGE: The Into<SurfaceTarget> here will now only
// pass the window handle, and fails if the "optional" display
// handle wasn't passed above to with_display_handle() too
let surface = instance.create_surface(window.clone()).unwrap();
let cap = surface.get_capabilities(&adapter);
let surface_format = cap.formats[0];
Expand Down
9 changes: 2 additions & 7 deletions player/src/bin/play.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,13 +79,8 @@ fn main() {
let instance = wgc::instance::Instance::new("player", instance_desc, None);

#[cfg(feature = "winit")]
let surface = unsafe {
instance.create_surface(
window.display_handle().unwrap().into(),
window.window_handle().unwrap().into(),
)
}
.unwrap();
let surface =
unsafe { instance.create_surface(None, window.window_handle().unwrap().into()) }.unwrap();

let (backends, device_desc) =
match actions.pop_if(|action| matches!(action, trace::Action::Init { .. })) {
Expand Down
32 changes: 21 additions & 11 deletions wgpu-core/src/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -227,26 +227,33 @@ impl Instance {
///
/// # Safety
///
/// - `display_handle` must be a valid object to create a surface upon.
/// - `display_handle` must be a valid object to create a surface upon,
/// falls back to the instance display handle otherwise.
/// - `window_handle` must remain valid as long as the returned
/// [`SurfaceId`] is being used.
pub unsafe fn create_surface(
&self,
display_handle: raw_window_handle::RawDisplayHandle,
display_handle: Option<raw_window_handle::RawDisplayHandle>,
window_handle: raw_window_handle::RawWindowHandle,
) -> Result<Surface, CreateSurfaceError> {
profiling::scope!("Instance::create_surface");

if let Some(instance_display_handle) = &self.display {
if instance_display_handle
.display_handle()
let instance_display_handle = self.display.as_ref().map(|d| {
d.display_handle()
.expect("Implementation did not provide a DisplayHandle")
.as_raw()
!= display_handle
{
return Err(CreateSurfaceError::MismatchingDisplayHandle);
});
let display_handle = match (instance_display_handle, display_handle) {
(Some(a), Some(b)) => {
if a != b {
return Err(CreateSurfaceError::MismatchingDisplayHandle);
}
a
}
}
(Some(hnd), None) => hnd,
(None, Some(hnd)) => hnd,
(None, None) => return Err(CreateSurfaceError::MissingDisplayHandle),
};

let mut errors = HashMap::default();
let mut surface_per_backend = HashMap::default();
Expand Down Expand Up @@ -932,6 +939,8 @@ pub enum CreateSurfaceError {
FailedToCreateSurfaceForAnyBackend(HashMap<Backend, hal::InstanceError>),
#[error("The display handle used to create this Instance does not match the one used to create a surface on it")]
MismatchingDisplayHandle,
#[error("Neither the `Instance` nor `create_surface()` parameters received a `DisplayHandle`")]
MissingDisplayHandle,
}

impl Global {
Expand All @@ -949,12 +958,13 @@ impl Global {
///
/// # Safety
///
/// - `display_handle` must be a valid object to create a surface upon.
/// - `display_handle` must be a valid object to create a surface upon,
/// falls back to the instance display handle otherwise.
/// - `window_handle` must remain valid as long as the returned
/// [`SurfaceId`] is being used.
pub unsafe fn instance_create_surface(
&self,
display_handle: raw_window_handle::RawDisplayHandle,
display_handle: Option<raw_window_handle::RawDisplayHandle>,
window_handle: raw_window_handle::RawWindowHandle,
id_in: Option<SurfaceId>,
) -> Result<SurfaceId, CreateSurfaceError> {
Expand Down
4 changes: 4 additions & 0 deletions wgpu-hal/src/dx12/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,10 @@ impl crate::Instance for super::Instance {
_display_handle: raw_window_handle::RawDisplayHandle,
window_handle: raw_window_handle::RawWindowHandle,
) -> Result<super::Surface, crate::InstanceError> {
assert!(matches!(
_display_handle,
raw_window_handle::RawDisplayHandle::Windows(_)
));
match window_handle {
raw_window_handle::RawWindowHandle::Win32(handle) => {
// https://github.com/rust-windowing/raw-window-handle/issues/171
Expand Down
1 change: 0 additions & 1 deletion wgpu-hal/src/gles/egl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -888,7 +888,6 @@ impl crate::Instance for Instance {
})
}

#[cfg_attr(target_os = "macos", allow(unused, unused_mut, unreachable_code))]
unsafe fn create_surface(
&self,
display_handle: raw_window_handle::RawDisplayHandle,
Expand Down
2 changes: 1 addition & 1 deletion wgpu-hal/src/gles/wgl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -563,12 +563,12 @@ impl crate::Instance for Instance {
})
}

#[cfg_attr(target_os = "macos", allow(unused, unused_mut, unreachable_code))]
unsafe fn create_surface(
&self,
_display_handle: RawDisplayHandle,
window_handle: RawWindowHandle,
) -> Result<Surface, crate::InstanceError> {
assert!(matches!(_display_handle, RawDisplayHandle::Windows(_)));
let window = if let RawWindowHandle::Win32(handle) = window_handle {
handle
} else {
Expand Down
22 changes: 12 additions & 10 deletions wgpu-hal/src/metal/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,20 +122,22 @@ impl crate::Instance for Instance {

unsafe fn create_surface(
&self,
_display_handle: raw_window_handle::RawDisplayHandle,
display_handle: raw_window_handle::RawDisplayHandle,
window_handle: raw_window_handle::RawWindowHandle,
) -> Result<Surface, crate::InstanceError> {
match window_handle {
match (display_handle, window_handle) {
#[cfg(any(target_os = "ios", target_os = "visionos"))]
raw_window_handle::RawWindowHandle::UiKit(handle) => {
Ok(unsafe { Surface::from_view(handle.ui_view.cast()) })
}
(
raw_window_handle::RawDisplayHandle::UiKit(_),
raw_window_handle::RawWindowHandle::UiKit(handle),
) => Ok(unsafe { Surface::from_view(handle.ui_view.cast()) }),
#[cfg(target_os = "macos")]
raw_window_handle::RawWindowHandle::AppKit(handle) => {
Ok(unsafe { Surface::from_view(handle.ns_view.cast()) })
}
_ => Err(crate::InstanceError::new(format!(
"window handle {window_handle:?} is not a Metal-compatible handle"
(
raw_window_handle::RawDisplayHandle::AppKit(_),
raw_window_handle::RawWindowHandle::AppKit(handle),
) => Ok(unsafe { Surface::from_view(handle.ns_view.cast()) }),
x => Err(crate::InstanceError::new(format!(
"window handle {x:?} is not a Metal-compatible handle"
))),
}
}
Expand Down
3 changes: 0 additions & 3 deletions wgpu-types/src/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,6 @@ pub struct InstanceDescriptor {
/// the `EventLoop`) here.
///
/// [`OwnedDisplayHandle`]: https://docs.rs/winit/latest/winit/event_loop/struct.OwnedDisplayHandle.html
// FUTURE: The RawDisplayHandle trait can/should be removed entirely from create_display()? At
// least `trait WindowHandle: HasWindowHandle + HasDisplayHandle` should really be removed as
// it's impractical and not implementable everywhere.
pub display: Option<alloc::boxed::Box<dyn WgpuHasDisplayHandle>>,
}

Expand Down
13 changes: 12 additions & 1 deletion wgpu/src/api/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,10 +176,19 @@ impl Instance {
/// Internally, this creates surfaces for all backends that are enabled for this instance.
///
/// See [`SurfaceTarget`] for what targets are supported.
/// See [`Instance::create_surface_unsafe`] for surface creation with unsafe target variants.
/// See [`Instance::create_surface_unsafe()`] for surface creation with unsafe target variants.
///
/// Most commonly used are window handles (or provider of windows handles)
/// which can be passed directly as they're automatically converted to [`SurfaceTarget`].
///
/// # Warning
///
/// This function does _not_ consume a [`raw_window_handle::HasDisplayHandle`] from your
/// `target` (no longer a trait requirement for [`WindowHandle`]) and instead expects
/// a display handle in [`InstanceDescriptor::display`].
///
/// If that cannot be provided, call [`Instance::create_surface_unsafe()`] with a
/// [`SurfaceTargetUnsafe::from_display_and_window()`].
pub fn create_surface<'window>(
&self,
target: impl Into<SurfaceTarget<'window>>,
Expand All @@ -191,6 +200,8 @@ impl Instance {
let mut surface = match target {
SurfaceTarget::Window(window) => unsafe {
let surface = self.create_surface_unsafe(
// TODO: Could also call from_display_and_window depending on the argument;
// currently this is a hidden breaking change.
SurfaceTargetUnsafe::from_window(&window).map_err(|e| CreateSurfaceError {
inner: CreateSurfaceErrorKind::RawHandle(e),
})?,
Expand Down
46 changes: 36 additions & 10 deletions wgpu/src/api/surface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,10 +230,10 @@ static_assertions::assert_impl_all!(Surface<'_>: Send, Sync);

crate::cmp::impl_eq_ord_hash_proxy!(Surface<'_> => .inner);

/// Super trait for window handles as used in [`SurfaceTarget`].
pub trait WindowHandle: HasWindowHandle + HasDisplayHandle + WasmNotSendSync {}
/// [`Send`]/[`Sync`] blanket trait for [`HasWindowHandle`] used in [`SurfaceTarget`].
pub trait WindowHandle: HasWindowHandle + WasmNotSendSync {}

impl<T> WindowHandle for T where T: HasWindowHandle + HasDisplayHandle + WasmNotSendSync {}
impl<T: HasWindowHandle + WasmNotSendSync> WindowHandle for T {}

/// The window/canvas/surface/swap-chain/etc. a surface is attached to, for use with safe surface creation.
///
Expand All @@ -259,6 +259,9 @@ pub enum SurfaceTarget<'window> {
/// - On macOS/Metal: will panic if not called on the main thread.
/// - On web: will panic if the `raw_window_handle` does not properly refer to a
/// canvas element.
// TODO: Certain backends (Vulkan) have the window/display extensions and handles completely
// separate from the rendering instance, and should hence support creating surfaces
// for different display handles/conncetions.
Window(Box<dyn WindowHandle + 'window>),

/// Surface from a `web_sys::HtmlCanvasElement`.
Expand Down Expand Up @@ -309,16 +312,19 @@ pub enum SurfaceTargetUnsafe {
/// If the specified display and window handle are not supported by any of the backends, then the surface
/// will not be supported by any adapters.
///
/// If the `raw_display_handle` is not [`None`] here and was not [`None`] in
/// [`wgt::InstanceDescriptor::display`], their values _must_ be identical.
///
/// # Safety
///
/// - `raw_window_handle` & `raw_display_handle` must be valid objects to create a surface upon.
/// - `raw_window_handle` & `raw_display_handle` must remain valid until after the returned
/// [`Surface`] is dropped.
RawHandle {
/// Raw display handle, underlying display must outlive the surface created from this.
raw_display_handle: raw_window_handle::RawDisplayHandle,
raw_display_handle: Option<raw_window_handle::RawDisplayHandle>,

/// Raw display handle, underlying window must outlive the surface created from this.
/// Raw window handle, underlying window must outlive the surface created from this.
raw_window_handle: raw_window_handle::RawWindowHandle,
},

Expand Down Expand Up @@ -384,18 +390,38 @@ pub enum SurfaceTargetUnsafe {
}

impl SurfaceTargetUnsafe {
/// Creates a [`SurfaceTargetUnsafe::RawHandle`] from a display and window.
///
/// The `display` is optional and may be omitted if it was also passed to
/// [`wgt::InstanceDescriptor::display`]. If passed to both it must (currently) be identical.
///
/// # Safety
///
/// - `display` must outlive the resulting surface target
/// (and subsequently the surface created for this target).
/// - `window` must outlive the resulting surface target
/// (and subsequently the surface created for this target).
pub unsafe fn from_display_and_window(
display: &impl HasDisplayHandle,
window: &impl HasWindowHandle,
) -> Result<Self, raw_window_handle::HandleError> {
Ok(Self::RawHandle {
raw_display_handle: Some(display.display_handle()?.as_raw()),
raw_window_handle: window.window_handle()?.as_raw(),
})
}

/// Creates a [`SurfaceTargetUnsafe::RawHandle`] from a window.
///
/// # Safety
///
/// - `window` must outlive the resulting surface target
/// (and subsequently the surface created for this target).
pub unsafe fn from_window<T>(window: &T) -> Result<Self, raw_window_handle::HandleError>
where
T: HasDisplayHandle + HasWindowHandle,
{
pub unsafe fn from_window(
window: &impl HasWindowHandle,
) -> Result<Self, raw_window_handle::HandleError> {
Ok(Self::RawHandle {
raw_display_handle: window.display_handle()?.as_raw(),
raw_display_handle: None,
raw_window_handle: window.window_handle()?.as_raw(),
})
}
Expand Down
Loading