Zach Reizner | 3a8100a | 2017-09-13 19:15:43 -0700 | [diff] [blame] | 1 | // Copyright 2018 The Chromium OS Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | //! Implementation for the transport agnostic virtio-gpu protocol, including display and rendering. |
| 6 | |
| 7 | use std::cell::RefCell; |
| 8 | use std::collections::btree_map::Entry; |
| 9 | use std::collections::BTreeMap as Map; |
| 10 | use std::os::unix::io::AsRawFd; |
| 11 | use std::rc::Rc; |
Zach Reizner | e9717c4 | 2018-06-14 17:58:37 -0700 | [diff] [blame] | 12 | use std::usize; |
Zach Reizner | 3a8100a | 2017-09-13 19:15:43 -0700 | [diff] [blame] | 13 | |
| 14 | use data_model::*; |
| 15 | |
Zach Reizner | aa57566 | 2018-08-15 10:46:32 -0700 | [diff] [blame] | 16 | use msg_socket::{MsgReceiver, MsgSender}; |
David Tolnay | 633426a | 2019-04-12 12:18:35 -0700 | [diff] [blame^] | 17 | use sys_util::{error, GuestAddress, GuestMemory}; |
Zach Reizner | 3a8100a | 2017-09-13 19:15:43 -0700 | [diff] [blame] | 18 | |
David Tolnay | 4c706a2 | 2019-04-12 11:48:03 -0700 | [diff] [blame] | 19 | use gpu_buffer::{Buffer, Device, Flags, Format}; |
| 20 | use gpu_display::*; |
| 21 | use gpu_renderer::{ |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 22 | format_fourcc as renderer_fourcc, Box3, Context as RendererContext, Image as RendererImage, |
| 23 | Renderer, Resource as GpuRendererResource, ResourceCreateArgs, |
| 24 | }; |
Zach Reizner | 3a8100a | 2017-09-13 19:15:43 -0700 | [diff] [blame] | 25 | |
Zach Reizner | c589929 | 2018-09-05 10:23:46 -0700 | [diff] [blame] | 26 | use super::protocol::{ |
| 27 | GpuResponse, GpuResponsePlaneInfo, VIRTIO_GPU_CAPSET_VIRGL, VIRTIO_GPU_CAPSET_VIRGL2, |
| 28 | }; |
David Tolnay | dc63ca9 | 2019-04-12 11:54:28 -0700 | [diff] [blame] | 29 | use crate::virtio::resource_bridge::*; |
Zach Reizner | 3a8100a | 2017-09-13 19:15:43 -0700 | [diff] [blame] | 30 | |
| 31 | const DEFAULT_WIDTH: u32 = 1280; |
| 32 | const DEFAULT_HEIGHT: u32 = 1024; |
| 33 | |
| 34 | /// Trait for virtio-gpu resources allocated by the guest. |
| 35 | trait VirglResource { |
| 36 | /// The width in pixels of this resource. |
| 37 | fn width(&self) -> u32; |
| 38 | |
| 39 | /// The height in pixels of this resource. |
| 40 | fn height(&self) -> u32; |
| 41 | |
| 42 | /// Associates the backing for this resource with the given guest memory. |
| 43 | fn attach_guest_backing(&mut self, mem: &GuestMemory, vecs: Vec<(GuestAddress, usize)>); |
| 44 | |
| 45 | /// Removes associated memory for this resource previously made with `attach_guest_backing`. |
| 46 | fn detach_guest_backing(&mut self); |
| 47 | |
| 48 | /// Returns the GPU `Buffer` for this resource, if it has one. |
| 49 | fn buffer(&self) -> Option<&Buffer> { |
| 50 | None |
| 51 | } |
| 52 | |
| 53 | /// Returns the renderer's concrete `GpuRendererResource` for this resource, if it has one. |
| 54 | fn gpu_renderer_resource(&mut self) -> Option<&mut GpuRendererResource> { |
| 55 | None |
| 56 | } |
| 57 | |
| 58 | /// Returns an import ID for this resource onto the given display, if successful. |
| 59 | fn import_to_display(&mut self, _display: &Rc<RefCell<GpuDisplay>>) -> Option<u32> { |
| 60 | None |
| 61 | } |
| 62 | |
| 63 | /// Copies the given rectangle of pixels from guest memory, using the backing specified from a |
| 64 | /// call to `attach_guest_backing`. |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 65 | fn write_from_guest_memory( |
| 66 | &mut self, |
| 67 | x: u32, |
| 68 | y: u32, |
| 69 | width: u32, |
| 70 | height: u32, |
| 71 | src_offset: u64, |
| 72 | mem: &GuestMemory, |
| 73 | ); |
Zach Reizner | 3a8100a | 2017-09-13 19:15:43 -0700 | [diff] [blame] | 74 | |
| 75 | /// Reads from the given rectangle of pixels in the resource to the `dst` slice of memory. |
| 76 | fn read_to_volatile(&mut self, x: u32, y: u32, width: u32, height: u32, dst: VolatileSlice); |
| 77 | } |
| 78 | |
| 79 | impl VirglResource for GpuRendererResource { |
| 80 | fn width(&self) -> u32 { |
| 81 | match self.get_info() { |
| 82 | Ok(info) => info.width, |
| 83 | Err(_) => 0, |
| 84 | } |
| 85 | } |
| 86 | fn height(&self) -> u32 { |
| 87 | match self.get_info() { |
| 88 | Ok(info) => info.height, |
| 89 | Err(_) => 0, |
| 90 | } |
| 91 | } |
| 92 | |
| 93 | fn attach_guest_backing(&mut self, mem: &GuestMemory, vecs: Vec<(GuestAddress, usize)>) { |
| 94 | if let Err(e) = self.attach_backing(&vecs[..], mem) { |
| 95 | error!("failed to attach backing to resource: {}", e); |
| 96 | } |
| 97 | } |
| 98 | |
| 99 | fn detach_guest_backing(&mut self) { |
| 100 | self.detach_backing(); |
| 101 | } |
| 102 | |
| 103 | fn gpu_renderer_resource(&mut self) -> Option<&mut GpuRendererResource> { |
| 104 | Some(self) |
| 105 | } |
| 106 | |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 107 | fn write_from_guest_memory( |
| 108 | &mut self, |
| 109 | x: u32, |
| 110 | y: u32, |
| 111 | width: u32, |
| 112 | height: u32, |
| 113 | src_offset: u64, |
| 114 | _mem: &GuestMemory, |
| 115 | ) { |
| 116 | let res = self.transfer_write( |
| 117 | None, |
| 118 | 0, |
| 119 | 0, |
| 120 | 0, |
| 121 | Box3 { |
| 122 | x, |
| 123 | y, |
| 124 | z: 0, |
| 125 | w: width, |
| 126 | h: height, |
| 127 | d: 0, |
| 128 | }, |
| 129 | src_offset, |
| 130 | ); |
Zach Reizner | 3a8100a | 2017-09-13 19:15:43 -0700 | [diff] [blame] | 131 | if let Err(e) = res { |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 132 | error!( |
| 133 | "failed to write to resource (x={} y={} w={} h={}, src_offset={}): {}", |
| 134 | x, y, width, height, src_offset, e |
| 135 | ); |
Zach Reizner | 3a8100a | 2017-09-13 19:15:43 -0700 | [diff] [blame] | 136 | } |
| 137 | } |
| 138 | |
| 139 | fn read_to_volatile(&mut self, x: u32, y: u32, width: u32, height: u32, dst: VolatileSlice) { |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 140 | let res = GpuRendererResource::read_to_volatile( |
| 141 | self, |
| 142 | None, |
| 143 | 0, |
| 144 | 0, |
| 145 | 0, |
| 146 | Box3 { |
| 147 | x, |
| 148 | y, |
| 149 | z: 0, |
| 150 | w: width, |
| 151 | h: height, |
| 152 | d: 0, |
| 153 | }, |
| 154 | 0, |
| 155 | dst, |
| 156 | ); |
Zach Reizner | 3a8100a | 2017-09-13 19:15:43 -0700 | [diff] [blame] | 157 | if let Err(e) = res { |
| 158 | error!("failed to read from resource: {}", e); |
| 159 | } |
| 160 | } |
| 161 | } |
| 162 | |
| 163 | /// A buffer backed with a `gpu_buffer::Buffer`. |
| 164 | struct BackedBuffer { |
| 165 | display_import: Option<(Rc<RefCell<GpuDisplay>>, u32)>, |
| 166 | backing: Vec<(GuestAddress, usize)>, |
| 167 | buffer: Buffer, |
David Riley | af9d7ed | 2018-05-22 15:37:22 -0700 | [diff] [blame] | 168 | gpu_renderer_resource: Option<GpuRendererResource>, |
David Riley | ba7c603 | 2018-05-17 17:08:16 -0700 | [diff] [blame] | 169 | _image: Option<RendererImage>, |
David Riley | af9d7ed | 2018-05-22 15:37:22 -0700 | [diff] [blame] | 170 | } |
| 171 | |
| 172 | impl BackedBuffer { |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 173 | fn new_renderer_registered( |
| 174 | buffer: Buffer, |
| 175 | gpu_renderer_resource: GpuRendererResource, |
| 176 | image: RendererImage, |
| 177 | ) -> BackedBuffer { |
David Riley | af9d7ed | 2018-05-22 15:37:22 -0700 | [diff] [blame] | 178 | BackedBuffer { |
| 179 | display_import: None, |
| 180 | backing: Vec::new(), |
| 181 | buffer, |
| 182 | gpu_renderer_resource: Some(gpu_renderer_resource), |
David Riley | ba7c603 | 2018-05-17 17:08:16 -0700 | [diff] [blame] | 183 | _image: Some(image), |
David Riley | af9d7ed | 2018-05-22 15:37:22 -0700 | [diff] [blame] | 184 | } |
| 185 | } |
Zach Reizner | 3a8100a | 2017-09-13 19:15:43 -0700 | [diff] [blame] | 186 | } |
| 187 | |
| 188 | impl From<Buffer> for BackedBuffer { |
| 189 | fn from(buffer: Buffer) -> BackedBuffer { |
| 190 | BackedBuffer { |
| 191 | display_import: None, |
| 192 | backing: Vec::new(), |
| 193 | buffer, |
David Riley | af9d7ed | 2018-05-22 15:37:22 -0700 | [diff] [blame] | 194 | gpu_renderer_resource: None, |
David Riley | ba7c603 | 2018-05-17 17:08:16 -0700 | [diff] [blame] | 195 | _image: None, |
Zach Reizner | 3a8100a | 2017-09-13 19:15:43 -0700 | [diff] [blame] | 196 | } |
| 197 | } |
| 198 | } |
| 199 | |
| 200 | impl VirglResource for BackedBuffer { |
| 201 | fn width(&self) -> u32 { |
| 202 | self.buffer.width() |
| 203 | } |
| 204 | |
| 205 | fn height(&self) -> u32 { |
| 206 | self.buffer.height() |
| 207 | } |
| 208 | |
David Riley | 64cc43d | 2018-10-17 16:01:48 -0700 | [diff] [blame] | 209 | fn attach_guest_backing(&mut self, mem: &GuestMemory, vecs: Vec<(GuestAddress, usize)>) { |
| 210 | self.backing = vecs.clone(); |
| 211 | if let Some(ref mut resource) = self.gpu_renderer_resource { |
| 212 | if let Err(e) = resource.attach_backing(&vecs[..], mem) { |
| 213 | error!("failed to attach backing to BackBuffer resource: {}", e); |
| 214 | } |
| 215 | } |
Zach Reizner | 3a8100a | 2017-09-13 19:15:43 -0700 | [diff] [blame] | 216 | } |
| 217 | |
| 218 | fn detach_guest_backing(&mut self) { |
David Riley | 64cc43d | 2018-10-17 16:01:48 -0700 | [diff] [blame] | 219 | if let Some(ref mut resource) = self.gpu_renderer_resource { |
| 220 | resource.detach_backing(); |
| 221 | } |
| 222 | self.backing.clear(); |
Zach Reizner | 3a8100a | 2017-09-13 19:15:43 -0700 | [diff] [blame] | 223 | } |
| 224 | |
David Riley | af9d7ed | 2018-05-22 15:37:22 -0700 | [diff] [blame] | 225 | fn gpu_renderer_resource(&mut self) -> Option<&mut GpuRendererResource> { |
| 226 | self.gpu_renderer_resource.as_mut() |
| 227 | } |
| 228 | |
Zach Reizner | 3a8100a | 2017-09-13 19:15:43 -0700 | [diff] [blame] | 229 | fn buffer(&self) -> Option<&Buffer> { |
| 230 | Some(&self.buffer) |
| 231 | } |
| 232 | |
| 233 | fn import_to_display(&mut self, display: &Rc<RefCell<GpuDisplay>>) -> Option<u32> { |
| 234 | if let Some((ref self_display, import)) = self.display_import { |
| 235 | if Rc::ptr_eq(&self_display, display) { |
| 236 | return Some(import); |
| 237 | } |
| 238 | } |
| 239 | let dmabuf = match self.buffer.export_plane_fd(0) { |
| 240 | Ok(dmabuf) => dmabuf, |
| 241 | Err(e) => { |
| 242 | error!("failed to get dmabuf for scanout: {}", e); |
| 243 | return None; |
| 244 | } |
| 245 | }; |
| 246 | |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 247 | match display.borrow_mut().import_dmabuf( |
| 248 | dmabuf.as_raw_fd(), |
| 249 | 0, /* offset */ |
| 250 | self.buffer.stride(), |
| 251 | self.buffer.format_modifier(), |
| 252 | self.buffer.width(), |
| 253 | self.buffer.height(), |
| 254 | self.buffer.format().into(), |
| 255 | ) { |
Zach Reizner | 3a8100a | 2017-09-13 19:15:43 -0700 | [diff] [blame] | 256 | Ok(import_id) => { |
| 257 | self.display_import = Some((display.clone(), import_id)); |
| 258 | Some(import_id) |
| 259 | } |
| 260 | Err(e) => { |
David Tolnay | b4bd00f | 2019-02-12 17:51:26 -0800 | [diff] [blame] | 261 | error!("failed to import dmabuf for display: {}", e); |
Zach Reizner | 3a8100a | 2017-09-13 19:15:43 -0700 | [diff] [blame] | 262 | None |
| 263 | } |
| 264 | } |
| 265 | } |
| 266 | |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 267 | fn write_from_guest_memory( |
| 268 | &mut self, |
| 269 | x: u32, |
| 270 | y: u32, |
| 271 | width: u32, |
| 272 | height: u32, |
| 273 | src_offset: u64, |
| 274 | mem: &GuestMemory, |
| 275 | ) { |
Zach Reizner | e9717c4 | 2018-06-14 17:58:37 -0700 | [diff] [blame] | 276 | if src_offset >= usize::MAX as u64 { |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 277 | error!( |
| 278 | "failed to write to resource with given offset: {}", |
| 279 | src_offset |
| 280 | ); |
| 281 | return; |
Zach Reizner | e9717c4 | 2018-06-14 17:58:37 -0700 | [diff] [blame] | 282 | } |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 283 | let res = self.buffer.write_from_sg( |
| 284 | x, |
| 285 | y, |
| 286 | width, |
| 287 | height, |
| 288 | 0, // plane |
| 289 | src_offset as usize, |
| 290 | self.backing |
| 291 | .iter() |
| 292 | .map(|&(addr, len)| mem.get_slice(addr.offset(), len as u64).unwrap_or_default()), |
| 293 | ); |
Zach Reizner | 3a8100a | 2017-09-13 19:15:43 -0700 | [diff] [blame] | 294 | if let Err(e) = res { |
David Tolnay | b4bd00f | 2019-02-12 17:51:26 -0800 | [diff] [blame] | 295 | error!("failed to write to resource from guest memory: {}", e) |
Zach Reizner | 3a8100a | 2017-09-13 19:15:43 -0700 | [diff] [blame] | 296 | } |
| 297 | } |
| 298 | |
| 299 | fn read_to_volatile(&mut self, x: u32, y: u32, width: u32, height: u32, dst: VolatileSlice) { |
| 300 | if let Err(e) = self.buffer.read_to_volatile(x, y, width, height, 0, dst) { |
David Tolnay | b4bd00f | 2019-02-12 17:51:26 -0800 | [diff] [blame] | 301 | error!("failed to copy resource: {}", e); |
Zach Reizner | 3a8100a | 2017-09-13 19:15:43 -0700 | [diff] [blame] | 302 | } |
| 303 | } |
| 304 | } |
| 305 | |
| 306 | /// The virtio-gpu backend state tracker. |
| 307 | /// |
| 308 | /// Commands from the virtio-gpu protocol can be submitted here using the methods, and they will be |
| 309 | /// realized on the hardware. Most methods return a `GpuResponse` that indicate the success, |
| 310 | /// failure, or requested data for the given command. |
| 311 | pub struct Backend { |
| 312 | display: Rc<RefCell<GpuDisplay>>, |
| 313 | device: Device, |
| 314 | renderer: Renderer, |
David Tolnay | fdac5ed | 2019-03-08 16:56:14 -0800 | [diff] [blame] | 315 | resources: Map<u32, Box<dyn VirglResource>>, |
Zach Reizner | 3a8100a | 2017-09-13 19:15:43 -0700 | [diff] [blame] | 316 | contexts: Map<u32, RendererContext>, |
| 317 | scanout_surface: Option<u32>, |
| 318 | cursor_surface: Option<u32>, |
| 319 | scanout_resource: u32, |
| 320 | cursor_resource: u32, |
| 321 | } |
| 322 | |
| 323 | impl Backend { |
| 324 | /// Creates a new backend for virtio-gpu that realizes all commands using the given `device` for |
| 325 | /// allocating buffers, `display` for showing the results, and `renderer` for submitting |
| 326 | /// rendering commands. |
| 327 | pub fn new(device: Device, display: GpuDisplay, renderer: Renderer) -> Backend { |
| 328 | Backend { |
| 329 | display: Rc::new(RefCell::new(display)), |
| 330 | device, |
| 331 | renderer, |
| 332 | resources: Default::default(), |
| 333 | contexts: Default::default(), |
| 334 | scanout_surface: None, |
| 335 | cursor_surface: None, |
| 336 | scanout_resource: 0, |
| 337 | cursor_resource: 0, |
| 338 | } |
| 339 | } |
| 340 | |
| 341 | /// Gets a reference to the display passed into `new`. |
| 342 | pub fn display(&self) -> &Rc<RefCell<GpuDisplay>> { |
| 343 | &self.display |
| 344 | } |
| 345 | |
| 346 | /// Processes the internal `display` events and returns `true` if the main display was closed. |
| 347 | pub fn process_display(&mut self) -> bool { |
| 348 | let mut display = self.display.borrow_mut(); |
| 349 | display.dispatch_events(); |
| 350 | self.scanout_surface |
| 351 | .map(|s| display.close_requested(s)) |
| 352 | .unwrap_or(false) |
| 353 | } |
| 354 | |
Zach Reizner | aa57566 | 2018-08-15 10:46:32 -0700 | [diff] [blame] | 355 | pub fn process_resource_bridge(&self, resource_bridge: &ResourceResponseSocket) { |
| 356 | let request = match resource_bridge.recv() { |
| 357 | Ok(msg) => msg, |
| 358 | Err(e) => { |
David Tolnay | b4bd00f | 2019-02-12 17:51:26 -0800 | [diff] [blame] | 359 | error!("error receiving resource bridge request: {}", e); |
Zach Reizner | aa57566 | 2018-08-15 10:46:32 -0700 | [diff] [blame] | 360 | return; |
| 361 | } |
| 362 | }; |
| 363 | |
| 364 | let response = match request { |
| 365 | ResourceRequest::GetResource { id } => self |
| 366 | .resources |
| 367 | .get(&id) |
| 368 | .and_then(|resource| resource.buffer()) |
| 369 | .and_then(|buffer| buffer.export_plane_fd(0).ok()) |
| 370 | .map(|fd| ResourceResponse::Resource(fd)) |
| 371 | .unwrap_or(ResourceResponse::Invalid), |
| 372 | }; |
| 373 | |
| 374 | if let Err(e) = resource_bridge.send(&response) { |
David Tolnay | b4bd00f | 2019-02-12 17:51:26 -0800 | [diff] [blame] | 375 | error!("error sending resource bridge request: {}", e); |
Zach Reizner | aa57566 | 2018-08-15 10:46:32 -0700 | [diff] [blame] | 376 | } |
| 377 | } |
| 378 | |
Zach Reizner | 3a8100a | 2017-09-13 19:15:43 -0700 | [diff] [blame] | 379 | /// Gets the list of supported display resolutions as a slice of `(width, height)` tuples. |
| 380 | pub fn display_info(&self) -> &[(u32, u32)] { |
| 381 | &[(DEFAULT_WIDTH, DEFAULT_HEIGHT)] |
| 382 | } |
| 383 | |
| 384 | /// Creates a 2D resource with the given properties and associated it with the given id. |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 385 | pub fn create_resource_2d( |
| 386 | &mut self, |
| 387 | id: u32, |
| 388 | width: u32, |
| 389 | height: u32, |
| 390 | fourcc: u32, |
| 391 | ) -> GpuResponse { |
Zach Reizner | 3a8100a | 2017-09-13 19:15:43 -0700 | [diff] [blame] | 392 | if id == 0 { |
| 393 | return GpuResponse::ErrInvalidResourceId; |
| 394 | } |
| 395 | match self.resources.entry(id) { |
| 396 | Entry::Vacant(slot) => { |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 397 | let res = self.device.create_buffer( |
| 398 | width, |
| 399 | height, |
| 400 | Format::from(fourcc), |
| 401 | Flags::empty().use_scanout(true).use_linear(true), |
| 402 | ); |
Zach Reizner | 3a8100a | 2017-09-13 19:15:43 -0700 | [diff] [blame] | 403 | match res { |
| 404 | Ok(res) => { |
| 405 | slot.insert(Box::from(BackedBuffer::from(res))); |
| 406 | GpuResponse::OkNoData |
| 407 | } |
| 408 | Err(_) => { |
| 409 | error!("failed to create renderer resource {}", fourcc); |
| 410 | GpuResponse::ErrUnspec |
| 411 | } |
| 412 | } |
| 413 | } |
| 414 | Entry::Occupied(_) => GpuResponse::ErrInvalidResourceId, |
| 415 | } |
| 416 | } |
| 417 | |
| 418 | /// Removes the guest's reference count for the given resource id. |
| 419 | pub fn unref_resource(&mut self, id: u32) -> GpuResponse { |
| 420 | match self.resources.remove(&id) { |
| 421 | Some(_) => GpuResponse::OkNoData, |
| 422 | None => GpuResponse::ErrInvalidResourceId, |
| 423 | } |
| 424 | } |
| 425 | |
| 426 | /// Sets the given resource id as the source of scanout to the display. |
| 427 | pub fn set_scanout(&mut self, id: u32) -> GpuResponse { |
| 428 | let mut display = self.display.borrow_mut(); |
| 429 | if id == 0 { |
| 430 | if let Some(surface) = self.scanout_surface.take() { |
| 431 | display.release_surface(surface); |
| 432 | } |
| 433 | self.scanout_resource = 0; |
| 434 | if let Some(surface) = self.cursor_surface.take() { |
| 435 | display.release_surface(surface); |
| 436 | } |
| 437 | self.cursor_resource = 0; |
| 438 | GpuResponse::OkNoData |
| 439 | } else if self.resources.get_mut(&id).is_some() { |
| 440 | self.scanout_resource = id; |
| 441 | |
| 442 | if self.scanout_surface.is_none() { |
| 443 | match display.create_surface(None, DEFAULT_WIDTH, DEFAULT_HEIGHT) { |
| 444 | Ok(surface) => self.scanout_surface = Some(surface), |
David Tolnay | b4bd00f | 2019-02-12 17:51:26 -0800 | [diff] [blame] | 445 | Err(e) => error!("failed to create display surface: {}", e), |
Zach Reizner | 3a8100a | 2017-09-13 19:15:43 -0700 | [diff] [blame] | 446 | } |
| 447 | } |
| 448 | GpuResponse::OkNoData |
| 449 | } else { |
| 450 | GpuResponse::ErrInvalidResourceId |
| 451 | } |
| 452 | } |
| 453 | |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 454 | fn flush_resource_to_surface( |
| 455 | &mut self, |
| 456 | resource_id: u32, |
| 457 | surface_id: u32, |
| 458 | x: u32, |
| 459 | y: u32, |
| 460 | width: u32, |
| 461 | height: u32, |
| 462 | ) -> GpuResponse { |
Zach Reizner | 3a8100a | 2017-09-13 19:15:43 -0700 | [diff] [blame] | 463 | let resource = match self.resources.get_mut(&resource_id) { |
| 464 | Some(r) => r, |
| 465 | None => return GpuResponse::ErrInvalidResourceId, |
| 466 | }; |
| 467 | |
| 468 | if let Some(import_id) = resource.import_to_display(&self.display) { |
| 469 | self.display.borrow_mut().flip_to(surface_id, import_id); |
| 470 | return GpuResponse::OkNoData; |
| 471 | } |
| 472 | |
| 473 | // Import failed, fall back to a copy. |
| 474 | let display = self.display.borrow_mut(); |
| 475 | // Prevent overwriting a buffer that is currently being used by the compositor. |
| 476 | if display.next_buffer_in_use(surface_id) { |
| 477 | return GpuResponse::OkNoData; |
| 478 | } |
| 479 | let fb = match display.framebuffer_memory(surface_id) { |
| 480 | Some(fb) => fb, |
| 481 | None => { |
| 482 | error!("failed to access framebuffer for surface {}", surface_id); |
| 483 | return GpuResponse::ErrUnspec; |
| 484 | } |
| 485 | }; |
| 486 | |
| 487 | resource.read_to_volatile(x, y, width, height, fb); |
| 488 | display.flip(surface_id); |
| 489 | |
| 490 | GpuResponse::OkNoData |
| 491 | } |
| 492 | |
| 493 | /// Flushes the given rectangle of pixels of the given resource to the display. |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 494 | pub fn flush_resource( |
| 495 | &mut self, |
| 496 | id: u32, |
| 497 | x: u32, |
| 498 | y: u32, |
| 499 | width: u32, |
| 500 | height: u32, |
| 501 | ) -> GpuResponse { |
Zach Reizner | 3a8100a | 2017-09-13 19:15:43 -0700 | [diff] [blame] | 502 | if id == 0 { |
| 503 | return GpuResponse::OkNoData; |
| 504 | } |
| 505 | |
| 506 | let mut response = GpuResponse::OkNoData; |
| 507 | |
| 508 | if id == self.scanout_resource { |
| 509 | if let Some(surface_id) = self.scanout_surface { |
| 510 | response = self.flush_resource_to_surface(id, surface_id, x, y, width, height); |
| 511 | } |
| 512 | } |
| 513 | |
| 514 | if response != GpuResponse::OkNoData { |
| 515 | return response; |
| 516 | } |
| 517 | |
| 518 | if id == self.cursor_resource { |
| 519 | if let Some(surface_id) = self.cursor_surface { |
| 520 | response = self.flush_resource_to_surface(id, surface_id, x, y, width, height); |
| 521 | } |
| 522 | } |
| 523 | |
David Tolnay | 5bbbf61 | 2018-12-01 17:49:30 -0800 | [diff] [blame] | 524 | response |
Zach Reizner | 3a8100a | 2017-09-13 19:15:43 -0700 | [diff] [blame] | 525 | } |
| 526 | |
| 527 | /// Copes the given rectangle of pixels of the given resource's backing memory to the host side |
| 528 | /// resource. |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 529 | pub fn transfer_to_resource_2d( |
| 530 | &mut self, |
| 531 | id: u32, |
| 532 | x: u32, |
| 533 | y: u32, |
| 534 | width: u32, |
| 535 | height: u32, |
| 536 | src_offset: u64, |
| 537 | mem: &GuestMemory, |
| 538 | ) -> GpuResponse { |
Zach Reizner | 3a8100a | 2017-09-13 19:15:43 -0700 | [diff] [blame] | 539 | match self.resources.get_mut(&id) { |
| 540 | Some(res) => { |
Zach Reizner | e9717c4 | 2018-06-14 17:58:37 -0700 | [diff] [blame] | 541 | res.write_from_guest_memory(x, y, width, height, src_offset, mem); |
Zach Reizner | 3a8100a | 2017-09-13 19:15:43 -0700 | [diff] [blame] | 542 | GpuResponse::OkNoData |
| 543 | } |
| 544 | None => GpuResponse::ErrInvalidResourceId, |
| 545 | } |
| 546 | } |
| 547 | |
| 548 | /// Attaches backing memory to the given resource, represented by a `Vec` of `(address, size)` |
| 549 | /// tuples in the guest's physical address space. |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 550 | pub fn attach_backing( |
| 551 | &mut self, |
| 552 | id: u32, |
| 553 | mem: &GuestMemory, |
| 554 | vecs: Vec<(GuestAddress, usize)>, |
| 555 | ) -> GpuResponse { |
Zach Reizner | 3a8100a | 2017-09-13 19:15:43 -0700 | [diff] [blame] | 556 | match self.resources.get_mut(&id) { |
| 557 | Some(resource) => { |
| 558 | resource.attach_guest_backing(mem, vecs); |
| 559 | GpuResponse::OkNoData |
| 560 | } |
| 561 | None => GpuResponse::ErrInvalidResourceId, |
| 562 | } |
| 563 | } |
| 564 | |
| 565 | /// Detaches any backing memory from the given resource, if there is any. |
| 566 | pub fn detach_backing(&mut self, id: u32) -> GpuResponse { |
| 567 | match self.resources.get_mut(&id) { |
| 568 | Some(resource) => { |
| 569 | resource.detach_guest_backing(); |
| 570 | GpuResponse::OkNoData |
| 571 | } |
| 572 | None => GpuResponse::ErrInvalidResourceId, |
| 573 | } |
| 574 | } |
| 575 | |
| 576 | /// Updates the cursor's memory to the given id, and sets its position to the given coordinates. |
| 577 | pub fn update_cursor(&mut self, id: u32, x: u32, y: u32) -> GpuResponse { |
| 578 | if id == 0 { |
| 579 | if let Some(surface) = self.cursor_surface.take() { |
| 580 | self.display.borrow_mut().release_surface(surface); |
| 581 | } |
| 582 | self.cursor_resource = 0; |
| 583 | GpuResponse::OkNoData |
| 584 | } else if let Some(resource) = self.resources.get_mut(&id) { |
| 585 | self.cursor_resource = id; |
| 586 | if self.cursor_surface.is_none() { |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 587 | match self.display.borrow_mut().create_surface( |
| 588 | self.scanout_surface, |
| 589 | resource.width(), |
| 590 | resource.height(), |
| 591 | ) { |
Zach Reizner | 3a8100a | 2017-09-13 19:15:43 -0700 | [diff] [blame] | 592 | Ok(surface) => self.cursor_surface = Some(surface), |
| 593 | Err(e) => { |
David Tolnay | b4bd00f | 2019-02-12 17:51:26 -0800 | [diff] [blame] | 594 | error!("failed to create cursor surface: {}", e); |
Zach Reizner | 3a8100a | 2017-09-13 19:15:43 -0700 | [diff] [blame] | 595 | return GpuResponse::ErrUnspec; |
| 596 | } |
| 597 | } |
| 598 | } |
| 599 | |
| 600 | let cursor_surface = self.cursor_surface.unwrap(); |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 601 | self.display.borrow_mut().set_position(cursor_surface, x, y); |
Zach Reizner | 3a8100a | 2017-09-13 19:15:43 -0700 | [diff] [blame] | 602 | |
| 603 | // Gets the resource's pixels into the display by importing the buffer. |
| 604 | if let Some(import_id) = resource.import_to_display(&self.display) { |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 605 | self.display.borrow_mut().flip_to(cursor_surface, import_id); |
Zach Reizner | 3a8100a | 2017-09-13 19:15:43 -0700 | [diff] [blame] | 606 | return GpuResponse::OkNoData; |
| 607 | } |
| 608 | |
| 609 | // Importing failed, so try copying the pixels into the surface's slower shared memory |
| 610 | // framebuffer. |
| 611 | if let Some(buffer) = resource.buffer() { |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 612 | if let Some(fb) = self.display.borrow_mut().framebuffer_memory(cursor_surface) { |
| 613 | if let Err(e) = |
| 614 | buffer.read_to_volatile(0, 0, buffer.width(), buffer.height(), 0, fb) |
| 615 | { |
David Tolnay | b4bd00f | 2019-02-12 17:51:26 -0800 | [diff] [blame] | 616 | error!("failed to copy resource to cursor: {}", e); |
Zach Reizner | 3a8100a | 2017-09-13 19:15:43 -0700 | [diff] [blame] | 617 | return GpuResponse::ErrInvalidParameter; |
| 618 | } |
| 619 | } |
| 620 | self.display.borrow_mut().flip(cursor_surface); |
| 621 | } |
| 622 | GpuResponse::OkNoData |
| 623 | } else { |
| 624 | GpuResponse::ErrInvalidResourceId |
| 625 | } |
| 626 | } |
| 627 | |
| 628 | /// Moves the cursor's position to the given coordinates. |
| 629 | pub fn move_cursor(&mut self, x: u32, y: u32) -> GpuResponse { |
| 630 | if let Some(cursor_surface) = self.cursor_surface { |
| 631 | if let Some(scanout_surface) = self.scanout_surface { |
| 632 | let display = self.display.borrow_mut(); |
| 633 | display.set_position(cursor_surface, x, y); |
| 634 | display.commit(scanout_surface); |
| 635 | } |
| 636 | } |
| 637 | GpuResponse::OkNoData |
| 638 | } |
| 639 | |
| 640 | /// Gets the renderer's capset information associated with `index`. |
| 641 | pub fn get_capset_info(&self, index: u32) -> GpuResponse { |
Gurchetan Singh | 046df60 | 2018-10-02 16:07:26 -0700 | [diff] [blame] | 642 | let id = match index { |
| 643 | 0 => VIRTIO_GPU_CAPSET_VIRGL, |
| 644 | 1 => VIRTIO_GPU_CAPSET_VIRGL2, |
| 645 | _ => return GpuResponse::ErrInvalidParameter, |
| 646 | }; |
| 647 | let (version, size) = self.renderer.get_cap_set_info(id); |
| 648 | GpuResponse::OkCapsetInfo { id, version, size } |
Zach Reizner | 3a8100a | 2017-09-13 19:15:43 -0700 | [diff] [blame] | 649 | } |
| 650 | |
| 651 | /// Gets the capset of `version` associated with `id`. |
| 652 | pub fn get_capset(&self, id: u32, version: u32) -> GpuResponse { |
| 653 | GpuResponse::OkCapset(self.renderer.get_cap_set(id, version)) |
| 654 | } |
| 655 | |
| 656 | /// Creates a fresh renderer context with the given `id`. |
| 657 | pub fn create_renderer_context(&mut self, id: u32) -> GpuResponse { |
| 658 | if id == 0 { |
| 659 | return GpuResponse::ErrInvalidContextId; |
| 660 | } |
| 661 | match self.contexts.entry(id) { |
| 662 | Entry::Occupied(_) => GpuResponse::ErrInvalidContextId, |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 663 | Entry::Vacant(slot) => match self.renderer.create_context(id) { |
| 664 | Ok(ctx) => { |
| 665 | slot.insert(ctx); |
| 666 | GpuResponse::OkNoData |
Zach Reizner | 3a8100a | 2017-09-13 19:15:43 -0700 | [diff] [blame] | 667 | } |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 668 | Err(e) => { |
| 669 | error!("failed to create renderer ctx: {}", e); |
| 670 | GpuResponse::ErrUnspec |
| 671 | } |
| 672 | }, |
Zach Reizner | 3a8100a | 2017-09-13 19:15:43 -0700 | [diff] [blame] | 673 | } |
| 674 | } |
| 675 | |
| 676 | /// Destorys the renderer context associated with `id`. |
| 677 | pub fn destroy_renderer_context(&mut self, id: u32) -> GpuResponse { |
| 678 | match self.contexts.remove(&id) { |
| 679 | Some(_) => GpuResponse::OkNoData, |
| 680 | None => GpuResponse::ErrInvalidContextId, |
| 681 | } |
| 682 | } |
| 683 | |
| 684 | /// Attaches the indicated resource to the given context. |
| 685 | pub fn context_attach_resource(&mut self, ctx_id: u32, res_id: u32) -> GpuResponse { |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 686 | match ( |
| 687 | self.contexts.get_mut(&ctx_id), |
| 688 | self.resources |
| 689 | .get_mut(&res_id) |
| 690 | .and_then(|res| res.gpu_renderer_resource()), |
| 691 | ) { |
Zach Reizner | 3a8100a | 2017-09-13 19:15:43 -0700 | [diff] [blame] | 692 | (Some(ctx), Some(res)) => { |
| 693 | ctx.attach(res); |
| 694 | GpuResponse::OkNoData |
| 695 | } |
| 696 | (None, _) => GpuResponse::ErrInvalidContextId, |
| 697 | (_, None) => GpuResponse::ErrInvalidResourceId, |
| 698 | } |
| 699 | } |
| 700 | |
| 701 | /// detaches the indicated resource to the given context. |
| 702 | pub fn context_detach_resource(&mut self, ctx_id: u32, res_id: u32) -> GpuResponse { |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 703 | match ( |
| 704 | self.contexts.get_mut(&ctx_id), |
| 705 | self.resources |
| 706 | .get_mut(&res_id) |
| 707 | .and_then(|res| res.gpu_renderer_resource()), |
| 708 | ) { |
Zach Reizner | 3a8100a | 2017-09-13 19:15:43 -0700 | [diff] [blame] | 709 | (Some(ctx), Some(res)) => { |
| 710 | ctx.detach(res); |
| 711 | GpuResponse::OkNoData |
| 712 | } |
| 713 | (None, _) => GpuResponse::ErrInvalidContextId, |
| 714 | (_, None) => GpuResponse::ErrInvalidResourceId, |
| 715 | } |
| 716 | } |
| 717 | |
David Riley | af9d7ed | 2018-05-22 15:37:22 -0700 | [diff] [blame] | 718 | pub fn validate_args_as_fourcc(args: ResourceCreateArgs) -> Option<u32> { |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 719 | if args.depth == 1 && args.array_size == 1 && args.last_level == 0 && args.nr_samples == 0 { |
David Riley | af9d7ed | 2018-05-22 15:37:22 -0700 | [diff] [blame] | 720 | renderer_fourcc(args.format) |
| 721 | } else { |
| 722 | None |
| 723 | } |
| 724 | } |
| 725 | |
Zach Reizner | 3a8100a | 2017-09-13 19:15:43 -0700 | [diff] [blame] | 726 | /// Creates a 3D resource with the given properties and associated it with the given id. |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 727 | pub fn resource_create_3d( |
| 728 | &mut self, |
| 729 | id: u32, |
| 730 | target: u32, |
| 731 | format: u32, |
| 732 | bind: u32, |
| 733 | width: u32, |
| 734 | height: u32, |
| 735 | depth: u32, |
| 736 | array_size: u32, |
| 737 | last_level: u32, |
| 738 | nr_samples: u32, |
| 739 | flags: u32, |
| 740 | ) -> GpuResponse { |
Zach Reizner | 3a8100a | 2017-09-13 19:15:43 -0700 | [diff] [blame] | 741 | if id == 0 { |
| 742 | return GpuResponse::ErrInvalidResourceId; |
| 743 | } |
David Riley | af9d7ed | 2018-05-22 15:37:22 -0700 | [diff] [blame] | 744 | |
| 745 | let create_args = ResourceCreateArgs { |
| 746 | handle: id, |
| 747 | target, |
| 748 | format, |
| 749 | bind, |
| 750 | width, |
| 751 | height, |
| 752 | depth, |
| 753 | array_size, |
| 754 | last_level, |
| 755 | nr_samples, |
| 756 | flags, |
| 757 | }; |
| 758 | |
Zach Reizner | 3a8100a | 2017-09-13 19:15:43 -0700 | [diff] [blame] | 759 | match self.resources.entry(id) { |
| 760 | Entry::Occupied(_) => GpuResponse::ErrInvalidResourceId, |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 761 | Entry::Vacant(slot) => match Backend::validate_args_as_fourcc(create_args) { |
| 762 | Some(fourcc) => { |
| 763 | let buffer = match self.device.create_buffer( |
| 764 | width, |
| 765 | height, |
| 766 | Format::from(fourcc), |
Gurchetan Singh | 5aaa63f | 2019-03-25 17:24:52 -0700 | [diff] [blame] | 767 | Flags::empty().use_scanout(true).use_rendering(true), |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 768 | ) { |
| 769 | Ok(buffer) => buffer, |
| 770 | Err(e) => { |
| 771 | error!("failed to create buffer for 3d resource {}: {}", format, e); |
| 772 | return GpuResponse::ErrUnspec; |
David Riley | af9d7ed | 2018-05-22 15:37:22 -0700 | [diff] [blame] | 773 | } |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 774 | }; |
| 775 | |
| 776 | let dma_buf_fd = match buffer.export_plane_fd(0) { |
| 777 | Ok(dma_buf_fd) => dma_buf_fd, |
| 778 | Err(e) => { |
| 779 | error!("failed to export plane fd: {}", e); |
| 780 | return GpuResponse::ErrUnspec; |
| 781 | } |
| 782 | }; |
| 783 | |
| 784 | let image = match self.renderer.image_from_dmabuf( |
| 785 | fourcc, |
| 786 | width, |
| 787 | height, |
| 788 | dma_buf_fd.as_raw_fd(), |
| 789 | buffer.plane_offset(0), |
| 790 | buffer.plane_stride(0), |
| 791 | ) { |
| 792 | Ok(image) => image, |
| 793 | Err(e) => { |
| 794 | error!("failed to create egl image: {}", e); |
| 795 | return GpuResponse::ErrUnspec; |
| 796 | } |
| 797 | }; |
| 798 | |
| 799 | let res = self.renderer.import_resource(create_args, &image); |
| 800 | match res { |
| 801 | Ok(res) => { |
Zach Reizner | c589929 | 2018-09-05 10:23:46 -0700 | [diff] [blame] | 802 | let format_modifier = buffer.format_modifier(); |
| 803 | let mut plane_info = Vec::with_capacity(buffer.num_planes()); |
| 804 | for plane_index in 0..buffer.num_planes() { |
| 805 | plane_info.push(GpuResponsePlaneInfo { |
| 806 | stride: buffer.plane_stride(plane_index), |
| 807 | offset: buffer.plane_offset(plane_index), |
| 808 | }); |
| 809 | } |
David Tolnay | 902e7c8 | 2019-04-08 23:00:57 -0700 | [diff] [blame] | 810 | let backed = BackedBuffer::new_renderer_registered(buffer, res, image); |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 811 | slot.insert(Box::new(backed)); |
Zach Reizner | c589929 | 2018-09-05 10:23:46 -0700 | [diff] [blame] | 812 | GpuResponse::OkResourcePlaneInfo { |
| 813 | format_modifier, |
| 814 | plane_info, |
| 815 | } |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 816 | } |
| 817 | Err(e) => { |
| 818 | error!("failed to import renderer resource: {}", e); |
| 819 | GpuResponse::ErrUnspec |
David Riley | af9d7ed | 2018-05-22 15:37:22 -0700 | [diff] [blame] | 820 | } |
Zach Reizner | 3a8100a | 2017-09-13 19:15:43 -0700 | [diff] [blame] | 821 | } |
| 822 | } |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 823 | None => { |
| 824 | let res = self.renderer.create_resource(create_args); |
| 825 | match res { |
| 826 | Ok(res) => { |
| 827 | slot.insert(Box::new(res)); |
| 828 | GpuResponse::OkNoData |
| 829 | } |
| 830 | Err(e) => { |
| 831 | error!("failed to create renderer resource: {}", e); |
| 832 | GpuResponse::ErrUnspec |
| 833 | } |
| 834 | } |
| 835 | } |
| 836 | }, |
Zach Reizner | 3a8100a | 2017-09-13 19:15:43 -0700 | [diff] [blame] | 837 | } |
| 838 | } |
| 839 | |
| 840 | /// Copes the given 3D rectangle of pixels of the given resource's backing memory to the host |
| 841 | /// side resource. |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 842 | pub fn transfer_to_resource_3d( |
| 843 | &mut self, |
| 844 | ctx_id: u32, |
| 845 | res_id: u32, |
| 846 | x: u32, |
| 847 | y: u32, |
| 848 | z: u32, |
| 849 | width: u32, |
| 850 | height: u32, |
| 851 | depth: u32, |
| 852 | level: u32, |
| 853 | stride: u32, |
| 854 | layer_stride: u32, |
| 855 | offset: u64, |
| 856 | ) -> GpuResponse { |
Zach Reizner | 3a8100a | 2017-09-13 19:15:43 -0700 | [diff] [blame] | 857 | let ctx = match ctx_id { |
| 858 | 0 => None, |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 859 | id => match self.contexts.get(&id) { |
| 860 | None => return GpuResponse::ErrInvalidContextId, |
| 861 | ctx => ctx, |
| 862 | }, |
Zach Reizner | 3a8100a | 2017-09-13 19:15:43 -0700 | [diff] [blame] | 863 | }; |
| 864 | match self.resources.get_mut(&res_id) { |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 865 | Some(res) => match res.gpu_renderer_resource() { |
| 866 | Some(res) => { |
| 867 | let transfer_box = Box3 { |
| 868 | x, |
| 869 | y, |
| 870 | z, |
| 871 | w: width, |
| 872 | h: height, |
| 873 | d: depth, |
| 874 | }; |
| 875 | let res = |
| 876 | res.transfer_write(ctx, level, stride, layer_stride, transfer_box, offset); |
| 877 | match res { |
| 878 | Ok(_) => GpuResponse::OkNoData, |
| 879 | Err(e) => { |
| 880 | error!("failed to transfer to host: {}", e); |
| 881 | GpuResponse::ErrUnspec |
Zach Reizner | 3a8100a | 2017-09-13 19:15:43 -0700 | [diff] [blame] | 882 | } |
| 883 | } |
Zach Reizner | 3a8100a | 2017-09-13 19:15:43 -0700 | [diff] [blame] | 884 | } |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 885 | None => GpuResponse::ErrInvalidResourceId, |
| 886 | }, |
Zach Reizner | 3a8100a | 2017-09-13 19:15:43 -0700 | [diff] [blame] | 887 | None => GpuResponse::ErrInvalidResourceId, |
| 888 | } |
| 889 | } |
| 890 | |
| 891 | /// Copes the given rectangle of pixels from the resource to the given resource's backing |
| 892 | /// memory. |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 893 | pub fn transfer_from_resource_3d( |
| 894 | &mut self, |
| 895 | ctx_id: u32, |
| 896 | res_id: u32, |
| 897 | x: u32, |
| 898 | y: u32, |
| 899 | z: u32, |
| 900 | width: u32, |
| 901 | height: u32, |
| 902 | depth: u32, |
| 903 | level: u32, |
| 904 | stride: u32, |
| 905 | layer_stride: u32, |
| 906 | offset: u64, |
| 907 | ) -> GpuResponse { |
Zach Reizner | 3a8100a | 2017-09-13 19:15:43 -0700 | [diff] [blame] | 908 | let ctx = match ctx_id { |
| 909 | 0 => None, |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 910 | id => match self.contexts.get(&id) { |
| 911 | None => return GpuResponse::ErrInvalidContextId, |
| 912 | ctx => ctx, |
| 913 | }, |
Zach Reizner | 3a8100a | 2017-09-13 19:15:43 -0700 | [diff] [blame] | 914 | }; |
| 915 | match self.resources.get_mut(&res_id) { |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 916 | Some(res) => match res.gpu_renderer_resource() { |
| 917 | Some(res) => { |
| 918 | let transfer_box = Box3 { |
| 919 | x, |
| 920 | y, |
| 921 | z, |
| 922 | w: width, |
| 923 | h: height, |
| 924 | d: depth, |
| 925 | }; |
| 926 | let res = |
| 927 | res.transfer_read(ctx, level, stride, layer_stride, transfer_box, offset); |
| 928 | match res { |
| 929 | Ok(_) => GpuResponse::OkNoData, |
| 930 | Err(e) => { |
| 931 | error!("failed to transfer from host: {}", e); |
| 932 | GpuResponse::ErrUnspec |
Zach Reizner | 3a8100a | 2017-09-13 19:15:43 -0700 | [diff] [blame] | 933 | } |
| 934 | } |
Zach Reizner | 3a8100a | 2017-09-13 19:15:43 -0700 | [diff] [blame] | 935 | } |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 936 | None => GpuResponse::ErrInvalidResourceId, |
| 937 | }, |
Zach Reizner | 3a8100a | 2017-09-13 19:15:43 -0700 | [diff] [blame] | 938 | None => GpuResponse::ErrInvalidResourceId, |
| 939 | } |
| 940 | } |
| 941 | |
| 942 | /// Submits a command buffer to the given rendering context. |
| 943 | pub fn submit_command(&mut self, ctx_id: u32, commands: &mut [u8]) -> GpuResponse { |
| 944 | match self.contexts.get_mut(&ctx_id) { |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 945 | Some(ctx) => match ctx.submit(&mut commands[..]) { |
| 946 | Ok(_) => GpuResponse::OkNoData, |
| 947 | Err(e) => { |
| 948 | error!("failed to submit command buffer: {}", e); |
| 949 | GpuResponse::ErrUnspec |
Zach Reizner | 3a8100a | 2017-09-13 19:15:43 -0700 | [diff] [blame] | 950 | } |
Zach Reizner | 55a9e50 | 2018-10-03 10:22:32 -0700 | [diff] [blame] | 951 | }, |
Zach Reizner | 3a8100a | 2017-09-13 19:15:43 -0700 | [diff] [blame] | 952 | None => GpuResponse::ErrInvalidContextId, |
| 953 | } |
| 954 | } |
David Riley | f89e0b5 | 2018-05-17 17:14:42 -0700 | [diff] [blame] | 955 | |
| 956 | pub fn create_fence(&mut self, ctx_id: u32, fence_id: u32) -> GpuResponse { |
| 957 | // There is a mismatch of ordering that is intentional. |
| 958 | // This create_fence matches the other functions in Backend, yet |
| 959 | // the renderer matches the virgl interface. |
| 960 | match self.renderer.create_fence(fence_id, ctx_id) { |
| 961 | Ok(_) => GpuResponse::OkNoData, |
| 962 | Err(e) => { |
| 963 | error!("failed to create fence: {}", e); |
| 964 | GpuResponse::ErrUnspec |
| 965 | } |
| 966 | } |
| 967 | } |
| 968 | |
| 969 | pub fn fence_poll(&mut self) -> u32 { |
| 970 | self.renderer.poll() |
| 971 | } |
David Riley | c9ce2da | 2018-05-22 15:36:31 -0700 | [diff] [blame] | 972 | |
| 973 | pub fn force_ctx_0(&mut self) { |
| 974 | self.renderer.force_ctx_0(); |
| 975 | } |
Zach Reizner | 3a8100a | 2017-09-13 19:15:43 -0700 | [diff] [blame] | 976 | } |