OpenGL超级宝典第七版学习笔记-着色器存储块(shader storage block)

20170321-shader storage block
1、 shader storage block与uniform block最大的区别是在shader中可以对前者进行写入操作,甚至是对其成员的内存进行原子操作,而后者在shader中是只读的。shader storage block拥有更高的大小上限
2、 shader storage block支持std140的布局方式,同时也支持std430的方式。std430的布局方式能使整型和浮点型的数组、包含他们的结构体的布局更加紧凑,这一点正是std140最缺乏的,这样能使像C++之类的编程语言的编译器更加有效率的使用内存。
3、 声明一个shader storage block的例子:
layout(binding=0,std430)myShaderStorageBlock
{
vec4aa;
vec3 bb;
intarr[];
MyStructurestructure;
};

20170321-shader storage block-原子内存操作(atomic memory oprations)(2)
1、 原子操作是指为了保证结果的正确性,如果在内存读取后可能紧跟着有内存写入的操作,那么在这一系列操作期间是绝对不能被打断的。(还有一种原子操作的概念是不可能被打断的最小的操作,比如一条CPU指令,是不可能在指令执行期间被打断的),类似于多线程的数据同步问题中的原子锁的原子概念,产生这种概念的原因都是一样的。由于GPU是并行工作的,各个shader的执行必然是并行的,可以看做是多线程的,那么当多个shader需要同时访问/修改同一处内存时,便可能产生数据不同步的问题。
2、 为了解决这个问题,OpenGL提供了一系列原子操作的函数,这些函数分为int版本和uint版本的,各版本的返回值及参数都是相应的类型的。这一系列函数都是atomatic*()形式的。
3、 在有多个shader程序在同一时间使用同一原子操作函数(调用)操作同一段内存时,这些调用会被序列化(serialized),即会顺序执行,而不会同时执行。这样就不会保证你在进行原子内存操作时会得到特定的返回值。
4、 当你从缓冲区中读取数据时,你不必担心shader读取的顺序,但是当你需要通过变量或者通过原子操作对内存进行写入时,就要注意规避风险了。
5、 内存写入风险分为三类:一是写入完之后立即读取(Read-After-Write,RAW),这种风险发生与否与系统架构有关。(有些系统架构下,编译器或者CPU的换序优化会更改读与写的顺序。);二是写完之后立即再写(Write-After-Write,WAW),即程序前后两次往同一内存中写入数据,同样由于换序问题最终内存中的数据不一定是你以为的后写入的那个值。三是读完后立即写入(WAR),产生的原因同上。
6、 在主程序中使用分界符(barrier)解决数据同步的问题。barrier影响了他指定的内存子系统的内存操作顺序。使用glMemoryBarrier(GLbitfield barriers)可以在主程序中插入分界符。该函数的唯一的参数指定了其影响的内存子系统。几个常见用例如下:
(1)、参数指定为GL_SHADER_STORAGE_BARRIER_BIT且该函数在shader中的往shader storage block中写数据的操作之后调用,那么在数据写入完之前,其他读取这段内存的shader的操作将会被“阻塞”,写入完之后,这些数据才会对其他shader可见。
(2)参数指定为GL_UNIFORM_BARRIER_BIT,且该函数同样在shader中的往内存中写数据的操作之后调用,而且之后想把这块内存当做uniform buffer用。
(3)参数指定为GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT,道理同上。
7、在shader代码中使用barrier。比如在某函数中你定义了一些往缓冲区写数据的操作,之后立即调用memoryBarrier()函数,之后读取该缓冲区的内容,就能读取到最新的值,若不调用memoryBarrier()函数,那么之后获取的数据可能还是原来的数据。
8、原子计数器(atomic  counter)。原子计数器是一种特殊的变量,它的存储在不同的shader调用中是共享的。这种存储是由缓冲区对象支撑的,并且GLSL为他提供了在缓冲区中进行自增和自减的函数。
9、在shader中声明一个原子计数器的方法:
layout(binding=0) uniform atomic_uintmy_variable;
9、 OpenGL提供了许多用以绑定存储了原子计数器值的缓冲区的绑定点。在缓冲区中原子计数器变量是以偏移的方式进行存储的。缓冲区绑定索引与原子计数器在绑定点绑定的缓冲区中的偏移值是可以用bingding与offset这两个layout  qualifier进行指定的,并且可以应用于一个原子计数器的uniform变量的声明,比如:
layout(binding=0,offset=8) uniform atomic_uintmyVariable;
10、GLSL原子计数器自增函数:uintatomicCounterIncrement(atomic_uint c);此函数获取到c的值,让其加1并写回到c代表的原子计数器,但是返回的是原来的值。
11、GLSL原子计数器自增函数:uintatomicCounterDecrement(atomic_uint c);此函数获取到c的值,让其减1并写回到c代表的原子计数器,返回的是新的值。
12、获取原子计数器的值:uintatomicCounter(atomic_uint c);
13、shader执行时,原子计数器是存储在图形处理器的特殊的内存中的,这就是为什么原子计数器的操作要比对一般的如shader storage block的成员的内存的原子操作快的多。但是当shader执行完毕后,这些值会被写回到内存中,所以原子计数器的自增和自减操作也会面临上面所说的内存操作风险。同样使用glMemoryBarrier(GL_ATOMIC_COUNTER_BARRIER_BIT)可以为原子计数器添加分界符,以规避风险。
14、当你往某缓冲区写完数据这一操作能体现在原子计数器的值的变化上,即操作完成后,
根据你的逻辑,原子计数器的值会变化,那么应该在原子计数器变化前调用:
glMemoryBarrier(GL_ATOMIC_COUNTER_BARRIER_BIT ),以防止在数据操作完前就改变了原子计数器的值。如果你要用原子计数器改变一个缓冲区中的值,并且
改完之后要用这个缓冲区做其他事情,那么在做其他
事情之前应该调用分界符函数,传递的参数应与你要做
其他事情时把他当做的类型一致
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值