// ============================================================================
// BLENDER'S HASH LAYER -- ONE TEXT, COMPILED AS BOTH GLSL AND C++.
//
// Ported from `source/blender/blenlib/intern/noise.cc` in the Blender tree at
// /storage/projects/code/blender-source, commit 61a36e08, BLENDER_VERSION 502.
// Structure and order follow that file deliberately, so the two can be diffed
// by eye when Blender moves. Names follow Blender's with a `bn_` prefix,
// because GLSL has no namespaces and a bare `hash` in the global scope of a
// concatenated density shader is an accident waiting for a second prelude.
//
// WHY IT IS ONE TEXT. Everything above this file inherits it: Voronoi's cell
// offsets are this hash, Perlin's lattice gradients are this hash, and
// Blender's White Noise Texture node is this hash and nothing else. A CPU copy
// and a GPU copy that agree by review are how ProcNodes_Hash2D shipped an iq
// float hash against an integer LCG -- the right shape, unrelated numbers, and
// an erosion chain above it tuned against a correlation that could not
// converge. There is no version of that failure available here, because there
// is only one implementation.
//
// It compiles three ways, exactly as erosion_filter.inc does:
// C++ blender_noise_cpp_shim.h supplies the GLSL types and builtins
// GPU src/gpu/blender_hash_glsl.gen.h, GENERATED at build time
// gate tests/test_hash_parity.cpp compiles both and compares uint-exactly
//
// ---------------------------------------------------------------------------
// THE SUBSET, AND THE TWO WAYS OUT OF IT
//
// This file may only use what GLSL and C++ spell identically. Two constructs
// in the original C++ are outside that subset and are written differently
// here. Both changes are shape, not arithmetic:
//
// 1. C++ REFERENCES. hash_bit_mix/final took `uint32_t &`. GLSL's `inout` is
// not the same thing, and erosion_filter.inc records the same decision:
// they take and RETURN a uvec3 instead.
//
// 2. THE BIT CAST. C++ needs memcpy (a union or reinterpret_cast is UB);
// GLSL has floatBitsToUint. The shim supplies floatBitsToUint over the
// memcpy, so this file names the GLSL builtin.
//
// ⚠ A BARE DECIMAL LITERAL IS A HAZARD IN THIS FILE. In GLSL `1.0` is a
// float; in C++ it is a DOUBLE, so `1.0 / float(x)` divides in double on one
// side and in float on the other, and the two results need not round to the
// same float. 1.0 and 2.0 survive it -- they are exactly representable, so
// narrowing them is exact -- but a reciprocal like 1/2147483647 does not.
// That is why bn_int_to_float_01 below writes `float(1)` and not `1.0`.
// ============================================================================
// GUARDED AS A WHOLE, because every texture node that stands on this hash
// emits it as a prelude and a prelude is emitted per node. GpuCompiler drops
// one it has already emitted by string comparison, but Math's helpers record
// why that is not enough on its own: when one prelude CONTAINS another,
// string-dedup either drops them or defines them twice depending on which
// node came first. GLSL has a preprocessor; let it do the work.
//
// Harmless on the C++ side, which includes this exactly once through
// blender_noise_cpp_shim.h.
#ifndef PN_BLENDER_HASH
#define PN_BLENDER_HASH
// The C++ side defines this as `inline`; GLSL leaves it empty. blender_noise.h
// reaches more than one translation unit, so without it the second one is a
// multiple-definition link error -- the trap erosion_cpp_shim.h already has,
// and the reason its debug_* statics can only live in noise_nodes.cpp.
#ifndef BN_FN
#define BN_FN
#endif
// --- Jenkins Lookup3, https://burtleburtle.net/bob/c/lookup3.c ---
BN_FN uint bn_hash_bit_rotate(uint x, uint k) {
return (x << k) | (x >> (32u - k));
}
BN_FN uvec3 bn_hash_bit_mix(uvec3 v) {
uint a = v.x, b = v.y, c = v.z;
a -= c; a ^= bn_hash_bit_rotate(c, 4u); c += b;
b -= a; b ^= bn_hash_bit_rotate(a, 6u); a += c;
c -= b; c ^= bn_hash_bit_rotate(b, 8u); b += a;
a -= c; a ^= bn_hash_bit_rotate(c, 16u); c += b;
b -= a; b ^= bn_hash_bit_rotate(a, 19u); a += c;
c -= b; c ^= bn_hash_bit_rotate(b, 4u); b += a;
return uvec3(a, b, c);
}
BN_FN uvec3 bn_hash_bit_final(uvec3 v) {
uint a = v.x, b = v.y, c = v.z;
c ^= b; c -= bn_hash_bit_rotate(b, 14u);
a ^= c; a -= bn_hash_bit_rotate(c, 11u);
b ^= a; b -= bn_hash_bit_rotate(a, 25u);
c ^= b; c -= bn_hash_bit_rotate(b, 16u);
a ^= c; a -= bn_hash_bit_rotate(c, 4u);
b ^= a; b -= bn_hash_bit_rotate(a, 14u);
c ^= b; c -= bn_hash_bit_rotate(b, 24u);
return uvec3(a, b, c);
}
BN_FN uint bn_hash(uint kx) {
uvec3 v = uvec3(0xdeadbeefu + (1u << 2u) + 13u);
v.x += kx;
v = bn_hash_bit_final(v);
return v.z;
}
BN_FN uint bn_hash(uint kx, uint ky) {
uvec3 v = uvec3(0xdeadbeefu + (2u << 2u) + 13u);
v.y += ky;
v.x += kx;
v = bn_hash_bit_final(v);
return v.z;
}
BN_FN uint bn_hash(uint kx, uint ky, uint kz) {
uvec3 v = uvec3(0xdeadbeefu + (3u << 2u) + 13u);
v.z += kz;
v.y += ky;
v.x += kx;
v = bn_hash_bit_final(v);
return v.z;
}
BN_FN uint bn_hash(uint kx, uint ky, uint kz, uint kw) {
uvec3 v = uvec3(0xdeadbeefu + (4u << 2u) + 13u);
v.x += kx;
v.y += ky;
v.z += kz;
v = bn_hash_bit_mix(v);
v.x += kw;
v = bn_hash_bit_final(v);
return v.z;
}
// The hash is over the float's BIT PATTERN, not its value.
BN_FN uint bn_hash_float(float k) { return bn_hash(floatBitsToUint(k)); }
BN_FN uint bn_hash_float(vec2 k) {
return bn_hash(floatBitsToUint(k.x), floatBitsToUint(k.y));
}
BN_FN uint bn_hash_float(vec3 k) {
return bn_hash(floatBitsToUint(k.x), floatBitsToUint(k.y), floatBitsToUint(k.z));
}
BN_FN uint bn_hash_float(vec4 k) {
return bn_hash(floatBitsToUint(k.x), floatBitsToUint(k.y), floatBitsToUint(k.z),
floatBitsToUint(k.w));
}
BN_FN float bn_uint_to_float_01(uint k) {
return float(k) / float(0xFFFFFFFFu);
}
// Hashing INTEGERS into [0, 1] -- Blender's `hash_to_float`, as against the
// `hash_float_to_float` family below which hashes the BIT PATTERNS of floats.
// Random Value uses these: its inputs are an integer id and an integer seed,
// not coordinates.
BN_FN float bn_hash_to_float(uint kx) { return bn_uint_to_float_01(bn_hash(kx)); }
BN_FN float bn_hash_to_float(uint kx, uint ky) {
return bn_uint_to_float_01(bn_hash(kx, ky));
}
BN_FN float bn_hash_to_float(uint kx, uint ky, uint kz) {
return bn_uint_to_float_01(bn_hash(kx, ky, kz));
}
BN_FN float bn_hash_to_float(uint kx, uint ky, uint kz, uint kw) {
return bn_uint_to_float_01(bn_hash(kx, ky, kz, kw));
}
BN_FN float bn_hash_float_to_float(float k) { return bn_uint_to_float_01(bn_hash_float(k)); }
BN_FN float bn_hash_float_to_float(vec2 k) { return bn_uint_to_float_01(bn_hash_float(k)); }
BN_FN float bn_hash_float_to_float(vec3 k) { return bn_uint_to_float_01(bn_hash_float(k)); }
BN_FN float bn_hash_float_to_float(vec4 k) { return bn_uint_to_float_01(bn_hash_float(k)); }
BN_FN vec2 bn_hash_float_to_float2(vec2 k) {
return vec2(bn_hash_float_to_float(k),
bn_hash_float_to_float(vec3(k.x, k.y, 1.0)));
}
BN_FN vec2 bn_hash_float_to_float2(vec3 k) {
return vec2(bn_hash_float_to_float(vec3(k.x, k.y, k.z)),
bn_hash_float_to_float(vec3(k.z, k.x, k.y)));
}
BN_FN vec2 bn_hash_float_to_float2(vec4 k) {
return vec2(bn_hash_float_to_float(vec4(k.x, k.y, k.z, k.w)),
bn_hash_float_to_float(vec4(k.z, k.x, k.w, k.y)));
}
BN_FN vec3 bn_hash_float_to_float3(float k) {
return vec3(bn_hash_float_to_float(k),
bn_hash_float_to_float(vec2(k, 1.0)),
bn_hash_float_to_float(vec2(k, 2.0)));
}
BN_FN vec3 bn_hash_float_to_float3(vec2 k) {
return vec3(bn_hash_float_to_float(k),
bn_hash_float_to_float(vec3(k.x, k.y, 1.0)),
bn_hash_float_to_float(vec3(k.x, k.y, 2.0)));
}
BN_FN vec3 bn_hash_float_to_float3(vec3 k) {
return vec3(bn_hash_float_to_float(k),
bn_hash_float_to_float(vec4(k.x, k.y, k.z, 1.0)),
bn_hash_float_to_float(vec4(k.x, k.y, k.z, 2.0)));
}
BN_FN vec3 bn_hash_float_to_float3(vec4 k) {
return vec3(bn_hash_float_to_float(k),
bn_hash_float_to_float(vec4(k.z, k.x, k.w, k.y)),
bn_hash_float_to_float(vec4(k.w, k.z, k.y, k.x)));
}
BN_FN vec4 bn_hash_float_to_float4(vec4 k) {
return vec4(bn_hash_float_to_float(k),
bn_hash_float_to_float(vec4(k.w, k.x, k.y, k.z)),
bn_hash_float_to_float(vec4(k.z, k.w, k.x, k.y)),
bn_hash_float_to_float(vec4(k.y, k.z, k.w, k.x)));
}
// --- PCG integer hashes ---
//
// "Hash Functions for GPU Rendering", JCGT 2020, https://jcgt.org/published/0009/03/02/
// Blender's variants are modified to use SIGNED integers only, so they can also
// be written in OSL, and the shift is therefore ARITHMETIC.
//
// VORONOI USES THESE, NOT THE JENKINS FLOAT HASH ABOVE -- except in 1D, which
// uses hash_float_to_float. Reading that off the source rather than assuming
// one hash per file is the difference between a port that matches and one that
// is the right shape with unrelated numbers.
//
// The multiplies and adds are done through uint and reinterpreted: signed
// overflow is undefined behaviour in C++, and GLSL leaves it undefined too, so
// neither language may be trusted to wrap a signed int. Both define the
// unsigned wrap.
BN_FN int bn_wrap_mul(int a, int b) {
return int(uint(a) * uint(b));
}
BN_FN int bn_wrap_add(int a, int b) {
return int(uint(a) + uint(b));
}
BN_FN ivec2 bn_hash_pcg2d_i(ivec2 v) {
v.x = bn_wrap_add(bn_wrap_mul(v.x, 1664525), 1013904223);
v.y = bn_wrap_add(bn_wrap_mul(v.y, 1664525), 1013904223);
v.x = bn_wrap_add(v.x, bn_wrap_mul(v.y, 1664525));
v.y = bn_wrap_add(v.y, bn_wrap_mul(v.x, 1664525));
v.x ^= v.x >> 16;
v.y ^= v.y >> 16;
v.x = bn_wrap_add(v.x, bn_wrap_mul(v.y, 1664525));
v.y = bn_wrap_add(v.y, bn_wrap_mul(v.x, 1664525));
return v;
}
BN_FN ivec3 bn_hash_pcg3d_i(ivec3 v) {
v.x = bn_wrap_add(bn_wrap_mul(v.x, 1664525), 1013904223);
v.y = bn_wrap_add(bn_wrap_mul(v.y, 1664525), 1013904223);
v.z = bn_wrap_add(bn_wrap_mul(v.z, 1664525), 1013904223);
v.x = bn_wrap_add(v.x, bn_wrap_mul(v.y, v.z));
v.y = bn_wrap_add(v.y, bn_wrap_mul(v.z, v.x));
v.z = bn_wrap_add(v.z, bn_wrap_mul(v.x, v.y));
v.x ^= v.x >> 16;
v.y ^= v.y >> 16;
v.z ^= v.z >> 16;
v.x = bn_wrap_add(v.x, bn_wrap_mul(v.y, v.z));
v.y = bn_wrap_add(v.y, bn_wrap_mul(v.z, v.x));
v.z = bn_wrap_add(v.z, bn_wrap_mul(v.x, v.y));
return v;
}
BN_FN ivec4 bn_hash_pcg4d_i(ivec4 v) {
v.x = bn_wrap_add(bn_wrap_mul(v.x, 1664525), 1013904223);
v.y = bn_wrap_add(bn_wrap_mul(v.y, 1664525), 1013904223);
v.z = bn_wrap_add(bn_wrap_mul(v.z, 1664525), 1013904223);
v.w = bn_wrap_add(bn_wrap_mul(v.w, 1664525), 1013904223);
v.x = bn_wrap_add(v.x, bn_wrap_mul(v.y, v.w));
v.y = bn_wrap_add(v.y, bn_wrap_mul(v.z, v.x));
v.z = bn_wrap_add(v.z, bn_wrap_mul(v.x, v.y));
v.w = bn_wrap_add(v.w, bn_wrap_mul(v.y, v.z));
v.x ^= v.x >> 16;
v.y ^= v.y >> 16;
v.z ^= v.z >> 16;
v.w ^= v.w >> 16;
v.x = bn_wrap_add(v.x, bn_wrap_mul(v.y, v.w));
v.y = bn_wrap_add(v.y, bn_wrap_mul(v.z, v.x));
v.z = bn_wrap_add(v.z, bn_wrap_mul(v.x, v.y));
v.w = bn_wrap_add(v.w, bn_wrap_mul(v.y, v.z));
return v;
}
// float(1), not 1.0 -- see the literal hazard at the top of this file.
//
// ⚠ MEASURED 2026-09-05, and the precaution is NOT load-bearing HERE: writing
// `1.0` gives the same 1056/1056. The reason is worth keeping, because it is
// a property of this constant and not of the rule -- float(0x7fffffff) is
// 2147483648.0, i.e. 2^31, because 2147483647 has no float32 spelling. A
// power of two has an exact reciprocal, so double and float arrive at the
// same bits. Change the divisor to anything that is not a power of two and
// the rule starts mattering again, which is why it stays written this way.
BN_FN float bn_int_to_float_01(int k) {
return float(k & 0x7fffffff) * (float(1) / float(0x7fffffff));
}
BN_FN vec2 bn_hash_int_to_float2(ivec2 k) {
ivec2 h = bn_hash_pcg2d_i(k);
return vec2(bn_int_to_float_01(h.x), bn_int_to_float_01(h.y));
}
BN_FN vec3 bn_hash_int_to_float3(ivec3 k) {
ivec3 h = bn_hash_pcg3d_i(k);
return vec3(bn_int_to_float_01(h.x), bn_int_to_float_01(h.y), bn_int_to_float_01(h.z));
}
BN_FN vec4 bn_hash_int_to_float4(ivec4 k) {
ivec4 h = bn_hash_pcg4d_i(k);
return vec4(bn_int_to_float_01(h.x), bn_int_to_float_01(h.y),
bn_int_to_float_01(h.z), bn_int_to_float_01(h.w));
}
BN_FN vec3 bn_hash_int_to_float3(ivec2 k) {
return bn_hash_int_to_float3(ivec3(k.x, k.y, 0));
}
BN_FN vec3 bn_hash_int_to_float3(ivec4 k) {
vec4 h = bn_hash_int_to_float4(k);
return vec3(h.x, h.y, h.z);
}
#endif // PN_BLENDER_HASH