Read 8-Bit Integers in GLSL via GL_EXT_shader_8bit_storage

The GL_EXT_shader_8bit_storage extension allows GLSL shaders to store and read 8-bit integers directly from byte-addressable storage buffers, significantly reducing memory bandwidth and eliminating the overhead of manual data packing. By introducing native 8-bit types directly into buffer layouts, shaders can perform scalar and vector byte-sized memory loads without relying on manual bit-shifting or bitmasking operations.

Enabling the Extension

To use 8-bit integers in storage buffers, the extension must be explicitly declared in the shader:

#version 450
#extension GL_EXT_shader_8bit_storage : require

If you also intend to perform arithmetic directly on 8-bit types rather than automatically promoting them to larger integer types, you will typically pair this with GL_EXT_shader_explicit_arithmetic_types_int8.

Defining the Storage Buffer

With the extension enabled, the types int8_t, uint8_t, and their vector variants (such as i8vec2, u8vec4) become valid inside uniform and shader storage buffer objects.

Under the standard layout qualifiers (such as std430), 8-bit values have a base alignment of 1 byte. This removes the requirement to pad data to 4-byte (32-bit) boundaries.

layout(std430, binding = 0) readonly buffer InputBuffer {
    uint8_t  singleByte;
    uint8_t  byteArray[];
};

Reading 8-Bit Values

Reading an 8-bit integer is syntactically identical to accessing standard 32-bit types. The hardware performs direct byte-addressed memory loads:

void main() {
    uint index = gl_GlobalInvocationID.x;

    // Direct scalar read
    uint8_t rawValue = byteArray[index];

    // Automatic type promotion
    uint convertedValue = uint(rawValue);
}

Under-the-Hood Mechanics

When an 8-bit read instruction executes:

  1. Direct Memory Addressing: Unlike standard GLSL storage buffers that require reading a 32-bit dword and manually extracting bytes using bitfieldExtract or shifts (>>), the GPU generates native byte-load instructions where supported by hardware.
  2. Alignment and Packing: Arrays of uint8_t are packed sequentially with single-byte stride. Vector types like u8vec4 are aligned to 4 bytes, occupying exactly 4 bytes in memory.
  3. Sign and Zero Extension: When reading signed (int8_t) or unsigned (uint8_t) types into wider registers (like int or uint), the underlying assembly automatically applies sign extension or zero extension, ensuring data integrity during type promotion.