summary refs log tree commit diff
path: root/resources/src/gpu_allocator.rs
blob: ea38ba4bd2b05a78c4efa22d6c8669c7bb07d83d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
// 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.

use std::fs::File;

#[cfg(feature = "wl-dmabuf")]
use libc::EINVAL;

#[cfg(feature = "wl-dmabuf")]
use gpu_buffer;
use sys_util;

#[derive(Debug)]
pub enum GpuAllocatorError {
    OpenGpuBufferDevice,
    CreateGpuBufferDevice,
}

/// Struct that describes the offset and stride of a plane located in GPU memory.
#[derive(Clone, Copy, Debug, PartialEq, Default)]
pub struct GpuMemoryPlaneDesc {
    pub stride: u32,
    pub offset: u32,
}

/// Struct that describes a GPU memory allocation that consists of up to 3 planes.
#[derive(Clone, Copy, Debug, Default)]
pub struct GpuMemoryDesc {
    pub planes: [GpuMemoryPlaneDesc; 3],
}

/// Trait that needs to be implemented in order to service GPU memory allocation
/// requests. Implementations are expected to support some set of buffer sizes and
/// formats but every possible combination is not required.
pub trait GpuMemoryAllocator {
    /// Allocates GPU memory for a buffer of a specific size and format. The memory
    /// layout for the returned buffer must be linear. A file handle and the
    /// description of the planes for the buffer are returned on success.
    ///
    /// # Arguments
    /// * `width` - Width of buffer.
    /// * `height` - Height of buffer.
    /// * `format` - Fourcc format of buffer.
    fn allocate(&self, width: u32, height: u32, format: u32)
        -> sys_util::Result<(File, GpuMemoryDesc)>;
}

#[cfg(feature = "wl-dmabuf")]
pub struct GpuBufferDevice {
    device: gpu_buffer::Device,
}

#[cfg(feature = "wl-dmabuf")]
impl GpuMemoryAllocator for GpuBufferDevice {
    fn allocate(&self, width: u32, height: u32, format: u32) ->
        sys_util::Result<(File, GpuMemoryDesc)>
    {
        let buffer = match self.device.create_buffer(
            width,
            height,
            gpu_buffer::Format::from(format),
            // Linear layout is a requirement as virtio wayland guest expects
            // this for CPU access to the buffer. Scanout and texturing are
            // optional as the consumer (wayland compositor) is expected to
            // fall-back to a less efficient meachnisms for presentation if
            // neccesary. In practice, linear buffers for commonly used formats
            // will also support scanout and texturing.
            gpu_buffer::Flags::empty().use_linear(true)) {
            Ok(v) => v,
            Err(_) => return Err(sys_util::Error::new(EINVAL)),
        };
        // We only support one FD. Buffers with multiple planes are supported
        // as long as each plane is associated with the same handle.
        let fd = match buffer.export_plane_fd(0) {
            Ok(v) => v,
            Err(e) => return Err(sys_util::Error::new(e)),
        };

        let mut desc = GpuMemoryDesc::default();
        for i in 0..buffer.num_planes() {
            // Use stride and offset for plane if handle matches first plane.
            if buffer.plane_handle(i) == buffer.plane_handle(0) {
                desc.planes[i] = GpuMemoryPlaneDesc { stride: buffer.plane_stride(i),
                                                      offset: buffer.plane_offset(i) }
            }
        }

        Ok((fd, desc))
    }
}

#[cfg(feature = "wl-dmabuf")]
pub fn create_gpu_memory_allocator() -> Result<Option<Box<GpuMemoryAllocator>>, GpuAllocatorError> {
    let undesired: &[&str] = &["vgem", "pvr"];
    let fd = gpu_buffer::rendernode::open_device(undesired)
        .map_err(|_| GpuAllocatorError::OpenGpuBufferDevice)?;
    let device = gpu_buffer::Device::new(fd)
        .map_err(|_| GpuAllocatorError::CreateGpuBufferDevice)?;
    Ok(Some(Box::new(GpuBufferDevice { device })))
}

#[cfg(not(feature = "wl-dmabuf"))]
pub fn create_gpu_memory_allocator() -> Result<Option<Box<GpuMemoryAllocator>>, GpuAllocatorError> {
    Ok(None)
}