我正在尝试在OpenGL中构建计算着色器以执行骨架化算法。我已经在仅CPU版本中测试了该算法,在那儿是正确的。但是,将其移植到计算着色器代码时遇到了一些麻烦。

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

我有两个用于输入和输出的内存区域。我正在尝试在主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 file\n");
        printf("Error: %s\n", strerror(errno));
        return NULL;
    }

    fd = open(pFile, O_RDONLY);

    if (fd == -1)
    {
        printf("Error opening file. Error: %s\n", strerror(errno));
        return NULL;
    }

    source = new GLchar[buf.st_size + 1];

    if (read(fd, source, buf.st_size) == -1)
    {
        printf("Error reading file. Error: %s\n", strerror(errno));
        delete[] source;
        return NULL;
    }

    source[buf.st_size] = '\0'; //Shader compiler needs null to know end of input

    return source;
}


// Shader sources
const GLchar* vertexSource =
    "#version 450 core\n"
    "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 core\n"
    "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 error\n", line);
                break;
            case GL_INVALID_ENUM:
                printf("%d: Invalid enum!\n", line);
                break;
            case GL_INVALID_VALUE:
                printf("%d: Invalid value\n", line);
                break;
            case GL_INVALID_OPERATION:
                printf("%d: Invalid operation\n", line);
                break;
            case GL_INVALID_FRAMEBUFFER_OPERATION:
                printf("%d: Invalid framebuffer operation\n", line);
                break;
            case GL_OUT_OF_MEMORY:
                printf("%d: Out of memory\n", 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 good\n");
    }
    else
    {
        printf("link bad\n");
        GLchar log[4096];
        GLsizei len;

        glGetProgramInfoLog(computeProgram, 4096, &len, log);

        printf("%s\n", 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_0\n");
            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_1\n");
            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: %d\n",  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: %d\n",  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_1\n");
        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_0\n");
        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: %d\n", 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 GLEW\n");
        return -1;
    }

    initGL();
    runComputeProgram(data, data2);
    checkError(__LINE__);
    glutMainLoop();
    return 0;


}

计算着色器
#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)

通过修改计算着色器中的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()设置输入和输出缓冲区。

10-04 12:35