title: How do I draw vertices that are stored in a SSBO?
body: <p>This is a question following <a href="https://stackoverflow.com/questions/59616117/opengl-and-loading-reading-data-in-aosoa-hybrid-soa-format">OpenGL and loading/reading data in AoSoA (hybrid SoA) format</a>.</p>
  
  <p>I am trying to use a shader storage buffer object (SSBO) to store vertex data which is represented in <a href="https://en.wikipedia.org/wiki/AoS_and_SoA#Array_of_Structures_of_Arrays" rel="nofollow noreferrer">AoSoA</a> format. I am having trouble drawing the vertices, which obviously means that I am doing something wrong somewhere. The problem is that I can't seem to figure out what or where. The answer to the initial question above seems to indicate that I should not be using vertex attribute arrays, so the question then becomes, how do I render this SSBO, given the code I am about to present?</p>
  
  <p><strong>VertexData structure</strong></p>
  
  <pre class="lang-cpp prettyprint-override"><code>constexpr auto VECTOR_WIDTH = 4;
  constexpr auto VERTEX_COUNT = 16;
  
  struct VertexData
  {
      std::array&lt;float, VECTOR_WIDTH&gt; px;
      std::array&lt;float, VECTOR_WIDTH&gt; py;
  };
  // Later stored in a std::vector
  std::vector&lt;VertexData&gt; vertices(VERTEX_COUNT / VECTOR_WIDTH);
  </code></pre>
  
  <p><strong>Vertex shader</strong> (should this really be a compute shader?)</p>
  
  <pre class="lang-cpp prettyprint-override"><code>struct Vertex4
  {
      float px[4]; // position x
      float py[4]; // position y
  };
  
  layout(std430, binding=0) buffer VertexData
  {
      Vertex4 vertices[];
  };
  
  void main()
  {
    int dataIx = gl_VertexID / 4;
    int vertexIx = gl_VertexID % 4;
    vec2 vertexPosition = vec2(vertices[dataIx].px[vertexIx], vertices[dataIx].py[vertexIx]);
  }
  </code></pre>
  
  <p><strong>Assign <code>vertexPosition</code> index</strong></p>
  
  <pre class="lang-cpp prettyprint-override"><code>// Do I need this? Where do I use it? glEnableVertexAttribArray(position_attrib_index)?
  const GLuint position_attrib_index = 0;
  glBindAttribLocation(program, position_attrib_index, "vertexPosition");
  </code></pre>
  
  <p><strong>SSBO setup</strong></p>
  
  <pre class="lang-cpp prettyprint-override"><code>const GLuint ssbo_binding_point = 0;
  GLuint ssbo{};
  glGenBuffers(1, &amp;ssbo);
  glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo);
  //glBufferStorage(GL_SHADER_STORAGE_BUFFER, vertices.size() * sizeof(VertexData), vertices.data(), GL_MAP_WRITE_BIT);
  glBufferData(GL_SHADER_STORAGE_BUFFER, vertices.size() * sizeof(VertexData), vertices.data(), GL_STATIC_DRAW);
  const auto block_index = glGetProgramResourceIndex(program, GL_SHADER_STORAGE_BLOCK, "VertexData");
  glShaderStorageBlockBinding(program, block_index, ssbo_binding_point);
  glBindBufferBase(GL_SHADER_STORAGE_BUFFER, ssbo_binding_point, ssbo);
  glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
  </code></pre>
  
  <p><strong>Render loop</strong></p>
  
  <pre class="lang-cpp prettyprint-override"><code>while (!glfwWindowShouldClose(window)) {
      process_input(window);
      glClear(GL_COLOR_BUFFER_BIT);
  
      glUseProgram(program);
  
      // ???
  
      glfwSwapBuffers(window);
      glfwPollEvents();
  }
  </code></pre>
  
  <hr>
  
  <p>I just can't seem to figure out how this is supposed to work. Grabbing at straws, I also tried creating a VAO with a later call to <code>glDrawArrays(GL_POINTS, 0, VERTEX_COUNT)</code>, but it didn't work either:</p>
  
  <pre class="lang-cpp prettyprint-override"><code>GLuint vao{};
  glGenVertexArrays(1, &amp;vao);
  glBindVertexArray(vao);
  glEnableVertexAttribArray(position_attrib_index);
  glVertexAttribPointer(position_attrib_index, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
  </code></pre>
  
  <p>It seems to me that I should be using <code>position_attrib_index</code> (which should point to <code>vertexPosition</code>) for something, the question is for what?</p>
  
  <p><strong>Complete example code</strong></p>
  
  <ul>
  <li>requires OpenGL 4.3, <a href="http://glew.sourceforge.net/" rel="nofollow noreferrer">GLEW</a> and <a href="https://www.glfw.org/" rel="nofollow noreferrer">GLFW</a></li>
  <li>build command example: <code>g++ -std=c++17 main.cpp -lGLEW -lglfw -lGL -o ssbo</code></li>
  </ul>
  
  
  
  <pre><code>#include &lt;GL/glew.h&gt;
  #include &lt;GLFW/glfw3.h&gt;
  #include &lt;array&gt;
  #include &lt;iostream&gt;
  #include &lt;vector&gt;
  
  void process_input(GLFWwindow *window)
  {
      if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) {
          glfwSetWindowShouldClose(window, true);
      }
  }
  
  void glfw_error_callback(int error_code, const char *description)
  {
      std::cerr &lt;&lt; "GLFW Error: [" &lt;&lt; error_code &lt;&lt; "] " &lt;&lt; description &lt;&lt; '\n';
  }
  
  void framebuffer_size_callback(GLFWwindow *window, int width, int height)
  {
      glViewport(0, 0, width, height);
  }
  
  auto create_glfw_window()
  {
      glfwInit();
      glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
      glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
      glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
      return glfwCreateWindow(800, 600, "OpenGL and AoSoA layout", nullptr, nullptr);
  }
  
  void set_callbacks(GLFWwindow *window)
  {
      glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
      glfwSetErrorCallback(glfw_error_callback);
  }
  
  void print_versions()
  {
      std::cout &lt;&lt; "Using GLFW " &lt;&lt; glfwGetVersionString() &lt;&lt; '\n';
      std::cout &lt;&lt; "Using GLEW " &lt;&lt; glewGetString(GLEW_VERSION) &lt;&lt; '\n';
  }
  
  bool init_loader()
  {
      GLenum err = glewInit();
      if (GLEW_OK != err) {
          std::cerr &lt;&lt; "GLEW error: " &lt;&lt; glewGetErrorString(err);
      }
      return err == GLEW_OK;
  }
  
  void GLAPIENTRY MessageCallback(
      GLenum source,
      GLenum type,
      GLuint id,
      GLenum severity,
      GLsizei length,
      const GLchar* message,
      const void* userParam = nullptr)
  {
      std::cerr &lt;&lt; "[GL DEBUG] " &lt;&lt; (type == GL_DEBUG_TYPE_ERROR ? "Error: " : "") &lt;&lt; message &lt;&lt; '\n';
  }
  
  constexpr auto VECTOR_WIDTH = 4;
  constexpr auto VERTEX_COUNT = 16;
  
  struct VertexData
  {
      std::array&lt;float, VECTOR_WIDTH&gt; px;
      std::array&lt;float, VECTOR_WIDTH&gt; py;
  };
  
  static const char* vertex_shader_source =
      "#version 430\n"
      "struct Vertex4\n"
      "{\n"
      "    float px[4]; // position x\n"
      "    float py[4]; // position y\n"
      "};\n"
      "layout(std430, binding=0) buffer VertexData\n"
      "{\n"
      "    Vertex4 vertices[];\n"
      "};\n"
      "void main()\n"
      "{\n"
      "  int dataIx = gl_VertexID / 4;\n"
      "  int vertexIx = gl_VertexID % 4;\n"
      "  vec2 vertexPosition = vec2(vertices[dataIx].px[vertexIx], vertices[dataIx].py[vertexIx]);\n"
      "}\n";
  
  static const char* fragment_shader_source =
      "#version 430\n"
      "out vec4 out_color;\n"
      "void main()\n"
      "{\n"
      "    out_color = vec4(1.0, 0.5, 0.5, 0.25);\n"
      "}\n";
  
  int main(int argc, char *argv[])
  {
      glewExperimental = GL_TRUE;
  
      auto window = create_glfw_window();
  
      if (window == nullptr) {
          std::cout &lt;&lt; "Failed to create GLFW window" &lt;&lt; std::endl;
          glfwTerminate();
          return -1;
      }
  
      glfwMakeContextCurrent(window);
  
      set_callbacks(window);
      init_loader();
      print_versions();
  
      glEnable(GL_DEBUG_OUTPUT);
      glDebugMessageCallback(MessageCallback, nullptr);
  
      std::vector&lt;VertexData&gt; vertices(VERTEX_COUNT / VECTOR_WIDTH);
  
      vertices[0] = {
          {-0.75f, 0.75f, 0.75f, -0.75f},
          {-0.75f, -0.75f, 0.75f, 0.75f}
      };
      vertices[1] = {
          {-0.50f, 0.50f, 0.50f, -0.50f},
          {-0.50f, -0.50f, 0.50f, 0.50f},
      };
      vertices[2] = {
          {-0.25f, 0.25f, 0.25f, -0.25f},
          {-0.25f, -0.25f, 0.25f, 0.25f},
      };
      vertices[3] = {
          {-0.05f, 0.05f, 0.05f, -0.05f},
          {-0.05f, -0.05f, 0.05f, 0.05f},
      };
  
      auto vertex_shader = glCreateShader(GL_VERTEX_SHADER);
      glShaderSource(vertex_shader, 1, &amp;vertex_shader_source, nullptr);
      glCompileShader(vertex_shader);
  
      auto fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
      glShaderSource(fragment_shader, 1, &amp;fragment_shader_source, nullptr);
      glCompileShader(fragment_shader);
  
      auto program = glCreateProgram();
      glAttachShader(program, vertex_shader);
      glAttachShader(program, fragment_shader);
  
      const GLuint position_attrib_index = 0;
      glBindAttribLocation(program, position_attrib_index, "vertexPosition");
  
      glLinkProgram(program);
  
      //glUseProgram(program);
  
      glDeleteShader(vertex_shader);
      glDeleteShader(fragment_shader);
  
      //
      // SSBO
      //
      const GLuint ssbo_binding_point = 0;
      GLuint ssbo{};
      glGenBuffers(1, &amp;ssbo);
      glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo);
      //glBufferStorage(GL_SHADER_STORAGE_BUFFER, vertices.size() * sizeof(VertexData), vertices.data(), GL_MAP_WRITE_BIT);
      glBufferData(GL_SHADER_STORAGE_BUFFER, vertices.size() * sizeof(VertexData), vertices.data(), GL_STATIC_DRAW);
      const auto block_index = glGetProgramResourceIndex(program, GL_SHADER_STORAGE_BLOCK, "VertexData");
      glShaderStorageBlockBinding(program, block_index, ssbo_binding_point);
      glBindBufferBase(GL_SHADER_STORAGE_BUFFER, ssbo_binding_point, ssbo);
      glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
  
      //
      // VAO
      //
      //GLuint vao{};
      //glGenVertexArrays(1, &amp;vao);
      //glBindVertexArray(vao);
      //glEnableVertexAttribArray(position_attrib_index);
      //glVertexAttribPointer(position_attrib_index, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
  
      glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
      glEnable(GL_BLEND);
      glClearColor(0.15f, 0.15f, 0.2f, 1.0f);
      glPointSize(10.0f);
  
      while (!glfwWindowShouldClose(window)) {
          process_input(window);
          glClear(GL_COLOR_BUFFER_BIT);
  
          glUseProgram(program);
          //glDrawArrays(GL_POINTS, 0, VERTEX_COUNT);
  
          glfwSwapBuffers(window);
          glfwPollEvents();
      }
  
      glfwDestroyWindow(window);
      glfwTerminate();
  
      return 0;
  }
  </code></pre>