分享来自音视频技术社群关键帧的音视频开发圈的第 18 期面试题精选。
下面是第 18 期面试题精选:
- 1、聊聊 OpenGL glFlush 和 glFinish 区别?
- 2、怎么实现 OpenGL 多线程同步?
- 3、如何实现 OpenGL 资源共享?
- 4、OpenGL 纹理缓存要如何设计?
1、聊聊 OpenGL glFlush 和 glFinish 区别?
一般来说,我们在使用 OpenGL 的时候,指令不是立即执行的。它们首先被送到指令缓冲区,然后才被送到硬件执行。glFinish
和 glFlush
都是强制将命令缓冲区的内容提交给硬件执行。
1)glFlush:
glFlush
清空缓冲区,将指令送往缓硬件立即执行,但是它是将命令传送完毕之后立即返回,不会等待指令执行完毕。这些指令会在有限时间内执行完毕。
2)glFinish:
glFinish
将缓冲区的指令立即送往硬件执行,但是要一直等到硬件执行完这些指令之后才返回。如果直接绘制到前缓冲,那么在你想保存屏幕截图之前,就需要调用这个函数,确保绘制完毕。如果使用双缓冲,则这个函数不会有太大作用。
如果调用 glFinish
,通常会带来性能上的损失。因为它会是的 GPU 和 CPU 之间的并行性丧失。一般,我们提交给驱动的任务被分组,然后被送到硬件上(在缓冲区交换的时候)。如果调用 glFinish
,就强制驱动将命令送到 GPU。然后 CPU 等待直到被传送的命令全部执行完毕。这样在 GPU 工作的整个期间内,CPU 没有工作(至少在这个线程上)。而在 CPU 工作时(通常是在对命令分组),GPU 没有工作。因此造成性能上的下降。
3)glFlush 和 glFinish 区别:
一般使用 glFlush
的目的是确保在调用之后,CPU 没有 OpenGL 相关的事情需要做,命令会送到硬件执行。调用 glFinish
的目的是确保当返回之后,没有相关工作留下需要继续做。
2、怎么实现 OpenGL 多线程同步?
1)OpenGL 为什么需要同步?
一般情况下我们调用 OpenGL 方法后,并不是马上有效果的,如果在 B 线程使用 A 线程的纹理有概率出现渲染异常,因为 A 纹理还没有渲染完成。
2)glFinish 同步方案
如果要某处确保之前的 OpenGL 执行完,通常会用到 glFinish
,确保效果正常。但 glFinish
只能保证本线程对应的命令队列中的命令执行完,这就意味着不能在一个线程中等待另一个线程的 OpenGL 命令执行完,这就有很大的限制。
3)Fence 同步方案
回想我们在 CPU 上的同步操作,例如我们在一个线程中 wait,在另一个线程中 notify,这很容易实现在一个线程中等待另一个线程的指定任务执行完成,这也是我们很常用的操作,但在对于 GPU,无法用 glFinish
来实现类似的逻辑。到了OpenGL ES 3.0,我们可以用 fence
实现,使用越来也很简单,就是在一个线程中插入一个 fence
,然后在另一个线程中就可以去等待这个 fence
。
例如我们有这样一种逻辑,在 GLThread 0 中渲染一个纹理,在另一个线程 GLThread 1 中将这个纹理拿去使用,那就需要确保在 GLThread 1 使用这个纹理时,GLThread 0 对这个纹理的渲染已经完成,有了 fence
后,我们可以在 GLThread 0 渲染操作之后插入一个 fence
,然后在 GLThread 1 要使用这个纹理时去等这个 fence
。
插入 fence
的代码,通常线程 A 插入:
GLsync fenceSyncObject = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
glFlush();
这个方法调用后会往当前线程的命令队列中插入一个 fence
并返回一个 long 型变量来代码这个 fence
同步对象,以便于其它地方去等待它。glFlush 这里作用保证 fence
指令一定可以推入 GPU。
等待 fence
的代码,通常线程 B 等待:
glClientWaitSync(fence, 0, GL_TIMEOUT_IGNORED);
glDeleteSync(fence);
有 2 个方法可以用于等待, glWaitSync
和 glClientWaitSync
,它们的差别是 glWaitSync
是在 GPU 上等待,glClientWaitSync
是在 CPU 上等待。这样好处不会阻塞 CPU ,提高渲染效率。
3、如何实现 OpenGL 资源共享?
1)资源共享基础描述
OpenGL 渲染中有一个线程相关的上下文 (Context), OpenGL 所创建的资源,其实对程序员可见的仅仅是上下文 ID 而已,其内容依赖于这个上下文,有时候为了方便起见,在某个线程中创建了上下文之后,所有的 OpenGL 操作都转到此线程来调用。这样在简单的 2d/3d 渲染中尚可,但是如果涉及复杂的 OpenGL 渲染时,这样就未必足够, 事实上 OpenGL 已经考虑到这一点, 上下文是可以在多个线程间共享的,在使用 eglCreateContext 时, 可以传入一个已创建成功的上下文, 这样就可以得到一个共享的上下文 (Shared Context).
OpenGL 的绘制命令都是作用在当前的 Context 上,这个 Current Context 是一个线程私有(thread-local)的变量,也就是说如果我们在线程中绘制,那么需要为该线程制定一个 Current Context 的,当多个线程参与绘制任务时,需要原线程解绑再重新绑定新的线程。多个线程不能同时指定同一个 Context 为 Current Context,否则会导致崩溃。
2)OpenGL 可以共享哪些资源?
可以共享的资源:
- 纹理;
- shader;
- program 着色器程序;
- buffer 类对象,如 VBO、 EBO、 RBO 等 。
不可以共享的资源:
FBO 帧缓冲区对象(不属于 buffer 类); VAO 顶点数组对象(不属于 buffer 类)。 在不可以共享的资源中,FBO 和 VAO 属于资源管理型对象,FBO 负责管理几种缓冲区,本身不占用资源,VAO 负责管理 VBO 或 EBO ,本身也不占用资源。
3)OpenGL 共享上下文实现?
iOS ShareContext 实现:
- (EAGLContext *)shareContext:(nullable EAGLContext *)context {
EAGLContext *shareContext = [[EAGLContext alloc] initWithAPI:context.API sharegroup:context.sharegroup];
return shareContext;
}
Android ShareContext 实现:
public EGLContext eglCreateContext(EGLDisplay display, EGLConfig config, EGLContext share_context, int[] attrib_list) {
EGLContext result = mEgl10.eglCreateContext(display, config,
share_context, attrib_list);
return result;
}
4)OpenGL 共享上下文有哪些注意事项?
- 每个线程同时只能绑定(eglMakeCurrent)一个 Context ,但可以按顺序切换不同 Context。
- 多线程共享 Context 需要注意同步,可以使用 fence 或者 glFinish。
- FBO、VAO 不同 Context 不可以共享。
4、OpenGL 纹理缓存要如何设计?
1)OpenGL 纹理缓存用途?
- 播放器场景:解码器解码后的纹理上屏,通用情况解码后的纹理立即渲染即可,但如果解码后的纹理添加缓存模块,缓存模块可以大大优化播放器的渲染帧率(4K 模式)。
- 转码场景:编码与解码通常为 2 个不同线程,解码需要有自己的纹理缓存,这样异步编码模块可以最快速度获取解码纹理数据。
2)OpenGL 纹理缓存如何设计?
- 需要一个可复用的纹理数组,设置一个最大上限。
- 每个纹理需要忙碌或空闲的状态,当空闲情况下可以进行复用。
- 一个 FBO 频繁更换绑定不同的纹理,将内容数据刷新到指定纹理上。
- 外层纹理使用完成后将纹理状态设置为空闲。
3)FBO 绑定指定纹理如何实现?
代码示例如下:
void AttachTextureToFBO (GLuint texId) {
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texId, 0);
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
check(status);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
更多的音视频知识、面试题、技术方案干货可以进群来看:
版权声明:本文内容转自互联网,本文观点仅代表作者本人。本站仅提供信息存储空间服务,所有权归原作者所有。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至1393616908@qq.com 举报,一经查实,本站将立刻删除。