VapourSynth-llvmexpr
Loading...
Searching...
No Matches
GLSLGenerator.cpp
Go to the documentation of this file.
1
19
20#include "GLSLGenerator.hpp"
21#include "../Sorting.hpp"
22
23#include <algorithm>
24#include <cmath>
25#include <cstdlib>
26#include <format>
27#include <numbers>
28#include <ranges>
29#include <stdexcept>
30
32 const std::vector<Token>& tokens, int num_inputs,
33 int num_intermediate_inputs, [[maybe_unused]] int width,
34 [[maybe_unused]] int height, bool mirror_boundary,
35 const std::map<std::pair<int, std::string>, int>& prop_map,
36 const analysis::ExpressionAnalysisResults& analysis_results)
37 : tokens(tokens), num_inputs(num_inputs),
38 num_intermediate_inputs(num_intermediate_inputs),
39 mirror_boundary(mirror_boundary), prop_map(prop_map),
40 analysis(analysis_results) {
41
42 const auto& var_result = analysis.getVariableUsageResult();
43 for (const auto& var_name : var_result.all_vars) {
44 user_variables.insert(var_name);
45 }
46
47#ifndef NDEBUG
48 if (const char* env = std::getenv("LLVMEXPR_GLSL_STRUCTURIZECFG_DEBUG")) {
49 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
50 debug_structurize_cfg = (env[0] != '\0' && env[0] != '0');
51 }
52#endif
53}
54
55void GLSLGenerator::emit(const std::string& code) { out << code; }
56
57void GLSLGenerator::emitLine(const std::string& code) {
58 for (int i = 0; i < indent_level; ++i) {
59 out << " ";
60 }
61 out << code << "\n";
62}
63
64void GLSLGenerator::emitNewline() { out << "\n"; }
65
66void GLSLGenerator::indent() { ++indent_level; }
67
68void GLSLGenerator::dedent() {
69 if (indent_level > 0) {
70 --indent_level;
71 }
72}
73
74void GLSLGenerator::debugEmitCfgComment() {
75#ifdef NDEBUG
76 return;
77#else
78 if (!debug_structurize_cfg) {
79 return;
80 }
81 const auto& cfg = getCodegenCfgBlocks();
82 const auto& structurize = analysis.getStructurizeCFGResult();
83
84 emitLine("// --- llvmexpr GLSL StructurizeCFG debug ---");
85 emitLine(std::format("// blocks = {}, structurize.success = {}", cfg.size(),
86 structurize.success ? 1 : 0));
87 for (size_t i = 0; i < cfg.size(); ++i) {
88 const auto& b = cfg[i];
89 std::string succs;
90 for (size_t j = 0; j < b.successors.size(); ++j) {
91 succs += std::format("{}{}", b.successors[j],
92 (j + 1 == b.successors.size()) ? "" : ",");
93 }
94 // NOLINTNEXTLINE(cppcoreguidelines-avoid-magic-numbers)
95 int ip = (i < structurize.ipdom.size()) ? structurize.ipdom[i] : -999;
96 emitLine(std::format("// B{}: [{}..{}) succ=[{}] ipdom={}", i,
97 b.start_token_idx, b.end_token_idx, succs, ip));
98 }
99 for (const auto& [hdr, follow] : structurize.loop_follow) {
100 emitLine(std::format("// loop header {} follow {}", hdr, follow));
101 }
102 emitLine("// --- end structurize debug ---");
103#endif
104}
105
106const std::vector<analysis::CFGBlock>&
107GLSLGenerator::getCodegenCfgBlocks() const {
108 const auto& structurize = analysis.getStructurizeCFGResult();
109 if (!structurize.structured_cfg_blocks.empty()) {
110 return structurize.structured_cfg_blocks;
111 }
112 return analysis.getCFGBlocks();
113}
114
115const std::vector<int>& GLSLGenerator::getCodegenStackDepthIn() const {
116 const auto& structurize = analysis.getStructurizeCFGResult();
117 if (!structurize.structured_stack_depth_in.empty()) {
118 return structurize.structured_stack_depth_in;
119 }
120 return analysis.getStackDepthIn();
121}
122
123int GLSLGenerator::computeBranchJoin(int t, int f, int stop_block) const {
124 const auto& structurize = analysis.getStructurizeCFGResult();
125 int join = lcaPostdom(t, f, structurize.ipdom);
126 if (join == -1 && stop_block != -1) {
127 join = stop_block;
128 }
129 return join;
130}
131
132std::string GLSLGenerator::getLoopBreakFlag(int header) {
133 auto it = loop_break_flags.find(header);
134 if (it != loop_break_flags.end()) {
135 return it->second;
136 }
137 std::string name = std::format("_brk_{}", break_flag_counter++);
138 loop_break_flags.emplace(header, name);
139 return name;
140}
141
142std::optional<int>
143GLSLGenerator::findEnclosingLoopForFollow(int target_block,
144 const LoopContext& loop_ctx) const {
145 const auto& structurize = analysis.getStructurizeCFGResult();
146 for (int header : loop_ctx.header_stack | std::views::reverse) {
147 auto fit = structurize.loop_follow.find(header);
148 if (fit != structurize.loop_follow.end() &&
149 fit->second == target_block) {
150 return header;
151 }
152 }
153 return std::nullopt;
154}
155
156void GLSLGenerator::emitUnwindBreakIfNeeded(const LoopContext& loop_ctx) {
157 std::string cond;
158 if (structured_exit_enabled) {
159 cond = "_llvmexpr_exit";
160 }
161 for (int header : loop_ctx.header_stack) {
162 auto it = loop_break_flags.find(header);
163 if (it == loop_break_flags.end()) {
164 continue;
165 }
166 if (!cond.empty()) {
167 cond += " || ";
168 }
169 cond += it->second;
170 }
171 if (cond.empty()) {
172 return;
173 }
174
175 emitLine(std::format("if ({}) {{", cond));
176 indent();
177 emitLine("break;");
178 dedent();
179 emitLine("}");
180}
181
182std::string GLSLGenerator::newTemp() {
183 return std::format("t_{}", temp_counter++);
184}
185
186std::string GLSLGenerator::newSlot() {
187 return std::format("s_{}", slot_counter++);
188}
189
190std::string GLSLGenerator::pop() {
191 if (stack.empty()) {
192 throw std::runtime_error("GLSLGenerator: stack underflow");
193 }
194 std::string val = stack.back();
195 stack.pop_back();
196 return val;
197}
198
199void GLSLGenerator::push(const std::string& val) { stack.push_back(val); }
200
201std::string GLSLGenerator::peek(int offset) const {
202 if (static_cast<size_t>(offset) >= stack.size()) {
203 throw std::runtime_error("GLSLGenerator: invalid stack peek");
204 }
205 return stack[stack.size() - 1 - offset];
206}
207
208std::string GLSLGenerator::floatLiteral(double val) {
209 if (std::isnan(val)) {
210 return "(0.0 / 0.0)"; // NaN
211 }
212 if (std::isinf(val)) {
213 return val > 0 ? "(1.0 / 0.0)" : "(-1.0 / 0.0)";
214 }
215 std::string s = std::format("{:.10g}", val);
216 if (s.find('.') == std::string::npos && s.find('e') == std::string::npos) {
217 s += ".0";
218 }
219 return s;
220}
221
222std::string GLSLGenerator::binaryOp(const std::string& op) {
223 std::string b = pop();
224 std::string a = pop();
225 std::string temp = newTemp();
226 emitLine(std::format("float {} = {} {} {};", temp, a, op, b));
227 return temp;
228}
229
230std::string GLSLGenerator::binaryCmp(const std::string& op) {
231 std::string b = pop();
232 std::string a = pop();
233 std::string temp = newTemp();
234 emitLine(std::format("float {} = ({} {} {}) ? 1.0 : 0.0;", temp, a, op, b));
235 return temp;
236}
237
238std::string GLSLGenerator::unaryFn(const std::string& fn) {
239 std::string a = pop();
240 std::string temp = newTemp();
241 emitLine(std::format("float {} = {}({});", temp, fn, a));
242 return temp;
243}
244
245std::string GLSLGenerator::binaryFn(const std::string& fn) {
246 std::string b = pop();
247 std::string a = pop();
248 std::string temp = newTemp();
249 emitLine(std::format("float {} = {}({}, {});", temp, fn, a, b));
250 return temp;
251}
252
253std::string GLSLGenerator::emitClampCoord(const std::string& coord,
254 const std::string& max_dim) {
255 std::string temp = newTemp();
256 emitLine(
257 std::format("int {} = clamp({}, 0, {} - 1);", temp, coord, max_dim));
258 return temp;
259}
260
261std::string GLSLGenerator::emitMirrorCoord(const std::string& coord,
262 const std::string& max_dim) {
263 std::string temp = newTemp();
264 emitLine(std::format("int {};", temp));
265 emitLine("{");
266 indent();
267 emitLine(std::format("int _period = 2 * ({});", max_dim));
268 emitLine(
269 std::format("int _mod = int(mod(float({}), float(_period)));", coord));
270 emitLine(std::format("if (_mod >= ({})) {{ {} = _period - 1 - _mod; }}",
271 max_dim, temp));
272 emitLine(std::format("else {{ {} = _mod; }}", temp));
273 dedent();
274 emitLine("}");
275 return temp;
276}
277
278std::string GLSLGenerator::emitFinalCoord(const std::string& coord,
279 const std::string& max_dim,
280 bool use_mirror) {
281 if (use_mirror) {
282 return emitMirrorCoord(coord, max_dim);
283 }
284 return emitClampCoord(coord, max_dim);
285}
286
287std::string GLSLGenerator::emitPixelIndex(const std::string& x,
288 const std::string& y) {
289 std::string temp = newTemp();
290 emitLine(
291 std::format("uint {} = uint({}) + uint({}) * pc.width;", temp, x, y));
292 return temp;
293}
294
295std::string GLSLGenerator::emitPixelLoad(int clip_idx, const std::string& x,
296 const std::string& y,
297 bool use_mirror) {
298 std::string final_x = emitFinalCoord(x, "int(pc.width)", use_mirror);
299 std::string final_y = emitFinalCoord(y, "int(pc.height)", use_mirror);
300 std::string idx = emitPixelIndex(final_x, final_y);
301 std::string temp = newTemp();
302 emitLine(std::format("float {} = src{}.data[{}];", temp, clip_idx, idx));
303 return temp;
304}
305
307 out.str("");
308 out.clear();
309 indent_level = 0;
310 temp_counter = 0;
311 slot_counter = 0;
312 break_flag_counter = 0;
313 loop_break_flags.clear();
314 stack.clear();
315
316 emitHeader();
317 emitBufferDeclarations();
318 emitHelperFunctions();
319 emitMainFunction();
320
321 return out.str();
322}
323
324void GLSLGenerator::emitHeader() {
325 emitLine("#version 450");
326 emitLine("#extension GL_EXT_scalar_block_layout : enable");
327 emitNewline();
328 emitLine(
329 "layout(local_size_x = 256, local_size_y = 1, local_size_z = 1) in;");
330 emitNewline();
331 emitLine("layout(push_constant) uniform PushConstants {");
332 indent();
333 emitLine("uint width;");
334 emitLine("uint height;");
335 emitLine("uint numInputs;");
336 emitLine("int frameNumber;");
337 dedent();
338 emitLine("} pc;");
339 emitNewline();
340}
341
342void GLSLGenerator::emitBufferDeclarations() {
343 // Input buffers
344 for (int i = 0; i < num_inputs; ++i) {
345 emitLine(std::format("layout(std430, set = 0, binding = {}) readonly "
346 "buffer InputBuffer{} {{",
347 i, i));
348 indent();
349 emitLine("float data[];");
350 dedent();
351 emitLine(std::format("}} src{};", i));
352 emitNewline();
353 }
354
355 // Intermediate buffers
356 for (int i = 0; i < num_intermediate_inputs; ++i) {
357 emitLine(std::format("layout(std430, set = 0, binding = {}) readonly "
358 "buffer IntermediateBuffer{} {{",
359 num_inputs + i, i));
360 indent();
361 emitLine("float data[];");
362 dedent();
363 emitLine(std::format("}} buf{};", i));
364 emitNewline();
365 }
366
367 // Output buffer
368 emitLine(std::format("layout(std430, set = 0, binding = {}) writeonly "
369 "buffer OutputBuffer {{",
370 num_inputs + num_intermediate_inputs));
371 indent();
372 emitLine("float data[];");
373 dedent();
374 emitLine("} dst;");
375 emitNewline();
376
377 // Props buffer
378 emitLine(std::format(
379 "layout(std430, set = 0, binding = {}) readonly buffer PropsBuffer {{",
380 num_inputs + num_intermediate_inputs + 1));
381 indent();
382 emitLine("float props[];");
383 dedent();
384 emitLine("} propsData;");
385 emitNewline();
386}
387
388void GLSLGenerator::emitHelperFunctions() {
389 // The OpenGLĀ® Shading Language, Version 4.60.8, Chapter 8.3
390 // genFType round(genFType x), genDType round(genDType x):
391 // Returns a value equal to the nearest integer to x.
392 // The fraction 0.5 will round in a direction chosen
393 // by the implementation, presumably the
394 // direction that is fastest. This includes the
395 // possibility that round(x) returns the same value
396 // as roundEven(x) for all values of x
397
398 // We implement halfway cases round away from zero to match CPU semantics.
399 emitLine("float llvmexpr_round(float x) {");
400 indent();
401 emitLine("return (x >= 0.0) ? floor(x + 0.5) : ceil(x - 0.5);");
402 dedent();
403 emitLine("}");
404 emitNewline();
405}
406
407void GLSLGenerator::emitVariableDeclarations() {
408 // User-defined variables
409 for (const auto& var_name : user_variables) {
410 emitLine(std::format("float u_{};", var_name));
411 }
412
413 // TODO: Move to analysis/
414 for (const auto& token : tokens) {
415 if (token.type == TokenType::ArrayAllocStatic) {
416 const auto& payload = std::get<TokenPayloadArrayOp>(token.payload);
417 if (!arrays.contains(payload.name)) {
418 arrays[payload.name] = payload.static_size;
419 emitLine(std::format("float a_{}[{}];", payload.name,
420 payload.static_size));
421 }
422 }
423 }
424
425 // Stack slot variables for merge points
426 const auto& cfg_blocks = getCodegenCfgBlocks();
427 const auto& stack_depth_in = getCodegenStackDepthIn();
428 const auto& structurize = analysis.getStructurizeCFGResult();
429
430 std::set<int> force_slots;
431 for (const auto& [header, follow] : structurize.loop_follow) {
432 if (follow != -1) {
433 force_slots.insert(follow);
434 }
435 }
436
437 for (size_t i = 0; i < cfg_blocks.size(); ++i) {
438 if (cfg_blocks[i].predecessors.size() > 1 ||
439 force_slots.contains((int)i)) {
440 int depth = stack_depth_in[i];
441 std::vector<std::string> slots;
442 for (int j = 0; j < depth; ++j) {
443 std::string slot = newSlot();
444 emitLine(std::format("float {};", slot));
445 slots.push_back(slot);
446 }
447 block_entry_stack[static_cast<int>(i)] = slots;
448 }
449 }
450}
451
452void GLSLGenerator::emitMainFunction() {
453 emitLine("void main() {");
454 indent();
455
456 emitLine("uint gid = gl_GlobalInvocationID.x;");
457 emitLine("uint totalPixels = pc.width * pc.height;");
458 emitNewline();
459 emitLine("if (gid >= totalPixels) {");
460 indent();
461 emitLine("return;");
462 dedent();
463 emitLine("}");
464 emitNewline();
465
466 emitLine("int X = int(gid % pc.width);");
467 emitLine("int Y = int(gid / pc.width);");
468 emitNewline();
469
470 emitVariableDeclarations();
471 emitNewline();
472
473 debugEmitCfgComment();
474 emitMainFunctionStructured();
475
476 dedent();
477 emitLine("}");
478}
479
480void GLSLGenerator::emitMainFunctionStateMachine() {
481 const auto& cfg_blocks = getCodegenCfgBlocks();
482
483 emitLine("int _state = 0;");
484 emitLine("float _result = 0.0;");
485 emitNewline();
486 emitLine("while (_state != -1) {");
487 indent();
488 emitLine("switch (_state) {");
489
490 for (size_t i = 0; i < cfg_blocks.size(); ++i) {
491 emitLine(std::format("case {}:", i));
492 indent();
493
494 if (block_entry_stack.contains(static_cast<int>(i))) {
495 stack = block_entry_stack[static_cast<int>(i)];
496 }
497
498 emitBlockCode(static_cast<int>(i));
499
500 const auto& block = cfg_blocks[i];
501 if (block.successors.empty()) {
502 if (!stack.empty()) {
503 std::string result = pop();
504 emitLine(std::format("_result = {};", result));
505 }
506 emitLine("_state = -1;");
507 } else if (block.successors.size() == 1) {
508 int next = block.successors[0];
509 emitStackToEntrySlots(next);
510 emitLine(std::format("_state = {};", next));
511 } else {
512 std::string cond = pop();
513 int true_target = block.successors[0];
514 int false_target = block.successors[1];
515 emitStackToEntrySlots(true_target);
516 emitStackToEntrySlots(false_target);
517 emitLine(std::format("_state = ({} > 0.0) ? {} : {};", cond,
518 true_target, false_target));
519 }
520
521 emitLine("break;");
522 dedent();
523 }
524
525 emitLine("}"); // end switch
526 dedent();
527 emitLine("}"); // end while
528 emitNewline();
529
530 emitLine("if (floatBitsToUint(_result) != 0x7FC0E71Fu) {");
531 indent();
532 emitLine("dst.data[gid] = _result;");
533 dedent();
534 emitLine("}");
535}
536
537void GLSLGenerator::emitSetResultAndExit(const std::string& result_expr) {
538 emitLine(std::format("_result = {};", result_expr));
539 emitLine("_llvmexpr_exit = true;");
540 emitLine("break;");
541}
542
543void GLSLGenerator::emitResultEpilogueStore() {
544 emitLine("if (floatBitsToUint(_result) != 0x7FC0E71Fu) {");
545 indent();
546 emitLine("dst.data[gid] = _result;");
547 dedent();
548 emitLine("}");
549 emitLine("return;");
550}
551
552bool GLSLGenerator::isLoopHeaderActive(int header,
553 const LoopContext& loop_ctx) const {
554 return std::ranges::find(loop_ctx.header_stack, header) !=
555 loop_ctx.header_stack.end();
556}
557
558bool GLSLGenerator::canEdgeToBlock(int target_block, int stop_block,
559 LoopContext& loop_ctx) const {
560 CanKey key{.start_block = target_block,
561 .stop_block = stop_block,
562 .header_stack = loop_ctx.header_stack};
563 if (auto it = can_edge_cache.find(key); it != can_edge_cache.end()) {
564 return it->second;
565 }
566 if (can_edge_in_progress.contains(key)) {
567 return false;
568 }
569 can_edge_in_progress.insert(key);
570
571 auto finish = [&](bool ok) -> bool {
572 can_edge_in_progress.erase(key);
573 can_edge_cache.emplace(std::move(key), ok);
574 return ok;
575 };
576
577 try {
578 const auto& structurize = analysis.getStructurizeCFGResult();
579 int current_header =
580 loop_ctx.header_stack.empty() ? -1 : loop_ctx.header_stack.back();
581
582 if (target_block == stop_block) {
583 if (current_header == -1) {
584 return finish(true);
585 }
586 int follow = -1;
587 auto it = structurize.loop_follow.find(current_header);
588 if (it != structurize.loop_follow.end()) {
589 follow = it->second;
590 }
591 if (stop_block == follow) {
592 return finish(true);
593 }
594 if (structurize.inLoop(current_header, stop_block)) {
595 return finish(true);
596 }
597 // Leaving to an outer follow via break-flag lowering.
598 return finish(
599 findEnclosingLoopForFollow(stop_block, loop_ctx).has_value());
600 }
601
602 if (current_header != -1) {
603 int follow = -1;
604 auto it = structurize.loop_follow.find(current_header);
605 if (it != structurize.loop_follow.end()) {
606 follow = it->second;
607 }
608 if (target_block == current_header) {
609 return finish(true); // continue
610 }
611 if (target_block == follow) {
612 return finish(true); // break
613 }
614 if (!structurize.inLoop(current_header, target_block)) {
615 // Multi-level break to an outer loop follow.
616 return finish(findEnclosingLoopForFollow(target_block, loop_ctx)
617 .has_value());
618 }
619 }
620 return finish(canStructureFrom(target_block, stop_block, loop_ctx));
621 } catch (...) {
622 can_edge_in_progress.erase(key);
623 throw;
624 }
625}
626
627bool GLSLGenerator::canStructureFrom(int start_block, int stop_block,
628 LoopContext& loop_ctx) const {
629 CanKey key{.start_block = start_block,
630 .stop_block = stop_block,
631 .header_stack = loop_ctx.header_stack};
632 if (auto it = can_structure_cache.find(key);
633 it != can_structure_cache.end()) {
634 return it->second;
635 }
636 if (can_structure_in_progress.contains(key)) {
637 return false;
638 }
639 can_structure_in_progress.insert(key);
640
641 struct Visitor {
642 const GLSLGenerator* gen;
643
644 bool handleLoop(int block, int follow, LoopContext& ctx) const {
645 ctx.header_stack.push_back(block);
646 bool ok = gen->canStructureFrom(block, follow, ctx);
647 ctx.header_stack.pop_back();
648 return ok;
649 }
650 [[nodiscard]] bool visitBlock(int /*block*/) const { return true; }
651 [[nodiscard]] bool handleNoSuccessors(int /*block*/) const {
652 return true;
653 }
654 [[nodiscard]] bool handleLoopExitOrContinue(int /*block*/, int /*next*/,
655 int /*current_header*/,
656 int /*follow*/) const {
657 return true;
658 }
659 [[nodiscard]] bool handleSimpleEdge(int /*block*/, int /*next*/) const {
660 return true;
661 }
662 [[nodiscard]] bool handleNonlocalEdge(int /*block*/, int next,
663 int stop_block,
664 const LoopContext& ctx) const {
665 auto saved = ctx;
666 return gen->canEdgeToBlock(next, stop_block, saved);
667 }
668 [[nodiscard]] bool handleBranch(int /*block*/, int t, int f, int join,
669 int /*stop_block*/,
670 const LoopContext& ctx) const {
671 auto saved_t = ctx;
672 auto saved_f = ctx;
673 return gen->canEdgeToBlock(t, join, saved_t) &&
674 gen->canEdgeToBlock(f, join, saved_f);
675 }
676 [[nodiscard]] bool handleLoopBreak(int /*join*/) const { return true; }
677 } visitor{this};
678
679 try {
680 bool ok = traverseStructure(start_block, stop_block, loop_ctx, visitor);
681 can_structure_in_progress.erase(key);
682 can_structure_cache.emplace(std::move(key), ok);
683 return ok;
684 } catch (...) {
685 can_structure_in_progress.erase(key);
686 throw;
687 }
688}
689
690int GLSLGenerator::lcaPostdom(int a, int b,
691 const std::vector<int>& ipdom) const {
692 if (a == -1 || b == -1) {
693 return -1;
694 }
695 std::set<int> ancestors;
696 int x = a;
697 while (x != -1) {
698 ancestors.insert(x);
699 if (x < 0 || static_cast<size_t>(x) >= ipdom.size()) {
700 break;
701 }
702 x = ipdom[x];
703 }
704 x = b;
705 while (x != -1) {
706 if (ancestors.contains(x)) {
707 return x;
708 }
709 if (x < 0 || static_cast<size_t>(x) >= ipdom.size()) {
710 break;
711 }
712 x = ipdom[x];
713 }
714 return -1;
715}
716
717void GLSLGenerator::emitStackToEntrySlots(int target_block) {
718 if (!block_entry_stack.contains(target_block)) {
719 return;
720 }
721 const auto& slots = block_entry_stack[target_block];
722 for (size_t j = 0; j < slots.size() && j < stack.size(); ++j) {
723 emitLine(std::format("{} = {};", slots[j], stack[j]));
724 }
725}
726
727void GLSLGenerator::emitEdgeToBlock(int target_block, int stop_block,
728 LoopContext& loop_ctx, bool& ok) {
729 if (!ok) {
730 return;
731 }
732
733 const auto& structurize = analysis.getStructurizeCFGResult();
734 int current_header =
735 loop_ctx.header_stack.empty() ? -1 : loop_ctx.header_stack.back();
736
737 if (current_header != -1) {
738 int follow = -1;
739 auto it = structurize.loop_follow.find(current_header);
740 if (it != structurize.loop_follow.end()) {
741 follow = it->second;
742 }
743
744 // Jumping to the stop block inside a loop exits the loop.
745 if (target_block == stop_block && stop_block == follow) {
746 emitStackToEntrySlots(stop_block);
747 emitLine("break;");
748 return;
749 }
750
751 if (target_block == current_header) {
752 emitStackToEntrySlots(target_block);
753 emitLine("continue;");
754 return;
755 }
756 if (target_block == follow) {
757 emitStackToEntrySlots(follow);
758 emitLine("break;");
759 return;
760 }
761 if (!structurize.inLoop(current_header, target_block)) {
762 // Multi-level break to an outer loop follow.
763 auto outer = findEnclosingLoopForFollow(target_block, loop_ctx);
764 if (!outer.has_value()) {
765 ok = false;
766 return;
767 }
768 emitStackToEntrySlots(target_block);
769 emitLine(std::format("{} = true;", getLoopBreakFlag(*outer)));
770 emitLine("break;");
771 return;
772 }
773 }
774
775 if (target_block == stop_block) {
776 emitStackToEntrySlots(target_block);
777 return;
778 }
779
780 emitStackToEntrySlots(target_block);
781 emitStructuredFrom(target_block, stop_block, loop_ctx, ok);
782}
783
784void GLSLGenerator::emitStructuredFrom(int start_block, int stop_block,
785 LoopContext& loop_ctx, bool& ok) {
786 if (!ok) {
787 return;
788 }
789
790 struct Visitor {
791 GLSLGenerator* gen;
792 bool& ok;
793
794 bool handleLoop(int block, int follow, LoopContext& ctx) const {
795 std::string flag = gen->getLoopBreakFlag(block);
796 gen->emitLine(std::format("bool {} = false;", flag));
797 gen->emitLine("while (true) {");
798 gen->indent();
799 ctx.header_stack.push_back(block);
800 gen->emitStructuredFrom(block, follow, ctx, ok);
801 ctx.header_stack.pop_back();
802 gen->dedent();
803 gen->emitLine("}");
804 gen->emitUnwindBreakIfNeeded(ctx);
805 return ok;
806 }
807 [[nodiscard]] bool visitBlock(int block) const {
808 if (gen->block_entry_stack.contains(block)) {
809 gen->stack = gen->block_entry_stack[block];
810 }
811 gen->emitBlockCode(block);
812 return true;
813 }
814 [[nodiscard]] bool handleNoSuccessors(int /*block*/) const {
815 std::string result_expr = gen->stack.empty() ? "0.0" : gen->pop();
816 gen->emitSetResultAndExit(result_expr);
817 return true;
818 }
819 [[nodiscard]] bool handleLoopExitOrContinue(int /*block*/, int next,
820 int current_header,
821 int follow) const {
822 if (next == current_header) {
823 gen->emitStackToEntrySlots(next);
824 gen->emitLine("continue;");
825 return true;
826 }
827 if (next == follow) {
828 gen->emitStackToEntrySlots(next);
829 gen->emitLine("break;");
830 return true;
831 }
832 ok = false;
833 return false;
834 }
835 [[nodiscard]] bool handleSimpleEdge(int /*block*/, int next) const {
836 gen->emitStackToEntrySlots(next);
837 return true;
838 }
839 [[nodiscard]] bool handleNonlocalEdge(int /*block*/, int next,
840 int stop_block,
841 LoopContext& ctx) const {
842 gen->emitEdgeToBlock(next, stop_block, ctx, ok);
843 return ok;
844 }
845 bool handleBranch(int /*block*/, int t, int f, int join,
846 int /*stop_block*/, LoopContext& ctx) const {
847 std::string cond = gen->pop();
848 auto base_stack = gen->stack; // stack after popping condition
849
850 if (join == f) {
851 gen->emitLine(std::format("if ({} > 0.0) {{", cond));
852 gen->indent();
853 gen->emitEdgeToBlock(t, join, ctx, ok);
854 gen->dedent();
855 gen->emitLine("}");
856 gen->stack = base_stack;
857 gen->emitUnwindBreakIfNeeded(ctx);
858 return ok;
859 }
860
861 if (join == t) {
862 gen->emitLine(std::format("if (!({} > 0.0)) {{", cond));
863 gen->indent();
864 gen->emitEdgeToBlock(f, join, ctx, ok);
865 gen->dedent();
866 gen->emitLine("}");
867 gen->stack = base_stack;
868 gen->emitUnwindBreakIfNeeded(ctx);
869 return ok;
870 }
871
872 gen->emitLine(std::format("if ({} > 0.0) {{", cond));
873 gen->indent();
874 gen->emitEdgeToBlock(t, join, ctx, ok);
875 gen->dedent();
876 gen->emitLine("} else {");
877 gen->indent();
878 gen->stack = base_stack;
879 gen->emitEdgeToBlock(f, join, ctx, ok);
880 gen->dedent();
881 gen->emitLine("}");
882 gen->stack = base_stack;
883 gen->emitUnwindBreakIfNeeded(ctx);
884 return ok;
885 }
886 [[nodiscard]] bool handleLoopBreak(int join) const {
887 gen->emitStackToEntrySlots(join);
888 gen->emitLine("break;");
889 return true;
890 }
891 } visitor{.gen = this, .ok = ok};
892
893 if (!traverseStructure(start_block, stop_block, loop_ctx, visitor)) {
894 ok = false;
895 }
896}
897
898void GLSLGenerator::emitMainFunctionStructured() {
899 LoopContext loop_ctx;
900 bool ok = analysis.getStructurizeCFGResult().success &&
901 canStructureFrom(0, -1, loop_ctx);
902
903 if (!ok) {
904#ifndef NDEBUG
905 if (debug_structurize_cfg) {
906 emitLine("// structurize: preflight can_structure_from() failed. "
907 "falling back to state machine");
908 }
909#endif
910 emitMainFunctionStateMachine();
911 return;
912 }
913
914 loop_ctx = {};
915 bool emit_ok = true;
916 emitLine("{");
917 indent();
918
919 emitLine("float _result = 0.0;");
920 emitLine("bool _llvmexpr_exit = false;");
921 emitLine("do {");
922 indent();
923
924 structured_exit_enabled = true;
925 emitStructuredFrom(0, -1, loop_ctx, emit_ok);
926 structured_exit_enabled = false;
927
928 dedent();
929 emitLine("} while (false);");
930 emitNewline();
931 emitResultEpilogueStore();
932
933 dedent();
934 emitLine("}");
935
936 // Should be unreachable
937 // Fallback: structuring unexpectedly failed at emit time"
938 // TODO: Raise a exception instead
939 if (!emit_ok) {
940 emitNewline();
941#ifndef NDEBUG
942 if (debug_structurize_cfg) {
943 emitLine("// structurize: unexpected emit-time failure. falling "
944 "back "
945 "to state machine");
946 }
947#endif
948 emitMainFunctionStateMachine();
949 }
950}
951
952void GLSLGenerator::emitBlockCode(int block_idx) {
953 const auto& cfg_blocks = getCodegenCfgBlocks();
954 const auto& block = cfg_blocks[block_idx];
955
956 for (int i = block.start_token_idx; i < block.end_token_idx; ++i) {
957 processToken(tokens[i]);
958 }
959}
960
961void GLSLGenerator::processToken(const Token& token) {
962 switch (token.type) {
963
964 // Literals & Constants
965 case TokenType::Number: {
966 const auto& payload = std::get<TokenPayloadNumber>(token.payload);
967 push(floatLiteral(payload.value));
968 break;
969 }
971 push("float(X)");
972 break;
974 push("float(Y)");
975 break;
977 push("float(pc.width)");
978 break;
980 push("float(pc.height)");
981 break;
983 push("float(pc.frameNumber)");
984 break;
986 push(floatLiteral(std::numbers::pi));
987 break;
988
989 // Binary Operators
990 case TokenType::Add:
991 push(binaryOp("+"));
992 break;
993 case TokenType::Sub:
994 push(binaryOp("-"));
995 break;
996 case TokenType::Mul:
997 push(binaryOp("*"));
998 break;
999 case TokenType::Div:
1000 push(binaryOp("/"));
1001 break;
1002 case TokenType::Mod: {
1003 std::string b = pop();
1004 std::string a = pop();
1005 std::string temp = newTemp();
1006 emitLine(std::format("float {} = mod({}, {});", temp, a, b));
1007 push(temp);
1008 break;
1009 }
1010 case TokenType::Pow:
1011 push(binaryFn("pow"));
1012 break;
1013 case TokenType::Min:
1014 push(binaryFn("min"));
1015 break;
1016 case TokenType::Max:
1017 push(binaryFn("max"));
1018 break;
1019 case TokenType::Atan2: {
1020 std::string x_val = pop();
1021 std::string y_val = pop();
1022 std::string temp = newTemp();
1023 emitLine(std::format("float {} = atan({}, {});", temp, y_val, x_val));
1024 push(temp);
1025 break;
1026 }
1027 case TokenType::Copysign: {
1028 std::string sign_val = pop();
1029 std::string mag_val = pop();
1030 std::string temp = newTemp();
1031 emitLine(std::format("float {} = sign({}) * abs({});", temp, sign_val,
1032 mag_val));
1033 push(temp);
1034 break;
1035 }
1036
1037 // Comparisons
1038 case TokenType::Gt:
1039 push(binaryCmp(">"));
1040 break;
1041 case TokenType::Lt:
1042 push(binaryCmp("<"));
1043 break;
1044 case TokenType::Ge:
1045 push(binaryCmp(">="));
1046 break;
1047 case TokenType::Le:
1048 push(binaryCmp("<="));
1049 break;
1050 case TokenType::Eq:
1051 push(binaryCmp("=="));
1052 break;
1053
1054 // Logical operators
1055 case TokenType::And: {
1056 std::string b = pop();
1057 std::string a = pop();
1058 std::string temp = newTemp();
1059 emitLine(std::format(
1060 "float {} = (({} > 0.0) && ({} > 0.0)) ? 1.0 : 0.0;", temp, a, b));
1061 push(temp);
1062 break;
1063 }
1064 case TokenType::Or: {
1065 std::string b = pop();
1066 std::string a = pop();
1067 std::string temp = newTemp();
1068 emitLine(std::format(
1069 "float {} = (({} > 0.0) || ({} > 0.0)) ? 1.0 : 0.0;", temp, a, b));
1070 push(temp);
1071 break;
1072 }
1073 case TokenType::Xor: {
1074 std::string b = pop();
1075 std::string a = pop();
1076 std::string temp = newTemp();
1077 emitLine(std::format(
1078 "float {} = (({} > 0.0) != ({} > 0.0)) ? 1.0 : 0.0;", temp, a, b));
1079 push(temp);
1080 break;
1081 }
1082 case TokenType::Not: {
1083 std::string a = pop();
1084 std::string temp = newTemp();
1085 emitLine(std::format("float {} = ({} <= 0.0) ? 1.0 : 0.0;", temp, a));
1086 push(temp);
1087 break;
1088 }
1089
1090 // Bitwise operators
1091 case TokenType::Bitand: {
1092 std::string b = pop();
1093 std::string a = pop();
1094 std::string temp = newTemp();
1095 emitLine(std::format("float {} = float(int(llvmexpr_round({})) & "
1096 "int(llvmexpr_round({})));",
1097 temp, a, b));
1098 push(temp);
1099 break;
1100 }
1101 case TokenType::Bitor: {
1102 std::string b = pop();
1103 std::string a = pop();
1104 std::string temp = newTemp();
1105 emitLine(std::format("float {} = float(int(llvmexpr_round({})) | "
1106 "int(llvmexpr_round({})));",
1107 temp, a, b));
1108 push(temp);
1109 break;
1110 }
1111 case TokenType::Bitxor: {
1112 std::string b = pop();
1113 std::string a = pop();
1114 std::string temp = newTemp();
1115 emitLine(std::format("float {} = float(int(llvmexpr_round({})) ^ "
1116 "int(llvmexpr_round({})));",
1117 temp, a, b));
1118 push(temp);
1119 break;
1120 }
1121 case TokenType::Bitnot: {
1122 std::string a = pop();
1123 std::string temp = newTemp();
1124 emitLine(std::format("float {} = float(~int(llvmexpr_round({})));",
1125 temp, a));
1126 push(temp);
1127 break;
1128 }
1129
1130 // Unary math functions
1131 case TokenType::Sqrt: {
1132 std::string a = pop();
1133 std::string temp = newTemp();
1134 emitLine(std::format("float {} = sqrt(max({}, 0.0));", temp, a));
1135 push(temp);
1136 break;
1137 }
1138 case TokenType::Exp:
1139 push(unaryFn("exp"));
1140 break;
1141 case TokenType::Log:
1142 push(unaryFn("log"));
1143 break;
1144 case TokenType::Abs:
1145 push(unaryFn("abs"));
1146 break;
1147 case TokenType::Floor:
1148 push(unaryFn("floor"));
1149 break;
1150 case TokenType::Ceil:
1151 push(unaryFn("ceil"));
1152 break;
1153 case TokenType::Trunc:
1154 push(unaryFn("trunc"));
1155 break;
1156 case TokenType::Round:
1157 push(unaryFn("llvmexpr_round"));
1158 break;
1159 case TokenType::Sin:
1160 push(unaryFn("sin"));
1161 break;
1162 case TokenType::Cos:
1163 push(unaryFn("cos"));
1164 break;
1165 case TokenType::Tan:
1166 push(unaryFn("tan"));
1167 break;
1168 case TokenType::Asin:
1169 push(unaryFn("asin"));
1170 break;
1171 case TokenType::Acos:
1172 push(unaryFn("acos"));
1173 break;
1174 case TokenType::Atan:
1175 push(unaryFn("atan"));
1176 break;
1177 case TokenType::Exp2:
1178 push(unaryFn("exp2"));
1179 break;
1180 case TokenType::Log10: {
1181 // log10(x) = log(x) / log(10)
1182 std::string a = pop();
1183 std::string temp = newTemp();
1184 emitLine(std::format("float {} = log({}) / log(10.0);", temp, a));
1185 push(temp);
1186 break;
1187 }
1188 case TokenType::Log2:
1189 push(unaryFn("log2"));
1190 break;
1191 case TokenType::Sinh:
1192 push(unaryFn("sinh"));
1193 break;
1194 case TokenType::Cosh:
1195 push(unaryFn("cosh"));
1196 break;
1197 case TokenType::Tanh:
1198 push(unaryFn("tanh"));
1199 break;
1200 case TokenType::Sgn: {
1201 std::string a = pop();
1202 std::string temp = newTemp();
1203 emitLine(std::format("float {} = ({} != 0.0) ? sign({}) : 0.0;", temp,
1204 a, a));
1205 push(temp);
1206 break;
1207 }
1208 case TokenType::Neg: {
1209 std::string a = pop();
1210 std::string temp = newTemp();
1211 emitLine(std::format("float {} = -{};", temp, a));
1212 push(temp);
1213 break;
1214 }
1215
1216 // Ternary and multi-arg
1217 case TokenType::Ternary: {
1218 std::string c = pop(); // false branch
1219 std::string b = pop(); // true branch
1220 std::string a = pop(); // condition
1221 std::string temp = newTemp();
1222 emitLine(
1223 std::format("float {} = ({} > 0.0) ? {} : {};", temp, a, b, c));
1224 push(temp);
1225 break;
1226 }
1227 case TokenType::Clip:
1228 case TokenType::Clamp: {
1229 std::string max_val = pop();
1230 std::string min_val = pop();
1231 std::string val = pop();
1232 std::string temp = newTemp();
1233 emitLine(std::format("float {} = clamp({}, {}, {});", temp, val,
1234 min_val, max_val));
1235 push(temp);
1236 break;
1237 }
1238 case TokenType::Fma: {
1239 std::string c = pop();
1240 std::string b = pop();
1241 std::string a = pop();
1242 std::string temp = newTemp();
1243 emitLine(std::format("float {} = fma({}, {}, {});", temp, a, b, c));
1244 push(temp);
1245 break;
1246 }
1247
1248 // Stack manipulation
1249 case TokenType::Dup: {
1250 const auto& payload = std::get<TokenPayloadStackOp>(token.payload);
1251 push(peek(payload.n));
1252 break;
1253 }
1254 case TokenType::Drop: {
1255 const auto& payload = std::get<TokenPayloadStackOp>(token.payload);
1256 for (int i = 0; i < payload.n; ++i) {
1257 (void)pop();
1258 }
1259 break;
1260 }
1261 case TokenType::Swap: {
1262 const auto& payload = std::get<TokenPayloadStackOp>(token.payload);
1263 size_t top_idx = stack.size() - 1;
1264 size_t other_idx = stack.size() - 1 - payload.n;
1265 std::swap(stack[top_idx], stack[other_idx]);
1266 break;
1267 }
1268 case TokenType::SortN: {
1269 const auto& payload = std::get<TokenPayloadStackOp>(token.payload);
1270 int n = payload.n;
1271 if (n < 2) {
1272 break;
1273 }
1274
1275 // Pop n values
1276 std::vector<std::string> values(n);
1277 for (int i = 0; i < n; ++i) {
1278 values[i] = pop();
1279 }
1280
1281 auto network = get_sorting_network(n);
1282 for (const auto& pair : network) {
1283 std::string temp_min = newTemp();
1284 std::string temp_max = newTemp();
1285 int idx1 = pair.first;
1286 int idx2 = pair.second;
1287 emitLine(std::format("float {} = min({}, {});", temp_min,
1288 values[idx1], values[idx2]));
1289 emitLine(std::format("float {} = max({}, {});", temp_max,
1290 values[idx1], values[idx2]));
1291 values[idx1] = temp_min;
1292 values[idx2] = temp_max;
1293 }
1294
1295 // Push back in reverse order (smallest on top)
1296 for (int i = n - 1; i >= 0; --i) {
1297 push(values[i]);
1298 }
1299 break;
1300 }
1301 case TokenType::ArgminN:
1302 case TokenType::ArgmaxN: {
1303 const auto& payload = std::get<TokenPayloadStackOp>(token.payload);
1304 int n = payload.n;
1305 if (n < 1) {
1306 push(floatLiteral(0.0));
1307 break;
1308 }
1309
1310 std::vector<std::string> values(n);
1311 for (int i = 0; i < n; ++i) {
1312 values[i] = pop();
1313 }
1314
1315 struct Node {
1316 std::string val;
1317 std::string idx;
1318 };
1319 std::vector<Node> current_level;
1320 current_level.reserve(n);
1321 for (int i = 0; i < n; ++i) {
1322 current_level.push_back(
1323 {values[i], floatLiteral(static_cast<double>(n - 1 - i))});
1324 }
1325
1326 bool is_max = (token.type == TokenType::ArgmaxN);
1327 std::string cmp = is_max ? ">" : "<";
1328
1329 while (current_level.size() > 1) {
1330 std::vector<Node> next_level;
1331 for (size_t i = 0; i < current_level.size(); i += 2) {
1332 if (i + 1 < current_level.size()) {
1333 std::string winner_val = newTemp();
1334 std::string winner_idx = newTemp();
1335 const auto& left = current_level[i];
1336 const auto& right = current_level[i + 1];
1337
1338 // Prefer smaller original index on tie.
1339 std::string cond = std::format(
1340 "({} {} {}) || ({} == {} && {} < {})", left.val, cmp,
1341 right.val, left.val, right.val, left.idx, right.idx);
1342
1343 emitLine(std::format("float {} = ({}) ? {} : {};",
1344 winner_val, cond, left.val,
1345 right.val));
1346 emitLine(std::format("float {} = ({}) ? {} : {};",
1347 winner_idx, cond, left.idx,
1348 right.idx));
1349 next_level.push_back({winner_val, winner_idx});
1350 } else {
1351 next_level.push_back(current_level[i]);
1352 }
1353 }
1354 current_level = std::move(next_level);
1355 }
1356 push(current_level[0].idx);
1357 break;
1358 }
1359 case TokenType::ArgsortN: {
1360 const auto& payload = std::get<TokenPayloadStackOp>(token.payload);
1361 int n = payload.n;
1362 if (n < 1) {
1363 break;
1364 }
1365 if (n == 1) {
1366 (void)pop();
1367 push(floatLiteral(0.0));
1368 break;
1369 }
1370
1371 std::vector<std::string> values(n);
1372 std::vector<std::string> indices(n);
1373 for (int i = 0; i < n; ++i) {
1374 values[i] = pop();
1375 indices[i] = floatLiteral(static_cast<double>(n - 1 - i));
1376 }
1377
1378 auto network = get_sorting_network(n);
1379 for (const auto& pair : network) {
1380 int i1 = pair.first;
1381 int i2 = pair.second;
1382
1383 std::string cond = std::format(
1384 "({} > {}) || ({} == {} && {} > {})", values[i1], values[i2],
1385 values[i1], values[i2], indices[i1], indices[i2]);
1386
1387 std::string next_v1 = newTemp();
1388 std::string next_v2 = newTemp();
1389 std::string next_i1 = newTemp();
1390 std::string next_i2 = newTemp();
1391
1392 emitLine(std::format("float {} = ({}) ? {} : {};", next_v1, cond,
1393 values[i2], values[i1]));
1394 emitLine(std::format("float {} = ({}) ? {} : {};", next_v2, cond,
1395 values[i1], values[i2]));
1396 emitLine(std::format("float {} = ({}) ? {} : {};", next_i1, cond,
1397 indices[i2], indices[i1]));
1398 emitLine(std::format("float {} = ({}) ? {} : {};", next_i2, cond,
1399 indices[i1], indices[i2]));
1400
1401 values[i1] = next_v1;
1402 values[i2] = next_v2;
1403 indices[i1] = next_i1;
1404 indices[i2] = next_i2;
1405 }
1406
1407 for (int i = n - 1; i >= 0; --i) {
1408 push(indices[i]);
1409 }
1410 break;
1411 }
1412
1413 // Control flow
1415 case TokenType::Jump:
1416 // These are no-ops during token processing
1417 break;
1418
1419 // Variables
1420 case TokenType::VarStore: {
1421 const auto& payload = std::get<TokenPayloadVar>(token.payload);
1422 std::string val = pop();
1423 emitLine(std::format("u_{} = {};", payload.name, val));
1424 break;
1425 }
1426 case TokenType::VarLoad: {
1427 const auto& payload = std::get<TokenPayloadVar>(token.payload);
1428 push(std::format("u_{}", payload.name));
1429 break;
1430 }
1431
1432 // Pixel access
1433 case TokenType::ClipCur: {
1434 const auto& payload = std::get<TokenPayloadClipAccess>(token.payload);
1435 std::string idx_temp = newTemp();
1436 emitLine(std::format("uint {} = gid;", idx_temp));
1437 std::string val_temp = newTemp();
1438 emitLine(std::format("float {} = src{}.data[{}];", val_temp,
1439 payload.clip_idx, idx_temp));
1440 push(val_temp);
1441 break;
1442 }
1443 case TokenType::ClipRel: {
1444 const auto& payload = std::get<TokenPayloadClipAccess>(token.payload);
1445 bool use_mirror =
1446 payload.has_mode ? payload.use_mirror : mirror_boundary;
1447
1448 std::string x_expr = std::format("X + {}", payload.rel_x);
1449 std::string y_expr = std::format("Y + {}", payload.rel_y);
1450
1451 std::string final_x =
1452 emitFinalCoord(x_expr, "int(pc.width)", use_mirror);
1453 std::string final_y =
1454 emitFinalCoord(y_expr, "int(pc.height)", use_mirror);
1455 std::string idx = emitPixelIndex(final_x, final_y);
1456
1457 std::string val_temp = newTemp();
1458 emitLine(std::format("float {} = src{}.data[{}];", val_temp,
1459 payload.clip_idx, idx));
1460 push(val_temp);
1461 break;
1462 }
1463 case TokenType::ClipAbs: {
1464 const auto& payload = std::get<TokenPayloadClipAccess>(token.payload);
1465 std::string coord_y = pop();
1466 std::string coord_x = pop();
1467 bool use_mirror =
1468 payload.has_mode ? payload.use_mirror : mirror_boundary;
1469
1470 std::string x_int = newTemp();
1471 std::string y_int = newTemp();
1472 emitLine(std::format("int {} = int(roundEven({}));", x_int, coord_x));
1473 emitLine(std::format("int {} = int(roundEven({}));", y_int, coord_y));
1474
1475 push(emitPixelLoad(payload.clip_idx, x_int, y_int, use_mirror));
1476 break;
1477 }
1478
1479 case TokenType::BufferCur: {
1480 const auto& payload = std::get<TokenPayloadBufferAccess>(token.payload);
1481 std::string temp = newTemp();
1482 emitLine(std::format("float {} = buf{}.data[gid];", temp,
1483 payload.buffer_idx));
1484 push(temp);
1485 break;
1486 }
1487 case TokenType::BufferRel: {
1488 const auto& payload = std::get<TokenPayloadBufferAccess>(token.payload);
1489 bool use_mirror =
1490 payload.has_mode ? payload.use_mirror : mirror_boundary;
1491
1492 std::string x_expr = std::format("X + {}", payload.rel_x);
1493 std::string y_expr = std::format("Y + {}", payload.rel_y);
1494
1495 std::string final_x =
1496 emitFinalCoord(x_expr, "int(pc.width)", use_mirror);
1497 std::string final_y =
1498 emitFinalCoord(y_expr, "int(pc.height)", use_mirror);
1499 std::string idx = emitPixelIndex(final_x, final_y);
1500
1501 std::string temp = newTemp();
1502 emitLine(std::format("float {} = buf{}.data[{}];", temp,
1503 payload.buffer_idx, idx));
1504 push(temp);
1505 break;
1506 }
1507 case TokenType::BufferAbs: {
1508 const auto& payload = std::get<TokenPayloadBufferAccess>(token.payload);
1509 std::string coord_y = pop();
1510 std::string coord_x = pop();
1511 bool use_mirror =
1512 payload.has_mode ? payload.use_mirror : mirror_boundary;
1513
1514 std::string x_int = newTemp();
1515 std::string y_int = newTemp();
1516
1517 emitLine(std::format("int {} = int(roundEven({}));", x_int, coord_x));
1518 emitLine(std::format("int {} = int(roundEven({}));", y_int, coord_y));
1519
1520 std::string final_x =
1521 emitFinalCoord(x_int, "int(pc.width)", use_mirror);
1522 std::string final_y =
1523 emitFinalCoord(y_int, "int(pc.height)", use_mirror);
1524 std::string idx = emitPixelIndex(final_x, final_y);
1525
1526 std::string temp = newTemp();
1527 emitLine(std::format("float {} = buf{}.data[{}];", temp,
1528 payload.buffer_idx, idx));
1529 push(temp);
1530 break;
1531 }
1532
1533 // Property access
1534 case TokenType::PropAccess: {
1535 const auto& payload = std::get<TokenPayloadPropAccess>(token.payload);
1536 auto key = std::make_pair(payload.clip_idx, payload.prop_name);
1537 int prop_idx = prop_map.at(key);
1538 std::string temp = newTemp();
1539 emitLine(
1540 std::format("float {} = propsData.props[{}];", temp, prop_idx));
1541 push(temp);
1542 break;
1543 }
1544 case TokenType::PropExists: {
1545 const auto& payload = std::get<TokenPayloadPropAccess>(token.payload);
1546 auto key = std::make_pair(payload.clip_idx, payload.prop_name);
1547 if (prop_map.contains(key)) {
1548 int prop_idx = prop_map.at(key);
1549 std::string temp = newTemp();
1550 emitLine(std::format(
1551 "float {} = (floatBitsToUint(propsData.props[{}]) == "
1552 "0x7FC0BEEFu) ? 0.0 : 1.0;",
1553 temp, prop_idx));
1554 push(temp);
1555 } else {
1556 push("0.0");
1557 }
1558 break;
1559 }
1560
1561 // Array operations
1563 // This is a no-op at token processing time
1564 break;
1565 case TokenType::ArrayLoad: {
1566 const auto& payload = std::get<TokenPayloadArrayOp>(token.payload);
1567 std::string idx = pop();
1568 std::string temp = newTemp();
1569 emitLine(
1570 std::format("float {} = a_{}[int({})];", temp, payload.name, idx));
1571 push(temp);
1572 break;
1573 }
1574 case TokenType::ArrayStore: {
1575 const auto& payload = std::get<TokenPayloadArrayOp>(token.payload);
1576 std::string idx = pop();
1577 std::string val = pop();
1578 emitLine(std::format("a_{}[int({})] = {};", payload.name, idx, val));
1579 break;
1580 }
1581
1582 case TokenType::StoreAbs: {
1583 std::string coord_y = pop();
1584 std::string coord_x = pop();
1585 std::string val = pop();
1586 std::string x_int = newTemp();
1587 std::string y_int = newTemp();
1588 emitLine(std::format("int {} = int({});", x_int, coord_x));
1589 emitLine(std::format("int {} = int({});", y_int, coord_y));
1590 std::string idx = emitPixelIndex(x_int, y_int);
1591 emitLine(std::format("dst.data[{}] = {};", idx, val));
1592 break;
1593 }
1594
1596 push("uintBitsToFloat(0x7FC0E71Fu)");
1597 break;
1598 }
1599
1600 default:
1601 throw std::runtime_error(
1602 std::format("GLSLGenerator: unhandled token type {}",
1603 static_cast<int>(token.type)));
1604 }
1605}
constexpr std::vector< std::pair< int, int > > get_sorting_network(int n)
Definition Sorting.hpp:2149
@ ArrayAllocStatic
Definition Tokenizer.hpp:51
std::string generate()
GLSLGenerator(const std::vector< Token > &tokens, int num_inputs, int num_intermediate_inputs, int width, int height, bool mirror_boundary, const std::map< std::pair< int, std::string >, int > &prop_map, const analysis::ExpressionAnalysisResults &analysis_results)
TokenType type
PayloadVariant payload