How GLSL bitfieldInsert Replaces Bit Segments

The built-in GLSL function bitfieldInsert allows developers to pack and manipulate data at the bit level by overwriting a specific range of bits within a target integer using bits from a source integer. This guide explains the function's syntax, its internal bit-replacement mechanics, and how it executes across both scalar integers and vector types.

Function Syntax and Parameters

In GLSL, bitfieldInsert is overloaded for signed and unsigned 32-bit integers, supporting both scalars and vectors:

genIType bitfieldInsert(genIType base, genIType insert, int offset, int bits);
genUType bitfieldInsert(genUType base, genUType insert, int offset, int bits);

The function accepts four arguments:

  • base: The original integer scalar or vector that receives the new bits.
  • insert: The integer scalar or vector containing the bits to be inserted.
  • offset: The bit index where the insertion begins, counted from the least significant bit (bit 0).
  • bits: The total count of bits to copy from insert into base.

The Replacement Mechanism

The function extracts the lowest bits from insert (from bit 0 to bits - 1) and writes them into base starting at offset through offset + bits - 1. All bits in base outside of this specified range remain unchanged.

Conceptually, bitfieldInsert executes the following bitwise operations:

  1. A bitmask of width bits is created: mask = (1 << bits) - 1.
  2. The relevant bits from insert are isolated and shifted to the destination position: (insert & mask) << offset.
  3. The target region in base is cleared: base & ~(mask << offset).
  4. The cleared base and the shifted insert bits are combined with a bitwise OR operation.

If bits is equal to 0, the function leaves base completely unmodified. If offset < 0, bits < 0, or offset + bits > 32, the result is undefined by the GLSL specification.

Scalar vs. Vector Processing

When applied to scalar types (int or uint), bitfieldInsert operates on a single 32-bit sequence.

When applied to vector types (ivec2, uvec3, ivec4, etc.), the replacement occurs component-wise:

  • The offset and bits parameters remain scalar integers that define the same range for every component.
  • For each component \(i\), the lowest bits of insert[i] overwrite the bit range [offset, offset + bits - 1] of base[i].

Practical Example

// Scalar example
uint baseValue = 0x00000000u;
uint insertValue = 0x0000000Fu; // 4 ones: 1111 in binary
int offset = 4;
int bitCount = 4;

// Inserts the lowest 4 bits of insertValue into baseValue starting at bit 4
uint resultScalar = bitfieldInsert(baseValue, insertValue, offset, bitCount);
// resultScalar is 0x000000F0u

// Vector example
uvec2 baseVec = uvec2(0xFFFFFFFFu, 0x00000000u);
uvec2 insertVec = uvec2(0x00000000u, 0xFFFFFFFFu);

// Overwrites bits 8 through 15 across both vector components
uvec2 resultVec = bitfieldInsert(baseVec, insertVec, 8, 8);
// resultVec.x becomes 0xFFFF00FFu
// resultVec.y becomes 0x0000FF00u