summary refs log tree commit diff
path: root/devices/src/virtio/gpu/mod.rs
diff options
context:
space:
mode:
authorZach Reizner <zachr@google.com>2019-04-16 15:09:20 -0700
committerCommit Bot <commit-bot@chromium.org>2019-07-25 22:15:48 +0000
commitf5285c647acacb4f25ef8cf9334254b976e71686 (patch)
treefcc238ec97736727a9c18b3b9de29be3dce3983e /devices/src/virtio/gpu/mod.rs
parentb2110bef59d72529d99c722df9b3e9a1d705e6f4 (diff)
downloadcrosvm-f5285c647acacb4f25ef8cf9334254b976e71686.tar
crosvm-f5285c647acacb4f25ef8cf9334254b976e71686.tar.gz
crosvm-f5285c647acacb4f25ef8cf9334254b976e71686.tar.bz2
crosvm-f5285c647acacb4f25ef8cf9334254b976e71686.tar.lz
crosvm-f5285c647acacb4f25ef8cf9334254b976e71686.tar.xz
crosvm-f5285c647acacb4f25ef8cf9334254b976e71686.tar.zst
crosvm-f5285c647acacb4f25ef8cf9334254b976e71686.zip
gpu_display: add X11 backend
This change adds an X11 backend to the gpu_display crate. With this
addition, the virtio-gpu device can display to traditional linux
desktops that only have X11 output.

Change-Id: I86c80cac91ca5bdc97588194a44040273ae69385
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/1591572
Reviewed-by: Stéphane Marchesin <marcheu@chromium.org>
Commit-Queue: Zach Reizner <zachr@chromium.org>
Tested-by: Zach Reizner <zachr@chromium.org>
Tested-by: kokoro <noreply+kokoro@google.com>
Auto-Submit: Zach Reizner <zachr@chromium.org>
Diffstat (limited to 'devices/src/virtio/gpu/mod.rs')
-rw-r--r--devices/src/virtio/gpu/mod.rs198
1 files changed, 130 insertions, 68 deletions
diff --git a/devices/src/virtio/gpu/mod.rs b/devices/src/virtio/gpu/mod.rs
index 6cf60d9..c81afa5 100644
--- a/devices/src/virtio/gpu/mod.rs
+++ b/devices/src/virtio/gpu/mod.rs
@@ -9,6 +9,7 @@ use std::cell::RefCell;
 use std::collections::VecDeque;
 use std::i64;
 use std::mem::{self, size_of};
+use std::num::NonZeroU8;
 use std::os::unix::io::{AsRawFd, RawFd};
 use std::path::{Path, PathBuf};
 use std::rc::Rc;
@@ -25,7 +26,7 @@ use sys_util::{
 
 use gpu_buffer::Device;
 use gpu_display::*;
-use gpu_renderer::{format_fourcc, Renderer};
+use gpu_renderer::{Renderer, RendererFlags};
 
 use super::{
     resource_bridge::*, AvailIter, Queue, VirtioDevice, INTERRUPT_STATUS_USED_RING, TYPE_GPU,
@@ -107,24 +108,12 @@ impl Frontend {
             GpuCommand::GetDisplayInfo(_) => {
                 GpuResponse::OkDisplayInfo(self.backend.display_info().to_vec())
             }
-            GpuCommand::ResourceCreate2d(info) => {
-                let format = info.format.to_native();
-                match format_fourcc(format) {
-                    Some(fourcc) => self.backend.create_resource_2d(
-                        info.resource_id.to_native(),
-                        info.width.to_native(),
-                        info.height.to_native(),
-                        fourcc,
-                    ),
-                    None => {
-                        warn!(
-                            "failed to create resource with unrecognized pipe format {}",
-                            format
-                        );
-                        GpuResponse::ErrInvalidParameter
-                    }
-                }
-            }
+            GpuCommand::ResourceCreate2d(info) => self.backend.create_resource_2d(
+                info.resource_id.to_native(),
+                info.width.to_native(),
+                info.height.to_native(),
+                info.format.to_native(),
+            ),
             GpuCommand::ResourceUnref(info) => {
                 self.backend.unref_resource(info.resource_id.to_native())
             }
@@ -602,13 +591,123 @@ impl Worker {
     }
 }
 
+/// Indicates a backend that should be tried for the gpu to use for display.
+///
+/// Several instances of this enum are used in an ordered list to give the gpu device many backends
+/// to use as fallbacks in case some do not work.
+#[derive(Clone)]
+pub enum DisplayBackend {
+    /// Use the wayland backend with the given socket path if given.
+    Wayland(Option<PathBuf>),
+    /// Open a connection to the X server at the given display if given.
+    X(Option<String>),
+    /// Emulate a display without actually displaying it.
+    Null,
+}
+
+impl DisplayBackend {
+    fn build(&self) -> std::result::Result<GpuDisplay, GpuDisplayError> {
+        match self {
+            DisplayBackend::Wayland(path) => GpuDisplay::open_wayland(path.as_ref()),
+            DisplayBackend::X(display) => GpuDisplay::open_x(display.as_ref()),
+            DisplayBackend::Null => unimplemented!(),
+        }
+    }
+
+    fn is_x(&self) -> bool {
+        match self {
+            DisplayBackend::X(_) => true,
+            _ => false,
+        }
+    }
+}
+
+/// Builds a Device for doing buffer allocation and sharing via dmabuf.
+fn build_buffer_device() -> Option<Device> {
+    const UNDESIRED_CARDS: &[&str] = &["vgem", "pvr"];
+    let drm_card = match gpu_buffer::rendernode::open_device(UNDESIRED_CARDS) {
+        Ok(f) => f,
+        Err(()) => {
+            error!("failed to open render node for GBM");
+            return None;
+        }
+    };
+    match Device::new(drm_card) {
+        Ok(d) => Some(d),
+        Err(()) => {
+            error!("failed to create GBM device from render node");
+            None
+        }
+    }
+}
+
+// Builds a gpu backend with one of the given possible display backends, or None if they all
+// failed.
+fn build_backend(
+    possible_displays: &[DisplayBackend],
+    gpu_device_socket: VmMemoryControlRequestSocket,
+) -> Option<Backend> {
+    let mut buffer_device = None;
+    let mut renderer_flags = RendererFlags::default();
+    let mut display_opt = None;
+    for display in possible_displays {
+        match display.build() {
+            Ok(c) => {
+                // If X11 is being used, that's an indication that the renderer should also be using
+                // glx. Otherwise, we are likely in an enviroment in which GBM will work for doing
+                // allocations of buffers we wish to display. TODO(zachr): this is a heuristic (or
+                // terrible hack depending on your POV). We should do something either smarter or
+                // more configurable
+                if display.is_x() {
+                    renderer_flags = RendererFlags::new().use_glx(true);
+                } else {
+                    buffer_device = build_buffer_device();
+                }
+                display_opt = Some(c);
+                break;
+            }
+            Err(e) => error!("failed to open display: {}", e),
+        };
+    }
+    let display = match display_opt {
+        Some(d) => d,
+        None => {
+            error!("failed to open any displays");
+            return None;
+        }
+    };
+
+    if cfg!(debug_assertions) {
+        let ret = unsafe { libc::dup2(libc::STDOUT_FILENO, libc::STDERR_FILENO) };
+        if ret == -1 {
+            warn!("unable to dup2 stdout to stderr: {}", Error::last());
+        }
+    }
+
+    let renderer = match Renderer::init(renderer_flags) {
+        Ok(r) => r,
+        Err(e) => {
+            error!("failed to initialize gpu renderer: {}", e);
+            return None;
+        }
+    };
+
+    Some(Backend::new(
+        buffer_device,
+        display,
+        renderer,
+        gpu_device_socket,
+    ))
+}
+
 pub struct Gpu {
     config_event: bool,
     exit_evt: EventFd,
     gpu_device_socket: Option<VmMemoryControlRequestSocket>,
     resource_bridges: Vec<ResourceResponseSocket>,
     kill_evt: Option<EventFd>,
-    wayland_socket_path: PathBuf,
+    num_scanouts: NonZeroU8,
+    display_backends: Vec<DisplayBackend>,
 }
 
 impl Gpu {
@@ -618,13 +717,17 @@ impl Gpu {
         resource_bridges: Vec<ResourceResponseSocket>,
         wayland_socket_path: P,
     ) -> Gpu {
+        let display_backends = vec![DisplayBackend::Wayland(Some(
+            wayland_socket_path.as_ref().to_owned(),
+        ))];
         Gpu {
             config_event: false,
             exit_evt,
             gpu_device_socket,
             resource_bridges,
             kill_evt: None,
-            wayland_socket_path: wayland_socket_path.as_ref().to_path_buf(),
+            num_scanouts: NonZeroU8::new(1).unwrap(),
+            display_backends,
         }
     }
 
@@ -636,7 +739,7 @@ impl Gpu {
         virtio_gpu_config {
             events_read: Le32::from(events_read),
             events_clear: Le32::from(0),
-            num_scanouts: Le32::from(1),
+            num_scanouts: Le32::from(self.num_scanouts.get() as u32),
             num_capsets: Le32::from(3),
         }
     }
@@ -747,51 +850,15 @@ impl VirtioDevice for Gpu {
         let ctrl_evt = queue_evts.remove(0);
         let cursor_queue = queues.remove(0);
         let cursor_evt = queue_evts.remove(0);
-        let socket_path = self.wayland_socket_path.clone();
+        let display_backends = self.display_backends.clone();
         if let Some(gpu_device_socket) = self.gpu_device_socket.take() {
             let worker_result =
                 thread::Builder::new()
                     .name("virtio_gpu".to_string())
                     .spawn(move || {
-                        const UNDESIRED_CARDS: &[&str] = &["vgem", "pvr"];
-                        let drm_card = match gpu_buffer::rendernode::open_device(UNDESIRED_CARDS) {
-                            Ok(f) => f,
-                            Err(()) => {
-                                error!("failed to open card");
-                                return;
-                            }
-                        };
-
-                        let device = match Device::new(drm_card) {
-                            Ok(d) => d,
-                            Err(()) => {
-                                error!("failed to open device");
-                                return;
-                            }
-                        };
-
-                        let display = match GpuDisplay::new(socket_path) {
-                            Ok(c) => c,
-                            Err(e) => {
-                                error!("failed to open display: {}", e);
-                                return;
-                            }
-                        };
-
-                        if cfg!(debug_assertions) {
-                            let ret =
-                                unsafe { libc::dup2(libc::STDOUT_FILENO, libc::STDERR_FILENO) };
-                            if ret == -1 {
-                                warn!("unable to dup2 stdout to stderr: {}", Error::last());
-                            }
-                        }
-
-                        let renderer = match Renderer::init() {
-                            Ok(r) => r,
-                            Err(e) => {
-                                error!("failed to initialize gpu renderer: {}", e);
-                                return;
-                            }
+                        let backend = match build_backend(&display_backends, gpu_device_socket) {
+                            Some(backend) => backend,
+                            None => return,
                         };
 
                         Worker {
@@ -806,12 +873,7 @@ impl VirtioDevice for Gpu {
                             cursor_evt,
                             resource_bridges,
                             kill_evt,
-                            state: Frontend::new(Backend::new(
-                                device,
-                                display,
-                                renderer,
-                                gpu_device_socket,
-                            )),
+                            state: Frontend::new(backend),
                         }
                         .run()
                     });