设为首页 加入收藏

TOP

《OpenGL编程指南(原书第8版)》——计算着色器(一)
2015-08-31 21:23:20 来源: 作者: 【 】 浏览:54
Tags:OpenGL 编程 指南 计算 着色

《OpenGL编程指南(原书第8版)》针对OpenGL4.3版本的各种特性进行了全新阐述,并全面介绍了OpenGL和OpenGL着色语言,第一次将着色器的技术与函数功能为中心的经典技术介绍相结合,呈现最新的OpenGL编程技术。


由于图形处理器每秒能够进行数以亿计次的计算,它已成为一种性能十分惊人的器件。过去,这种处理器主要被设计用于承担实时图形渲染中海量的数学运算。然而,其潜在的计算能力也可用于处理与图形无关的任务,特别是当无法很好地与固定功能的图形管线结合的时候。为了使得这种应用成为可能,OpenG引入一种特殊的着色器:计算着色器。计算着色器可以认为是一个只有一级的管线,没有固定的输入和输出,所有默认的输入通过一组内置变量来传递。当需要额外的输入时,可以通过那些固定的输入输出来控制对纹理和缓冲的访问。所有可见的副作用是图像存储,原子操作,以及对原子计数器的访问。然而加上通用的显存读写操作,这些看上去似乎有限的功能使计算着色器获得一定程度的灵活性,同时摆脱图形相关的束缚,以及打开广阔的应用空间。


OpenGL中的计算着色器和其他着色器很相似。它通过glCreateShader() 函数创建,用glCompilerShader()进行编译,通过glAttachShader()对程序进行绑定,最后按通用的做法用glLinkProgram()对这些程序进行链接。计算着色器使用GLSL编写,原则上,所有其他图形着色器(比如顶点着色器,几何着色器或者片元着色器)能够使用的功能它都可以使用。当然,这不包括诸如几何着色器中的EmitVertex()或者EndPrimitive()等功能,以及其他类似的与图形管线特有的内建变量。另一方面,计算着色器也包含一些独有的内置变量和函数,这些变量和函数在OpenGL管线的其他地方无法访问。


?


?


正如图形着色器被置于管线的不同阶段用来操作与图形相关的单元一样,将计算着色器被有效地放入一个一级的计算管线中,然后处理与计算相关的单元。按照这种类比,顶点着色器作用于每个顶点,几何着色器作用于每个图元,而片元着色器则作用于每个片元。图形硬件主要通过并行来获得性能,这种并行则通过大量的顶点、图元和片元流过相应的管线阶段而得以实现。而在计算着色器中,这种并行性则显得更为直接,任务以组为单位进行执行,我们称为工作组(work group)。拥有邻居的工作组被称为本地工作组(local workgroup), 这些组可以组成更大的组,称为全局工作组(global workgroup),而其通常作为执行命令的一个单位。


计算着色器会被全局工作组中每一个本地工作组中的每一个单元调用一次,工作组的每一个单元称为工作项(work item),每一次调用称为一次执行。执行的单元之间可以通过变量和显存进行通信,且可执行同步操作保持一致性。图12-1 对这种工作方式进行了说明。在这个简化的例子中,全局工作组包含16个本地工作组, 而每个本地工作组又包含16个执行单元,排成4*4的网格。每个执行单元拥有一个2维向量表示的索引值。


尽管在图12-1中,全局和本地工作组都是2维的,而事实上它们是3维的,为了能够在逻辑上适应1维、2维的任务,只需要把额外的那2维或1维的大小设为0即可。计算着色器的每一个执行单元本质上是相互独立的,可以并行地在支持OpenGL的GPU硬件上执行。实际中,大部分OpenGL硬件都会把这些执行单元打包成较小的集合(lockstep),然后把这些小集合拼起来组成本地工作组。本地工作组的大小在计算着色器的源代码中用输入布局限定符来设置。全局工作组的大小则是本地工作组大小的整数倍。当计算着色器执行的时候,它可以内置变量来知道当前在本地工作组中的相对坐标、本地工作组的大小, 以及本地工作组在全局工作组中的相对坐标。基于这些还能进一步获得执行单元在全局工作组中的坐标等。着色器根据这些变量来决定应该负责计算任务中的哪些部分,同时也能知道一个工作组中的其他执行单元,以便于共享数据。


?


?


图12-1 计算工作量的图示


输入布局限定符在计算着色器中声明本地工作组的大小,分别使用local_size_x、local_size_y以及local_size_z,它们的默认值都是1。举例来说如果忽略local_size_z,就会创建N * M的2维组。比如在例子12.1中就声明了一个本地工作组大小为16 * 16的着色器。


例12.1简单的本地工作组声明



?


尽管例子12.1中的着色器什么事情也没做,它仍然是一个“完整”的着色器,可以正常的编译、链接并且在OpenGL硬件中执行。要创建一个计算着色器,只需调用glCreateShader ()函数,将类型设置为GL_COMPUTE_SHADER,并且调用glShaderSource()函数来设置着色器的源代码, 接着就能按正常编译了。然后把着色器附加到一个程序上,调用glLinkProgram()。这样就会产生计算着色器阶段需要的可执行程序。例12.2展示了从创建到链接一个计算程序(使用“计算程序”来表示使用计算着色器来编译的程序)的完整步骤。


例12.2 创建,编译和链接计算着色器



一旦像例12.2中那样创建并链接一个计算着色器后,就可以用glUseProgram()函数把它设置为当前要执行的程序,然后用glDispatchCompute()把工作组发送到计算管线上,其原型如下:


?


Void glDispatchCompute(GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z);


在3个维度上分发计算工作组。num_groups_x,num_groups_y和num_groups_z分别设置工作组在X,Y和Z维度上的数量。每个参数都必须大于0,小于或等于一个与设备相关的常量数组GL_MAX_COMPUTE_WORK_GROUP_SIZE的对应元素。


在调用glDispatchCompute()时,OpenGL会创建一个包含大小为num_groups_x * num_groups_y * num_gourps_z的本地工作组的3维数组。注意三个维度中一个或两个维度可以为1或者glDispatchCompute()的参数的任何值。所以计算着色器中执行单元的总数是这个3维数组的大小乘以着色器代码中定义的本地工作组的大小。可想而知,这种方法可以为图像处理器创建非常大规模的工作负载,而通过计算着色器则可以相对容易地获得并行性。


正如glDrawArraysIndirect()和glDrawArrays()的关系一样,除了使用glDispatchCompute()之外通过glDispatchComputeIndirect()可以使用存储在缓冲区对象上的参数来发送计算任务。缓冲区对象被绑定在GL_DISPATCH_INDIRECT_BUFFER上,并且缓冲区中存储的参数包含三个打包在一起的无符号整数。这三个无符号整数的作用和glDispatchCompute()中的参数是等价的。参考glDispatchComputeIndirect的原型如下:


void glDispatchComputeIndirect(GLintptr indirect);


在三个维度上分发计算工作组,同时使

首页 上一页 1 2 3 下一页 尾页 1/3/3
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇快速排序的简单实现 下一篇C语言宏定义#define用法

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: