mirror of
https://github.com/gfx-rs/wgpu.git
synced 2024-11-22 06:44:14 +00:00
[wgpu-hal] Upgrade glutin
to 0.31
(#6150)
* [wgpu-hal] Upgrade `glutin` to `0.31` `glutin 0.30` onwards completely refactored its internals to no longer be reliant on `winit`, as they (by default) have no direct relation except needing to perform _some_ operations (platform-specific) at strategic times in window creation and event loop handling. Most of that is handled by a new `glutin-winit` introp crate, while the core `glutin` crate now exclusively focuses on wrapping the various OpenGL context APIs (CGL, EGL, WGL, ...). This does result in a little more verbose handling to get the right `GLDisplay`, `GLConfig`, `GLContext` and `GLSurface`, but gives much more control and makes all intricacies more explicit. Most of the code was copied from `glutin 0.31`'s example crate, with the code for transparency support removed. Note that the example doesn't at all handle event loop events properly: resizes and redraws are not listened to, and mobile-specific surface events (`Resumed` and `Suspended`) are equally ignored. * [wgpu-hal] Implement proper `Surface` availability semantics in `raw-gles` example
This commit is contained in:
parent
a9047c2af5
commit
690a3fb3fb
@ -4,10 +4,10 @@ skip-tree = [
|
|||||||
# We never enable loom in any of our dependencies but it causes dupes
|
# We never enable loom in any of our dependencies but it causes dupes
|
||||||
{ name = "loom", version = "0.7.2" },
|
{ name = "loom", version = "0.7.2" },
|
||||||
{ name = "windows-sys", version = "0.45" },
|
{ name = "windows-sys", version = "0.45" },
|
||||||
{ name = "winit", version = "0.27" },
|
|
||||||
{ name = "winit", version = "0.29" },
|
{ name = "winit", version = "0.29" },
|
||||||
{ name = "rustc_version", version = "0.2.3" },
|
{ name = "rustc_version", version = "0.2.3" },
|
||||||
{ name = "sourcemap", version = "7.1.1" },
|
{ name = "sourcemap", version = "7.1.1" },
|
||||||
|
{ name = "miniz_oxide", version = "0.7.4" },
|
||||||
]
|
]
|
||||||
skip = [
|
skip = [
|
||||||
{ name = "hlsl-snapshots", version = "0.1.0" },
|
{ name = "hlsl-snapshots", version = "0.1.0" },
|
||||||
|
904
Cargo.lock
generated
904
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -153,7 +153,8 @@ windows-core = { version = "0.58", default-features = false }
|
|||||||
# Gles dependencies
|
# Gles dependencies
|
||||||
khronos-egl = "6"
|
khronos-egl = "6"
|
||||||
glow = "0.14.0"
|
glow = "0.14.0"
|
||||||
glutin = "0.29.1"
|
glutin = { version = "0.31", default-features = false }
|
||||||
|
glutin-winit = { version = "0.4", default-features = false }
|
||||||
glutin_wgl_sys = "0.6"
|
glutin_wgl_sys = "0.6"
|
||||||
|
|
||||||
# DX and GLES dependencies
|
# DX and GLES dependencies
|
||||||
|
@ -207,5 +207,8 @@ env_logger.workspace = true
|
|||||||
glam.workspace = true # for ray-traced-triangle example
|
glam.workspace = true # for ray-traced-triangle example
|
||||||
winit.workspace = true # for "halmark" example
|
winit.workspace = true # for "halmark" example
|
||||||
|
|
||||||
[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
|
[target.'cfg(not(any(target_arch = "wasm32", windows, target_os = "ios")))'.dev-dependencies]
|
||||||
glutin.workspace = true # for "gles" example
|
glutin-winit = { workspace = true, features = ["egl"] } # for "raw-gles" example
|
||||||
|
glutin = { workspace = true, features = ["egl"] } # for "raw-gles" example
|
||||||
|
rwh_05 = { version = "0.5", package = "raw-window-handle" } # temporary compatibility for glutin-winit in "raw-gles" example
|
||||||
|
winit = { workspace = true, features = ["rwh_05"] } # for "raw-gles" example
|
||||||
|
@ -10,61 +10,198 @@
|
|||||||
|
|
||||||
extern crate wgpu_hal as hal;
|
extern crate wgpu_hal as hal;
|
||||||
|
|
||||||
#[cfg(not(any(windows, target_arch = "wasm32")))]
|
#[cfg(not(any(windows, target_arch = "wasm32", target_os = "ios")))]
|
||||||
fn main() {
|
fn main() {
|
||||||
|
use std::{ffi::CString, num::NonZeroU32};
|
||||||
|
|
||||||
|
use glutin::{
|
||||||
|
config::GlConfig as _,
|
||||||
|
context::{NotCurrentGlContext as _, PossiblyCurrentGlContext as _},
|
||||||
|
display::{GetGlDisplay as _, GlDisplay as _},
|
||||||
|
surface::GlSurface as _,
|
||||||
|
};
|
||||||
|
use glutin_winit::GlWindow as _;
|
||||||
|
use rwh_05::HasRawWindowHandle as _;
|
||||||
|
|
||||||
env_logger::init();
|
env_logger::init();
|
||||||
println!("Initializing external GL context");
|
println!("Initializing external GL context");
|
||||||
|
|
||||||
let event_loop = glutin::event_loop::EventLoop::new();
|
let event_loop = winit::event_loop::EventLoop::new().unwrap();
|
||||||
let window_builder = glutin::window::WindowBuilder::new();
|
// Only Windows requires the window to be present before creating the display.
|
||||||
let gl_context = unsafe {
|
// Other platforms don't really need one.
|
||||||
glutin::ContextBuilder::new()
|
let window_builder = cfg!(windows).then(|| {
|
||||||
.with_gl(glutin::GlRequest::Specific(glutin::Api::OpenGlEs, (3, 0)))
|
winit::window::WindowBuilder::new()
|
||||||
.build_windowed(window_builder, &event_loop)
|
.with_title("WGPU raw GLES example (press Escape to exit)")
|
||||||
.unwrap()
|
|
||||||
.make_current()
|
|
||||||
.unwrap()
|
|
||||||
};
|
|
||||||
let inner_size = gl_context.window().inner_size();
|
|
||||||
|
|
||||||
println!("Hooking up to wgpu-hal");
|
|
||||||
let exposed = unsafe {
|
|
||||||
<hal::api::Gles as hal::Api>::Adapter::new_external(|name| {
|
|
||||||
gl_context.get_proc_address(name)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
.expect("GL adapter can't be initialized");
|
|
||||||
|
|
||||||
fill_screen(&exposed, inner_size.width, inner_size.height);
|
|
||||||
|
|
||||||
println!("Showing the window");
|
|
||||||
gl_context.swap_buffers().unwrap();
|
|
||||||
|
|
||||||
event_loop.run(move |event, _, control_flow| {
|
|
||||||
use glutin::{
|
|
||||||
event::{Event, KeyboardInput, VirtualKeyCode, WindowEvent},
|
|
||||||
event_loop::ControlFlow,
|
|
||||||
};
|
|
||||||
*control_flow = ControlFlow::Wait;
|
|
||||||
|
|
||||||
match event {
|
|
||||||
Event::LoopDestroyed => (),
|
|
||||||
Event::WindowEvent {
|
|
||||||
event:
|
|
||||||
WindowEvent::CloseRequested
|
|
||||||
| WindowEvent::KeyboardInput {
|
|
||||||
input:
|
|
||||||
KeyboardInput {
|
|
||||||
virtual_keycode: Some(VirtualKeyCode::Escape),
|
|
||||||
..
|
|
||||||
},
|
|
||||||
..
|
|
||||||
},
|
|
||||||
..
|
|
||||||
} => *control_flow = ControlFlow::Exit,
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// The template will match only the configurations supporting rendering
|
||||||
|
// to Windows.
|
||||||
|
let template = glutin::config::ConfigTemplateBuilder::new();
|
||||||
|
|
||||||
|
let display_builder = glutin_winit::DisplayBuilder::new().with_window_builder(window_builder);
|
||||||
|
|
||||||
|
// Find the config with the maximum number of samples, so our triangle will be
|
||||||
|
// smooth.
|
||||||
|
pub fn gl_config_picker(
|
||||||
|
configs: Box<dyn Iterator<Item = glutin::config::Config> + '_>,
|
||||||
|
) -> glutin::config::Config {
|
||||||
|
configs
|
||||||
|
.reduce(|accum, config| {
|
||||||
|
if config.num_samples() > accum.num_samples() {
|
||||||
|
config
|
||||||
|
} else {
|
||||||
|
accum
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.expect("Failed to find a matching config")
|
||||||
|
}
|
||||||
|
|
||||||
|
let (mut window, gl_config) = display_builder
|
||||||
|
.build(&event_loop, template, gl_config_picker)
|
||||||
|
.expect("Failed to build window and config from display");
|
||||||
|
|
||||||
|
println!("Picked a config with {} samples", gl_config.num_samples());
|
||||||
|
|
||||||
|
let raw_window_handle = window.as_ref().map(|window| window.raw_window_handle());
|
||||||
|
|
||||||
|
// XXX The display could be obtained from any object created by it, so we can
|
||||||
|
// query it from the config.
|
||||||
|
let gl_display = gl_config.display();
|
||||||
|
|
||||||
|
// Glutin tries to create an OpenGL context by default. Force it to use any version of GLES.
|
||||||
|
let context_attributes = glutin::context::ContextAttributesBuilder::new()
|
||||||
|
// WGPU expects GLES 3.0+.
|
||||||
|
.with_context_api(glutin::context::ContextApi::Gles(Some(Version::new(3, 0))))
|
||||||
|
.build(raw_window_handle);
|
||||||
|
|
||||||
|
let mut not_current_gl_context = Some(unsafe {
|
||||||
|
gl_display
|
||||||
|
.create_context(&gl_config, &context_attributes)
|
||||||
|
.expect("failed to create context")
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut state = None;
|
||||||
|
|
||||||
|
// Only needs to be loaded once
|
||||||
|
let mut exposed = None;
|
||||||
|
|
||||||
|
event_loop
|
||||||
|
.run(move |event, window_target| {
|
||||||
|
use winit::{
|
||||||
|
event::{Event, KeyEvent, WindowEvent},
|
||||||
|
event_loop::ControlFlow,
|
||||||
|
keyboard::{Key, NamedKey},
|
||||||
|
};
|
||||||
|
window_target.set_control_flow(ControlFlow::Wait);
|
||||||
|
|
||||||
|
match event {
|
||||||
|
// Event::LoopExiting => (),
|
||||||
|
Event::WindowEvent {
|
||||||
|
window_id: _,
|
||||||
|
event:
|
||||||
|
WindowEvent::CloseRequested
|
||||||
|
| WindowEvent::KeyboardInput {
|
||||||
|
event:
|
||||||
|
KeyEvent {
|
||||||
|
logical_key: Key::Named(NamedKey::Escape),
|
||||||
|
..
|
||||||
|
},
|
||||||
|
..
|
||||||
|
},
|
||||||
|
} => window_target.exit(),
|
||||||
|
Event::Resumed => {
|
||||||
|
let window = window.take().unwrap_or_else(|| {
|
||||||
|
let window_builder = winit::window::WindowBuilder::new()
|
||||||
|
.with_title("WGPU raw GLES example (press Escape to exit)");
|
||||||
|
glutin_winit::finalize_window(window_target, window_builder, &gl_config)
|
||||||
|
.unwrap()
|
||||||
|
});
|
||||||
|
|
||||||
|
let attrs = window.build_surface_attributes(Default::default());
|
||||||
|
let gl_surface = unsafe {
|
||||||
|
gl_config
|
||||||
|
.display()
|
||||||
|
.create_window_surface(&gl_config, &attrs)
|
||||||
|
.expect("Cannot create GL WindowSurface")
|
||||||
|
};
|
||||||
|
|
||||||
|
// Make it current.
|
||||||
|
let gl_context = not_current_gl_context
|
||||||
|
.take()
|
||||||
|
.unwrap()
|
||||||
|
.make_current(&gl_surface)
|
||||||
|
.expect("GL context cannot be made current with WindowSurface");
|
||||||
|
|
||||||
|
// The context needs to be current for the Renderer to set up shaders and
|
||||||
|
// buffers. It also performs function loading, which needs a current context on
|
||||||
|
// WGL.
|
||||||
|
println!("Hooking up to wgpu-hal");
|
||||||
|
exposed.get_or_insert_with(|| {
|
||||||
|
unsafe {
|
||||||
|
<hal::api::Gles as hal::Api>::Adapter::new_external(|name| {
|
||||||
|
// XXX: On WGL this should only be called after the context was made current
|
||||||
|
gl_config
|
||||||
|
.display()
|
||||||
|
.get_proc_address(&CString::new(name).expect(name))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
.expect("GL adapter can't be initialized")
|
||||||
|
});
|
||||||
|
|
||||||
|
assert!(state.replace((gl_context, gl_surface, window)).is_none());
|
||||||
|
}
|
||||||
|
Event::Suspended => {
|
||||||
|
// This event is only raised on Android, where the backing NativeWindow for a GL
|
||||||
|
// Surface can appear and disappear at any moment.
|
||||||
|
println!("Android window removed");
|
||||||
|
|
||||||
|
// Destroy the GL Surface and un-current the GL Context before ndk-glue releases
|
||||||
|
// the window back to the system.
|
||||||
|
let (gl_context, ..) = state.take().unwrap();
|
||||||
|
assert!(not_current_gl_context
|
||||||
|
.replace(gl_context.make_not_current().unwrap())
|
||||||
|
.is_none());
|
||||||
|
}
|
||||||
|
Event::WindowEvent {
|
||||||
|
window_id: _,
|
||||||
|
event: WindowEvent::Resized(size),
|
||||||
|
} => {
|
||||||
|
if size.width != 0 && size.height != 0 {
|
||||||
|
// Some platforms like EGL require resizing GL surface to update the size
|
||||||
|
// Notable platforms here are Wayland and macOS, other don't require it
|
||||||
|
// and the function is no-op, but it's wise to resize it for portability
|
||||||
|
// reasons.
|
||||||
|
if let Some((gl_context, gl_surface, _)) = &state {
|
||||||
|
gl_surface.resize(
|
||||||
|
gl_context,
|
||||||
|
NonZeroU32::new(size.width).unwrap(),
|
||||||
|
NonZeroU32::new(size.height).unwrap(),
|
||||||
|
);
|
||||||
|
// XXX: If there's a state for fill_screen(), this would need to be updated too.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Event::WindowEvent {
|
||||||
|
window_id: _,
|
||||||
|
event: WindowEvent::RedrawRequested,
|
||||||
|
} => {
|
||||||
|
if let (Some(exposed), Some((gl_context, gl_surface, window))) =
|
||||||
|
(&exposed, &state)
|
||||||
|
{
|
||||||
|
let inner_size = window.inner_size();
|
||||||
|
|
||||||
|
fill_screen(exposed, inner_size.width, inner_size.height);
|
||||||
|
|
||||||
|
println!("Showing the window");
|
||||||
|
gl_surface
|
||||||
|
.swap_buffers(gl_context)
|
||||||
|
.expect("Failed to swap buffers");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.expect("Couldn't run event loop");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "emscripten")]
|
#[cfg(target_os = "emscripten")]
|
||||||
@ -117,10 +254,20 @@ fn main() {
|
|||||||
fill_screen(&exposed, 640, 400);
|
fill_screen(&exposed, 640, 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(windows, all(target_arch = "wasm32", not(target_os = "emscripten"))))]
|
#[cfg(any(
|
||||||
fn main() {}
|
windows,
|
||||||
|
all(target_arch = "wasm32", not(target_os = "emscripten")),
|
||||||
|
target_os = "ios"
|
||||||
|
))]
|
||||||
|
fn main() {
|
||||||
|
eprintln!("This example is not supported on Windows and non-emscripten wasm32")
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(any(not(any(windows, target_arch = "wasm32")), target_os = "emscripten"))]
|
#[cfg(not(any(
|
||||||
|
windows,
|
||||||
|
all(target_arch = "wasm32", not(target_os = "emscripten")),
|
||||||
|
target_os = "ios"
|
||||||
|
)))]
|
||||||
fn fill_screen(exposed: &hal::ExposedAdapter<hal::api::Gles>, width: u32, height: u32) {
|
fn fill_screen(exposed: &hal::ExposedAdapter<hal::api::Gles>, width: u32, height: u32) {
|
||||||
use hal::{Adapter as _, CommandEncoder as _, Device as _, Queue as _};
|
use hal::{Adapter as _, CommandEncoder as _, Device as _, Queue as _};
|
||||||
|
|
||||||
|
@ -391,11 +391,11 @@ impl Surface {
|
|||||||
read.as_ref().map(|it| it.raw)
|
read.as_ref().map(|it| it.raw)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the present timing information which will be used for the next [presentation](crate::Queue::present) of this surface,
|
/// Set the present timing information which will be used for the next [presentation](crate::Queue::present()) of this surface,
|
||||||
/// using [VK_GOOGLE_display_timing].
|
/// using [VK_GOOGLE_display_timing].
|
||||||
///
|
///
|
||||||
/// This can be used to give an id to presentations, for future use of `VkPastPresentationTimingGOOGLE`.
|
/// This can be used to give an id to presentations, for future use of [`vk::PastPresentationTimingGOOGLE`].
|
||||||
/// Note that `wgpu-hal` does *not* provide a way to use that API - you should manually access this through `ash`.
|
/// Note that `wgpu-hal` does *not* provide a way to use that API - you should manually access this through [`ash`].
|
||||||
///
|
///
|
||||||
/// This can also be used to add a "not before" timestamp to the presentation.
|
/// This can also be used to add a "not before" timestamp to the presentation.
|
||||||
///
|
///
|
||||||
@ -1213,11 +1213,11 @@ impl crate::Queue for Queue {
|
|||||||
ssc.device
|
ssc.device
|
||||||
.features
|
.features
|
||||||
.contains(wgt::Features::VULKAN_GOOGLE_DISPLAY_TIMING),
|
.contains(wgt::Features::VULKAN_GOOGLE_DISPLAY_TIMING),
|
||||||
"`next_present_times` should only be set if `VULKAN_GOOGLE_DISPLAY_TIMING` is enabled"
|
"`next_present_time` should only be set if `VULKAN_GOOGLE_DISPLAY_TIMING` is enabled"
|
||||||
);
|
);
|
||||||
present_times = [present_time];
|
present_times = [present_time];
|
||||||
display_timing = vk::PresentTimesInfoGOOGLE::default().times(&present_times);
|
display_timing = vk::PresentTimesInfoGOOGLE::default().times(&present_times);
|
||||||
// SAFETY: We know that VK_GOOGLE_display_timing is present because of the safety contract on `next_present_times`.
|
// SAFETY: We know that VK_GOOGLE_display_timing is present because of the safety contract on `next_present_time`.
|
||||||
vk_info.push_next(&mut display_timing)
|
vk_info.push_next(&mut display_timing)
|
||||||
} else {
|
} else {
|
||||||
vk_info
|
vk_info
|
||||||
|
@ -958,7 +958,7 @@ bitflags::bitflags! {
|
|||||||
///
|
///
|
||||||
/// This feature does not have a `wgpu`-level API, and so users of wgpu wishing
|
/// This feature does not have a `wgpu`-level API, and so users of wgpu wishing
|
||||||
/// to use this functionality must access it using various `as_hal` functions,
|
/// to use this functionality must access it using various `as_hal` functions,
|
||||||
/// primarily [`Surface::as_hal`], to then use.
|
/// primarily [`Surface::as_hal()`], to then use.
|
||||||
///
|
///
|
||||||
/// Supported platforms:
|
/// Supported platforms:
|
||||||
/// - Vulkan (with [VK_GOOGLE_display_timing])
|
/// - Vulkan (with [VK_GOOGLE_display_timing])
|
||||||
@ -966,7 +966,7 @@ bitflags::bitflags! {
|
|||||||
/// This is a native only feature.
|
/// This is a native only feature.
|
||||||
///
|
///
|
||||||
/// [VK_GOOGLE_display_timing]: https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_GOOGLE_display_timing.html
|
/// [VK_GOOGLE_display_timing]: https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_GOOGLE_display_timing.html
|
||||||
/// [`Surface::as_hal`]: https://docs.rs/wgpu/latest/wgpu/struct.Surface.html#method.as_hal
|
/// [`Surface::as_hal()`]: https://docs.rs/wgpu/latest/wgpu/struct.Surface.html#method.as_hal
|
||||||
const VULKAN_GOOGLE_DISPLAY_TIMING = 1 << 62;
|
const VULKAN_GOOGLE_DISPLAY_TIMING = 1 << 62;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user