3#include <avnd/introspection/gfx.hpp>
5#include <Process/ExecutionContext.hpp>
7#include <Crousti/File.hpp>
8#include <Crousti/GppCoroutines.hpp>
9#include <Crousti/GppShaders.hpp>
10#include <Crousti/MessageBus.hpp>
11#include <Crousti/TextureFormat.hpp>
12#include <Crousti/TextureConversion.hpp>
13#include <Gfx/GfxExecNode.hpp>
14#include <Gfx/Graph/Node.hpp>
15#include <Gfx/Graph/OutputNode.hpp>
16#include <Gfx/Graph/RenderList.hpp>
17#include <Gfx/Graph/RenderState.hpp>
19#include <score/tools/ThreadPool.hpp>
21#include <ossia-qt/invoke.hpp>
23#include <QCoreApplication>
25#include <QtGui/private/qrhi_p.h>
27#include <ossia/detail/small_flat_map.hpp>
28#include <avnd/binding/ossia/port_run_postprocess.hpp>
29#include <avnd/binding/ossia/port_run_preprocess.hpp>
30#include <avnd/binding/ossia/soundfiles.hpp>
31#include <avnd/concepts/parameter.hpp>
32#include <avnd/introspection/input.hpp>
33#include <avnd/introspection/output.hpp>
34#include <fmt/format.h>
35#include <gpp/layout.hpp>
37#include <score_plugin_avnd_export.h>
44 void initWorker(
this auto& self, std::shared_ptr<T>& state)
noexcept
46 if constexpr(avnd::has_worker<T>)
48 auto ptr = QPointer{&self};
49 auto& tq = score::TaskPool::instance();
50 using worker_type =
decltype(state->worker);
52 auto wk_state = std::weak_ptr{state};
53 state->worker.request = [ptr, &tq, wk_state]<
typename... Args>(Args&&... f) {
54 using type_of_result =
decltype(worker_type::work(std::forward<Args>(f)...));
55 tq.post([... ff = std::forward<Args>(f), wk_state, ptr]()
mutable {
56 if constexpr(std::is_void_v<type_of_result>)
58 worker_type::work(std::forward<
decltype(ff)>(ff)...);
64 auto res = worker_type::work(std::forward<
decltype(ff)>(ff)...);
69 QCoreApplication::instance(),
70 [res = std::move(res), wk_state, ptr]()
mutable {
72 if(
auto state = wk_state.lock())
82template <
typename GpuNodeRenderer,
typename Node>
91 bool can_process_message(std::size_t N)
93 if(mess.input.size() <= N)
96 if(prev_mess.input.size() == mess.input.size())
98 auto& prev = prev_mess.input[N];
99 auto& next = mess.input[N];
100 if(prev.index() == 1 && next.index() == 1)
102 if(ossia::get<ossia::value>(prev) == ossia::get<ossia::value>(next))
111 void operator()(avnd::parameter
auto& t,
auto field_index)
113 if(!can_process_message(field_index))
116 if(
auto val = ossia::get_if<ossia::value>(&mess.input[field_index]))
118 oscr::from_ossia_value(t, *val, t.value);
119 if_possible(t.update(state));
123#if OSCR_HAS_MMAP_FILE_STORAGE
124 template <avnd::raw_file_port Field, std::
size_t NField>
125 void operator()(Field& t, avnd::field_index<NField> field_index)
128 using node_type = std::remove_cvref_t<
decltype(gpu.node())>;
129 using file_ports = avnd::raw_file_input_introspection<Node>;
131 if(!can_process_message(field_index))
134 auto val = ossia::get_if<ossia::value>(&mess.input[field_index]);
138 static constexpr bool has_text =
requires {
decltype(Field::file)::text; };
139 static constexpr bool has_mmap =
requires {
decltype(Field::file)::mmap; };
142 if(
auto hdl = loadRawfile(*val, ctx, has_text, has_mmap))
144 static constexpr auto N = file_ports::field_index_to_index(NField);
145 if constexpr(avnd::port_can_process<Field>)
149 auto func = executePortPreprocess<Field>(*hdl);
150 const_cast<node_type&
>(gpu.node())
152 state, hdl, avnd::predicate_index<N>{}, avnd::field_index<NField>{});
158 const_cast<node_type&
>(gpu.node())
160 state, hdl, avnd::predicate_index<N>{}, avnd::field_index<NField>{});
166 template <avnd::buffer_port Field, std::
size_t NField>
167 void operator()(Field& t, avnd::field_index<NField> field_index)
169 using node_type = std::remove_cvref_t<
decltype(gpu.node())>;
170 auto& node =
const_cast<node_type&
>(gpu.node());
171 auto val = ossia::get_if<ossia::render_target_spec>(&mess.input[field_index]);
174 node.process(NField, *val);
177 template <avnd::texture_port Field, std::
size_t NField>
178 void operator()(Field& t, avnd::field_index<NField> field_index)
180 using node_type = std::remove_cvref_t<
decltype(gpu.node())>;
181 auto& node =
const_cast<node_type&
>(gpu.node());
182 auto val = ossia::get_if<ossia::render_target_spec>(&mess.input[field_index]);
185 node.process(NField, *val);
188 template <avnd::geometry_port Field, std::
size_t NField>
189 void operator()(Field& t, avnd::field_index<NField> field_index)
191 using node_type = std::remove_cvref_t<
decltype(gpu.node())>;
192 auto& node =
const_cast<node_type&
>(gpu.node());
197 void operator()(
auto& t,
auto field_index) =
delete;
202 template <
typename Self,
typename Node_T>
203 static void processControlIn(
208 avnd::input_introspection<Node_T>::for_all_n(
209 avnd::get_inputs<Node_T>(state),
210 GpuProcessIns<Self, Node_T>{self, state, renderer_mess, mess, ctx});
211 renderer_mess = mess;
217 std::weak_ptr<Execution::ExecutionCommandQueue> queue;
218 Gfx::exec_controls control_outs;
222 template <
typename Node_T>
223 void processControlOut(Node_T& state)
const noexcept
225 if(!this->control_outs.empty())
227 auto q = this->queue.lock();
232 avnd::parameter_output_introspection<Node_T>::for_all(
233 avnd::get_outputs(state), [&]<avnd::parameter T>(
const T& t) {
234 qq.enqueue([v = oscr::to_ossia_value(t, t.value),
235 port = control_outs[parm_k]]()
mutable {
236 std::swap(port->value, v);
237 port->changed = true;
247struct SCORE_PLUGIN_AVND_EXPORT GpuNodeElements
249 [[no_unique_address]] oscr::soundfile_storage<T> soundfiles;
251 [[no_unique_address]] oscr::midifile_storage<T> midifiles;
253#if defined(OSCR_HAS_MMAP_FILE_STORAGE)
254 [[no_unique_address]] oscr::raw_file_storage<T> rawfiles;
257 template <std::
size_t N, std::
size_t NField>
259 auto& state,
const std::shared_ptr<oscr::raw_file_data>& hdl,
260 avnd::predicate_index<N>, avnd::field_index<NField>)
263 state, hdl, avnd::predicate_index<N>{}, avnd::field_index<NField>{});
270 :
score::gfx::NodeModel{}
274 virtual ~CustomGfxNodeBase();
282 virtual ~CustomGfxOutputNodeBase();
287struct CustomGpuNodeBase
294 std::weak_ptr<Execution::ExecutionCommandQueue>&& q, Gfx::exec_controls&& ctls,
296 : GpuControlOuts{
std::move(q),
std::move(ctls)}
301 virtual ~CustomGpuNodeBase() =
default;
304 QString vertex, fragment, compute;
309struct SCORE_PLUGIN_AVND_EXPORT CustomGpuOutputNodeBase
315 CustomGpuOutputNodeBase(
316 std::weak_ptr<Execution::ExecutionCommandQueue> q, Gfx::exec_controls&& ctls,
318 virtual ~CustomGpuOutputNodeBase();
321 std::weak_ptr<score::gfx::RenderList> m_renderer{};
322 std::shared_ptr<score::gfx::RenderState> m_renderState{};
323 std::function<void()> m_update;
325 QString vertex, fragment, compute;
330 void setRenderer(std::shared_ptr<score::gfx::RenderList>)
override;
333 void startRendering()
override;
334 void render()
override;
335 void stopRendering()
override;
336 bool canRender()
const override;
337 void onRendererChange()
override;
341 std::function<
void()> onUpdate, std::function<
void()> onResize)
override;
343 void destroyOutput()
override;
344 std::shared_ptr<score::gfx::RenderState> renderState()
const override;
346 Configuration configuration() const noexcept override;
349template <typename Node_T, typename Node>
350void prepareNewState(
std::shared_ptr<Node_T>& eff, const Node& parent)
352 if constexpr(avnd::has_worker<Node_T>)
354 parent.initWorker(eff);
356 if constexpr(avnd::has_processor_to_gui_bus<Node_T>)
358 auto& process = parent.processModel;
359 eff->send_message = [ptr = QPointer{&process}](
auto&& b)
mutable {
363 if(ptr && ptr->to_ui)
364 MessageBusSender{ptr->to_ui}(std::move(b));
371 avnd::init_controls(*eff);
373 if constexpr(avnd::can_prepare<Node_T>)
375 if constexpr(avnd::function_reflection<&Node_T::prepare>::count == 1)
377 using prepare_type = avnd::first_argument<&Node_T::prepare>;
379 if_possible(t.instance = parent.instance);
389struct port_to_type_enum
391 template <std::
size_t I, avnd::buffer_port F>
392 constexpr auto operator()(avnd::field_reflection<I, F> p)
394 return score::gfx::Types::Buffer;
397 template <std::
size_t I, avnd::cpu_texture_port F>
398 constexpr auto operator()(avnd::field_reflection<I, F> p)
400 using texture_type = std::remove_cvref_t<
decltype(F::texture)>;
401 return avnd::cpu_fixed_format_texture<texture_type> ? score::gfx::Types::Image
402 : score::gfx::Types::Buffer;
405 template <std::
size_t I, avnd::sampler_port F>
406 constexpr auto operator()(avnd::field_reflection<I, F> p)
408 return score::gfx::Types::Image;
410 template <std::
size_t I, avnd::image_port F>
411 constexpr auto operator()(avnd::field_reflection<I, F> p)
413 return score::gfx::Types::Image;
415 template <std::
size_t I, avnd::attachment_port F>
416 constexpr auto operator()(avnd::field_reflection<I, F> p)
418 return score::gfx::Types::Image;
421 template <std::
size_t I, avnd::geometry_port F>
422 constexpr auto operator()(avnd::field_reflection<I, F> p)
424 return score::gfx::Types::Geometry;
426 template <std::
size_t I, avnd::mono_audio_port F>
427 constexpr auto operator()(avnd::field_reflection<I, F> p)
429 return score::gfx::Types::Audio;
431 template <std::
size_t I, avnd::poly_audio_port F>
432 constexpr auto operator()(avnd::field_reflection<I, F> p)
434 return score::gfx::Types::Audio;
436 template <std::
size_t I, avnd::
int_parameter F>
437 constexpr auto operator()(avnd::field_reflection<I, F> p)
439 return score::gfx::Types::Int;
441 template <std::
size_t I, avnd::enum_parameter F>
442 constexpr auto operator()(avnd::field_reflection<I, F> p)
444 return score::gfx::Types::Int;
446 template <std::
size_t I, avnd::
float_parameter F>
447 constexpr auto operator()(avnd::field_reflection<I, F> p)
449 return score::gfx::Types::Float;
451 template <std::
size_t I, avnd::parameter F>
452 constexpr auto operator()(avnd::field_reflection<I, F> p)
454 using value_type = std::remove_cvref_t<
decltype(F::value)>;
456 if constexpr(std::is_aggregate_v<value_type>)
458 constexpr int sz = boost::pfr::tuple_size_v<value_type>;
459 if constexpr(sz == 2)
461 return score::gfx::Types::Vec2;
463 else if constexpr(sz == 3)
465 return score::gfx::Types::Vec3;
467 else if constexpr(sz == 4)
469 return score::gfx::Types::Vec4;
472 return score::gfx::Types::Empty;
474 template <std::
size_t I,
typename F>
475 constexpr auto operator()(avnd::field_reflection<I, F> p)
477 return score::gfx::Types::Empty;
481template <
typename Node_T>
482inline void initGfxPorts(
auto* self,
auto& input,
auto& output)
484 avnd::input_introspection<Node_T>::for_all(
485 [self, &input]<
typename Field, std::size_t I>(avnd::field_reflection<I, Field> f) {
486 static constexpr auto type = port_to_type_enum{}(f);
489 avnd::output_introspection<Node_T>::for_all(
491 &output]<
typename Field, std::size_t I>(avnd::field_reflection<I, Field> f) {
492 static constexpr auto type = port_to_type_enum{}(f);
497static QRhiBuffer* getInputBuffer(
503 const auto& inputs = parent.
input;
504 SCORE_ASSERT(port_index == 0);
507 for(
auto& edge : p->edges)
509 auto src_node = edge->source->node;
513 return src_renderer->bufferForOutput(*edge->source);
522static void readbackInputBuffer(
524 , QRhiResourceUpdateBatch& res
526 , QRhiBufferReadbackResult& readback
532 if(
auto buf = getInputBuffer(renderer, parent, port_index))
535 res.readBackBuffer(buf, 0, buf->size(), &readback);
539static void recreateOutputBuffer(
541 QRhiResourceUpdateBatch& res, QRhiBuffer*& buf)
543 const auto bytesize = avnd::get_bytesize(cpu_buf);
548 buf = renderer.
state.rhi->newBuffer(
550 , QRhiBuffer::StorageBuffer | QRhiBuffer::VertexBuffer
557 cpu_buf.changed =
false;
561 else if(buf->size() != bytesize)
564 buf->setSize(bytesize);
569static void uploadOutputBuffer(
571 QRhiResourceUpdateBatch& res, QRhiBuffer*& buf)
575 const auto bytesize = avnd::get_bytesize(cpu_buf);
576 recreateOutputBuffer(renderer, cpu_buf, res, buf);
578 cpu_buf.changed =
false;
582static void uploadOutputBuffer(
584 QRhiResourceUpdateBatch& res, QRhiBuffer*& buf)
590struct buffer_inputs_storage;
593 requires (avnd::buffer_input_introspection<T>::size > 0)
594struct buffer_inputs_storage<T>
596 QRhiBufferReadbackResult m_readbacks[avnd::cpu_buffer_input_introspection<T>::size];
597 QRhiBuffer* m_gpubufs[avnd::gpu_buffer_input_introspection<T>::size];
599 void readInputBuffers(
602 if constexpr(avnd::cpu_buffer_input_introspection<T>::size > 0)
607 avnd::cpu_buffer_input_introspection<T>::for_all_n(
608 avnd::get_inputs<T>(state),
609 [&]<
typename Field, std::size_t N>
610 (Field& t, avnd::predicate_index<N> np)
612 auto& readback = m_readbacks[N].data;
613 t.buffer.bytes =
reinterpret_cast<unsigned char*
>(readback.data());
614 t.buffer.bytesize = readback.size();
615 t.buffer.changed =
true;
619 if constexpr(avnd::gpu_buffer_input_introspection<T>::size > 0)
624 avnd::gpu_buffer_input_introspection<T>::for_all_n2(
625 avnd::get_inputs<T>(state),
626 [&]<
typename Field, std::size_t N, std::size_t NField>
627 (Field& t, avnd::predicate_index<N> np, avnd::field_index<NField> nf)
629 auto* buf = m_gpubufs[N];
631 m_gpubufs[N] = getInputBuffer(renderer, parent, nf);
635 t.buffer.handle = buf;
636 t.buffer.bytesize = buf->size();
642 void inputAboutToFinish(
644 QRhiResourceUpdateBatch*& res,
648 avnd::cpu_buffer_input_introspection<T>::for_all_n2(
649 avnd::get_inputs<T>(state),
650 [&]<
typename Field, std::size_t N, std::size_t NField>
651 (Field& port, avnd::predicate_index<N> np, avnd::field_index<NField> nf) {
652 readbackInputBuffer(renderer, *res, parent, m_readbacks[N], nf);
654 avnd::gpu_buffer_input_introspection<T>::for_all_n2(
655 avnd::get_inputs<T>(state),
656 [&]<
typename Field, std::size_t N, std::size_t NField>
657 (Field& port, avnd::predicate_index<N> np, avnd::field_index<NField> nf) {
658 m_gpubufs[N] = getInputBuffer(renderer, parent, nf);
664 requires (avnd::buffer_input_introspection<T>::size == 0)
665struct buffer_inputs_storage<T>
667 static void readInputBuffers(
auto&&...)
672 static void inputAboutToFinish(
auto&&...)
679struct buffer_outputs_storage;
682 requires (avnd::buffer_output_introspection<T>::size > 0)
683struct buffer_outputs_storage<T>
685 std::pair<const score::gfx::Port*, QRhiBuffer*> m_buffers[avnd::buffer_output_introspection<T>::size];
687 QRhiResourceUpdateBatch* currentResourceUpdateBatch{};
688 std::pair<const score::gfx::Port*, QRhiBuffer*>
691 auto& rhi = *renderer.
state.rhi;
692 QRhiBuffer* buffer =
nullptr;
693 if(
const auto bs = avnd::get_bytesize(buf); bs > 0)
695 buffer = rhi.newBuffer(
697 , QRhiBuffer::StorageBuffer | QRhiBuffer::VertexBuffer
703 return {&port, buffer};
709 avnd::buffer_output_introspection<T>::for_all_n2(
710 avnd::get_outputs<T>(state), [&]<
typename Field, std::size_t N, std::size_t NField>
711 (Field& port, avnd::predicate_index<N> np, avnd::field_index<NField> nf) {
712 SCORE_ASSERT(parent.
output.size() > nf);
713 SCORE_ASSERT(parent.
output[nf]->type == score::gfx::Types::Buffer);
715 if constexpr(
requires { port.buffer.upload((
const char*)
nullptr, 1000, 0); })
717 auto& [gfx_port, buf] = m_buffers[N];
718 gfx_port = parent.
output[nf];
719 buf = renderer.
state.rhi->newBuffer(
721 , QRhiBuffer::StorageBuffer | QRhiBuffer::VertexBuffer
726 port.buffer.upload = [
this, &renderer, &port] (
const char* data, int64_t offset, int64_t bytesize) {
727 SCORE_ASSERT(currentResourceUpdateBatch);
728 auto& [gfx_port, buf] = m_buffers[N];
734 buf = renderer.
state.rhi->newBuffer(
736 , QRhiBuffer::StorageBuffer | QRhiBuffer::VertexBuffer
743 buf = renderer.
state.rhi->newBuffer(
745 , QRhiBuffer::StorageBuffer | QRhiBuffer::VertexBuffer
752 else if(buf->size() != bytesize)
755 buf->setSize(bytesize);
764 m_buffers[N] = createOutput(renderer, *parent.
output[nf], port.buffer);
769 void prepareUpload(QRhiResourceUpdateBatch& res)
771 currentResourceUpdateBatch = &res;
776 avnd::buffer_output_introspection<T>::for_all_n(
777 avnd::get_outputs<T>(state), [&]<std::size_t N>(
auto& t, avnd::predicate_index<N> idx) {
778 auto& [port, buf] = m_buffers[N];
779 uploadOutputBuffer(renderer, t.buffer, res, buf);
786 for(
auto& [p, buf] : m_buffers)
788 renderer.releaseBuffer(buf);
794 requires (avnd::buffer_output_introspection<T>::size == 0)
795struct buffer_outputs_storage<T>
797 static void init(
auto&&...)
802 static void prepareUpload(
auto&&...)
806 static void upload(
auto&&...)
811 static void release(
auto&&...)
818template <
typename Tex>
822 auto& rhi = *renderer.
state.rhi;
824 if(size.width() > 0 && size.height() > 0)
826 texture = rhi.newTexture(
827 gpp::qrhi::textureFormat(texture_spec), size, 1, QRhiTexture::Flag{});
832 auto sampler = rhi.newSampler(
833 QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
834 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
842struct texture_inputs_storage;
845 requires (avnd::texture_input_introspection<T>::size > 0)
846struct texture_inputs_storage<T>
848 ossia::small_flat_map<const score::gfx::Port*, score::gfx::TextureRenderTarget, 2>
851 QRhiReadbackResult m_readbacks[avnd::texture_input_introspection<T>::size];
853 template <
typename Tex>
858 static constexpr auto flags
859 = QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource;
860 auto texture = renderer.
state.rhi->newTexture(
861 gpp::qrhi::textureFormat(texture_spec), spec.size, 1, flags);
862 SCORE_ASSERT(texture->create());
870 avnd::cpu_texture_input_introspection<T>::for_all_n(
871 avnd::get_inputs<T>(*self.state),
872 [&]<
typename F, std::size_t K>(F& t, avnd::predicate_index<K>) {
874 auto& parent = self.node();
875 auto spec = parent.resolveRenderTargetSpecs(K, renderer);
876 if constexpr(requires {
881 spec.size.rwidth() = t.request_width;
882 spec.size.rheight() = t.request_height;
885 createInput(renderer, parent.
input[K], t.texture, spec);
887 if constexpr(avnd::cpu_fixed_format_texture<
decltype(t.texture)>)
889 t.texture.width = spec.size.width();
890 t.texture.height = spec.size.height();
895 void runInitialPasses(
auto& self, QRhi& rhi)
901 avnd::cpu_texture_input_introspection<T>::for_all_n(
902 avnd::get_inputs<T>(*self.state), [&]<std::size_t K>(
auto& t, avnd::predicate_index<K>) {
903 oscr::loadInputTexture(rhi, m_readbacks, t.texture, K);
911 for(
auto [port, rt] : m_rts)
916 void inputAboutToFinish(
auto& parent,
const score::gfx::Port& p, QRhiResourceUpdateBatch*& res)
918 const auto& inputs = parent.
input;
919 auto index_of_port = ossia::find(inputs, &p) - inputs.begin();
921 auto tex = m_rts[&p].texture;
922 auto& readback = m_readbacks[index_of_port];
924 res->readBackTexture(QRhiReadbackDescription{tex}, &readback);
930 requires (avnd::texture_input_introspection<T>::size == 0)
931struct texture_inputs_storage<T>
933 static void init(
auto&&...) { }
934 static void runInitialPasses(
auto&&...) { }
935 static void release(
auto&&...) { }
936 static void inputAboutToFinish(
auto&&...) { }
941template <avnd::cpu_texture Tex>
944 auto& [sampler, texture] = self.m_samplers[k];
947 auto sz = texture->pixelSize();
948 if(cpu_tex.width == sz.width() && cpu_tex.height == sz.height())
953 if(cpu_tex.width > 0 && cpu_tex.height > 0)
955 QRhiTexture* oldtex = texture;
956 QRhiTexture* newtex = renderer.
state.rhi->newTexture(
957 gpp::qrhi::textureFormat(cpu_tex), QSize{cpu_tex.width, cpu_tex.height}, 1,
958 QRhiTexture::Flag{});
960 for(
auto& [edge, pass] : self.m_p)
962 score::gfx::replaceTexture(*pass.srb, sampler, newtex);
967 oldtex->deleteLater();
974 for(
auto& [edge, pass] : self.m_p)
976 score::gfx::replaceTexture(*pass.srb, sampler, &renderer.emptyTexture());
982template <avnd::cpu_texture Tex>
983static void uploadOutputTexture(
auto& self,
985 QRhiResourceUpdateBatch* res)
989 if(
auto texture = updateTexture(self, renderer, k, cpu_tex))
992 = QByteArray::fromRawData((
const char*)cpu_tex.bytes, cpu_tex.bytesize());
993 if constexpr(
requires { Tex::RGB; })
997 const QByteArray rgb = buf;
999 rgba.resize(cpu_tex.width * cpu_tex.height * 4);
1000 auto src = (
const unsigned char*)rgb.constData();
1001 auto dst = (
unsigned char*)rgba.data();
1002 for(
int rgb_byte = 0, rgba_byte = 0, N = rgb.size(); rgb_byte < N;)
1004 dst[rgba_byte + 0] = src[rgb_byte + 0];
1005 dst[rgba_byte + 1] = src[rgb_byte + 1];
1006 dst[rgba_byte + 2] = src[rgb_byte + 2];
1007 dst[rgba_byte + 3] = 255;
1016 QRhiTextureSubresourceUploadDescription sd(buf);
1017 QRhiTextureUploadDescription desc{QRhiTextureUploadEntry{0, 0, sd}};
1019 res->uploadTexture(texture, desc);
1022 cpu_tex.changed =
false;
1027static const constexpr auto generic_texgen_vs = R
"_(#version 450
1028layout(location = 0) in vec2 position;
1029layout(location = 1) in vec2 texcoord;
1031layout(binding=3) uniform sampler2D y_tex;
1032layout(location = 0) out vec2 v_texcoord;
1034layout(std140, binding = 0) uniform renderer_t {
1035 mat4 clipSpaceCorrMatrix;
1039out gl_PerVertex { vec4 gl_Position; };
1043#if defined(QSHADER_SPIRV) || defined(QSHADER_GLSL)
1044 v_texcoord = vec2(texcoord.x, 1. - texcoord.y);
1046 v_texcoord = texcoord;
1048 gl_Position = renderer.clipSpaceCorrMatrix * vec4(position.xy, 0.0, 1.);
1052static const constexpr auto generic_texgen_fs = R
"_(#version 450
1053layout(location = 0) in vec2 v_texcoord;
1054layout(location = 0) out vec4 fragColor;
1056layout(std140, binding = 0) uniform renderer_t {
1057mat4 clipSpaceCorrMatrix;
1061layout(binding=3) uniform sampler2D y_tex;
1065 fragColor = texture(y_tex, v_texcoord);
1070struct texture_outputs_storage;
1074 requires (avnd::texture_output_introspection<T>::size > 0)
1075struct texture_outputs_storage<T>
1080 self.defaultMeshInit(renderer, mesh, res);
1081 self.processUBOInit(renderer);
1085 std::tie(self.m_vertexS, self.m_fragmentS)
1088 avnd::cpu_texture_output_introspection<T>::for_all(
1089 avnd::get_outputs<T>(*self.state), [&](
auto& t) {
1090 self.m_samplers.push_back(
1091 createOutputTexture(renderer, t.texture, QSize{t.texture.width, t.texture.height}));
1094 self.defaultPassesInit(renderer, mesh);
1097 void runInitialPasses(
auto& self,
1099 QRhiResourceUpdateBatch*& res)
1101 avnd::cpu_texture_output_introspection<T>::for_all_n(
1102 avnd::get_outputs<T>(*self.state), [&]<std::size_t N>(
auto& t, avnd::predicate_index<N>) {
1103 uploadOutputTexture(self, renderer, N, t.texture, res);
1110 for(
auto& [sampl, texture] : self.m_samplers)
1113 texture->deleteLater();
1121 requires (avnd::texture_output_introspection<T>::size == 0)
1122struct texture_outputs_storage<T>
1128 static void runInitialPasses(
auto& self,
1130 QRhiResourceUpdateBatch*& res)
1139struct geometry_outputs_storage;
1142 requires (avnd::geometry_output_introspection<T>::size > 0)
1143struct geometry_outputs_storage<T>
1145 ossia::geometry_spec specs[avnd::geometry_output_introspection<T>::size];
1147 template <avnd::geometry_port Field>
1148 void reload_mesh(Field& ctrl, ossia::geometry_spec& spc)
1150 spc.meshes = std::make_shared<ossia::mesh_list>();
1151 auto& ossia_meshes = *spc.meshes;
1152 if constexpr(avnd::static_geometry_type<Field> || avnd::dynamic_geometry_type<Field>)
1154 ossia_meshes.meshes.resize(1);
1155 load_geometry(ctrl, ossia_meshes.meshes[0]);
1158 avnd::static_geometry_type<
decltype(Field::mesh)>
1159 || avnd::dynamic_geometry_type<
decltype(Field::mesh)>)
1161 ossia_meshes.meshes.resize(1);
1162 load_geometry(ctrl.mesh, ossia_meshes.meshes[0]);
1166 load_geometry(ctrl, ossia_meshes);
1170 template <avnd::geometry_port Field, std::
size_t N>
1173 avnd::predicate_index<N>)
1175 auto edge_sink = edge.sink;
1178 ossia::geometry_spec& spc = specs[N];
1184 reload_mesh(ctrl, spc);
1190 auto& ossia_meshes = *spc.meshes;
1192 bool need_reload =
false;
1193 if constexpr(avnd::static_geometry_type<Field> || avnd::dynamic_geometry_type<Field>)
1195 SCORE_ASSERT(ossia_meshes.meshes.size() == 1);
1196 need_reload = update_geometry(ctrl, ossia_meshes.meshes[0]);
1199 avnd::static_geometry_type<
decltype(Field::mesh)>
1200 || avnd::dynamic_geometry_type<
decltype(Field::mesh)>)
1202 SCORE_ASSERT(ossia_meshes.meshes.size() == 1);
1203 need_reload = update_geometry(ctrl.mesh, ossia_meshes.meshes[0]);
1207 need_reload = update_geometry(ctrl, ossia_meshes);
1212 reload_mesh(ctrl, spc);
1216 ctrl.dirty_mesh =
false;
1222 auto rendered_node = pnode->renderedNodes.find(&renderer);
1223 SCORE_ASSERT(rendered_node != pnode->renderedNodes.end());
1225 auto it = std::find(
1226 edge_sink->node->input.begin(), edge_sink->node->input.end(), edge_sink);
1227 SCORE_ASSERT(it != edge_sink->node->input.end());
1228 int n = it - edge_sink->node->input.begin();
1230 rendered_node->second->process(n, spc);
1234 if constexpr(
requires { ctrl.transform; })
1236 if(ctrl.dirty_transform)
1238 ossia::transform3d transform;
1239 std::copy_n(ctrl.transform, std::ssize(ctrl.transform), transform.matrix);
1240 ctrl.dirty_transform =
false;
1241 pnode->process(n, transform);
1250 avnd::geometry_output_introspection<T>::for_all_n(
1251 avnd::get_outputs(state),
1252 [&](
auto& field,
auto pred) { this->upload(renderer, field, edge, pred); });
1258 requires (avnd::geometry_output_introspection<T>::size == 0)
1259struct geometry_outputs_storage<T>
1261 static void upload(
auto&&...)
Root data model for visual nodes.
Definition score-plugin-gfx/Gfx/Graph/Node.hpp:75
std::vector< Port * > input
Input ports of that node.
Definition score-plugin-gfx/Gfx/Graph/Node.hpp:104
virtual void process(Message &&msg)
Process a message from the execution engine.
Definition Node.cpp:25
ossia::small_pod_vector< Port *, 1 > output
Output ports of that node.
Definition score-plugin-gfx/Gfx/Graph/Node.hpp:110
Common base class for most single-pass, simple nodes.
Definition score-plugin-gfx/Gfx/Graph/Node.hpp:204
Renderer for a given node.
Definition NodeRenderer.hpp:11
Base class for sink nodes (QWindow, spout, syphon, NDI output, ...)
Definition OutputNode.hpp:24
Common base class for nodes that map to score processes.
Definition score-plugin-gfx/Gfx/Graph/Node.hpp:177
List of nodes to be rendered to an output.
Definition RenderList.hpp:19
bool requiresDepth() const noexcept
Whether this list of rendering actions requires depth testing at all.
Definition RenderList.hpp:142
const score::gfx::Mesh & defaultTriangle() const noexcept
A triangle mesh correct for this API.
Definition RenderList.cpp:370
RenderState & state
RenderState corresponding to this RenderList.
Definition RenderList.hpp:94
QRhiTexture & emptyTexture() const noexcept
Texture to use when a texture is missing.
Definition RenderList.hpp:117
TreeNode< DeviceExplorerNode > Node
Definition DeviceNode.hpp:74
Definition Factories.hpp:19
GraphicsApi
Available graphics APIs to use.
Definition RenderState.hpp:20
void uploadStaticBufferWithStoredData(QRhiResourceUpdateBatch *ub, QRhiBuffer *buf, int offset, int64_t bytesize, const char *data)
Schedule a Static buffer update when we can guarantee the buffer outlives the frame.
Definition score-plugin-gfx/Gfx/Graph/Utils.hpp:337
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:393
TextureRenderTarget createRenderTarget(const RenderState &state, QRhiTexture *tex, int samples, bool depth)
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:97
Definition DocumentContext.hpp:18
Connection between two score::gfx::Port.
Definition score-plugin-gfx/Gfx/Graph/Utils.hpp:70
Definition score-plugin-gfx/Gfx/Graph/Node.hpp:50
Port of a score::gfx::Node.
Definition score-plugin-gfx/Gfx/Graph/Utils.hpp:52
Definition score-plugin-gfx/Gfx/Graph/Node.hpp:57
Stores a sampler and the texture currently associated with it.
Definition score-plugin-gfx/Gfx/Graph/Utils.hpp:25