Appending a vector member variable to a buffer layout for VboMesh

Hello all,

I’m trying to find the correct syntax (or a better method) for passing an array/vector that is a member of a struct I’m using to create a VboMesh. I’m mapping the Here’s the use case:

Simple object:

struct Line {
    ci::vec2 mPos;
    ci::ColorA mColor;
    float mWidth;

Create a buffer layout to map data to attributes:

geom::BufferLayout LineBufferLayout;
LineBufferLayout.append(geom::Attrib::POSITION,	2, sizeof(Line), offsetof(Line, mPos));
LineBufferLayout.append(geom::Attrib::COLOR,	4, sizeof(Line), offsetof(Line, mColor));
LineBufferLayout.append(geom::Attrib::CUSTOM_0, 1, sizeof(Line), offsetof(Line, mWidth));

Create VboMesh with that Layout (nevermind the GL_POINTS designation), and declare the custom attribute when creating the batch:

auto vboMesh = gl::VboMesh::create(mLineList.size(), GL_POINTS, { { LineBufferLayout, mVbo } });
mLineBatch = gl::Batch::create(vboMesh, mSharedShader, { { geom::CUSTOM_0, "iLineWidth" } });

This all works great for previous examples of passing in a single vertex for the base object. However, I need Line to contain a vector of all of the vertices that will be on that line to pass to the VBO so I can access them in the geometry shader:

struct Line {
    std::vector<ci::vec2> mVerts;
    ci::ColorA mColor;
    float mWidth;

Does BufferLayout support passing an array of values as an attribute (geom::POSITION or geom::CUSTOM)? I can’t seem to find the right syntax. Do I have to use the lower level mVbo->bufferSubData() methods to do a more custom mapping?

You could use an SSBO to access all the vertices in the shader.

I’m not super familiar with SSBOs but it seems like they’re just a pool of data that can be passed to the GPU rather than passing the data on a per-instance level, no?

I could bind half a dozen vertices to the custom attributes CUSTOM_1/2/3/4/5/etc. but that seems clumsy at best.

Binding the vector to a custom attribute like so:

LineBufferLayout.append(geom::Attrib::CUSTOM_0, 2, sizeof(TravelingLine), offsetof(TravelingLine, mVerts));
mLineBatch = gl::Batch::create(vboMesh, mSharedShader, { { geom::CUSTOM_0, "iVerts" } });

Yields the error:

cinder::gl::VboMesh::buildVao[500] Batch GlslProg expected an Attrib of USER_DEFINED, with name iVerts[0] but vertex data doesn't provide it.

How many vertices are we talking about?

A vector can grow or shrink in size dynamically, which internally is implemented as a pointer to some far off memory, along with a few bookkeeping variables. The size of your struct would be fixed, but the data it encapsulates isn’t and so you would not be able to upload it that way.

However, you can decide to have a fixed number of vertices in there, something like:

struct Line {
    ci::vec2  mPos;          // (attribute 0)
    float     mWidth;
    uint32_t  mSize;         // the number of vertices in use
    ci:ColorA mColor;        // (attribute 1)
    ci::vec2  mVertices[14]; // (attribute 2...15)

(note: the order in which I have specified the member variables is compatible with the std140 layout rules. This is true as long as you have an even number of vertices).

There is a limit to the number of attributes a vertex shader supports, but I believe it’s a minimum of 16. That gives you 14 vertices per line to work with.


Right, seems obvious now :man_facepalming: The fixed size array did the trick along with declaring the custom attribute name in array style.

{ geom::CUSTOM_0, "iVertices[0]" }

Interesting note however: passing the array of vertices still only counts as a single attribute! I was able to pass in around 25 vertices in the array and still be able to pass in other custom attributes before the shader complains about space allocation.