How unpackHalf2x16 Works in GLSL
The GLSL built-in function unpackHalf2x16 unpacks a
single 32-bit unsigned integer into a two-component 32-bit
floating-point vector (vec2). It achieves this by treating
the input integer as two contiguous 16-bit chunks, interpreting each
chunk as an IEEE 754 half-precision floating-point number (binary16),
and converting each into a standard 32-bit single-precision float
(binary32). This process is essential in real-time graphics for
decompressing packed data such as UV coordinates, normals, or
half-precision framebuffers to save memory bandwidth.
Bit Splitting
The function takes a single uint v as its parameter. The
32-bit integer is divided into two separate 16-bit bitfields:
- Component X (Least Significant Bits): Extracted
from bits 0 through 15 (
v & 0xFFFFu). - Component Y (Most Significant Bits): Extracted from
bits 16 through 31 (
v >> 16u).
The 16-Bit Float Layout (binary16)
Each extracted 16-bit value conforms to the IEEE 754 binary16 standard, structured as:
- 1 Sign Bit (Bit 15): Determines if the value is positive (0) or negative (1).
- 5 Exponent Bits (Bits 10–14): Encodes the exponent with a bias of 15.
- 10 Mantissa/Fraction Bits (Bits 0–9): Encodes the fractional part with an implicit leading bit of 1 for normalized numbers.
Conversion to 32-Bit Float (binary32)
To produce standard GLSL float values for the returned
vec2, the hardware converts each 16-bit float into a 32-bit
float (1 sign bit, 8 exponent bits with a bias of 127, and 23 mantissa
bits):
- Sign Bit: The 1-bit sign is copied directly to bit 31 of the target 32-bit float.
- Exponent: The 5-bit exponent is re-biased. Because the 16-bit bias is 15 and the 32-bit bias is 127, the exponent is adjusted by adding the difference (\(127 - 15 = 112\)) to normalized exponents.
- Mantissa: The 10-bit mantissa is shifted left into the higher bits of the 23-bit target mantissa, filling the lower 13 bits with zeros.
Special Cases Handling
The conversion process automatically handles IEEE 754 floating-point edge cases:
- Zero: If both the exponent and mantissa are zero,
the result becomes a positive or negative 32-bit zero (
+0.0or-0.0). - Subnormal (Denormalized) Numbers: If the exponent is zero and the mantissa is non-zero, the value is normalized during conversion or flushed to zero depending on the GPU's floating-point handling mode.
- Infinities and NaNs: If the 5-bit exponent is all
ones (
0x1F), it is converted to a 32-bit infinity (if the mantissa is zero) or a 32-bit NaN (if the mantissa is non-zero).
Result Output
Once both the lower and upper 16-bit segments are transformed into standard 32-bit floats, the function returns them combined as:
vec2 result;
result.x = convertHalfToFloat(uint16_lower);
result.y = convertHalfToFloat(uint16_upper);This operation runs directly on GPU hardware using dedicated bit-manipulation and unpacking instructions, ensuring minimal runtime overhead.