昨天我遇到一个问题, 问题如下:
我使用了延迟渲染, 我的渲染流程是: Pass1 --> CUDA 并行计算 -->Pass2
CUDA 并行计算中需要使用 Pass1 渲染生成的两张纹理, 然而我在 GPU 端使用 CUDA 计算时发现纹理为空 (数据全是 0 值), 但是如果将两张纹理的数据传回 CPU 端, 打印出来是有值的, 且是正确的值. 如果在 CUDA 并行计算之前先将纹理数据传回 CPU, 这时发现 CUDA 并行计算中纹理是正常有值的... 这个现象很奇怪, 我开始想了想会不会是阻塞啥原因, 但我对 OpenGL 阻塞过程不了解, 没看到过相关的资料, 简单思考了一下觉得不是阻塞的原因, 我觉得可能是其中的一张纹理有问题, 牵连导致这个问题..... 最终验证发现还是 OpenGL 阻塞的原因. 在 CUDA 并行计算之前加上 glFinish() 函数即可.
为啥加上 glFinish()函数就解决了呢? 解释这个之前, 先说一下 glFlush()和 glFinish()函数的作用:
一个 OpenGL 渲染程序会调用很多的 OpenGL 命令, 而 OpenGL 是异步的, CPU 将这些耗时的命令发送到 GPU 端, 然后直接返回继续执行, 这些 OpenGL 相关指令存储在 GPU 的缓存中一条条的执行, 但是 CPU 也不是直接发送给 GPU 的, CPU 自己有缓存, 先存储在自己的缓存中, 之后再发送过去(有时机, 例如遇到某些刷新的命令等). 现在开始介绍以上两个函数的作用.
glFlush(): 将缓存在 CPU 端的命令发送到 GPU 上, 清空缓存, 发送完立即返回.
glFinish(): 将缓存在 CPU 端的命令发送到 GPU 上, 清空缓存, 发送完, 等待 GPU 执行完在返回.
看到这里就可以理解我加上 glFinish() 可以解决问题的原因了. 我没加 glFinish() 时, CUDA 并行计算时, 这时 Pass1 实际没有执行完, 故纹理为空, CUDA 中拿不到正确的纹理数据, 加上 glFinish() 后实际就是加入了 GPU 阻塞, 等待 Pass1 执行完, 然后执行 CUDA 并行计算.
注: 说到这里, 谈点我思考的问题:
1, 在 OpenGL 渲染中, 不管是 Pass1 --> CUDA 并行计算 -->Pass2, 或者 Pass1 --> Pass2 或者 Pass(只有一个 Pass), 我们统计两次渲染之间的时间差值就可以计算帧率, 为啥不会因为异步问题计算不准呢? 因为 glfwSwapBuffers(glfw_window) 命令会将所有 CPU 端的命令发送到 GPU 端, 并等待其执行完, 然后再交换前后缓冲.
2, 对于 Pass1 --> Pass2 这样的延迟渲染, Pass1 和 Pass2 都是作为命令发送到 GPU 端, 按序执行, 故不会出现 Pass2 拿到的数据 (由 Pass1 处理的) 不正确情况, 我们不需要加阻塞保障其执行.
另外不要错误以为, CPU 运行到 Pass2 处看见 Pass1 还在执行, CPU 阻塞等待 Pass1 执行完然后发送 Pass2 指令.
3, 为啥在 CUDA 并行计算前, 对纹理进行一次数据传回可以让 CUDA 获取正确的纹理数据 ? 我觉得 (差不多肯定是这样, 哈哈, 自信......) 拷贝纹理数据的 OpenGL API 虽然与 GPU 相关, 但是其与 CPU 也相关, 需要在 CPU 端的内存上接收传回的数据, 从 CPU 端考虑它也会阻塞的.
4,Pass1 和 CUDA 并行计算 都是在 GPU 上执行的, 而 CUDA 拿不到正确的纹理数据, 可以认为 Pass1 和 CUDA 并行计算 同时在 GPU 上并行执行(我猜的, 应该是吧......).
来源: https://www.cnblogs.com/chen9510/p/12158299.html