鄭 飛, 楊博文, 耿玉榮, 蘆守鵬
(1.西安郵電大學(xué) 計(jì)算機(jī)學(xué)院,陜西 西安 710121; 2.西安郵電大學(xué) 電子工程學(xué)院,陜西 西安 710121)
近年來,隨著計(jì)算機(jī)行業(yè)的不斷發(fā)展,同時(shí)促進(jìn)了移動(dòng)終端的圖形處理單元(graphics processing unit,GPU)軟件開發(fā)發(fā)展。以往的計(jì)算機(jī)軟件開發(fā)大多為二層結(jié)構(gòu),現(xiàn)在已經(jīng)開始向多層結(jié)構(gòu)發(fā)展,這對(duì)于軟件開發(fā)領(lǐng)域來說具有重要的意義[1]。特別是隨著移動(dòng)設(shè)備的普及,在移動(dòng)終端驅(qū)動(dòng)開發(fā)中分層技術(shù)的應(yīng)用,能夠使軟件系統(tǒng)的完整性與清晰性得以提高,為軟件的靈活使用提供強(qiáng)有力的支持,為移動(dòng)終端的GPU軟件開發(fā)的發(fā)展提供強(qiáng)有力的保障,因此眾多專家學(xué)者們將目光轉(zhuǎn)向了移動(dòng)終端的GPU驅(qū)動(dòng)分層技術(shù)的研究設(shè)計(jì)當(dāng)中。由于移動(dòng)終端的GPU用戶態(tài)驅(qū)動(dòng)系統(tǒng)處于應(yīng)用程序和內(nèi)核態(tài)驅(qū)動(dòng)的中間位置,在移動(dòng)終端的GPU驅(qū)動(dòng)設(shè)計(jì)中起著至關(guān)重要的作用,因此,如何分層設(shè)計(jì)移動(dòng)終端的GPU用戶態(tài)驅(qū)動(dòng)來降低軟件系統(tǒng)耦合度并且保證繪圖速率成為迫切需要解決的問題。
針對(duì)上述問題,眾多學(xué)者在相關(guān)領(lǐng)域開展了大量?jī)?yōu)化研究。文獻(xiàn)[2]和文獻(xiàn)[3]提出了在圖形應(yīng)用程序啟動(dòng)時(shí)加載到程序中,可以被不同的應(yīng)用程序共享。文獻(xiàn)[4]提出了是在圖形應(yīng)用程序運(yùn)行過程中的任何時(shí)間加載。文獻(xiàn)[5]是在對(duì)曲面進(jìn)行網(wǎng)格劃分時(shí),使用一種有效的內(nèi)存管理策略使得網(wǎng)格單元數(shù)量大大減少,并在合理的時(shí)間內(nèi)建立大規(guī)模和高分辨率的曲面。文獻(xiàn)[6]介紹了一種細(xì)分曲面的方法,其目標(biāo)是使細(xì)分面易于渲染為點(diǎn)、線或者三角形,優(yōu)點(diǎn)在于繪制出的曲面光滑且真實(shí),但是這種做法會(huì)增加GPU的功耗。文獻(xiàn)[7]是首先對(duì)曲面某個(gè)面計(jì)算出各個(gè)頂點(diǎn)的位置,然后相鄰面去共享這些頂點(diǎn)從而構(gòu)建三角形,這種方法的優(yōu)點(diǎn)在于頂點(diǎn)數(shù)量的減少會(huì)提高GPU繪制的效率并降低功耗,但是顯示出的圖像缺乏深度感和層次感。
文獻(xiàn)[8]通過對(duì)OpenGL ES復(fù)雜著色程序的分析,在圖形驅(qū)動(dòng)中引入了對(duì)著色程序編譯的方法,優(yōu)點(diǎn)在于圖形驅(qū)動(dòng)中加入對(duì)游戲引擎的優(yōu)化,但并沒有在圖形驅(qū)動(dòng)中針對(duì)常規(guī)圖形應(yīng)用程序做出優(yōu)化。
以上方法雖然在用戶態(tài)驅(qū)動(dòng)設(shè)計(jì)中具有較好的穩(wěn)定性,但沒有對(duì)軟件系統(tǒng)進(jìn)行分層設(shè)計(jì),并且未對(duì)曲面圖形的繪制進(jìn)行處理和圖形應(yīng)用程序做出優(yōu)化,不方便圖形應(yīng)用程序的調(diào)用。為了降低軟件耦合度和保證繪圖速率,本文提出了一種用于移動(dòng)終端的GPU用戶態(tài)驅(qū)動(dòng)的設(shè)計(jì)方法,采用了一種包含EGL層、OpenGL ES基礎(chǔ)庫(kù)層和OpenGL ES擴(kuò)展庫(kù)層的三層結(jié)構(gòu)來實(shí)現(xiàn)用戶態(tài)驅(qū)動(dòng)的設(shè)計(jì)。
嵌入式圖形庫(kù)(embedded graphics library,EGL)[8]定義了控制顯示(Display),上下文(Context)以及表面(Surface)的統(tǒng)一平臺(tái)接口,如圖1所示。
圖1 EGL平臺(tái)調(diào)用總體設(shè)計(jì)圖
在EGL標(biāo)準(zhǔn)中提供了一組標(biāo)準(zhǔn)的圖形應(yīng)用程序編程接口(application programming interface,API)函數(shù)名,在對(duì)EGL庫(kù)中的API函數(shù)調(diào)用時(shí)不但需要對(duì)API函數(shù)及其參數(shù)進(jìn)行預(yù)處理,而且由于API函數(shù)之間存在關(guān)聯(lián)性需要將EGL庫(kù)劃分為6層并分別實(shí)現(xiàn)每一層的功能。具體劃分的6個(gè)層次分別為獲取Display顯示器、對(duì)EGL庫(kù)進(jìn)行初始化、指定可用的渲染Surface的配置屬性、創(chuàng)建一個(gè)能顯示的渲染目標(biāo)窗口、創(chuàng)建一個(gè)渲染上下文Context,最后進(jìn)行綁定操作。
當(dāng)上述六層全部實(shí)現(xiàn)后,也就完成了EGL庫(kù)的代碼設(shè)計(jì),最后生成libegl.so動(dòng)態(tài)鏈接庫(kù)文件。此時(shí)OpenGL ES就可以在渲染Surface上進(jìn)行圖形的繪制,并使用標(biāo)準(zhǔn)API同步圖形在屏幕上。
完成了EGL庫(kù)的設(shè)計(jì),此時(shí)OpenGL ES就可以在渲染Surface上進(jìn)行圖形的繪制,之后進(jìn)行OpenGL ES基礎(chǔ)庫(kù)的設(shè)計(jì)。OpenGL ES的基礎(chǔ)庫(kù)用來完成繪圖的最核心功能,包括圖形應(yīng)用程序與用戶態(tài)驅(qū)動(dòng)接口、EGL驅(qū)動(dòng)層、API預(yù)處理層、API容錯(cuò)處理層、著色指令解析層等。
將設(shè)計(jì)EGL庫(kù)各層的代碼打包為libegl.cpp文件,另外,還需要一個(gè)聲明EGL中API函數(shù)名的頭文件egl.h,即編譯生成第三方的EGL動(dòng)態(tài)鏈接庫(kù)文件libegl.so。
庫(kù)文件成功生成后,可以通過Android系統(tǒng)內(nèi)置的API函數(shù)loadLibrary()加載動(dòng)態(tài)庫(kù),即完成了圖形應(yīng)用程序與用戶態(tài)驅(qū)動(dòng)接口的設(shè)計(jì)。
當(dāng)開發(fā)某個(gè)具體的OpenGL ES圖形應(yīng)用時(shí),都會(huì)在源文件中的開頭部分對(duì)EGL的屬性變量初始化和具體化,初始化是由動(dòng)態(tài)鏈接庫(kù)文件完成的,而具體化是通過應(yīng)用程序員對(duì)每個(gè)屬性變量賦予特定的值完成的。
圖形應(yīng)用程序加載了動(dòng)態(tài)鏈接庫(kù)后,需要對(duì)框架層傳下來的抽象函數(shù)名和參數(shù)進(jìn)行識(shí)別和解析,即需要對(duì)這些API函數(shù)及其參數(shù)進(jìn)行合理的管理,及初始化操作。
應(yīng)用程序的著色器代碼運(yùn)行前,除EGL對(duì)渲染環(huán)境初始化,還要進(jìn)行OpenGL ES 的上下文初始化。定義一個(gè)上下文初始化的函數(shù)InitContext()。初始化后等待應(yīng)用程序刷新上下文。
首先定義與OpenGL ES上下文和著色器相關(guān)的結(jié)構(gòu)體,其次對(duì)從應(yīng)用程序獲取到的上下文內(nèi)容和著色器狀態(tài)進(jìn)行判斷識(shí)別,然后判斷參數(shù)的返回值是否正確。最后還需要某些固定功能的API函數(shù)設(shè)置流水線的狀態(tài),至此各種狀態(tài)值已經(jīng)設(shè)置并傳送完畢,下一步要將合理的狀態(tài)值送入頂點(diǎn)著色器和片元著色器進(jìn)行運(yùn)算并解析為可被底層GPU硬件識(shí)別的二進(jìn)制代碼。
將圖形應(yīng)用程序編譯[9]為二進(jìn)制指令,需將著色語言涉及到的主要數(shù)據(jù)類型,內(nèi)置變量和函數(shù)在驅(qū)動(dòng)軟件層面上進(jìn)行相應(yīng)的轉(zhuǎn)換。
在將著色語言涉及到的主要數(shù)據(jù)類型,內(nèi)置變量和函數(shù)進(jìn)行了定義和聲明后,下一步就需要對(duì)應(yīng)用程序中使用到的圖形API函數(shù)名及參數(shù)進(jìn)行二進(jìn)制定義。為了生成可被GPU硬件識(shí)別的二進(jìn)制機(jī)器碼[10],以二進(jìn)制的形式來表示一條指令總共包括160位。劃分為6個(gè)部分,包括4個(gè)參數(shù)碼和2個(gè)操作碼,其中操作碼包括函數(shù)碼和控制碼。
在將函數(shù)及其參數(shù)轉(zhuǎn)換到二進(jìn)制指令的過程中,是用兩種方法進(jìn)行轉(zhuǎn)換的。第一種方法是靜態(tài)轉(zhuǎn)換,主要針對(duì)函數(shù)碼與預(yù)留位的轉(zhuǎn)換。最后通過自定義函數(shù)static void function_code(char des[],char *src)將函數(shù)碼與預(yù)留位總共22位存入字符數(shù)組中。
另一種方法是動(dòng)態(tài)轉(zhuǎn)換,這主要是針對(duì)1個(gè)10位的控制碼和4個(gè)32位的參數(shù)碼。在API容錯(cuò)處理層已經(jīng)對(duì)函數(shù)的參數(shù)做了預(yù)處理,此時(shí)對(duì)應(yīng)用程序傳入合理的參數(shù)值進(jìn)行二進(jìn)制轉(zhuǎn)換。這就需要自定義函數(shù)實(shí)現(xiàn)其轉(zhuǎn)換的過程。
根據(jù)上述描述,控制碼以及函數(shù)參數(shù)已經(jīng)全被轉(zhuǎn)換為二進(jìn)制,下一步就是自定義一個(gè)函數(shù)void para_class(char des[],char src[],int pos)將轉(zhuǎn)換后的二進(jìn)制分組放置在對(duì)應(yīng)的各位中,至此一條GPU的指令拼接成功,拼接的過程如圖2所示。
圖2 GPU指令的拼接過程
由上述著色指令解析層解析后的二進(jìn)制GPU指令會(huì)通過字符數(shù)組傳到用戶態(tài)驅(qū)動(dòng)的緩沖區(qū)Ubuffer中,從而實(shí)現(xiàn)與用戶空間和內(nèi)核空間接口的數(shù)據(jù)對(duì)接。
為了提高移動(dòng)GPU的工作效率,在OpenGL ES基礎(chǔ)庫(kù)的支持下設(shè)計(jì)其擴(kuò)展庫(kù),主要是對(duì)某些曲面圖形的繪制和矩陣[11]操作進(jìn)行了封裝和接口實(shí)現(xiàn)。
當(dāng)矩陣沿著其中一個(gè)方向變換后,圖形變換到一個(gè)新的位置,此時(shí)想要圖形回到原來的位置,需要在新矩陣的基礎(chǔ)上左乘一個(gè)逆矩陣,則變換矩陣與其逆矩陣的乘積是單位矩陣,此時(shí)恢復(fù)到變換前的圖形狀態(tài),而模型視圖涉及到的三個(gè)變換矩陣的逆矩陣只需修改其中的某些元素即可。平移、縮放和旋轉(zhuǎn)的逆矩陣如下
(1)
(2)
(3)
而對(duì)于上述正余弦的值,可使用CORDIC算法[12]進(jìn)行計(jì)算。
基于本文的硬件結(jié)構(gòu)、CORDIC算法的特點(diǎn),則在計(jì)算過程中使用10次迭代,當(dāng)計(jì)算出正弦值和余弦值后,存入上述矩陣后即進(jìn)行旋轉(zhuǎn)矩陣的變換。
針對(duì)OpenGL ES常用曲面圖形[13]的繪制,一般方法是對(duì)將很多三角形按照某種規(guī)則去構(gòu)建各種曲面,而每當(dāng)開發(fā)一個(gè)空間曲面的應(yīng)用程序時(shí),都要按照函數(shù)Draw Elements()中的繪圖模式去重新拼接一次曲面,這樣做會(huì)占用GPU的資源。而在用戶態(tài)驅(qū)動(dòng)中可將常用空間曲面圖形的繪制封裝為多個(gè)接口函數(shù),繪制此類圖形時(shí)只需要調(diào)用這些接口函數(shù)并傳入一定的數(shù)值即可。
3.2.1 空間圓錐體
空間圓錐體的初始狀態(tài)設(shè)置圓錐底面的中心位于XOZ坐標(biāo)平面原點(diǎn),并設(shè)圓錐地面半徑為R,圓錐的高度為h??梢詫A錐體看作是圓柱體的特殊情況,此時(shí)空間圓錐體就由圓錐面和圓面構(gòu)成。
根據(jù)上述推導(dǎo)原理,構(gòu)成圓錐體側(cè)面小三角形的通用頂點(diǎn)坐標(biāo)為(Xk-1,0,Zk-1),(Xk,0,Zk),(0,h,0)。根據(jù)通用坐標(biāo)中涉及的數(shù)值,自定義一個(gè)繪制圓錐體的接口函數(shù)int GlesCylinder()。對(duì)其他常用曲面圖形的繪制可根據(jù)曲面圖形的特點(diǎn)尋找一種合理的劃分方法,并按照上述類似的原理推得,然后再去定義調(diào)用接口。
3.2.2 曲面圖形的頂點(diǎn)數(shù)處理
為了提高繪圖速率,在GPU用戶態(tài)驅(qū)動(dòng)中需要對(duì)圖形應(yīng)用程序中傳下來的頂點(diǎn)數(shù)量加以限制,如果超出數(shù)量則自動(dòng)指定三角形的數(shù)量,自動(dòng)指定的方法是首先根據(jù)圖形應(yīng)用程序設(shè)置的角度跨距變量Span計(jì)算出圓面三角形的總個(gè)數(shù)n,此時(shí)圓錐體頂點(diǎn)的總個(gè)數(shù)為n+3,如果n+3<600,則繼續(xù)執(zhí)行繪圖過程,反之則將角度跨距翻倍再次賦值給變量Span,繼續(xù)執(zhí)行判斷的過程,直到頂點(diǎn)的總個(gè)數(shù)小于600,此時(shí)按照最后計(jì)算出的角度跨距繪制圓錐體。
測(cè)試的環(huán)境以Xilinx Zynq—7000系列的ZC706 FPGA開發(fā)板為硬件開發(fā)平臺(tái),并在開發(fā)板上外接一個(gè)觸摸顯示屏。然后在硬件平臺(tái)上移植Android 5.1版本的操作系統(tǒng)為驅(qū)動(dòng)運(yùn)行的環(huán)境,最后通過在FPGA開發(fā)板中嵌入GPU原型系統(tǒng)并加入設(shè)備樹文件以取得與圖形處理器的聯(lián)系。
圖3中為圓錐體測(cè)試程序的繪制示意圖。測(cè)試結(jié)束后通過與PC平臺(tái)運(yùn)行相同的應(yīng)用程序得出的圖像對(duì)比后發(fā)現(xiàn)在本文描述的硬件平臺(tái)上設(shè)計(jì)的圖形驅(qū)動(dòng)程序能夠正確顯示預(yù)期的圖形。
圖3 測(cè)試程序繪制示意
對(duì)擁有不同頂點(diǎn)圓錐體的應(yīng)用程序進(jìn)行測(cè)試,具體數(shù)據(jù)如表1所示。
表1 空間圓錐體的測(cè)試數(shù)據(jù)
經(jīng)過限制頂點(diǎn)數(shù)方法處理優(yōu)化后,具體數(shù)據(jù)如表2所示。
表2 空間圓椎體優(yōu)化后的測(cè)試數(shù)據(jù)
運(yùn)行傳統(tǒng)的EGL應(yīng)用程序,具體數(shù)據(jù)如表3所示。
表3 繪圖平均時(shí)間對(duì)比分析
由表1,表2和表3測(cè)試數(shù)據(jù)對(duì)比,經(jīng)過限制頂點(diǎn)數(shù)優(yōu)化處理后,計(jì)算可得繪制圖形的速率比之前提高了24.24 %,此時(shí)圖形頂點(diǎn)個(gè)數(shù)控制在600左右。繪圖效果上,逐一與未限制頂點(diǎn)數(shù)的繪制圖像做效果對(duì)比,發(fā)現(xiàn)繪圖效果上肉眼無差異。
本文提出了一種用于移動(dòng)終端的GPU用戶態(tài)驅(qū)動(dòng)設(shè)計(jì)方法,通過對(duì)用戶態(tài)驅(qū)動(dòng)的分層研究,降低了驅(qū)動(dòng)軟件系統(tǒng)的耦合度和設(shè)計(jì)復(fù)雜度,以便于程序更好的調(diào)用。實(shí)驗(yàn)表明:通過對(duì)空間曲面頂點(diǎn)數(shù)量加以限制的方法優(yōu)化后,提高了圖形應(yīng)用程序的繪制速率,同時(shí)為將來實(shí)現(xiàn)OpenGL ES更多新的功能,兼容其他圖形API的研究及應(yīng)用打下基礎(chǔ)。