Skip to content

Commit 5b1e433

Browse files
committed
wayland: add and implement EventLoopExtDrm
This extension trait allows general-purpose applications to query the preferred GPU device so that they do not engage the dedicated GPU unnecessarily. Without such a hint, applications are likely to choose the first available device which will always be the dedicated GPU which is undesirable on mobile devices. Similarly, on desktop dual-GPU systems where the integrated GPU is otherwise unused, it is preferred for all applications to use the dedicated GPU.
1 parent eb66c25 commit 5b1e433

File tree

8 files changed

+131
-0
lines changed

8 files changed

+131
-0
lines changed

winit-wayland/src/event_loop/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use std::time::{Duration, Instant};
1212

1313
use calloop::ping::Ping;
1414
use dpi::LogicalSize;
15+
use libc::dev_t;
1516
use rustix::event::{PollFd, PollFlags};
1617
use rustix::pipe::{self, PipeFlags};
1718
use sctk::reexports::calloop_wayland_source::WaylandSource;
@@ -566,6 +567,10 @@ impl EventLoop {
566567
fn exit_code(&self) -> Option<i32> {
567568
self.active_event_loop.exit_code()
568569
}
570+
571+
pub fn main_drm_device(&self) -> Option<dev_t> {
572+
self.active_event_loop.state.borrow().linux_dmabuf_manager.as_ref()?.device()
573+
}
569574
}
570575

571576
impl AsFd for EventLoop {

winit-wayland/src/state.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ use crate::types::wp_fractional_scaling::FractionalScalingManager;
3333
use crate::types::wp_viewporter::ViewporterState;
3434
use crate::types::xdg_activation::XdgActivationState;
3535
use crate::types::xdg_toplevel_icon_manager::XdgToplevelIconManagerState;
36+
use crate::types::zwp_linux_dmabuf::LinuxDmabufManager;
3637
use crate::window::{WindowRequests, WindowState};
3738
use crate::WindowId;
3839

@@ -112,6 +113,9 @@ pub struct WinitState {
112113
/// KWin blur manager.
113114
pub kwin_blur_manager: Option<KWinBlurManager>,
114115

116+
/// Dmabuf manager.
117+
pub linux_dmabuf_manager: Option<LinuxDmabufManager>,
118+
115119
/// Loop handle to re-register event sources, such as keyboard repeat.
116120
pub loop_handle: LoopHandle<'static, Self>,
117121

@@ -161,6 +165,8 @@ impl WinitState {
161165
(None, None)
162166
};
163167

168+
let linux_dmabuf_manager = LinuxDmabufManager::new(globals, queue_handle).ok();
169+
164170
let shm = Shm::bind(globals, queue_handle).map_err(|err| os_error!(err))?;
165171
let image_pool = Arc::new(Mutex::new(SlotPool::new(2, &shm).unwrap()));
166172

@@ -186,6 +192,7 @@ impl WinitState {
186192
viewporter_state,
187193
fractional_scaling_manager,
188194
kwin_blur_manager: KWinBlurManager::new(globals, queue_handle).ok(),
195+
linux_dmabuf_manager,
189196

190197
seats,
191198
text_input_state: TextInputState::new(globals, queue_handle).ok(),

winit-wayland/src/types/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ pub mod wp_fractional_scaling;
66
pub mod wp_viewporter;
77
pub mod xdg_activation;
88
pub mod xdg_toplevel_icon_manager;
9+
pub mod zwp_linux_dmabuf;
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
use libc::dev_t;
2+
use sctk::globals::GlobalData;
3+
use wayland_client::globals::{BindError, GlobalList};
4+
use wayland_client::{delegate_dispatch, Connection, Dispatch, QueueHandle};
5+
use wayland_protocols::wp::linux_dmabuf::zv1::client::zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1;
6+
use wayland_protocols::wp::linux_dmabuf::zv1::client::zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1;
7+
use wayland_protocols::wp::linux_dmabuf::zv1::client::{
8+
zwp_linux_dmabuf_feedback_v1, zwp_linux_dmabuf_v1,
9+
};
10+
11+
use crate::state::WinitState;
12+
13+
#[derive(Debug)]
14+
pub struct LinuxDmabufManager {
15+
_manager: ZwpLinuxDmabufV1,
16+
feedback: LinuxDmabufFeedback,
17+
}
18+
19+
#[derive(Debug)]
20+
pub struct LinuxDmabufFeedback {
21+
_feedback: ZwpLinuxDmabufFeedbackV1,
22+
device: Option<dev_t>,
23+
pending_device: Option<dev_t>,
24+
}
25+
26+
impl LinuxDmabufManager {
27+
pub fn new(
28+
globals: &GlobalList,
29+
queue_handle: &QueueHandle<WinitState>,
30+
) -> Result<Self, BindError> {
31+
let manager: ZwpLinuxDmabufV1 = globals.bind(queue_handle, 4..=5, GlobalData)?;
32+
let feedback = manager.get_default_feedback(queue_handle, GlobalData);
33+
let feedback =
34+
LinuxDmabufFeedback { _feedback: feedback, device: None, pending_device: None };
35+
Ok(Self { _manager: manager, feedback })
36+
}
37+
38+
pub fn device(&self) -> Option<dev_t> {
39+
self.feedback.device
40+
}
41+
}
42+
43+
impl Dispatch<ZwpLinuxDmabufV1, GlobalData, WinitState> for LinuxDmabufManager {
44+
fn event(
45+
_state: &mut WinitState,
46+
_proxy: &ZwpLinuxDmabufV1,
47+
_event: zwp_linux_dmabuf_v1::Event,
48+
_data: &GlobalData,
49+
_conn: &Connection,
50+
_qhandle: &QueueHandle<WinitState>,
51+
) {
52+
// nothing
53+
}
54+
}
55+
56+
impl Dispatch<ZwpLinuxDmabufFeedbackV1, GlobalData, WinitState> for LinuxDmabufManager {
57+
fn event(
58+
state: &mut WinitState,
59+
_proxy: &ZwpLinuxDmabufFeedbackV1,
60+
event: zwp_linux_dmabuf_feedback_v1::Event,
61+
_data: &GlobalData,
62+
_conn: &Connection,
63+
_qhandle: &QueueHandle<WinitState>,
64+
) {
65+
use zwp_linux_dmabuf_feedback_v1::Event;
66+
match event {
67+
Event::Done => {
68+
let manager = state.linux_dmabuf_manager.as_mut().unwrap();
69+
if let Some(device) = manager.feedback.pending_device.take() {
70+
manager.feedback.device = Some(device);
71+
}
72+
},
73+
Event::MainDevice { device } => {
74+
let dev = dev_t::from_ne_bytes(device.try_into().unwrap());
75+
state.linux_dmabuf_manager.as_mut().unwrap().feedback.pending_device = Some(dev);
76+
},
77+
_ => {},
78+
}
79+
}
80+
}
81+
82+
delegate_dispatch!(WinitState: [ZwpLinuxDmabufV1: GlobalData] => LinuxDmabufManager);
83+
delegate_dispatch!(WinitState: [ZwpLinuxDmabufFeedbackV1: GlobalData] => LinuxDmabufManager);

winit/src/changelog/unreleased.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ changelog entry.
8383
- Each platform now has corresponding `WindowAttributes` struct instead of trait extension.
8484
- On Wayland, added implementation for `Window::set_window_icon`
8585
- Add `Window::request_ime_update` to atomically apply set of IME changes.
86+
- Add `EventLoopExtDrm` to query the main GPU device on multi-GPU systems on
87+
Linux.
8688

8789
### Changed
8890

winit/src/platform/drm.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
use libc::dev_t;
2+
3+
use crate::event_loop::EventLoop;
4+
5+
/// Additional methods on [`EventLoop`] that are specific to platforms using the Linux
6+
/// Direct Rendering Manager (DRM).
7+
pub trait EventLoopExtDrm {
8+
/// Returns the device that the system prefers to use.
9+
///
10+
/// The EGL EGL_EXT_device_drm and Vulkan VK_EXT_physical_device_drm extensions can be
11+
/// used to select a matching device for accelerated rendering.
12+
///
13+
/// This function returns `None` if the device is unknown.
14+
fn main_drm_device(&self) -> Option<dev_t>;
15+
}
16+
17+
impl EventLoopExtDrm for EventLoop {
18+
fn main_drm_device(&self) -> Option<dev_t> {
19+
self.event_loop.main_drm_device()
20+
}
21+
}

winit/src/platform/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ pub use winit_web as web;
3737
pub use winit_win32 as windows;
3838
#[cfg(x11_platform)]
3939
pub use winit_x11 as x11;
40+
#[cfg(any(x11_platform, wayland_platform))]
41+
pub mod drm;
4042

4143
#[cfg(any(windows_platform, macos_platform, x11_platform, wayland_platform, docsrs))]
4244
pub mod scancode;

winit/src/platform_impl/linux/mod.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use std::env;
77
use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd};
88
use std::time::Duration;
99

10+
use libc::dev_t;
1011
pub(crate) use winit_common::xkb::{physicalkey_to_scancode, scancode_to_physicalkey};
1112
use winit_core::application::ApplicationHandler;
1213
use winit_core::error::{EventLoopError, NotSupportedError};
@@ -169,6 +170,15 @@ impl EventLoop {
169170
pub fn window_target(&self) -> &dyn ActiveEventLoop {
170171
x11_or_wayland!(match self; EventLoop(evlp) => evlp.window_target())
171172
}
173+
174+
pub fn main_drm_device(&self) -> Option<dev_t> {
175+
match self {
176+
#[cfg(wayland_platform)]
177+
EventLoop::Wayland(evlp) => evlp.main_drm_device(),
178+
#[cfg(x11_platform)]
179+
EventLoop::X(_) => None,
180+
}
181+
}
172182
}
173183

174184
impl AsFd for EventLoop {

0 commit comments

Comments
 (0)