EGL简介


EGL简介

1.EGLDisplay

获取

​ 使用EGL的入口点,获取EGLDisplay

1
EGLDisplay eglGetDisplay(EGLNativeDisplayType display_id);

EGL_DEFAULT_DISPLAY:是display_id的默认值,通常是当前显示

  • X11平台示例
1
2
3
4
5
6
Display* x11_display = XOpenDisplay(NULL); // 打开默认 X11 显示
EGLDisplay display = eglGetDisplay((EGLNativeDisplayType)x11_display);
if (display == EGL_NO_DISPLAY) {
fprintf(stderr, "Failed to get EGLDisplay for X11\n");
return -1;
}
  • wayland
1
2
3
4
5
6
struct wl_display* wayland_display = wl_display_connect(NULL); // 连接到默认 Wayland 显示
EGLDisplay display = eglGetDisplay((EGLNativeDisplayType)wayland_display);
if (display == EGL_NO_DISPLAY) {
fprintf(stderr, "Failed to get EGLDisplay for Wayland\n");
return -1;
}
  • windows平台
1
2
3
4
5
6
HDC hdc = GetDC(hwnd); // 获取窗口设备上下文
EGLDisplay display = eglGetDisplay((EGLNativeDisplayType)hdc);
if (display == EGL_NO_DISPLAY) {
fprintf(stderr, "Failed to get EGLDisplay for Windows\n");
return -1;
}
  • 扩展
1
2
3
4
5
// egl1.5 core函数
EGLDisplay eglGetPlatformDisplay(EGLenum platform, void *native_display,const EGLAttrib *attrib_list)
// egl 扩展
EGLDisplay eglGetPlatformDisplayEXT(EGLenum platform, void *native_display,const EGLint *int_attribs)

初始化EGLDisplay

​ 获取EGLDisplay后需要初始化,初始化接口,指向具体的EGL主副版本;

1
2
EGLBoolean eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor);

查询EGLDisplay信息

​ 查询EGLDisplay信息

1
2
3
EGLint numConfigs;
eglGetConfigs(display, NULL, 0, &numConfigs);
printf("Number of configurations: %d\n", numConfigs);

销毁EGLDisplay

​ 终止连接

1
eglTerminate(display);

2.EGLSurface

​ 关键对象之一,用于表示一个绘制表面,可以与窗口、屏幕缓冲区(Pbuffer Surface)、或屏幕(Pixmap Surface)关联,是图形渲染的目标。可以将渲染内容输出到屏幕或者离屏缓冲区中。

​ 有三种类型的EGLSurface:

  1. Window Surface:渲染到屏幕上的窗口;
  2. Pbuffer Surface:离屏渲染表面,渲染到内存缓冲区,适合需要生成纹理或图像的应用;
  3. Pixmap Surface:渲染内容输出到像素对象(Pixmap),较少使用;

创建

​ 创建windowSurface

1
2
3
4
5
6
EGLSurface eglCreateWindowSurface(
EGLDisplay dpy,
EGLConfig config,
EGLNativeWindowType win,
const EGLint *attrib_list
);

​ 创建PbufferSurface

1
2
3
4
5
6
7
8
9
10
EGLint pbuffer_attribs[] = {
EGL_WIDTH, 800,
EGL_HEIGHT, 600,
EGL_NONE
};
EGLSurface eglCreatePbufferSurface(
EGLDisplay dpy,
EGLConfig config,
const EGLint *attrib_list
);

​ 创建到本地像素对象

1
2
3
4
5
6
EGLSurface eglCreatePixmapSurface(
EGLDisplay dpy,
EGLConfig config,
EGLNativePixmapType pixmap,
const EGLint *attrib_list
);

绑定和渲染

​ 在使用EGLSuface之前,需要与EGLContext绑定;

1
2
3
4
5
6
EGLBoolean eglMakeCurrent(
EGLDisplay dpy,
EGLSurface draw,//绘制
EGLSurface read,//读取,一般与绘制一致
EGLContext ctx
);

​ 对于WindowSurface需要调用eglSwapBuffer来提交绘制结果到屏幕;

1
eglSwapBuffers(display, surface);

销毁

​ 不在使用时需要释放资源

1
EGLBoolean eglDestroySurface(EGLDisplay dpy, EGLSurface surface);

查询

​ 可以使用eglQuerySurface获取EGLSurface的信息

1
2
3
4
5
6
EGLBoolean eglQuerySurface(
EGLDisplay dpy,
EGLSurface surface,
EGLint attribute,
EGLint *value
);

3.EGLConext

​ 渲染上下文,是图形API的运行环境,管理状态、资源和命令的执行,只有通过有效的EGLConext,命令才会被正确的发送到GPU执行。在多线程渲染中,可以使用多个EGLConext,同一个EGLContext也可以被多个线程共享。

创建

1
2
3
4
5
6
EGLContext eglCreateContext(
EGLDisplay dpy,
EGLConfig config,
EGLContext share_context,//指定另一个EGLConext,资源共享
const EGLint *attrib_list
);

绑定

​ 创建EGLSurface和EGLContext后,需要绑定才能发出渲染命令。

1
2
3
4
5
6
EGLBoolean eglMakeCurrent(
EGLDisplay dpy,
EGLSurface draw,
EGLSurface read,
EGLContext ctx
);

​ 解除绑定

1
eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);

销毁

​ 不再需要时,释放资源

1
EGLBoolean eglDestroyContext(EGLDisplay dpy, EGLContext ctx);

共享

可以共享纹理、帧缓冲,上下文之间的状态是独立的。另外共享的上下文必须基于相同的EGLConfig创建;

注意事项

​ 每个线程只能绑定一个EGLConext,但同一个EGLConext可以在不同线程中绑定和解绑;应该避免频繁的上下文绑定解绑操作,合理利用共享上下文机制来管理纹理和缓冲区;在销毁EGLConext前,确保所有资源已释放;

4.EGLSurface渲染到纹理对象

绑定PbufferSurface和纹理

​ 通过EGL,可以将EGLSurface(PbufferSurface或者PixmapSurface)直接渲染到GLES的纹理对象,可以实现高效的数据共享和后续的纹理操作;常用于离屏渲染(offscreen rendering),渲染内容不会直接显示在屏幕上,而是作为纹理使用,也可以用作后续处理,锐化、模糊的等。

1
2
3
4
5
EGLBoolean eglBindTexImage(
EGLDisplay dpy,
EGLSurface surface,
EGLint buffer //通常未EGL_BACK_BUFFER
);

​ 主要步骤:

1.创建Surface支持纹理绑定

绑定的Surface在创建时,必须支持纹理绑定,EGL_TETURE_2D;

1
2
3
4
5
6
7
8
9
10
11
12
13
// 1. 创建 PbufferSurface
EGLint pbufferAttribs[] = {
EGL_WIDTH, 256,
EGL_HEIGHT, 256,
EGL_TEXTURE_TARGET, EGL_TEXTURE_2D, // 设置为支持 2D 纹理绑定
EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA, // 支持 RGBA 格式
EGL_NONE
};
EGLSurface pbufferSurface = eglCreatePbufferSurface(display, config, pbufferAttribs);
if (pbufferSurface == EGL_NO_SURFACE) {
printf("Failed to create PbufferSurface\n");
return -1;
}

2.创建GLES纹理

1
2
3
4
5
6
// 2. 创建 OpenGL ES 纹理
GLuint texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

3.绑定PbufferSurface到纹理

1
2
3
4
5
// 3. 绑定 PbufferSurface 到纹理
if (eglBindTexImage(display, pbufferSurface, EGL_BACK_BUFFER) == EGL_FALSE) {
printf("Failed to bind PbufferSurface to texture\n");
return -1;
}

4.使用纹理绘制

1
2
3
4
// 4. 使用纹理进行渲染
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, texture);
// 绘制使用此纹理的对象(略)

5.解除绑定

1
eglReleaseTexImage(display, pbufferSurface, EGL_BACK_BUFFER);

6.清理资源

1
2
3
// 清理资源
eglDestroySurface(display, pbufferSurface);
glDeleteTextures(1, &texture);

解除绑定

1
2
3
4
5
EGLBoolean eglReleaseTexImage(
EGLDisplay dpy,
EGLSurface surface,
EGLint buffer
);
  • buffer:通常未EGL_BACK_BUFFER

5.EGLImage

​ EGL允许相同API的上下文之间共享大量状态(包括纹理对象),但是某些情况下需要在不同api之间(GLES、VK、EGL)共享状态,比如OpenVG图像作为OGLES纹理对象。那么EGLImages对象就是可以实现不同客户端api的相互共享的资源对象;

EGLImageKHR:EGLImage的实际类型,表示共享的图形数据,作为桥梁,在不同API之间访问,可以减少数据拷贝,提供性能。

创建

​ 从一个资源创建EGLImage,比如纹理或者缓冲区

1
2
3
4
5
EGLImage eglCreateImage(EGLDisplay       dpy,
EGLContext ctx,
EGLenum target,
EGLClientBuffer buffer,
const EGLAttrib* attrib_list);

target:资源的目标类型,

  1. EGL_GL_TEXTURE_2D_KHR:OpenGL ES 2D 纹理。
  2. EGL_GL_RENDERBUFFER_KHR:OpenGL ES 渲染缓冲区。
  3. EGL_NATIVE_PIXMAP_KHR:本地 pixmap。
  4. EGL_LINUX_DMA_BUF_EXT:扩展目标类型,使用该类型是,buffer参数为null,dma

buffer:客户端缓冲区资源句柄,比如:OGLES的纹理对象ID。

绑定

​ 将一个EGLImage绑定到GLES的纹理

1
void glEGLImageTargetTexture2DOES(GLenum target, GLeglImageOES image);

target:纹理目标类型,通常未GL_TEXTURE_2D

image:要绑定的EGLImage对象

手动触发

​ 显式通知EGL同步对象触发,用于与其他线程或客户端同步,确保资源在同步对象的状态改变后安全使用;

1
2
3
4
5
const char* extensions = eglQueryString(display, EGL_EXTENSIONS);
if (strstr(extensions, "EGL_KHR_fence_sync") == NULL) {
printf("EGL_KHR_fence_sync not supported\n");
}

销毁

销毁一个EGLImage对象,释放资源。

1
EGLBoolean eglDestroyImageKHR(EGLDisplay dpy, EGLImageKHR image);

示例

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
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include <stdio.h>

int main() {
// 初始化 EGL 和 OpenGL ES 环境(略)

// 1. 创建一个 OpenGL ES 纹理
GLuint texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);

// 2. 创建 EGLImageKHR 对象
EGLImageKHR image = eglCreateImageKHR(
display,
context,
EGL_GL_TEXTURE_2D_KHR,
(EGLClientBuffer)texture,
NULL
);
if (image == EGL_NO_IMAGE_KHR) {
printf("Failed to create EGLImage\n");
return -1;
}

// 3. 使用 EGLImage 在另一个 OpenGL ES 纹理中
GLuint shared_texture;
glGenTextures(1, &shared_texture);
glBindTexture(GL_TEXTURE_2D, shared_texture);
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image);

// 4. 使用共享的纹理进行渲染(略)

// 5. 清理资源
eglDestroyImageKHR(display, image);
glDeleteTextures(1, &texture);
glDeleteTextures(1, &shared_texture);

return 0;
}

6.EGLSync

​ 该对象提供一种同步机制,处理不同图形操作或者多个图形任务之间的顺序,在多线程或者多进程之间尤其重要。同步需求主要在等待GPU渲染任务的完成,确保后续任务可以在正确的时间执行,多线程之间同步渲染命令的执行,实现多步渲染操作的顺序控制。

创建

​ 创建EGLSync对象

1
2
3
4
5
EGLSyncKHR eglCreateSyncKHR(
EGLDisplay display,
EGLenum type,
const EGLint *attrib_list
);

type:同步对象类型

  1. EGL_SYNC_FENCE_KHR:这是一种基于 GPU 的同步对象,通常用于等待 GPU 完成某些操作,如渲染。
  2. EGL_SYNC_NATIVE_FENCE_FD_KHR:这个同步对象与底层操作系统的同步机制兼容,通常用于与 Linux 上的 fence 或 sync 文件描述符配合使用。

attrib_list:同步对象属性的整数数组,取决type类型

  1. EGL_SYNC_FENCE_KHR 类型的同步对象可能不需要任何附加属性;
  2. EGL_SYNC_NATIVE_FENCE_FD_KHR 类型的同步对象则可能需要指定某些操作系统特定的属性。如果没有附加的属性,传入 NULL 即可。

等待

​ 等待特定的同步对象达到某种状态

1
2
3
4
5
6
EGLint eglClientWaitSync(
EGLDisplay dpy,
EGLSyncKHR sync,
EGLint flags,
EGLTime timeout
);

​ 常用于CPU在等待GPU的任务完成后再继续CPU上的计算操作,避免后续操作错误依赖未完成的GPU任务。

销毁

​ 销毁同步对象,在销毁前确保没有其他地方依赖等待使用,如果提前销毁会有意想不到的情况。

1
2
3
4
EGLBoolean eglDestroySyncKHR(
EGLDisplay dpy,
EGLSyncKHR sync
);

7.eglSwapBuffers

​ 交换前后台缓冲区的函数,在双缓冲或多缓冲的图形系统用于将渲染结果从后台缓冲区显示到前台缓冲区。

1
2
3
4
EGLBoolean eglSwapBuffers(
EGLDisplay dpy,
EGLSurface surface //eglCreateWindowSurface创建
);

eglSwapInterval

​ 控制V-sync间隔,控制缓冲区交换的频率,控制每秒交换的次数

1
2
3
4
EGLBoolean eglSwapInterval(
EGLDisplay dpy,
EGLint interval
);

interval

  • 0:禁用垂直同步

  • 1:启用垂直同步,每个垂直同步周期交换一次,即和显示器的刷新率保持一致,一般是60Hz或者120Hz

  • 2:每两个垂直同步周期交换一次,降低GPU负载

eglCopyBuffers

​ 用于把EGLSurface上的图像数据复制到一个外部的缓冲区,一般用于把图像渲染结果保存到外部图像缓冲区,或者把图像数据传输到GPU之外的存储空间。

1
2
3
4
5
EGLBoolean eglCopyBuffers(
EGLDisplay dpy,
EGLSurface surface,// eglCreateWindowSuface创建
EGLNativePixmapType target //目标缓冲区,一般为外部
);

​ 将EGLSurface上的图像数据直接复制到原生平台的缓冲区或纹理中,然后用于后续的处理,或者保存为图像文件。主要应用场景:截图、跨平台渲染、后处理等;

8.Others

1.eglBindAPI

​ 指定客户端使用哪种API,OpenGL、OpenGLES、OpenVG


文章作者: bigbear
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 bigbear !
  目录