fix(fn-run): propagar stdout/stderr de bash functions library-style #1

Open
dataforge wants to merge 537 commits from auto/0077-fn-run-bash-mudo into master
11 changed files with 8457 additions and 0 deletions
Showing only changes of commit 0c7a8393ab - Show all commits
Binary file not shown.

After

Width:  |  Height:  |  Size: 966 B

@@ -0,0 +1,127 @@
// Demo de gl_texture_load (cpp/functions/gfx/gl_texture_load.{h,cpp}).
// Carga assets/sample.png y lo muestra con ImGui::Image. Sliders para tint
// RGB que se aplican como modulacion (ImGui::Image acepta tint_col).
//
// Limitacion: el "zoom UV" se simula moviendo uv0/uv1 (que ImGui::Image acepta
// nativamente). Asi evitamos compilar un shader custom adicional para la demo.
#include "demos.h"
#include "demo.h"
#include "gfx/gl_texture_load.h"
#include "gfx/gl_loader.h"
#include <imgui.h>
#include <cstdio>
#include <cstring>
namespace gallery {
namespace {
struct TextureState {
fn::GlTexture tex{};
bool tried_load = false;
std::string_view err;
char err_buf[256] = {0};
float tint[3] = {1.0f, 1.0f, 1.0f};
float zoom = 1.0f; // 1.0 = sin zoom; >1 hace UV mas pequeno
};
TextureState& state() {
static TextureState s;
return s;
}
// Resuelve un path para el asset. Probamos varios candidatos relativos al cwd
// del binario (puede lanzarse desde build/ o desde la raiz del repo).
const char* resolve_sample_path() {
static const char* candidates[] = {
"assets/sample.png",
"apps/primitives_gallery/assets/sample.png",
"cpp/apps/primitives_gallery/assets/sample.png",
"../cpp/apps/primitives_gallery/assets/sample.png",
"../../cpp/apps/primitives_gallery/assets/sample.png",
"../../../cpp/apps/primitives_gallery/assets/sample.png",
nullptr,
};
for (int i = 0; candidates[i]; i++) {
FILE* f = std::fopen(candidates[i], "rb");
if (f) { std::fclose(f); return candidates[i]; }
}
return candidates[0]; // devolver el primer candidato para que el error sea mas descriptivo
}
} // namespace
void demo_gl_texture() {
demo_header("gl_texture_load", "v1.0.0",
"Carga PNG/JPG/HDR desde disco a una textura GL lista para sampler2D. "
"Vendorea stb_image (cpp/vendor/stb/). Demo: assets/sample.png "
"(damero 256x256), tint RGB modulando ImGui::Image, zoom UV.");
auto& s = state();
if (!s.tried_load) {
// Asegurar simbolos GL resueltos (Linux no-op, Windows wglGetProcAddress).
fn::gfx::gl_loader_init();
const char* path = resolve_sample_path();
s.tex = fn::gl_texture_load(path, /*flip_y=*/true, /*srgb=*/false);
if (!s.tex.ok()) {
std::snprintf(s.err_buf, sizeof(s.err_buf),
"no se pudo cargar '%s': %s",
path, fn::gl_texture_last_error());
}
s.tried_load = true;
}
if (!s.tex.ok()) {
ImGui::TextColored(ImVec4(1.0f, 0.4f, 0.4f, 1.0f), "%s", s.err_buf);
ImGui::TextWrapped(
"El binario busca el PNG en varios paths relativos al cwd. "
"Lanzar desde la raiz del repo o desde cpp/build/ deberia funcionar.");
return;
}
section("Texture info");
ImGui::Text("size: %d x %d px", s.tex.w, s.tex.h);
ImGui::Text("channels: %d (forzado a RGBA en upload)", s.tex.channels);
ImGui::Text("gl_id: %u", (unsigned)s.tex.id);
section("Tint + zoom");
ImGui::SliderFloat3("tint RGB", s.tint, 0.0f, 2.0f, "%.2f");
ImGui::SliderFloat("zoom UV", &s.zoom, 0.25f, 4.0f, "%.2fx");
section("Preview");
// Calcular UVs centradas con zoom: 1.0 = (0,0)-(1,1), 2.0 = (0.25,0.25)-(0.75,0.75)
float u_half = 0.5f / (s.zoom > 0.001f ? s.zoom : 0.001f);
ImVec2 uv0(0.5f - u_half, 0.5f - u_half);
ImVec2 uv1(0.5f + u_half, 0.5f + u_half);
ImVec4 tint(s.tint[0], s.tint[1], s.tint[2], 1.0f);
// Conversion GLuint -> ImTextureID. ImGui::Image acepta cualquier id de
// textura del backend; en imgui_impl_opengl3 es directamente el GLuint.
ImTextureID tid = (ImTextureID)(intptr_t)s.tex.id;
ImGui::ImageWithBg(tid, ImVec2(384.0f, 384.0f), uv0, uv1,
ImVec4(0, 0, 0, 0), tint);
code_block(
"#include \"gfx/gl_texture_load.h\"\n\n"
"auto tex = fn::gl_texture_load(\"assets/sample.png\");\n"
"if (!tex.ok()) {\n"
" fprintf(stderr, \"%s\\n\", fn::gl_texture_last_error());\n"
" return 1;\n"
"}\n"
"// uso en shader:\n"
"glUseProgram(prog);\n"
"fn::gl_texture_bind_uniform(prog, \"u_tex\", tex, /*unit=*/0);\n"
"glDrawArrays(GL_TRIANGLES, 0, 6);\n\n"
"// o en ImGui directamente:\n"
"ImGui::Image((ImTextureID)(intptr_t)tex.id, ImVec2(w, h));"
);
}
} // namespace gallery
+4
View File
@@ -32,6 +32,8 @@ PFNGLUNIFORM3FPROC fn_glUniform3f = nullptr;
PFNGLUNIFORM4FPROC fn_glUniform4f = nullptr;
PFNGLUNIFORM4FVPROC fn_glUniform4fv = nullptr;
PFNGLUSEPROGRAMPROC fn_glUseProgram = nullptr;
PFNGLACTIVETEXTUREPROC fn_glActiveTexture = nullptr;
PFNGLGENERATEMIPMAPPROC fn_glGenerateMipmap = nullptr;
namespace fn::gfx {
@@ -70,6 +72,8 @@ bool gl_loader_init() {
LOAD(glUniform4f);
LOAD(glUniform4fv);
LOAD(glUseProgram);
LOAD(glActiveTexture);
LOAD(glGenerateMipmap);
#undef LOAD
return true;
+4
View File
@@ -38,6 +38,8 @@
extern PFNGLUNIFORM4FPROC fn_glUniform4f;
extern PFNGLUNIFORM4FVPROC fn_glUniform4fv;
extern PFNGLUSEPROGRAMPROC fn_glUseProgram;
extern PFNGLACTIVETEXTUREPROC fn_glActiveTexture;
extern PFNGLGENERATEMIPMAPPROC fn_glGenerateMipmap;
#define glAttachShader fn_glAttachShader
#define glBindBuffer fn_glBindBuffer
@@ -69,6 +71,8 @@
#define glUniform4f fn_glUniform4f
#define glUniform4fv fn_glUniform4fv
#define glUseProgram fn_glUseProgram
#define glActiveTexture fn_glActiveTexture
#define glGenerateMipmap fn_glGenerateMipmap
#else
#define GL_GLEXT_PROTOTYPES
#include <GL/gl.h>
+151
View File
@@ -0,0 +1,151 @@
#include "gl_texture_load.h"
#include "../../vendor/stb/stb_image.h"
#include <cstring>
#include <string>
namespace fn {
namespace {
// Error thread-local — cada thread mantiene su propio mensaje.
thread_local std::string g_last_error;
bool ends_with_ci(const char* s, const char* suffix) {
if (!s || !suffix) return false;
size_t ls = std::strlen(s);
size_t lf = std::strlen(suffix);
if (lf > ls) return false;
const char* a = s + ls - lf;
for (size_t i = 0; i < lf; i++) {
char ca = a[i]; char cb = suffix[i];
if (ca >= 'A' && ca <= 'Z') ca = char(ca - 'A' + 'a');
if (cb >= 'A' && cb <= 'Z') cb = char(cb - 'A' + 'a');
if (ca != cb) return false;
}
return true;
}
GLuint upload_ldr(const unsigned char* pixels, int w, int h, bool srgb) {
GLuint id = 0;
glGenTextures(1, &id);
if (id == 0) return 0;
glBindTexture(GL_TEXTURE_2D, id);
GLint internal = srgb ? GL_SRGB8_ALPHA8 : GL_RGBA8;
glTexImage2D(GL_TEXTURE_2D, 0, internal, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glGenerateMipmap(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, 0);
return id;
}
GLuint upload_hdr(const float* pixels, int w, int h) {
GLuint id = 0;
glGenTextures(1, &id);
if (id == 0) return 0;
glBindTexture(GL_TEXTURE_2D, id);
// GL_RGBA16F + GL_FLOAT — GL 3.0+. En GL 2.1 no hay GL_RGBA16F; aceptamos
// la limitacion (driver moderno).
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, w, h, 0, GL_RGBA, GL_FLOAT, pixels);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glGenerateMipmap(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, 0);
return id;
}
void set_error(const char* msg) {
g_last_error = msg ? msg : "unknown error";
}
} // namespace
GlTexture gl_texture_load(const char* path, bool flip_y, bool srgb) {
GlTexture tex{};
if (!path || !*path) {
set_error("gl_texture_load: empty path");
return tex;
}
stbi_set_flip_vertically_on_load(flip_y ? 1 : 0);
if (ends_with_ci(path, ".hdr")) {
int w = 0, h = 0, ch = 0;
float* px = stbi_loadf(path, &w, &h, &ch, 4);
if (!px) {
set_error(stbi_failure_reason());
return tex;
}
tex.id = upload_hdr(px, w, h);
tex.w = w; tex.h = h; tex.channels = ch;
stbi_image_free(px);
if (tex.id == 0) set_error("glGenTextures returned 0");
else g_last_error.clear();
return tex;
}
int w = 0, h = 0, ch = 0;
unsigned char* px = stbi_load(path, &w, &h, &ch, 4); // forzamos RGBA
if (!px) {
set_error(stbi_failure_reason());
return tex;
}
tex.id = upload_ldr(px, w, h, srgb);
tex.w = w; tex.h = h; tex.channels = ch;
stbi_image_free(px);
if (tex.id == 0) set_error("glGenTextures returned 0");
else g_last_error.clear();
return tex;
}
GlTexture gl_texture_load_from_memory(const unsigned char* data, int size,
bool flip_y, bool srgb) {
GlTexture tex{};
if (!data || size <= 0) {
set_error("gl_texture_load_from_memory: empty buffer");
return tex;
}
stbi_set_flip_vertically_on_load(flip_y ? 1 : 0);
int w = 0, h = 0, ch = 0;
unsigned char* px = stbi_load_from_memory(data, size, &w, &h, &ch, 4);
if (!px) {
set_error(stbi_failure_reason());
return tex;
}
tex.id = upload_ldr(px, w, h, srgb);
tex.w = w; tex.h = h; tex.channels = ch;
stbi_image_free(px);
if (tex.id == 0) set_error("glGenTextures returned 0");
else g_last_error.clear();
return tex;
}
void gl_texture_destroy(GlTexture& tex) {
if (tex.id != 0) {
glDeleteTextures(1, &tex.id);
tex.id = 0;
}
tex.w = tex.h = tex.channels = 0;
}
const char* gl_texture_last_error() {
return g_last_error.c_str();
}
void gl_texture_bind_uniform(GLuint program, const char* name,
const GlTexture& tex, int unit) {
if (program == 0 || !name || !tex.ok()) return;
glActiveTexture(GL_TEXTURE0 + unit);
glBindTexture(GL_TEXTURE_2D, tex.id);
GLint loc = glGetUniformLocation(program, name);
if (loc >= 0) glUniform1i(loc, unit);
}
} // namespace fn
+44
View File
@@ -0,0 +1,44 @@
#pragma once
// gl_texture_load — carga PNG/JPG/BMP/TGA/HDR desde disco o memoria a una
// textura OpenGL lista para usar como sampler2D en shaders. Vendorea stb_image
// en cpp/vendor/stb/. Funcion impura: hace I/O y crea recursos GPU.
#include "gl_loader.h"
namespace fn {
struct GlTexture {
GLuint id = 0;
int w = 0;
int h = 0;
int channels = 0;
bool ok() const { return id != 0; }
};
// Carga desde disco. flip_y=true (por defecto) coincide con la convencion
// de OpenGL (V hacia arriba). srgb=true sube como GL_SRGB8_ALPHA8 (gamma
// correcto sin pow(c, 2.2) en shader). Si el path termina en .hdr se carga
// como float (GL_RGBA16F).
//
// Si falla, devuelve GlTexture con id=0 (ok() == false). Llama a
// gl_texture_last_error() para obtener el detalle.
GlTexture gl_texture_load(const char* path, bool flip_y = true, bool srgb = false);
// Igual que arriba pero leyendo de un buffer en memoria (PNG/JPG/etc bytes).
GlTexture gl_texture_load_from_memory(const unsigned char* data, int size,
bool flip_y = true, bool srgb = false);
// Libera la textura GPU y deja id=0.
void gl_texture_destroy(GlTexture& tex);
// Mensaje de error del ultimo gl_texture_load* en este thread. "" si no hubo
// fallo. Permanece valido hasta el siguiente gl_texture_load* en el thread.
const char* gl_texture_last_error();
// Helper: bind a una texture unit y subir uniform sampler2D al programa.
// program debe ser un GLuint valido, name el nombre del uniform sampler2D,
// unit el slot de textura (0..N).
void gl_texture_bind_uniform(GLuint program, const char* name,
const GlTexture& tex, int unit);
} // namespace fn
+108
View File
@@ -0,0 +1,108 @@
---
name: gl_texture_load
kind: function
lang: cpp
domain: gfx
version: "1.0.0"
purity: impure
signature: "GlTexture gl_texture_load(const char* path, bool flip_y, bool srgb)"
description: "Carga PNG/JPG/BMP/TGA/HDR desde disco (o memoria) a una textura OpenGL lista para usar como sampler2D. Vendorea stb_image. Soporta sRGB (GL_SRGB8_ALPHA8) y HDR (GL_RGBA16F via stbi_loadf). Genera mipmaps automaticamente. flip_y=true por defecto coincide con la convencion UV de OpenGL."
tags: [opengl, texture, image, png, jpg, hdr, stb_image, gfx]
uses_functions: [gl_loader_cpp_gfx]
uses_types: []
returns: []
returns_optional: false
error_type: "error_go_core"
imports: [stb_image.h, GL/gl.h, GL/glext.h]
tested: false
tests: []
test_file_path: ""
file_path: "cpp/functions/gfx/gl_texture_load.cpp"
framework: opengl
params:
- name: path
desc: "Ruta al archivo de imagen (PNG/JPG/BMP/TGA o .hdr para float). UTF-8."
- name: flip_y
desc: "Si es true, voltea verticalmente al cargar (convencion OpenGL: V hacia arriba). Default true."
- name: srgb
desc: "Si es true, sube como GL_SRGB8_ALPHA8 (gamma correcto sin pow(c, 2.2) en shader). Solo aplica a LDR. Default false."
output: "GlTexture con id=GLuint listo para glBindTexture, w/h/channels de la imagen original. Si falla, id=0 y ok()==false; usar gl_texture_last_error() para detalle."
---
# gl_texture_load
Funcion impura del registry C++ que carga una imagen desde disco (o memoria) y devuelve una textura OpenGL lista para samplear desde un shader. Vendorea [stb_image](https://github.com/nothings/stb) en `cpp/vendor/stb/`.
## API
```cpp
namespace fn {
struct GlTexture {
GLuint id = 0;
int w = 0;
int h = 0;
int channels = 0;
bool ok() const { return id != 0; }
};
GlTexture gl_texture_load(const char* path, bool flip_y = true, bool srgb = false);
GlTexture gl_texture_load_from_memory(const unsigned char* data, int size,
bool flip_y = true, bool srgb = false);
void gl_texture_destroy(GlTexture& tex);
const char* gl_texture_last_error();
void gl_texture_bind_uniform(GLuint program, const char* name,
const GlTexture& tex, int unit);
} // namespace fn
```
## Uso
```cpp
#include "gfx/gl_texture_load.h"
auto tex = fn::gl_texture_load("assets/noise.png");
if (!tex.ok()) {
std::fprintf(stderr, "error: %s\n", fn::gl_texture_last_error());
return 1;
}
glUseProgram(prog);
fn::gl_texture_bind_uniform(prog, "u_noise", tex, /*unit=*/0);
glDrawArrays(GL_TRIANGLES, 0, 6);
// al destruir:
fn::gl_texture_destroy(tex);
```
## Comportamiento
- Forzamos 4 canales (RGBA) al decodificar via stb (`req_comp=4`), asi el upload a `glTexImage2D` siempre es regular.
- Filtros: `GL_LINEAR_MIPMAP_LINEAR` (min) + `GL_LINEAR` (mag). Mipmaps generados con `glGenerateMipmap`.
- Wrap: `GL_REPEAT` en S y T.
- HDR (`.hdr`): `stbi_loadf` + `GL_RGBA16F` (driver con GL 3.0+). En este caso `srgb` se ignora.
- sRGB (LDR + `srgb=true`): internal format `GL_SRGB8_ALPHA8`. El driver convierte a linear automaticamente al samplear.
- `flip_y=true` por defecto — la mayoria de PNGs vienen con (0,0) arriba; OpenGL espera (0,0) abajo.
## Errores
- En fallo, `id == 0` y `ok() == false`. El detalle (string de stb o "glGenTextures returned 0") se guarda en un `thread_local` accesible via `gl_texture_last_error()` hasta el siguiente `gl_texture_load*` en el mismo thread.
## Limites
- Tamano practico: <= 8192 px por lado (depende del driver — `GL_MAX_TEXTURE_SIZE`).
- No reentrante con la misma stb-flag global: stb usa una variable global para `flip_vertically`. Si dos threads cargan en paralelo, el resultado puede mezclar los flags. Para paralelo serio, serializar las llamadas o usar `stbi__ldr_to_hdr` directo con flag local.
## Composicion
Depende de `gl_loader_cpp_gfx` para los simbolos `glActiveTexture`, `glGenerateMipmap`, `glUseProgram`, `glGetUniformLocation`, `glUniform1i` (en Linux son simbolos directos via `GL_GLEXT_PROTOTYPES`; en Windows se resuelven con `wglGetProcAddress`).
Compone naturalmente con:
- `gl_shader_cpp_gfx` — para usar la textura en un fragment shader (uniform sampler2D).
- `shader_canvas_cpp_gfx` — un canvas full-screen que samplea la textura con uniforms de tint/zoom.
## Atribucion
stb_image v2.30 — public domain (MIT-0). Ver `cpp/vendor/stb/README.md`.
+27
View File
@@ -0,0 +1,27 @@
# stb (vendored)
Cabeceras header-only de [nothings/stb](https://github.com/nothings/stb), dominio publico (MIT-0 / unlicense).
## Pinned
- `stb_image.h` — v2.30 — commit `f0569113c93ad095470c54bf34a17b36646bbbb5`
- Source: https://raw.githubusercontent.com/nothings/stb/f0569113c93ad095470c54bf34a17b36646bbbb5/stb_image.h
## Layout
- `stb_image.h` — header de stb (no modificar).
- `stb_image_impl.cpp` — UNICO traductor de la implementacion. Define `STB_IMAGE_IMPLEMENTATION` antes del include para que el cuerpo de stb_image se emita una sola vez en todo el binario.
## Uso
Incluir `<stb_image.h>` desde cualquier .cpp del proyecto **sin** definir `STB_IMAGE_IMPLEMENTATION`. Solo `stb_image_impl.cpp` define el simbolo, evitando duplicados al linkar.
Las funciones del registry que envuelven stb_image viven en `cpp/functions/gfx/` (p.ej. `gl_texture_load`).
## Actualizar
```bash
cd cpp/vendor/stb
curl -L -o stb_image.h https://raw.githubusercontent.com/nothings/stb/<COMMIT>/stb_image.h
# Actualizar el commit en este README.
```
+7988
View File
File diff suppressed because it is too large Load Diff
+4
View File
@@ -0,0 +1,4 @@
// Single TU that materializa la implementacion de stb_image (header-only).
// Cualquier otro .cpp que incluya stb_image.h NO debe definir STB_IMAGE_IMPLEMENTATION.
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"