OpenGLES编程思想

来源:互联网 发布:手机淘宝2015旧版本5.4 编辑:程序博客网 时间:2024/06/05 16:04

     最近在看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的组合。


未完待续。。



0 0
原创粉丝点击