OpenGL高级特性之利用Image内存模型&计算着色器&原子操作实现(直方图模型)通用计算

Image与Texture的区别

参考:
https://stackoverflow.com/questions/35716267/opengl-glactivetexture-vs-glbindimagetexture

  1. 取值方式差别:texture是通过映射到0-1范围内的任何浮点数组取值,而Image则是通过像素坐标取值,限定于图像维度之内的有符号整数组取值,如[100,23]。
  2. 访问方式不同:texture只支持读。而Image支持读和写,并且支持原子操作(AtomicOps)
  3. 本身存储内容不同:texture由各个mipmap level的图像组成,因此可说是数张图像,而Image只包含一张图像

Image 读/写操作

参考:
1. OpenGL wiki: https://www.khronos.org/opengl/wiki/Image_Load_Store#Image_variables
2. CUDA Samples 之 histogram

  1. Image变量声明:Image的格式和Texture一样,因为Image就是由Texture绑定生成的。但是需要留心的是某些格式的Texture并不存在与之对应格式的Image
    在GLSL中的常规声明方式:layout(r32ui) uniform restrict uimage2D image 。注意,布局中的r32ui是不能随便更改的,因为Atomic的操作数只允许unsigned int(其实我觉得这很有问题,正常图像都是多个通道的啊),因此纹理的格式就限定为单通道32位无符号。另外,Image的格式为uimage,如果直接声明为image,shader编译会直接不通过。

  2. 常规读写操作:imageLoad()imageStore()是常规的读写函数,注意坐标参数必须是正整数,因为image不能像sampler那样支持插值取样的操作,必须像索引二维数组那样操作。

  3. 原子操作:为了实现直方图模型计算需求,则需要imageAtomicAdd()这样的函数去做原子操作。CUDA中也有类似的操作也成为Atomic操作。有兴趣可以做做对比。这里絮叨一下直方图计算模型,简单说就是一处读,任意处写任意次。对于CPU来说这好像很方便,但是对于GPU这种SIMD的计算模型来说不太容易。很可能要很多线程同对某个地址的内存执行写操作,这就要求我们的写操作具有原子性。与直方图模型对应的是滤波模型或者卷积模型,这类操作只需要从多处读值,然后计算写入当前计算单元对应的内存,对于不同的计算单元,他们对应的内存是完全分开无关的。

示例代码验证

参考:
Nicol Bolas的答案: https://stackoverflow.com/questions/14285849/trouble-with-imagestore-opengl-4-3

为了验证原子操作,我们让多个线程对同一个地址做写操作 ivec2 coord_atomic=ivec2(coord.x,150) 就是简单地让height个线程同时对一个地址[coord.x,150]执行写操作。
验证示例代码如下:

cpp:

void OGLImage::init()
{
    // shader
    m_shader.init();
    m_shader.attach(GL_COMPUTE_SHADER, "image_writer.glsl");
    m_shader.link();
    m_shader.use();

    // create texture and bind it to image
    glGenTextures(1, &texture_id);
    glBindTexture(GL_TEXTURE_2D, texture_id);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 512, 424, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
    glBindImageTexture(0, texture_id, 0, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA8); //bind level 0 of texture to image
    glBindTexture(GL_TEXTURE_2D, 0);

}

void OGLImage::computeImage()
{
    m_shader.use();
    GLuint location = m_shader.location("image");
    glUniform1i(location, 0);
    glDispatchCompute(width, height, 1);
}

glsl:

#version 430

layout(r32ui) uniform restrict uimage2D image;

layout (local_size_x = 1, local_size_y = 1) in;

uint color2Atom(uvec4 color)
{
    return color.r | (color.g << 8) | (color.b << 16) | (color.a << 24);
}

uvec4 atom2Color(uint atom)
{
    uvec4 result;
    result.r = atom & uint(0xff);
    result.g = (atom >> 8) & uint(0xff);
    result.b = (atom >> 16) & uint(0xff);
    result.a = (atom >> 24) & uint(0xff);
    return result;
}

void main() {
    ivec2 coord= ivec2(gl_GlobalInvocationID.xy);
    ivec2 coord_atomic=ivec2(coord.x,150);
    imageAtomicAdd(image, coord_atomic, color2Atom(uvec4(1,0,1,255)));
}

你可能感兴趣的:(OpenGL)