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