rutabaga_gfx/
gfxstream.rs

1// Copyright 2020 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//! gfxstream: Handles 3D virtio-gpu hypercalls using gfxstream.
6//!
7//! External code found at <https://android.googlesource.com/device/generic/vulkan-cereal/>.
8
9#![cfg(feature = "gfxstream")]
10
11use std::convert::TryInto;
12use std::io::IoSliceMut;
13use std::mem::size_of;
14use std::os::raw::c_char;
15use std::os::raw::c_int;
16use std::os::raw::c_uint;
17use std::os::raw::c_void;
18use std::panic::catch_unwind;
19use std::process::abort;
20use std::ptr::null;
21use std::ptr::null_mut;
22use std::sync::Arc;
23
24use crate::generated::virgl_renderer_bindings::iovec;
25use crate::generated::virgl_renderer_bindings::virgl_box;
26use crate::generated::virgl_renderer_bindings::virgl_renderer_resource_create_args;
27use crate::renderer_utils::*;
28use crate::rutabaga_core::RutabagaComponent;
29use crate::rutabaga_core::RutabagaContext;
30use crate::rutabaga_core::RutabagaResource;
31use crate::rutabaga_os::FromRawDescriptor;
32use crate::rutabaga_os::IntoRawDescriptor;
33use crate::rutabaga_os::RawDescriptor;
34use crate::rutabaga_os::SafeDescriptor;
35use crate::rutabaga_utils::*;
36
37// See `virtgpu-gfxstream-renderer.h` for definitions
38const STREAM_RENDERER_PARAM_NULL: u64 = 0;
39const STREAM_RENDERER_PARAM_USER_DATA: u64 = 1;
40const STREAM_RENDERER_PARAM_RENDERER_FLAGS: u64 = 2;
41const STREAM_RENDERER_PARAM_FENCE_CALLBACK: u64 = 3;
42const STREAM_RENDERER_PARAM_WIN0_WIDTH: u64 = 4;
43const STREAM_RENDERER_PARAM_WIN0_HEIGHT: u64 = 5;
44const STREAM_RENDERER_PARAM_DEBUG_CALLBACK: u64 = 6;
45
46const STREAM_RENDERER_MAX_PARAMS: usize = 6;
47
48#[repr(C)]
49#[derive(Clone, Copy, Debug)]
50pub struct stream_renderer_param {
51    key: u64,
52    value: u64,
53}
54
55#[repr(C)]
56#[derive(Copy, Clone, Default)]
57pub struct stream_renderer_handle {
58    pub os_handle: i64,
59    pub handle_type: u32,
60}
61
62#[repr(C)]
63#[derive(Copy, Clone, Default)]
64pub struct stream_renderer_vulkan_info {
65    pub memory_index: u32,
66    pub device_uuid: [u8; 16],
67    pub driver_uuid: [u8; 16],
68}
69
70#[repr(C)]
71pub struct stream_renderer_command {
72    pub ctx_id: u32,
73    pub cmd_size: u32,
74    pub cmd: *const u8,
75    pub num_in_fences: u32,
76    pub in_fence_descriptors: *const u64,
77}
78
79#[allow(non_camel_case_types)]
80pub type stream_renderer_create_blob = ResourceCreateBlob;
81
82#[allow(non_camel_case_types)]
83pub type stream_renderer_resource_create_args = virgl_renderer_resource_create_args;
84
85#[allow(non_camel_case_types)]
86pub type stream_renderer_box = virgl_box;
87
88#[allow(non_camel_case_types)]
89pub type stream_renderer_fence = RutabagaFence;
90
91#[allow(non_camel_case_types)]
92pub type stream_renderer_debug = RutabagaDebug;
93
94extern "C" {
95    // Entry point for the stream renderer.
96    fn stream_renderer_init(
97        stream_renderer_params: *mut stream_renderer_param,
98        num_params: u64,
99    ) -> c_int;
100
101    // Shutdown entry point for the renderer.
102    fn stream_renderer_teardown();
103
104    // virtio-gpu-3d ioctl functions (begin)
105
106    // In gfxstream, the resource create/transfer ioctls correspond to creating buffers for API
107    // forwarding and the notification of new API calls forwarded by the guest, unless they
108    // correspond to minigbm resource targets (PIPE_TEXTURE_2D), in which case they create globally
109    // visible shared GL textures to support gralloc.
110    fn stream_renderer_resource_create(
111        args: *mut stream_renderer_resource_create_args,
112        iov: *mut iovec,
113        num_iovs: u32,
114    ) -> c_int;
115
116    fn stream_renderer_resource_unref(res_handle: u32);
117    fn stream_renderer_context_destroy(handle: u32);
118    fn stream_renderer_transfer_read_iov(
119        handle: u32,
120        ctx_id: u32,
121        level: u32,
122        stride: u32,
123        layer_stride: u32,
124        box_: *mut stream_renderer_box,
125        offset: u64,
126        iov: *mut iovec,
127        iovec_cnt: c_int,
128    ) -> c_int;
129    fn stream_renderer_transfer_write_iov(
130        handle: u32,
131        ctx_id: u32,
132        level: c_int,
133        stride: u32,
134        layer_stride: u32,
135        box_: *mut stream_renderer_box,
136        offset: u64,
137        iovec: *mut iovec,
138        iovec_cnt: c_uint,
139    ) -> c_int;
140    fn stream_renderer_submit_cmd(cmd: *const stream_renderer_command) -> c_int;
141    fn stream_renderer_resource_attach_iov(
142        res_handle: c_int,
143        iov: *mut iovec,
144        num_iovs: c_int,
145    ) -> c_int;
146    fn stream_renderer_resource_detach_iov(
147        res_handle: c_int,
148        iov: *mut *mut iovec,
149        num_iovs: *mut c_int,
150    );
151    fn stream_renderer_create_fence(fence: *const stream_renderer_fence) -> c_int;
152    fn stream_renderer_ctx_attach_resource(ctx_id: c_int, res_handle: c_int);
153    fn stream_renderer_ctx_detach_resource(ctx_id: c_int, res_handle: c_int);
154    fn stream_renderer_get_cap_set(set: u32, max_ver: *mut u32, max_size: *mut u32);
155    fn stream_renderer_fill_caps(set: u32, version: u32, caps: *mut c_void);
156
157    fn stream_renderer_flush(res_handle: u32);
158    fn stream_renderer_create_blob(
159        ctx_id: u32,
160        res_handle: u32,
161        create_blob: *const stream_renderer_create_blob,
162        iovecs: *const iovec,
163        num_iovs: u32,
164        handle: *const stream_renderer_handle,
165    ) -> c_int;
166
167    fn stream_renderer_export_blob(res_handle: u32, handle: *mut stream_renderer_handle) -> c_int;
168    fn stream_renderer_resource_map(
169        res_handle: u32,
170        map: *mut *mut c_void,
171        out_size: *mut u64,
172    ) -> c_int;
173    fn stream_renderer_resource_unmap(res_handle: u32) -> c_int;
174    fn stream_renderer_resource_map_info(res_handle: u32, map_info: *mut u32) -> c_int;
175    fn stream_renderer_vulkan_info(
176        res_handle: u32,
177        vulkan_info: *mut stream_renderer_vulkan_info,
178    ) -> c_int;
179    fn stream_renderer_context_create(
180        handle: u32,
181        nlen: u32,
182        name: *const c_char,
183        context_init: u32,
184    ) -> c_int;
185}
186
187/// The virtio-gpu backend state tracker which supports accelerated rendering.
188pub struct Gfxstream {
189    /// Cookie used by Gfxstream, should be held as long as the renderer is alive.
190    _cookie: Box<RutabagaCookie>,
191}
192
193struct GfxstreamContext {
194    ctx_id: u32,
195    fence_handler: RutabagaFenceHandler,
196}
197
198impl RutabagaContext for GfxstreamContext {
199    fn submit_cmd(&mut self, commands: &mut [u8], fence_ids: &[u64]) -> RutabagaResult<()> {
200        if !fence_ids.is_empty() {
201            return Err(RutabagaError::Unsupported);
202        }
203
204        if commands.len() % size_of::<u32>() != 0 {
205            return Err(RutabagaError::InvalidCommandSize(commands.len()));
206        }
207
208        // TODO(b/315870313): Add safety comment
209        #[allow(clippy::undocumented_unsafe_blocks)]
210        let ret = unsafe {
211            let cmd = stream_renderer_command {
212                ctx_id: self.ctx_id,
213                cmd_size: commands.len().try_into()?,
214                cmd: commands.as_mut_ptr(),
215                num_in_fences: 0,
216                in_fence_descriptors: null(),
217            };
218
219            stream_renderer_submit_cmd(&cmd as *const stream_renderer_command)
220        };
221        ret_to_res(ret)
222    }
223
224    fn attach(&mut self, resource: &mut RutabagaResource) {
225        // SAFETY:
226        // The context id and resource id must be valid because the respective instances ensure
227        // their lifetime.
228        unsafe {
229            stream_renderer_ctx_attach_resource(self.ctx_id as i32, resource.resource_id as i32);
230        }
231    }
232
233    fn detach(&mut self, resource: &RutabagaResource) {
234        // SAFETY:
235        // The context id and resource id must be valid because the respective instances ensure
236        // their lifetime.
237        unsafe {
238            stream_renderer_ctx_detach_resource(self.ctx_id as i32, resource.resource_id as i32);
239        }
240    }
241
242    fn component_type(&self) -> RutabagaComponentType {
243        RutabagaComponentType::Gfxstream
244    }
245
246    fn context_create_fence(&mut self, fence: RutabagaFence) -> RutabagaResult<()> {
247        if fence.ring_idx as u32 == 1 {
248            self.fence_handler.call(fence);
249            return Ok(());
250        }
251
252        // SAFETY:
253        // Safe because RutabagaFences and stream_renderer_fence are ABI identical
254        let ret = unsafe { stream_renderer_create_fence(&fence as *const stream_renderer_fence) };
255
256        ret_to_res(ret)
257    }
258}
259
260impl Drop for GfxstreamContext {
261    fn drop(&mut self) {
262        // SAFETY:
263        // The context is safe to destroy because nothing else can be referencing it.
264        unsafe {
265            stream_renderer_context_destroy(self.ctx_id);
266        }
267    }
268}
269
270extern "C" fn write_context_fence(cookie: *mut c_void, fence: *const RutabagaFence) {
271    catch_unwind(|| {
272        assert!(!cookie.is_null());
273        // SAFETY:
274        // We trust gfxstream not give a dangling pointer
275        let cookie = unsafe { &*(cookie as *mut RutabagaCookie) };
276        if let Some(handler) = &cookie.fence_handler {
277            // SAFETY:
278            // We trust gfxstream not give a dangling pointer
279            unsafe { handler.call(*fence) };
280        }
281    })
282    .unwrap_or_else(|_| abort())
283}
284
285extern "C" fn gfxstream_debug_callback(cookie: *mut c_void, debug: *const stream_renderer_debug) {
286    catch_unwind(|| {
287        assert!(!cookie.is_null());
288        // SAFETY:
289        // We trust gfxstream not give a dangling pointer
290        let cookie = unsafe { &*(cookie as *mut RutabagaCookie) };
291        if let Some(handler) = &cookie.debug_handler {
292            // SAFETY:
293            // We trust gfxstream not give a dangling pointer
294            unsafe { handler.call(*debug) };
295        }
296    })
297    .unwrap_or_else(|_| abort())
298}
299
300impl Gfxstream {
301    pub fn init(
302        display_width: u32,
303        display_height: u32,
304        gfxstream_flags: GfxstreamFlags,
305        fence_handler: RutabagaFenceHandler,
306        debug_handler: Option<RutabagaDebugHandler>,
307    ) -> RutabagaResult<Box<dyn RutabagaComponent>> {
308        let use_debug = debug_handler.is_some();
309        let mut cookie = Box::new(RutabagaCookie {
310            render_server_fd: None,
311            fence_handler: Some(fence_handler),
312            debug_handler,
313        });
314
315        let mut stream_renderer_params: [stream_renderer_param; STREAM_RENDERER_MAX_PARAMS] = [
316            stream_renderer_param {
317                key: STREAM_RENDERER_PARAM_USER_DATA,
318                // Safe as cookie outlives the stream renderer (stream_renderer_teardown called
319                // at Gfxstream Drop)
320                value: &mut *cookie as *mut RutabagaCookie as u64,
321            },
322            stream_renderer_param {
323                key: STREAM_RENDERER_PARAM_RENDERER_FLAGS,
324                value: gfxstream_flags.into(),
325            },
326            stream_renderer_param {
327                key: STREAM_RENDERER_PARAM_FENCE_CALLBACK,
328                value: write_context_fence as usize as u64,
329            },
330            stream_renderer_param {
331                key: STREAM_RENDERER_PARAM_WIN0_WIDTH,
332                value: display_width as u64,
333            },
334            stream_renderer_param {
335                key: STREAM_RENDERER_PARAM_WIN0_HEIGHT,
336                value: display_height as u64,
337            },
338            if use_debug {
339                stream_renderer_param {
340                    key: STREAM_RENDERER_PARAM_DEBUG_CALLBACK,
341                    value: gfxstream_debug_callback as usize as u64,
342                }
343            } else {
344                stream_renderer_param {
345                    key: STREAM_RENDERER_PARAM_NULL,
346                    value: 0,
347                }
348            },
349        ];
350
351        // TODO(b/315870313): Add safety comment
352        #[allow(clippy::undocumented_unsafe_blocks)]
353        unsafe {
354            ret_to_res(stream_renderer_init(
355                stream_renderer_params.as_mut_ptr(),
356                stream_renderer_params.len() as u64,
357            ))?;
358        }
359
360        Ok(Box::new(Gfxstream { _cookie: cookie }))
361    }
362
363    fn map_info(&self, resource_id: u32) -> RutabagaResult<u32> {
364        let mut map_info = 0;
365        // SAFETY:
366        // Safe because `map_info` is a local stack variable owned by us.
367        let ret = unsafe { stream_renderer_resource_map_info(resource_id, &mut map_info) };
368        ret_to_res(ret)?;
369
370        Ok(map_info | RUTABAGA_MAP_ACCESS_RW)
371    }
372
373    fn vulkan_info(&self, resource_id: u32) -> RutabagaResult<VulkanInfo> {
374        let mut vulkan_info: stream_renderer_vulkan_info = Default::default();
375        // SAFETY:
376        // Safe because `vulkan_info` is a local stack variable owned by us.
377        let ret = unsafe { stream_renderer_vulkan_info(resource_id, &mut vulkan_info) };
378        ret_to_res(ret)?;
379
380        Ok(VulkanInfo {
381            memory_idx: vulkan_info.memory_index,
382            device_id: DeviceId {
383                device_uuid: vulkan_info.device_uuid,
384                driver_uuid: vulkan_info.driver_uuid,
385            },
386        })
387    }
388
389    fn export_blob(&self, resource_id: u32) -> RutabagaResult<Arc<RutabagaHandle>> {
390        let mut stream_handle: stream_renderer_handle = Default::default();
391        // TODO(b/315870313): Add safety comment
392        #[allow(clippy::undocumented_unsafe_blocks)]
393        let ret = unsafe { stream_renderer_export_blob(resource_id, &mut stream_handle) };
394        ret_to_res(ret)?;
395
396        let raw_descriptor = stream_handle.os_handle as RawDescriptor;
397        // SAFETY:
398        // Safe because the handle was just returned by a successful gfxstream call so it must be
399        // valid and owned by us.
400        let handle = unsafe { SafeDescriptor::from_raw_descriptor(raw_descriptor) };
401
402        Ok(Arc::new(RutabagaHandle {
403            os_handle: handle,
404            handle_type: stream_handle.handle_type,
405        }))
406    }
407}
408
409impl Drop for Gfxstream {
410    fn drop(&mut self) {
411        // SAFETY: Safe because Gfxstream was successfully initialized.
412        unsafe {
413            stream_renderer_teardown();
414        }
415    }
416}
417
418impl RutabagaComponent for Gfxstream {
419    fn get_capset_info(&self, capset_id: u32) -> (u32, u32) {
420        let mut version = 0;
421        let mut size = 0;
422        // SAFETY:
423        // Safe because gfxstream is initialized by now and properly size stack variables are
424        // used for the pointers.
425        unsafe {
426            stream_renderer_get_cap_set(capset_id, &mut version, &mut size);
427        }
428        (version, size)
429    }
430
431    fn get_capset(&self, capset_id: u32, version: u32) -> Vec<u8> {
432        let (_, max_size) = self.get_capset_info(capset_id);
433        let mut buf = vec![0u8; max_size as usize];
434        // SAFETY:
435        // Safe because gfxstream is initialized by now and the given buffer is sized properly
436        // for the given cap id/version.
437        unsafe {
438            stream_renderer_fill_caps(capset_id, version, buf.as_mut_ptr() as *mut c_void);
439        }
440
441        buf
442    }
443
444    fn create_fence(&mut self, fence: RutabagaFence) -> RutabagaResult<()> {
445        // SAFETY:
446        // Safe because RutabagaFences and stream_renderer_fence are ABI identical
447        let ret = unsafe { stream_renderer_create_fence(&fence as *const stream_renderer_fence) };
448        ret_to_res(ret)
449    }
450
451    fn create_3d(
452        &self,
453        resource_id: u32,
454        resource_create_3d: ResourceCreate3D,
455    ) -> RutabagaResult<RutabagaResource> {
456        let mut args = virgl_renderer_resource_create_args {
457            handle: resource_id,
458            target: resource_create_3d.target,
459            format: resource_create_3d.format,
460            bind: resource_create_3d.bind,
461            width: resource_create_3d.width,
462            height: resource_create_3d.height,
463            depth: resource_create_3d.depth,
464            array_size: resource_create_3d.array_size,
465            last_level: resource_create_3d.last_level,
466            nr_samples: resource_create_3d.nr_samples,
467            flags: resource_create_3d.flags,
468        };
469
470        // SAFETY:
471        // Safe because gfxstream is initialized by now, and the return value is checked before
472        // returning a new resource. The backing buffers are not supplied with this call.
473        let ret = unsafe { stream_renderer_resource_create(&mut args, null_mut(), 0) };
474        ret_to_res(ret)?;
475
476        Ok(RutabagaResource {
477            resource_id,
478            handle: None,
479            blob: false,
480            blob_mem: 0,
481            blob_flags: 0,
482            map_info: None,
483            info_2d: None,
484            info_3d: None,
485            vulkan_info: None,
486            backing_iovecs: None,
487            component_mask: 1 << (RutabagaComponentType::Gfxstream as u8),
488            size: 0,
489            mapping: None,
490        })
491    }
492
493    fn attach_backing(
494        &self,
495        resource_id: u32,
496        vecs: &mut Vec<RutabagaIovec>,
497    ) -> RutabagaResult<()> {
498        // TODO(b/315870313): Add safety comment
499        #[allow(clippy::undocumented_unsafe_blocks)]
500        let ret = unsafe {
501            stream_renderer_resource_attach_iov(
502                resource_id as i32,
503                vecs.as_mut_ptr() as *mut iovec,
504                vecs.len() as i32,
505            )
506        };
507        ret_to_res(ret)
508    }
509
510    fn detach_backing(&self, resource_id: u32) {
511        // TODO(b/315870313): Add safety comment
512        #[allow(clippy::undocumented_unsafe_blocks)]
513        unsafe {
514            stream_renderer_resource_detach_iov(
515                resource_id as i32,
516                std::ptr::null_mut(),
517                std::ptr::null_mut(),
518            );
519        }
520    }
521
522    fn unref_resource(&self, resource_id: u32) {
523        // SAFETY:
524        // The resource is safe to unreference destroy because no user of these bindings can still
525        // be holding a reference.
526        unsafe {
527            stream_renderer_resource_unref(resource_id);
528        }
529    }
530
531    fn transfer_write(
532        &self,
533        ctx_id: u32,
534        resource: &mut RutabagaResource,
535        transfer: Transfer3D,
536    ) -> RutabagaResult<()> {
537        if transfer.is_empty() {
538            return Ok(());
539        }
540
541        let mut transfer_box = VirglBox {
542            x: transfer.x,
543            y: transfer.y,
544            z: transfer.z,
545            w: transfer.w,
546            h: transfer.h,
547            d: transfer.d,
548        };
549
550        // SAFETY:
551        // Safe because only stack variables of the appropriate type are used.
552        let ret = unsafe {
553            stream_renderer_transfer_write_iov(
554                resource.resource_id,
555                ctx_id,
556                transfer.level as i32,
557                transfer.stride,
558                transfer.layer_stride,
559                &mut transfer_box as *mut VirglBox as *mut stream_renderer_box,
560                transfer.offset,
561                null_mut(),
562                0,
563            )
564        };
565        ret_to_res(ret)
566    }
567
568    fn transfer_read(
569        &self,
570        ctx_id: u32,
571        resource: &mut RutabagaResource,
572        transfer: Transfer3D,
573        buf: Option<IoSliceMut>,
574    ) -> RutabagaResult<()> {
575        if transfer.is_empty() {
576            return Ok(());
577        }
578
579        let mut transfer_box = VirglBox {
580            x: transfer.x,
581            y: transfer.y,
582            z: transfer.z,
583            w: transfer.w,
584            h: transfer.h,
585            d: transfer.d,
586        };
587
588        let mut iov = RutabagaIovec {
589            base: null_mut(),
590            len: 0,
591        };
592
593        let (iovecs, num_iovecs) = match buf {
594            Some(mut buf) => {
595                iov.base = buf.as_mut_ptr() as *mut c_void;
596                iov.len = buf.len();
597                (&mut iov as *mut RutabagaIovec as *mut iovec, 1)
598            }
599            None => (null_mut(), 0),
600        };
601
602        // SAFETY:
603        // Safe because only stack variables of the appropriate type are used.
604        let ret = unsafe {
605            stream_renderer_transfer_read_iov(
606                resource.resource_id,
607                ctx_id,
608                transfer.level,
609                transfer.stride,
610                transfer.layer_stride,
611                &mut transfer_box as *mut VirglBox as *mut stream_renderer_box,
612                transfer.offset,
613                iovecs,
614                num_iovecs,
615            )
616        };
617        ret_to_res(ret)
618    }
619
620    fn resource_flush(&self, resource: &mut RutabagaResource) -> RutabagaResult<()> {
621        // TODO(b/315870313): Add safety comment
622        #[allow(clippy::undocumented_unsafe_blocks)]
623        unsafe {
624            stream_renderer_flush(resource.resource_id);
625        }
626        Ok(())
627    }
628
629    fn create_blob(
630        &mut self,
631        ctx_id: u32,
632        resource_id: u32,
633        resource_create_blob: ResourceCreateBlob,
634        mut iovec_opt: Option<Vec<RutabagaIovec>>,
635        handle_opt: Option<RutabagaHandle>,
636    ) -> RutabagaResult<RutabagaResource> {
637        let mut iovec_ptr = null_mut();
638        let mut num_iovecs = 0;
639        if let Some(ref mut iovecs) = iovec_opt {
640            iovec_ptr = iovecs.as_mut_ptr();
641            num_iovecs = iovecs.len() as u32;
642        }
643
644        let mut handle_ptr = null();
645        let mut stream_handle: stream_renderer_handle = Default::default();
646        if let Some(handle) = handle_opt {
647            stream_handle.handle_type = handle.handle_type;
648            stream_handle.os_handle = handle.os_handle.into_raw_descriptor() as i64;
649            handle_ptr = &stream_handle;
650        }
651
652        // TODO(b/315870313): Add safety comment
653        #[allow(clippy::undocumented_unsafe_blocks)]
654        let ret = unsafe {
655            stream_renderer_create_blob(
656                ctx_id,
657                resource_id,
658                &resource_create_blob as *const stream_renderer_create_blob,
659                iovec_ptr as *const iovec,
660                num_iovecs,
661                handle_ptr,
662            )
663        };
664
665        ret_to_res(ret)?;
666
667        Ok(RutabagaResource {
668            resource_id,
669            handle: self.export_blob(resource_id).ok(),
670            blob: true,
671            blob_mem: resource_create_blob.blob_mem,
672            blob_flags: resource_create_blob.blob_flags,
673            map_info: self.map_info(resource_id).ok(),
674            info_2d: None,
675            info_3d: None,
676            vulkan_info: self.vulkan_info(resource_id).ok(),
677            backing_iovecs: iovec_opt,
678            component_mask: 1 << (RutabagaComponentType::Gfxstream as u8),
679            size: resource_create_blob.size,
680            mapping: None,
681        })
682    }
683
684    fn map(&self, resource_id: u32) -> RutabagaResult<RutabagaMapping> {
685        let mut map: *mut c_void = null_mut();
686        let mut size: u64 = 0;
687
688        // SAFETY:
689        // Safe because the Stream renderer wraps and validates use of vkMapMemory.
690        let ret = unsafe { stream_renderer_resource_map(resource_id, &mut map, &mut size) };
691        if ret != 0 {
692            return Err(RutabagaError::MappingFailed(ret));
693        }
694        Ok(RutabagaMapping {
695            ptr: map as u64,
696            size,
697        })
698    }
699
700    fn unmap(&self, resource_id: u32) -> RutabagaResult<()> {
701        // SAFETY:
702        // Safe because the Stream renderer wraps and validates use of vkMapMemory.
703        let ret = unsafe { stream_renderer_resource_unmap(resource_id) };
704        ret_to_res(ret)
705    }
706
707    fn create_context(
708        &self,
709        ctx_id: u32,
710        context_init: u32,
711        context_name: Option<&str>,
712        fence_handler: RutabagaFenceHandler,
713    ) -> RutabagaResult<Box<dyn RutabagaContext>> {
714        let mut name: &str = "gpu_renderer";
715        if let Some(name_string) = context_name.filter(|s| !s.is_empty()) {
716            name = name_string;
717        }
718
719        // SAFETY:
720        // Safe because gfxstream is initialized by now and the context name is statically
721        // allocated. The return value is checked before returning a new context.
722        let ret = unsafe {
723            stream_renderer_context_create(
724                ctx_id,
725                name.len() as u32,
726                name.as_ptr() as *const c_char,
727                context_init,
728            )
729        };
730        ret_to_res(ret)?;
731        Ok(Box::new(GfxstreamContext {
732            ctx_id,
733            fence_handler,
734        }))
735    }
736}