-
Notifications
You must be signed in to change notification settings - Fork 90
Introduce a display subsystem for GPU output #319
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
38c4a1a
to
7f463ef
Compare
This is similar concept to the mpsc::channel but uses an EventFd to notify the reader when data is availible. This seems like it could be generally useful, when we need to use Epoll to also wait for other file descriptor events. This is just a simple implementation using EventFd and could maybe be optimized. (maybe even using a unix pipe directly) Signed-off-by: Matej Hrica <[email protected]>
We wrongly expected an extra virtio_gpu_ctrl_hdr for VIRTIO_GPU_CMD_GET_DISPLAY_INFO The command doesn't have any body and we have already read the ctrl_header. Signed-off-by: Matej Hrica <[email protected]>
Introduce a DisplayBackend trait and a DisplayBackendNoop implementation. Hook up the gpu device to use the display backend. For now this only hooks up the simplest and slowest option of display output - the output is copied to the CPU using the transfer_read operation and passed to the display backend. The DisplayBackendNoop doesn't provide any displays, so this doesn't display anything yet. Introduce a new public API function krun_set_display() for configuring VM displays. The virtio-gpu code is based on rust-vmm/vhost-device-gpu. Signed-off-by: Matej Hrica <[email protected]>
Add a DisplayBackend implementation using GTK. This also introduces a new public API function krun_set_display_backend_gtk() for enabling the gtk display backend. Signed-off-by: Matej Hrica <[email protected]>
The --display option can be provided multiple times. Specyfing at least one display automatically enables the GTK display backend. Signed-off-by: Matej Hrica <[email protected]>
On macOS I'm getting this:
I haven't investigated further yet. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't speak to the logic of the subsystem since I'm not familiar with what's best practices here, but the code itself LGTM.
Could use a rebase, though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
some comments here and there
|
||
fn build_overlay(window: &Window) -> Overlay { | ||
let overlay_bar = HeaderBar::builder() | ||
.visible(true) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
.visible(true) |
all widgets are visible by default
|
||
let overlay_unfullscreen_btn = Button::builder() | ||
.tooltip_text("Exit fullscreen mode") | ||
.icon_name("view-restore") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
where this icon comes from?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I looked at this list, and picked icon names: https://specifications.freedesktop.org/icon-naming-spec/latest/#names
(but looking at it again, it seems this is from 2007, so I'm not sure if I should be rely on this 😄 )
Is it a bad assumption, that these icons will always exist?
|
||
pub fn gtk_display_main_loop(rx: PollableChannelReciever<DisplayEvent>, displays: DisplayInfoList) { | ||
gtk4::init().expect("Failed to initialize GTK"); | ||
let main_loop = glib::MainLoop::new(None, false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
any reason for running a manual main loop instead of using gtk::Application?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, originally I also wanted to also use a custom MainContext
and only use gtk from this worker thread. This is technically a library and theoretically other glib/gtk things could be running (but more of a theoretical than a real problem I guess). I feel like the Rust bindings always want you to use the global context anyway (for example the source::unix_fd_add_local
), or maybe I just don't understand, how to use it properly. I'm not really familiar with glib/gtk C API anyway.
But it looks like this might be a moot point anyway, because on macOS it seems gtk has to be initialized on the main thread anyway...
@@ -29,6 +30,8 @@ virtio-bindings = "0.2.0" | |||
vm-memory = { version = ">=0.13", features = ["backend-mmap"] } | |||
zerocopy = { version = "0.6.3", optional = true } | |||
zerocopy-derive = { version = "0.6.3", optional = true } | |||
gtk4 = { version = "0.9.6", features = ["v4_14"], optional = true } | |||
glib = { version = "0.20.9", optional = true } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it is already pulled by gtk4, no need for a specific dep.
height: i32, | ||
format: gdk::MemoryFormat, | ||
) -> Self { | ||
let header_bar = HeaderBar::builder().build(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let header_bar = HeaderBar::builder().build(); | |
let header_bar = HeaderBar::default(); |
is enough i guess
// Enforce a minimum window size: | ||
.height_request(64) | ||
.titlebar(&header_bar) | ||
.visible(true) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same as other widgets
.build(); | ||
|
||
let actions = SimpleActionGroup::new(); | ||
actions.add_action_entries([ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why use a separate action group ? i would recommend using a gtk::Window which is an gio::ActionMap already
]); | ||
window.insert_action_group("scanout", Some(&actions)); | ||
|
||
let fullscreen_btn = gtk4::Button::builder() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
weird that some types are imported, some are not ;-)
|
||
// Unset the width/height after the window is created to allow resizing the window to | ||
// be smaller | ||
picture.set_size_request(-1, -1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that looks like a hack.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes it is. Any idea how to do this properly, though?
I wanted to set the default window width/height, I see Window.set_default_size
exits, but what to set the dimensions to? The window height should be the height of the image + height of the header bar, which I don't know how to obtain, and even if I did calculating this manually feels even more hacky and error prone.
I don't see a way to set the default, but not minimal dimensions of the Picture widget (which would automatically create the window with correct size).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You will have to create a custom paintable, see https://github.com/gtk-rs/gtk4-rs/tree/main/examples/custom_paintable or other custom paintables examples.
use gtk4::{ | ||
gdk, | ||
gio::{ActionEntry, Cancellable, SimpleActionGroup}, | ||
prelude::{ActionMapExtManual, GtkWindowExt, WidgetExt}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
prelude::{ActionMapExtManual, GtkWindowExt, WidgetExt}, | |
prelude::*, |
as stuff can move from ExtManual to Ext with time, so avoid potential code fixing with version bumps
fn build_kill_vm_btn() -> Button { | ||
let kill_vm_btn = Button::builder() | ||
.label("Kill VM") | ||
.action_name("scanout.kill") | ||
.build(); | ||
kill_vm_btn.set_css_classes(&["destructive-action"]); | ||
kill_vm_btn | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
seems too much to do this in a separate function. Also use add_css_class
, the function you are using removes all the pre-existing CSS classes in the widget if any.
This PR adds initial support for the more traditional display output from the VM by basically having virtual displays (until now we only had cross-domain/wayland).
This implementes 2 display backends:
DisplayBackendNoop
- used by default, pretty much returns an error for every operationDisplayBackendGtk
- implementation using GTK (works on Linux, to be tested on macOS)This introduces 2 new public API function:
int32_t krun_set_display_backend_gtk(uint32_t ctx_id)
int32_t krun_set_display(uint32_t ctx_id, uint32_t display_id, uint32_t width, uint32_t height)
Current limitations to be addressed by future PRs:
You can test this using the newly added
--display
option inchroot_vm
and running something graphical in the guest - the simplest option is probablykmscube
:$ ./chroot_vm --display=0:1920:1080 rootfs_fedora kmscube