mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-07 01:53:24 +09:00
[Feature] (gbm): MobileGL's own GBM, so the compositor's buffers are the display host's frames
kwin never creates an EGL window surface. Its undefined libgbm symbols are seventeen entry points whose whole purpose is to hand out gbm_bo fds, and those fds are what Anland's display producer sends to the daemon on the phone - so the question "whose pixels are on the screen" is answered by "who allocated the bo", and today that is Mesa. The client can be loaded, initialized and rendering through glvnd with no window surface ever created, which is exactly what was measured. This is a complete GBM: every public entry point of the vendored Mesa header, with the semantics a compositor relies on - fds the caller owns, real mmap with refcounting, a swapchain that refuses to hand out a busy buffer, import by fd, and teardown that leaves no descriptor or mapping behind. The buffers come from the display host's frames; where there is no host, the device allocates from the dma-buf heap, falling back to an uncached heap and then to memfd, so the library stands on its own. The vendored gbm.h is Mesa's, verbatim: the entry points are the ABI, not our idea of it.
This commit is contained in:
Vendored
+538
@@ -0,0 +1,538 @@
|
||||
/*
|
||||
* Copyright © 2011 Intel Corporation
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* Authors:
|
||||
* Benjamin Franzke <benjaminfranzke@googlemail.com>
|
||||
*/
|
||||
|
||||
#ifndef _GBM_H_
|
||||
#define _GBM_H_
|
||||
|
||||
#define __GBM__ 1
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* \file gbm.h
|
||||
* \brief Generic Buffer Manager
|
||||
*/
|
||||
|
||||
struct gbm_device;
|
||||
struct gbm_bo;
|
||||
struct gbm_surface;
|
||||
|
||||
/**
|
||||
* \mainpage The Generic Buffer Manager
|
||||
*
|
||||
* This module provides an abstraction that the caller can use to request a
|
||||
* buffer from the underlying memory management system for the platform.
|
||||
*
|
||||
* This allows the creation of portable code whilst still allowing access to
|
||||
* the underlying memory manager.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Abstraction representing the handle to a buffer allocated by the
|
||||
* manager
|
||||
*/
|
||||
union gbm_bo_handle {
|
||||
void *ptr;
|
||||
int32_t s32;
|
||||
uint32_t u32;
|
||||
int64_t s64;
|
||||
uint64_t u64;
|
||||
};
|
||||
|
||||
/** Format of the allocated buffer */
|
||||
enum gbm_bo_format {
|
||||
/** RGB with 8 bits per channel in a 32 bit value */
|
||||
GBM_BO_FORMAT_XRGB8888,
|
||||
/** ARGB with 8 bits per channel in a 32 bit value */
|
||||
GBM_BO_FORMAT_ARGB8888
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* The FourCC format codes are taken from the drm_fourcc.h definition, and
|
||||
* re-namespaced. New GBM formats must not be added, unless they are
|
||||
* identical ports from drm_fourcc.
|
||||
*/
|
||||
#define __gbm_fourcc_code(a,b,c,d) ((uint32_t)(a) | ((uint32_t)(b) << 8) | \
|
||||
((uint32_t)(c) << 16) | ((uint32_t)(d) << 24))
|
||||
|
||||
#define GBM_FORMAT_BIG_ENDIAN (1<<31) /* format is big endian instead of little endian */
|
||||
|
||||
/* color index */
|
||||
#define GBM_FORMAT_C8 __gbm_fourcc_code('C', '8', ' ', ' ') /* [7:0] C */
|
||||
|
||||
/* 8 bpp Red */
|
||||
#define GBM_FORMAT_R8 __gbm_fourcc_code('R', '8', ' ', ' ') /* [7:0] R */
|
||||
|
||||
/* 16 bpp Red */
|
||||
#define GBM_FORMAT_R16 __gbm_fourcc_code('R', '1', '6', ' ') /* [15:0] R little endian */
|
||||
|
||||
/* 16 bpp RG */
|
||||
#define GBM_FORMAT_GR88 __gbm_fourcc_code('G', 'R', '8', '8') /* [15:0] G:R 8:8 little endian */
|
||||
|
||||
/* 32 bpp RG */
|
||||
#define GBM_FORMAT_RG1616 __gbm_fourcc_code('R', 'G', '3', '2') /* [31:0] R:G 16:16 little endian */
|
||||
#define GBM_FORMAT_GR1616 __gbm_fourcc_code('G', 'R', '3', '2') /* [31:0] G:R 16:16 little endian */
|
||||
|
||||
/* 8 bpp RGB */
|
||||
#define GBM_FORMAT_RGB332 __gbm_fourcc_code('R', 'G', 'B', '8') /* [7:0] R:G:B 3:3:2 */
|
||||
#define GBM_FORMAT_BGR233 __gbm_fourcc_code('B', 'G', 'R', '8') /* [7:0] B:G:R 2:3:3 */
|
||||
|
||||
/* 16 bpp RGB */
|
||||
#define GBM_FORMAT_XRGB4444 __gbm_fourcc_code('X', 'R', '1', '2') /* [15:0] x:R:G:B 4:4:4:4 little endian */
|
||||
#define GBM_FORMAT_XBGR4444 __gbm_fourcc_code('X', 'B', '1', '2') /* [15:0] x:B:G:R 4:4:4:4 little endian */
|
||||
#define GBM_FORMAT_RGBX4444 __gbm_fourcc_code('R', 'X', '1', '2') /* [15:0] R:G:B:x 4:4:4:4 little endian */
|
||||
#define GBM_FORMAT_BGRX4444 __gbm_fourcc_code('B', 'X', '1', '2') /* [15:0] B:G:R:x 4:4:4:4 little endian */
|
||||
|
||||
#define GBM_FORMAT_ARGB4444 __gbm_fourcc_code('A', 'R', '1', '2') /* [15:0] A:R:G:B 4:4:4:4 little endian */
|
||||
#define GBM_FORMAT_ABGR4444 __gbm_fourcc_code('A', 'B', '1', '2') /* [15:0] A:B:G:R 4:4:4:4 little endian */
|
||||
#define GBM_FORMAT_RGBA4444 __gbm_fourcc_code('R', 'A', '1', '2') /* [15:0] R:G:B:A 4:4:4:4 little endian */
|
||||
#define GBM_FORMAT_BGRA4444 __gbm_fourcc_code('B', 'A', '1', '2') /* [15:0] B:G:R:A 4:4:4:4 little endian */
|
||||
|
||||
#define GBM_FORMAT_XRGB1555 __gbm_fourcc_code('X', 'R', '1', '5') /* [15:0] x:R:G:B 1:5:5:5 little endian */
|
||||
#define GBM_FORMAT_XBGR1555 __gbm_fourcc_code('X', 'B', '1', '5') /* [15:0] x:B:G:R 1:5:5:5 little endian */
|
||||
#define GBM_FORMAT_RGBX5551 __gbm_fourcc_code('R', 'X', '1', '5') /* [15:0] R:G:B:x 5:5:5:1 little endian */
|
||||
#define GBM_FORMAT_BGRX5551 __gbm_fourcc_code('B', 'X', '1', '5') /* [15:0] B:G:R:x 5:5:5:1 little endian */
|
||||
|
||||
#define GBM_FORMAT_ARGB1555 __gbm_fourcc_code('A', 'R', '1', '5') /* [15:0] A:R:G:B 1:5:5:5 little endian */
|
||||
#define GBM_FORMAT_ABGR1555 __gbm_fourcc_code('A', 'B', '1', '5') /* [15:0] A:B:G:R 1:5:5:5 little endian */
|
||||
#define GBM_FORMAT_RGBA5551 __gbm_fourcc_code('R', 'A', '1', '5') /* [15:0] R:G:B:A 5:5:5:1 little endian */
|
||||
#define GBM_FORMAT_BGRA5551 __gbm_fourcc_code('B', 'A', '1', '5') /* [15:0] B:G:R:A 5:5:5:1 little endian */
|
||||
|
||||
#define GBM_FORMAT_RGB565 __gbm_fourcc_code('R', 'G', '1', '6') /* [15:0] R:G:B 5:6:5 little endian */
|
||||
#define GBM_FORMAT_BGR565 __gbm_fourcc_code('B', 'G', '1', '6') /* [15:0] B:G:R 5:6:5 little endian */
|
||||
|
||||
/* 24 bpp RGB */
|
||||
#define GBM_FORMAT_RGB888 __gbm_fourcc_code('R', 'G', '2', '4') /* [23:0] R:G:B little endian */
|
||||
#define GBM_FORMAT_BGR888 __gbm_fourcc_code('B', 'G', '2', '4') /* [23:0] B:G:R little endian */
|
||||
|
||||
/* 32 bpp RGB */
|
||||
#define GBM_FORMAT_XRGB8888 __gbm_fourcc_code('X', 'R', '2', '4') /* [31:0] x:R:G:B 8:8:8:8 little endian */
|
||||
#define GBM_FORMAT_XBGR8888 __gbm_fourcc_code('X', 'B', '2', '4') /* [31:0] x:B:G:R 8:8:8:8 little endian */
|
||||
#define GBM_FORMAT_RGBX8888 __gbm_fourcc_code('R', 'X', '2', '4') /* [31:0] R:G:B:x 8:8:8:8 little endian */
|
||||
#define GBM_FORMAT_BGRX8888 __gbm_fourcc_code('B', 'X', '2', '4') /* [31:0] B:G:R:x 8:8:8:8 little endian */
|
||||
|
||||
#define GBM_FORMAT_ARGB8888 __gbm_fourcc_code('A', 'R', '2', '4') /* [31:0] A:R:G:B 8:8:8:8 little endian */
|
||||
#define GBM_FORMAT_ABGR8888 __gbm_fourcc_code('A', 'B', '2', '4') /* [31:0] A:B:G:R 8:8:8:8 little endian */
|
||||
#define GBM_FORMAT_RGBA8888 __gbm_fourcc_code('R', 'A', '2', '4') /* [31:0] R:G:B:A 8:8:8:8 little endian */
|
||||
#define GBM_FORMAT_BGRA8888 __gbm_fourcc_code('B', 'A', '2', '4') /* [31:0] B:G:R:A 8:8:8:8 little endian */
|
||||
|
||||
#define GBM_FORMAT_XRGB2101010 __gbm_fourcc_code('X', 'R', '3', '0') /* [31:0] x:R:G:B 2:10:10:10 little endian */
|
||||
#define GBM_FORMAT_XBGR2101010 __gbm_fourcc_code('X', 'B', '3', '0') /* [31:0] x:B:G:R 2:10:10:10 little endian */
|
||||
#define GBM_FORMAT_RGBX1010102 __gbm_fourcc_code('R', 'X', '3', '0') /* [31:0] R:G:B:x 10:10:10:2 little endian */
|
||||
#define GBM_FORMAT_BGRX1010102 __gbm_fourcc_code('B', 'X', '3', '0') /* [31:0] B:G:R:x 10:10:10:2 little endian */
|
||||
|
||||
#define GBM_FORMAT_ARGB2101010 __gbm_fourcc_code('A', 'R', '3', '0') /* [31:0] A:R:G:B 2:10:10:10 little endian */
|
||||
#define GBM_FORMAT_ABGR2101010 __gbm_fourcc_code('A', 'B', '3', '0') /* [31:0] A:B:G:R 2:10:10:10 little endian */
|
||||
#define GBM_FORMAT_RGBA1010102 __gbm_fourcc_code('R', 'A', '3', '0') /* [31:0] R:G:B:A 10:10:10:2 little endian */
|
||||
#define GBM_FORMAT_BGRA1010102 __gbm_fourcc_code('B', 'A', '3', '0') /* [31:0] B:G:R:A 10:10:10:2 little endian */
|
||||
|
||||
/* 64 bpp RGB */
|
||||
#define GBM_FORMAT_XBGR16161616 __gbm_fourcc_code('X', 'B', '4', '8') /* [63:0] x:B:G:R 16:16:16:16 little endian */
|
||||
|
||||
#define GBM_FORMAT_ABGR16161616 __gbm_fourcc_code('A', 'B', '4', '8') /* [63:0] A:B:G:R 16:16:16:16 little endian */
|
||||
|
||||
/*
|
||||
* Floating point 64bpp RGB
|
||||
* IEEE 754-2008 binary16 half-precision float
|
||||
* [15:0] sign:exponent:mantissa 1:5:10
|
||||
*/
|
||||
#define GBM_FORMAT_XBGR16161616F __gbm_fourcc_code('X', 'B', '4', 'H') /* [63:0] x:B:G:R 16:16:16:16 little endian */
|
||||
|
||||
#define GBM_FORMAT_ABGR16161616F __gbm_fourcc_code('A', 'B', '4', 'H') /* [63:0] A:B:G:R 16:16:16:16 little endian */
|
||||
|
||||
/* packed YCbCr */
|
||||
#define GBM_FORMAT_YUYV __gbm_fourcc_code('Y', 'U', 'Y', 'V') /* [31:0] Cr0:Y1:Cb0:Y0 8:8:8:8 little endian */
|
||||
#define GBM_FORMAT_YVYU __gbm_fourcc_code('Y', 'V', 'Y', 'U') /* [31:0] Cb0:Y1:Cr0:Y0 8:8:8:8 little endian */
|
||||
#define GBM_FORMAT_UYVY __gbm_fourcc_code('U', 'Y', 'V', 'Y') /* [31:0] Y1:Cr0:Y0:Cb0 8:8:8:8 little endian */
|
||||
#define GBM_FORMAT_VYUY __gbm_fourcc_code('V', 'Y', 'U', 'Y') /* [31:0] Y1:Cb0:Y0:Cr0 8:8:8:8 little endian */
|
||||
|
||||
#define GBM_FORMAT_AYUV __gbm_fourcc_code('A', 'Y', 'U', 'V') /* [31:0] A:Y:Cb:Cr 8:8:8:8 little endian */
|
||||
|
||||
/*
|
||||
* 2 plane YCbCr
|
||||
* index 0 = Y plane, [7:0] Y
|
||||
* index 1 = Cr:Cb plane, [15:0] Cr:Cb little endian
|
||||
* or
|
||||
* index 1 = Cb:Cr plane, [15:0] Cb:Cr little endian
|
||||
*/
|
||||
#define GBM_FORMAT_NV12 __gbm_fourcc_code('N', 'V', '1', '2') /* 2x2 subsampled Cr:Cb plane */
|
||||
#define GBM_FORMAT_NV21 __gbm_fourcc_code('N', 'V', '2', '1') /* 2x2 subsampled Cb:Cr plane */
|
||||
#define GBM_FORMAT_NV16 __gbm_fourcc_code('N', 'V', '1', '6') /* 2x1 subsampled Cr:Cb plane */
|
||||
#define GBM_FORMAT_NV61 __gbm_fourcc_code('N', 'V', '6', '1') /* 2x1 subsampled Cb:Cr plane */
|
||||
|
||||
/*
|
||||
* 3 plane YCbCr
|
||||
* index 0: Y plane, [7:0] Y
|
||||
* index 1: Cb plane, [7:0] Cb
|
||||
* index 2: Cr plane, [7:0] Cr
|
||||
* or
|
||||
* index 1: Cr plane, [7:0] Cr
|
||||
* index 2: Cb plane, [7:0] Cb
|
||||
*/
|
||||
#define GBM_FORMAT_YUV410 __gbm_fourcc_code('Y', 'U', 'V', '9') /* 4x4 subsampled Cb (1) and Cr (2) planes */
|
||||
#define GBM_FORMAT_YVU410 __gbm_fourcc_code('Y', 'V', 'U', '9') /* 4x4 subsampled Cr (1) and Cb (2) planes */
|
||||
#define GBM_FORMAT_YUV411 __gbm_fourcc_code('Y', 'U', '1', '1') /* 4x1 subsampled Cb (1) and Cr (2) planes */
|
||||
#define GBM_FORMAT_YVU411 __gbm_fourcc_code('Y', 'V', '1', '1') /* 4x1 subsampled Cr (1) and Cb (2) planes */
|
||||
#define GBM_FORMAT_YUV420 __gbm_fourcc_code('Y', 'U', '1', '2') /* 2x2 subsampled Cb (1) and Cr (2) planes */
|
||||
#define GBM_FORMAT_YVU420 __gbm_fourcc_code('Y', 'V', '1', '2') /* 2x2 subsampled Cr (1) and Cb (2) planes */
|
||||
#define GBM_FORMAT_YUV422 __gbm_fourcc_code('Y', 'U', '1', '6') /* 2x1 subsampled Cb (1) and Cr (2) planes */
|
||||
#define GBM_FORMAT_YVU422 __gbm_fourcc_code('Y', 'V', '1', '6') /* 2x1 subsampled Cr (1) and Cb (2) planes */
|
||||
#define GBM_FORMAT_YUV444 __gbm_fourcc_code('Y', 'U', '2', '4') /* non-subsampled Cb (1) and Cr (2) planes */
|
||||
#define GBM_FORMAT_YVU444 __gbm_fourcc_code('Y', 'V', '2', '4') /* non-subsampled Cr (1) and Cb (2) planes */
|
||||
|
||||
struct gbm_format_name_desc {
|
||||
char name[5];
|
||||
};
|
||||
|
||||
/**
|
||||
* Flags to indicate the intended use for the buffer - these are passed into
|
||||
* gbm_bo_create(). The caller must set the union of all the flags that are
|
||||
* appropriate
|
||||
*
|
||||
* \sa Use gbm_device_is_format_supported() to check if the combination of format
|
||||
* and use flags are supported
|
||||
*/
|
||||
enum gbm_bo_flags {
|
||||
/**
|
||||
* Buffer is going to be presented to the screen using an API such as KMS
|
||||
*/
|
||||
GBM_BO_USE_SCANOUT = (1 << 0),
|
||||
/**
|
||||
* Buffer is going to be used as cursor
|
||||
*/
|
||||
GBM_BO_USE_CURSOR = (1 << 1),
|
||||
/**
|
||||
* Deprecated
|
||||
*/
|
||||
GBM_BO_USE_CURSOR_64X64 = GBM_BO_USE_CURSOR,
|
||||
/**
|
||||
* Buffer is to be used for rendering - for example it is going to be used
|
||||
* as the storage for a color buffer
|
||||
*/
|
||||
GBM_BO_USE_RENDERING = (1 << 2),
|
||||
/**
|
||||
* Buffer can be used for gbm_bo_write. This is guaranteed to work
|
||||
* with GBM_BO_USE_CURSOR, but may not work for other combinations.
|
||||
*/
|
||||
GBM_BO_USE_WRITE = (1 << 3),
|
||||
/**
|
||||
* Buffer is linear, i.e. not tiled.
|
||||
*/
|
||||
GBM_BO_USE_LINEAR = (1 << 4),
|
||||
/**
|
||||
* Buffer is protected, i.e. encrypted and not readable by CPU or any
|
||||
* other non-secure / non-trusted components nor by non-trusted OpenGL,
|
||||
* OpenCL, and Vulkan applications.
|
||||
*/
|
||||
GBM_BO_USE_PROTECTED = (1 << 5),
|
||||
|
||||
/**
|
||||
* The buffer will be used for front buffer rendering. On some
|
||||
* platforms this may (for example) disable framebuffer compression
|
||||
* to avoid problems with compression flags data being out of sync
|
||||
* with pixel data.
|
||||
*/
|
||||
GBM_BO_USE_FRONT_RENDERING = (1 << 6),
|
||||
|
||||
/**
|
||||
* Allow the driver to select fixed-rate compression parameters.
|
||||
*/
|
||||
GBM_BO_FIXED_COMPRESSION_DEFAULT = (1 << 7),
|
||||
|
||||
/**
|
||||
* Fixed-rate compression: at least 1bpc, less than 2bpc
|
||||
*/
|
||||
GBM_BO_FIXED_COMPRESSION_1BPC = (2 << 7),
|
||||
|
||||
/**
|
||||
* Fixed-rate compression: at least 2bpc, less than 3bpc
|
||||
*/
|
||||
GBM_BO_FIXED_COMPRESSION_2BPC = (3 << 7),
|
||||
|
||||
/**
|
||||
* Fixed-rate compression: at least 3bpc, less than 4bpc
|
||||
*/
|
||||
GBM_BO_FIXED_COMPRESSION_3BPC = (4 << 7),
|
||||
|
||||
/**
|
||||
* Fixed-rate compression: at least 4bpc, less than 5bpc
|
||||
*/
|
||||
GBM_BO_FIXED_COMPRESSION_4BPC = (5 << 7),
|
||||
|
||||
/**
|
||||
* Fixed-rate compression: at least 5bpc, less than 6bpc
|
||||
*/
|
||||
GBM_BO_FIXED_COMPRESSION_5BPC = (6 << 7),
|
||||
|
||||
/**
|
||||
* Fixed-rate compression: at least 6bpc, less than 7bpc
|
||||
*/
|
||||
GBM_BO_FIXED_COMPRESSION_6BPC = (7 << 7),
|
||||
|
||||
/**
|
||||
* Fixed-rate compression: at least 7bpc, less than 8bpc
|
||||
*/
|
||||
GBM_BO_FIXED_COMPRESSION_7BPC = (8 << 7),
|
||||
|
||||
/**
|
||||
* Fixed-rate compression: at least 8bpc, less than 9bpc
|
||||
*/
|
||||
GBM_BO_FIXED_COMPRESSION_8BPC = (9 << 7),
|
||||
|
||||
/**
|
||||
* Fixed-rate compression: at least 9bpc, less than 10bpc
|
||||
*/
|
||||
GBM_BO_FIXED_COMPRESSION_9BPC = (10 << 7),
|
||||
|
||||
/**
|
||||
* Fixed-rate compression: at least 10bpc, less than 11bpc
|
||||
*/
|
||||
GBM_BO_FIXED_COMPRESSION_10BPC = (11 << 7),
|
||||
|
||||
/**
|
||||
* Fixed-rate compression: at least 11bpc, less than 12bpc
|
||||
*/
|
||||
GBM_BO_FIXED_COMPRESSION_11BPC = (12 << 7),
|
||||
|
||||
/**
|
||||
* Fixed-rate compression: at least 12bpc, no maximum rate
|
||||
*/
|
||||
GBM_BO_FIXED_COMPRESSION_12BPC = (13 << 7),
|
||||
|
||||
/* next available value is (1 << 11) */
|
||||
};
|
||||
|
||||
#define GBM_BO_FIXED_COMPRESSION_MASK (((1 << 11) - 1) & ~((1 << 7) - 1))
|
||||
|
||||
int
|
||||
gbm_device_get_fd(struct gbm_device *gbm);
|
||||
|
||||
const char *
|
||||
gbm_device_get_backend_name(struct gbm_device *gbm);
|
||||
|
||||
int
|
||||
gbm_device_is_format_supported(struct gbm_device *gbm,
|
||||
uint32_t format, uint32_t flags);
|
||||
|
||||
int
|
||||
gbm_device_get_format_modifier_plane_count(struct gbm_device *gbm,
|
||||
uint32_t format,
|
||||
uint64_t modifier);
|
||||
|
||||
void
|
||||
gbm_device_destroy(struct gbm_device *gbm);
|
||||
|
||||
struct gbm_device *
|
||||
gbm_create_device(int fd);
|
||||
|
||||
struct gbm_bo *
|
||||
gbm_bo_create(struct gbm_device *gbm,
|
||||
uint32_t width, uint32_t height,
|
||||
uint32_t format, uint32_t flags);
|
||||
|
||||
struct gbm_bo *
|
||||
gbm_bo_create_with_modifiers(struct gbm_device *gbm,
|
||||
uint32_t width, uint32_t height,
|
||||
uint32_t format,
|
||||
const uint64_t *modifiers,
|
||||
const unsigned int count);
|
||||
|
||||
struct gbm_bo *
|
||||
gbm_bo_create_with_modifiers2(struct gbm_device *gbm,
|
||||
uint32_t width, uint32_t height,
|
||||
uint32_t format,
|
||||
const uint64_t *modifiers,
|
||||
const unsigned int count,
|
||||
uint32_t flags);
|
||||
|
||||
#define GBM_BO_IMPORT_WL_BUFFER 0x5501
|
||||
#define GBM_BO_IMPORT_EGL_IMAGE 0x5502
|
||||
#define GBM_BO_IMPORT_FD 0x5503
|
||||
#define GBM_BO_IMPORT_FD_MODIFIER 0x5504
|
||||
|
||||
struct gbm_import_fd_data {
|
||||
int fd;
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
uint32_t stride;
|
||||
uint32_t format;
|
||||
};
|
||||
|
||||
#define GBM_MAX_PLANES 4
|
||||
|
||||
struct gbm_import_fd_modifier_data {
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
uint32_t format;
|
||||
uint32_t num_fds;
|
||||
int fds[GBM_MAX_PLANES];
|
||||
int strides[GBM_MAX_PLANES];
|
||||
int offsets[GBM_MAX_PLANES];
|
||||
uint64_t modifier;
|
||||
};
|
||||
|
||||
struct gbm_bo *
|
||||
gbm_bo_import(struct gbm_device *gbm, uint32_t type,
|
||||
void *buffer, uint32_t flags);
|
||||
|
||||
/**
|
||||
* Flags to indicate the type of mapping for the buffer - these are
|
||||
* passed into gbm_bo_map(). The caller must set the union of all the
|
||||
* flags that are appropriate.
|
||||
*
|
||||
* These flags are independent of the GBM_BO_USE_* creation flags. However,
|
||||
* mapping the buffer may require copying to/from a staging buffer.
|
||||
*
|
||||
* See also: pipe_map_flags
|
||||
*/
|
||||
enum gbm_bo_transfer_flags {
|
||||
/**
|
||||
* Buffer contents read back (or accessed directly) at transfer
|
||||
* create time.
|
||||
*/
|
||||
GBM_BO_TRANSFER_READ = (1 << 0),
|
||||
/**
|
||||
* Buffer contents will be written back at unmap time
|
||||
* (or modified as a result of being accessed directly).
|
||||
*/
|
||||
GBM_BO_TRANSFER_WRITE = (1 << 1),
|
||||
/**
|
||||
* Read/modify/write
|
||||
*/
|
||||
GBM_BO_TRANSFER_READ_WRITE = (GBM_BO_TRANSFER_READ | GBM_BO_TRANSFER_WRITE),
|
||||
};
|
||||
|
||||
void *
|
||||
gbm_bo_map(struct gbm_bo *bo,
|
||||
uint32_t x, uint32_t y, uint32_t width, uint32_t height,
|
||||
uint32_t flags, uint32_t *stride, void **map_data);
|
||||
|
||||
void
|
||||
gbm_bo_unmap(struct gbm_bo *bo, void *map_data);
|
||||
|
||||
uint32_t
|
||||
gbm_bo_get_width(struct gbm_bo *bo);
|
||||
|
||||
uint32_t
|
||||
gbm_bo_get_height(struct gbm_bo *bo);
|
||||
|
||||
uint32_t
|
||||
gbm_bo_get_stride(struct gbm_bo *bo);
|
||||
|
||||
uint32_t
|
||||
gbm_bo_get_stride_for_plane(struct gbm_bo *bo, int plane);
|
||||
|
||||
uint32_t
|
||||
gbm_bo_get_format(struct gbm_bo *bo);
|
||||
|
||||
uint32_t
|
||||
gbm_bo_get_bpp(struct gbm_bo *bo);
|
||||
|
||||
uint32_t
|
||||
gbm_bo_get_offset(struct gbm_bo *bo, int plane);
|
||||
|
||||
struct gbm_device *
|
||||
gbm_bo_get_device(struct gbm_bo *bo);
|
||||
|
||||
union gbm_bo_handle
|
||||
gbm_bo_get_handle(struct gbm_bo *bo);
|
||||
|
||||
int
|
||||
gbm_bo_get_fd(struct gbm_bo *bo);
|
||||
|
||||
uint64_t
|
||||
gbm_bo_get_modifier(struct gbm_bo *bo);
|
||||
|
||||
int
|
||||
gbm_bo_get_plane_count(struct gbm_bo *bo);
|
||||
|
||||
union gbm_bo_handle
|
||||
gbm_bo_get_handle_for_plane(struct gbm_bo *bo, int plane);
|
||||
|
||||
int
|
||||
gbm_bo_get_fd_for_plane(struct gbm_bo *bo, int plane);
|
||||
|
||||
int
|
||||
gbm_bo_write(struct gbm_bo *bo, const void *buf, size_t count);
|
||||
|
||||
void
|
||||
gbm_bo_set_user_data(struct gbm_bo *bo, void *data,
|
||||
void (*destroy_user_data)(struct gbm_bo *, void *));
|
||||
|
||||
void *
|
||||
gbm_bo_get_user_data(struct gbm_bo *bo);
|
||||
|
||||
void
|
||||
gbm_bo_destroy(struct gbm_bo *bo);
|
||||
|
||||
struct gbm_surface *
|
||||
gbm_surface_create(struct gbm_device *gbm,
|
||||
uint32_t width, uint32_t height,
|
||||
uint32_t format, uint32_t flags);
|
||||
|
||||
struct gbm_surface *
|
||||
gbm_surface_create_with_modifiers(struct gbm_device *gbm,
|
||||
uint32_t width, uint32_t height,
|
||||
uint32_t format,
|
||||
const uint64_t *modifiers,
|
||||
const unsigned int count);
|
||||
|
||||
struct gbm_surface *
|
||||
gbm_surface_create_with_modifiers2(struct gbm_device *gbm,
|
||||
uint32_t width, uint32_t height,
|
||||
uint32_t format,
|
||||
const uint64_t *modifiers,
|
||||
const unsigned int count,
|
||||
uint32_t flags);
|
||||
|
||||
struct gbm_bo *
|
||||
gbm_surface_lock_front_buffer(struct gbm_surface *surface);
|
||||
|
||||
void
|
||||
gbm_surface_release_buffer(struct gbm_surface *surface, struct gbm_bo *bo);
|
||||
|
||||
int
|
||||
gbm_surface_has_free_buffers(struct gbm_surface *surface);
|
||||
|
||||
void
|
||||
gbm_surface_destroy(struct gbm_surface *surface);
|
||||
|
||||
char *
|
||||
gbm_format_get_name(uint32_t gbm_format, struct gbm_format_name_desc *desc);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -889,6 +889,65 @@ if(NOT ANDROID)
|
||||
)
|
||||
endif()
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# MobileGLGbm: the GBM drop-in the container's compositor loads.
|
||||
#
|
||||
# kwin - the Wayland compositor in the container half of the split - allocates the
|
||||
# buffers it renders into and scans out through libgbm, and the dma-buf fds it gets
|
||||
# back are what the Anland display producer ships to the phone. Mesa's libgbm cannot
|
||||
# serve that: its buffers belong to the host GPU, and the phone would receive a copy
|
||||
# through a path that was never designed to carry one. This target is the library
|
||||
# that is loaded in its place, under the name a loader looks for.
|
||||
#
|
||||
# It is deliberately NOT part of libMobileGL.so. The compositor needs one file named
|
||||
# libgbm.so that it loads instead of Mesa's, and folding this code into the GL
|
||||
# library would put a second GL frontend, a second Vulkan instance and a second
|
||||
# glslang in the compositor's address space to serve an API that shares none of them.
|
||||
#
|
||||
# The include paths below are borrowed rather than linked. Includes.h - which this
|
||||
# library includes for the MobileGL types and the MGLOG macros - pulls in the GL,
|
||||
# EGL, Vulkan, glslang, SPIRV-Cross and xxHash HEADERS transitively, and headers are
|
||||
# all this library wants from them: linking any of those static libraries into a
|
||||
# libgbm would embed a copy of each in a process that already has one.
|
||||
#
|
||||
# -Bsymbolic-functions, for the same reason the main target carries it: the point of
|
||||
# this library is to REPLACE a libgbm that may already be loaded in the process, so
|
||||
# every internal reference has to bind to this file's own definitions rather than to
|
||||
# whatever else the process has under the same names.
|
||||
# ---------------------------------------------------------------------------
|
||||
add_library(MobileGLGbm SHARED
|
||||
MobileGL/MG_Gbm/GbmDevice.cpp
|
||||
MobileGL/MG_Gbm/GbmApi.cpp
|
||||
# MGLOG_I/W/E expand to a call to MobileGL::MG_Util::Debug::Log, and this is the
|
||||
# one translation unit that defines it. It is compiled in rather than linked from
|
||||
# libMobileGL.so because this library has to load into a compositor that has no
|
||||
# libMobileGL in it at all - and because a libgbm with an undefined symbol is a
|
||||
# library that fails on the first diagnostic rather than at a link step somebody
|
||||
# could have read.
|
||||
MobileGL/MG_Util/Debug/Log.cpp
|
||||
)
|
||||
|
||||
# The vendored gbm.h first: this library implements THAT declaration set, and a
|
||||
# system <gbm.h> from another Mesa version on the include path ahead of it would
|
||||
# change the ABI under the implementations in GbmApi.cpp.
|
||||
target_include_directories(MobileGLGbm PRIVATE
|
||||
${CMAKE_SOURCE_DIR}/3rdparty/gbm/include
|
||||
${MOBILEGL_INCLUDE_DIR}
|
||||
${CMAKE_SOURCE_DIR}/3rdparty/glslang
|
||||
${CMAKE_SOURCE_DIR}/3rdparty/SPIRV-Cross
|
||||
${CMAKE_SOURCE_DIR}/3rdparty/xxHash
|
||||
${CMAKE_SOURCE_DIR}/3rdparty/Vulkan-Headers/include
|
||||
)
|
||||
|
||||
set_target_properties(MobileGLGbm PROPERTIES
|
||||
OUTPUT_NAME gbm
|
||||
POSITION_INDEPENDENT_CODE ON
|
||||
)
|
||||
|
||||
if (UNIX AND NOT APPLE)
|
||||
target_link_options(MobileGLGbm PRIVATE "LINKER:-Bsymbolic-functions")
|
||||
endif()
|
||||
|
||||
if (TRACY_ENABLE)
|
||||
target_link_libraries(${CMAKE_PROJECT_NAME} PUBLIC Tracy::TracyClient)
|
||||
target_link_libraries(${CMAKE_PROJECT_NAME}_s PUBLIC Tracy::TracyClient)
|
||||
|
||||
@@ -0,0 +1,663 @@
|
||||
// MobileGL - MobileGL/MG_Gbm/GbmApi.cpp
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
//
|
||||
// The extern "C" surface: every entry point gbm.h declares, and nothing else.
|
||||
//
|
||||
// This file is a translation layer, and it is kept that way on purpose. Each entry
|
||||
// point does three things - check the pointers it was handed, take the device's one
|
||||
// mutex for the whole call, and hand the work to GbmDevice.cpp - so the concurrency
|
||||
// rule ("one mutex per device, taken once at the API boundary, never inside the
|
||||
// allocation code") is visible in one place instead of being spread over the
|
||||
// backends. A missing lock is a bug that only shows up in the compositor, so the
|
||||
// boilerplate is deliberately identical in every function.
|
||||
//
|
||||
// Two conventions are observable from outside and are stated here because they are
|
||||
// the ones a caller can get wrong:
|
||||
//
|
||||
// * gbm_bo_get_fd and gbm_bo_get_fd_for_plane return a DUP the caller owns. The
|
||||
// caller closes what it was given; the buffer's own descriptor is closed once,
|
||||
// when the buffer is destroyed, and never by a caller's close.
|
||||
// * Nothing here throws and nothing here aborts. A failure is reported the way
|
||||
// GBM reports failures: null or -1 with errno set, so that the strerror(errno)
|
||||
// a caller prints describes the failure that actually happened.
|
||||
|
||||
#include "GbmInternal.h"
|
||||
|
||||
#include <cerrno>
|
||||
|
||||
using namespace MobileGL;
|
||||
using namespace MobileGL::MG_Gbm;
|
||||
|
||||
namespace
|
||||
{
|
||||
// The one mutex that guards a record, reached through the device that owns it.
|
||||
// Null means the pointer was not one this library handed out, and every entry
|
||||
// point turns that into its own failure answer rather than dereferencing it.
|
||||
//
|
||||
// A buffer or surface with no owner cannot exist while the library is used
|
||||
// correctly - the device destroys everything it still holds before it goes away
|
||||
// - so null here is a defence against a stale pointer rather than a state the
|
||||
// code produces.
|
||||
std::mutex* MutexOf(struct gbm_device* device) noexcept
|
||||
{
|
||||
return device != nullptr ? &device->Impl.Mutex : nullptr;
|
||||
}
|
||||
|
||||
std::mutex* MutexOf(struct gbm_bo* bo) noexcept
|
||||
{
|
||||
return bo != nullptr ? MutexOf(bo->Impl.Owner) : nullptr;
|
||||
}
|
||||
|
||||
std::mutex* MutexOf(struct gbm_surface* surface) noexcept
|
||||
{
|
||||
return surface != nullptr ? MutexOf(surface->Impl.Owner) : nullptr;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Device
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
extern "C" struct gbm_device* gbm_create_device(int fd)
|
||||
{
|
||||
// No lock to take: the device being built is not reachable by any other thread
|
||||
// until this call returns it.
|
||||
return GbmDeviceCreate(fd);
|
||||
}
|
||||
|
||||
extern "C" void gbm_device_destroy(struct gbm_device* gbm)
|
||||
{
|
||||
std::mutex* mutex = MutexOf(gbm);
|
||||
if (mutex == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// The lock is taken and deliberately never released: the mutex lives inside the
|
||||
// record this call deletes, and unlocking a destroyed mutex is worse than
|
||||
// leaving a dead one locked. Any other thread touching this device after its
|
||||
// owner declared it dead is already outside what GBM can be asked to survive.
|
||||
mutex->lock();
|
||||
GbmDeviceDestroy(*gbm);
|
||||
}
|
||||
|
||||
extern "C" int gbm_device_get_fd(struct gbm_device* gbm)
|
||||
{
|
||||
std::mutex* mutex = MutexOf(gbm);
|
||||
if (mutex == nullptr)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
std::lock_guard<std::mutex> guard(*mutex);
|
||||
// The device's own dup of the fd it was created with, or -1 when it was created
|
||||
// without one and no render node could be opened. That is not an error state:
|
||||
// the self-allocated backend never talks to the node, so a device can be fully
|
||||
// usable and still have no descriptor to report.
|
||||
return gbm->Impl.DeviceFd;
|
||||
}
|
||||
|
||||
extern "C" const char* gbm_device_get_backend_name(struct gbm_device* gbm)
|
||||
{
|
||||
std::mutex* mutex = MutexOf(gbm);
|
||||
if (mutex == nullptr)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return nullptr;
|
||||
}
|
||||
std::lock_guard<std::mutex> guard(*mutex);
|
||||
// The name says which memory this device hands out ("mobilegl-dma-heap" or
|
||||
// "mobilegl-memfd"), because the callers that read it - a compositor logging its
|
||||
// backend, a peer test recording what it got - are asking exactly that. The
|
||||
// string lives in the device and outlives every call that can read it.
|
||||
return gbm->Impl.BackendName.c_str();
|
||||
}
|
||||
|
||||
extern "C" int gbm_device_is_format_supported(struct gbm_device* gbm, uint32_t format, uint32_t flags)
|
||||
{
|
||||
std::mutex* mutex = MutexOf(gbm);
|
||||
if (mutex == nullptr)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return 0;
|
||||
}
|
||||
std::lock_guard<std::mutex> guard(*mutex);
|
||||
// Every buffer this device produces is linear, mappable and shareable, so the
|
||||
// usage flags do not narrow the answer to any of the usual combinations -
|
||||
// including GBM_BO_USE_SCANOUT, which a linear dma-buf from the system heap
|
||||
// satisfies. The one request the backend cannot honour is protected memory.
|
||||
if ((flags & GBM_BO_USE_PROTECTED) != 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return GbmFormatIsSupported(format) ? 1 : 0;
|
||||
}
|
||||
|
||||
extern "C" int gbm_device_get_format_modifier_plane_count(struct gbm_device* gbm, uint32_t format, uint64_t modifier)
|
||||
{
|
||||
std::mutex* mutex = MutexOf(gbm);
|
||||
if (mutex == nullptr)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return 0;
|
||||
}
|
||||
std::lock_guard<std::mutex> guard(*mutex);
|
||||
// Only linear memory exists here, so any other modifier names a buffer this
|
||||
// device will never produce and the answer is zero planes. INVALID is answered
|
||||
// as well, because it means "no modifier was declared", which is the same
|
||||
// layout question asked without an answer.
|
||||
if (modifier != DrmFormatModLinear && modifier != DrmFormatModInvalid)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
GbmPlaneLayout planes[GbmMaxPlanes] = {};
|
||||
Int planeCount = 0;
|
||||
SizeT totalBytes = 0;
|
||||
if (!GbmDescribeFormat(format, 1, 1, planes, planeCount, totalBytes))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return planeCount;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Buffer objects
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
extern "C" struct gbm_bo* gbm_bo_create(struct gbm_device* gbm, uint32_t width, uint32_t height, uint32_t format,
|
||||
uint32_t flags)
|
||||
{
|
||||
std::mutex* mutex = MutexOf(gbm);
|
||||
if (mutex == nullptr)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return nullptr;
|
||||
}
|
||||
std::lock_guard<std::mutex> guard(*mutex);
|
||||
// No modifier list: the buffer is linear and gbm_bo_get_modifier says so.
|
||||
return GbmBufferCreate(*gbm, width, height, format, flags, nullptr, 0);
|
||||
}
|
||||
|
||||
extern "C" struct gbm_bo* gbm_bo_create_with_modifiers(struct gbm_device* gbm, uint32_t width, uint32_t height,
|
||||
uint32_t format, const uint64_t* modifiers,
|
||||
const unsigned int count)
|
||||
{
|
||||
// The modifier-less variant is the one with no flags parameter, so it is the
|
||||
// flags-0 case of the other, and saying that here keeps one implementation.
|
||||
return gbm_bo_create_with_modifiers2(gbm, width, height, format, modifiers, count, 0);
|
||||
}
|
||||
|
||||
extern "C" struct gbm_bo* gbm_bo_create_with_modifiers2(struct gbm_device* gbm, uint32_t width, uint32_t height,
|
||||
uint32_t format, const uint64_t* modifiers,
|
||||
const unsigned int count, uint32_t flags)
|
||||
{
|
||||
std::mutex* mutex = MutexOf(gbm);
|
||||
if (mutex == nullptr)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return nullptr;
|
||||
}
|
||||
std::lock_guard<std::mutex> guard(*mutex);
|
||||
return GbmBufferCreate(*gbm, width, height, format, flags, modifiers, (Uint32)count);
|
||||
}
|
||||
|
||||
extern "C" struct gbm_bo* gbm_bo_import(struct gbm_device* gbm, uint32_t type, void* buffer, uint32_t flags)
|
||||
{
|
||||
std::mutex* mutex = MutexOf(gbm);
|
||||
if (mutex == nullptr)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return nullptr;
|
||||
}
|
||||
std::lock_guard<std::mutex> guard(*mutex);
|
||||
return GbmBufferImport(*gbm, type, buffer, flags);
|
||||
}
|
||||
|
||||
extern "C" void* gbm_bo_map(struct gbm_bo* bo, uint32_t x, uint32_t y, uint32_t width, uint32_t height,
|
||||
uint32_t flags, uint32_t* stride, void** map_data)
|
||||
{
|
||||
std::mutex* mutex = MutexOf(bo);
|
||||
if (mutex == nullptr)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return nullptr;
|
||||
}
|
||||
// The rectangle's size is not used to size the mapping: the whole buffer is
|
||||
// mapped once, at its own length, and the rectangle is applied as an offset into
|
||||
// it. That is what lets two overlapping maps share one address, and it is why a
|
||||
// buffer's mapping stays valid for a caller that only ever asks for a corner.
|
||||
(void)width;
|
||||
(void)height;
|
||||
(void)flags;
|
||||
std::lock_guard<std::mutex> guard(*mutex);
|
||||
Uint32 mapStride = 0;
|
||||
void* address = GbmBufferMap(*bo, x, y, mapStride);
|
||||
if (address == nullptr)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
if (stride != nullptr)
|
||||
{
|
||||
*stride = mapStride;
|
||||
}
|
||||
if (map_data != nullptr)
|
||||
{
|
||||
// The same address the function returns. Callers pass back either one to
|
||||
// gbm_bo_unmap, and both have to name this mapping.
|
||||
*map_data = address;
|
||||
}
|
||||
return address;
|
||||
}
|
||||
|
||||
extern "C" void gbm_bo_unmap(struct gbm_bo* bo, void* map_data)
|
||||
{
|
||||
std::mutex* mutex = MutexOf(bo);
|
||||
if (mutex == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
std::lock_guard<std::mutex> guard(*mutex);
|
||||
GbmBufferUnmap(*bo, map_data);
|
||||
}
|
||||
|
||||
extern "C" uint32_t gbm_bo_get_width(struct gbm_bo* bo)
|
||||
{
|
||||
std::mutex* mutex = MutexOf(bo);
|
||||
if (mutex == nullptr)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return 0;
|
||||
}
|
||||
std::lock_guard<std::mutex> guard(*mutex);
|
||||
return bo->Impl.Width;
|
||||
}
|
||||
|
||||
extern "C" uint32_t gbm_bo_get_height(struct gbm_bo* bo)
|
||||
{
|
||||
std::mutex* mutex = MutexOf(bo);
|
||||
if (mutex == nullptr)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return 0;
|
||||
}
|
||||
std::lock_guard<std::mutex> guard(*mutex);
|
||||
return bo->Impl.Height;
|
||||
}
|
||||
|
||||
extern "C" uint32_t gbm_bo_get_stride(struct gbm_bo* bo)
|
||||
{
|
||||
std::mutex* mutex = MutexOf(bo);
|
||||
if (mutex == nullptr)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return 0;
|
||||
}
|
||||
std::lock_guard<std::mutex> guard(*mutex);
|
||||
return bo->Impl.Strides[0];
|
||||
}
|
||||
|
||||
extern "C" uint32_t gbm_bo_get_stride_for_plane(struct gbm_bo* bo, int plane)
|
||||
{
|
||||
std::mutex* mutex = MutexOf(bo);
|
||||
if (mutex == nullptr)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return 0;
|
||||
}
|
||||
std::lock_guard<std::mutex> guard(*mutex);
|
||||
// A plane this buffer does not have is answered with zero, which is the only
|
||||
// value a caller can tell apart from a real stride.
|
||||
if (plane < 0 || plane >= bo->Impl.PlaneCount)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return 0;
|
||||
}
|
||||
return bo->Impl.Strides[plane];
|
||||
}
|
||||
|
||||
extern "C" uint32_t gbm_bo_get_format(struct gbm_bo* bo)
|
||||
{
|
||||
std::mutex* mutex = MutexOf(bo);
|
||||
if (mutex == nullptr)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return 0;
|
||||
}
|
||||
std::lock_guard<std::mutex> guard(*mutex);
|
||||
return bo->Impl.Format;
|
||||
}
|
||||
|
||||
extern "C" uint32_t gbm_bo_get_bpp(struct gbm_bo* bo)
|
||||
{
|
||||
std::mutex* mutex = MutexOf(bo);
|
||||
if (mutex == nullptr)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return 0;
|
||||
}
|
||||
std::lock_guard<std::mutex> guard(*mutex);
|
||||
// Bits per pixel: a 32 bit format answers 32, which is what a caller computing a
|
||||
// row length in bits or a row length in bytes from the same number expects.
|
||||
return bo->Impl.BytesPerPixel * 8;
|
||||
}
|
||||
|
||||
extern "C" uint32_t gbm_bo_get_offset(struct gbm_bo* bo, int plane)
|
||||
{
|
||||
std::mutex* mutex = MutexOf(bo);
|
||||
if (mutex == nullptr)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return 0;
|
||||
}
|
||||
std::lock_guard<std::mutex> guard(*mutex);
|
||||
if (plane < 0 || plane >= bo->Impl.PlaneCount)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return 0;
|
||||
}
|
||||
return bo->Impl.Offsets[plane];
|
||||
}
|
||||
|
||||
extern "C" struct gbm_device* gbm_bo_get_device(struct gbm_bo* bo)
|
||||
{
|
||||
std::mutex* mutex = MutexOf(bo);
|
||||
if (mutex == nullptr)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return nullptr;
|
||||
}
|
||||
return bo->Impl.Owner;
|
||||
}
|
||||
|
||||
extern "C" union gbm_bo_handle gbm_bo_get_handle(struct gbm_bo* bo)
|
||||
{
|
||||
union gbm_bo_handle handle;
|
||||
handle.ptr = nullptr;
|
||||
std::mutex* mutex = MutexOf(bo);
|
||||
if (mutex == nullptr)
|
||||
{
|
||||
errno = EINVAL;
|
||||
handle.s32 = -1;
|
||||
return handle;
|
||||
}
|
||||
std::lock_guard<std::mutex> guard(*mutex);
|
||||
// The kernel-level name of a dma-buf in this process is its file descriptor. A
|
||||
// GEM handle would be a name that exists only relative to a DRM node, and this
|
||||
// library deliberately does not require one, so the fd is the only answer here
|
||||
// that is true for every buffer it hands out. Callers that need a name for a
|
||||
// device should ask for the fd and pass that.
|
||||
handle.s32 = bo->Impl.Fds[0];
|
||||
return handle;
|
||||
}
|
||||
|
||||
extern "C" int gbm_bo_get_fd(struct gbm_bo* bo)
|
||||
{
|
||||
std::mutex* mutex = MutexOf(bo);
|
||||
if (mutex == nullptr)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
std::lock_guard<std::mutex> guard(*mutex);
|
||||
// A dup, close-on-exec, owned by the caller. Closing it cannot affect the buffer
|
||||
// or any other caller's copy.
|
||||
return GbmBufferDupFd(*bo, 0);
|
||||
}
|
||||
|
||||
extern "C" uint64_t gbm_bo_get_modifier(struct gbm_bo* bo)
|
||||
{
|
||||
std::mutex* mutex = MutexOf(bo);
|
||||
if (mutex == nullptr)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return DrmFormatModInvalid;
|
||||
}
|
||||
std::lock_guard<std::mutex> guard(*mutex);
|
||||
return bo->Impl.Modifier;
|
||||
}
|
||||
|
||||
extern "C" int gbm_bo_get_plane_count(struct gbm_bo* bo)
|
||||
{
|
||||
std::mutex* mutex = MutexOf(bo);
|
||||
if (mutex == nullptr)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return 0;
|
||||
}
|
||||
std::lock_guard<std::mutex> guard(*mutex);
|
||||
return bo->Impl.PlaneCount;
|
||||
}
|
||||
|
||||
extern "C" union gbm_bo_handle gbm_bo_get_handle_for_plane(struct gbm_bo* bo, int plane)
|
||||
{
|
||||
union gbm_bo_handle handle;
|
||||
handle.ptr = nullptr;
|
||||
std::mutex* mutex = MutexOf(bo);
|
||||
if (mutex == nullptr)
|
||||
{
|
||||
errno = EINVAL;
|
||||
handle.s32 = -1;
|
||||
return handle;
|
||||
}
|
||||
std::lock_guard<std::mutex> guard(*mutex);
|
||||
if (plane < 0 || plane >= bo->Impl.PlaneCount)
|
||||
{
|
||||
errno = EINVAL;
|
||||
handle.s32 = -1;
|
||||
return handle;
|
||||
}
|
||||
// Same reasoning as gbm_bo_get_handle: the fd is the buffer's kernel name. Every
|
||||
// plane of a self-allocated buffer lives in the one dma-buf, so all of them
|
||||
// answer with that descriptor; the plane's position inside it is
|
||||
// gbm_bo_get_offset.
|
||||
const Int fd = bo->Impl.Fds[plane] >= 0 ? bo->Impl.Fds[plane] : bo->Impl.Fds[0];
|
||||
handle.s32 = fd;
|
||||
return handle;
|
||||
}
|
||||
|
||||
extern "C" int gbm_bo_get_fd_for_plane(struct gbm_bo* bo, int plane)
|
||||
{
|
||||
std::mutex* mutex = MutexOf(bo);
|
||||
if (mutex == nullptr)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
std::lock_guard<std::mutex> guard(*mutex);
|
||||
return GbmBufferDupFd(*bo, plane);
|
||||
}
|
||||
|
||||
extern "C" int gbm_bo_write(struct gbm_bo* bo, const void* buf, size_t count)
|
||||
{
|
||||
std::mutex* mutex = MutexOf(bo);
|
||||
if (mutex == nullptr || buf == nullptr)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
std::lock_guard<std::mutex> guard(*mutex);
|
||||
if (count == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (count > bo->Impl.AllocationSize)
|
||||
{
|
||||
// Refused rather than truncated: a caller that writes 4 MiB into a 1 MiB
|
||||
// buffer and is told "0 bytes written, success" would read back memory it
|
||||
// never wrote and call the result correct.
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
// The write goes through the same counted mapping the rest of the API uses, so a
|
||||
// caller that already holds the buffer mapped does not get a second mapping (or
|
||||
// lose its own) behind its back.
|
||||
Uint32 stride = 0;
|
||||
void* address = GbmBufferMap(*bo, 0, 0, stride);
|
||||
if (address == nullptr)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
memcpy(address, buf, count);
|
||||
GbmBufferUnmap(*bo, address);
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern "C" void gbm_bo_set_user_data(struct gbm_bo* bo, void* data,
|
||||
void (*destroy_user_data)(struct gbm_bo*, void*))
|
||||
{
|
||||
std::mutex* mutex = MutexOf(bo);
|
||||
if (mutex == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
std::lock_guard<std::mutex> guard(*mutex);
|
||||
// Assigning over existing data does not call the old callback: the record calls
|
||||
// it exactly once, when the buffer goes away, which is the one moment a caller
|
||||
// can still recognise the pointer it registered.
|
||||
bo->Impl.UserData = data;
|
||||
bo->Impl.UserDataDestroy = destroy_user_data;
|
||||
}
|
||||
|
||||
extern "C" void* gbm_bo_get_user_data(struct gbm_bo* bo)
|
||||
{
|
||||
std::mutex* mutex = MutexOf(bo);
|
||||
if (mutex == nullptr)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return nullptr;
|
||||
}
|
||||
std::lock_guard<std::mutex> guard(*mutex);
|
||||
return bo->Impl.UserData;
|
||||
}
|
||||
|
||||
extern "C" void gbm_bo_destroy(struct gbm_bo* bo)
|
||||
{
|
||||
std::mutex* mutex = MutexOf(bo);
|
||||
if (mutex == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
std::lock_guard<std::mutex> guard(*mutex);
|
||||
// Drops this caller's reference. A buffer a surface still holds stays alive -
|
||||
// that is what the surface's own reference is for - so destroying a buffer that
|
||||
// is currently presented is not a use-after-free waiting to happen.
|
||||
GbmBufferRelease(*bo->Impl.Owner, *bo);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Surfaces
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
extern "C" struct gbm_surface* gbm_surface_create(struct gbm_device* gbm, uint32_t width, uint32_t height,
|
||||
uint32_t format, uint32_t flags)
|
||||
{
|
||||
std::mutex* mutex = MutexOf(gbm);
|
||||
if (mutex == nullptr)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return nullptr;
|
||||
}
|
||||
std::lock_guard<std::mutex> guard(*mutex);
|
||||
return GbmSurfaceCreate(*gbm, width, height, format, flags, nullptr, 0);
|
||||
}
|
||||
|
||||
extern "C" struct gbm_surface* gbm_surface_create_with_modifiers(struct gbm_device* gbm, uint32_t width,
|
||||
uint32_t height, uint32_t format,
|
||||
const uint64_t* modifiers,
|
||||
const unsigned int count)
|
||||
{
|
||||
// As with the buffer pair: the modifier-less form is the flags-0 case.
|
||||
return gbm_surface_create_with_modifiers2(gbm, width, height, format, modifiers, count, 0);
|
||||
}
|
||||
|
||||
extern "C" struct gbm_surface* gbm_surface_create_with_modifiers2(struct gbm_device* gbm, uint32_t width,
|
||||
uint32_t height, uint32_t format,
|
||||
const uint64_t* modifiers,
|
||||
const unsigned int count, uint32_t flags)
|
||||
{
|
||||
std::mutex* mutex = MutexOf(gbm);
|
||||
if (mutex == nullptr)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return nullptr;
|
||||
}
|
||||
std::lock_guard<std::mutex> guard(*mutex);
|
||||
return GbmSurfaceCreate(*gbm, width, height, format, flags, modifiers, (Uint32)count);
|
||||
}
|
||||
|
||||
extern "C" struct gbm_bo* gbm_surface_lock_front_buffer(struct gbm_surface* surface)
|
||||
{
|
||||
std::mutex* mutex = MutexOf(surface);
|
||||
if (mutex == nullptr)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return nullptr;
|
||||
}
|
||||
std::lock_guard<std::mutex> guard(*mutex);
|
||||
// Null with errno = EBUSY when the compositor still holds every buffer: that is a
|
||||
// state the caller has to handle, and pretending otherwise by handing out a
|
||||
// buffer that is still being scanned out would be a torn frame rather than an
|
||||
// error.
|
||||
return GbmSurfaceLockFront(*surface);
|
||||
}
|
||||
|
||||
extern "C" void gbm_surface_release_buffer(struct gbm_surface* surface, struct gbm_bo* bo)
|
||||
{
|
||||
std::mutex* mutex = MutexOf(surface);
|
||||
if (mutex == nullptr || bo == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
std::lock_guard<std::mutex> guard(*mutex);
|
||||
GbmSurfaceReleaseBuffer(*surface, *bo);
|
||||
}
|
||||
|
||||
extern "C" int gbm_surface_has_free_buffers(struct gbm_surface* surface)
|
||||
{
|
||||
std::mutex* mutex = MutexOf(surface);
|
||||
if (mutex == nullptr)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return 0;
|
||||
}
|
||||
std::lock_guard<std::mutex> guard(*mutex);
|
||||
// Truthful by construction: the swapchain exists from the moment the surface
|
||||
// does, so this answers about buffers that were really allocated rather than
|
||||
// about how many could still be.
|
||||
return GbmSurfaceHasFreeBuffers(*surface) ? 1 : 0;
|
||||
}
|
||||
|
||||
extern "C" void gbm_surface_destroy(struct gbm_surface* surface)
|
||||
{
|
||||
std::mutex* mutex = MutexOf(surface);
|
||||
if (mutex == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
std::lock_guard<std::mutex> guard(*mutex);
|
||||
GbmSurfaceDestroy(*surface);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Format names
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
extern "C" char* gbm_format_get_name(uint32_t gbm_format, struct gbm_format_name_desc* desc)
|
||||
{
|
||||
if (desc == nullptr)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return nullptr;
|
||||
}
|
||||
// The name of a fourcc is its four characters, most significant byte last, and
|
||||
// that is all there is to it: a caller compares these strings against "AR24" and
|
||||
// friends, and the big-endian bit is part of the value rather than something to
|
||||
// spell out here.
|
||||
desc->name[0] = (char)(gbm_format & 0xffu);
|
||||
desc->name[1] = (char)((gbm_format >> 8) & 0xffu);
|
||||
desc->name[2] = (char)((gbm_format >> 16) & 0xffu);
|
||||
desc->name[3] = (char)((gbm_format >> 24) & 0xffu);
|
||||
desc->name[4] = '\0';
|
||||
return desc->name;
|
||||
}
|
||||
|
||||
// End of Source File Header
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,358 @@
|
||||
// MobileGL - MobileGL/MG_Gbm/GbmInternal.h
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
//
|
||||
// This header carries the internal model shared by the GBM front-end files: the
|
||||
// device, buffer-object and surface records, the two allocation backends, and the
|
||||
// wire protocol used to talk to a phone that owns the real display hardware.
|
||||
//
|
||||
// The reason a GBM implementation has to exist in this tree at all is that the
|
||||
// container half of MobileGL runs a Wayland compositor (kwin). That compositor
|
||||
// needs libgbm to allocate the buffers it renders into, and the fd of such a
|
||||
// buffer is handed to the Anland display producer, which ships the frame to the
|
||||
// phone. If Mesa's libgbm is loaded, the compositor renders into Mesa buffers and
|
||||
// the phone ends up receiving a copy through a path that was never designed to
|
||||
// carry them. When this library is loaded instead, the buffers are either frames
|
||||
// the phone itself offered to us, or buffers allocated from the kernel's dma-heap
|
||||
// so that they are dma-bufs the display host can consume directly.
|
||||
//
|
||||
// The ownership rules below are the whole contract, because every one of them is
|
||||
// observable from outside this library:
|
||||
//
|
||||
// * A buffer record owns its descriptors. gbm_bo_get_fd and
|
||||
// gbm_bo_get_fd_for_plane hand out a dup, so a caller that closes what it was
|
||||
// given cannot invalidate the buffer, and the matching close happens exactly
|
||||
// once, in ~GbmBufferObject.
|
||||
// * A mapping is one mmap of the buffer's fd, counted rather than duplicated, so
|
||||
// two overlapping gbm_bo_map calls share one address and the last matching
|
||||
// gbm_bo_unmap releases it.
|
||||
// * A surface owns one reference per swapchain buffer, and every
|
||||
// gbm_surface_lock_front_buffer hands the caller a reference of its own. That
|
||||
// is what makes gbm_bo_destroy on a locked buffer harmless: it drops the
|
||||
// caller's reference and leaves the surface's bookkeeping intact.
|
||||
// * One mutex per device. GBM calls arrive from the compositor's render thread
|
||||
// and from its main thread, and the state they touch (the buffer table, the
|
||||
// swapchains, the backends) is small enough that one lock is cheaper than
|
||||
// several - and, more importantly, one lock cannot be taken in the wrong order.
|
||||
//
|
||||
// The helpers declared at the bottom of this file are the entire internal interface
|
||||
// between GbmDevice.cpp (the records and the self-allocated backend) and GbmApi.cpp
|
||||
// (the extern "C" surface). Every one of them assumes the device mutex is already
|
||||
// held: the lock is taken once, in the API layer, so that a compositor call can
|
||||
// never interleave with another half way through a swapchain update.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Includes.h>
|
||||
|
||||
#include <gbm.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace MobileGL::MG_Gbm
|
||||
{
|
||||
// DRM_FORMAT_MOD_LINEAR and DRM_FORMAT_MOD_INVALID. drm_fourcc.h is not part of
|
||||
// libgbm's interface, and these two values are frozen kernel UAPI, so they are
|
||||
// spelled out here rather than made a build dependency on libdrm's headers.
|
||||
static constexpr Uint64 DrmFormatModLinear = 0ULL;
|
||||
static constexpr Uint64 DrmFormatModInvalid = 0x00ffffffffffffffULL;
|
||||
|
||||
// Every buffer this library hands out is described by one of these. HostFrame
|
||||
// buffers are owned by the phone and only borrowed by us, so they must be
|
||||
// released back over the channel instead of being freed locally.
|
||||
enum class GbmBackend
|
||||
{
|
||||
None = 0,
|
||||
HostFrame,
|
||||
DmaHeap,
|
||||
MemFd,
|
||||
};
|
||||
|
||||
// The phone offers frames with this packed struct immediately followed by an
|
||||
// AHardwareBuffer native handle in the ancillary data of the same message.
|
||||
// The layout is fixed by the phone side, so the packing attribute is load
|
||||
// bearing and the field order may not be rearranged. Nothing in this run reads
|
||||
// or writes it: it is the hook the host-frame backend plugs into, kept here so
|
||||
// that the wire format has one home and does not have to be re-derived.
|
||||
struct GbmFrameOffer
|
||||
{
|
||||
uint32_t Magic; // 'M','G','G','F' little endian on both ends of the wire
|
||||
uint32_t Version;
|
||||
uint32_t Seq;
|
||||
uint32_t Index;
|
||||
uint32_t Width;
|
||||
uint32_t Height;
|
||||
uint32_t StrideBytes;
|
||||
uint32_t Format;
|
||||
uint32_t UsageLo;
|
||||
uint32_t UsageHi;
|
||||
} __attribute__((packed));
|
||||
|
||||
// The container replies with this once it no longer reads the frame, which is
|
||||
// what lets the phone recycle the slot. Without it the display host would run
|
||||
// out of buffers after a handful of frames.
|
||||
struct GbmFrameRelease
|
||||
{
|
||||
uint32_t Magic; // 'M','G','G','R'
|
||||
uint32_t Version;
|
||||
uint32_t Seq;
|
||||
} __attribute__((packed));
|
||||
|
||||
static constexpr uint32_t GbmFrameOfferMagic = 0x4647474Du; // "MGGF"
|
||||
static constexpr uint32_t GbmFrameReleaseMagic = 0x5247474Du; // "MGGR"
|
||||
static constexpr uint32_t GbmHostProtocolVersion = 1;
|
||||
|
||||
// An AHardwareBuffer native handle as it appears on the wire: three counts
|
||||
// followed by numFds file descriptors and numInts integers. The counts are
|
||||
// 32 bit even on 64 bit Android, which is why they are spelled out here
|
||||
// rather than using the platform's native_handle_t.
|
||||
struct AHBNativeHandleHeader
|
||||
{
|
||||
int32_t Version;
|
||||
int32_t NumFds;
|
||||
int32_t NumInts;
|
||||
} __attribute__((packed));
|
||||
|
||||
static constexpr uint32_t GbmMaxPlanes = 4;
|
||||
|
||||
// How one plane of a buffer is laid out inside the single allocation that backs
|
||||
// it. Multi-plane formats keep every plane in one dma-buf and describe the
|
||||
// separation with Offsets, which is the layout a modifier of
|
||||
// DRM_FORMAT_MOD_LINEAR can express and the only one this library produces.
|
||||
struct GbmPlaneLayout
|
||||
{
|
||||
Uint32 Stride = 0;
|
||||
Uint32 Offset = 0;
|
||||
SizeT Size = 0;
|
||||
};
|
||||
|
||||
// One imported or locally allocated buffer. The struct is private to this
|
||||
// library; gbm_bo is the public opaque name and is defined in terms of it.
|
||||
struct GbmBufferObject
|
||||
{
|
||||
Uint32 Width = 0;
|
||||
Uint32 Height = 0;
|
||||
Uint32 Format = 0;
|
||||
Uint32 Stride = 0;
|
||||
Uint64 Modifier = DrmFormatModLinear;
|
||||
Uint64 Usage = 0;
|
||||
Int PlaneCount = 1;
|
||||
// Bytes per pixel of plane 0. Kept beside the layout because gbm_bo_map
|
||||
// needs it to place a sub-rectangle, and because a format this library can
|
||||
// allocate but cannot describe in bytes would be a format it must refuse.
|
||||
Uint32 BytesPerPixel = 0;
|
||||
|
||||
// Fds owned by this record. get_fd and get_fd_for_plane hand out dups so
|
||||
// that a caller closing its fd can never invalidate the buffer. Index 0 is
|
||||
// the only one a self-allocated buffer has: every plane of a linear image
|
||||
// lives in that one dma-buf, and Fds[1..] stay -1 for it. An import with
|
||||
// several descriptors fills several entries, and the destructor closes
|
||||
// every entry that is not -1.
|
||||
Int Fds[GbmMaxPlanes] = {-1, -1, -1, -1};
|
||||
Uint32 Strides[GbmMaxPlanes] = {0, 0, 0, 0};
|
||||
Uint32 Offsets[GbmMaxPlanes] = {0, 0, 0, 0};
|
||||
Uint64 Modifiers[GbmMaxPlanes] = {0, 0, 0, 0};
|
||||
|
||||
// The number of bytes the descriptors actually back, rounded up to the page
|
||||
// size because that is what both backends allocate. It is the only length
|
||||
// that is safe to mmap: a mapping longer than the object faults on the last
|
||||
// page, and a shorter one would hide the caller's own stride arithmetic.
|
||||
SizeT AllocationSize = 0;
|
||||
|
||||
GbmBackend Backend = GbmBackend::None;
|
||||
|
||||
// Set only for host frames: the slot index and sequence number that the
|
||||
// release message has to mention so the phone can recycle the right slot.
|
||||
Uint32 HostIndex = 0;
|
||||
Uint32 HostSeq = 0;
|
||||
Bool HostOwned = false;
|
||||
|
||||
// How many maps are outstanding. The mapping itself is kept here so that
|
||||
// unmap can find and tear down the right one.
|
||||
Int MapRefCount = 0;
|
||||
void* MapAddress = nullptr;
|
||||
SizeT MapSize = 0;
|
||||
|
||||
// Surfaces keep their buffers alive by count rather than by ownership, so
|
||||
// the device knows when a bo is truly unreferenced.
|
||||
Int RefCount = 1;
|
||||
|
||||
// gbm_bo_set_user_data's pair. The destroy callback is invoked with the
|
||||
// gbm_bo that is going away, once, from the destructor below.
|
||||
void* UserData = nullptr;
|
||||
void (*UserDataDestroy)(::gbm_bo*, void*) = nullptr;
|
||||
|
||||
::gbm_device* Owner = nullptr;
|
||||
|
||||
// The record releases its own descriptors and its own mapping, so that no
|
||||
// failure path anywhere in this library can leak either one - an allocation
|
||||
// that fails half way through construction still cleans up when the record
|
||||
// it was building is destroyed.
|
||||
~GbmBufferObject();
|
||||
};
|
||||
|
||||
// A real swapchain. The compositor asks for a buffer to render into, presents
|
||||
// it, and later returns it; the three states below are exactly the states the
|
||||
// GBM API can express.
|
||||
struct GbmSurface
|
||||
{
|
||||
Uint32 Width = 0;
|
||||
Uint32 Height = 0;
|
||||
Uint32 Format = 0;
|
||||
Uint32 Flags = 0;
|
||||
|
||||
std::vector<::gbm_bo*> Buffers;
|
||||
// Index of the buffer currently handed to the compositor as the front
|
||||
// buffer, or -1 when nothing has been locked.
|
||||
Int LockedIndex = -1;
|
||||
// Indices of buffers the compositor has released and that may be handed
|
||||
// out again, plus the one that is locked.
|
||||
std::vector<Bool> Free;
|
||||
|
||||
::gbm_device* Owner = nullptr;
|
||||
|
||||
// Dropping the surface's references here is what keeps the creation path
|
||||
// free of a second failure path: if the third swapchain buffer cannot be
|
||||
// allocated, unwinding the half-built surface returns the two that exist.
|
||||
~GbmSurface();
|
||||
};
|
||||
|
||||
// The device ties one backend choice, one host channel and one mutex together.
|
||||
// The single mutex is deliberate: GBM calls arrive from the compositor's
|
||||
// render thread and from its main thread, and the state they touch (the bo
|
||||
// table, the backends) is small enough that one lock is cheaper than several.
|
||||
struct GbmDevice
|
||||
{
|
||||
// The fd this device was created with. It is a private dup taken at
|
||||
// creation, never the caller's descriptor, so gbm_device_destroy can close
|
||||
// it without ever closing an fd the caller still has open. -1 when the
|
||||
// caller passed none and no render node could be opened: the self-allocated
|
||||
// backend does not need a DRM node, so that is a working device and not an
|
||||
// error.
|
||||
Int DeviceFd = -1;
|
||||
// The dma-heap node this device allocates from, or -1 when none could be
|
||||
// opened, in which case every allocation falls back to a memfd.
|
||||
Int HeapFd = -1;
|
||||
String HeapPath;
|
||||
|
||||
String SocketPath;
|
||||
GbmBackend DefaultBackend = GbmBackend::DmaHeap;
|
||||
Bool HostAvailable = false;
|
||||
// The name gbm_device_get_backend_name reports. It names what this device
|
||||
// actually does, because a compositor that logs it is asking exactly that.
|
||||
String BackendName = "mobilegl";
|
||||
|
||||
std::mutex Mutex;
|
||||
std::unordered_map<::gbm_bo*, GbmBufferObject*> Buffers;
|
||||
std::unordered_map<::gbm_surface*, GbmSurface*> Surfaces;
|
||||
|
||||
// The host channel is a separate translation unit so that the socket
|
||||
// protocol never leaks into the allocation paths. This run implements only
|
||||
// the self-allocated backend, so the pointer stays null and HostAvailable
|
||||
// stays false; the member and the wire structs above are the hook.
|
||||
class GbmHostChannel* Host = nullptr;
|
||||
|
||||
// Closes the heap node and the private device dup, so that a device which
|
||||
// fails to finish constructing still releases the descriptors it opened.
|
||||
~GbmDevice();
|
||||
};
|
||||
|
||||
// ---- format description ------------------------------------------------
|
||||
|
||||
// Fills planes[]/planeCount/totalBytes with the linear layout this library
|
||||
// would allocate for format at width x height, and returns false for a format
|
||||
// it cannot lay out. gbm_bo_create refuses a format this refuses, because a
|
||||
// buffer whose plane layout was guessed is worse than a buffer that was not
|
||||
// handed out at all.
|
||||
Bool GbmDescribeFormat(Uint32 format, Uint32 width, Uint32 height, GbmPlaneLayout planes[GbmMaxPlanes],
|
||||
Int& planeCount, SizeT& totalBytes) noexcept;
|
||||
|
||||
// Whether this library can allocate the format at all.
|
||||
Bool GbmFormatIsSupported(Uint32 format) noexcept;
|
||||
|
||||
// Bytes per pixel of plane 0: what gbm_bo_map needs to place a sub-rectangle,
|
||||
// and half of what gbm_bo_get_bpp reports.
|
||||
Uint32 GbmFormatBytesPerPixel(Uint32 format) noexcept;
|
||||
|
||||
// ---- records and the self-allocated backend ----------------------------
|
||||
|
||||
// Builds a device. The only helper here that is called without the mutex,
|
||||
// because no other thread can see the device it is building.
|
||||
::gbm_device* GbmDeviceCreate(Int fd) noexcept;
|
||||
|
||||
// Tears the device down: every remaining surface, every remaining buffer, the
|
||||
// heap node, the private device fd, and the record itself.
|
||||
void GbmDeviceDestroy(::gbm_device& device) noexcept;
|
||||
|
||||
// Allocates a buffer from the device's heap node, or from a memfd when there
|
||||
// is none, and records it in the device's table. Returns null with errno set.
|
||||
::gbm_bo* GbmBufferCreate(::gbm_device& device, Uint32 width, Uint32 height, Uint32 format, Uint64 usage,
|
||||
const Uint64* modifiers, Uint32 modifierCount) noexcept;
|
||||
|
||||
// Wraps descriptors the caller already owns. The descriptors are duped, so the
|
||||
// caller keeps ownership of its own; type is GBM_BO_IMPORT_FD or
|
||||
// GBM_BO_IMPORT_FD_MODIFIER.
|
||||
::gbm_bo* GbmBufferImport(::gbm_device& device, Uint32 type, const void* buffer, Uint64 usage) noexcept;
|
||||
|
||||
// Drops one reference, destroying the buffer when it was the last one.
|
||||
void GbmBufferRelease(::gbm_device& device, ::gbm_bo& bo) noexcept;
|
||||
|
||||
// Destroys a buffer outright, whatever its reference count says. Only the
|
||||
// device's own teardown uses this: by then no caller's reference is meaningful.
|
||||
void GbmBufferForceDestroy(::gbm_device& device, ::gbm_bo& bo) noexcept;
|
||||
|
||||
// Hands out a dup of the descriptor backing a plane, which the caller then owns.
|
||||
// A plane this buffer does not have is refused with errno = EINVAL.
|
||||
Int GbmBufferDupFd(::gbm_bo& bo, Int plane) noexcept;
|
||||
|
||||
// Maps the whole buffer once and counts the call; strideOut receives the
|
||||
// stride of plane 0. The rect (x, y) is applied to the returned address.
|
||||
void* GbmBufferMap(::gbm_bo& bo, Uint32 x, Uint32 y, Uint32& strideOut) noexcept;
|
||||
|
||||
// Drops one mapping; the last one unmaps.
|
||||
void GbmBufferUnmap(::gbm_bo& bo, void* mapData) noexcept;
|
||||
|
||||
// ---- swapchains --------------------------------------------------------
|
||||
|
||||
// Builds a surface and its swapchain up front, so that
|
||||
// gbm_surface_has_free_buffers can answer truthfully from the first call.
|
||||
::gbm_surface* GbmSurfaceCreate(::gbm_device& device, Uint32 width, Uint32 height, Uint32 format, Uint64 usage,
|
||||
const Uint64* modifiers, Uint32 modifierCount) noexcept;
|
||||
|
||||
// Unregisters the surface and destroys it, returning the swapchain buffers it
|
||||
// still owns to the device.
|
||||
void GbmSurfaceDestroy(::gbm_surface& surface) noexcept;
|
||||
|
||||
// Hands out the next free swapchain buffer with a reference of its own, or
|
||||
// null with errno = EBUSY when the compositor still holds every one of them.
|
||||
::gbm_bo* GbmSurfaceLockFront(::gbm_surface& surface) noexcept;
|
||||
|
||||
// Takes a locked buffer back, dropping the reference the lock handed out.
|
||||
void GbmSurfaceReleaseBuffer(::gbm_surface& surface, ::gbm_bo& bo) noexcept;
|
||||
|
||||
Bool GbmSurfaceHasFreeBuffers(::gbm_surface& surface) noexcept;
|
||||
} // namespace MobileGL::MG_Gbm
|
||||
|
||||
// The public opaque types are the internal records. Callers only ever see the
|
||||
// pointers, so this costs nothing and removes a layer of casting.
|
||||
struct gbm_device
|
||||
{
|
||||
MobileGL::MG_Gbm::GbmDevice Impl;
|
||||
};
|
||||
|
||||
struct gbm_bo
|
||||
{
|
||||
MobileGL::MG_Gbm::GbmBufferObject Impl;
|
||||
};
|
||||
|
||||
struct gbm_surface
|
||||
{
|
||||
MobileGL::MG_Gbm::GbmSurface Impl;
|
||||
};
|
||||
|
||||
// End of Source File Header
|
||||
Reference in New Issue
Block a user