歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

OpenGL錯誤和性能Tips

1. 接口使用不當

1)GLES中的glAAx 形式的接口使用,glTranslatex,glRotatex,glScalex等函數。

float posx = 100.0f, posy = 100.0f, posz = 100.0f;
glTranslatef(posx, posy, posz);
//等價於
int fpX = (int)(posx * 65536), fpY = (int)(posy * 65536), fpZ = (int)(posz * 65536);
glTranslatex(fpX, fpY, fpZ);

2. GLEnum使用不當

1)離線渲染框架中,通過幀緩存測試繪制任意凹凸多邊形,framebuffer中設置幀緩沖區很隱晦:
錯誤做法:為深度緩沖區創建一個GL_DEPTH_COMPONENT24格式的RenderBuffer;為模板緩沖區創建一個GL_STENCIL_INDEX格式的RenderBuffer。
正確做法:模板、深度 共用一個RenderBuffer,而且格式是GL_DEPTH24_STENCIL8。具體參見:http://www.linuxidc.com/Linux/2014-03/98792.htm
2)獲取模型視圖矩陣用GL_MODELVIEW_MATRIX而不是GL_MODELVIEW,獲取投影視圖矩陣用GL_PROJECTION_MATRIX而不是GL_PROJECTION; 塗點用GL_POINTS而不是 GL_POINT;畫線用GL_LINES而不是GL_LINE。GL_POINT、GL_LINE、GL_FILL 用來指定多邊形填充模式。 BTW:Android中比較怪,java層gles1.0沒法直接查詢變量的值,native層gl.h可以。java層GL10中沒有查詢數據的GLenum和接口函數,GL11中有;android-ndk-r7c\platforms\android-8\arch-x86\usr\include\GLES\gl.h 比較全面。  

3. 隱晦的邏輯問題

1)繪制過程開啟了GL_CULL_FACE,建模過程中三角形頂點順序為CW導致繪制無結果。默認是GL_CCW 調試方法:修改多邊形填充模式GL_LINE確認圖形輪廓線是否繪制成功;或者禁掉背面剔除:glDisable(GL_CULL_FACE);
解決方法:glFrontFace( GL_CW / GL_CCW )修改默認配置。
2)關閉深度測試,導致混合失效?? http://stackoverflow.com/questions/3773353/opengl-blend-problem   3)深度緩沖區溢出 Android GLSurfaceView默認采用16bit的深度緩沖區,精度在0-65536,參見:http://www.linuxidc.com/Linux/2014-03/98791.htm

    public void setRenderer(Renderer renderer) {
        checkRenderThreadState();
        if (mEGLConfigChooser == null) {  // here
            mEGLConfigChooser = new SimpleEGLConfigChooser(true);
        }
        if (mEGLContextFactory == null) {
            mEGLContextFactory = new DefaultContextFactory();
        }
        if (mEGLWindowSurfaceFactory == null) {
            mEGLWindowSurfaceFactory = new DefaultWindowSurfaceFactory();
        }
        mGLThread = new GLThread(renderer);
        mGLThread.start();
    }
 
 /**
    * This class will choose a RGB_565 surface with
    * or without a depth buffer.
    *
    */
    private class SimpleEGLConfigChooser extends ComponentSizeChooser {
        public SimpleEGLConfigChooser(boolean withDepthBuffer) {
            super(5, 6, 5, 0, withDepthBuffer ? 16 : 0, 0);
        }
    }

4)android中不同資源目錄導致紋理資源被隱式修改:2冪次的紋理放在drawable目錄下,在高分屏android機器上從drawable中加載紋理資源後進行1.2倍隱式擴充,導致紋理不再是2的冪次。http://www.linuxidc.com/Linux/2014-03/98793.htm

4. gl狀態

1)上次適配AntTweakBar GLES時,它庫中EndDraw函數結束繪制時將當前矩陣堆棧設為TEXTURE_MATRIX,下次進入渲染函數時在BeginDraw中各種嘗試修改模型視圖失效實際修改的都是紋理矩陣,glGetError返回也為0。

2)批量繪制地圖上路網數據, 阈值達到一定程度,隨機性crash!
調試:glVertexPointer上傳頂點,禁掉VertexArray,ColorArray,TextureArray後調用glDrawElements不會crash,說明問題出在上面這三個狀態設置,進一步檢查發現上傳頂點中只有位置和顏色,但是GL_TEXTURE_ARRAY沒有禁掉。所以才會出現隨機性crash。  

5. 性能Tips

1)紋理打包,避免使用小紋理;可以一定程度實現批處理。
TextureAtlas思想,一系列Icon靜態打包到一個資源中,通過紋理坐標索引不同的資源。紋理烘焙思想。
2)避免紋理動態申請、釋放,地圖渲染中涉及大量的瓦片,而且他們的格式和尺寸都一致。舊做法:glTexImage2D創建紋理,淘汰瓦片時glDeleteTextures刪除瓦片紋理。改進後的做法:淘汰瓦片時glTexSubImage2D用新的瓦片內容替換就瓦片顯存內容。 3)頂點數據批量上傳,batch batch batch,盡可能減少opengl 函數調用次數,draw calls 需求:100個item縮放動畫

// 所有item同步做動畫
glScale(scalex, scaley, scalez);
batch_draw_100_itmes();

// 不同步動畫

// V1 通過opengl做動畫
for (int cursor = 0; cursor < 100; cursor++)
{
 glScale3fv(items[cursor].scaleVar);
 items[cursor].single_draw_item();
}
 
// V2 CPU對頂點進行計算,然後批量繪制
for (int cursor = 0; cursor < 100; cursor++)
{
 cpu_scale_vertices(items[cursor].scaleVar); 
}
batch_draw_100_items();

當每個item動畫個異步執行時,V1的實現item縮放過度依賴於opengl,而且沒法實現批處理! V2犧牲點CPU計算性能換來GPU的批量繪制。

改進:cpu_scale_vertices操作可以單獨在update線程中執行, batch_draw_100_items在GL繪制線程中。 4)一幀繪制中,FBO不能切換過頻繁。做路況渲染中,為每個256*256的瓦片建立一個FBO,FBO直接與紋理關聯;每幀可能繪制3-5個瓦片,即進行3-5次FBO切換。實測發現FBO切換開銷遠大於數據上傳OpenGL的開銷。 5)一些非常耗性能操作:
通過glReadPixels獲取幀緩存區內容,導致GPU中斷,效率非常低。網上有一種PBO異步方式獲取幀緩存的做法效率不錯,適合PC。
通過glCopyTexImage2D將幀緩區內容拷內到紋理,會導致渲染中斷,如下圖十分耗時。替代做法:將紋理與FBO綁定,直接渲染到紋理,RTT。
大紋理切換開銷也十分大,手機端一般別超過1024*1024,不同手機支持紋理最大尺寸可以通過接口查詢。
Copyright © Linux教程網 All Rights Reserved