Files
fn_registry/cpp/functions/gfx/gpu_compute_program.cpp
T
egutierrez 07d06d5e7d feat(cpp/gfx): GPU compute primitives for Monte Carlo (G1-G7)
Stack base de compute shaders OpenGL 4.3 para cargas Monte Carlo intensivas
en GPU. Reutiliza el patron de graph_force_layout_gpu (SSBO + compute) y se
integra con el resto del registry sin nuevos simbolos en gl_loader (todo lo
que se necesita ya estaba expuesto).

- gpu_ssbo: lifecycle de Shader Storage Buffer Objects.
- gpu_compute_program: compila compute GLSL 4.3 con preamble inyectable
  (mismo pattern de gl_shader::compile_fragment).
- gpu_dispatch: dispatch_1d/2d/3d con ceil(N/local) automatico + barrier
  helpers (storage, uniform, image, buffer_update, all).
- gpu_rng_glsl: PCG32 GLSL (uniform/normal/below) + SplitMix64 seed walkers
  para sembrar deterministicamente N walkers desde un master seed.
- gpu_histogram_1d: SSBO float[N] -> uint[nbins] via atomicAdd.
- gpu_histogram_2d: SSBO float[2N] xy-interleaved -> uint[nx*ny] +
  to_density helper para alimentar heatmap_cpp_viz.
- gpu_reduce: workgroup-shared sum/min/max/mean (local 256, partials CPU).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 11:52:08 +02:00

105 lines
3.3 KiB
C++

#include "gfx/gl_loader.h"
#include "gfx/gpu_compute_program.h"
#include <cstdio>
#include <regex>
#include <string>
namespace fn::gfx {
static int parse_err_line(const char* log) {
std::regex re1(R"(ERROR:\s*\d+:(\d+):)");
std::regex re2(R"(\d+\((\d+)\))");
std::cmatch m;
if (std::regex_search(log, m, re1)) return std::stoi(m[1].str());
if (std::regex_search(log, m, re2)) return std::stoi(m[1].str());
return -1;
}
static int count_lines(const std::string& s) {
int n = 0;
for (char c : s) if (c == '\n') ++n;
return n;
}
static ComputeCompileResult compile_with_layout(const std::string& layout_line,
const std::string& user_body,
const std::string& preamble) {
ComputeCompileResult r;
// Header fijo. count = lineas que sumamos antes del user_body, para
// restar al err_line del log.
std::string header = "#version 430 core\n";
header += layout_line;
if (!preamble.empty()) {
header += preamble;
if (preamble.back() != '\n') header += '\n';
}
int header_lines = count_lines(header);
const char* srcs[2] = { header.c_str(), user_body.c_str() };
GLuint sh = glCreateShader(GL_COMPUTE_SHADER);
glShaderSource(sh, 2, srcs, nullptr);
glCompileShader(sh);
GLint ok = 0;
glGetShaderiv(sh, GL_COMPILE_STATUS, &ok);
if (!ok) {
GLint len = 0;
glGetShaderiv(sh, GL_INFO_LOG_LENGTH, &len);
r.err_msg.resize(static_cast<std::size_t>(len));
if (len > 0) glGetShaderInfoLog(sh, len, nullptr, &r.err_msg[0]);
int line = parse_err_line(r.err_msg.c_str());
r.err_line = (line > header_lines) ? (line - header_lines) : line;
glDeleteShader(sh);
return r;
}
GLuint prog = glCreateProgram();
glAttachShader(prog, sh);
glLinkProgram(prog);
glDeleteShader(sh);
glGetProgramiv(prog, GL_LINK_STATUS, &ok);
if (!ok) {
GLint len = 0;
glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &len);
r.err_msg.resize(static_cast<std::size_t>(len));
if (len > 0) glGetProgramInfoLog(prog, len, nullptr, &r.err_msg[0]);
r.err_line = parse_err_line(r.err_msg.c_str());
glDeleteProgram(prog);
return r;
}
r.program = prog;
r.ok = true;
return r;
}
ComputeCompileResult compile_compute(const std::string& user_body,
int local_size_x,
const std::string& preamble) {
char buf[64];
std::snprintf(buf, sizeof(buf),
"layout(local_size_x = %d) in;\n", local_size_x);
return compile_with_layout(buf, user_body, preamble);
}
ComputeCompileResult compile_compute_2d(const std::string& user_body,
int local_size_x,
int local_size_y,
const std::string& preamble) {
char buf[96];
std::snprintf(buf, sizeof(buf),
"layout(local_size_x = %d, local_size_y = %d) in;\n",
local_size_x, local_size_y);
return compile_with_layout(buf, user_body, preamble);
}
void delete_compute_program(unsigned int program) {
if (program) glDeleteProgram(program);
}
} // namespace fn::gfx