diff --git a/devices/src/virtio/gpu/backend.rs b/devices/src/virtio/gpu/backend.rs index 8aadc7315e..561ce74bb4 100644 --- a/devices/src/virtio/gpu/backend.rs +++ b/devices/src/virtio/gpu/backend.rs @@ -18,7 +18,8 @@ use sys_util::{GuestAddress, GuestMemory}; use super::gpu_buffer::{Device, Buffer, Format, Flags}; use super::gpu_display::*; use super::gpu_renderer::{Box3, Renderer, Context as RendererContext, - Resource as GpuRendererResource, ResourceCreateArgs}; + Resource as GpuRendererResource, ResourceCreateArgs, + format_fourcc as renderer_fourcc}; use super::protocol::GpuResponse; @@ -154,6 +155,19 @@ struct BackedBuffer { display_import: Option<(Rc>, u32)>, backing: Vec<(GuestAddress, usize)>, buffer: Buffer, + gpu_renderer_resource: Option, +} + +impl BackedBuffer { + fn new_renderer_registered(buffer: Buffer, + gpu_renderer_resource: GpuRendererResource) -> BackedBuffer { + BackedBuffer { + display_import: None, + backing: Vec::new(), + buffer, + gpu_renderer_resource: Some(gpu_renderer_resource), + } + } } impl From for BackedBuffer { @@ -162,6 +176,7 @@ impl From for BackedBuffer { display_import: None, backing: Vec::new(), buffer, + gpu_renderer_resource: None, } } } @@ -183,6 +198,10 @@ impl VirglResource for BackedBuffer { self.backing.clear() } + fn gpu_renderer_resource(&mut self) -> Option<&mut GpuRendererResource> { + self.gpu_renderer_resource.as_mut() + } + fn buffer(&self) -> Option<&Buffer> { Some(&self.buffer) } @@ -646,6 +665,17 @@ impl Backend { } } + pub fn validate_args_as_fourcc(args: ResourceCreateArgs) -> Option { + if args.depth == 1 && + args.array_size == 1 && + args.last_level == 0 && + args.nr_samples == 0 { + renderer_fourcc(args.format) + } else { + None + } + } + /// Creates a 3D resource with the given properties and associated it with the given id. pub fn resource_create_3d(&mut self, id: u32, @@ -663,31 +693,90 @@ impl Backend { if id == 0 { return GpuResponse::ErrInvalidResourceId; } + + let create_args = ResourceCreateArgs { + handle: id, + target, + format, + bind, + width, + height, + depth, + array_size, + last_level, + nr_samples, + flags, + }; + match self.resources.entry(id) { Entry::Occupied(_) => GpuResponse::ErrInvalidResourceId, Entry::Vacant(slot) => { - let res = self.renderer - .create_resource(ResourceCreateArgs { - handle: id, - target, - format, - bind, - width, - height, - depth, - array_size, - last_level, - nr_samples, - flags, - }); - match res { - Ok(res) => { - slot.insert(Box::new(res)); - GpuResponse::OkNoData - } - Err(e) => { - error!("failed to create renderer resource: {}", e); - GpuResponse::ErrUnspec + match Backend::validate_args_as_fourcc(create_args) { + Some(fourcc) => { + let buffer = match self.device + .create_buffer(width, + height, + Format::from(fourcc), + Flags::empty().use_scanout(true).use_linear(true)) { + Ok(buffer) => buffer, + Err(e) => { + error!("failed to create buffer for 3d resource {}: {}", format, e); + return GpuResponse::ErrUnspec; + } + }; + + let dma_buf_fd = match buffer.export_plane_fd(0) { + Ok(dma_buf_fd) => dma_buf_fd, + Err(e) => { + error!("failed to export plane fd: {}", e); + return GpuResponse::ErrUnspec + } + }; + + let image = match self.renderer + .image_from_dmabuf(fourcc, + width, + height, + dma_buf_fd.as_raw_fd(), + buffer.plane_offset(0), + buffer.plane_stride(0)) { + Ok(image) => image, + Err(e) => { + error!("failed to create egl image: {}", e); + return GpuResponse::ErrUnspec + } + }; + + let res = self.renderer + .import_resource(create_args, image); + match res { + Ok(res) => { + let mut backed = + BackedBuffer::new_renderer_registered(buffer, + res); + slot.insert(Box::new(backed)); + GpuResponse::OkNoData + } + Err(e) => { + error!("failed to import renderer resource: {}", + e); + GpuResponse::ErrUnspec + } + } + }, + None => { + let res = self.renderer.create_resource(create_args); + match res { + Ok(res) => { + slot.insert(Box::new(res)); + GpuResponse::OkNoData + } + Err(e) => { + error!("failed to create renderer resource: {}", + e); + GpuResponse::ErrUnspec + } + } } } } diff --git a/gpu_renderer/src/lib.rs b/gpu_renderer/src/lib.rs index dfb8bf9025..7a8c7fa1b0 100644 --- a/gpu_renderer/src/lib.rs +++ b/gpu_renderer/src/lib.rs @@ -19,7 +19,7 @@ use std::marker::PhantomData; use std::mem::{size_of, transmute, uninitialized}; use std::ops::Deref; use std::os::raw::{c_void, c_int, c_uint, c_char}; -use std::os::unix::io::FromRawFd; +use std::os::unix::io::{FromRawFd, RawFd}; use std::ptr::{null, null_mut}; use std::rc::Rc; use std::result; @@ -32,7 +32,10 @@ use generated::virglrenderer::*; pub use generated::virglrenderer::{virgl_renderer_resource_create_args, virgl_renderer_resource_info}; use generated::epoxy_egl::{EGL_CONTEXT_CLIENT_VERSION, EGL_SURFACE_TYPE, EGL_OPENGL_ES_API, - EGL_NONE, EGL_GL_TEXTURE_2D_KHR, EGLDEBUGPROCKHR, EGLAttrib, + EGL_NONE, EGL_GL_TEXTURE_2D_KHR, EGL_WIDTH, EGL_HEIGHT, + EGL_LINUX_DRM_FOURCC_EXT, EGL_DMA_BUF_PLANE0_FD_EXT, + EGL_DMA_BUF_PLANE0_OFFSET_EXT, EGL_DMA_BUF_PLANE0_PITCH_EXT, + EGL_LINUX_DMA_BUF_EXT, EGLDEBUGPROCKHR, EGLAttrib, EGLuint64KHR, EGLNativeDisplayType, EGLConfig, EGLContext, EGLDisplay, EGLSurface, EGLClientBuffer, EGLBoolean, EGLint, EGLenum, EGLImageKHR}; use generated::p_defines::{PIPE_TEXTURE_1D, PIPE_TEXTURE_2D, PIPE_BIND_SAMPLER_VIEW}; @@ -301,6 +304,7 @@ impl Deref for EGLFunctions { pub struct Renderer { no_sync_send: PhantomData<*mut ()>, egl_funcs: EGLFunctions, + display: EGLDisplay, } impl Renderer { @@ -397,6 +401,7 @@ impl Renderer { Ok(Renderer { no_sync_send: PhantomData, egl_funcs, + display }) } @@ -458,6 +463,22 @@ impl Renderer { }) } + /// Imports a resource from an EGLImage. + pub fn import_resource(&self, + mut args: virgl_renderer_resource_create_args, + image: Image) + -> Result { + let ret = unsafe { virgl_renderer_resource_import_eglimage(&mut args, image.image) }; + ret_to_res(ret)?; + Ok(Resource { + id: args.handle, + backing_iovecs: Vec::new(), + backing_mem: None, + egl_funcs: self.egl_funcs.clone(), + no_sync_send: PhantomData, + }) + } + /// Helper that creates a simple 1 dimensional resource with basic metadata. pub fn create_tex_1d(&self, id: u32, width: u32) -> Result { self.create_resource(virgl_renderer_resource_create_args { @@ -491,6 +512,47 @@ impl Renderer { flags: 0, }) } + + /// Creates an EGLImage from a DMA buffer. + pub fn image_from_dmabuf(&self, + fourcc: u32, + width: u32, + height: u32, + fd: RawFd, + offset: u32, + stride: u32) -> Result { + let mut attrs = [EGL_WIDTH as EGLint, + width as EGLint, + EGL_HEIGHT as EGLint, + height as EGLint, + EGL_LINUX_DRM_FOURCC_EXT as EGLint, + fourcc as EGLint, + EGL_DMA_BUF_PLANE0_FD_EXT as EGLint, + fd as EGLint, + EGL_DMA_BUF_PLANE0_OFFSET_EXT as EGLint, + offset as EGLint, + EGL_DMA_BUF_PLANE0_PITCH_EXT as EGLint, + stride as EGLint, + EGL_NONE as EGLint]; + + let image = unsafe { + (self.egl_funcs.CreateImageKHR)(self.display, + 0 as EGLContext, + EGL_LINUX_DMA_BUF_EXT, + null_mut() as EGLClientBuffer, + attrs.as_mut_ptr()) + }; + + if image.is_null() { + return Err(Error::CreateImage); + } + + Ok(Image { + egl_funcs: self.egl_funcs.clone(), + egl_dpy: self.display, + image + }) + } } /// A context in which resources can be attached/detached and commands can be submitted. @@ -548,6 +610,21 @@ impl Drop for Context { } } +/// A wrapper of an EGLImage to manage its destruction. +pub struct Image { + egl_funcs: EGLFunctions, + egl_dpy: EGLDisplay, + image: EGLImageKHR, +} + +impl Drop for Image { + fn drop(&mut self) { + unsafe { + (self.egl_funcs.DestroyImageKHR)(self.egl_dpy, self.image); + } + } +} + /// A DMABUF file descriptor handle and metadata returned from `Resource::export`. #[derive(Debug)] pub struct ExportedResource {