Loading...
Searching...
No Matches
CpuGeneratorNode.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::GenericNodeRenderer
14{
15 using texture_outputs = avnd::texture_output_introspection<Node_T>;
16 Node_T state;
17 score::gfx::Message m_last_message{};
18 ossia::time_value m_last_time{-1};
19
20 const GfxNode<Node_T>& node() const noexcept
21 {
22 return static_cast<const GfxNode<Node_T>&>(score::gfx::NodeRenderer::node);
23 }
24
25 GfxRenderer(const GfxNode<Node_T>& p)
26 : score::gfx::GenericNodeRenderer{p}
27 {
28 prepareNewState(state, p);
29 }
30
32 renderTargetForInput(const score::gfx::Port& p) override
33 {
34 return {};
35 }
36
37 template <typename Tex>
38 void
39 createOutput(score::gfx::RenderList& renderer, const Tex& texture_spec, QSize size)
40 {
41 auto& rhi = *renderer.state.rhi;
42 QRhiTexture* texture = &renderer.emptyTexture();
43 if(size.width() > 0 && size.height() > 0)
44 {
45 texture = rhi.newTexture(
46 gpp::qrhi::textureFormat<Tex>(), size, 1, QRhiTexture::Flag{});
47
48 texture->create();
49 }
50
51 auto sampler = rhi.newSampler(
52 QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
53 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
54
55 sampler->create();
56 this->m_samplers.push_back({sampler, texture});
57 }
58
59 template <avnd::cpu_texture Tex>
60 QRhiTexture* updateTexture(score::gfx::RenderList& renderer, int k, const Tex& cpu_tex)
61 {
62 auto& [sampler, texture] = this->m_samplers[k];
63 if(texture)
64 {
65 auto sz = texture->pixelSize();
66 if(cpu_tex.width == sz.width() && cpu_tex.height == sz.height())
67 return texture;
68 }
69
70 // Check the texture size
71 if(cpu_tex.width > 0 && cpu_tex.height > 0)
72 {
73 QRhiTexture* oldtex = texture;
74 QRhiTexture* newtex = renderer.state.rhi->newTexture(
75 gpp::qrhi::textureFormat<Tex>(), QSize{cpu_tex.width, cpu_tex.height}, 1,
76 QRhiTexture::Flag{});
77 newtex->create();
78 for(auto& [edge, pass] : this->m_p)
79 if(pass.srb)
80 score::gfx::replaceTexture(*pass.srb, sampler, newtex);
81 texture = newtex;
82
83 if(oldtex && oldtex != &renderer.emptyTexture())
84 {
85 oldtex->deleteLater();
86 }
87
88 return newtex;
89 }
90 else
91 {
92 for(auto& [edge, pass] : this->m_p)
93 if(pass.srb)
94 score::gfx::replaceTexture(*pass.srb, sampler, &renderer.emptyTexture());
95
96 return &renderer.emptyTexture();
97 }
98 }
99
100 void uploadOutputTexture(
101 score::gfx::RenderList& renderer, int k, avnd::cpu_texture auto& cpu_tex,
102 QRhiResourceUpdateBatch* res)
103 {
104 if(cpu_tex.changed)
105 {
106 if(auto texture = updateTexture(renderer, k, cpu_tex))
107 {
108 // Upload it
109 {
110 QRhiTextureSubresourceUploadDescription sd(cpu_tex.bytes, cpu_tex.bytesize());
111 QRhiTextureUploadDescription desc{QRhiTextureUploadEntry{0, 0, sd}};
112
113 res->uploadTexture(texture, desc);
114 }
115
116 cpu_tex.changed = false;
117 k++;
118 }
119 }
120 }
121
122 void init(score::gfx::RenderList& renderer, QRhiResourceUpdateBatch& res) override
123 {
124 auto& parent = node();
125 if constexpr(requires { state.prepare(); })
126 {
127 parent.processControlIn(
128 *this, state, m_last_message, parent.last_message, parent.m_ctx);
129 state.prepare();
130 }
131
132 const auto& mesh = renderer.defaultTriangle();
133 this->defaultMeshInit(renderer, mesh, res);
134 this->processUBOInit(renderer);
135 // Not needed here as we do not have a GPU pass:
136 // this->m_material.init(renderer, this->node.input, this->m_samplers);
137 std::tie(this->m_vertexS, this->m_fragmentS)
138 = score::gfx::makeShaders(renderer.state, generic_texgen_vs, generic_texgen_fs);
139
140 // Init textures for the outputs
141 avnd::cpu_texture_output_introspection<Node_T>::for_all(
142 avnd::get_outputs<Node_T>(state), [&](auto& t) {
143 createOutput(renderer, t.texture, QSize{t.texture.width, t.texture.height});
144 });
145
146 this->defaultPassesInit(renderer, mesh);
147 }
148
149 void update(
150 score::gfx::RenderList& renderer, QRhiResourceUpdateBatch& res,
151 score::gfx::Edge* edge) override
152 {
153 this->defaultUBOUpdate(renderer, res);
154 }
155
156 void release(score::gfx::RenderList& r) override
157 {
158 // Free outputs
159 for(auto& [sampl, texture] : this->m_samplers)
160 {
161 if(texture != &r.emptyTexture())
162 texture->deleteLater();
163 texture = nullptr;
164 }
165
166 this->defaultRelease(r);
167 }
168
169 void runInitialPasses(
170 score::gfx::RenderList& renderer, QRhiCommandBuffer& commands,
171 QRhiResourceUpdateBatch*& res, score::gfx::Edge& edge) override
172 {
173 auto& parent = node();
174 // If we are paused, we don't run the processor implementation.
175 if(parent.last_message.token.date == m_last_time)
176 {
177 return;
178 }
179 m_last_time = parent.last_message.token.date;
180
181 parent.processControlIn(
182 *this, state, m_last_message, parent.last_message, parent.m_ctx);
183
184 // Run the processor
185 state();
186
187 // Upload output textures
188 int k = 0;
189 avnd::cpu_texture_output_introspection<Node_T>::for_all(
190 avnd::get_outputs<Node_T>(state), [&](auto& t) {
191 uploadOutputTexture(renderer, k, t.texture, res);
192 k++;
193 });
194
195 commands.resourceUpdate(res);
196 res = renderer.state.rhi->nextResourceUpdateBatch();
197
198 // Copy the data to the model node
199 parent.processControlOut(this->state);
200 }
201};
202
203template <typename Node_T>
204 requires(avnd::texture_input_introspection<Node_T>::size == 0
205 && avnd::texture_output_introspection<Node_T>::size > 0)
206struct GfxNode<Node_T> final
207 : CustomGfxNodeBase
208 , GpuWorker
209 , GpuControlIns
210 , GpuControlOuts
211 , GpuNodeElements<Node_T>
212{
213 oscr::ProcessModel<Node_T>& processModel;
214 GfxNode(
216 std::weak_ptr<Execution::ExecutionCommandQueue> q, Gfx::exec_controls ctls, int id,
217 const score::DocumentContext& ctx)
218 : CustomGfxNodeBase{ctx}
219 , GpuControlOuts{std::move(q), std::move(ctls)}
220 , processModel{element}
221 {
222 this->instance = id;
223
224 initGfxPorts<Node_T>(this, this->input, this->output);
225 }
226
228 createRenderer(score::gfx::RenderList& r) const noexcept override
229 {
230 return new GfxRenderer<Node_T>{*this};
231 }
232};
233}
234#endif
Definition score-plugin-avnd/Crousti/ProcessModel.hpp:79
Renderer for a given node.
Definition NodeRenderer.hpp:11
List of nodes to be rendered to an output.
Definition RenderList.hpp:19
const score::gfx::Mesh & defaultTriangle() const noexcept
A triangle mesh correct for this API.
Definition RenderList.cpp:305
RenderState & state
RenderState corresponding to this RenderList.
Definition RenderList.hpp:89
QRhiTexture & emptyTexture() const noexcept
Texture to use when a texture is missing.
Definition RenderList.hpp:112
Definition Factories.hpp:19
std::pair< QShader, QShader > makeShaders(const RenderState &v, QString vert, QString frag)
Get a pair of compiled vertex / fragment shaders from GLSL 4.5 sources.
Definition score-plugin-gfx/Gfx/Graph/Utils.cpp:390
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