Loading...
Searching...
No Matches
ISFVisitors.hpp
1#pragma once
2#include <isf.hpp>
3
4#include <ossia/detail/variant.hpp>
5
6namespace score::gfx
7{
8// ---------------------------------------------------------------------------
9// Descriptor port walker
10// ---------------------------------------------------------------------------
11//
12// The single source of truth for "how many input ports / output ports /
13// samplers does each desc.inputs entry produce?", shared by CSF port_indices,
14// RawRaster port_idx, RawRaster bindAuxTexturesInit and IsfBindingsBuilder.
15// Mirrors `isf_input_port_vis` in ISFNode.cpp, which is the actual
16// port-creation code.
17//
18// When a new isf::*_input variant is added, update isf_input_port_vis and the
19// matching `operator()` here -- keep them in lockstep.
21{
22 int inlets{};
23 int outlets{};
24 int samplers{};
26
27 port_counts& operator+=(const port_counts& o) noexcept
28 {
29 inlets += o.inlets;
30 outlets += o.outlets;
31 samplers += o.samplers;
32 return *this;
33 }
34};
35
36// Returns the port_counts contributed by a single input variant. Mirrors
37// isf_input_port_vis (ISFNode.cpp) one-to-one.
39{
40 port_counts operator()(const isf::float_input&) const noexcept { return {1, 0, 0}; }
41 port_counts operator()(const isf::long_input&) const noexcept { return {1, 0, 0}; }
42 port_counts operator()(const isf::event_input&) const noexcept { return {1, 0, 0}; }
43 port_counts operator()(const isf::bool_input&) const noexcept { return {1, 0, 0}; }
44 port_counts operator()(const isf::point2d_input&) const noexcept { return {1, 0, 0}; }
45 port_counts operator()(const isf::point3d_input&) const noexcept { return {1, 0, 0}; }
46 port_counts operator()(const isf::color_input&) const noexcept { return {1, 0, 0}; }
47 port_counts operator()(const isf::audio_input&) const noexcept { return {1, 0, 0}; }
48 port_counts operator()(const isf::audioHist_input&) const noexcept{ return {1, 0, 0}; }
49 port_counts operator()(const isf::audioFFT_input&) const noexcept { return {1, 0, 0}; }
50
51 port_counts operator()(const isf::image_input& in) const noexcept
52 {
53 // GrabsFromSource means no own render target, so the matching depth
54 // sampler (image_input.depth==true) is not created in initInputSamplers
55 // either.
56 const bool grabs = (in.dimensions == 3 || in.is_array || in.is_static);
57 const int extra_depth_sampler = (in.depth && !grabs) ? 1 : 0;
58 return {1, 0, 1 + extra_depth_sampler};
59 }
60 port_counts operator()(const isf::cubemap_input&) const noexcept { return {1, 0, 1}; }
61 port_counts operator()(const isf::texture_input&) const noexcept { return {1, 0, 1}; }
62
63 port_counts operator()(const isf::storage_input& in) const noexcept
64 {
65 // read_only: 1 input port (no output, no sampler).
66 // write/read_write: 1 output port; +1 input port if the layout's last
67 // field is a flexible array (synthesized long_input for sizing).
68 if(in.access == "read_only")
69 return {1, 0, 0};
70 port_counts c{0, 1, 0};
71 if(!in.layout.empty()
72 && in.layout.back().type.find("[]") != std::string::npos)
73 c.inlets = 1;
74 return c;
75 }
76
77 port_counts operator()(const isf::uniform_input&) const noexcept
78 {
79 return {1, 0, 0};
80 }
81
82 port_counts operator()(const isf::csf_image_input& in) const noexcept
83 {
84 // read_only: 1 input port; write/read_write: 1 output port (no input).
85 if(in.access == "read_only")
86 return {1, 0, 0};
87 return {0, 1, 0};
88 }
89
90 port_counts operator()(const isf::geometry_input& in) const noexcept
91 {
92 port_counts c{};
93 if(in.attributes.empty())
94 {
95 // Pass-through: 1 inlet + 1 outlet
96 c.inlets = 1;
97 c.outlets = 1;
98 }
99 else
100 {
101 for(const auto& attr : in.attributes)
102 if(attr.access == "read_only" || attr.access == "read_write")
103 { c.inlets = 1; break; }
104 for(const auto& attr : in.attributes)
105 if(attr.access == "write_only" || attr.access == "read_write")
106 { c.outlets = 1; break; }
107 }
108 // $USER ports → synthesized long_input each (1 inlet)
109 if(in.vertex_count.find("$USER") != std::string::npos) c.inlets++;
110 if(in.instance_count.find("$USER") != std::string::npos) c.inlets++;
111 for(const auto& aux : in.auxiliary)
112 if(aux.size.find("$USER") != std::string::npos)
113 c.inlets++;
114 return c;
115 }
116};
117
118// Walk desc.inputs once. For each input, the visitor receives:
119// - the isf::input entry
120// - the cumulative port_counts BEFORE this input (so cur.inlets is the
121// index of the first input port this entry creates, if any)
122// - the per-input port_counts delta (how many ports this entry creates)
123// Cumulative state is then advanced before moving on.
124//
125// Callers needing a non-zero starting offset (e.g. RawRaster's port 0 is
126// the implicit Geometry input) can pass it in `start` — its inlets/outlets
127// are accumulated upfront.
128template <typename F>
129inline void walk_descriptor_inputs(
130 const isf::descriptor& desc, port_counts start, F&& fn)
131{
132 port_counts cur = start;
133 for(const auto& inp : desc.inputs)
134 {
135 port_counts delta = ossia::visit(isf_input_port_count_vis{}, inp.data);
136 fn(inp, cur, delta);
137 cur += delta;
138 }
139}
140
141// Convenience overload: zero starting offset.
142template <typename F>
143inline void walk_descriptor_inputs(const isf::descriptor& desc, F&& fn)
144{
145 walk_descriptor_inputs(desc, port_counts{}, std::forward<F>(fn));
146}
147
149{
150 int sz{};
151 void operator()(const isf::float_input&) noexcept { sz += 4; }
152
153 void operator()(const isf::long_input&) noexcept { sz += 4; }
154
155 void operator()(const isf::event_input&) noexcept
156 {
157 sz += 4; // bool
158 }
159
160 void operator()(const isf::bool_input&) noexcept
161 {
162 sz += 4; // bool
163 }
164
165 void operator()(const isf::point2d_input&) noexcept
166 {
167 if(sz % 8 != 0)
168 sz += 4;
169 sz += 2 * 4;
170 }
171
172 void operator()(const isf::point3d_input&) noexcept
173 {
174 while(sz % 16 != 0)
175 {
176 sz += 4;
177 }
178 sz += 3 * 4;
179 }
180
181 void operator()(const isf::color_input&) noexcept
182 {
183 while(sz % 16 != 0)
184 {
185 sz += 4;
186 }
187 sz += 4 * 4;
188 }
189
190 void operator()(const isf::image_input&) noexcept { }
191 void operator()(const isf::cubemap_input&) noexcept { }
192
193 void operator()(const isf::audio_input&) noexcept { }
194 void operator()(const isf::audioFFT_input&) noexcept { }
195 void operator()(const isf::audioHist_input&) noexcept { }
196
197 // CSF-specific input handlers
198 void operator()(const isf::storage_input& in) noexcept
199 {
200 // Must match what isf_input_port_vis (ISFNode.cpp) writes into the blob,
201 // and the synthesized "size" int it creates: only a writable buffer whose
202 // layout ends in a flexible-array member. Reserving for every write buffer
203 // would desync this from the port visitor and the generated GLSL
204 // Params/material_t block.
205 if(in.access.contains("write") && !in.layout.empty()
206 && in.layout.back().type.find("[]") != std::string::npos)
207 {
208 (*this)(isf::long_input{});
209 }
210 }
211
212 void operator()(const isf::uniform_input&) noexcept
213 {
214 // UBO inputs are bound from an upstream Buffer port; they do not
215 // contribute to the material UBO size.
216 }
217
218 void operator()(const isf::texture_input in) noexcept { }
219
220 void operator()(const isf::csf_image_input& in) noexcept
221 {
222 // isf_input_port_vis writes nothing into the material blob for write
223 // csf_image inputs (its point2d/long synthesis is commented out), so
224 // reserve nothing here: the size visitor, the port visitor and the
225 // generated uniform block must agree.
226 }
227
228 void operator()(const isf::geometry_input& in) noexcept
229 {
230 // Geometry inputs don't contribute to the material UBO size.
231 // Their buffers are bound separately as SSBOs.
232 // But $USER ports create long_input (int) entries in the material UBO.
233 if(in.vertex_count.find("$USER") != std::string::npos)
234 (*this)(isf::long_input{});
235 if(in.instance_count.find("$USER") != std::string::npos)
236 (*this)(isf::long_input{});
237 for(const auto& aux : in.auxiliary)
238 if(aux.size.find("$USER") != std::string::npos)
239 (*this)(isf::long_input{});
240 }
241};
242}
Graphics rendering pipeline for ossia score.
Definition Filter/PreviewWidget.hpp:11
Definition ISFVisitors.hpp:39
Definition ISFVisitors.hpp:149
Definition ISFVisitors.hpp:21
int outlets
score output ports created
Definition ISFVisitors.hpp:23
int samplers
Definition ISFVisitors.hpp:24
int inlets
score input ports created by this desc.inputs entry
Definition ISFVisitors.hpp:22