劉敏娜 李延香 魏浩



摘要:針對OpenGL渲染圖形要多次訪問緩存區的問題,提出一種OpenGL和CUDA混合編程的圖形渲染算法來加速PerlinKernel生成虛擬地形圖。首先,通過OpenGL將緩存映射到CUDA內存空間,利用CUDA完成加速計算任務;然后,為幾何圖形設置開始和結束的位置標志,使用基元重啟對圖形進行組合;最后,對緩沖區對象進行渲染。實驗結果表明,改進后的基于基元重啟的混合算法在GTX650GPU上的平均幀速率為960fps,幀速率提高6%,算法改進后渲染方法的執行效率提高了63倍。實驗證實基元重啟可以提高3D處理性能。
關鍵詞:圖形渲染;OpenGL;CUDA;基元重啟;Perlin
中圖分類號:TP393文獻標識碼:A
1引言
圖形渲染在核試驗、DNA分子分布、天氣預報等大規模科學計算任務中扮演著重要的角色[1,2]。在OpenGL中圖形渲染是由CPU進行單獨完成的,CPU從RAM中獲得數據并且處理數據,然后寫入RAM中[3]。這樣做性能并不高,原因如下:①CPU的負載重而導致響應速度慢,影響渲染速度和質量;②圖形渲染中使用了multiDraw()方法,繪圖開銷過大。multiDraw()用一條命令代替了多條glDraw*()方法,但是使用這個方法,導致頂點數組擴大了1/2,大量的冗余數據傳輸到CPU中,造成極大的開銷[4-7]。
本文提出了一種CUDA和OpenGL混合圖形渲染的方法。CUDA(ComputeUnifiedDeviceArchitecture),是NVIDIA顯卡廠商推出的通用并行計算架構,該架構使GPU能夠解決復雜的計算問題[8-10]。在本文中,為了進一步提高數據傳輸效率,圖形渲染中引入了基元重啟,使用基元重啟可以對幾何圖形進行組合,組合之后,需要處理的集合圖形數據更少,系統運行速度更快。
2.1傳統的OpenGL圖形渲染
OpenGL中的頂點,顏色,法線和其它頂點屬性數據都是由GLTools庫管理的。每次調用glDrawArrays、glDrawElements等一些需要頂點數據的函數時,信息是從一個帶有本地GPU的高性能系統中的應用程序內存中獲取的,數據將從應用程序的內存中通過PCI-Express接口總線傳遞到GPU本地內存[11],將會耗費大量時間,降低應用程序的運行速度。
如果將對象的所有頂點數據打包到單個緩沖區中,程序中必定包含循環,會產生很多OpenGL調用,每次調用都會有一定的系統開銷。如果場景中存在大量對象,每個對象都有相關的三角形,那么對glDrawArrays的調用中的開銷將會積累,從而對應用程序性能產生負面影響。
為了提高系統的處理效率,可以將大批非連接的三角形組合成三角形帶,如圖1所示。本來獨立的三角型需要3個頂點才能進行表示,經過組合之后每個三角形所需的頂點數減少到1個(不包含三角形帶中的第一個三角形)。這樣需要處理的集合數據更少了,系統運行速度會更快。
但是問題是,一個三角形可以通過單次調用glDrawArrays或者glDrawElements進行渲染,一組三角形帶的渲染就要單獨對OpenGL進行多次調用,這意味著在一個使用條帶化集合圖形的程序中有更多的函數,這可能會抵消使用條帶化所獲得的性能提升。所以針對條帶化處理應該有更好的方法來提高系統的性能,文中提出了基元重啟算法。
2.2基元重啟
基元重啟是設定一個可以被OpenGL狀態機識別的數據值,該數據值用于標識當前圖形圖元已經繪制完成。下一個數據項表示同樣類型的另一個圖形圖元開始。基元重啟應用在GL_TRIANGLE_TRIP、GL_TRIANGLE_FAN、GL_LINE_STRIP和GL_LINE_LOOP幾何圖形類型中。方法在一個條帶(或者扇,環)結束和另一個開始時通知OpenGL。在幾何圖形中指示出一個條帶結束,下一個條帶的開始位置,實現時需要在元素數組中放置一個作為保留值的特殊標志。由于OpenGL是從元素數組中獲取頂點索引(或者在內部生成它們),在glDrawArrays非索引繪制命令情況下,會檢查這個特殊索引值,并且在遇到它時結束當前條帶并在下一個頂點開始一個新的條帶。在基元重啟模式開啟時,OpenGL會在遇到它時停止當前的條帶并開始一個新的條帶。
如圖2所示,三角形帶由9個頂點組成,在單個連接的三角形帶中一共產生了8個三角形。通過開啟基元重啟模式并將基元重啟索引設置為5,三角形帶會在頂點4處結束,如圖3所示。頂點5的實際位置將被忽略,下一個進行處理的頂點6將稱為新的三角形帶的起始位置。所以,在向OpenGL傳遞9個頂點的情況下,得到兩個獨立的三角形帶,一個繪制3個三角形,另一個繪制2個三角形。這樣一次數據傳輸的數據量就增加了。
基元重啟的好處:
1)基元重啟的標記值和繪圖的數據可同時生成并存放于GPU上。
2)不同基元重啟標記值可以指定不同數量的繪圖元素,可通過給glDrawElements()指定不同的繪圖模式,繪制任意數量的線段、三角條帶、三角扇面等圖形,所以可以繪制不規則的網格或表面。
3)通過安排索引可以優化渲染性能,達到在紋理單元中最大限度重用數據高速緩存。
2.3基于基元重啟的OpenGL和CUDA圖形渲染的實現
采用Perlin噪聲生成器創建虛擬地形的立體地圖[12],通過按鍵命令改變地形特征。框架分成4個獨立的文件,分別如下:
1)kernelVBO.cpp,CUDA數據生成器;
2)simpleGLmain.cpp,定義OpenGL和GLUT的主體框架;
3)simpleVBO.cpp,是通用的OpenGL與CUDA設置以及內存管理;
4)callbackVBO.cpp,用于定義鍵盤,鼠標,顯示的回調函數。
kernelVBO
kernelVBO的處理流程如下:
1)指定Perlin噪聲計算中使用的頭文件、變量和方法;
2)colorElevation()方法根據地形各點的海拔返回像素的顏色,選用不同顏色可以為用戶呈現一種觀看地圖的感覺;
3)錯誤檢查函數;
4)k_perlin()函數利用Perlin噪聲生成地形圖,將低于海平面的區域的高度設為0。調用cudaThreadSynchronize(),主機刷新OpenGL緩沖區需要等待Kernel執行完畢。
simpleGLmain
simpleGLmain用來在屏幕上打開一個窗口,并設置一些基本的視角變換參數。調用gluPerspective(),在三維空間中設置一個攝像機,從該攝像機的位置觀察CUDA生成的數據。當數據或視角位置發生變化時,OpenGL會識別和刷新顯示像素。
渲染需要進行如下3D變換:
[1]視角變換,場景中攝像機的安放與定點;
[2]模型變換,安排場景構成;
[3]投影變換,調整攝像機的變焦;
[4]可視區變換選擇最終的尺寸。
OpenGL的視角、模型、投影、可視區變換,以及坐標系統的設定都要慎重。
simpleGLmain處理流程:
1)main函數初始化用于計算幀速率的定時器,然后調用用戶自定義的方法初始化CUDAKernel,然后注冊用戶的回調函數,最后調用GLUT主循環;
2)計算幀速率,并將其顯示在窗口的標題欄中;
3)GLUT和OpenGL初始化函數創建一個窗口,并在3D空間中指定一個視角位置。
simpleVBO
simpleVBO的算法如下:
1)定義創建和映射色彩信息PBO和頂點VBO的邏輯。
2)creatVBO()調用glBufferData(),在GPU上分配圖形緩存區。GL_DYNAMIC_DRAW標志位告知OpenGL該數據存儲會被重復修改與使用。cudaGraphicsGLRegisterBuffer()將緩沖對象注冊為CUDA可訪問數據。deleteVBO()方法將OpenGL緩沖對象解除注冊并釋放對應的存儲空間。
3)runCUDA()完成所有色彩PBO的與頂點VBO的映射和檢索指針。地址傳給lauch_kernel()方法,并被用戶自定義的Kernel使用。Lanuch_kernel()在返回之前會等待所有的Kernel執行完畢,因此該方法返回時可以安全交回OpenGL資源。
使用基元重啟渲染三角形時,通過glPrimituveRestartIndexNV()方法告知OpenGL狀態機重啟的標記值,然后在OpenGL客戶狀態機上啟動基元重啟功能。調用glDrawElement()渲染數據。當渲染結束,在OpenGL狀態機上關閉基元重啟。
3實驗結果及性能分析
實驗平臺為Intel2.3GHzCore2雙核處理器,圖形加速器為NVIDIA公司的GTX650,核心頻率為1000MHz,顯存頻率為4500MHz,顯存位寬為128b,顯存為1024MB,操作系統為Linux,運行的是PerlinKernel生成虛擬地形圖的示例。驅動程序的版本號是9.18.13.2057,總線采用PCIExpress3.0x16接口。應用程序在VS2010環境編譯及執行,采用CUDA平臺GPU設備編寫kernel程序,程序使用擴展的C語言編寫。
表1中的數據代表在最差情況幀速率方案中,程序混合使用CUDA和OpenGL所展示的能力和速度。實際的應用程序僅會重新計算渲染場景所必需的最小數據量,顯然會獲得更高的性能。幀速率包含了重新計算每個頂點3D坐標和顏色的時間,以及圖像中每個像素顏色所需要的時間。
在ParallelNsight中查看程序執行時間,PerlinKernel只消耗了很短的(幾乎可以忽略不計的)執行時間,執行的時間主要耗費在OpenGL的緩沖交換,需要多次進行緩沖數據交換。
在ParallelNsight中查看OpenGLAPI函數調用的情況,得到各種渲染方法的執行時間:基元重啟,大約30μs,multidraw大約1900μs,逐一繪制各扇形:大約1000000μs。執行時間雷達圖如下圖5所示。因為基元重啟減少了PCIe總線上的數據傳輸,避免了數據傳輸對性能的影響,因此使用基元重啟比使用優化的OpenGL的multiDraw()方法性能要高出1900/30=63倍。
4結語
本文研究了基于基元重啟的OpenGL和CUDA圖形渲染算法,在OpenGL和CUDA之間實現互操作的原理,OpenGL將內存映射入CUDA內存之后,利用CUDA進行并行處理。但是如果兩者之間的數據需要頻繁交換,一定程度上會降低執行效率。通過在傳輸數據上設置基元重啟標志,一次傳輸的數據信息量大了,這樣降低了數據的交換次數,提高了處理的速度。從實驗結果上分析,采用OpenGL和CUDA混合編程改寫Perlin噪聲生成的人工地形程序比使用優化的OpenGLMultiDraw()方法性能有了大幅度的提高。接下來需要解決的問題是OpenGL和CUDA混合編程實現網絡攝像頭的現場視頻流處理。
參考文獻
[1]張權.渲染技術在虛擬仿真中的應用[J].電子制作,2015,(09):62-63.
[2]QIAOShaojie,WANGYouwei,NIShengqiao.HighspeedimagerenderingmethodbasedonOpenGL[J].ComputerEngineeringandDesign,2011,47(21):47-49.(喬少杰,王有為,倪勝巧.基于OpenGL的快速圖像渲染方法[J].計算機應用研究,2008,25(5):1589-1595.)
[3]孫曉潔,葉樺,費樹岷.基于OpenGL的混凝土泵車智能臂架系統仿真[J].科技通報,2014(05):22-25.
[4]吳恩華.輻射度技術用于隨機分維幾何面的全局光照計算[J].計算機學報,2000,(05):321-323.
[5]ZHAOJianbin,LILingqiao,YANGHuihua.Researchongraphicrenderingenginewiththreadlevelparallelismmethod[J].ComputerEngineeringandDesign,2011,47(21):47-49.(趙建斌,李靈巧,楊輝華.線程級并行計算在圖形渲染引擎中的研究[J].計算機工程與設計,2011,32(12):4143-4147.
[6]RICHARDS.WRIGHT,JR.NicholasHaemel.OpenGlSuperBibleFifthEdition[M].北京:人民郵電出版社,2012:380-420.
[7]李妮,陳錚,龔光紅.多核并行計算技術在景象匹配仿真中的應用[J].系統工程與電子技術,2010,32(2):428-432.
[8]張林波,遲學斌,莫則堯,等.并行計算導論[M].北京:清華大學出版社,2006:1-59.
[9]RobFarber.CUDAApplicationDesignandDevelopment[M].北京:機械工業出版社,2012:178-185.
[10]GORDERPF.Multicoreprocessorsforscienceandengineering[J].ComputinginScience&Engineering,2007,9(2):3-7.
[11]NVIDIACUDAProgrammingGuide,version2.1[S].NVIDIA,2008.
[12]項予,許森.基于Perlin噪聲的動態水面實時渲染[J].計算機工程與設計,2013,34(11):3966-3969.
第35卷第1期2016年3月計算技術與自動化ComputingTechnologyandAutomationVol35,No1Mar.2016第35卷第1期2016年3月計算技術與自動化ComputingTechnologyandAutomationVol35,No1Mar.2016