• iOS 中OpenGL ES 优化 笔记 1


    1,避免同步和Flushing操作

    OpenGL ES的命令执行通常是在command buffer中积累一定量的命令后,再做批处理执行,这样效率会更高;但是一些OpenGL ES命令必须flush command buffer,也有需要同时flush和阻塞直到命令执行完毕,过度调用这类函数会严重影响性能。

    glFlush 发送命令buffer到图形硬件,一直阻塞直到提交到图形硬件,但是不用等到命令执行,提交完成即可。 
    glFinish,glReadPixels 不仅flush命令到图形硬件,而且阻塞直到所有提交的命令执行完成。 
    command buffer满了会自动执行flush。

    通常在OpenGL ES中两种情况下,调用glFlush 或者 glFinish: 
    1,App进入后台,此时在GPU运行命令会崩溃; 
    2,在不同的contexts中共享OpenGL ES objects(比如VBO或textures)需要调用glFlush来使得在不同的context中使用。

    2,避免过度调用Query OpenGL ES 查询状态的函数,glGet*()之类的。

    像glGetError(),需要检索任何状态变量之前执行以前的命令,这种同步机制迫使图形硬件与CPU同步运行,减少图形硬件并行执行的可能性。 
    通常我们会执行像glCheckFramebufferStatus,glGetProgramInfoLog,glValidateProgram等等,来查询相关状态是否合法,GPUImage就是这么做的。

    3,用OpenGL ES来管理资源,像顶点,纹理坐标,法线等数据

    很多数据都可以直接存储在OpenGL ES rendering context中,OpenGL ES实现可以将数据转换成最适合图形硬件的格式,一次来提升性能;比如说一些不常改变的数据,可以通过在GPU申请专用内存存储(像VBO),比如:

    1 glGenBuffers(1, &staticBuffer);
    2 glBindBuffer(GL_ARRAY_BUFFER, staticBuffer);
    3 glBufferData(GL_ARRAY_BUFFER, sizeof(staticVertexData), staticVertexData, GL_STATIC_DRAW); //GL_STATIC_DRAW 就是hint,还有常变动的数据使用GL_DYNAMIC_DRAW或GL_STREAM_DRAW,在OpenGL ES中后两者等价的

    4,使用双缓冲来避免资源冲突

    当CPU和GPU同时访问OpenGL ES对象时,比如其中一个CPU想改变一个正在被GPU使用的对象数据,会阻塞直到没有再被使用;一旦修改开始,另一个想要访问就必须直到修改完成。都是同步操作;

    比如说从CPU传输texture object到GPU,可以显式地创建两个相同大小的对象;下图展示了双缓冲方法。当GPU操作一个纹理时,CPU会修改另一个纹理。在初始启动之后,CPU或GPU都不会闲置。尽管显示了纹理,这个解决方案几乎适用于任何类型的OpenGL ES对象。

    双缓冲示例

    5,注意OpenGL ES的状态

    OpenGL ES实现维护一组复杂的状态数据,包括设置为glEnable或glDisable函数的开关、当前的shader program和uniform attributes、当前绑定的texture,以及当前绑定的顶点缓冲区和它们启用的顶点属性。硬件有一个当前状态,它被编译并缓存起来。切换状态很昂贵,所以最好设计你的应用程序来最小化状态切换。

    不要再次设置已经设置了的状态。一旦启用了特性,就不需要再次启用它。例如,如果多次调用相同参数的glUniform函数,OpenGL ES只会简单地执行指令更新状态。

    6,用OpenGL ES对象封装状态,通常指使用VBO和VAO

    很多数据从一开始初始化后,不再需要逐帧重新配置,使用VAO和VBO共同管理对象状态,减少很多的不必要指令执行。使用VBO减少CPU到GPU之间的数据拷贝次数,VAO通常用来管理多个VBO,常见用法:

     1 // 创建和绑定vao。
     2 glGenVertexArrays(1,&vao1);
     3 glBindVertexArray(vao1);
     4  
     5 // 在刚创建的vao1中配置各属性
     6 glBindBuffer(GL_ARRAY_BUFFER, vbo1);
     7  
     8 // 指定各个属性数据,格式,大小和起始地址
     9 glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE,
    10 sizeof(staticFmt), (void*)offsetof(staticFmt,position));
    11 glEnableVertexAttribArray(GLKVertexAttribPosition);
    12 glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_UNSIGNED_SHORT, GL_TRUE,
    13 sizeof(staticFmt), (void*)offsetof(staticFmt,texcoord));
    14 glEnableVertexAttribArray(GLKVertexAttribTexCoord0);
    15 glVertexAttribPointer(GLKVertexAttribNormal, 3, GL_FLOAT, GL_FALSE,
    16 sizeof(staticFmt), (void*)offsetof(staticFmt,normal));
    17 glEnableVertexAttribArray(GLKVertexAttribNormal);
    18  
    19 glBindBuffer(GL_ARRAY_BUFFER, vbo2);
    20 glVertexAttribPointer(GLKVertexAttribColor, 4, GL_UNSIGNED_BYTE, GL_TRUE,
    21 sizeof(dynamicFmt), (void*)offsetof(dynamicFmt,color));
    22 glEnableVertexAttribArray(GLKVertexAttribColor);
    23  
    24 // 返回执行各项配置之前的状态
    25 glBindBuffer(GL_ARRAY_BUFFER,0);
    26 glBindVertexArray(0);

    7,组织绘制指令最小化状态变更(这里没看太懂,先记录

    改变OpenGL ES状态没有立即的效果。相反,当您发出一个绘制指令时,OpenGL ES将执行绘制一组状态值所需的工作。您可以通过最小化状态更改来减少重新配置图形管道的CPU时间。例如,在应用程序中保留一个状态向量,并且只在绘制指令之间的状态更改时设置相应的OpenGL ES状态。 
    另一个有用的算法是状态排序,跟踪您需要做的绘图操作和每个需要的状态变化量,然后排序它们以连续使用相同的状态执行操作。

    OpenGL ES的iOS实现可以缓存一些需要在状态间进行有效切换的配置数据,但是每个独特的状态集的初始配置需要更长的时间。对于一致的性能,您可以“预热”每个状态设置,您计划在设置例程中使用:

    启用您计划使用的状态配置或着色器。 
    使用状态配置绘制少量顶点。 
    刷新OpenGL ES上下文,就不会显示在这个预热阶段的绘图。

    参考网站:

    苹果的OpenGL ES文档

  • 相关阅读:
    apache2.4+php7.3.2+mysql5.7
    redis 中的key值过期后,触发通知事件
    zookeeper之分布式锁以及分布式计数器(通过curator框架实现)
    SpringCloud学习笔记(4)——Zuul
    SpringCloud学习笔记(3)——Hystrix
    SpringCloud学习笔记(2)——Ribbon
    c++各种排序的简单实现
    c++动态规划dp算法题
    华为机试练习
    ubuntu遇到的 the system is runing low-graphics mode 问题
  • 原文地址:https://www.cnblogs.com/edisongz/p/8366404.html
Copyright © 2020-2023  润新知