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(
111 cpu_tex.bytes, cpu_tex.width * cpu_tex.height * 4);
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, int id,
218 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:77
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:297
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:372
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: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