Loading...
Searching...
No Matches
CpuAnalysisNode.hpp
1#pragma once
2
3#if SCORE_PLUGIN_GFX
4#include <Crousti/GfxNode.hpp>
5
6namespace oscr
7{
8
9template <typename Node_T>
10 requires(
11 avnd::texture_input_introspection<Node_T>::size > 0
12 && avnd::texture_output_introspection<Node_T>::size == 0)
13struct GfxRenderer<Node_T> final : score::gfx::OutputNodeRenderer
14{
15 using texture_inputs = avnd::texture_input_introspection<Node_T>;
16 Node_T state;
17 score::gfx::Message m_last_message{};
18 ossia::small_flat_map<const score::gfx::Port*, score::gfx::TextureRenderTarget, 2>
19 m_rts;
20
21 std::vector<QRhiReadbackResult> m_readbacks;
22 ossia::time_value m_last_time{-1};
23
24 const GfxNode<Node_T>& node() const noexcept
25 {
26 return static_cast<const GfxNode<Node_T>&>(score::gfx::NodeRenderer::node);
27 }
28 GfxRenderer(const GfxNode<Node_T>& p)
29 : score::gfx::OutputNodeRenderer{p}
30 , m_readbacks(texture_inputs::size)
31 {
32 prepareNewState(state, p);
33 }
34
36 renderTargetForInput(const score::gfx::Port& p) override
37 {
38 auto it = m_rts.find(&p);
39 SCORE_ASSERT(it != m_rts.end());
40 return it->second;
41 }
42
43 template <typename Tex>
44 void createInput(
45 score::gfx::RenderList& renderer, int k, const Tex& texture_spec, QSize size)
46 {
47 auto port = this->node().input[k];
48 static constexpr auto flags
49 = QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource;
50 auto texture = renderer.state.rhi->newTexture(
51 gpp::qrhi::textureFormat<Tex>(), size, 1, flags);
52 SCORE_ASSERT(texture->create());
54 renderer.state, texture, renderer.samples(), renderer.requiresDepth());
55 }
56
57 QRhiTexture* texture(int k) const noexcept
58 {
59 auto port = this->node().input[k];
60 auto it = m_rts.find(port);
61 SCORE_ASSERT(it != m_rts.end());
62 SCORE_ASSERT(it->second.texture);
63 return it->second.texture;
64 }
65
66 void loadInputTexture(QRhi& rhi, avnd::cpu_texture auto& cpu_tex, int k)
67 {
68 auto& buf = m_readbacks[k].data;
69 if(buf.size() != 4 * cpu_tex.width * cpu_tex.height)
70 {
71 cpu_tex.bytes = nullptr;
72 }
73 else
74 {
75 cpu_tex.bytes = reinterpret_cast<unsigned char*>(buf.data());
76
77 if(rhi.isYUpInFramebuffer())
78 if(cpu_tex.width * cpu_tex.height > 0)
79 inplaceMirror(cpu_tex.bytes, cpu_tex.width, cpu_tex.height);
80
81 cpu_tex.changed = true;
82 }
83 }
84
85 void init(score::gfx::RenderList& renderer, QRhiResourceUpdateBatch& res) override
86 {
87 if constexpr(requires { state.prepare(); })
88 {
89 this->node().processControlIn(
90 *this, state, m_last_message, this->node().last_message, this->node().m_ctx);
91 state.prepare();
92 }
93
94 // Init input render targets
95 int k = 0;
96 avnd::cpu_texture_input_introspection<Node_T>::for_all(
97 avnd::get_inputs<Node_T>(state), [&]<typename F>(F& t) {
98 QSize sz = renderer.state.renderSize;
99 if constexpr(requires {
100 t.request_width;
101 t.request_height;
102 })
103 {
104 sz.rwidth() = t.request_width;
105 sz.rheight() = t.request_height;
106 }
107 createInput(renderer, k, t.texture, sz);
108 if constexpr(avnd::cpu_fixed_format_texture<decltype(t.texture)>)
109 {
110 t.texture.width = sz.width();
111 t.texture.height = sz.height();
112 }
113 k++;
114 });
115 }
116
117 void update(
118 score::gfx::RenderList& renderer, QRhiResourceUpdateBatch& res,
119 score::gfx::Edge* edge) override
120 {
121 bool updated = false;
122 /*
123 int k = 0;
124 avnd::cpu_texture_input_introspection<Node_T>::for_all(
125 avnd::get_inputs<Node_T>(state), [&]<typename F>(F& t) {
126 if constexpr(requires {
127 t.request_width;
128 t.request_height;
129 })
130 {
131 const auto tex = this->texture(k)->pixelSize();
132 if(tex.width() != t.request_width || tex.height() != t.request_height)
133 {
134 QSize sz{t.request_width, t.request_height};
135
136 // Release
137 auto port = parent.input[k];
138
139 m_rts[port].release();
140 createInput(renderer, k, t.texture, sz);
141
142 t.texture.width = sz.width();
143 t.texture.height = sz.height();
144
145 updated = true;
146 }
147 }
148 k++;
149 });
150*/
151 if(updated)
152 {
153 // We must notify the graph that the previous nodes have to be recomputed
154 }
155 }
156
157 void release(score::gfx::RenderList& r) override
158 {
159 // Free inputs
160 // TODO investigate why reference does not work here:
161 for(auto [port, rt] : m_rts)
162 rt.release();
163 m_rts.clear();
164 }
165
166 void inputAboutToFinish(
167 score::gfx::RenderList& renderer, const score::gfx::Port& p,
168 QRhiResourceUpdateBatch*& res) override
169 {
170 auto& parent = this->node();
171 res = renderer.state.rhi->nextResourceUpdateBatch();
172 const auto& inputs = parent.input;
173 auto index_of_port = ossia::find(inputs, &p) - inputs.begin();
174 SCORE_ASSERT(index_of_port == 0);
175 {
176 auto tex = m_rts[&p].texture;
177 auto& readback = m_readbacks[index_of_port];
178 readback = {};
179 res->readBackTexture(QRhiReadbackDescription{tex}, &readback);
180 }
181 }
182
183 void runInitialPasses(
184 score::gfx::RenderList& renderer, QRhiCommandBuffer& commands,
185 QRhiResourceUpdateBatch*& res, score::gfx::Edge& edge) override
186 {
187 auto& parent = this->node();
188 auto& rhi = *renderer.state.rhi;
189
190 // Insert a synchronisation point to allow readbacks to complete
191 rhi.finish();
192
193 // If we are paused, we don't run the processor implementation.
194 if(parent.last_message.token.date == m_last_time)
195 {
196 return;
197 }
198 m_last_time = parent.last_message.token.date;
199
200 // Fetch input textures (if any)
201 {
202 // Copy the readback output inside the structure
203 // TODO it would be much better to do this inside the readback's
204 // "completed" callback.
205 int k = 0;
206 avnd::cpu_texture_input_introspection<Node_T>::for_all(
207 avnd::get_inputs<Node_T>(state), [&](auto& t) {
208 loadInputTexture(rhi, t.texture, k);
209 k++;
210 });
211 }
212
213 parent.processControlIn(
214 *this, state, m_last_message, parent.last_message, parent.m_ctx);
215
216 // Run the processor
217 state();
218
219 // Copy the data to the model node
220 parent.processControlOut(this->state);
221
222 // Copy the geometry
223 // FIXME we need something such as port_run_{pre,post}process for GPU nodes
224 avnd::geometry_output_introspection<Node_T>::for_all_n2(
225 state.outputs, [&]<std::size_t F, std::size_t P>(
226 auto& t, avnd::predicate_index<P>, avnd::field_index<F>) {
227 postprocess_geometry(t);
228 });
229 }
230 template <avnd::geometry_port Field>
231 void postprocess_geometry(Field& ctrl)
232 {
233 using namespace avnd;
234 bool mesh_dirty{};
235 bool tform_dirty{};
236 mesh_dirty = ctrl.dirty_mesh;
237
238 if(ctrl.dirty_mesh)
239 {
240 auto meshes = std::make_shared<ossia::mesh_list>();
241 auto& ossia_meshes = *meshes;
242 if constexpr(static_geometry_type<Field> || dynamic_geometry_type<Field>)
243 {
244 ossia_meshes.meshes.resize(1);
245 oscr::load_geometry(ctrl, ossia_meshes.meshes[0]);
246 }
247 else if constexpr(
248 static_geometry_type<decltype(Field::mesh)>
249 || dynamic_geometry_type<decltype(Field::mesh)>)
250 {
251 ossia_meshes.meshes.resize(1);
252 oscr::load_geometry(ctrl.mesh, ossia_meshes.meshes[0]);
253 }
254 else
255 {
256 oscr::load_geometry(ctrl, ossia_meshes);
257 }
258 }
259 ctrl.dirty_mesh = false;
260
261 // if constexpr(requires { ctrl.transform; })
262 // {
263 // if(ctrl.dirty_transform)
264 // {
265 // std::copy_n(
266 // ctrl.transform, std::ssize(ctrl.transform), port.data.transform.matrix);
267 // tform_dirty = true;
268 // ctrl.dirty_transform = false;
269 // }
270 // }
271 //
272 // port.data.flags = {};
273 // if(mesh_dirty)
274 // port.data.flags = port.data.flags | ossia::geometry_port::dirty_meshes;
275 // if(tform_dirty)
276 // port.data.flags = port.data.flags | ossia::geometry_port::dirty_transform;
277 }
278};
279
280template <typename Node_T>
281 requires(avnd::texture_input_introspection<Node_T>::size > 0
282 && avnd::texture_output_introspection<Node_T>::size == 0)
283struct GfxNode<Node_T> final
284 : CustomGpuOutputNodeBase
285 , GpuNodeElements<Node_T>
286{
287 oscr::ProcessModel<Node_T>& processModel;
288 GfxNode(
290 std::weak_ptr<Execution::ExecutionCommandQueue> q, Gfx::exec_controls ctls, int id,
291 const score::DocumentContext& ctx)
292 : CustomGpuOutputNodeBase{std::move(q), std::move(ctls), ctx}
293 , processModel{element}
294 {
295 this->instance = id;
296
297 initGfxPorts<Node_T>(this, this->input, this->output);
298 }
299
301 createRenderer(score::gfx::RenderList& r) const noexcept override
302 {
303 return new GfxRenderer<Node_T>{*this};
304 }
305};
306}
307#endif
Definition score-plugin-avnd/Crousti/ProcessModel.hpp:79
Definition OutputNode.hpp:11
List of nodes to be rendered to an output.
Definition RenderList.hpp:19
bool requiresDepth() const noexcept
Whether this list of rendering actions requires depth testing at all.
Definition RenderList.hpp:137
RenderState & state
RenderState corresponding to this RenderList.
Definition RenderList.hpp:89
Definition Factories.hpp:19
TextureRenderTarget createRenderTarget(const RenderState &state, QRhiTexture *tex, int samples, bool depth)
Create a render target from a texture.
Definition score-plugin-gfx/Gfx/Graph/Utils.cpp:10
Base toolkit upon which the software is built.
Definition Application.cpp:97
STL namespace.
Definition DocumentContext.hpp:18
Connection between two score::gfx::Port.
Definition score-plugin-gfx/Gfx/Graph/Utils.hpp:66
Definition score-plugin-gfx/Gfx/Graph/Node.hpp:50
Port of a score::gfx::Node.
Definition score-plugin-gfx/Gfx/Graph/Utils.hpp:48
Useful abstraction for storing all the data related to a render target.
Definition score-plugin-gfx/Gfx/Graph/Utils.hpp:111