在OpenGL中使用Compute Shader(一)

先了解一些Compute Shader的基本概念
https://www.cg.tuwien.ac.at/courses/Realtime/repetitorium/VU.WS.2014/rtr_rep_2014_ComputeShader.pdf

最小执行单元为thread,也称为invocation,一个thread执行一遍shader程序
网格化的thread构成了work group,可以是1维、2维或者3维的。shader程序中用layout语句指定的local_size_x,local_size_y,local_size_z决定了work group的大小。
网格化的work group构成了dispatch,同样也可以是1维、2维或者3维的。glDispatchCompute中的三个参数指定了x, y, z三维的大小

ps:
https://www.khronos.org/assets/uploads/developers/library/2014-siggraph-bof/KITE-BOF_Aug14.pdf
根据资料,work group的尺寸也可以完全由c程序决定,GLSL中的声明改为
layout( local_size_variable ) in;

对比cuda的概念,compute shader中的work group就是cuda的block,dispatch就是cuda的grid。

OpenGL 4.2中Image介绍
https://blog.csdn.net/u010462297/article/details/50469950

image类型数据与sampler类型数据的区别:sampler取样本的时候,可以是非整数坐标,而且需要对周边像素进行插值才能获取。而image类型只能取整数坐标,直接取出原始数据样本

ps: gl_GlobalInvocationID.x的类型是uint,而imageLoad需要的坐标参数为int。可以用int(表达式)进行强制类型转换

要开始跑计算,首先要解决的是数据的输入和输出问题。ComputeShader中参数的声明非常类似原来的FragmentShader,还是使用uniform作为输入。不过数据类型可以使用image类型了,不再是sampler类型,解决精准取值的问题。另外,作为输入的image,还需要在layout指令中指明格式,例如r32f或者rgba32f(这里只有1、2、4色的格式,没有rgb三色的格式。不过比较奇怪的一点,作为输出的image可以不指明格式,暂时不知道为什么)。而且,image对象需要调用glBindImageTexture跟纹理对象进行绑定。而且还有一点需要非常注意
“A very important restriction for using shader images is that the underlying texture must have been allocated using "immutable" storage, i.e. via glTexStorage*()-like functions, and not glTexImage2D().”

cpu与gpu之间传数据,往gpu传入使用 glTexSubImage,从gpu读出使用glGetTexImage(glReadPixels需要跟FBO一起使用才可以,而glGetTexImage可以直接从texture读取)

至于OpenGL的初始化工作,还是采用freeglut+glew的经典组合就可以完成。

一个最简单的例子,传输一个一维数组,然后给数组里面每个元素加1。

#include <GL/glew.h>
#include <GL/freeglut.h>
#include <stdio.h>

void InitWindow(int, char*[]);
void Initialize(int, char*[]);
void CreateShaders(void);
void CreateTexture(void);

GLuint programId, computeShaderId;
GLuint inputTexId, outputTexId;

const int kArraySize = 32;

const GLchar* Program = " \
    #version 430\n\
    layout (local_size_x = 16, local_size_y = 1) in;\n\
    layout (r32f, binding = 0) uniform image1D in_array; \n\
    layout (r32f, binding = 1) uniform image1D out_array; \n\
    \
    void main() \n\
    { \
        int pos = int(gl_GlobalInvocationID.x);\n\
        vec4 value = imageLoad(in_array, pos);\n\
        value.x += 1.0f;\n\
        imageStore(out_array, pos, value);\n\
    } \
";

void CheckGLErrors() 
{
    GLenum e = glGetError();
    if (e != GL_NO_ERROR) {
        fprintf(stderr, "OpenGL error: %s (%d)\n", gluErrorString(e), e);
        exit(20);
    }
}

// 创建opengl的窗口
void InitWindow(int argc, char* argv[])
{
    glutInit(&argc, argv);

    glutInitContextVersion(2, 0);
    glutInitContextFlags(GLUT_FORWARD_COMPATIBLE);
    glutInitContextProfile(GLUT_CORE_PROFILE);

    int WindowHandle = glutCreateWindow("test");

    if (WindowHandle < 1) {
        fprintf(
            stderr,
            "ERROR: Could not create a new rendering window.\n"
        );
        exit(EXIT_FAILURE);
    }

    //glutDisplayFunc(RenderFunction);
    //glutCloseFunc(Cleanup);
}

void Initialize(int argc, char* argv[])
{
    GLenum GlewInitResult;

    InitWindow(argc, argv);

    //glewExperimental = GL_TRUE;
    GlewInitResult = glewInit();

    if (GLEW_OK != GlewInitResult) {
        fprintf(
            stderr,
            "ERROR: %s\n",
            glewGetErrorString(GlewInitResult)
        );
        exit(EXIT_FAILURE);
    }

    fprintf(
        stdout,
        "INFO: OpenGL Version: %s\n",
        glGetString(GL_VERSION)
    );

    CreateShaders();
    CreateTexture();
}

void CreateShaders(void)
{
    GLchar messages[256];
    GLenum ErrorCheckValue = glGetError();

    /* Compile the shader. */
    computeShaderId = glCreateShader(GL_COMPUTE_SHADER);
    glShaderSource(computeShaderId, 1, &Program, NULL);
    glCompileShader(computeShaderId);

    /* Print the compilation log. */
    glGetShaderInfoLog(computeShaderId, sizeof(messages), NULL, messages);
    printf("Compile Log: %s\n", messages);

    /* Set up program objects. */
    programId = glCreateProgram();

    /* Create a complete program object. */
    glAttachShader(programId, computeShaderId);
    glLinkProgram(programId);

    /* And print the link log. */
    glGetProgramInfoLog(programId, sizeof(messages), NULL, messages);
    printf("Link Log: %s\n", messages);

    CheckGLErrors();
}

void CreateTexture(void)
{
    // Create the input texture
    glGenTextures(1, &inputTexId);

    // And bind it to texture unit 0
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_1D, inputTexId);
    // Set texture size and format
    glTexStorage1D(GL_TEXTURE_1D, 1, GL_R32F, kArraySize);

    // Create the output texture
    glGenTextures(1, &outputTexId);

    // And bind it to texture unit 1
    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_1D, outputTexId);
    // Set texture size and format
    glTexStorage1D(GL_TEXTURE_1D, 1, GL_R32F, kArraySize);

    glBindImageTexture(0, inputTexId, 0, GL_FALSE, 0, GL_READ_ONLY, GL_R32F);
    glBindImageTexture(1, outputTexId, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_R32F);

    CheckGLErrors();
}

void DoCompute()
{
    float *inputData = new float[kArraySize];
    float *outputData = new float[kArraySize];

    int i;
    for (i = 0; i < kArraySize; i++)
    {
        inputData[i] = i;
    }

    glBindTexture(GL_TEXTURE_1D, inputTexId);
    glTexSubImage1D(GL_TEXTURE_1D, 0, 0, kArraySize, GL_RED, GL_FLOAT, inputData);


    { // launch compute shaders!
        glUseProgram(programId);
        glDispatchCompute((GLuint)kArraySize/16, 1, 1);
    }

    // make sure writing to image has finished before read
    glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);

    glBindTexture(GL_TEXTURE_1D, outputTexId);
    glGetTexImage(GL_TEXTURE_1D, 0, GL_RED, GL_FLOAT, outputData);
    glBindTexture(GL_TEXTURE_2D, 0);

    CheckGLErrors();

    for (i = 0; i < kArraySize; i++)
    {
        printf("%f ", outputData[i]);
    }

    delete []outputData;
    delete []inputData;
}

int main(int argc, char *argv[])
{
    Initialize(argc, argv);

    DoCompute();
    //glutMainLoop();

    return(0);
}

参考
http://wili.cc/blog/opengl-cs.html
https://antongerdelan.net/opengl/compute.html
https://www.cnblogs.com/chen9510/p/12000320.html
https://blog.csdn.net/koibiki/article/details/80590885
https://arm-software.github.io/opengl-es-sdk-for-android/compute_intro.html

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容