0

I use a rendering loop as follow;

  1. Orphan the data and map the buffer.
  2. Record the command and write the generated vertex in the buffer.
  3. Unmap the buffer.
  4. Iterate over the commands that can change states, bind textures or draw.

At the moment I use a single interleaved vertex format (SoA) that has all the attribute any of my shaders can use.

struct OneSizeFitAllVertex
{
    float pos[3];
    float uv0[2];
    float uv1[2];
    float col[4];
};

When using simpler shader that only use the position and color for example, I would only write the attribute that I care about in the mapped memory and the shader code would simply ignore all the unused attributes.

Because this feel wasteful, I am considering using different vertex format for each of my shaders.

Simple objects rendered using the simple shader would use SimpleVertex:

struct SimpleVertex
{
    float pos[3];
    float col[4];
};

While others, multi-textured objects, would be rendered using multitexture shader and use MultitextureVertex:

struct MultitextureVertex
{
    float pos[3];
    float uv0[2];
    float uv1[2];
}; 

How should I handle these different format?

Should I write all the vertex of different format in the same mapped buffer and change my AttribPointers before drawing? This would save some space.

Should I map a different buffers for each vertex formats? Perhaps more efficient.

Or should I keep the 'one size fit all' vertex format? This is easier.

I am curious to learn what is the best practice in this situation. Thanks.

Regular Guy
  • 81
  • 1
  • 2
  • Aside from my favorite recommendation of "whatever makes your life the easiest and gets the job done right the quickest", if moving to multiple vertex structures would cause you to duplicate data (e.g., you'd use the same position values in two separate arrays), that would be bad. One thing that's unclear is if you are using different shaders on the same data (like if you have an object, and switch between different appearances by switching shaders), or each shader has its own data. However, if all your shaders are using data from the same array, your approach is the simplest. – radical7 Jun 07 '17 at 01:35

1 Answers1

0

There can be a lot of variations based on your underlying system architecture, but let's assume you're using a discrete GPU (e.g., AMD or NVIDIA) with dedicated graphics memory.

You don't mention if you interleave your attributes in an array of structures (AoS) (perhaps something like the following):

struct Vertex {
    float position[3];
    float normal[3];
    float uv[2];
    ...
};

or group similar attributes together (commonly called a structure of arrays or (SoA))

struct VertexAttributes {
    float positions[N][3];
    float normals[N][3];
    float uv[N][2];
    ...
};

This is relevant given you're buffer-mapping approach. When you map in an entire buffer, the GPU likely needs to copy its version of the buffer to the CPU, who provides you the pointer to update the values. When you unmap the buffer, the driver will copy the buffer back to the GPU. With the AoS layout and your technique, you'll touch small subsections of the entire buffer, and since the GPU driver doesn't know which bits of memory you've updated, it's only recourse is to copy the entire thing back to the GPU. Depending on the size, this can have significant impact at several levels (poor CPU cache-read utilization, consuming lots of CPU-to-GPU bus bandwidth, etc.). Unfortunately, there aren't good alternatives if you're only updating a small fraction of your vertex attributes. However, if you're updating all of the attributes, this approach is okay (although it's often recommended to use glBufferSubData or similar commands, which save the read back from the GPU to the CPU).

Conversely, if you're using the SoA approach, mapping the entire buffer in will cause the similar problems, but the situation can be better. Since the values for a specific attribute are contiguous in memory, you can use something like glMapBufferRange to only map in the memory you need (but again, I'd still recommend using glBufferSubData for the previously stated reasons). Given your current scenario, this is what I'd recommend.

radical7
  • 8,957
  • 3
  • 24
  • 33
  • I am using AoS, but this is beside the point. My question is about what i should do if i have program that use *struct vert_uv { float pos[3]; float uv[2] };* and *struct vert_col { float pos[3]; float col[4] };*? Sorry for the poor explanation, i will try to update my question when i figure out a way to express it more clearly. – Regular Guy Jun 06 '17 at 19:48