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