Files
fn_registry/dev/issues/0027-cpp-gl-compute-pingpong.md
T
egutierrez 461bb77298 docs(issues): añadir 0025-0036 — features C++ para registry y primitives_gallery
12 issues nuevos para implementacion paralela: text_editor, file_watcher,
gl_texture_load, gl_compute+pingpong+DAG Compute, ImPlot3D, mesh_viewer,
audio reactivo, animation curves, sql_workbench, http+ws inspector,
scientific viz (5 charts), map_tiles, image_canvas + webcam_texture.

Cada issue añade funciones al registry y un demo propio en
primitives_gallery/demos_<feature>.cpp para minimizar conflictos en paralelo.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 20:48:18 +02:00

7.6 KiB
Raw Blame History

0027 — C++ gl_compute_shader + gl_pingpong_fbo + DAG node Compute

APP Metadata

Campo Valor
ID 0027
Estado pendiente
Prioridad alta
Tipo feature — C++ gfx (cpp/functions/gfx)

Dependencias

Recomendado leer primero gl_loader_cpp_gfx, gl_shader_cpp_gfx, gl_framebuffer_cpp_gfx, dag_catalog_cpp_gfx, dag_compile_cpp_gfx. Sin bloqueos.

Desbloquea: simulaciones GPGPU (particulas, fluidos), feedback loops (reaction-diffusion, blur multipass, bloom), cualquier shader que necesite estado entre frames. Triplica el alcance del DAG visual de shaders_lab.


Objetivo

Tres adiciones complementarias:

  1. gl_compute_shader_cpp_gfx — carga + dispatch de compute shaders (GLSL #version 430 core + layout(local_size_*) in). API parejo a gl_shader.
  2. gl_pingpong_fbo_cpp_gfx — wrapper de dos gl_framebuffer con swap A↔B en cada paso. Permite usar el output del frame N-1 como input del frame N (feedback).
  3. DAG kind Compute — nuevo DagNodeKind::Compute en dag_catalog/dag_compile. Nodo que ejecuta un compute shader sobre la imagen actual, con N iteraciones configurable.

Demo en primitives_gallery: reaction-diffusion (Gray-Scott) usando ping-pong FBO + compute shader.

Contexto

gl_shader solo soporta fragment shaders. El DAG actual es estrictamente fragment-pipeline (gen → op → blend). No hay forma de:

  • Persistir estado entre frames (necesario para feedback effects).
  • Hacer GPGPU verdadero (compute con buffers / images).
  • Implementar efectos como fluid sim, particulas o blur multipass eficientes.

OpenGL compute shaders requieren #version 430 core (= GL 4.3). El proyecto ya pide GL 3.3+; subir el minimo solo para los nodos compute es aceptable (graceful degrade si la GPU no lo soporta).

Arquitectura

cpp/functions/gfx/
├── gl_compute_shader.h          # NEW
├── gl_compute_shader.cpp        # NEW
├── gl_compute_shader.md         # NEW
├── gl_pingpong_fbo.h            # NEW
├── gl_pingpong_fbo.cpp          # NEW
├── gl_pingpong_fbo.md           # NEW
├── dag_types.h                  # MOD: añadir DagNodeKind::Compute
├── dag_catalog.cpp/.h           # MOD: registrar nodos compute (ej: ReactionDiffusion)
├── dag_compile.cpp/.h           # MOD: emitir paso compute en pipeline
└── dag_uniforms.cpp/.h          # MOD si los compute tienen params
cpp/apps/primitives_gallery/
├── demos_compute.cpp            # NEW
├── demos.h                      # MOD
├── main.cpp                     # MOD
└── CMakeLists.txt               # MOD

Pure core / impure shell

  • gl_compute_shader y gl_pingpong_fbo: ambos purity: impure, kind: function.
  • dag_catalog/dag_compile: ya son pure, mantener.
  • dag_node_editor / dag_panel: ya son impure, no se tocan en este issue (los nodos compute aparecen automaticamente al añadirse al catalog).

API propuesta

namespace fn {

// gl_compute_shader.h
struct GlComputeProgram {
    GLuint program = 0;
    int local_x = 1, local_y = 1, local_z = 1;
    bool ok() const { return program != 0; }
};

GlComputeProgram gl_compute_compile(const char* glsl_body);  // body sin version
void gl_compute_destroy(GlComputeProgram&);

// Dispatch: bind images, set uniforms (callback opcional), glDispatchCompute, barrier.
void gl_compute_dispatch(const GlComputeProgram&, int groups_x, int groups_y, int groups_z = 1);
void gl_compute_bind_image(int unit, GLuint texture, GLenum access /*GL_READ_ONLY|WRITE|READ_WRITE*/, GLenum format = GL_RGBA8);

const char* gl_compute_last_error();

// gl_pingpong_fbo.h — dos FBO RGBA8 del mismo tamaño con A/B swap.
struct PingpongFbo {
    Framebuffer a, b;
    bool a_is_front = true;  // front = ultimo escrito
};

PingpongFbo pingpong_create(int w, int h);
void pingpong_destroy(PingpongFbo&);
void pingpong_resize(PingpongFbo&, int w, int h);
void pingpong_swap(PingpongFbo&);
const Framebuffer& pingpong_front(const PingpongFbo&);  // ultima lectura
const Framebuffer& pingpong_back (const PingpongFbo&);  // siguiente write target
}

DAG: nuevo kind Compute

dag_catalog registra al menos un nodo de ejemplo: compute_blur_separable o compute_reaction_diffusion. Tras este issue queda abierto añadir mas (cada uno = un nuevo DagNode).

dag_compile: cuando emite un paso Compute, decide bindings (image read = ping-front, image write = pong-back) y devuelve metadatos para que el host haga dispatch + swap correctamente.

Tareas

Fase 1 — gl_compute_shader

  • 1.1 Implementar wrapper minimo. Detectar version GL >= 4.3 al inicializar; si no, retornar program == 0 y guardar error "compute shaders require OpenGL 4.3+".
  • 1.2 Helper gl_compute_bind_image con glBindImageTexture.
  • 1.3 .md con frontmatter (purity: impure, kind: function, error_type).

Fase 2 — gl_pingpong_fbo

  • 2.1 Implementar wrapper que reusa el gl_framebuffer existente. Resize propaga a los dos.
  • 2.2 .md con frontmatter.

Fase 3 — DAG Compute kind

  • 3.1 Añadir DagNodeKind::Compute en dag_types.h + serializacion (json IO).
  • 3.2 dag_catalog: registrar nodo de ejemplo compute_blur_2pass (separable Gaussian blur, 2 dispatches via flag uniform direction).
  • 3.3 dag_compile: emitir step compute con suficiente metadata para que el host (un nuevo helper) haga el dispatch.
  • 3.4 Helper dag_compute_run_step() — recibe DagStep compute + PingpongFbo + uniforms y hace dispatch + swap.
  • 4.1 demos_compute.cpp con demo_compute_reaction_diffusion(): PingpongFbo 512×512 + compute shader Gray-Scott con sliders (feed, kill, dt). Usa ImGui::Image para mostrar el front buffer cada frame. Boton "reset" rellena con seed.
  • 4.2 Registrar en gallery.

Fase 5 — Tests + docs

  • 5.1 Test (Linux + GPU disponible, opcional via env var FN_GPU_TESTS=1): compila un compute simple que escribe vec4(1,0,0,1) y verifica via glReadPixels.
  • 5.2 Test puro de dag_compile con un pipeline que mezcla nodos Gen + Compute.
  • 5.3 ./fn index + ./fn show *_cpp_gfx para los nuevos.

Ejemplo de uso

auto cs = fn::gl_compute_compile(R"glsl(
  layout(local_size_x=8, local_size_y=8) in;
  layout(rgba8, binding=0) uniform image2D src;
  layout(rgba8, binding=1) uniform image2D dst;
  void main() {
    ivec2 p = ivec2(gl_GlobalInvocationID.xy);
    vec4 c = imageLoad(src, p);
    imageStore(dst, p, vec4(1.0 - c.rgb, 1.0));
  }
)glsl");

auto pp = fn::pingpong_create(512, 512);
glUseProgram(cs.program);
fn::gl_compute_bind_image(0, fn::pingpong_front(pp).color_tex, GL_READ_ONLY);
fn::gl_compute_bind_image(1, fn::pingpong_back (pp).color_tex, GL_WRITE_ONLY);
fn::gl_compute_dispatch(cs, 64, 64);
fn::pingpong_swap(pp);

Decisiones de diseño

  • Subir minimo a GL 4.3 solo para nodos Compute — el resto del pipeline sigue compilando con 3.3. gl_compute_compile falla con error claro en GPUs viejas.
  • Ping-pong fuera de FBO — wrapper aparte, no fusionado con gl_framebuffer (mantenemos primitivos atomicos).
  • Catalog del DAG abierto — un solo nodo compute en este issue. Otros (fluid, particles) se añaden en issues posteriores reusando los wrappers.

Riesgos

  • macOS no soporta compute shaders en GL 4.1 — documentar limitacion. En macOS el path Compute queda inactivo.
  • Memory barriers: olvidar glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT) produce datos basura. Encapsular en gl_compute_dispatch.
  • Dispatch grande bloquea UI: documentar que dispatches > 1M pixels deben dividirse o bajar local_size.