OpenGL ES 依次渲染到不同紋理速度很慢?

實際情況簡化如下:有兩個Shader Language 程序,分別需要渲染到不同大小的texture上(一個的輸出是另一個的輸入),循環執行如下。

for (int i=0; i&<1000; ++i) { glBindFramebuffer(GL_FRAMEBUFFER, fbo1); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, outTex1, 0); // program1 draw something ... glBindFramebuffer(GL_FRAMEBUFFER, fbo2); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, outTex2, 0); // program2 draw something ... }

outTex1, outTex2 每次申請新的或者固定使用,不影響結果。

實際測試發現,有些機器在 glBindFramebuffer 耗時,有些則在 glFramebufferTexture2D 較慢。這個問題主要是切換附著的紋理造成的,試了 glClear glInvalidateFramebuffer detach 貌似都沒什麼用,請高手支支招吧

=========== 補充問題 ==========

如果 program1 或 program2 有一個比較耗時(比如掃描整個texture ...),則切換渲染目標時,可能不是停滯 1-2 ms,而是30+ms。這個是因為切換 RT 導致GPU的並行執行變成串列嗎?

(確定是的,通過 glFinish 驗證 )

===========

這個問題搜索 Ping Pong Framebuffer也有人遇到,但是好像沒找到有價值的結果。


這件事情和OpenGL ES沒關係,和tile-based GPU有關係。只是碰巧兩者經常是重合的罷了。

比較一下傳統GPU和tile-based GPU在處理同樣兩個渲染任務上的區別,你就能知道原因了。

任務1:在渲染目標(RT)A上,渲染一個三角形T1,再渲染一個三角形T2

任務2:在渲染目標(RT)A上,渲染一個三角形T1,在渲染目標B上,渲染一個三角形T2

對於傳統GPU,任務1的流程是這樣的

  1. 切換到RT A
  2. 把整個T1從頭到尾畫出來,和RT A混合
  3. 把整個T2從頭到尾畫出來,和RT A混合

任務2的流程是這樣的

  1. 切換到RT A
  2. 把整個T1從頭到尾畫出來,和RT A混合
  3. 切換到RT B
  4. 把整個T2從頭到尾畫出來,和RT B混合

而在tile-based GPU上,任務1的流程是這樣的

  1. 切換到RT A
  2. 把RT A切成N個tile
  3. 把tile 0載入cache
  4. 把T1在tile 0的區域畫出來,和cache里的內容混合
  5. 把T2在tile 0的區域畫出來,和cache里的內容混合
  6. 把tile 1載入cache
  7. 把T1在tile 1的區域畫出來,和cache里的內容混合
  8. 把T2在tile 1的區域畫出來,和cache里的內容混合
  9. 。。。循環到tile N
  10. 把tile 0的cache寫入RT A
  11. 把tile 1的cache寫入RT A
  12. 。。。循環到tile N

任務2的流程就麻煩多了

  1. 切換到RT A
  2. 把RT A切成N個tile
  3. 把tile 0載入cache
  4. 把T1在tile 0的區域畫出來,和cache里的內容混合
  5. (咦,tile 0沒東西可畫了,等吧)
  6. 把tile 1載入cache
  7. 把T1在tile 1的區域畫出來,和cache里的內容混合
  8. (咦,tile 1沒東西可畫了,等吧)
  9. 。。。循環到tile N
  10. 把tile 0的cache寫入RT A
  11. 把tile 1的cache寫入RT A
  12. 。。。循環到tile N
  13. 切換到RT B
  14. 把tile 0載入cache
  15. 把T2在tile 0的區域畫出來,和cache里的內容混合
  16. (咦,tile 0沒東西可畫了,等吧)
  17. 把tile 1載入cache
  18. 把T2在tile 1的區域畫出來,和cache里的內容混合
  19. (咦,tile 1沒東西可畫了,等吧)
  20. 。。。循環到tile N
  21. 把tile 0的cache寫入RT B
  22. 把tile 1的cache寫入RT B
  23. 。。。循環到tile N

這個簡單的對比已經可以很明顯看出,在傳統GPU上,切換RT帶來的代價很小,而tile-based上,就會需要等待、把tile cache寫入RT、把新RT載入tile cache這一系列的開銷。以至於在tile-based上,切換RT經常需要0.5-1.5ms的停滯時間。這麼一來性能就會非常糟糕。

那麼, glClear和glInvalidateFramebuffer,其實省掉的是把RT的內容載入tile cache的時間。因為那告訴了驅動,RT里的內容我不要了,別載入。對於整個流程有所改進,但其他步驟仍然沒變。


switch rendertarget時driver通常會flush gpu internal cache,和clflush類似,都是耗時的操作。

可以試試用固定rt,渲染後blit到另一個buffer上去。


推薦閱讀:

cocos2dx 如何做到無卡頓載入紋理?
理論計算機圖形渲染技術是否已經到了沒有什麼可以研究的地步了?

TAG:OpenGL | 圖形處理器GPU | OpenGLES |