--- name: gpu_rng_glsl kind: function lang: cpp domain: gfx version: "1.0.0" purity: pure signature: "std::string glsl_rng_preamble(int seed_binding); void seed_walkers_init(uint64_t master_seed, uint32_t* out, int count)" description: "Generador de preamble GLSL con primitivas PCG32 (uniform, normal, below) inyectables en compute shaders, mas helper CPU para sembrar N seeds deterministas via SplitMix64. Sin GL ni I/O — funciones puras que producen string y rellenan array." tags: [glsl, rng, pcg32, splitmix64, montecarlo, compute, gpu, gfx] uses_functions: [] uses_types: [] returns: [] returns_optional: false error_type: "" imports: [string, cstdint, cstdio] tested: false tests: [] test_file_path: "" file_path: "cpp/functions/gfx/gpu_rng_glsl.cpp" framework: opengl params: - name: seed_binding desc: "Indice del binding std430 donde reside el SSBO uint rng_seeds[]. Tipicamente reservar el ultimo binding del shader (8 o 9)." - name: master_seed desc: "Semilla maestra de la que se derivan todas las semillas individuales. 0 se sustituye por la constante de Knuth para evitar arranques degenerados." - name: out desc: "Buffer destino de count uint32. Garantiza out[i] != 0 (requisito de PCG32)." - name: count desc: "Numero de semillas a generar. Igual al numero de threads/walkers que ejecutaran el compute." output: "glsl_rng_preamble: string GLSL listo para concatenar como preamble de compile_compute. seed_walkers_init: rellena out[count] in-place. Ambas son puras." --- # gpu_rng_glsl Pareja CPU + GLSL para Monte Carlo en compute shaders. ## Calidad PCG32 (variante recomendada por O'Neill, 2014) tiene state de 32 bits, periodo 2^32 por chain y supera los tests de PractRand hasta varios TB. Para Monte Carlo no criptografico es la opcion estandar moderna. Cada thread tiene su propio state independiente (sin contention atomico), asi que el periodo agregado de N chains es N · 2^32 — mas que suficiente para 10^10 samples. `rng_normal` usa Box-Muller con cos (descarta la sin) — ~30 ciclos GPU por sample, un orden de magnitud mas barato que el Ziggurat y sin tablas de lookup que machacarian el cache. ## API GLSL inyectada por `glsl_rng_preamble` ```glsl layout(std430, binding = ) buffer RngSeeds { uint rng_seeds[]; }; uint pcg32 (inout uint state); float rng_uniform (inout uint state); // [0, 1) float rng_normal (inout uint state); // N(0, 1) uint rng_below (inout uint state, uint n); // [0, n) sin sesgo ``` ## Patron de uso CPU: ```cpp std::vector seeds(N); fn::gfx::seed_walkers_init(0xC0FFEE, seeds.data(), N); fn::gfx::Ssbo seed_ssbo = fn::gfx::ssbo_create( N * sizeof(unsigned int), seeds.data(), GL_DYNAMIC_COPY); auto rng_src = fn::gfx::glsl_rng_preamble(/*seed_binding=*/9); auto r = fn::gfx::compile_compute(my_body, 64, rng_src); ``` GLSL (`my_body`): ```glsl layout(std430, binding = 0) buffer Out { float samples[]; }; uniform uint u_count; void main() { uint i = gl_GlobalInvocationID.x; if (i >= u_count) return; uint s = rng_seeds[i]; samples[i] = rng_normal(s); rng_seeds[i] = s; // persistir para el siguiente dispatch } ``` Despacho: ```cpp glUseProgram(r.program); fn::gfx::ssbo_bind(out_ssbo, 0); fn::gfx::ssbo_bind(seed_ssbo, 9); glUniform1ui(loc_count, N); fn::gfx::dispatch_1d(N, 64); fn::gfx::barrier_storage(); ``` ## Notas - El binding del SSBO de seeds es parametro porque cada shader puede usar bindings distintos para sus datos. Convencion sugerida: el ultimo binding del shader (8 o 9). - Para reproducibilidad bit-exacta entre runs, mantener `master_seed` y N constantes; los samples seran identicos. Util para tests numericos. - `seed_walkers_init` es deterministica y no depende de GL — se puede invocar en tests unitarios CPU sin contexto OpenGL. - Para muestreo categorico (sampleTier de vr_tiered_lab) usar `rng_uniform` con cumulative-sum manual en el shader; un helper dedicado se puede añadir si aparece en multiples kernels.