重用bindBufferBase和OpenGL计算着色器

Reusing bindBufferBase and OpenGL compute shader

本文关键字:计算 OpenGL bindBufferBase 重用      更新时间:2023-10-16

我试图在OpenGL中构建一个计算着色器来执行骨架化算法。我在一个只有CPU的版本中测试了这个算法,它在那里是正确的。然而,我有一些麻烦移植它来计算着色器代码。

问题是,无论我运行多少次计算着色器调用,第一次调用后输出永远不会改变。实际上,如果我在while循环结束时取出check,程序将永远不会终止。

我有两个内存区域用于输入和输出。我试图在主while循环中使用glBindBufferBase()做一个技巧,在那里我交换了它们中的两个(最后一轮的输出成为当前一轮的输入)。参见main.cpp中的第270 - 318行。这样我就不会在CPU和GPU之间来回复制数据了。

我的问题是:

1)我可以用glBindBuffers做这个技巧,在那里我交换它们,这样我就可以多次操作数据,而不把它移回CPU?在测试一个较小的问题(只是添加短数组)时,它起作用了。

2)如果技巧是好的,我错在哪里?

注意:这段代码需要一个640 x 400大小的。pgm(黑白图像),名为"test.pgm"。您可以在GIMP中创建一个,但请确保将其保存为二进制文件,而不是ASCII文件。

此代码使用以下标志

进行编译
g++ -g pgm.cpp main.cpp -lglut -lGLU -lGL -lm -lGLEW -o test

另外,请原谅我使用c++,但使用C风格的技巧。我花在C上的时间比c++多。

main.cpp

// Include standard headers
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <math.h>
#include <errno.h>
//For stat
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
// Include GLEW
#include <GL/glew.h>
//Glut
#include <GL/glut.h>
//Project specific
#include "skeletonize.hpp"
#include "PGM.hpp"
// OpenGL shader info
GLuint programID;
GLuint output_image;
#define IMG_0               0
#define IMG_1               1
#define CMD                 2
#define NUM_BUFS            3
#define CMD_BUF_WIDTH       0
#define CMD_BUF_HEIGHT      1
#define CMD_BUF_CMD         2
#define CMD_BUF_RESPONSE    3
#define CMD_BUF_LEN         4
#define CMD_EXPAND          1
#define CMD_THIN_N          2
#define CMD_THIN_S          3
#define CMD_THIN_E          4
#define CMD_THIN_W          5
#define CMD_NORMALIZE       6
#define CMD_REGULARIZE      7
#define INITIALIZED         0
#define NOT_FINISHED        1
GLuint computeProgram;
GLuint buffers[NUM_BUFS];       //SSBO objects, one for IMG_0, one for IMG_1, and one for commands/response
static GLchar* computeSource;
GLuint shaderProgram;

//TODO: don't really need 2 textures yet, but will eventually when doing overlay of original image.
GLuint textures[2];

GLchar* LoadSource(const char* pFile)
{
    struct stat buf;
    GLchar *source;
    int fd;
    if (stat(pFile, &buf) == -1)
    {
        printf("Error opening filen");
        printf("Error: %sn", strerror(errno));
        return NULL;
    }
    fd = open(pFile, O_RDONLY);
    if (fd == -1)
    {
        printf("Error opening file. Error: %sn", strerror(errno));
        return NULL;
    }
    source = new GLchar[buf.st_size + 1];
    if (read(fd, source, buf.st_size) == -1)
    {
        printf("Error reading file. Error: %sn", strerror(errno));
        delete[] source;
        return NULL;
    }
    source[buf.st_size] = ''; //Shader compiler needs null to know end of input
    return source;
}

// Shader sources
const GLchar* vertexSource =
    "#version 450 coren"
    "in vec2 position;"
    "in vec2 texcoord;"
    "out vec2 Texcoord;"
    "void main()"
    "{"
    "    Texcoord = texcoord;"
    "    gl_Position = vec4(position, 0.0, 1.0);"
    "}";
const GLchar* fragmentSource =
    "#version 450 coren"
    "in vec2 Texcoord;"
    "out vec4 outColor;"
    "uniform sampler2D texData;"
    "void main()"
    "{"
    "   vec4 imColor = texture(texData, Texcoord);"
    "   outColor = vec4(0.0, imColor.r, 0.0, 1.0);"
    //"    outColor = texture(texData, Texcoord);"
    //"    outColor = vec4(1.0, 1.0, 0.0, 1.0);"
    "}";

void checkError(int line)
{
    GLint err;
    do
    {
        err = glGetError();
        switch (err)
        {
            case GL_NO_ERROR:
                //printf("%d: No errorn", line);
                break;
            case GL_INVALID_ENUM:
                printf("%d: Invalid enum!n", line);
                break;
            case GL_INVALID_VALUE:
                printf("%d: Invalid valuen", line);
                break;
            case GL_INVALID_OPERATION:
                printf("%d: Invalid operationn", line);
                break;
            case GL_INVALID_FRAMEBUFFER_OPERATION:
                printf("%d: Invalid framebuffer operationn", line);
                break;
            case GL_OUT_OF_MEMORY:
                printf("%d: Out of memoryn", line);
                break;
            default:
                printf("%d: glGetError default case. Should not happen!n", line);
        }
    } while (err != GL_NO_ERROR);
}
void display()
{
    glClearColor(0.0f, 0.0f, 1.0f, 0.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
    glFlush();
    glutSwapBuffers();
}

void reshape(int width,int height)
{
    double w2h = (height>0) ? (double)width/height : 1;
    //  Set viewport as entire window
    glViewport(0,0, width,height);
}


void runComputeProgram(uint32_t *data, uint32_t *data2)
{
    int width = 640;
    int height = 400;
    uint32_t *ptr;
    uint32_t cmd[CMD_BUF_LEN];
    computeSource = LoadSource("compute.shader");
    if (computeSource == NULL)
    {
        return;
    }
    GLuint computeShader = glCreateShader(GL_COMPUTE_SHADER);
    glShaderSource(computeShader, 1, &computeSource, NULL);
    glCompileShader(computeShader);

    computeProgram = glCreateProgram();
    glAttachShader(computeProgram, computeShader);
    glLinkProgram(computeProgram);
    GLint status;
    glGetProgramiv(computeProgram, GL_LINK_STATUS, &status);
    if (status == GL_TRUE)
    {
        printf("link goodn");
    }
    else
    {
        printf("link badn");
        GLchar log[4096];
        GLsizei len;
        glGetProgramInfoLog(computeProgram, 4096, &len, log);
        printf("%sn", log);
        return;
    }
    // First action is to transform the image into binary values (0, 1)
    cmd[CMD_BUF_CMD] = CMD_NORMALIZE;
    cmd[CMD_BUF_WIDTH] = width;
    cmd[CMD_BUF_HEIGHT] = height;
    glGenBuffers(NUM_BUFS, buffers);
    glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, buffers[CMD]);
    glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(cmd), cmd, GL_DYNAMIC_DRAW);
    glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, buffers[IMG_0]);
    glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(uint32_t) * width * height, data, GL_DYNAMIC_DRAW);
    glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, buffers[IMG_1]);
    glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(uint32_t) * width * height, data2, GL_DYNAMIC_DRAW);

    glUseProgram(computeProgram);
    glDispatchCompute(width / 16, height / 16, 1);
    glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);

    glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, buffers[IMG_1]);
    ptr = (uint32_t *)glMapBuffer(GL_SHADER_STORAGE_BUFFER, GL_READ_ONLY);
    glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
    // Rebind ptr for our while loop
    glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, buffers[CMD]);
    //glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(cmd), cmd, GL_DYNAMIC_DRAW);
    ptr = (uint32_t *)glMapBuffer(GL_SHADER_STORAGE_BUFFER, GL_READ_ONLY);
    int i = 0;
    do
    {   
        printf("iteration: %d", i);
        glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
        cmd[CMD_BUF_RESPONSE] = INITIALIZED;
        switch (i % 4)
        {
            case 0:
                cmd[CMD_BUF_CMD] = CMD_THIN_N;
                break;
            case 1:
                cmd[CMD_BUF_CMD] = CMD_THIN_S;
                break;
            case 2:
                cmd[CMD_BUF_CMD] = CMD_THIN_E;
                break;
            case 3:
                cmd[CMD_BUF_CMD] = CMD_THIN_W;
                break;
        }        
        glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(cmd), cmd, GL_DYNAMIC_DRAW);
        glDispatchCompute(width / 16, height / 16, 1);
        glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);
        if (i % 2 == 0)
        {
            printf("Input is now img_1. Output is img_0n");
            glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, buffers[IMG_1]);
            glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, buffers[IMG_0]);
            checkError(__LINE__);
        }
        else
        {
            printf("Input is now img_0. Output is img_1n");
            glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, buffers[IMG_0]);
            glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, buffers[IMG_1]);
            checkError(__LINE__);
        }

        glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, buffers[CMD]);
        ptr = (uint32_t *)glMapBuffer(GL_SHADER_STORAGE_BUFFER, GL_READ_ONLY);
        printf("cmd issued at start: %d response: %dn",  ptr[CMD_BUF_CMD], ptr[CMD_BUF_RESPONSE]);
        i++;
    } while(ptr[CMD_BUF_RESPONSE] != INITIALIZED && i < 10); //Using i < 10, otherwise this never terminates
    glUnmapBuffer(GL_SHADER_STORAGE_BUFFER); // Free ptr

    // Transform Binary image (0, 1) to (0, 0xFFFFFFFF) values for texture display
    cmd[CMD_BUF_CMD] = CMD_REGULARIZE;        
    glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, buffers[CMD]);
    glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(cmd), cmd, GL_DYNAMIC_DRAW);
    glDispatchCompute(width / 16, height / 16, 1);
    glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);
    glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, buffers[CMD]);
    ptr = (uint32_t *)glMapBuffer(GL_SHADER_STORAGE_BUFFER, GL_READ_ONLY);
    printf("Regularize: cmd: %d  width: %d  height: %d response: %dn",  ptr[CMD_BUF_CMD], ptr[CMD_BUF_WIDTH], ptr[CMD_BUF_HEIGHT], ptr[CMD_BUF_RESPONSE]);

    // Create texure
    glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);

    glGenTextures(2, textures);
    checkError(__LINE__);
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, textures[0]);
        checkError(__LINE__);
    if (i % 2 == 0)
    {
        printf("output image is img_1n");
        glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, buffers[IMG_1]);
        ptr = (uint32_t *)glMapBuffer(GL_SHADER_STORAGE_BUFFER, GL_READ_ONLY);
    }
    else
    {        
        printf("output image is img_0n");
        glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, buffers[IMG_0]);
        ptr = (uint32_t *)glMapBuffer(GL_SHADER_STORAGE_BUFFER, GL_READ_ONLY);
    }

    glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);

    glUseProgram(shaderProgram);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0,  GL_RED,  GL_UNSIGNED_INT, ptr);  //TODO: this is wrong. worry about later.
        checkError(__LINE__);
    glUniform1i(glGetUniformLocation(shaderProgram, "texData"), 0);
    checkError(__LINE__);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);


    checkError(__LINE__);
}
void initGL()
{
    // Vertices & texture init
    GLuint vao;
    glGenVertexArrays(1, &vao);
    glBindVertexArray(vao);
    GLuint vbo;
    glGenBuffers(1, &vbo);
    GLfloat vertices[] = {
        // X    Y      S    T
        -1.0f,  1.0f, 0.0f, 0.0f, // Top-left
         1.0f,  1.0f, 1.0f, 0.0f, // Top-right
         1.0f, -1.0f, 1.0f, 1.0f, // Bottom-right
        -1.0f, -1.0f, 0.0f, 1.0f  // Bottom-left
    };
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    GLuint ebo;
    glGenBuffers(1, &ebo);
    GLuint elements[] = {
        0, 1, 2,
        2, 3, 0
    };
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(elements), elements, GL_STATIC_DRAW);
    // Create shaders
    GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertexShader, 1, &vertexSource, NULL);
    glCompileShader(vertexShader);
    GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShader, 1, &fragmentSource, NULL);
    glCompileShader(fragmentShader);
    shaderProgram = glCreateProgram();
    glAttachShader(shaderProgram, vertexShader);
    glAttachShader(shaderProgram, fragmentShader);
    glBindFragDataLocation(shaderProgram, 0, "outColor");
    glLinkProgram(shaderProgram);
    glUseProgram(shaderProgram);
    // Vertex data specification
    GLint posAttrib = glGetAttribLocation(shaderProgram, "position");
    glEnableVertexAttribArray(posAttrib);
    glVertexAttribPointer(posAttrib, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), 0);
    GLint texAttrib = glGetAttribLocation(shaderProgram, "texcoord");
    glEnableVertexAttribArray(texAttrib);
    glVertexAttribPointer(texAttrib, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (void *)(2 * sizeof(GLfloat)));
    checkError(__LINE__);

}
int main(int argc, char** argv)
{
    // Image setup
    PGM pgmImage;
    pgmImage.ReadFile("test.pgm");
    uint32_t *data = new uint32_t[pgmImage.GetHeight() * pgmImage.GetWidth()];
    uint32_t *data2 = new uint32_t[pgmImage.GetHeight() * pgmImage.GetWidth()];
    unsigned int size = pgmImage.GetHeight() * pgmImage.GetWidth();
    uint8_t *pgmData = pgmImage.GetData();
    for (int i=0; i < size; i++)
    {
        data[i] = pgmData[i];
    }
    int count = 0;
    for (int i =0; i < pgmImage.GetHeight() * pgmImage.GetWidth(); i++)
    {
        if (data[i] == 0xFF)
        {
            count++;
        }
    }
    printf("count: %dn", count);
    // Window Setup
    glutInitWindowSize(640, 400);
    glutInitWindowPosition (140, 140);
    glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);
    glutInit(&argc, argv);
    glutCreateWindow( "OpenGL Application" );
    glutDisplayFunc(display);
    glutReshapeFunc(reshape);
    glewExperimental = true;
    if (glewInit() != GLEW_OK) {
        fprintf(stderr, "Failed to initialize GLEWn");
        return -1;
    }
    initGL();
    runComputeProgram(data, data2);
    checkError(__LINE__);
    glutMainLoop();
    return 0;

}

compute.shader

#version 450 core
//#extension GL_NV_shader_buffer_load : enable
#define WIDTH           0    // Width of image
#define HEIGHT          1    // Height of image
#define CMD             2    // Command to execute
#define RESPONSE        3    // Response to command
#define BUF_LEN         4
#define CMD_UNUSED      1    // TODO: remove this. Will have to be mirroed in C code.
#define CMD_THIN_N      2
#define CMD_THIN_S      3
#define CMD_THIN_E      4
#define CMD_THIN_W      5
#define CMD_NORMALIZE   6
#define CMD_REGULARIZE  7
#define NOT_FINISHED 1
layout (local_size_x = 16, local_size_y = 16) in;
//layout (local_size_x = 1) in; //TODO: remove
layout (std430, binding = 0) buffer Cmd {
   uint cmd_buf[BUF_LEN]; //Width, height, command, response
};
layout (std430, binding = 1) buffer Img1 {
   uint image_0[];
};
layout (std430, binding = 2) buffer Img2 {
    uint image_1[];
};

int sigma(uint data[9]) {
    int i;
    int sigma = 0;
    // Assume 9 pixels, A0 (pixel of interest) -> A8
    // In image, A0 is center
    // 1 2 3
    // 8 0 4
    // 7 6 5
    for (i=1; i < 9; i++)
    {
        sigma += int(data[i]);
    }
    return sigma;
}

int chi(uint data[9]) {
    int chi;
    // Assume 9 pixels, A0 (pixel of interest) -> A8
    // 1 2 3
    // 8 0 4
    // 7 6 5
    chi = int(data[1] != data[3]) +
          int(data[3] != data[5]) +
          int(data[5] != data[7]) +
          int(data[7] != data[1]) +
          2 * ( int((data[2] > data[1]) && (data[2] > data[3])) ) +
          int((data[4] > data[3]) && (data[4] > data[5])) +
          int((data[6] > data[5]) && (data[6] > data[7])) +
          int((data[8] > data[7]) && (data[8] > data[1]));
   return chi;
}
// 1 2 3
// 8 0 4
// 7 6 5
int getPos(in int x, int y) {
   return y * int(cmd_buf[WIDTH]) + x;
}
uint getVal(in int pos) {
   return image_0[ uint(pos) ];
}
int removePoint(uint neighborhood[9]) {
    int x = int(gl_GlobalInvocationID.x);
    int y = int(gl_GlobalInvocationID.y);
    if (chi(neighborhood) == 2 && sigma(neighborhood) != 1) {
        image_1[getPos(x, y)] = 0;
        cmd_buf[RESPONSE] = NOT_FINISHED;
        return 1;
    }
    else
    {
        //TODO: needed? Swapping back and forth between input and output should account for this
        image_1[getPos(x,y)] = 1;
    }   
    return 0;
}

void getNeighborhood(inout uint neighborhood[9]) {
    int x = int(gl_GlobalInvocationID.x);
    int y = int(gl_GlobalInvocationID.y);
    int bottom = int(cmd_buf[WIDTH] * (cmd_buf[HEIGHT] - 1));
    int pos = getPos(x, y);
    int width = int(cmd_buf[WIDTH]);
    int height = int(cmd_buf[HEIGHT]);
    uint pixel;
    int i = 0;
    for (i=1; i < 9; i++) {
       neighborhood[i] = 2;
    }
    if (pos < width) {
        // Pixel on top, fill outiside image with zero
       neighborhood[1] = 0;
       neighborhood[2] = 0;
       neighborhood[3] = 0;
    }
    if (pos % width == 0) {
        // Pixel is on left edge. Fill area outside of image with zero
        neighborhood[1] = 0;
        neighborhood[8] = 0;
        neighborhood[7] = 0;
    }
    if ((pos % width) == (width - 1)) {
        // Pixel is on right edge.
        neighborhood[3] = 0;
        neighborhood[4] = 0;
        neighborhood[5] = 0;
    }
    if (pos >= bottom) {
        // Pixel is on bottom edge.
        neighborhood[5] = 0;
        neighborhood[6] = 0;
        neighborhood[7] = 0;
    }
    // Get remaining pixels
    for (i=1; i < 9; i++) {
        if (neighborhood[i] == 2) {
            switch (i) {
                case 1:
                    // Upper left pixel
                    neighborhood[i] = getVal(pos - 1 - width);
                    break;
                case 2:
                    // Upper middle pixel
                    neighborhood[i] = getVal(pos - width);
                    break;
                case 3:
                    // Upper right pixel
                    neighborhood[i] = getVal(pos + 1 - width);
                    break;
                case 4:
                    // Right pixel
                    neighborhood[i] = getVal(pos + 1);
                    break;
                case 5:
                    // Bottom right pixel
                    neighborhood[i] = getVal(pos + width + 1);
                    break;
                case 6:
                    // Bottom middle pixel
                    neighborhood[i] = getVal(pos + width);
                    break;
                case 7:
                    // Bottom left pixel
                    neighborhood[i] = getVal(pos + width - 1);
                    break;
                case 8:
                    // Left pixel
                    neighborhood[i] = getVal(pos - 1);
                    break;
            }
        }
    }
}


void normalize() {
    int x = int(gl_GlobalInvocationID.x);
    int y = int(gl_GlobalInvocationID.y);
    uint val = image_0[getPos(x, y)] == 0x0 ? 0 : 1;
    image_0[getPos(x, y)] = val;
    image_1[getPos(x, y)] = val;
}

void regularize() {
    int x = int(gl_GlobalInvocationID.x);
    int y = int(gl_GlobalInvocationID.y);
    uint val = image_0[getPos(x, y)] == 0x0 ? 0 : 0xFFFFFFFF;
    if (val != 0xFFFFFFFF)
    {
        cmd_buf[RESPONSE] = 99; //Test Value -- TODO: remove
    }
    image_1[getPos(x, y)] = val;
}

// North-South-East-West skeletonization
void skeleton() {
    int x = int(gl_GlobalInvocationID.x);
    int y = int(gl_GlobalInvocationID.y);
    uint neighborhood[9];
    neighborhood[0] = getVal(getPos(x, y));
    // Only consider cases where the center is 1
    if (neighborhood[0] != 1) {
        return;
    }
    getNeighborhood(neighborhood);
    switch (cmd_buf[CMD]) {
        case CMD_THIN_N:
            //north
            if (neighborhood[2] == 0 && neighborhood[6] == 1) {
                removePoint(neighborhood);
            }
            break;
        case CMD_THIN_S:
            //south
            if (neighborhood[2] == 1 && neighborhood[6] == 0) {
                removePoint(neighborhood);
            }
            break;
        case CMD_THIN_E:
            //east
            if (neighborhood[4] == 0 && neighborhood[8] == 1) {
                removePoint(neighborhood);
            }
            break;
        case CMD_THIN_W:
            //west
            if (neighborhood[4] == 1 && neighborhood[8] == 0) {
                removePoint(neighborhood);
            }
            break;
    }
}

void main() {
    switch (cmd_buf[CMD]) {
        case CMD_THIN_N:
        case CMD_THIN_S:
        case CMD_THIN_E:
        case CMD_THIN_W:
            skeleton();
            break;
        case CMD_NORMALIZE:
            normalize();
            break;
        case CMD_REGULARIZE:
            regularize();
            break;
    }
}

pgm.cpp

#include "PGM.hpp"
#define PGM_HEADER "P5"
PGM::PGM()
{
    mpData = NULL;
    Clear();
}
PGM::~PGM()
{
    Clear();
}
uint8_t* PGM::GetData()
{
    return mpImgData;
}

uint16_t PGM::GetWidth()
{
    return mWidth;
}
uint16_t PGM::GetHeight()
{
    return mHeight;
}
uint8_t PGM::GetMaxWhite()
{
    return mMaxWhite;
}
void PGM::Clear()
{
    if (mpData != NULL)
    {
        delete[] mpData;
    }
    mpImgData = NULL;
    mWidth = 0;
    mHeight = 0;
    mMaxWhite = 255;
}
// Finds the 
int PGM::PopulateFields(size_t size)
{
    int i;
    bool EOL = false;
    bool haveWhite = false;
    bool comment = false;
    if (mpData == NULL) { return -1; }
    // Check header
    if ((mpData[0] != 0x50) || (mpData[1] != 0x35)) { return -2; }
    //Ignore the comment
    //Start at 3rd position in file, after "P5" header    
    for (i = 2; i < size; i++)
    {
        if (mpData[i] == '#') 
        { 
            comment = true;
            continue;
        }
        if (mpData[i] == 0x0A && comment == true)
        {
            comment = false;
            break;
        }
        if (comment == true) 
        { 
            continue; 
        }
    }
    // Get width and height
    i++;
    sscanf((char *)&mpData[i], "%4" SCNu16 " %4" SCNu16, &mWidth, &mHeight);
    for (i; i < size; i++)
    {
        //Move past the width and height we just found
        if (mpData[i] == 0x0A && EOL == false)
        {
            EOL = true;
            continue;
        }
        // If past the width and height, now at the range. Save it.
        if (EOL == true && haveWhite == false)
        {
            sscanf((char *)&mpData[i], "%3" SCNu8, &mMaxWhite);
            haveWhite = true;
        }
        if (haveWhite == true && mpData[i] == 0x0A)
        {
            i++; //Move to next element - start of the actual data
            break;
        }
    }
    if (i == size)
    {
        return -3; //Did not find the start of data.
    }
    mpImgData = &mpData[i];
    return 0;
}
// Reads a PGM file. Returns 0 on success, other values on failure
int PGM::ReadFile(const char *pPath)
{
    struct stat st;
    int fd;
    if (this->mpData != NULL)
    {
        Clear();
    }
    if (stat(pPath, &st) != 0)
    {
        return 1;
    }
    fd = open(pPath, O_RDONLY);
    if (fd == -1)
    {
        return 1;
    }
    //this->mpData = (uint8_t *) malloc(st.st_size);
    mpData = new uint8_t[st.st_size];
    if (this->mpData == NULL)
    {
        return 2;
    }
    if (read(fd, this->mpData, st.st_size) == -1)
    {
        Clear();
    }
    close(fd);
    PopulateFields(st.st_size);
    return 0;
}

pgm.hpp

#ifndef __PGM_H__
#define __PGM_H__
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <inttypes.h>
class PGM
{
  public:
    int         ReadFile(const char *pPath);
    uint8_t*    GetData();
    uint16_t    GetWidth();
    uint16_t    GetHeight();
    uint8_t     GetMaxWhite();
    PGM();
    ~PGM();
  private:
    void        Clear();
    int         PopulateFields(size_t size);
    uint8_t     *mpData;
    uint8_t     *mpImgData;
    uint16_t    mWidth;
    uint16_t    mHeight;
    uint8_t     mMaxWhite;
};
#endif // __PGM_H__

(答案也发布在交叉发布的网站上)

我找到了!这个问题源于互换中的一个问题。当我们交换缓冲区时,输出缓冲区变成输入缓冲区,输入缓冲区变成输出缓冲区。但是,现在作为输出的缓冲区没有更新以匹配!

To illustrate:
Init:
 In      Out
1 1 1   1 1 1
1 1 1   1 1 1
1 1 1   1 1 1
First Iteration (remove north corners):
 In      Out
1 1 1   0 1 0
1 1 1   1 1 1
1 1 1   1 1 1
Swap 
 In      Out
0 1 0   1 1 1
1 1 1   1 1 1
1 1 1   1 1 1
Second iteration (remove east corners):
 In      Out
0 1 0   1 1 1
1 1 1   1 1 1
1 1 1   1 1 0
... (and so on)

通过修改compute shader中的skeleton()将image_0复制到image_1,在此之前,算法可以正确工作。

void skeleton() {
    int x = int(gl_GlobalInvocationID.x);
    int y = int(gl_GlobalInvocationID.y);
    uint neighborhood[9];
    neighborhood[0] = getVal(getPos(x, y));
    image_1[getPos(x, y)] = image_0[getPos(x, y)];
    // Only consider cases where the center is 1
    if (neighborhood[0] != 1) {
        image_1[0] = 0;
        return;
    }
    ...

此外,我对代码进行了相当多的清理。例如,您可以在调度调用之前通过glBindBufferBase()设置进出缓冲区。