title: Reusing bindBufferBase and OpenGL compute shader
body: <p>I'm trying to build a compute shader in OpenGL to perform a skeletonization algorithm. I've tested the algorithm in a CPU only version, and it was correct there. However, I'm having some trouble porting it to compute shader code.</p>
  
  <p>The problem is that no matter how many invocations of the compute shader I run, the output never changes past the first invocation. In fact, if I take out the check at the end of the while loop, the program never terminates.</p>
  
  <p>I have two areas of memory that I use for input and output. I'm trying to do a trick with glBindBufferBase() in the main while loop where I swap the two of them (output of the last round becomes input for the current round). See lines 270 - 318 in main.cpp. This is so I don't copy the data back and forth between the CPU and GPU a bunch of times.</p>
  
  <p>So, my questions are:</p>
  
  <p>1) Can I do this trick with glBindBuffers, where I swap them around so that I can operate on the data multiple times without moving it back to the CPU? In testing on a smaller problem (just adding short arrays), it worked.</p>
  
  <p>2) If the trick is fine, where am I going wrong?</p>
  
  <p>Note: this code needs a 640 x 400 size .pgm (black and white image) called "test.pgm". You can make one in GIMP, but be sure to save it as a binary one instead of an ASCII one.</p>
  
  <p>This code is compiled with the following flags</p>
  
  <pre><code>g++ -g pgm.cpp main.cpp -lglut -lGLU -lGL -lm -lGLEW -o test
  </code></pre>
  
  <p>Also, forgive me for using C++ but doing C style tricks. I spend more time in C than I do C++.</p>
  
  <p>main.cpp</p>
  
  <pre><code>// Include standard headers
  #include &lt;stdio.h&gt;
  #include &lt;stdlib.h&gt;
  #include &lt;stdarg.h&gt;
  #include &lt;math.h&gt;
  #include &lt;errno.h&gt;
  
  //For stat
  #include &lt;sys/types.h&gt;
  #include &lt;sys/stat.h&gt;
  #include &lt;unistd.h&gt;
  
  // Include GLEW
  #include &lt;GL/glew.h&gt;
  
  //Glut
  #include &lt;GL/glut.h&gt;
  
  //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, &amp;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&gt;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, &amp;computeSource, NULL);
      glCompileShader(computeShader);
  
  
      computeProgram = glCreateProgram();
      glAttachShader(computeProgram, computeShader);
      glLinkProgram(computeProgram);
      GLint status;
      glGetProgramiv(computeProgram, GL_LINK_STATUS, &amp;status);
  
      if (status == GL_TRUE)
      {
          printf("link good\n");
      }
      else
      {
          printf("link bad\n");
          GLchar log[4096];
          GLsizei len;
  
          glGetProgramInfoLog(computeProgram, 4096, &amp;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 &amp;&amp; i &lt; 10); //Using i &lt; 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 &amp; texture init
  
      GLuint vao;
      glGenVertexArrays(1, &amp;vao);
      glBindVertexArray(vao);
  
      GLuint vbo;
      glGenBuffers(1, &amp;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, &amp;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, &amp;vertexSource, NULL);
      glCompileShader(vertexShader);
  
      GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
      glShaderSource(fragmentShader, 1, &amp;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 &lt; size; i++)
      {
          data[i] = pgmData[i];
      }
  
      int count = 0;
      for (int i =0; i &lt; 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(&amp;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;
  
  
  }
  </code></pre>
  
  <p>compute.shader</p>
  
  <pre><code>#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) -&gt; A8
      // In image, A0 is center
      // 1 2 3
      // 8 0 4
      // 7 6 5
  
      for (i=1; i &lt; 9; i++)
      {
          sigma += int(data[i]);
      }
  
      return sigma;
  }
  
  
  int chi(uint data[9]) {
      int chi;
  
      // Assume 9 pixels, A0 (pixel of interest) -&gt; 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] &gt; data[1]) &amp;&amp; (data[2] &gt; data[3])) ) +
            int((data[4] &gt; data[3]) &amp;&amp; (data[4] &gt; data[5])) +
            int((data[6] &gt; data[5]) &amp;&amp; (data[6] &gt; data[7])) +
            int((data[8] &gt; data[7]) &amp;&amp; (data[8] &gt; 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 &amp;&amp; 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 &lt; 9; i++) {
         neighborhood[i] = 2;
      }
  
      if (pos &lt; 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 &gt;= bottom) {
          // Pixel is on bottom edge.
          neighborhood[5] = 0;
          neighborhood[6] = 0;
          neighborhood[7] = 0;
      }
  
      // Get remaining pixels
      for (i=1; i &lt; 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 &amp;&amp; neighborhood[6] == 1) {
                  removePoint(neighborhood);
              }
              break;
          case CMD_THIN_S:
              //south
              if (neighborhood[2] == 1 &amp;&amp; neighborhood[6] == 0) {
                  removePoint(neighborhood);
              }
              break;
          case CMD_THIN_E:
              //east
              if (neighborhood[4] == 0 &amp;&amp; neighborhood[8] == 1) {
                  removePoint(neighborhood);
              }
              break;
          case CMD_THIN_W:
              //west
              if (neighborhood[4] == 1 &amp;&amp; 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;
      }
  }
  </code></pre>
  
  <p>pgm.cpp</p>
  
  <pre><code>#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 &lt; size; i++)
      {
          if (mpData[i] == '#') 
          { 
              comment = true;
              continue;
          }
  
          if (mpData[i] == 0x0A &amp;&amp; comment == true)
          {
              comment = false;
              break;
          }
  
          if (comment == true) 
          { 
              continue; 
          }
  
      }
  
      // Get width and height
      i++;
      sscanf((char *)&amp;mpData[i], "%4" SCNu16 " %4" SCNu16, &amp;mWidth, &amp;mHeight);
  
      for (i; i &lt; size; i++)
      {
          //Move past the width and height we just found
          if (mpData[i] == 0x0A &amp;&amp; EOL == false)
          {
              EOL = true;
              continue;
          }
  
          // If past the width and height, now at the range. Save it.
          if (EOL == true &amp;&amp; haveWhite == false)
          {
              sscanf((char *)&amp;mpData[i], "%3" SCNu8, &amp;mMaxWhite);
              haveWhite = true;
          }
  
          if (haveWhite == true &amp;&amp; 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 = &amp;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-&gt;mpData != NULL)
      {
          Clear();
      }
  
      if (stat(pPath, &amp;st) != 0)
      {
          return 1;
      }
  
      fd = open(pPath, O_RDONLY);
  
      if (fd == -1)
      {
          return 1;
      }
  
      //this-&gt;mpData = (uint8_t *) malloc(st.st_size);
      mpData = new uint8_t[st.st_size];
  
      if (this-&gt;mpData == NULL)
      {
          return 2;
      }
  
      if (read(fd, this-&gt;mpData, st.st_size) == -1)
      {
          Clear();
      }
  
      close(fd);
  
      PopulateFields(st.st_size);
  
      return 0;
  }
  </code></pre>
  
  <p>pgm.hpp</p>
  
  <pre><code>#ifndef __PGM_H__
  #define __PGM_H__
  
  #include &lt;stdint.h&gt;
  #include &lt;stdio.h&gt;
  #include &lt;stdlib.h&gt;
  #include &lt;string.h&gt;
  #include &lt;sys/types.h&gt;
  #include &lt;sys/stat.h&gt;
  #include &lt;fcntl.h&gt;
  #include &lt;unistd.h&gt;
  #include &lt;errno.h&gt;
  #include &lt;inttypes.h&gt;
  
  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__
  </code></pre>