egl+opengles+gbm
搭配使用情况;Author | Date | Version | Description |
---|---|---|---|
陈梓归 | 2022-11-15 | V1.0 | 第一个版本 |
简介:
一套标准的EGL绘制流程简介:
1. 获取 EGL Display 对象:eglGetDisplay
2. 初始化与 EGLDisplay 之间的连接:eglInitialize
3. 获取 EGLConfig 对象:eglChooseConfig / eglGetconfigs
4. 创建 EGLContext 实例:eglCreateContext
5. 创建 EGLSurface 实例:eglCreatewindowSurface / eglCratePbufferSurface
6. 连接 EGLContext 和 EGLSurface 上下文: eglMakeCurrent
7. 使用 OpenGL ES API 绘制图形:gl_*
8. 切换 front buffer 和 back buffer 显示:eglSwapBuffer
9. 断开并释放与 EGLSurface 关联的 EGLContext 对象:eglRelease
10. 删除 EGLSurface 对象
11. 删除 EGLContext 对象
12. 终止与 EGLDisplay 之间的连接
EGLDisplay eglGetDisplay(NativeDisplayType native_display)
EGLDisplay eglGetPlatformDisplay( EGLenum platform, void * native_display, const EGLAttrib * attrib_list);
EGLDisplay eglGetPlatformDisplayEXT( EGLenum platform, void *native_display, const EGLint *attrib_list);
以上三种用法基本一致:
特别是 EGLDisplay eglGetPlatformDisplay
和EGLDisplay eglGetPlatformDisplayEXT
它们与EGLDisplay eglGetDisplay(NativeDisplayType native_display);
细微区别;
compositor运行相当于是裸机运行没有窗口环境,首先必须通过GBM或者EGL_PLATFORM_DEVICE_EXT扩展这两种方式来获取EGLDisplay;
gbm_device
)作为EGL的本地平台,创建的句柄可以用来初始化EGL和创建渲染目标缓冲区
// get gdm_device
// path = "/dev/dri/renderD128" / "dev/dri/card0"
egl_gbm.render_fd = open(path, O_RDWR|O_CLOEXEC);
assert(-1 != egl_gbm.render_fd);
egl_gbm.gbm_device = gbm_create_device(egl_gbm.render_fd);
assert(NULL != egl_gbm.gbm_device);
// get display
1. egl_gbm.display = eglGetDisplay((EGLNativeDisplayType)egl_gbm.gbm_device);
2. egl_gbm.display = eglGetPlatformDisplay(EGL_PLATFORM_GBM_KHR, egl_gbm.gbm_device, NULL);
3. egl_gbm.display = eglGetPlatformDisplayEXT(EGL_PLATFORM_GBM_MESA, egl_gbm.gbm_device, NULL);
// wlroots里面从严谨性来说,通过GBM获取EGL Display的时候,eglGetPlatformDisplayEXT后面的参数应该是EGL_PLATFORM_GBM_MESA而不是EGL_PLATFORM_GBM_KHR;
相关EGL支持platform:
EGL_PLATFORM_DEVICE_EXT 0x313F
/dev/dri/card0
或者/dev/dri/renderD128
作为平台设备扩展来申请EGL Display;
EGL_PLATFORM_GBM_KHR / EGL_PLATFORM_GBM_MESA 0x31D7
EGL_PLATFORM_WAYLAND_KHR / EGL_PLATFORM_WAYLAND_EXT 0x31D8
EGL_PLATFORM_X11_KHR / EGL_PLATFORM_X11_EXT 0x31D5
补充:
Config Attribute | Describe | Default Value |
---|---|---|
EGL_BUFFER_SIZE | 颜色缓冲区中所有颜色分量的位数 | 0 |
EGL_RED_SIZE | 颜色缓冲区中红色分量的位数 | 0 |
EGL_GREEN_SIZE | 颜色缓冲区中绿色分量的位数 | 0 |
EGL_BLUE_SIZE | 颜色缓冲区中蓝色分量的位数 | 0 |
EGL_ALPHA_SIZE | 颜色缓冲区中Alpha值位数 | 0 |
EGL_LUMINANCE_SIZE | 颜色缓冲区中亮度位数 | 0 |
EGL_COLOR_BUFFER_TYPE | 颜色缓冲区类型:EGL_RGB_BUFFER / EGL_LUMINANCE_BUFFER | EGL_RGB_BUFFER |
EGL_CONFIG_ID | 唯一的EGLConfig标识符值 | EGL_DONT_CARE |
EGL_DEPTH_SIZE | 深度缓冲区位数 | 0 |
EGL_STENCIL_SIZE | 模版缓冲区位数 | 0 |
EGL_SURFACE_TYPE | 支持的表面类型:EGL_WINDOW_BIT、EGL_PBUFFER_BIT、EGL_PIXMAP_BIT | EGL_WINDOW_BIT |
EGL_RENDERABLE_TYPE | 支持的渲染接口:EGL_OPENGL_ES_BIT、EGL_OPENGL_ES2_BIT、EGL_OPENGL_ES3_BIT(KHR)、EGL_OPENGL_BIT | EGL_OPENGL_ES_BIT |
// 这是我们对配置的需求
const EGLint config_attribs[] = {
EGL_BUFFER_SIZE, 32, // color component bit 32
EGL_DEPTH_SIZE, EGL_DONT_CARE,
EGL_STENCIL_SIZE, EGL_DONT_CARE,
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_ALPHA_SIZE, 8,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_NONE,
};
EGLint max_num_configs, num_configs, config_index;
// 1. 获取此Display支持的可用的configs组数量; 140
if (!eglGetConfigs(display, NULL, 0, &max_num_configs)) {
fake_log(ERROR, "Failed to get display configs");
return false;
}
fake_log(INFO, "Display config max num = %d", max_num_configs);
// 2. 获取匹配我们需求的configs配置组; 20
EGLConfig *configs = malloc(num_configs * sizeof(EGLConfig));
if (!eglChooseConfig(display, config_attribs, configs, max_num_configs,
&num_configs)) {
fake_log(ERROR, "Failed to choose specify configs");
return false;
}
fake_log(INFO, "匹配 config_attribs Display choose config num = %d",
num_configs);
// 3. 在我们匹配的配置组里面,在匹配一下我们的格式需求,确定最终的配置;[20]
config_index = match_config_to_visual(display, GBM_FORMAT_ARGB8888, configs,
num_configs);
// ----
static int match_config_to_visual(EGLDisplay egl_display, EGLint visual_id,
EGLConfig *configs, int count) {
EGLint id;
for (int i = 0; i < count; ++i) {
if (!eglGetConfigAttrib(egl_display, configs[i], EGL_NATIVE_VISUAL_ID,
&id))
continue;
if (id == visual_id)
return i;
}
return -1;
}
标志 | 描述 | 默认 |
---|---|---|
EGL_CONTEXT_CLIENT_VERSION | 指定所使用的OpenGLES版本相关的上下文类型 | 1 |
/*
EGL_CONTEXT_CLIENT_VERSION, 3, //使用OpenGL ES 3.0 版本 API
EGL_CONTEXT_CLIENT_VERSION, 2, //使用OpenGL ES 2.0版本 API
EGL_CONTEXT_CLIENT_VERSION, 1, //使用OpenGL ES 1.0版本 API
*/
const ELGint attribList[] = {
EGL_CONTEXT_CLIENT_VERSION, 2, //使用OpenGL ES 2.0 版本 API
EGL_NONE
};
egl_gbm.context = eglCreateContext(egl_gbm.display, configs[config_index],
EGL_NO_CONTEXT, attribs);
标志 | 描述 | 默认 |
---|---|---|
EGL_RENDER__BUFFER | 指定渲染所用的缓冲区 EGL_BACK_BUFFER EGL_SINGLE_BUFFER |
EGL_BACK_BUFFER |
// use surface specify config
const EGLint attribList[] = {
EGL_RENDER_BUFFER, EGL_BACK_BUFFER,
EGL_NONE,
};
// attribList
egl_gbm.window_surface = egl_gbm.procs.eglCreatePlatformWindowSurfaceEXT(
egl_gbm.display, configs[config_index], egl_gbm.gbm_surface,
attribList);
标志 | 描述 | 默认 |
---|---|---|
EGL_WIDTH | 指定Pbuffer的宽度 | 0 |
EGL_HEIGHT | 指定Pbuffer的高度 | 0 |
EGL_LARGEST_PBUFFER | 如果请求的大小不可用,则选择内部最大的可用pbuffer大小 | EGL_FALSE |
EGL_TEXTURE_FORMAT | 如果pbuffer绑定到一个纹理贴图指定的纹理格式类型 EGL_TEXTURE_RGB EGL_TEXTURE_RGBA EGL_NO_TEXTURE |
EGL_NO_TEXTURE |
EGL_TEXTURE_TARGET | 指定Pbuffer作为纹理贴图时应该连接到的相关纹理目标 EGL_TEXTURE_2D EGL_NO_TEXTURE |
EGL_NO_TEXTURE |
EGLint pbuffer_attribs[] = {
EGL_WIDTH, 512,
EGL_HEIGHT, 512,
EGL_LARGEST_BUFFER, EGL_TRUE,
EGL_NONE
};
egl_gbm.pbuffer_surface = eglCreatePbufferSurface(egl_gbm.display, pbuffer_configs, pbuffer_attribList);
Surface是一个抽象的概念,可以理解成一个容器对象,里面附着有不同的buffer用于显示提交;
EGL中一共有三种Surface
WindowSurface
EGLSurface eglCreateWindowSurface( EGLDisplay display, EGLConfig config, NativeWindowType native_window, EGLint const * attrib_list);
EGLSurface eglCreatePlatformWindowSurface( EGLDisplay display, EGLConfig config, void * native_window, EGLAttrib const * attrib_list);
EGLSurface eglCreatePlatformWindowSurfaceEXT( EGLDisplay dpy, EGLConfig config, void *native_window, const EGLint *attrib_list);
EGL_EXT_platform_base
PbufferSurface
EGLSurface eglCreatePbufferSurface( EGLDisplay display, EGLConfig config, EGLint const * attrib_list);
PixmapSurface
EGLSurface eglCreatePixmapSurface( EGLDisplay display, EGLConfig config, NativePixmapType native_pixmap, EGLint const * attrib_list);
EGLSurface eglCreatePlatformPixmapSurface( EGLDisplay display, EGLConfig config, void * native_pixmap, EGLint const * attrib_list);
类型 | 绑定本地窗口句柄 | 绑定本地类型缓冲区 | 缓冲区 | 备注 |
---|---|---|---|---|
window surface | 是 | 否 | 多缓冲区 | 包括front buffer and back buffer; 默认在backbuffer中渲染,需要通过eglSwapBuffer来把渲染的结果显示到屏幕。 也有EGL_RENDER_TYPE可设置为EGL_SINGLE_BUFFER但这个要看ES的实现。一般无效; |
PBuffer | 否 | 否 | 单缓冲区 | 不绑定任何本地的东西。需要指定EGL_WIDTH,EGL_HEIGHT参数,来创建对应的大小。不可以被显示,调用eglSwapBuffer是无效的。这种缓冲区可以直接用作纹理数据; |
pixmapbuffer | 否 | 是(NativePixmapType) | 单缓冲区 | 一个Pixmap代表内存中的一张图片,如要上屏显示需转换成纹理 |
GBM Platform平台设备只支持window surface,下面我们GBM Platform Device为例子来进行创建:
gbm_surface
-> egl_window_surface
// create gbm device and gbm surface
egl_gbm.gbm_device = gbm_create_device(gbm_fd);
assert(NULL != egl_gbm.gbm_device);
egl_gbm.gbm_surface = gbm_surface_create(egl_gbm.gbm_device, egl_gbm.mode.hdisplay, egl_gbm.mode.vdisplay,
GBM_FORMAT_XRGB8888, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
assert(NULL != egl_gbm.gbm_surface);
// get window_surface, 三者区别同Display不再叙述;
1. egl_gbm.window_surface = eglCreateWindowSurface(egl_gbm.display,configs[config_index], (EGLNativeWindowType)egl_gbm.gbm_surface,attribList);
2. egl_gbm.window_surface = eglCreatePlatformWindowSurface(egl_gbm.display, configs[config_index],egl_gbm.gbm_surface, (EGLAttrib *)attribList);
3. egl_gbm.window_surface = egl_gbm.procs.eglCreatePlatformWindowSurfaceEXT(egl_gbm.display, configs[config_index], egl_gbm.gbm_surface,attribList);
// create context
egl_gbm.context = eglCreateContext(egl_gbm.display, configs[config_index], EGL_NO_CONTEXT, attribs);
// draw_color_use_window_surface
eglMakeCurrent(egl_gbm.display, egl_gbm.window_surface,
egl_gbm.window_surface, egl_gbm.context);
glClearColor(1.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
eglSwapBuffers(egl_gbm.display, egl_gbm.window_surface);
// scan_output_surface_to_display
drmModeSetCrtc
// read_output_surface_to_file
glReadPixels
// read_output_surface_to_texture --- need opengl es 3.0
glReadBuffer(GL_BACK)
glCopyTexImage2D
好,我们来回答问题一:是不是调用eglSwapBuffers函数以后图像就直接显示到屏幕上了?
// color buffer就是我们用来渲染和送显的buffer
// 最大四个
#if defined(HAVE_WAYLAND_PLATFORM) || defined(HAVE_DRM_PLATFORM)
struct {
#ifdef HAVE_WAYLAND_PLATFORM
struct wl_buffer *wl_buffer;
bool wl_release;
__DRIimage *dri_image;
/* for is_different_gpu case. NULL else */
__DRIimage *linear_copy;
/* for swrast */
void *data;
int data_size;
#endif
#ifdef HAVE_DRM_PLATFORM
struct gbm_bo *bo;
#endif
bool locked;
int age;
} color_buffers[4], *back, *current;
#endif
// GBM平台,eglSwapBuffers最终就调用dri2_drm_swap_buffers
// 第一次进来get_back_bo是小于0的,就会进去申请buffer;
static EGLBoolean
dri2_drm_swap_buffers(_EGLDisplay *disp, _EGLSurface *draw)
{
struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
struct dri2_egl_surface *dri2_surf = dri2_egl_surface(draw);
if (!dri2_dpy->flush) {
dri2_dpy->core->swapBuffers(dri2_surf->dri_drawable);
return EGL_TRUE;
}
if (dri2_surf->current)
_eglError(EGL_BAD_SURFACE, "dri2_swap_buffers");
for (unsigned i = 0; i < ARRAY_SIZE(dri2_surf->color_buffers); i++)
if (dri2_surf->color_buffers[i].age > 0)
dri2_surf->color_buffers[i].age++;
/* Make sure we have a back buffer in case we're swapping without
* ever rendering. */
if (get_back_bo(dri2_surf) < 0)
return _eglError(EGL_BAD_ALLOC, "dri2_swap_buffers");
dri2_surf->current = dri2_surf->back;
dri2_surf->current->age = 1;
dri2_surf->back = NULL;
dri2_flush_drawable_for_swapbuffers(disp, draw);
dri2_dpy->flush->invalidate(dri2_surf->dri_drawable);
return EGL_TRUE;
}
static int
get_back_bo(struct dri2_egl_surface *dri2_surf)
{
struct dri2_egl_display *dri2_dpy =
dri2_egl_display(dri2_surf->base.Resource.Display);
struct gbm_dri_surface *surf = dri2_surf->gbm_surf;
int age = 0;
if (dri2_surf->back == NULL) {
for (unsigned i = 0; i < ARRAY_SIZE(dri2_surf->color_buffers); i++) {
if (!dri2_surf->color_buffers[i].locked &&
dri2_surf->color_buffers[i].age >= age) {
dri2_surf->back = &dri2_surf->color_buffers[i];
age = dri2_surf->color_buffers[i].age;
}
}
}
if (dri2_surf->back == NULL)
return -1;
if (dri2_surf->back->bo == NULL) {
if (surf->base.v0.modifiers)
dri2_surf->back->bo = gbm_bo_create_with_modifiers(&dri2_dpy->gbm_dri->base,
surf->base.v0.width,
surf->base.v0.height,
surf->base.v0.format,
surf->base.v0.modifiers,
surf->base.v0.count);
else {
unsigned flags = surf->base.v0.flags;
if (dri2_surf->base.ProtectedContent)
flags |= GBM_BO_USE_PROTECTED;
dri2_surf->back->bo = gbm_bo_create(&dri2_dpy->gbm_dri->base,
surf->base.v0.width,
surf->base.v0.height,
surf->base.v0.format,
flags);
}
}
if (dri2_surf->back->bo == NULL)
return -1;
return 0;
}
通过mesa代码可以看出,简单来说eglSwapBuffers
= gbm_bo_create
+ get_back_bo
(第一次拿到为NULL,就会调用bo_create去创建) + glFlush
实际上eglSwapBuffers函数执行以后,只是提示上层(一般是compositor)有输出buffer可以用了,这个时候是把输出buffer显示到屏幕上还是输出到文件或者其他地方,由上层策略来决定;
1. 如果显示到屏幕上这一种就是kwin和weston这两种compositor的底层送显方式;
scan_output_surface_to_display()
eglMakeCurrent(egl_gbm.display, egl_gbm.window_surface,
egl_gbm.window_surface, egl_gbm.context);
egl_gbm.gbm_bo = gbm_surface_lock_front_buffer(egl_gbm.gbm_surface);
egl_gbm.handle = gbm_bo_get_handle(egl_gbm.gbm_bo).u32;
egl_gbm.pitch =
gbm_bo_get_stride(egl_gbm.gbm_bo); // pitch = mode.hdisplay * 4
// fake_log(ERROR, "handle = %d pitch = %d", egl_gbm.handle, egl_gbm.pitch);
drmModeAddFB(egl_gbm.card_fd, egl_gbm.mode.hdisplay, egl_gbm.mode.vdisplay,
24, 32, egl_gbm.pitch, egl_gbm.handle, &egl_gbm.fb_id);
drmModeSetCrtc(egl_gbm.card_fd, egl_gbm.crtc->crtc_id, egl_gbm.fb_id, 0, 0,
&egl_gbm.connector_id, 1, &egl_gbm.mode);
这里补充一个点,关于之前提到的card节点即可渲染又可送显,render节点只能渲染,这里有很好的体现:
fd | create gbm bo / gbm surface | drmModeSetCrtc |
---|---|---|
card | GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING |
yes |
card | GBM_BO_USE_RENDERING |
no |
render | GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING |
no |
render | GBM_BO_USE_RENDERING |
no |
2. 如果输出到其他地方,就是后面要说的off-screen;
2.1 到内存,read_output_surface_to_file:
eglMakeCurrent(egl_gbm.display, egl_gbm.window_surface,
egl_gbm.window_surface, egl_gbm.context);
static FILE *file = NULL;
static GLbyte *pbits = NULL; /* CPU memory to save image */
static uint32_t frame_cnt = 0;
uint32_t frame_size = 10 * 10 * 4;
if (!file) {
file = fopen("rgba.bin", "w+");
assert(file);
pbits = (GLbyte *)malloc(frame_size);
assert(pbits);
}
glReadPixels(0, 0, 10, 10, GL_RGBA, GL_UNSIGNED_BYTE, pbits);
size_t ret = fwrite(pbits, 1, frame_size, file);
2.2 到纹理,window buffer到纹理需要OpenglES3.0才支持
glReadBuffer(GL_BACK)
glCopyTexImage2D
wayland | GBM | Surface | scanout buffer关联 |
---|---|---|---|
Weston / Kwin | gbm_surface | egl_surface | eglSwapBuffer |
Wlroots | gbm_bo | no | EglImage + glEGLImageTargetRenderbufferStorageOES(FBO)《— attach — 》gbm_bo |
Global | Global Create | Global Bind |
---|---|---|
wl_shm | compositor | client/libEGL.so |
wl_drm / mali_buffer_sharing | libEGL.so | libEGL.so |
zwp_linux_dmabuf_v1 | compositor | client/libEGL.so |
稍微补充:
wayland server
调用BindWaylandDisplayWL
-> libEGL_mesa.so / libEGL_mali.so
-> 注册wl_drm / mali_buffer_sharing
的global;wayland client
调用eglInitialize
-> libEGL_mesa.so / libEGL_mali.so
-> 绑定 wl_drm / mali_buffer_sharing
的global;一共三种:
EGL_WAYLAND_BUFFER_WL
)接口创建EGLImage,这个EGLImage可以直接作为Compositor的输入纹理来使用,不需要额外的拷贝工作。EGL_LINUX_DMA_BUF_EXT
)
Buffer Zero Copying
;关于三者详解以后有时间会单独写一篇适配文档单独说明
Texture Image
应用static void draw_color_to_fbo_texture(){
// Texture
//off_screen_context
static const EGLint context_attribs[] = {
EGL_CONTEXT_CLIENT_VERSION, 2,
EGL_NONE
};
// 1. 可以看到我们创建的上下文是没有表面信息和配置信息的,不会造成切换的效率问题;
egl_gbm.off_screen_context = eglCreateContext(egl_gbm.display, EGL_NO_CONFIG_KHR, EGL_NO_CONTEXT, context_attribs);
eglMakeCurrent(egl_gbm.display, EGL_NO_SURFACE, EGL_NO_SURFACE,
egl_gbm.off_screen_context);
// 激活纹理单元(通道)-激活手枪
glActiveTexture(GL_TEXTURE0)
// 生成纹理对象-生成子弹
glGenTextures(1, &egl_gbm.texture_target_1);
// 绑定纹理目标 - 子弹放入弹孔
glBindTexture(GL_TEXTURE_2D, egl_gbm.texture_target_1);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, egl_gbm.mode.hdisplay, egl_gbm.mode.vdisplay, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
// Fbo
glGenFramebuffers(1, &egl_gbm.fbo);
glBindFramebuffer(GL_FRAMEBUFFER, egl_gbm.fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, egl_gbm.texture_target_1, 0);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
fprintf(stderr, "FBO creation failed\n");
}
glClearColor(0.0f, 1.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
glFlush();
read_draw_to_file(EGL_NO_SURFACE, EGL_NO_SURFACE, egl_gbm.off_screen_context);
glBindTexture(GL_TEXTURE_2D, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
eglMakeCurrent(egl_gbm.display, EGL_NO_SURFACE, EGL_NO_SURFACE,
EGL_NO_CONTEXT);
}
RenderBuffer Image
应用static void draw_color_to_fbo_renderbuffer_display(){
// dmabuf: create gbm_bo
egl_gbm.gbm_rbo = gbm_bo_create(
egl_gbm.gbm_device, egl_gbm.mode.hdisplay, egl_gbm.mode.vdisplay,
GBM_FORMAT_XRGB8888, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
assert(NULL != egl_gbm.gbm_rbo);
// RGB plane count = 1
// YUY may be plane count = 3
egl_gbm.plane_count = gbm_bo_get_plane_count(egl_gbm.gbm_rbo);
egl_gbm.strides[0] = gbm_bo_get_stride(egl_gbm.gbm_rbo);
egl_gbm.dmabuf_fds[0] = gbm_bo_get_fd(egl_gbm.gbm_rbo);
egl_gbm.offsets[0] = gbm_bo_get_offset(egl_gbm.gbm_rbo,0);
fake_log(ERROR, "plane_count = %d offset = %d strides = %d dmabuf_fds = %d", egl_gbm.plane_count, egl_gbm.offsets[0], egl_gbm.strides[0], egl_gbm.dmabuf_fds[0]);
// egl_image create
const EGLint attribs_test[] = {
EGL_WIDTH, egl_gbm.mode.hdisplay,
EGL_HEIGHT, egl_gbm.mode.vdisplay,
EGL_LINUX_DRM_FOURCC_EXT, GBM_FORMAT_ARGB8888,
EGL_DMA_BUF_PLANE0_FD_EXT, egl_gbm.dmabuf_fds[0],
EGL_DMA_BUF_PLANE0_OFFSET_EXT, 0,
EGL_DMA_BUF_PLANE0_PITCH_EXT, egl_gbm.strides[0],
EGL_NONE,
};
// EGL_KHR_image_base + EGL_EXT_image_dma_buf_import
egl_gbm.egl_image = egl_gbm.procs.eglCreateImageKHR(egl_gbm.display, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, NULL, attribs_test);
assert(EGL_NO_IMAGE_KHR != egl_gbm.egl_image);
// Render Buffer
//off_screen_context
static const EGLint context_attribs[] = {
EGL_CONTEXT_CLIENT_VERSION, 2,
EGL_NONE
};
egl_gbm.off_screen_context = eglCreateContext(egl_gbm.display, EGL_NO_CONFIG_KHR, EGL_NO_CONTEXT, context_attribs);
eglMakeCurrent(egl_gbm.display, EGL_NO_SURFACE, EGL_NO_SURFACE,
egl_gbm.off_screen_context);
glGenRenderbuffers(1, &egl_gbm.renderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, egl_gbm.renderbuffer);
// GL_OES_EGL_image
gles_fake.procs.glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER, egl_gbm.egl_image);
// Fbo
glGenFramebuffers(1, &egl_gbm.fbo);
glBindFramebuffer(GL_FRAMEBUFFER, egl_gbm.fbo);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_RENDERBUFFER, egl_gbm.renderbuffer);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
fprintf(stderr, "FBO creation failed\n");
}
glClearColor(0.0f, 0.0f, 1.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
glFlush();
read_draw_to_file(EGL_NO_SURFACE, EGL_NO_SURFACE, egl_gbm.off_screen_context);
egl_gbm.handle = gbm_bo_get_handle(egl_gbm.gbm_rbo).u32;
egl_gbm.pitch =
gbm_bo_get_stride(egl_gbm.gbm_rbo); // pitch = mode.hdisplay * 4
fake_log(ERROR, "handle = %d pitch = %d", egl_gbm.handle, egl_gbm.pitch);
drmModeAddFB(egl_gbm.card_fd, egl_gbm.mode.hdisplay, egl_gbm.mode.vdisplay,
24, 32, egl_gbm.pitch, egl_gbm.handle, &egl_gbm.fb_id);
drmModeSetCrtc(egl_gbm.card_fd, egl_gbm.crtc->crtc_id, egl_gbm.fb_id, 0, 0,
&egl_gbm.connector_id, 1, &egl_gbm.mode);
getchar();
glBindRenderbuffer(GL_RENDERBUFFER, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
eglDestroyImage(egl_gbm.display, egl_gbm.egl_image);
eglMakeCurrent(egl_gbm.display, EGL_NO_SURFACE, EGL_NO_SURFACE,
EGL_NO_CONTEXT);
}
完整代码路径:https://github.com/fakeczg/egl_gbm_off_screen