Understanding std430 vs std140 Layout in GLSL

In OpenGL Shading Language (GLSL), memory layouts dictate how data inside Uniform Buffer Objects (UBOs) and Shader Storage Buffer Objects (SSBOs) is aligned in GPU memory. This article explains how the std430 layout specification optimizes memory usage compared to std140, specifically focusing on how it eliminates aggressive 16-byte structure and array padding to allow tighter, more CPU-friendly memory packing.

The std140 Padding Problem

The std140 standard was introduced primarily for Uniform Buffer Objects to ensure hardware-agnostic cross-platform alignment. To guarantee efficient access across diverse GPU architectures, std140 enforces a strict 16-byte (vec4) base alignment rule on arrays and nested structures:

  1. Array Element Alignment: Every element in an array—even an array of simple scalars like float or int—has its base alignment and array stride rounded up to the base alignment of a vec4 (16 bytes).
  2. Structure Alignment: The size of any structure, as well as the stride between elements in an array of structures, is rounded up to a multiple of 16 bytes.

Under std140, an array declared as:

layout(std140) buffer DataBuffer {
    float values[4];
};

requires 64 bytes of memory instead of 16 bytes. Each float takes 4 bytes of data followed by 12 bytes of dead padding.

Similarly, an array of structures containing two floats:

struct Particle {
    float mass;
    float lifetime;
};

layout(std140) buffer ParticleBuffer {
    Particle particles[2];
};

requires 32 bytes total. Even though Particle only contains 8 bytes of actual data, std140 pads each instance to 16 bytes to satisfy the vec4 minimum stride requirement.

How std430 Eliminates Structure and Array Padding

The std430 layout qualifier, available exclusively for Shader Storage Buffer Objects (SSBOs), eliminates the mandatory 16-byte round-up rule. Instead, it adopts alignment rules that closely mirror standard C and C++ data structure alignment.

1. Natural Alignment for Scalar and Vector Arrays

In std430, the base alignment and array stride of an array are set directly to the natural base alignment of the underlying type, rather than rounding up to a vec4:

  • An array of float or int values has an alignment and stride of 4 bytes.
  • An array of vec2 values has an alignment and stride of 8 bytes.
  • An array of vec3 or vec4 values has an alignment and stride of 16 bytes.

Because the stride of float values[4] is 4 bytes in std430, the array occupies exactly 16 contiguous bytes without unused padding between elements.

2. Tighter Structure Stride

Under std430, the base alignment of a structure is equal to the largest base alignment of any of its members, rather than being forced to a minimum of 16 bytes. The size of the structure (and thus its stride in an array) is simply rounded up to a multiple of its own base alignment.

Using the previous Particle structure:

struct Particle {
    float mass;     // offset: 0, size: 4
    float lifetime; // offset: 4, size: 4
};

layout(std430) buffer ParticleBuffer {
    Particle particles[2]; // stride: 8 bytes
};
  1. The largest member in Particle is a float (4-byte alignment).
  2. The structure's base alignment becomes 4 bytes.
  3. The total size is 8 bytes, which is already a multiple of 4.
  4. The array stride becomes 8 bytes instead of being inflated to 16 bytes.

Practical Advantages of std430

  • Reduced Memory Footprint: Eliminating redundant padding can reduce buffer memory consumption by up to 75% for scalar arrays and compact structures.
  • Direct CPU-to-GPU Serialization: In C and C++, tightly packed structs match the std430 memory layout natively. Developers can map or upload standard CPU structs directly via glBufferData or glBufferSubData without writing custom serialization layers or inserting manual dummy padding fields.
  • Cache Efficiency: Packaged data improves GPU cache locality by preventing unused padding bytes from occupying cache lines during shader read and write operations.