[原] GLES在iOS和Android上的不同


本來GLES提供了與native platform的接口 EGL,

然而iOS沒有使用EGL接口, 而是自己搞了一套,叫做EAGL的類似東西, 雖然說大同小異,但是在做跨平台的時候還是很惡心.

elgMakeCurrent: 默認的EGL是需要surface和display的, iOS的EAGL實現, 對於用戶(程序猿)來說, 沒有surface和display這些東西.

 

iOS 下需要使用FBO + RBO, 然后直接通過native API: EAGLContext - presentRenderbuffer,將RBO的內容填充到目標窗口表面.

然而Android的GLES2.0下面, 如果使用了FBO + RBO, 那么只能用於離線渲染,離線讀寫, 沒有任何copy和swap/present接口可以將RBO swap到窗口表面.

http://www.khronos.org/opengles/sdk/docs/man/xhtml/glBindFramebuffer.xml:

Application created framebuffer objects (i.e. those with a non-zero name) differ from the default window-system-provided framebuffer in a few important ways. First, they have modifiable attachment points for a color buffer, a depth buffer, and a stencil buffer to which framebuffer attachable images may be attached and detached. Second, the size and format of the attached images are controlled entirely within the GL and are not affected by window-system events, such as pixel format selection, window resizes, and display mode changes. Third, when rendering to or reading from an application created framebuffer object, the pixel ownership test always succeeds (i.e. they own all their pixels). Fourth, there are no visible color buffer bitplanes, only a single "off-screen" color image attachment, so there is no sense of front and back buffers or swapping. Finally, there is no multisample buffer, so the value of the implementation-dependent state variables GL_SAMPLES and GL_SAMPLE_BUFFERS are both zero for application created framebuffer objects.

 

 

雖然GLES3.0 有glBlitFramebuffer(),可以直接將自己的FBO復制到context的默認FBO( fboID = 0)上,然后swap, 但是顯然目前的目標平台是GLES2.0. 所以現在對於安卓有兩個方案: 1.使用默認FBO,放棄自定義的FBO和RBO. 2.使用自定義FBO+RenderTexture, 最后使用full screen quad把這張texture繪制到默認FBO上,然后swap.

很顯然從效率上說,第一種好,因為第二種需要額外的內存開銷,並且有多余的draw call,還多了一次紋理采樣.但是最后可能要綜合兼容性/適配性等其他因素, 選擇最終方案.

 

On iOS, we use FBO and RBO to draw to the RBO, and use presentBuffer to swap the content of RBO to window surface(or layer).

but on Android GLES 2.0, still you can attach a RBO to your own FBO and render onto it, but there's no way that you can copy/swap your RBO to window surface.

although in GLES3.0 the glBlitFramebuffer is finally available, which can copy your own FBO to the default FBO( fboID = 0), and then yiou can swap it, that turns out not a sound solution since we're now targeting the GLES 2.0 platform.
Now we have 2 solutions: 1.to use the default FBO (0), and no other custom FBOs. 2. use own FBO, and RenderTexture instead of RBO, and finally draw the texture onto the default FBO via a full screen quad, and then swap it.
I believe the former one is better in performance because the latter one takes more memory and, uses extra draw call & texture sampling. But the compatiblity & adaptability also needs to be considered.

 

 



補充:

GL Extensions:  iOS每一代的硬件是固定的, 所以extension基本是固定的. 但是Android上由於顯示芯片可以隨廠家來配, 所以比較繁瑣. 所以整體的開發上, iOS有點像console平台,比如XBOX, 而Android更像PC平台,需要考慮各種CPU/GPU/等等問題.

 

對於前面FBO的問題, 由於不使用FBO的話,靈活性就大大降低.而且等於關閉了RenderToTexture的特性, 當然可以只有在RenderToTexture的時候才綁定FBO, 正常渲染使用默認FBO, 但是這樣看起來不夠elegant(也許還好吧), 而且對於現有的架構有一定的改動. 所以目前暫用的方案是使用FBO, 用RenderTexture作為backbuffer, 最后再繪制到默認FBO上,已經測試通過.
如果以后有時間做后續優化的話,可能這么做: 用默認FBO, RenderToTexture的時候再綁定自己的FBO.

 


FBO不適合動態改變attachment, 如果有多個rendertarget, 那么就用多個FBO, 掛接到render target上.

動態attach buffer的效率相對要低.

 

2014/12/20 更新一些細節:

在iOS上, 即使OpenGLES 2.0的設備和API, 也支持一些3.0才有的特性, 2.0上或許有擴展, 但是都是各個廠商各自為戰, 非常不方便.

1.Multisample (https://www.khronos.org/registry/gles/extensions/APPLE/APPLE_framebuffer_multisample.txt)

雖然它是Extension, 但是在iOS上是直接可以用的. 即RenderbufferStorageMultisampleAPPLE.

在android GLES 2.0上的Imagenation的Extension:

https://www.khronos.org/registry/gles/extensions/IMG/IMG_multisampled_render_to_texture.txt

因為是Imagenation(IMG)的擴展, 估計只能在PowerVR的GPU上才有.

 

2.MAXMIPLEVEL

https://www.khronos.org/registry/gles/extensions/APPLE/APPLE_texture_max_level.txt

這個GLES 2.0上只有擴展, 3.0才有. 但是iOS上也是固有的,即TEXTURE_MAX_LEVEL_APPLE, 其他平台就不一定了.


另外工作中發現某設備, 搭載的是GLES 3.0 capable的GPU, 但是是2.0的driver, 發現mipmap chain如果不完整(0-n)的話, 貼圖采樣會變成黑色, 這個是2.0的通病, 但是iOS上可以正常顯示, 應該是2.0的spec不夠清晰.

通過hack: 強制使用OpengGL ES 3.0的GL_TEXTURE_MAX_LEVEL / GL_TEXTURE_BASE_LEVEL, 竟然也能成功, 貼圖就不黑了...
還有就是在這種設備上強制使用ETC2 (2.0的header, ETC2用的是3.0header里面ETC貼圖對應的數字), 竟然也是可以的.

 

3.iOS上可以說有固定的貼圖格式: PVRTC. 因為他用的全部都是PVR(PowerVR)的GPU. 而android的GLES 2.0上各種壓縮貼圖格式ATITC, ASTC, ETC1, PVRTC, S3TC...

雖然ETC1支持的最廣泛, 但是ETC1壓縮不支持alpha通道....需要單獨加一張alpha貼圖, 或者其他處理方式:
http://malideveloper.arm.com/cn/develop-for-mali/sample-code/etcv1-texture-compression-and-alpha-channels/

上面的前兩個問題雖然惡心, 但是花時間總能找到渲染錯誤的原因, 而且有相對簡單的解決方案. 但這個壓縮紋理的問題才是是最頭疼的問題.

所以目前我們的產品(大型3D游戲 :P)暫時只支持OpenGL ES 3.0的設備, 因為3.0對於壓縮貼圖格式,有了統一的標准: ETC2. 它支持alpha通道. 還有單通道和雙通道壓縮格式(specular/normal map等).

 

另外一種方案是使用in-game-download, 根據設備的特性下載對應的資源包, 需要生成n種資源包.
我們沒有這么做是因為除了貼圖的問題以外, OpenGLES 2.0上shader的bug也非常多, 這個不知道是compiler的bug, 還是渲染的問題:

同樣的shader在iOS上沒有問題, 但在android設備上, 總會遇到問題, 比如屏幕上有黑塊, 或者某個shader渲染不對, 稍微改成其他等價的方式就好了, 比如之前貼過的bug:

1 //GLES2.0 fragment shader
2  
3 float   factor   = ( dir >= 0 ) ? 1 : 0;   //doesn't work! artifacts!
4 float   factor   = step(0, dir);           //f*******k! it works! the hell why?

改用3.0以后問題全好了. 所以說OpenGL ES 2.0 不適合做大型3D游戲. 至少是android上的不適合, 至少是目前不適合.

 

其他類似的問題應該還有很多, 比如iOS上固有glDiscardFramebufferEXT, 但是android仍然是Extension(https://www.khronos.org/registry/gles/extensions/EXT/EXT_discard_framebuffer.txt)

其他的暫時想不起來了.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM