OpenGLES編程思想

     最近在看gles的reference,想多瞭解一下gles的底層,gles是opengl在khronos在嵌入式設備上的圖形硬件的軟件訪問接口,很多東西和opengl似曾相似,但是和opengl又有很大的不同,最新的標準是gles3.2,標準文檔非常長,如果不是寫引擎沒必要對每個接口爛熟於心,但是爲了能夠了解他,我對他的編程思想做個總結,最重要的是理解gles的設計思路,然後在使用的時候也必將容易找到相關接口。所以本文基本不會列出gles的每個接口,不會記錄講解每個接口,而是希望能夠通過總結gles的設計思想,讓我們在開發過程中知道我們應該期待從gles中獲得哪個接口。


1.gles是什麼

  對於程序員開發者:非常簡單,他就是一組控制gpu工作指令,所有的指令包括三種:1.描述shader程序,2.給shader設置數據,3.shader之外的繪製狀態控制

  對於gl的實現者(那些具體爲硬件實現gles驅動的廠商要做的):也很簡單,控制對gpu的操作(當然也可能有無硬件的軟渲染):1.在gpu的顯存上存儲數據和framebuffer,2.在gpu的處理器上運行可執行的gpu程序。


2.gles 和egl

  通常在嵌入式設備上,gles需要和egl協同工作,gles負責圖像的繪製,但是對於創建和管理圖形context,窗口,同操作系統和其他硬件協作這些需要依靠egl(Embedded-System Graphics Library"),這些不是gles的範圍,egl也是一個跨平臺的統一的標準,協調多種平臺和硬件,具體來說你在手機上創建一個窗口供opengl渲染就需要egl。


3.Data Flow 數據流

 這是一個簡化版的es3.2的數據流,bufferdata經歷頂點,細分,及幾何shader 後,進入光柵化,片段shader,最後到frambuffer,但是這裏比2.0多了一條通路,在光柵化前,頂點數據可以feedbak回到一個buffer,這被稱爲transform feedback,即可以把頂點處理後的數據存儲下來,這個特性有很多應用,例如untiy依靠它做gpu上的蒙皮,第一遍先在vs裏做蒙皮計算,出來的結果回到feedback buffer,中止後面流程,然後再把feedback重新送到vs裏,執行我們客戶寫的vs shader。


4.全編程管線

gles沒有固定的渲染管線,也就是說gles的每個vs和fs都需要指定shader代碼,這和opengl可以用固定管線不同

5.client 和server

在gles裏有個client和server 的概念,client可以簡單認爲是cpu這一端,server可以簡單認爲是gpu那一端,client負責向server提交指令,server負責執行具體工作。在一個context下存在一個client和一個server。可以同時創建多個context,這樣會存在多個client和server,這多個server可以同時存在一塊顯卡,可以存在不同的物理顯卡上,這就是集羣渲染的概念,多個context之間也可以協作,可以通過定義waitobject協調多個context之間的異步等待,多個context之間也可以共享一些資源,(可以詳細參考async wait和shared Objects部分)

6.狀態機和對象

狀態機和對象我認爲是gles的核心編程思想了。

首先可以認爲整個gl的context就是一個大的多種狀態並存的狀態機,或者一個狀態機向量,我們使用各種gles的命令都是在改變這個狀態機的各個狀態。

gles(尤其從3.0後)開始大量使用object(對象)的概念,任何與資源操縱有關的概念都會使用object來封裝,雖然gl不是面向對象的編程,但是gles裏僞實現了一種類似的面向對象的概念,object概念是opengles編程思想裏最重要的概念,對於多數的初學者看到gles代碼裏各種莫名其妙的object,vbo,fbo,rbo一般會頭暈,這還是沒有先參透它的設計原理。

這種概念的做法一般是,我們認爲gpu硬件上裏面存在着很多個資源,但這些資源是有限的,比如Texture unit,framebuffer, array buffer, program都是資源,這裏面有些資源有多個,有的只有一個,雖然資源有限,但是對資源的使用和數據的設置卻可能有很多種,爲了能方便的操縱這些資源,對每個對某種資源的操縱都會封裝成一個對象的概念,這個對象通常用一個uint來表示,激活當前的這個對象,就等於將這個對象的設置應用於這個對象所關聯的資源。這樣我們就很方便的對一個資源在多個配置中切換。

舉個具體的例子.Texture Unit是有限的,這取決與硬件,比如當前只有4個紋理單元,但是我們可能遊戲中要用到100張圖,雖然我們一次渲染同時最多隻用到4個,但是我們要可能有100種不同的貼圖設置。我們可以預先把這100種都設好,創建100個texture object,然後對每個object bind到texture unit上,然後對這個textureunity的設置就等於去設置這個texture object,我們在渲染的時候,要用哪些texture ,就bind他們到這個object 就好了

把ResObject 綁定到一個res後,對這個res 的所有操作都等於應用在這個res object上,一個res同時只能綁定一個資源,解綁後,resobject仍然保留着配置好的信息,下次再次綁定後還保留着,這樣通過切換bind關係,gles就實現了對一個有限資源的多種配置設置的切換。

但是不是所有的Object的行爲都是一樣的,但是大部分object都要經歷GenObjectName, BindObject, Deleteobject這樣的過程,GenObject一般只是分配一個代表這個object的名字,用一個uint表示(因爲gles不是面向對象的,所以沒有class這種對象的概念,只能反覆在代碼中用一個unit來表示,這是對習慣了面向對象的開發者最不習慣的地方),然後BindObject是將這個對象綁定到這個資源上,這時這個object和這個資源就基本是一個概念了,對這個資源的操作都會記在這個object的狀態裏,同樣此時該資源的狀態也就是這個object裏的狀態。可以動態切換bind關係,DeleteObject則是刪掉這個object,如果對象在使用,一般會被推遲刪除。但是有些資源類型的對象則沒有gen的過程,有的資源使用CreateObject直接生成並綁定資源,還有資源的數量可以認爲是非常多的,例如shader object。

對象模型是理解opengles編程思想的最重要的概念。

下面將列出gles裏面最重要的一些資源及其object。

對象名稱object對應資源類型和數量備註
查詢對象QueryObject

三種:都只有1個資源

1.occlusion query:查詢這次繪製是否有可能進入到framebuffer,用於遮擋查詢測試(gles3.2纔有)

2. transform feedback :查詢transform feedback(gles3纔有)

3.primitives generated:查詢繪製的原語數量

 
顯卡上的內存對象BufferObject

都只有一個資源

Array:vertex attributes(最常用)

AtomicCounter :Atomic counter

COPY_READ:buffer copy sorce

Copy_write:buffer copy destination

element:vertex array indices

pixel_pack:pixel read target

pixel_unpack:texture data sorce

texture: texture data buffer(for buffer type texture)

transformfeedback:transform feedback buffer

uniform:uniform blockstorage


 
shaderShaderObject

資源沒有數量限制

Compute

Frag

Vertex

Geometry

Tess

 
一個編譯鏈接好的shader程序ProgramObject只有一個資源 
一個可以在鏈接後動態改變任意編譯階段的shader程序ProgramePipeLineObject只有一個資源 
TextureTextureObject

每個紋理單元的每種紋理只有一個資源

Texture Unit 0- Texture Unit N

對於每個Texture  Unit,有如下幾種類型:

2D

3D

CUBE

2D_MULTISAMPLE (沒有mipmap,一個texel可以存儲多重採樣的多個值,不能直接設置紋理數據,一般綁定在framebuffer上,render到texture)

BufferTexture:(一個一維的buffer,需要連接一個buffer object,可以從shader直接讀取,它的存儲字節限制相對較大,最少也有65536,所以一般用於讓shader方位一般unitform buffer object存儲不下的大緩存,這種texture不能通過I一般的採樣,只能通過texelFetch()直接獲取索引處的值,即不能進行濾波等操作,gles3.2纔有)

 
SamplerSamplerObject

每個紋理單元有一個資源

用於保存多當前紋理單元的各種採樣濾波操作

 
    
    


關於ProgrameObject 和ProgramePipeLineObject:他們都可以用來設定當前的shader 程序,但是有區別,通常有兩種方式:

      1.使用ProgrameObject,對於這個ProgramObject,分別設置它的vs shader object 和fs shader object,然後link,並綁定到資源,現在則在gpu運行該程序,

       2.使用ProgramePipeLineObject,對於vs shader object給他生成一個相應的program object,link,對於fs shader object 也爲他生成一個相應的programe object,link,然後將這兩個只有一個階段的programeobject 設置給這個programepipelineobject,將其綁定到資源,現在在gpu運行該程序

     使用第二種方式有這樣的好處,因爲programe object是link後的程序,所以使用方法1只使用program object,當其生成後沒有辦法動態改變它的某個階段的shader 程序,只能動態改變一組vs fs shader,如需改變,需要重新設置shader object,重新link;而使用第二種方式,你可以預先把所有的shader 都設置給一個programobject ,link好,最後可以動態的將program object 設定到program pipeline object上,就可以動態沒有消耗的改變當前某一個階段的shader。

     簡單來說,使用ProgramPipelineobject,可以方便的動態的只改變某個階段的shader,可以混用vs 和fs shader的組合。


未完待續。。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章