使用 QGraphicsScene 繪製 widget、產生 OpenGL Texture:續

| | 0 Comments| 16:13
Categories:

這篇基本上是之前《使用 QGraphicsScene 繪製 widget、產生 OpenGL Texture》一文的後續。

在當時,Heresy 有提過,如果真的用 QOpenGLPaintDevice 來給 QGraphicsScene 繪製到 Frame Buffer Object 上的話,會造成文字的破碎、無法識別的狀況;而 Heresy 當時的解法,是暫時放棄 OpenGL 繪圖,而讓 QGraphicsScene 先畫到 QImage 上(raster)、然後再把它傳到顯示當作為 OpenGL 的 texture。

理論上,這個方法的確是可行的,但是實際上,在後來使用的時候,卻發現:

當介面稍微多一些東西的時候,用 QGraphicsScene 把圖形介面畫在 QImage 上要花超過 150ms… orz

如果是一般視窗環境,或許還算勉強可以接受,但是在 VR 環境下,這樣連 10FPS 都到不了的速度,是完全無法被當作「堪用」的…

而且,這個時間還不包含從系統記憶體送到顯示卡記憶體的時間呢…

後來,在參考《Qt Graphics and Performance – An Overview》一文後,試著把 QImage 的格式從 RGBA8888 改成 RGB32,發先可以把快 150+ms 壓到 40ms 以下,算是快了相當地多~

(比較奇怪是,這樣在設定 OpenGL Texture 的時候,格式得改成 GL_BGRA…)

但是儘管如此,不到 30FPS 的更新率,基本上還是不夠快啊…不過,這或許也是沒有使用硬體加速的極限了?


而如果改用 QOpenGLPaintDevice、用 OpenGL 畫在 frame buffer object 上的話,文字會爛掉,但是時間可以壓到 20ms、甚至大部分是在 15ms 以下。而且,由於他直接是畫在顯示卡記憶體上了,所以也不需要後續的傳輸時間。

不過,考量到 VR 環境所需的更新綠,大概還是得看看怎麼解決使用 OpenGL 來繪製 UI 時,文字會產生的問題了…

本來有在考慮,不知道是不是因為 OpenGL stack 是和 3D 場景繪製共用,所以導致狀態混亂?所以也試著加上了 QPainter::beginNativePainting()參考),但是似乎沒有用。

最後,則是發現:

如果另外建立一個 OpenGL context 專門拿來畫圖形介面,似乎就可以解決大部分問題

但是…這樣下來,就變成一個程式會有兩個 OpenGL Context,需要使用 context sharing 來做管理,整個架構會變得相對複雜啊…

基本上,就是得另外建立一個 QOpenGLContext,並在呼叫 create() 之前,先透過他的提供的 setShareContext() 的函式,可以設定要和繪製 3D 場景的 context 共用資源;然後,則是在這個 context 中,建立需要的 OpenGL 物件(FBO、texture、vertex buffer object…)。

而由於這樣的架構下,會有多個 OpenGL conext,所以就得到處放 makeCurrent() 了…

另外,文字的問題,似乎也不能算是完全解決。

在使用獨立 context 後,雖然大部分的圖形介面、文字都可以正常顯示了,但是卻還是有部分的文字會出問題;而這次的問題,則是整個變黑框…

像右圖中,可以看到大部分的文字都可以正確顯示(不清楚是解析度問題);但是黑色方框的部分,則是 Tab 的文字,這邊就因為不明原因,無法正確顯示了。而錯誤的方式,也和之前使用同一份 context 時明顯不同。

後來又研究了好一陣子,最後發現,感覺這種文字的錯誤,似乎是因為 Heresy 有把整個圖形介面放大造成的

在經過測試後,發現似乎是把介面放大兩倍以內,都不會有問題;但是如果到三倍以上,就很容易出現這樣的問題?像上面的錯誤狀況是放大四倍時造成的,而如果把它改成只放大兩倍,就會是正確的(下圖)…

所以感覺上,這個問題似乎變成是 Qt 繪製圖形介面的程式,在需要處理放大時,文字的部分有問題? orz

不過,儘管如此已經解決了大部分的問題,其實還是有小部份的問題存在;像是上面的圖片如果仔細看的話,就會發現,「Translation」背後的 group 的淺灰色框線,其實沒有正確地斷開,而是連續的…

只是基本上,這個相對起來真的只是小問題就是了。


總之,現在算是勉強做出一個堪用的、使用 Qt 做出 VR 環境的圖形介面的框架了;由於影像也變小了,所以現在繪製的時間大致上是可以壓在 10ms 左右,速度也算讓人滿意了。

雖然看來應該還是有些小問題,但是就等真的碰到嚴重的狀況再說吧。

之後可能再考慮整理一下程式碼,把片段的程式碼放出來吧。


附註:

  • 在稍微研究一下後,也發現一個可能是效能瓶頸的問題,那就是是用 QGraphicsScene 畫 widget 的時候,他的 changed 事件傳過來的更新區域根本是全部、沒辦法局部更新啊! orz

  • 使用 OpenGL 繪製的話,由於座標系統定義不同,所以上下會是顛倒的。

    這部分可以透過 QOpenGLPaintDevice::setPaintFlipped() 這個函式來做調整。

  • 理論上這邊也可以不需要 QGraphicsScene、直接用 QWidgetrender() 這個函式(參考)來畫;不過,由於 QWidget 本身沒有 signal 可以用來觸發畫面更新的事件,所以真的要拿來處理畫面更新的重繪,必須要去繼承 QWidget、重新實作 paintEvent(),整個會很麻煩…

    QGraphicsScene 來做變動偵測,應該還是比較簡單的。

    • 這樣畫的時候,QWidget 不會去處理縮放的問題,所以要透過 QPainterQPaintDevice 的函式來做縮放的設定。

Leave a Reply

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *