• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2024 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 use std::collections::btree_map::Entry;
6 use std::collections::BTreeMap as Map;
7 use std::collections::BTreeSet as Set;
8 use std::io::Cursor;
9 use std::os::raw::c_void;
10 use std::path::Path;
11 use std::sync::Arc;
12 use std::sync::Mutex;
13 
14 use log::error;
15 use rutabaga_gfx::calculate_capset_mask;
16 use rutabaga_gfx::kumquat_support::kumquat_gpu_protocol::*;
17 use rutabaga_gfx::kumquat_support::RutabagaEvent;
18 use rutabaga_gfx::kumquat_support::RutabagaMemoryMapping;
19 use rutabaga_gfx::kumquat_support::RutabagaSharedMemory;
20 use rutabaga_gfx::kumquat_support::RutabagaStream;
21 use rutabaga_gfx::kumquat_support::RutabagaTube;
22 use rutabaga_gfx::ResourceCreate3D;
23 use rutabaga_gfx::ResourceCreateBlob;
24 use rutabaga_gfx::Rutabaga;
25 use rutabaga_gfx::RutabagaAsBorrowedDescriptor as AsBorrowedDescriptor;
26 use rutabaga_gfx::RutabagaBuilder;
27 use rutabaga_gfx::RutabagaComponentType;
28 use rutabaga_gfx::RutabagaDescriptor;
29 use rutabaga_gfx::RutabagaError;
30 use rutabaga_gfx::RutabagaFence;
31 use rutabaga_gfx::RutabagaFenceHandler;
32 use rutabaga_gfx::RutabagaHandle;
33 use rutabaga_gfx::RutabagaIovec;
34 use rutabaga_gfx::RutabagaResult;
35 use rutabaga_gfx::RutabagaWsi;
36 use rutabaga_gfx::Transfer3D;
37 use rutabaga_gfx::VulkanInfo;
38 use rutabaga_gfx::RUTABAGA_FLAG_FENCE;
39 use rutabaga_gfx::RUTABAGA_FLAG_FENCE_HOST_SHAREABLE;
40 use rutabaga_gfx::RUTABAGA_HANDLE_TYPE_MEM_SHM;
41 use rutabaga_gfx::RUTABAGA_MAP_ACCESS_RW;
42 use rutabaga_gfx::RUTABAGA_MAP_CACHE_CACHED;
43 
44 const SNAPSHOT_DIR: &str = "/tmp/";
45 
46 pub struct KumquatGpuConnection {
47     stream: RutabagaStream,
48 }
49 
50 pub struct KumquatGpuResource {
51     attached_contexts: Set<u32>,
52     mapping: Option<RutabagaMemoryMapping>,
53 }
54 
55 pub struct FenceData {
56     pub pending_fences: Map<u64, RutabagaEvent>,
57 }
58 
59 pub type FenceState = Arc<Mutex<FenceData>>;
60 
create_fence_handler(fence_state: FenceState) -> RutabagaFenceHandler61 pub fn create_fence_handler(fence_state: FenceState) -> RutabagaFenceHandler {
62     RutabagaFenceHandler::new(move |completed_fence: RutabagaFence| {
63         let mut state = fence_state.lock().unwrap();
64         match (*state).pending_fences.entry(completed_fence.fence_id) {
65             Entry::Occupied(o) => {
66                 let (_, mut event) = o.remove_entry();
67                 event.signal().unwrap();
68             }
69             Entry::Vacant(_) => {
70                 // This is fine, since an actual fence doesn't create emulated sync
71                 // entry
72             }
73         }
74     })
75 }
76 
77 pub struct KumquatGpu {
78     rutabaga: Rutabaga,
79     fence_state: FenceState,
80     id_allocator: u32,
81     resources: Map<u32, KumquatGpuResource>,
82 }
83 
84 impl KumquatGpu {
new(capset_names: String, renderer_features: String) -> RutabagaResult<KumquatGpu>85     pub fn new(capset_names: String, renderer_features: String) -> RutabagaResult<KumquatGpu> {
86         let capset_mask = calculate_capset_mask(capset_names.as_str().split(":"));
87         let fence_state = Arc::new(Mutex::new(FenceData {
88             pending_fences: Default::default(),
89         }));
90 
91         let fence_handler = create_fence_handler(fence_state.clone());
92 
93         let renderer_features_opt = if renderer_features.is_empty() {
94             None
95         } else {
96             Some(renderer_features)
97         };
98 
99         let rutabaga = RutabagaBuilder::new(RutabagaComponentType::CrossDomain, capset_mask)
100             .set_use_external_blob(true)
101             .set_use_egl(true)
102             .set_wsi(RutabagaWsi::Surfaceless)
103             .set_renderer_features(renderer_features_opt)
104             .build(fence_handler, None)?;
105 
106         Ok(KumquatGpu {
107             rutabaga,
108             fence_state,
109             id_allocator: 0,
110             resources: Default::default(),
111         })
112     }
113 
allocate_id(&mut self) -> u32114     pub fn allocate_id(&mut self) -> u32 {
115         self.id_allocator = self.id_allocator + 1;
116         self.id_allocator
117     }
118 }
119 
120 impl KumquatGpuConnection {
new(connection: RutabagaTube) -> KumquatGpuConnection121     pub fn new(connection: RutabagaTube) -> KumquatGpuConnection {
122         KumquatGpuConnection {
123             stream: RutabagaStream::new(connection),
124         }
125     }
126 
process_command(&mut self, kumquat_gpu: &mut KumquatGpu) -> RutabagaResult<bool>127     pub fn process_command(&mut self, kumquat_gpu: &mut KumquatGpu) -> RutabagaResult<bool> {
128         let mut hung_up = false;
129         let protocols = self.stream.read()?;
130 
131         for protocol in protocols {
132             match protocol {
133                 KumquatGpuProtocol::GetNumCapsets => {
134                     let resp = kumquat_gpu_protocol_ctrl_hdr {
135                         type_: KUMQUAT_GPU_PROTOCOL_RESP_NUM_CAPSETS,
136                         payload: kumquat_gpu.rutabaga.get_num_capsets(),
137                     };
138 
139                     self.stream.write(KumquatGpuProtocolWrite::Cmd(resp))?;
140                 }
141                 KumquatGpuProtocol::GetCapsetInfo(capset_index) => {
142                     let (capset_id, version, size) =
143                         kumquat_gpu.rutabaga.get_capset_info(capset_index)?;
144 
145                     let resp = kumquat_gpu_protocol_resp_capset_info {
146                         hdr: kumquat_gpu_protocol_ctrl_hdr {
147                             type_: KUMQUAT_GPU_PROTOCOL_RESP_CAPSET_INFO,
148                             ..Default::default()
149                         },
150                         capset_id,
151                         version,
152                         size,
153                         ..Default::default()
154                     };
155 
156                     self.stream.write(KumquatGpuProtocolWrite::Cmd(resp))?;
157                 }
158                 KumquatGpuProtocol::GetCapset(cmd) => {
159                     let capset = kumquat_gpu
160                         .rutabaga
161                         .get_capset(cmd.capset_id, cmd.capset_version)?;
162 
163                     let resp = kumquat_gpu_protocol_ctrl_hdr {
164                         type_: KUMQUAT_GPU_PROTOCOL_RESP_CAPSET,
165                         payload: capset.len().try_into()?,
166                     };
167 
168                     self.stream
169                         .write(KumquatGpuProtocolWrite::CmdWithData(resp, capset))?;
170                 }
171                 KumquatGpuProtocol::CtxCreate(cmd) => {
172                     let context_id = kumquat_gpu.allocate_id();
173                     let context_name: Option<String> =
174                         String::from_utf8(cmd.debug_name.to_vec()).ok();
175 
176                     kumquat_gpu.rutabaga.create_context(
177                         context_id,
178                         cmd.context_init,
179                         context_name.as_deref(),
180                     )?;
181 
182                     let resp = kumquat_gpu_protocol_ctrl_hdr {
183                         type_: KUMQUAT_GPU_PROTOCOL_RESP_CONTEXT_CREATE,
184                         payload: context_id,
185                     };
186 
187                     self.stream.write(KumquatGpuProtocolWrite::Cmd(resp))?;
188                 }
189                 KumquatGpuProtocol::CtxDestroy(ctx_id) => {
190                     kumquat_gpu.rutabaga.destroy_context(ctx_id)?;
191                 }
192                 KumquatGpuProtocol::CtxAttachResource(cmd) => {
193                     kumquat_gpu
194                         .rutabaga
195                         .context_attach_resource(cmd.ctx_id, cmd.resource_id)?;
196                 }
197                 KumquatGpuProtocol::CtxDetachResource(cmd) => {
198                     kumquat_gpu
199                         .rutabaga
200                         .context_detach_resource(cmd.ctx_id, cmd.resource_id)?;
201 
202                     let mut resource = kumquat_gpu
203                         .resources
204                         .remove(&cmd.resource_id)
205                         .ok_or(RutabagaError::InvalidResourceId)?;
206 
207                     resource.attached_contexts.remove(&cmd.ctx_id);
208                     if resource.attached_contexts.len() == 0 {
209                         if resource.mapping.is_some() {
210                             kumquat_gpu.rutabaga.detach_backing(cmd.resource_id)?;
211                         }
212 
213                         kumquat_gpu.rutabaga.unref_resource(cmd.resource_id)?;
214                     } else {
215                         kumquat_gpu.resources.insert(cmd.resource_id, resource);
216                     }
217                 }
218                 KumquatGpuProtocol::ResourceCreate3d(cmd) => {
219                     let resource_create_3d = ResourceCreate3D {
220                         target: cmd.target,
221                         format: cmd.format,
222                         bind: cmd.bind,
223                         width: cmd.width,
224                         height: cmd.height,
225                         depth: cmd.depth,
226                         array_size: cmd.array_size,
227                         last_level: cmd.last_level,
228                         nr_samples: cmd.nr_samples,
229                         flags: cmd.flags,
230                     };
231 
232                     let size = cmd.size as usize;
233                     let descriptor: RutabagaDescriptor =
234                         RutabagaSharedMemory::new("rutabaga_server", size as u64)?.into();
235 
236                     let clone = descriptor.try_clone()?;
237                     let mut vecs: Vec<RutabagaIovec> = Vec::new();
238 
239                     // Creating the mapping closes the cloned descriptor.
240                     let mapping = RutabagaMemoryMapping::from_safe_descriptor(
241                         clone,
242                         size,
243                         RUTABAGA_MAP_CACHE_CACHED | RUTABAGA_MAP_ACCESS_RW,
244                     )?;
245                     let rutabaga_mapping = mapping.as_rutabaga_mapping();
246 
247                     vecs.push(RutabagaIovec {
248                         base: rutabaga_mapping.ptr as *mut c_void,
249                         len: size,
250                     });
251 
252                     let resource_id = kumquat_gpu.allocate_id();
253 
254                     kumquat_gpu
255                         .rutabaga
256                         .resource_create_3d(resource_id, resource_create_3d)?;
257 
258                     kumquat_gpu.rutabaga.attach_backing(resource_id, vecs)?;
259                     kumquat_gpu.resources.insert(
260                         resource_id,
261                         KumquatGpuResource {
262                             attached_contexts: Default::default(),
263                             mapping: Some(mapping),
264                         },
265                     );
266 
267                     kumquat_gpu
268                         .rutabaga
269                         .context_attach_resource(cmd.ctx_id, resource_id)?;
270 
271                     let resp = kumquat_gpu_protocol_resp_resource_create {
272                         hdr: kumquat_gpu_protocol_ctrl_hdr {
273                             type_: KUMQUAT_GPU_PROTOCOL_RESP_RESOURCE_CREATE,
274                             ..Default::default()
275                         },
276                         resource_id,
277                         ..Default::default()
278                     };
279 
280                     self.stream.write(KumquatGpuProtocolWrite::CmdWithHandle(
281                         resp,
282                         RutabagaHandle {
283                             os_handle: descriptor,
284                             handle_type: RUTABAGA_HANDLE_TYPE_MEM_SHM,
285                         },
286                     ))?;
287                 }
288                 KumquatGpuProtocol::TransferToHost3d(cmd, emulated_fence) => {
289                     let resource_id = cmd.resource_id;
290 
291                     let transfer = Transfer3D {
292                         x: cmd.box_.x,
293                         y: cmd.box_.y,
294                         z: cmd.box_.z,
295                         w: cmd.box_.w,
296                         h: cmd.box_.h,
297                         d: cmd.box_.d,
298                         level: cmd.level,
299                         stride: cmd.stride,
300                         layer_stride: cmd.layer_stride,
301                         offset: cmd.offset,
302                     };
303 
304                     kumquat_gpu
305                         .rutabaga
306                         .transfer_write(cmd.ctx_id, resource_id, transfer)?;
307 
308                     let mut event: RutabagaEvent = emulated_fence.try_into()?;
309                     event.signal()?;
310                 }
311                 KumquatGpuProtocol::TransferFromHost3d(cmd, emulated_fence) => {
312                     let resource_id = cmd.resource_id;
313 
314                     let transfer = Transfer3D {
315                         x: cmd.box_.x,
316                         y: cmd.box_.y,
317                         z: cmd.box_.z,
318                         w: cmd.box_.w,
319                         h: cmd.box_.h,
320                         d: cmd.box_.d,
321                         level: cmd.level,
322                         stride: cmd.stride,
323                         layer_stride: cmd.layer_stride,
324                         offset: cmd.offset,
325                     };
326 
327                     kumquat_gpu
328                         .rutabaga
329                         .transfer_read(cmd.ctx_id, resource_id, transfer, None)?;
330 
331                     let mut event: RutabagaEvent = emulated_fence.try_into()?;
332                     event.signal()?;
333                 }
334                 KumquatGpuProtocol::CmdSubmit3d(cmd, mut cmd_buf, fence_ids) => {
335                     kumquat_gpu.rutabaga.submit_command(
336                         cmd.ctx_id,
337                         &mut cmd_buf[..],
338                         &fence_ids[..],
339                     )?;
340 
341                     if cmd.flags & RUTABAGA_FLAG_FENCE != 0 {
342                         let fence_id = kumquat_gpu.allocate_id() as u64;
343                         let fence = RutabagaFence {
344                             flags: cmd.flags,
345                             fence_id,
346                             ctx_id: cmd.ctx_id,
347                             ring_idx: cmd.ring_idx,
348                         };
349 
350                         let mut fence_descriptor_opt: Option<RutabagaHandle> = None;
351                         let actual_fence = cmd.flags & RUTABAGA_FLAG_FENCE_HOST_SHAREABLE != 0;
352                         if !actual_fence {
353                             let event: RutabagaEvent = RutabagaEvent::new()?;
354                             let clone = event.try_clone()?;
355                             let emulated_fence: RutabagaHandle = clone.into();
356 
357                             fence_descriptor_opt = Some(emulated_fence);
358                             let mut fence_state = kumquat_gpu.fence_state.lock().unwrap();
359                             (*fence_state).pending_fences.insert(fence_id, event);
360                         }
361 
362                         kumquat_gpu.rutabaga.create_fence(fence)?;
363 
364                         if actual_fence {
365                             fence_descriptor_opt =
366                                 Some(kumquat_gpu.rutabaga.export_fence(fence_id)?);
367                             kumquat_gpu.rutabaga.destroy_fences(&[fence_id])?;
368                         }
369 
370                         let fence_descriptor = fence_descriptor_opt
371                             .ok_or(RutabagaError::SpecViolation("No fence descriptor"))?;
372 
373                         let resp = kumquat_gpu_protocol_resp_cmd_submit_3d {
374                             hdr: kumquat_gpu_protocol_ctrl_hdr {
375                                 type_: KUMQUAT_GPU_PROTOCOL_RESP_CMD_SUBMIT_3D,
376                                 ..Default::default()
377                             },
378                             fence_id,
379                             handle_type: fence_descriptor.handle_type,
380                             ..Default::default()
381                         };
382 
383                         self.stream.write(KumquatGpuProtocolWrite::CmdWithHandle(
384                             resp,
385                             fence_descriptor,
386                         ))?;
387                     }
388                 }
389                 KumquatGpuProtocol::ResourceCreateBlob(cmd) => {
390                     let resource_id = kumquat_gpu.allocate_id();
391 
392                     let resource_create_blob = ResourceCreateBlob {
393                         blob_mem: cmd.blob_mem,
394                         blob_flags: cmd.blob_flags,
395                         blob_id: cmd.blob_id,
396                         size: cmd.size,
397                     };
398 
399                     kumquat_gpu.rutabaga.resource_create_blob(
400                         cmd.ctx_id,
401                         resource_id,
402                         resource_create_blob,
403                         None,
404                         None,
405                     )?;
406 
407                     let handle = kumquat_gpu.rutabaga.export_blob(resource_id)?;
408                     let mut vk_info: VulkanInfo = Default::default();
409                     if let Ok(vulkan_info) = kumquat_gpu.rutabaga.vulkan_info(resource_id) {
410                         vk_info = vulkan_info;
411                     }
412 
413                     kumquat_gpu.resources.insert(
414                         resource_id,
415                         KumquatGpuResource {
416                             attached_contexts: Set::from([cmd.ctx_id]),
417                             mapping: None,
418                         },
419                     );
420 
421                     let resp = kumquat_gpu_protocol_resp_resource_create {
422                         hdr: kumquat_gpu_protocol_ctrl_hdr {
423                             type_: KUMQUAT_GPU_PROTOCOL_RESP_RESOURCE_CREATE,
424                             ..Default::default()
425                         },
426                         resource_id,
427                         handle_type: handle.handle_type,
428                         vulkan_info: vk_info,
429                     };
430 
431                     self.stream
432                         .write(KumquatGpuProtocolWrite::CmdWithHandle(resp, handle))?;
433 
434                     kumquat_gpu
435                         .rutabaga
436                         .context_attach_resource(cmd.ctx_id, resource_id)?;
437                 }
438                 KumquatGpuProtocol::SnapshotSave => {
439                     kumquat_gpu.rutabaga.snapshot(&Path::new(SNAPSHOT_DIR))?;
440 
441                     let resp = kumquat_gpu_protocol_ctrl_hdr {
442                         type_: KUMQUAT_GPU_PROTOCOL_RESP_OK_SNAPSHOT,
443                         payload: 0,
444                     };
445 
446                     self.stream.write(KumquatGpuProtocolWrite::Cmd(resp))?;
447                 }
448                 KumquatGpuProtocol::SnapshotRestore => {
449                     kumquat_gpu.rutabaga.restore(&Path::new(SNAPSHOT_DIR))?;
450 
451                     let resp = kumquat_gpu_protocol_ctrl_hdr {
452                         type_: KUMQUAT_GPU_PROTOCOL_RESP_OK_SNAPSHOT,
453                         payload: 0,
454                     };
455 
456                     self.stream.write(KumquatGpuProtocolWrite::Cmd(resp))?;
457                 }
458                 KumquatGpuProtocol::OkNoData => {
459                     hung_up = true;
460                 }
461                 _ => {
462                     error!("Unsupported protocol {:?}", protocol);
463                     return Err(RutabagaError::Unsupported);
464                 }
465             };
466         }
467 
468         Ok(hung_up)
469     }
470 }
471 
472 impl AsBorrowedDescriptor for KumquatGpuConnection {
as_borrowed_descriptor(&self) -> &RutabagaDescriptor473     fn as_borrowed_descriptor(&self) -> &RutabagaDescriptor {
474         self.stream.as_borrowed_descriptor()
475     }
476 }
477