#include "gfx/gpu_rng_glsl.h" #include #include namespace fn::gfx { std::string glsl_rng_preamble(int seed_binding) { char header[256]; std::snprintf(header, sizeof(header), "layout(std430, binding = %d) buffer RngSeeds { uint rng_seeds[]; };\n", seed_binding); // PCG32 (M.E. O'Neill, 2014). Buena calidad estadistica con state de // 32 bits — suficiente para Monte Carlo no criptografico. Periodo 2^32 // por chain; con N chains independientes el periodo agregado es enorme. // // rng_uniform: float(state) * 2^-32 = [0, 1). // Nota: float tiene 24 bits de mantisa => ~1/2^23 spacing, OK para MC. // // rng_normal: Box-Muller polar. Descarta una de las dos normales // generadas en cada paso (suficiente; si se necesita perf extra se // puede cachear u2 entre llamadas con un flag por thread). // // rng_below: rejection sampling con mascara de potencia-de-2 superior. // Sesgo despreciable comparado con (state % n). static const char* body = "uint pcg32(inout uint state) {\n" " state = state * 747796405u + 2891336453u;\n" " uint word = ((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u;\n" " return (word >> 22u) ^ word;\n" "}\n" "float rng_uniform(inout uint state) {\n" " return float(pcg32(state)) * (1.0 / 4294967296.0);\n" "}\n" "float rng_normal(inout uint state) {\n" " float u1 = max(rng_uniform(state), 1e-7);\n" " float u2 = rng_uniform(state);\n" " return sqrt(-2.0 * log(u1)) * cos(6.28318530717958647692 * u2);\n" "}\n" "uint rng_below(inout uint state, uint n) {\n" " if (n == 0u) return 0u;\n" " uint mask = n - 1u;\n" " mask |= mask >> 1u; mask |= mask >> 2u; mask |= mask >> 4u;\n" " mask |= mask >> 8u; mask |= mask >> 16u;\n" " for (int k = 0; k < 16; ++k) {\n" " uint v = pcg32(state) & mask;\n" " if (v < n) return v;\n" " }\n" " return pcg32(state) % n;\n" "}\n"; std::string out; out.reserve(1024); out += header; out += body; return out; } void seed_walkers_init(unsigned long long master_seed, unsigned int* out, int count) { if (out == nullptr || count <= 0) return; // SplitMix64 — pequeno PRNG que genera bien-distribuidos uint64 a partir // de cualquier seed. Lo usamos como "stream" de seeds independientes // para PCG32. Si el master_seed es 0, lo sustituimos por la constante // de Knuth para evitar que SplitMix arranque desde un estado degenerado. std::uint64_t state = master_seed; if (state == 0ULL) state = 0x9E3779B97F4A7C15ULL; for (int i = 0; i < count; ++i) { state += 0x9E3779B97F4A7C15ULL; std::uint64_t z = state; z = (z ^ (z >> 30)) * 0xBF58476D1CE4E5B9ULL; z = (z ^ (z >> 27)) * 0x94D049BB133111EBULL; z = z ^ (z >> 31); std::uint32_t s = static_cast(z & 0xFFFFFFFFULL); if (s == 0u) s = 0x9E3779B9u; // PCG state must be non-zero out[i] = s; } } } // namespace fn::gfx