#include "gfx/gpu_histogram_1d.h" #include "gfx/gl_loader.h" #include "gfx/gpu_compute_program.h" #include "gfx/gpu_dispatch.h" #include #include namespace fn::gfx { // Pass 1: zero out bins. 1 thread por bin. static const char* k_clear_body = R"glsl( layout(std430, binding = 1) buffer Bins { uint bins[]; }; uniform uint u_nbins; void main() { uint i = gl_GlobalInvocationID.x; if (i < u_nbins) bins[i] = 0u; } )glsl"; // Pass 2: 1 thread por sample. floor((x - min) * inv_range * nbins). // Samples fuera del rango se descartan. atomicAdd contiguo: low contention // salvo que la distribucion este muy concentrada en pocos bins (caso real: // poco probable; si pasa, usar shared-memory bins por workgroup como // optimizacion futura). static const char* k_accum_body = R"glsl( layout(std430, binding = 0) readonly buffer Samples { float samples[]; }; layout(std430, binding = 1) coherent buffer Bins { uint bins[]; }; uniform uint u_count; uniform uint u_nbins; uniform float u_min; uniform float u_inv_range; void main() { uint i = gl_GlobalInvocationID.x; if (i >= u_count) return; float x = samples[i]; float t = (x - u_min) * u_inv_range; // [0, 1) si dentro if (t < 0.0 || t >= 1.0) return; uint b = uint(t * float(u_nbins)); if (b >= u_nbins) b = u_nbins - 1u; atomicAdd(bins[b], 1u); } )glsl"; GpuHistogram1D gpu_histogram_1d_create(int nbins) { GpuHistogram1D h{}; if (nbins <= 0) return h; // Programa "accumulate": el clear lo hacemos por glClearBufferData o // un re-upload de zeros (mas simple que un segundo programa, igual // throughput para nbins moderados <= 65536). auto r = compile_compute(k_accum_body, 64, ""); if (!r.ok) { std::fprintf(stderr, "[gpu_histogram_1d] compile error: %s\n", r.err_msg.c_str()); return h; } h.program = r.program; h.loc_count = static_cast(glGetUniformLocation(h.program, "u_count")); h.loc_nbins = static_cast(glGetUniformLocation(h.program, "u_nbins")); h.loc_min = static_cast(glGetUniformLocation(h.program, "u_min")); h.loc_inv_range = static_cast(glGetUniformLocation(h.program, "u_inv_range")); h.bins = ssbo_create(static_cast(nbins) * sizeof(unsigned int), nullptr, GL_DYNAMIC_COPY); h.nbins = nbins; // Inicializar a cero gpu_histogram_1d_clear(h); (void)k_clear_body; // (reservado para futura optimizacion shared-mem) return h; } void gpu_histogram_1d_clear(GpuHistogram1D& h) { if (h.bins.id == 0 || h.nbins <= 0) return; std::vector zeros(static_cast(h.nbins), 0u); ssbo_upload(h.bins, 0, static_cast(h.nbins) * sizeof(unsigned int), zeros.data()); } void gpu_histogram_1d_accumulate(GpuHistogram1D& h, const Ssbo& samples, int count, float range_min, float range_max) { if (h.program == 0 || count <= 0) return; float range = range_max - range_min; if (range <= 0.0f) return; glUseProgram(h.program); ssbo_bind(samples, 0); ssbo_bind(h.bins, 1); glUniform1ui(static_cast(h.loc_count), static_cast(count)); glUniform1ui(static_cast(h.loc_nbins), static_cast(h.nbins)); glUniform1f(static_cast(h.loc_min), range_min); glUniform1f(static_cast(h.loc_inv_range), 1.0f / range); dispatch_1d(count, 64); barrier_storage(); } void gpu_histogram_1d_readback(const GpuHistogram1D& h, unsigned int* out) { if (h.bins.id == 0 || h.nbins <= 0 || out == nullptr) return; barrier_buffer_update(); ssbo_readback(h.bins, 0, static_cast(h.nbins) * sizeof(unsigned int), out); } void gpu_histogram_1d_destroy(GpuHistogram1D& h) { delete_compute_program(h.program); h.program = 0; ssbo_destroy(h.bins); h.nbins = 0; } } // namespace fn::gfx