GBM for EGL (Linux)

Mesa GBM (Generic Buffer Manager) basically provides a EGL native window type (just like Wayland and X11), so one could obtain a real EGL surface and create render target buffers. With that then, GL can be used to render into these buffers, which will be shown to the display by queuing a page flip via KMS/DRM API.

用户应用程序直接对内存进行管理,通过EGL可以获取真实的EGL表面并创建渲染目标缓冲区

gbm(通用缓冲区管理),它提供了一种为Mesa绑定的图形渲染分配缓冲区的机制。GBM旨在被当做一个本地平台为了工作在DRM上的EGL或者openwfd。它创建的句柄可用于初始化EGL和创建渲染目标缓冲区。

MESA_platform_gbm

This extension defines how to create EGL resources from native GBM resources using the functions defined by EGL_EXT_platform_base. (GBM is a Generic Buffer Manager for Linux).

离屏渲染–简单示例

https://github.com/elima/gpu-playground/tree/master/render-nodes-minimal

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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <GLES3/gl31.h>
#include <assert.h>
#include <fcntl.h>
#include <gbm.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

/* a dummy compute shader that does nothing */
#define COMPUTE_SHADER_SRC " \
#version 310 es\n \
\
layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in; \
\
void main(void) { \
/* awesome compute code here */ \
} \
"

int32_t
main (int32_t argc, char* argv[])
{
bool res;

int32_t fd = open ("/dev/dri/renderD128", O_RDWR);
assert (fd > 0);

struct gbm_device *gbm = gbm_create_device (fd);
assert (gbm != NULL);

/* setup EGL from the GBM device */
EGLDisplay egl_dpy = eglGetPlatformDisplay (EGL_PLATFORM_GBM_MESA, gbm, NULL);
assert (egl_dpy != NULL);

res = eglInitialize (egl_dpy, NULL, NULL);
assert (res);

const char *egl_extension_st = eglQueryString (egl_dpy, EGL_EXTENSIONS);
assert (strstr (egl_extension_st, "EGL_KHR_create_context") != NULL);
assert (strstr (egl_extension_st, "EGL_KHR_surfaceless_context") != NULL);

static const EGLint config_attribs[] = {
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT_KHR,
EGL_NONE
};
EGLConfig cfg;
EGLint count;

res = eglChooseConfig (egl_dpy, config_attribs, &cfg, 1, &count);
assert (res);

res = eglBindAPI (EGL_OPENGL_ES_API);
assert (res);

static const EGLint attribs[] = {
EGL_CONTEXT_CLIENT_VERSION, 3,
EGL_NONE
};
EGLContext core_ctx = eglCreateContext (egl_dpy,
cfg,
EGL_NO_CONTEXT,
attribs);
assert (core_ctx != EGL_NO_CONTEXT);

res = eglMakeCurrent (egl_dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, core_ctx);
assert (res);

/* print some compute limits (not strictly necessary) */
GLint work_group_count[3] = {0};
for (unsigned i = 0; i < 3; i++)
glGetIntegeri_v (GL_MAX_COMPUTE_WORK_GROUP_COUNT,
i,
&work_group_count[i]);
printf ("GL_MAX_COMPUTE_WORK_GROUP_COUNT: %d, %d, %d\n",
work_group_count[0],
work_group_count[1],
work_group_count[2]);

GLint work_group_size[3] = {0};
for (unsigned i = 0; i < 3; i++)
glGetIntegeri_v (GL_MAX_COMPUTE_WORK_GROUP_SIZE, i, &work_group_size[i]);
printf ("GL_MAX_COMPUTE_WORK_GROUP_SIZE: %d, %d, %d\n",
work_group_size[0],
work_group_size[1],
work_group_size[2]);

GLint max_invocations;
glGetIntegerv (GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS, &max_invocations);
printf ("GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS: %d\n", max_invocations);

GLint mem_size;
glGetIntegerv (GL_MAX_COMPUTE_SHARED_MEMORY_SIZE, &mem_size);
printf ("GL_MAX_COMPUTE_SHARED_MEMORY_SIZE: %d\n", mem_size);

/* setup a compute shader */
GLuint compute_shader = glCreateShader (GL_COMPUTE_SHADER);

assert (glGetError () == GL_NO_ERROR);
const char *shader_source = COMPUTE_SHADER_SRC;

glShaderSource (compute_shader, 1, &shader_source, NULL);
assert (glGetError () == GL_NO_ERROR);

glCompileShader (compute_shader);
assert (glGetError () == GL_NO_ERROR);

GLuint shader_program = glCreateProgram ();

glAttachShader (shader_program, compute_shader);
assert (glGetError () == GL_NO_ERROR);

glLinkProgram (shader_program);
assert (glGetError () == GL_NO_ERROR);

glDeleteShader (compute_shader);

glUseProgram (shader_program);
assert (glGetError () == GL_NO_ERROR);

/* dispatch computation */
glDispatchCompute (1, 1, 1);
assert (glGetError () == GL_NO_ERROR);

printf ("Compute shader dispatched and finished successfully\n");

/* free stuff */
glDeleteProgram (shader_program);
eglDestroyContext (egl_dpy, core_ctx);
eglTerminate (egl_dpy);
gbm_device_destroy (gbm);
close (fd);

return 0;
}
1
gcc main.c `pkg-config --libs --cflags egl gbm gl`

主要分4部分:

  • 从渲染节点创建GBM设备
  • 设置(无表面的)EGL / OpenGL-ES上下文
  • 创建一个计算着色器程序
  • 调度计算着色器

GBM+EGL+OPENGL+render

利用GBM进行图像的离屏渲染

https://raw.githubusercontent.com/Winddoing/CodeWheel/master/egl/egl_gbm_render.c

参考

  • MESA_platform_gbm
  • drm gbm demo
  • EGLDisplay on GBM
  • Example: Run a headless OpenGL (ES) compute shader via DRM render-nodes