GLSL uaddCarry: Unsigned Addition with Carry
The GLSL built-in function uaddCarry adds two 32-bit
unsigned integers or vectors while simultaneously calculating whether an
arithmetic overflow occurred. Instead of discarding the excess
high-order bit, uaddCarry outputs the standard modulo \(2^{32}\) sum as its return value and writes
the resulting overflow (the carry-out bit) into an out
parameter. This mechanism enables precise multi-word arithmetic on GPUs,
allowing shaders to execute extended-precision integer calculations that
exceed standard 32-bit register capacities.
Function Signature and Mechanism
The standard declaration for uaddCarry in GLSL supports
both scalar and vector types:
genUType uaddCarry(genUType x, genUType y, out genUType carry);Here, genUType represents uint,
uvec2, uvec3, or uvec4.
When the function executes, it performs addition on the unsigned
arguments x and y:
- Sum Evaluation: The primary operation calculates \((x + y) \pmod{2^{32}}\). This 32-bit truncated sum is returned directly by the function.
- Carry Evaluation: If the mathematical sum of \(x\) and \(y\) is greater than or equal to \(2^{32}\) (equivalent to
0x100000000u), an arithmetic overflow has occurred. The function assigns1uto thecarryvariable. If no overflow occurs,carryis set to0u.
Component-Wise Processing
When used with vector types (uvec2, uvec3,
uvec4), uaddCarry operates strictly
component-wise. The addition and carry generation for each vector lane
occur independently:
uvec2 a = uvec2(0xFFFFFFFFu, 5u);
uvec2 b = uvec2(1u, 10u);
uvec2 carry;
uvec2 result = uaddCarry(a, b, carry);
// result is uvec2(0u, 15u)
// carry is uvec2(1u, 0u)In the first lane, 0xFFFFFFFFu + 1u overflows, producing
a sum of 0u and setting the first carry component to
1u. In the second lane, 5u + 10u yields
15u without overflowing, setting the second carry component
to 0u.
Emulating Multi-Word Precision
The primary practical application of uaddCarry is
emulating 64-bit or larger arithmetic in environments without native
64-bit integer support, such as cryptographic hashing algorithms or
high-precision counters.
To add two 64-bit values represented by two uvec2
structures (where the x component is the low 32 bits and
the y component is the high 32 bits),
uaddCarry chains the lower-half overflow into the
upper-half calculation:
uvec2 add64(uvec2 numA, uvec2 numB) {
uint carry;
uvec2 sum;
// Add lower 32-bit words and extract carry
sum.x = uaddCarry(numA.x, numB.x, carry);
// Add upper 32-bit words along with the carry from the lower addition
sum.y = numA.y + numB.y + carry;
return sum;
}Because modern graphics hardware includes dedicated carry-generation
logic in its arithmetic logic units (ALUs), uaddCarry
typically compiles into single, native machine instructions rather than
multi-step branches or software comparisons, maintaining high shader
execution performance.