昨天我遇到一個問題,問題如下:
我使用了延遲渲染,我的渲染流程是: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上並行執行(我猜的,應該是吧……)。