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