#include "gfx/gl_loader.h" #include "gfx/dag_node_previews.h" #include "gfx/dag_catalog.h" #include "gfx/gl_framebuffer.h" #include "gfx/fullscreen_quad.h" #include namespace fn::gfx { struct PreviewSlot { Framebuffer fb; bool inited = false; }; static std::unordered_map s_slots; static Quad s_quad; static bool s_quad_init = false; static PreviewSlot& slot_for(unsigned uid) { auto& slot = s_slots[uid]; if (!slot.inited) { fb_init(slot.fb); slot.inited = true; } return slot; } void dag_previews_render(const std::vector& pipeline, unsigned program, int width, int height) { if (!program || pipeline.empty()) return; if (!s_quad_init) { quad_init(s_quad); s_quad_init = true; } // Save GL state we'll touch GLint prev_fbo = 0; glGetIntegerv(GL_FRAMEBUFFER_BINDING, &prev_fbo); GLint prev_vp[4]; glGetIntegerv(GL_VIEWPORT, prev_vp); GLint prev_program = 0; glGetIntegerv(GL_CURRENT_PROGRAM, &prev_program); glUseProgram(program); GLint loc_target = glGetUniformLocation(program, "u_preview_target"); GLint loc_resolution = glGetUniformLocation(program, "u_resolution"); GLint loc_time = glGetUniformLocation(program, "u_time"); if (loc_resolution >= 0) { glUniform2f(loc_resolution, static_cast(width), static_cast(height)); } // u_time and u_params are already set by the main canvas pass for this frame. for (int i = 0; i < static_cast(pipeline.size()); ++i) { const DagStep& step = pipeline[static_cast(i)]; if (!step.preview_open) continue; const DagNodeDef* def = dag_find(step.name); if (!def || def->kind == DagKind::Output) continue; PreviewSlot& slot = slot_for(step.editor_uid); fb_resize(slot.fb, width, height); glBindFramebuffer(GL_FRAMEBUFFER, slot.fb.fbo); glViewport(0, 0, width, height); if (loc_target >= 0) glUniform1i(loc_target, i); if (loc_resolution >= 0) glUniform2f(loc_resolution, static_cast(width), static_cast(height)); quad_draw(s_quad); } // Reset preview target so the main canvas pass renders the real Output if (loc_target >= 0) glUniform1i(loc_target, -1); // Restore state glUseProgram(static_cast(prev_program)); glBindFramebuffer(GL_FRAMEBUFFER, static_cast(prev_fbo)); glViewport(prev_vp[0], prev_vp[1], prev_vp[2], prev_vp[3]); if (loc_time >= 0) {} // silence unused } unsigned dag_preview_texture(unsigned editor_uid) { auto it = s_slots.find(editor_uid); if (it == s_slots.end()) return 0; return it->second.fb.tex; } void dag_previews_destroy() { for (auto& kv : s_slots) { fb_destroy(kv.second.fb); } s_slots.clear(); if (s_quad_init) { quad_destroy(s_quad); s_quad_init = false; } } } // namespace fn::gfx