summary refs log blame commit diff
path: root/devices/src/virtio/gpu/backend.rs
blob: 0359dd34de00170a8ca816137d03642939d3c05c (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11










                                                                                                   
               


                  
                                         
                                                 
 


                                                
                                                                                               
                                                                                          
  
 


                                                                                         
                                      


































                                                                                                  








                               
































                                                                                                























                                      
                             



                                                                                       



                                                                                                 
















                                                        










                                                           
                                                       
                                  


                   




                                                   




                                                               
                                

         







                                             
                                        
                         












                                     






                                                                                             


                                        



                                                                    

     



                                                                             

















                                                                                       








                                                 




                                                                         
                                                                     




                    








                               
                                            
















                                                                                                  
                             
                                                                          




                                                                                                 
                                                     












                                                                                                   
                                                






































                                                                                                    



                                                                                     
                                                                         









                                                                  
                                                



                                                         
                                                                   


         





                                                                                              






                              




                                                     





                                                                      









































                                                                                   
                                                                                







                                             








                                 






























                                                                                       







                          





















                                                                                               
                



                                                                                                   









                                   

                                           
                                                                                  







                                                                                                  





                                         






























                                                                                                    




                                                               

                                                                       
                                                                         





                                                              
                                                                         


                                                                                   
                                                                             





                                                                                                  



                                                                                                
                                                                           
























                                                                       






                                                                 













                                                                       



                                                                           
                 




                                                                   












                                                                                        





                                                             










                                                                                        





                                                             








                                                           





                                                                     

     
                                                                                            













                              


                                                     














                                              

                                                                    











                                                                                     















                                                                                                 
















































                                                                                                  
                             
                         





                                                                                       
                         
                     
                        











                                                                                
             




                                                                                                 














                                   

                                



                                                                

                                               
















                                                                                                   

                         
                 

                                                          





                                                                                             














                                     

                                



                                                                

                                               
















                                                                                                  

                         
                 

                                                          






                                                                                       




                                                                     
                 
              


                                                     
















                                                                               



                                    
 
// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

//! Implementation for the transport agnostic virtio-gpu protocol, including display and rendering.

use std::cell::RefCell;
use std::collections::btree_map::Entry;
use std::collections::BTreeMap as Map;
use std::os::unix::io::AsRawFd;
use std::rc::Rc;
use std::usize;

use data_model::*;

use msg_socket::{MsgReceiver, MsgSender};
use sys_util::{error, GuestAddress, GuestMemory};

use gpu_buffer::{Buffer, Device, Flags, Format};
use gpu_display::*;
use gpu_renderer::{
    format_fourcc as renderer_fourcc, Box3, Context as RendererContext, Image as RendererImage,
    Renderer, Resource as GpuRendererResource, ResourceCreateArgs, VIRGL_RES_BIND_SCANOUT,
};

use super::protocol::{
    GpuResponse, GpuResponsePlaneInfo, VIRTIO_GPU_CAPSET_VIRGL, VIRTIO_GPU_CAPSET_VIRGL2,
};
use crate::virtio::resource_bridge::*;

const DEFAULT_WIDTH: u32 = 1280;
const DEFAULT_HEIGHT: u32 = 1024;

/// Trait for virtio-gpu resources allocated by the guest.
trait VirglResource {
    /// The width in pixels of this resource.
    fn width(&self) -> u32;

    /// The height in pixels of this resource.
    fn height(&self) -> u32;

    /// Associates the backing for this resource with the given guest memory.
    fn attach_guest_backing(&mut self, mem: &GuestMemory, vecs: Vec<(GuestAddress, usize)>);

    /// Removes associated memory for this resource previously made with `attach_guest_backing`.
    fn detach_guest_backing(&mut self);

    /// Returns the GPU `Buffer` for this resource, if it has one.
    fn buffer(&self) -> Option<&Buffer> {
        None
    }

    /// Returns the renderer's concrete `GpuRendererResource` for this resource, if it has one.
    fn gpu_renderer_resource(&mut self) -> Option<&mut GpuRendererResource> {
        None
    }

    /// Returns an import ID for this resource onto the given display, if successful.
    fn import_to_display(&mut self, _display: &Rc<RefCell<GpuDisplay>>) -> Option<u32> {
        None
    }

    /// Copies the given rectangle of pixels from guest memory, using the backing specified from a
    /// call to `attach_guest_backing`.
    fn write_from_guest_memory(
        &mut self,
        x: u32,
        y: u32,
        width: u32,
        height: u32,
        src_offset: u64,
        mem: &GuestMemory,
    );

    /// Reads from the given rectangle of pixels in the resource to the `dst` slice of memory.
    fn read_to_volatile(&mut self, x: u32, y: u32, width: u32, height: u32, dst: VolatileSlice);
}

impl VirglResource for GpuRendererResource {
    fn width(&self) -> u32 {
        match self.get_info() {
            Ok(info) => info.width,
            Err(_) => 0,
        }
    }
    fn height(&self) -> u32 {
        match self.get_info() {
            Ok(info) => info.height,
            Err(_) => 0,
        }
    }

    fn attach_guest_backing(&mut self, mem: &GuestMemory, vecs: Vec<(GuestAddress, usize)>) {
        if let Err(e) = self.attach_backing(&vecs[..], mem) {
            error!("failed to attach backing to resource: {}", e);
        }
    }

    fn detach_guest_backing(&mut self) {
        self.detach_backing();
    }

    fn gpu_renderer_resource(&mut self) -> Option<&mut GpuRendererResource> {
        Some(self)
    }

    fn write_from_guest_memory(
        &mut self,
        x: u32,
        y: u32,
        width: u32,
        height: u32,
        src_offset: u64,
        _mem: &GuestMemory,
    ) {
        let res = self.transfer_write(
            None,
            0,
            0,
            0,
            Box3 {
                x,
                y,
                z: 0,
                w: width,
                h: height,
                d: 0,
            },
            src_offset,
        );
        if let Err(e) = res {
            error!(
                "failed to write to resource (x={} y={} w={} h={}, src_offset={}): {}",
                x, y, width, height, src_offset, e
            );
        }
    }

    fn read_to_volatile(&mut self, x: u32, y: u32, width: u32, height: u32, dst: VolatileSlice) {
        let res = GpuRendererResource::read_to_volatile(
            self,
            None,
            0,
            0,
            0,
            Box3 {
                x,
                y,
                z: 0,
                w: width,
                h: height,
                d: 0,
            },
            0,
            dst,
        );
        if let Err(e) = res {
            error!("failed to read from resource: {}", e);
        }
    }
}

/// A buffer backed with a `gpu_buffer::Buffer`.
struct BackedBuffer {
    display_import: Option<(Rc<RefCell<GpuDisplay>>, u32)>,
    backing: Vec<(GuestAddress, usize)>,
    buffer: Buffer,
    gpu_renderer_resource: Option<GpuRendererResource>,
    _image: Option<RendererImage>,
}

impl BackedBuffer {
    fn new_renderer_registered(
        buffer: Buffer,
        gpu_renderer_resource: GpuRendererResource,
        image: RendererImage,
    ) -> BackedBuffer {
        BackedBuffer {
            display_import: None,
            backing: Vec::new(),
            buffer,
            gpu_renderer_resource: Some(gpu_renderer_resource),
            _image: Some(image),
        }
    }
}

impl From<Buffer> for BackedBuffer {
    fn from(buffer: Buffer) -> BackedBuffer {
        BackedBuffer {
            display_import: None,
            backing: Vec::new(),
            buffer,
            gpu_renderer_resource: None,
            _image: None,
        }
    }
}

impl VirglResource for BackedBuffer {
    fn width(&self) -> u32 {
        self.buffer.width()
    }

    fn height(&self) -> u32 {
        self.buffer.height()
    }

    fn attach_guest_backing(&mut self, mem: &GuestMemory, vecs: Vec<(GuestAddress, usize)>) {
        self.backing = vecs.clone();
        if let Some(ref mut resource) = self.gpu_renderer_resource {
            if let Err(e) = resource.attach_backing(&vecs[..], mem) {
                error!("failed to attach backing to BackBuffer resource: {}", e);
            }
        }
    }

    fn detach_guest_backing(&mut self) {
        if let Some(ref mut resource) = self.gpu_renderer_resource {
            resource.detach_backing();
        }
        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)
    }

    fn import_to_display(&mut self, display: &Rc<RefCell<GpuDisplay>>) -> Option<u32> {
        if let Some((ref self_display, import)) = self.display_import {
            if Rc::ptr_eq(&self_display, display) {
                return Some(import);
            }
        }
        let dmabuf = match self.buffer.export_plane_fd(0) {
            Ok(dmabuf) => dmabuf,
            Err(e) => {
                error!("failed to get dmabuf for scanout: {}", e);
                return None;
            }
        };

        match display.borrow_mut().import_dmabuf(
            dmabuf.as_raw_fd(),
            0, /* offset */
            self.buffer.stride(),
            self.buffer.format_modifier(),
            self.buffer.width(),
            self.buffer.height(),
            self.buffer.format().into(),
        ) {
            Ok(import_id) => {
                self.display_import = Some((display.clone(), import_id));
                Some(import_id)
            }
            Err(e) => {
                error!("failed to import dmabuf for display: {}", e);
                None
            }
        }
    }

    fn write_from_guest_memory(
        &mut self,
        x: u32,
        y: u32,
        width: u32,
        height: u32,
        src_offset: u64,
        mem: &GuestMemory,
    ) {
        if src_offset >= usize::MAX as u64 {
            error!(
                "failed to write to resource with given offset: {}",
                src_offset
            );
            return;
        }
        let res = self.buffer.write_from_sg(
            x,
            y,
            width,
            height,
            0, // plane
            src_offset as usize,
            self.backing
                .iter()
                .map(|&(addr, len)| mem.get_slice(addr.offset(), len as u64).unwrap_or_default()),
        );
        if let Err(e) = res {
            error!("failed to write to resource from guest memory: {}", e)
        }
    }

    fn read_to_volatile(&mut self, x: u32, y: u32, width: u32, height: u32, dst: VolatileSlice) {
        if let Err(e) = self.buffer.read_to_volatile(x, y, width, height, 0, dst) {
            error!("failed to copy resource: {}", e);
        }
    }
}

/// The virtio-gpu backend state tracker.
///
/// Commands from the virtio-gpu protocol can be submitted here using the methods, and they will be
/// realized on the hardware. Most methods return a `GpuResponse` that indicate the success,
/// failure, or requested data for the given command.
pub struct Backend {
    display: Rc<RefCell<GpuDisplay>>,
    device: Device,
    renderer: Renderer,
    resources: Map<u32, Box<dyn VirglResource>>,
    contexts: Map<u32, RendererContext>,
    scanout_surface: Option<u32>,
    cursor_surface: Option<u32>,
    scanout_resource: u32,
    cursor_resource: u32,
}

impl Backend {
    /// Creates a new backend for virtio-gpu that realizes all commands using the given `device` for
    /// allocating buffers, `display` for showing the results, and `renderer` for submitting
    /// rendering commands.
    pub fn new(device: Device, display: GpuDisplay, renderer: Renderer) -> Backend {
        Backend {
            display: Rc::new(RefCell::new(display)),
            device,
            renderer,
            resources: Default::default(),
            contexts: Default::default(),
            scanout_surface: None,
            cursor_surface: None,
            scanout_resource: 0,
            cursor_resource: 0,
        }
    }

    /// Gets a reference to the display passed into `new`.
    pub fn display(&self) -> &Rc<RefCell<GpuDisplay>> {
        &self.display
    }

    /// Processes the internal `display` events and returns `true` if the main display was closed.
    pub fn process_display(&mut self) -> bool {
        let mut display = self.display.borrow_mut();
        display.dispatch_events();
        self.scanout_surface
            .map(|s| display.close_requested(s))
            .unwrap_or(false)
    }

    pub fn process_resource_bridge(&self, resource_bridge: &ResourceResponseSocket) {
        let request = match resource_bridge.recv() {
            Ok(msg) => msg,
            Err(e) => {
                error!("error receiving resource bridge request: {}", e);
                return;
            }
        };

        let response = match request {
            ResourceRequest::GetResource { id } => self
                .resources
                .get(&id)
                .and_then(|resource| resource.buffer())
                .and_then(|buffer| buffer.export_plane_fd(0).ok())
                .map(ResourceResponse::Resource)
                .unwrap_or(ResourceResponse::Invalid),
        };

        if let Err(e) = resource_bridge.send(&response) {
            error!("error sending resource bridge request: {}", e);
        }
    }

    /// Gets the list of supported display resolutions as a slice of `(width, height)` tuples.
    pub fn display_info(&self) -> &[(u32, u32)] {
        &[(DEFAULT_WIDTH, DEFAULT_HEIGHT)]
    }

    /// Creates a 2D resource with the given properties and associated it with the given id.
    pub fn create_resource_2d(
        &mut self,
        id: u32,
        width: u32,
        height: u32,
        fourcc: u32,
    ) -> GpuResponse {
        if id == 0 {
            return GpuResponse::ErrInvalidResourceId;
        }
        match self.resources.entry(id) {
            Entry::Vacant(slot) => {
                let res = self.device.create_buffer(
                    width,
                    height,
                    Format::from(fourcc),
                    Flags::empty().use_scanout(true).use_linear(true),
                );
                match res {
                    Ok(res) => {
                        slot.insert(Box::from(BackedBuffer::from(res)));
                        GpuResponse::OkNoData
                    }
                    Err(_) => {
                        error!("failed to create renderer resource {}", fourcc);
                        GpuResponse::ErrUnspec
                    }
                }
            }
            Entry::Occupied(_) => GpuResponse::ErrInvalidResourceId,
        }
    }

    /// Removes the guest's reference count for the given resource id.
    pub fn unref_resource(&mut self, id: u32) -> GpuResponse {
        match self.resources.remove(&id) {
            Some(_) => GpuResponse::OkNoData,
            None => GpuResponse::ErrInvalidResourceId,
        }
    }

    /// Sets the given resource id as the source of scanout to the display.
    pub fn set_scanout(&mut self, id: u32) -> GpuResponse {
        let mut display = self.display.borrow_mut();
        if id == 0 {
            if let Some(surface) = self.scanout_surface.take() {
                display.release_surface(surface);
            }
            self.scanout_resource = 0;
            if let Some(surface) = self.cursor_surface.take() {
                display.release_surface(surface);
            }
            self.cursor_resource = 0;
            GpuResponse::OkNoData
        } else if self.resources.get_mut(&id).is_some() {
            self.scanout_resource = id;

            if self.scanout_surface.is_none() {
                match display.create_surface(None, DEFAULT_WIDTH, DEFAULT_HEIGHT) {
                    Ok(surface) => self.scanout_surface = Some(surface),
                    Err(e) => error!("failed to create display surface: {}", e),
                }
            }
            GpuResponse::OkNoData
        } else {
            GpuResponse::ErrInvalidResourceId
        }
    }

    fn flush_resource_to_surface(
        &mut self,
        resource_id: u32,
        surface_id: u32,
        x: u32,
        y: u32,
        width: u32,
        height: u32,
    ) -> GpuResponse {
        let resource = match self.resources.get_mut(&resource_id) {
            Some(r) => r,
            None => return GpuResponse::ErrInvalidResourceId,
        };

        if let Some(import_id) = resource.import_to_display(&self.display) {
            self.display.borrow_mut().flip_to(surface_id, import_id);
            return GpuResponse::OkNoData;
        }

        // Import failed, fall back to a copy.
        let display = self.display.borrow_mut();
        // Prevent overwriting a buffer that is currently being used by the compositor.
        if display.next_buffer_in_use(surface_id) {
            return GpuResponse::OkNoData;
        }
        let fb = match display.framebuffer_memory(surface_id) {
            Some(fb) => fb,
            None => {
                error!("failed to access framebuffer for surface {}", surface_id);
                return GpuResponse::ErrUnspec;
            }
        };

        resource.read_to_volatile(x, y, width, height, fb);
        display.flip(surface_id);

        GpuResponse::OkNoData
    }

    /// Flushes the given rectangle of pixels of the given resource to the display.
    pub fn flush_resource(
        &mut self,
        id: u32,
        x: u32,
        y: u32,
        width: u32,
        height: u32,
    ) -> GpuResponse {
        if id == 0 {
            return GpuResponse::OkNoData;
        }

        let mut response = GpuResponse::OkNoData;

        if id == self.scanout_resource {
            if let Some(surface_id) = self.scanout_surface {
                response = self.flush_resource_to_surface(id, surface_id, x, y, width, height);
            }
        }

        if response != GpuResponse::OkNoData {
            return response;
        }

        if id == self.cursor_resource {
            if let Some(surface_id) = self.cursor_surface {
                response = self.flush_resource_to_surface(id, surface_id, x, y, width, height);
            }
        }

        response
    }

    /// Copes the given rectangle of pixels of the given resource's backing memory to the host side
    /// resource.
    pub fn transfer_to_resource_2d(
        &mut self,
        id: u32,
        x: u32,
        y: u32,
        width: u32,
        height: u32,
        src_offset: u64,
        mem: &GuestMemory,
    ) -> GpuResponse {
        match self.resources.get_mut(&id) {
            Some(res) => {
                res.write_from_guest_memory(x, y, width, height, src_offset, mem);
                GpuResponse::OkNoData
            }
            None => GpuResponse::ErrInvalidResourceId,
        }
    }

    /// Attaches backing memory to the given resource, represented by a `Vec` of `(address, size)`
    /// tuples in the guest's physical address space.
    pub fn attach_backing(
        &mut self,
        id: u32,
        mem: &GuestMemory,
        vecs: Vec<(GuestAddress, usize)>,
    ) -> GpuResponse {
        match self.resources.get_mut(&id) {
            Some(resource) => {
                resource.attach_guest_backing(mem, vecs);
                GpuResponse::OkNoData
            }
            None => GpuResponse::ErrInvalidResourceId,
        }
    }

    /// Detaches any backing memory from the given resource, if there is any.
    pub fn detach_backing(&mut self, id: u32) -> GpuResponse {
        match self.resources.get_mut(&id) {
            Some(resource) => {
                resource.detach_guest_backing();
                GpuResponse::OkNoData
            }
            None => GpuResponse::ErrInvalidResourceId,
        }
    }

    /// Updates the cursor's memory to the given id, and sets its position to the given coordinates.
    pub fn update_cursor(&mut self, id: u32, x: u32, y: u32) -> GpuResponse {
        if id == 0 {
            if let Some(surface) = self.cursor_surface.take() {
                self.display.borrow_mut().release_surface(surface);
            }
            self.cursor_resource = 0;
            GpuResponse::OkNoData
        } else if let Some(resource) = self.resources.get_mut(&id) {
            self.cursor_resource = id;
            if self.cursor_surface.is_none() {
                match self.display.borrow_mut().create_surface(
                    self.scanout_surface,
                    resource.width(),
                    resource.height(),
                ) {
                    Ok(surface) => self.cursor_surface = Some(surface),
                    Err(e) => {
                        error!("failed to create cursor surface: {}", e);
                        return GpuResponse::ErrUnspec;
                    }
                }
            }

            let cursor_surface = self.cursor_surface.unwrap();
            self.display.borrow_mut().set_position(cursor_surface, x, y);

            // Gets the resource's pixels into the display by importing the buffer.
            if let Some(import_id) = resource.import_to_display(&self.display) {
                self.display.borrow_mut().flip_to(cursor_surface, import_id);
                return GpuResponse::OkNoData;
            }

            // Importing failed, so try copying the pixels into the surface's slower shared memory
            // framebuffer.
            if let Some(buffer) = resource.buffer() {
                if let Some(fb) = self.display.borrow_mut().framebuffer_memory(cursor_surface) {
                    if let Err(e) =
                        buffer.read_to_volatile(0, 0, buffer.width(), buffer.height(), 0, fb)
                    {
                        error!("failed to copy resource to cursor: {}", e);
                        return GpuResponse::ErrInvalidParameter;
                    }
                }
                self.display.borrow_mut().flip(cursor_surface);
            }
            GpuResponse::OkNoData
        } else {
            GpuResponse::ErrInvalidResourceId
        }
    }

    /// Moves the cursor's position to the given coordinates.
    pub fn move_cursor(&mut self, x: u32, y: u32) -> GpuResponse {
        if let Some(cursor_surface) = self.cursor_surface {
            if let Some(scanout_surface) = self.scanout_surface {
                let display = self.display.borrow_mut();
                display.set_position(cursor_surface, x, y);
                display.commit(scanout_surface);
            }
        }
        GpuResponse::OkNoData
    }

    /// Gets the renderer's capset information associated with `index`.
    pub fn get_capset_info(&self, index: u32) -> GpuResponse {
        let id = match index {
            0 => VIRTIO_GPU_CAPSET_VIRGL,
            1 => VIRTIO_GPU_CAPSET_VIRGL2,
            _ => return GpuResponse::ErrInvalidParameter,
        };
        let (version, size) = self.renderer.get_cap_set_info(id);
        GpuResponse::OkCapsetInfo { id, version, size }
    }

    /// Gets the capset of `version` associated with `id`.
    pub fn get_capset(&self, id: u32, version: u32) -> GpuResponse {
        GpuResponse::OkCapset(self.renderer.get_cap_set(id, version))
    }

    /// Creates a fresh renderer context with the given `id`.
    pub fn create_renderer_context(&mut self, id: u32) -> GpuResponse {
        if id == 0 {
            return GpuResponse::ErrInvalidContextId;
        }
        match self.contexts.entry(id) {
            Entry::Occupied(_) => GpuResponse::ErrInvalidContextId,
            Entry::Vacant(slot) => match self.renderer.create_context(id) {
                Ok(ctx) => {
                    slot.insert(ctx);
                    GpuResponse::OkNoData
                }
                Err(e) => {
                    error!("failed to create renderer ctx: {}", e);
                    GpuResponse::ErrUnspec
                }
            },
        }
    }

    /// Destorys the renderer context associated with `id`.
    pub fn destroy_renderer_context(&mut self, id: u32) -> GpuResponse {
        match self.contexts.remove(&id) {
            Some(_) => GpuResponse::OkNoData,
            None => GpuResponse::ErrInvalidContextId,
        }
    }

    /// Attaches the indicated resource to the given context.
    pub fn context_attach_resource(&mut self, ctx_id: u32, res_id: u32) -> GpuResponse {
        match (
            self.contexts.get_mut(&ctx_id),
            self.resources
                .get_mut(&res_id)
                .and_then(|res| res.gpu_renderer_resource()),
        ) {
            (Some(ctx), Some(res)) => {
                ctx.attach(res);
                GpuResponse::OkNoData
            }
            (None, _) => GpuResponse::ErrInvalidContextId,
            (_, None) => GpuResponse::ErrInvalidResourceId,
        }
    }

    /// detaches the indicated resource to the given context.
    pub fn context_detach_resource(&mut self, ctx_id: u32, res_id: u32) -> GpuResponse {
        match (
            self.contexts.get_mut(&ctx_id),
            self.resources
                .get_mut(&res_id)
                .and_then(|res| res.gpu_renderer_resource()),
        ) {
            (Some(ctx), Some(res)) => {
                ctx.detach(res);
                GpuResponse::OkNoData
            }
            (None, _) => GpuResponse::ErrInvalidContextId,
            (_, None) => GpuResponse::ErrInvalidResourceId,
        }
    }

    pub fn allocate_using_minigbm(args: ResourceCreateArgs) -> bool {
        return args.bind & VIRGL_RES_BIND_SCANOUT != 0
            && args.depth == 1
            && args.array_size == 1
            && args.last_level == 0
            && args.nr_samples == 0;
    }

    /// Creates a 3D resource with the given properties and associated it with the given id.
    pub fn resource_create_3d(
        &mut self,
        id: u32,
        target: u32,
        format: u32,
        bind: u32,
        width: u32,
        height: u32,
        depth: u32,
        array_size: u32,
        last_level: u32,
        nr_samples: u32,
        flags: u32,
    ) -> GpuResponse {
        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) => {
                if Backend::allocate_using_minigbm(create_args) {
                    match renderer_fourcc(create_args.format) {
                        Some(fourcc) => {
                            let buffer = match self.device.create_buffer(
                                width,
                                height,
                                Format::from(fourcc),
                                Flags::empty().use_scanout(true).use_rendering(true),
                            ) {
                                Ok(buffer) => buffer,
                                Err(e) => {
                                    // Attempt to allocate the buffer without scanout flag.
                                    match self.device.create_buffer(
                                        width,
                                        height,
                                        Format::from(fourcc),
                                        Flags::empty().use_rendering(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 format_modifier = buffer.format_modifier();
                                    let mut plane_info = Vec::with_capacity(buffer.num_planes());
                                    for plane_index in 0..buffer.num_planes() {
                                        plane_info.push(GpuResponsePlaneInfo {
                                            stride: buffer.plane_stride(plane_index),
                                            offset: buffer.plane_offset(plane_index),
                                        });
                                    }
                                    let backed =
                                        BackedBuffer::new_renderer_registered(buffer, res, image);
                                    slot.insert(Box::new(backed));
                                    GpuResponse::OkResourcePlaneInfo {
                                        format_modifier,
                                        plane_info,
                                    }
                                }
                                Err(e) => {
                                    error!("failed to import renderer resource: {}", e);
                                    GpuResponse::ErrUnspec
                                }
                            }
                        }
                        None => {
                            error!(
                                "failed to detemine fourcc for minigbm 3d resource {}",
                                format
                            );
                            return GpuResponse::ErrUnspec;
                        }
                    }
                } else {
                    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
                        }
                    }
                }
            }
        }
    }

    /// Copes the given 3D rectangle of pixels of the given resource's backing memory to the host
    /// side resource.
    pub fn transfer_to_resource_3d(
        &mut self,
        ctx_id: u32,
        res_id: u32,
        x: u32,
        y: u32,
        z: u32,
        width: u32,
        height: u32,
        depth: u32,
        level: u32,
        stride: u32,
        layer_stride: u32,
        offset: u64,
    ) -> GpuResponse {
        let ctx = match ctx_id {
            0 => None,
            id => match self.contexts.get(&id) {
                None => return GpuResponse::ErrInvalidContextId,
                ctx => ctx,
            },
        };
        match self.resources.get_mut(&res_id) {
            Some(res) => match res.gpu_renderer_resource() {
                Some(res) => {
                    let transfer_box = Box3 {
                        x,
                        y,
                        z,
                        w: width,
                        h: height,
                        d: depth,
                    };
                    let res =
                        res.transfer_write(ctx, level, stride, layer_stride, transfer_box, offset);
                    match res {
                        Ok(_) => GpuResponse::OkNoData,
                        Err(e) => {
                            error!("failed to transfer to host: {}", e);
                            GpuResponse::ErrUnspec
                        }
                    }
                }
                None => GpuResponse::ErrInvalidResourceId,
            },
            None => GpuResponse::ErrInvalidResourceId,
        }
    }

    /// Copes the given rectangle of pixels from the resource to the given resource's backing
    /// memory.
    pub fn transfer_from_resource_3d(
        &mut self,
        ctx_id: u32,
        res_id: u32,
        x: u32,
        y: u32,
        z: u32,
        width: u32,
        height: u32,
        depth: u32,
        level: u32,
        stride: u32,
        layer_stride: u32,
        offset: u64,
    ) -> GpuResponse {
        let ctx = match ctx_id {
            0 => None,
            id => match self.contexts.get(&id) {
                None => return GpuResponse::ErrInvalidContextId,
                ctx => ctx,
            },
        };
        match self.resources.get_mut(&res_id) {
            Some(res) => match res.gpu_renderer_resource() {
                Some(res) => {
                    let transfer_box = Box3 {
                        x,
                        y,
                        z,
                        w: width,
                        h: height,
                        d: depth,
                    };
                    let res =
                        res.transfer_read(ctx, level, stride, layer_stride, transfer_box, offset);
                    match res {
                        Ok(_) => GpuResponse::OkNoData,
                        Err(e) => {
                            error!("failed to transfer from host: {}", e);
                            GpuResponse::ErrUnspec
                        }
                    }
                }
                None => GpuResponse::ErrInvalidResourceId,
            },
            None => GpuResponse::ErrInvalidResourceId,
        }
    }

    /// Submits a command buffer to the given rendering context.
    pub fn submit_command(&mut self, ctx_id: u32, commands: &mut [u8]) -> GpuResponse {
        match self.contexts.get_mut(&ctx_id) {
            Some(ctx) => match ctx.submit(&mut commands[..]) {
                Ok(_) => GpuResponse::OkNoData,
                Err(e) => {
                    error!("failed to submit command buffer: {}", e);
                    GpuResponse::ErrUnspec
                }
            },
            None => GpuResponse::ErrInvalidContextId,
        }
    }

    pub fn create_fence(&mut self, ctx_id: u32, fence_id: u32) -> GpuResponse {
        // There is a mismatch of ordering that is intentional.
        // This create_fence matches the other functions in Backend, yet
        // the renderer matches the virgl interface.
        match self.renderer.create_fence(fence_id, ctx_id) {
            Ok(_) => GpuResponse::OkNoData,
            Err(e) => {
                error!("failed to create fence: {}", e);
                GpuResponse::ErrUnspec
            }
        }
    }

    pub fn fence_poll(&mut self) -> u32 {
        self.renderer.poll()
    }

    pub fn force_ctx_0(&mut self) {
        self.renderer.force_ctx_0();
    }
}