summary refs log blame commit diff
path: root/devices/src/virtio/gpu/backend.rs
blob: 75dbdafbdc200c8ce386f86dd1349ecb636faefc (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 resources::Alloc;
use sys_util::{error, GuestAddress, GuestMemory};

use gpu_display::*;
use gpu_renderer::{
    Box3, Context as RendererContext, Renderer, Resource as GpuRendererResource, ResourceCreateArgs,
};

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

use vm_control::{MaybeOwnedFd, VmMemoryControlRequestSocket, VmMemoryRequest, VmMemoryResponse};

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

struct VirtioGpuResource {
    width: u32,
    height: u32,
    gpu_resource: GpuRendererResource,
    display_import: Option<(Rc<RefCell<GpuDisplay>>, u32)>,
    kvm_slot: Option<u32>,
}

impl VirtioGpuResource {
    pub fn new(width: u32, height: u32, gpu_resource: GpuRendererResource) -> VirtioGpuResource {
        VirtioGpuResource {
            width,
            height,
            gpu_resource,
            display_import: None,
            kvm_slot: None,
        }
    }

    pub fn v2_new(kvm_slot: u32, gpu_resource: GpuRendererResource) -> VirtioGpuResource {
        // Choose DEFAULT_WIDTH and DEFAULT_HEIGHT, since that matches the default modes
        // for virtgpu-kms.
        VirtioGpuResource {
            width: DEFAULT_WIDTH,
            height: DEFAULT_HEIGHT,
            gpu_resource,
            display_import: None,
            kvm_slot: Some(kvm_slot),
        }
    }

    pub fn import_to_display(&mut self, display: &Rc<RefCell<GpuDisplay>>) -> Option<u32> {
        if let Some((self_display, import)) = &self.display_import {
            if Rc::ptr_eq(self_display, display) {
                return Some(*import);
            }
        }

        let (query, dmabuf) = match self.gpu_resource.export() {
            Ok(export) => (export.0, export.1),
            Err(e) => {
                error!("failed to query resource: {}", e);
                return None;
            }
        };

        match display.borrow_mut().import_dmabuf(
            dmabuf.as_raw_fd(),
            query.out_offsets[0],
            query.out_strides[0],
            query.out_modifier,
            self.width,
            self.height,
            query.out_fourcc,
        ) {
            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
            }
        }
    }

    pub fn write_from_guest_memory(
        &mut self,
        x: u32,
        y: u32,
        width: u32,
        height: u32,
        src_offset: u64,
        _mem: &GuestMemory,
    ) {
        let res = self.gpu_resource.transfer_write(
            None,
            0,
            0,
            0,
            Box3::new_2d(x, y, width, height),
            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
            );
        }
    }

    pub fn read_to_volatile(
        &mut self,
        x: u32,
        y: u32,
        width: u32,
        height: u32,
        dst: VolatileSlice,
        dst_stride: u32,
    ) {
        let res = self.gpu_resource.read_to_volatile(
            None,
            0,
            dst_stride,
            0, /* layer_stride */
            Box3::new_2d(x, y, width, height),
            0, /* offset */
            dst,
        );
        if let Err(e) = res {
            error!("failed to read from 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>>,
    renderer: Renderer,
    resources: Map<u32, VirtioGpuResource>,
    contexts: Map<u32, RendererContext>,
    gpu_device_socket: VmMemoryControlRequestSocket,
    scanout_surface: Option<u32>,
    cursor_surface: Option<u32>,
    scanout_resource: u32,
    cursor_resource: u32,
    pci_bar: Alloc,
}

impl Backend {
    /// Creates a new backend for virtio-gpu that realizes all commands using the given `display`
    /// for showing the results, and `renderer` for submitting rendering commands.
    ///
    /// All buffer allocations will be done internally by the renderer or the display and buffer
    /// data is copied as needed.
    pub fn new(
        display: GpuDisplay,
        renderer: Renderer,
        gpu_device_socket: VmMemoryControlRequestSocket,
        pci_bar: Alloc,
    ) -> Backend {
        Backend {
            display: Rc::new(RefCell::new(display)),
            renderer,
            gpu_device_socket,
            resources: Default::default(),
            contexts: Default::default(),
            scanout_surface: None,
            cursor_surface: None,
            scanout_resource: 0,
            cursor_resource: 0,
            pci_bar,
        }
    }

    /// 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.gpu_resource.export().ok())
                .and_then(|export| Some(export.1))
                .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,
        format: u32,
    ) -> GpuResponse {
        if id == 0 {
            return GpuResponse::ErrInvalidResourceId;
        }
        match self.resources.entry(id) {
            Entry::Vacant(slot) => {
                let gpu_resource = self.renderer.create_resource_2d(id, width, height, format);
                match gpu_resource {
                    Ok(gpu_resource) => {
                        let virtio_gpu_resource =
                            VirtioGpuResource::new(width, height, gpu_resource);
                        slot.insert(virtio_gpu_resource);
                        GpuResponse::OkNoData
                    }
                    Err(e) => {
                        error!("failed to create renderer resource: {}", e);
                        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 mut 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_region(surface_id, 0, 0, DEFAULT_WIDTH, DEFAULT_HEIGHT) {
            Some(fb) => fb,
            None => {
                error!("failed to access framebuffer for surface {}", surface_id);
                return GpuResponse::ErrUnspec;
            }
        };

        resource.read_to_volatile(
            0,
            0,
            DEFAULT_WIDTH,
            DEFAULT_HEIGHT,
            fb.as_volatile_slice(),
            fb.stride(),
        );
        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) => match resource.gpu_resource.attach_backing(&vecs[..], mem) {
                Ok(_) => GpuResponse::OkNoData,
                Err(_) => GpuResponse::ErrUnspec,
            },
            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.gpu_resource.detach_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(fb) = self.display.borrow_mut().framebuffer(cursor_surface) {
                resource.read_to_volatile(
                    0,
                    0,
                    resource.width,
                    resource.height,
                    fb.as_volatile_slice(),
                    fb.stride(),
                )
            }
            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 mut 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,
            2 => VIRTIO_GPU_CAPSET3,
            _ => 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),
        ) {
            (Some(ctx), Some(res)) => {
                ctx.attach(&res.gpu_resource);
                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),
        ) {
            (Some(ctx), Some(res)) => {
                ctx.detach(&res.gpu_resource);
                GpuResponse::OkNoData
            }
            (None, _) => GpuResponse::ErrInvalidContextId,
            (_, None) => GpuResponse::ErrInvalidResourceId,
        }
    }

    /// 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) => {
                let gpu_resource = self.renderer.create_resource(create_args);
                match gpu_resource {
                    Ok(gpu_resource) => {
                        let query = match gpu_resource.query() {
                            Ok(query) => query,
                            Err(_) => return GpuResponse::ErrUnspec,
                        };

                        let response = match query.out_num_fds {
                            0 => GpuResponse::OkNoData,
                            1 => {
                                let mut plane_info = Vec::with_capacity(4);
                                for plane_index in 0..4 {
                                    plane_info.push(GpuResponsePlaneInfo {
                                        stride: query.out_strides[plane_index],
                                        offset: query.out_offsets[plane_index],
                                    });
                                }

                                let format_modifier = query.out_modifier;
                                GpuResponse::OkResourcePlaneInfo {
                                    format_modifier,
                                    plane_info,
                                }
                            }
                            _ => return GpuResponse::ErrUnspec,
                        };

                        let virtio_gpu_resource =
                            VirtioGpuResource::new(width, height, gpu_resource);
                        slot.insert(virtio_gpu_resource);
                        response
                    }
                    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) => {
                let transfer_box = Box3 {
                    x,
                    y,
                    z,
                    w: width,
                    h: height,
                    d: depth,
                };
                let res = res.gpu_resource.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,
        }
    }

    /// 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) => {
                let transfer_box = Box3 {
                    x,
                    y,
                    z,
                    w: width,
                    h: height,
                    d: depth,
                };
                let res = res.gpu_resource.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,
        }
    }

    /// 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();
    }

    pub fn allocation_metadata(
        &mut self,
        request_id: u32,
        request: Vec<u8>,
        mut response: Vec<u8>,
    ) -> GpuResponse {
        let res = self.renderer.allocation_metadata(&request, &mut response);

        match res {
            Ok(_) => {
                let res_info = AllocationMetadataResponse {
                    request_id,
                    response,
                };

                GpuResponse::OkAllocationMetadata { res_info }
            }
            Err(_) => {
                error!("failed to get metadata");
                GpuResponse::ErrUnspec
            }
        }
    }

    pub fn resource_create_v2(
        &mut self,
        resource_id: u32,
        guest_memory_type: u32,
        guest_caching_type: u32,
        size: u64,
        pci_addr: u64,
        mem: &GuestMemory,
        vecs: Vec<(GuestAddress, usize)>,
        args: Vec<u8>,
    ) -> GpuResponse {
        match self.resources.entry(resource_id) {
            Entry::Vacant(entry) => {
                let resource = match self.renderer.resource_create_v2(
                    resource_id,
                    guest_memory_type,
                    guest_caching_type,
                    size,
                    mem,
                    &vecs,
                    &args,
                ) {
                    Ok(resource) => resource,
                    Err(e) => {
                        error!("failed to create resource: {}", e);
                        return GpuResponse::ErrUnspec;
                    }
                };

                match guest_memory_type {
                    VIRTIO_GPU_MEMORY_HOST_COHERENT => {
                        let dma_buf_fd = match resource.export() {
                            Ok(export) => export.1,
                            Err(e) => {
                                error!("failed to export plane fd: {}", e);
                                return GpuResponse::ErrUnspec;
                            }
                        };

                        let request = VmMemoryRequest::RegisterMemoryAtAddress(
                            self.pci_bar,
                            MaybeOwnedFd::Borrowed(dma_buf_fd.as_raw_fd()),
                            size as usize,
                            pci_addr,
                        );

                        match self.gpu_device_socket.send(&request) {
                            Ok(_resq) => match self.gpu_device_socket.recv() {
                                Ok(response) => match response {
                                    VmMemoryResponse::RegisterMemory { pfn: _, slot } => {
                                        entry.insert(VirtioGpuResource::v2_new(slot, resource));
                                        GpuResponse::OkNoData
                                    }
                                    VmMemoryResponse::Err(e) => {
                                        error!("received an error: {}", e);
                                        GpuResponse::ErrUnspec
                                    }
                                    _ => {
                                        error!("recieved an unexpected response");
                                        GpuResponse::ErrUnspec
                                    }
                                },
                                Err(e) => {
                                    error!("failed to receive data: {}", e);
                                    GpuResponse::ErrUnspec
                                }
                            },
                            Err(e) => {
                                error!("failed to send request: {}", e);
                                GpuResponse::ErrUnspec
                            }
                        }
                    }
                    _ => {
                        entry.insert(VirtioGpuResource::new(
                            DEFAULT_WIDTH,
                            DEFAULT_HEIGHT,
                            resource,
                        ));

                        GpuResponse::OkNoData
                    }
                }
            }
            Entry::Occupied(_) => GpuResponse::ErrInvalidResourceId,
        }
    }

    pub fn resource_v2_unref(&mut self, resource_id: u32) -> GpuResponse {
        match self.resources.remove(&resource_id) {
            Some(entry) => match entry.kvm_slot {
                Some(kvm_slot) => {
                    let request = VmMemoryRequest::UnregisterMemory(kvm_slot);
                    match self.gpu_device_socket.send(&request) {
                        Ok(_resq) => match self.gpu_device_socket.recv() {
                            Ok(response) => match response {
                                VmMemoryResponse::Ok => GpuResponse::OkNoData,
                                VmMemoryResponse::Err(e) => {
                                    error!("received an error: {}", e);
                                    GpuResponse::ErrUnspec
                                }
                                _ => {
                                    error!("recieved an unexpected response");
                                    GpuResponse::ErrUnspec
                                }
                            },
                            Err(e) => {
                                error!("failed to receive data: {}", e);
                                GpuResponse::ErrUnspec
                            }
                        },
                        Err(e) => {
                            error!("failed to send request: {}", e);
                            GpuResponse::ErrUnspec
                        }
                    }
                }
                None => GpuResponse::OkNoData,
            },
            None => GpuResponse::ErrInvalidResourceId,
        }
    }
}