docs(issues): marcar 0025 y 0026 como completados + WIP master
Wave 1 de parallel-fix-issues integrada a master: - 0025: text_editor_cpp_core + file_watcher_cpp_core - 0026: gl_texture_load_cpp_gfx (vendor: stb_image v2.30) Ademas se commitea WIP previo de master que estaba sin commitear (cambios en shaders_lab, dag_*, framework, tokens, kpi_card, gl_loader.md, etc.) para dejar HEAD buildable. Notas: - Algunos deps del gallery (button.cpp, toolbar.cpp, modal_dialog.cpp...) siguen UNTRACKED — gating con FN_BUILD_GALLERY=ON (default OFF) para que master build (sin flag) no los necesite. - Build OK con y sin flag. fn index registra 904 functions. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,17 +1,31 @@
|
||||
#include "gfx/dag_compile.h"
|
||||
#include "gfx/dag_catalog.h"
|
||||
#include <algorithm>
|
||||
#include <regex>
|
||||
#include <sstream>
|
||||
|
||||
namespace fn::gfx {
|
||||
|
||||
static constexpr int MAX_NODES = 16;
|
||||
static constexpr int MAX_NODES = 16;
|
||||
static constexpr int MAX_PARAM_VEC4S = 64; // 256 floats — enough for 16 nodes × ~16 floats each
|
||||
|
||||
std::vector<int> dag_param_layout(const std::vector<DagStep>& pipeline) {
|
||||
std::vector<int> base(pipeline.size(), 0);
|
||||
int cursor = 0;
|
||||
for (size_t i = 0; i < pipeline.size(); ++i) {
|
||||
const DagNodeDef* def = dag_find(pipeline[i].name);
|
||||
int pc = def ? static_cast<int>(def->param_defaults.size()) : 0;
|
||||
base[i] = cursor;
|
||||
cursor += dag_vec4_count(pc);
|
||||
}
|
||||
return base;
|
||||
}
|
||||
|
||||
std::string compile_dag_to_glsl(const std::vector<DagStep>& pipeline) {
|
||||
const int n = static_cast<int>(std::min(pipeline.size(), static_cast<size_t>(MAX_NODES)));
|
||||
std::ostringstream out;
|
||||
|
||||
out << "uniform vec4 u_params[16];\n";
|
||||
out << "uniform vec4 u_params[" << MAX_PARAM_VEC4S << "];\n";
|
||||
out << "uniform int u_preview_target; // -1 = real Output; >=0 = show out_<i>\n\n";
|
||||
|
||||
if (n == 0) {
|
||||
@@ -23,6 +37,8 @@ std::string compile_dag_to_glsl(const std::vector<DagStep>& pipeline) {
|
||||
return out.str();
|
||||
}
|
||||
|
||||
std::vector<int> base = dag_param_layout(pipeline);
|
||||
|
||||
// Emit per-node functions (skip Output: it's a sink, no body)
|
||||
for (int i = 0; i < n; ++i) {
|
||||
const DagStep& step = pipeline[static_cast<size_t>(i)];
|
||||
@@ -38,7 +54,7 @@ std::string compile_dag_to_glsl(const std::vector<DagStep>& pipeline) {
|
||||
if (ni >= 4) out << ", vec4 d";
|
||||
if (ni > 0) out << ", ";
|
||||
out << "vec2 uv) {\n";
|
||||
out << def->body_glsl(i) << "\n";
|
||||
out << def->body_glsl(base[static_cast<size_t>(i)]) << "\n";
|
||||
out << "}\n\n";
|
||||
}
|
||||
|
||||
@@ -67,8 +83,9 @@ std::string compile_dag_to_glsl(const std::vector<DagStep>& pipeline) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (last_valid_out < 0) return "vec4(0.0, 0.0, 0.0, 1.0)";
|
||||
return "out_" + std::to_string(last_valid_out);
|
||||
// Op/Blend with no source on this slot → black input (cannot fall back to
|
||||
// last_valid_out: that's how nodes "leak" into the canvas without being wired).
|
||||
return "vec4(0.0, 0.0, 0.0, 1.0)";
|
||||
};
|
||||
|
||||
out << " vec4 out_" << i << " = node_" << i << "(";
|
||||
@@ -81,6 +98,7 @@ std::string compile_dag_to_glsl(const std::vector<DagStep>& pipeline) {
|
||||
|
||||
last_valid_out = i;
|
||||
}
|
||||
(void)last_valid_out;
|
||||
|
||||
// Preview branch: if u_preview_target points to a valid out_<i>, emit it
|
||||
// and bail out before the Output-driven fragColor.
|
||||
@@ -95,6 +113,9 @@ std::string compile_dag_to_glsl(const std::vector<DagStep>& pipeline) {
|
||||
// Resolve fragColor: if there's an Output node with a connection, use that; else fallback.
|
||||
auto seed = [&]() { out << " fragColor = vec4(0.04, 0.04, 0.06, 1.0);\n"; };
|
||||
|
||||
// Strict policy: only emit what is wired into the Output node. With no
|
||||
// Output present, or with Output left disconnected, paint the seed color —
|
||||
// never silently fall back to the last evaluated node.
|
||||
if (output_idx >= 0) {
|
||||
const std::string& sid = pipeline[static_cast<size_t>(output_idx)].source_ids[0];
|
||||
int src = -1;
|
||||
@@ -104,9 +125,7 @@ std::string compile_dag_to_glsl(const std::vector<DagStep>& pipeline) {
|
||||
}
|
||||
}
|
||||
if (src >= 0) out << " fragColor = out_" << src << ";\n";
|
||||
else seed();
|
||||
} else if (last_valid_out >= 0) {
|
||||
out << " fragColor = out_" << last_valid_out << ";\n";
|
||||
else seed();
|
||||
} else {
|
||||
seed();
|
||||
}
|
||||
@@ -116,6 +135,56 @@ std::string compile_dag_to_glsl(const std::vector<DagStep>& pipeline) {
|
||||
return out.str();
|
||||
}
|
||||
|
||||
std::string compile_dag_to_glsl_baked(const std::vector<DagStep>& pipeline) {
|
||||
std::string s = compile_dag_to_glsl(pipeline);
|
||||
|
||||
// Compute total vec4 slots actually used by the pipeline.
|
||||
auto base = dag_param_layout(pipeline);
|
||||
int total = 0;
|
||||
for (size_t i = 0; i < pipeline.size(); ++i) {
|
||||
const DagNodeDef* def = dag_find(pipeline[i].name);
|
||||
int pc = def ? static_cast<int>(def->param_defaults.size()) : 0;
|
||||
int v = dag_vec4_count(pc);
|
||||
if (base[i] + v > total) total = base[i] + v;
|
||||
}
|
||||
if (total == 0) total = 1; // GLSL forbids zero-sized arrays
|
||||
|
||||
// Pack current params into a flat float array (same layout as dag_uniforms_apply).
|
||||
std::vector<float> data(static_cast<size_t>(total * 4), 0.0f);
|
||||
for (size_t i = 0; i < pipeline.size(); ++i) {
|
||||
const DagNodeDef* def = dag_find(pipeline[i].name);
|
||||
if (!def) continue;
|
||||
int pc = static_cast<int>(def->param_defaults.size());
|
||||
int b = base[i] * 4;
|
||||
for (int k = 0; k < pc && k < static_cast<int>(pipeline[i].params.size()); ++k) {
|
||||
data[static_cast<size_t>(b + k)] = pipeline[i].params[static_cast<size_t>(k)];
|
||||
}
|
||||
}
|
||||
|
||||
// Build `const vec4 u_params[N] = vec4[N](vec4(...), ...);`
|
||||
std::ostringstream init;
|
||||
init << "const vec4 u_params[" << total << "] = vec4[" << total << "](";
|
||||
for (int i = 0; i < total; ++i) {
|
||||
if (i > 0) init << ", ";
|
||||
init << "vec4("
|
||||
<< data[static_cast<size_t>(i * 4 + 0)] << ", "
|
||||
<< data[static_cast<size_t>(i * 4 + 1)] << ", "
|
||||
<< data[static_cast<size_t>(i * 4 + 2)] << ", "
|
||||
<< data[static_cast<size_t>(i * 4 + 3)] << ")";
|
||||
}
|
||||
init << ");";
|
||||
|
||||
// Replace the uniform u_params declaration with the const array.
|
||||
static const std::regex up_re(R"(uniform\s+vec4\s+u_params\[\d+\];)");
|
||||
s = std::regex_replace(s, up_re, init.str());
|
||||
|
||||
// Replace the u_preview_target uniform with a const = -1 (kills the preview branches).
|
||||
static const std::regex pt_re(R"(uniform\s+int\s+u_preview_target;[^\n]*)");
|
||||
s = std::regex_replace(s, pt_re, "const int u_preview_target = -1;");
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
} // namespace fn::gfx
|
||||
|
||||
#ifdef DAG_COMPILE_TEST
|
||||
@@ -136,22 +205,23 @@ int main() {
|
||||
assert(contains(s, "fragColor = vec4(0.04"));
|
||||
}
|
||||
|
||||
// 2. Single Gen → fragColor = out_0
|
||||
// 2. Single Gen + Output wired → fragColor = out_0
|
||||
{
|
||||
std::vector<DagStep> p;
|
||||
DagStep g; g.id = "a"; g.name = "plasma";
|
||||
p.push_back(g);
|
||||
DagStep g; g.id = "a"; g.name = "plasma"; p.push_back(g);
|
||||
DagStep o; o.id = "out"; o.name = "output"; o.source_ids[0] = "a"; p.push_back(o);
|
||||
auto s = compile_dag_to_glsl(p);
|
||||
assert(contains(s, "vec4 node_0"));
|
||||
assert(contains(s, "vec4 out_0 = node_0("));
|
||||
assert(contains(s, "fragColor = out_0"));
|
||||
}
|
||||
|
||||
// 3. Gen + Op → Op uses out_0 as input a
|
||||
// 3. Gen + Op + Output → Op uses out_0 as input, fragColor = out_1
|
||||
{
|
||||
std::vector<DagStep> p;
|
||||
DagStep g; g.id = "a"; g.name = "plasma"; p.push_back(g);
|
||||
DagStep o; o.id = "b"; o.name = "invert"; o.source_ids[0] = "a"; p.push_back(o);
|
||||
DagStep f; f.id = "out"; f.name = "output"; f.source_ids[0] = "b"; p.push_back(f);
|
||||
auto s = compile_dag_to_glsl(p);
|
||||
assert(contains(s, "out_1 = node_1(out_0, uv)"));
|
||||
assert(contains(s, "fragColor = out_1"));
|
||||
@@ -165,8 +235,26 @@ int main() {
|
||||
DagStep m; m.id = "m"; m.name = "blend_mix";
|
||||
m.source_ids[0] = "a"; m.source_ids[1] = "b";
|
||||
p.push_back(m);
|
||||
DagStep o; o.id = "out"; o.name = "output"; o.source_ids[0] = "m"; p.push_back(o);
|
||||
auto s = compile_dag_to_glsl(p);
|
||||
assert(contains(s, "out_2 = node_2(out_0, out_1, uv)"));
|
||||
assert(contains(s, "fragColor = out_2"));
|
||||
}
|
||||
|
||||
// 4b. Strict mode: nodes without Output → seed (never leaks last node).
|
||||
// Note: the preview branch emits `if (u_preview_target == i) fragColor = out_i;`
|
||||
// which we don't penalise; what matters is the *final* fragColor (after the
|
||||
// preview ifs) — that must be the seed, not a node output.
|
||||
{
|
||||
std::vector<DagStep> p;
|
||||
DagStep g; g.id = "a"; g.name = "plasma"; p.push_back(g);
|
||||
auto s = compile_dag_to_glsl(p);
|
||||
assert(contains(s, "vec4 out_0 = node_0(")); // node still emitted
|
||||
// The seed line must appear *after* the last preview branch
|
||||
size_t seed_pos = s.rfind("fragColor = vec4(0.04");
|
||||
size_t preview_pos = s.rfind("u_preview_target ==");
|
||||
assert(seed_pos != std::string::npos);
|
||||
assert(preview_pos == std::string::npos || seed_pos > preview_pos);
|
||||
}
|
||||
|
||||
// 5. Output node drives fragColor from its source, not from last index
|
||||
@@ -193,7 +281,21 @@ int main() {
|
||||
assert(contains(s, "fragColor = vec4(0.04"));
|
||||
}
|
||||
|
||||
std::printf("dag_compile: 6/6 asserts passed\n");
|
||||
// 7. Baked variant: const arrays, no uniforms u_params / u_preview_target
|
||||
{
|
||||
std::vector<DagStep> p;
|
||||
DagStep g; g.id = "a"; g.name = "plasma"; g.params = {2.0f, 3.0f}; p.push_back(g);
|
||||
DagStep o; o.id = "out"; o.name = "output"; o.source_ids[0] = "a"; p.push_back(o);
|
||||
auto s = compile_dag_to_glsl_baked(p);
|
||||
assert(!contains(s, "uniform vec4 u_params"));
|
||||
assert(!contains(s, "uniform int u_preview_target"));
|
||||
assert(contains(s, "const vec4 u_params["));
|
||||
assert(contains(s, "vec4(2")); // baked first param
|
||||
assert(contains(s, "const int u_preview_target = -1"));
|
||||
assert(contains(s, "fragColor = out_0"));
|
||||
}
|
||||
|
||||
std::printf("dag_compile: 8/8 asserts passed\n");
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user