| // 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 data_model::*; |
| |
| 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}; |
| |
| use super::protocol::GpuResponse; |
| |
| 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, |
| 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, |
| _mem: &GuestMemory) { |
| let res = self.transfer_write(None, |
| 0, |
| 0, |
| 0, |
| Box3 { |
| x, |
| y, |
| z: 0, |
| w: width, |
| h: height, |
| d: 0, |
| }, |
| 0); |
| if let Err(e) = res { |
| error!("failed to write to resource (x={} y={} w={} h={}): {}", |
| x, |
| y, |
| width, |
| height, |
| 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, |
| } |
| |
| impl From<Buffer> for BackedBuffer { |
| fn from(buffer: Buffer) -> BackedBuffer { |
| BackedBuffer { |
| display_import: None, |
| backing: Vec::new(), |
| buffer, |
| } |
| } |
| } |
| |
| 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; |
| } |
| |
| fn detach_guest_backing(&mut self) { |
| self.backing.clear() |
| } |
| |
| 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, |
| mem: &GuestMemory) { |
| let res = self.buffer |
| .write_from_sg(x, |
| y, |
| width, |
| height, |
| 0, |
| self.backing |
| .iter() |
| .map(|&(addr, len)| { |
| mem.get_slice(addr.offset(), len as u64).unwrap() |
| })); |
| 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<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) |
| } |
| |
| /// 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); |
| } |
| } |
| |
| return 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, |
| mem: &GuestMemory) |
| -> GpuResponse { |
| match self.resources.get_mut(&id) { |
| Some(res) => { |
| res.write_from_guest_memory(x, y, width, height, 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 { |
| match index { |
| 0 => { |
| let id = 1; // VIRTIO_GPU_CAPSET_VIRGL |
| let (version, size) = self.renderer.get_cap_set_info(id); |
| GpuResponse::OkCapsetInfo { id, version, size } |
| } |
| _ => GpuResponse::ErrInvalidParameter, |
| } |
| } |
| |
| /// 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, |
| } |
| } |
| |
| /// 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; |
| } |
| 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 |
| } |
| } |
| } |
| } |
| } |
| |
| /// 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, |
| } |
| } |
| } |