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 void operator()(
auto& t,
auto field_index) =
delete;
193 template <
typename Self,
typename Node_T>
194 static void processControlIn(
199 avnd::input_introspection<Node_T>::for_all_n(
200 avnd::get_inputs<Node_T>(state),
201 GpuProcessIns<Self, Node_T>{self, state, renderer_mess, mess, ctx});
202 renderer_mess = mess;
208 std::weak_ptr<Execution::ExecutionCommandQueue> queue;
209 Gfx::exec_controls control_outs;
213 template <
typename Node_T>
214 void processControlOut(Node_T& state)
const noexcept
216 if(!this->control_outs.empty())
218 auto q = this->queue.lock();
223 avnd::parameter_output_introspection<Node_T>::for_all(
224 avnd::get_outputs(state), [&]<avnd::parameter T>(
const T& t) {
225 qq.enqueue([v = oscr::to_ossia_value(t, t.value),
226 port = control_outs[parm_k]]()
mutable {
227 std::swap(port->value, v);
228 port->changed = true;
238struct SCORE_PLUGIN_AVND_EXPORT GpuNodeElements
240 [[no_unique_address]] oscr::soundfile_storage<T> soundfiles;
242 [[no_unique_address]] oscr::midifile_storage<T> midifiles;
244#if defined(OSCR_HAS_MMAP_FILE_STORAGE)
245 [[no_unique_address]] oscr::raw_file_storage<T> rawfiles;
248 template <std::
size_t N, std::
size_t NField>
250 auto& state,
const std::shared_ptr<oscr::raw_file_data>& hdl,
251 avnd::predicate_index<N>, avnd::field_index<NField>)
254 state, hdl, avnd::predicate_index<N>{}, avnd::field_index<NField>{});
261 :
score::gfx::NodeModel{}
265 virtual ~CustomGfxNodeBase();
273 virtual ~CustomGfxOutputNodeBase();
278struct CustomGpuNodeBase
285 std::weak_ptr<Execution::ExecutionCommandQueue>&& q, Gfx::exec_controls&& ctls,
287 : GpuControlOuts{
std::move(q),
std::move(ctls)}
292 virtual ~CustomGpuNodeBase() =
default;
295 QString vertex, fragment, compute;
300struct SCORE_PLUGIN_AVND_EXPORT CustomGpuOutputNodeBase
306 CustomGpuOutputNodeBase(
307 std::weak_ptr<Execution::ExecutionCommandQueue> q, Gfx::exec_controls&& ctls,
309 virtual ~CustomGpuOutputNodeBase();
312 std::weak_ptr<score::gfx::RenderList> m_renderer{};
313 std::shared_ptr<score::gfx::RenderState> m_renderState{};
314 std::function<void()> m_update;
316 QString vertex, fragment, compute;
321 void setRenderer(std::shared_ptr<score::gfx::RenderList>)
override;
324 void startRendering()
override;
325 void render()
override;
326 void stopRendering()
override;
327 bool canRender()
const override;
328 void onRendererChange()
override;
332 std::function<
void()> onUpdate, std::function<
void()> onResize)
override;
334 void destroyOutput()
override;
335 std::shared_ptr<score::gfx::RenderState> renderState()
const override;
337 Configuration configuration() const noexcept override;
340template <typename Node_T, typename Node>
341void prepareNewState(
std::shared_ptr<Node_T>& eff, const Node& parent)
343 if constexpr(avnd::has_worker<Node_T>)
345 parent.initWorker(eff);
347 if constexpr(avnd::has_processor_to_gui_bus<Node_T>)
349 auto& process = parent.processModel;
350 eff->send_message = [ptr = QPointer{&process}](
auto&& b)
mutable {
354 if(ptr && ptr->to_ui)
355 MessageBusSender{ptr->to_ui}(std::move(b));
362 avnd::init_controls(*eff);
364 if constexpr(avnd::can_prepare<Node_T>)
366 if constexpr(avnd::function_reflection<&Node_T::prepare>::count == 1)
368 using prepare_type = avnd::first_argument<&Node_T::prepare>;
370 if_possible(t.instance = parent.instance);
380struct port_to_type_enum
382 template <std::
size_t I, avnd::buffer_port F>
383 constexpr auto operator()(avnd::field_reflection<I, F> p)
385 return score::gfx::Types::Buffer;
388 template <std::
size_t I, avnd::cpu_texture_port F>
389 constexpr auto operator()(avnd::field_reflection<I, F> p)
391 using texture_type = std::remove_cvref_t<
decltype(F::texture)>;
392 return avnd::cpu_fixed_format_texture<texture_type> ? score::gfx::Types::Image
393 : score::gfx::Types::Buffer;
396 template <std::
size_t I, avnd::sampler_port F>
397 constexpr auto operator()(avnd::field_reflection<I, F> p)
399 return score::gfx::Types::Image;
401 template <std::
size_t I, avnd::image_port F>
402 constexpr auto operator()(avnd::field_reflection<I, F> p)
404 return score::gfx::Types::Image;
406 template <std::
size_t I, avnd::attachment_port F>
407 constexpr auto operator()(avnd::field_reflection<I, F> p)
409 return score::gfx::Types::Image;
412 template <std::
size_t I, avnd::geometry_port F>
413 constexpr auto operator()(avnd::field_reflection<I, F> p)
415 return score::gfx::Types::Geometry;
417 template <std::
size_t I, avnd::mono_audio_port F>
418 constexpr auto operator()(avnd::field_reflection<I, F> p)
420 return score::gfx::Types::Audio;
422 template <std::
size_t I, avnd::poly_audio_port F>
423 constexpr auto operator()(avnd::field_reflection<I, F> p)
425 return score::gfx::Types::Audio;
427 template <std::
size_t I, avnd::
int_parameter F>
428 constexpr auto operator()(avnd::field_reflection<I, F> p)
430 return score::gfx::Types::Int;
432 template <std::
size_t I, avnd::enum_parameter F>
433 constexpr auto operator()(avnd::field_reflection<I, F> p)
435 return score::gfx::Types::Int;
437 template <std::
size_t I, avnd::
float_parameter F>
438 constexpr auto operator()(avnd::field_reflection<I, F> p)
440 return score::gfx::Types::Float;
442 template <std::
size_t I, avnd::parameter F>
443 constexpr auto operator()(avnd::field_reflection<I, F> p)
445 using value_type = std::remove_cvref_t<
decltype(F::value)>;
447 if constexpr(std::is_aggregate_v<value_type>)
449 constexpr int sz = boost::pfr::tuple_size_v<value_type>;
450 if constexpr(sz == 2)
452 return score::gfx::Types::Vec2;
454 else if constexpr(sz == 3)
456 return score::gfx::Types::Vec3;
458 else if constexpr(sz == 4)
460 return score::gfx::Types::Vec4;
463 return score::gfx::Types::Empty;
465 template <std::
size_t I,
typename F>
466 constexpr auto operator()(avnd::field_reflection<I, F> p)
468 return score::gfx::Types::Empty;
472template <
typename Node_T>
473inline void initGfxPorts(
auto* self,
auto& input,
auto& output)
475 avnd::input_introspection<Node_T>::for_all(
476 [self, &input]<
typename Field, std::size_t I>(avnd::field_reflection<I, Field> f) {
477 static constexpr auto type = port_to_type_enum{}(f);
480 avnd::output_introspection<Node_T>::for_all(
482 &output]<
typename Field, std::size_t I>(avnd::field_reflection<I, Field> f) {
483 static constexpr auto type = port_to_type_enum{}(f);
488static QRhiBuffer* getInputBuffer(
494 const auto& inputs = parent.
input;
495 SCORE_ASSERT(port_index == 0);
498 for(
auto& edge : p->edges)
500 auto src_node = edge->source->node;
504 return src_renderer->bufferForOutput(*edge->source);
513static void readbackInputBuffer(
515 , QRhiResourceUpdateBatch& res
517 , QRhiBufferReadbackResult& readback
523 if(
auto buf = getInputBuffer(renderer, parent, port_index))
526 res.readBackBuffer(buf, 0, buf->size(), &readback);
530static void recreateOutputBuffer(
532 QRhiResourceUpdateBatch& res, QRhiBuffer*& buf)
534 const auto bytesize = avnd::get_bytesize(cpu_buf);
539 buf = renderer.
state.rhi->newBuffer(
541 , QRhiBuffer::StorageBuffer | QRhiBuffer::VertexBuffer
548 cpu_buf.changed =
false;
552 else if(buf->size() != bytesize)
555 buf->setSize(bytesize);
560static void uploadOutputBuffer(
562 QRhiResourceUpdateBatch& res, QRhiBuffer*& buf)
566 const auto bytesize = avnd::get_bytesize(cpu_buf);
567 recreateOutputBuffer(renderer, cpu_buf, res, buf);
569 cpu_buf.changed =
false;
573static void uploadOutputBuffer(
575 QRhiResourceUpdateBatch& res, QRhiBuffer*& buf)
581struct buffer_inputs_storage;
584 requires (avnd::buffer_input_introspection<T>::size > 0)
585struct buffer_inputs_storage<T>
587 QRhiBufferReadbackResult m_readbacks[avnd::cpu_buffer_input_introspection<T>::size];
588 QRhiBuffer* m_gpubufs[avnd::gpu_buffer_input_introspection<T>::size];
590 void readInputBuffers(
593 if constexpr(avnd::cpu_buffer_input_introspection<T>::size > 0)
598 avnd::cpu_buffer_input_introspection<T>::for_all_n(
599 avnd::get_inputs<T>(state),
600 [&]<
typename Field, std::size_t N>
601 (Field& t, avnd::predicate_index<N> np)
603 auto& readback = m_readbacks[N].data;
604 t.buffer.bytes =
reinterpret_cast<unsigned char*
>(readback.data());
605 t.buffer.bytesize = readback.size();
606 t.buffer.changed =
true;
610 if constexpr(avnd::gpu_buffer_input_introspection<T>::size > 0)
615 avnd::gpu_buffer_input_introspection<T>::for_all_n2(
616 avnd::get_inputs<T>(state),
617 [&]<
typename Field, std::size_t N, std::size_t NField>
618 (Field& t, avnd::predicate_index<N> np, avnd::field_index<NField> nf)
620 auto* buf = m_gpubufs[N];
622 m_gpubufs[N] = getInputBuffer(renderer, parent, nf);
626 t.buffer.handle = buf;
627 t.buffer.bytesize = buf->size();
633 void inputAboutToFinish(
635 QRhiResourceUpdateBatch*& res,
639 avnd::cpu_buffer_input_introspection<T>::for_all_n2(
640 avnd::get_inputs<T>(state),
641 [&]<
typename Field, std::size_t N, std::size_t NField>
642 (Field& port, avnd::predicate_index<N> np, avnd::field_index<NField> nf) {
643 readbackInputBuffer(renderer, *res, parent, m_readbacks[N], nf);
645 avnd::gpu_buffer_input_introspection<T>::for_all_n2(
646 avnd::get_inputs<T>(state),
647 [&]<
typename Field, std::size_t N, std::size_t NField>
648 (Field& port, avnd::predicate_index<N> np, avnd::field_index<NField> nf) {
649 m_gpubufs[N] = getInputBuffer(renderer, parent, nf);
655 requires (avnd::buffer_input_introspection<T>::size == 0)
656struct buffer_inputs_storage<T>
658 static void readInputBuffers(
auto&&...)
663 static void inputAboutToFinish(
auto&&...)
670struct buffer_outputs_storage;
673 requires (avnd::buffer_output_introspection<T>::size > 0)
674struct buffer_outputs_storage<T>
676 std::pair<const score::gfx::Port*, QRhiBuffer*> m_buffers[avnd::buffer_output_introspection<T>::size];
678 QRhiResourceUpdateBatch* currentResourceUpdateBatch{};
679 std::pair<const score::gfx::Port*, QRhiBuffer*>
682 auto& rhi = *renderer.
state.rhi;
683 QRhiBuffer* buffer =
nullptr;
684 if(
const auto bs = avnd::get_bytesize(buf); bs > 0)
686 buffer = rhi.newBuffer(
688 , QRhiBuffer::StorageBuffer | QRhiBuffer::VertexBuffer
694 return {&port, buffer};
700 avnd::buffer_output_introspection<T>::for_all_n2(
701 avnd::get_outputs<T>(state), [&]<
typename Field, std::size_t N, std::size_t NField>
702 (Field& port, avnd::predicate_index<N> np, avnd::field_index<NField> nf) {
703 SCORE_ASSERT(parent.
output.size() > nf);
704 SCORE_ASSERT(parent.
output[nf]->type == score::gfx::Types::Buffer);
706 if constexpr(
requires { port.buffer.upload((
const char*)
nullptr, 1000, 0); })
708 auto& [gfx_port, buf] = m_buffers[N];
709 gfx_port = parent.
output[nf];
710 buf = renderer.
state.rhi->newBuffer(
712 , QRhiBuffer::StorageBuffer | QRhiBuffer::VertexBuffer
717 port.buffer.upload = [
this, &renderer, &port] (
const char* data, int64_t offset, int64_t bytesize) {
718 SCORE_ASSERT(currentResourceUpdateBatch);
719 auto& [gfx_port, buf] = m_buffers[N];
725 buf = renderer.
state.rhi->newBuffer(
727 , QRhiBuffer::StorageBuffer | QRhiBuffer::VertexBuffer
734 buf = renderer.
state.rhi->newBuffer(
736 , QRhiBuffer::StorageBuffer | QRhiBuffer::VertexBuffer
743 else if(buf->size() != bytesize)
746 buf->setSize(bytesize);
755 m_buffers[N] = createOutput(renderer, *parent.
output[nf], port.buffer);
760 void prepareUpload(QRhiResourceUpdateBatch& res)
762 currentResourceUpdateBatch = &res;
767 avnd::buffer_output_introspection<T>::for_all_n(
768 avnd::get_outputs<T>(state), [&]<std::size_t N>(
auto& t, avnd::predicate_index<N> idx) {
769 auto& [port, buf] = m_buffers[N];
770 uploadOutputBuffer(renderer, t.buffer, res, buf);
777 for(
auto& [p, buf] : m_buffers)
779 renderer.releaseBuffer(buf);
785 requires (avnd::buffer_output_introspection<T>::size == 0)
786struct buffer_outputs_storage<T>
788 static void init(
auto&&...)
793 static void prepareUpload(
auto&&...)
797 static void upload(
auto&&...)
802 static void release(
auto&&...)
809template <
typename Tex>
813 auto& rhi = *renderer.
state.rhi;
815 if(size.width() > 0 && size.height() > 0)
817 texture = rhi.newTexture(
818 gpp::qrhi::textureFormat(texture_spec), size, 1, QRhiTexture::Flag{});
823 auto sampler = rhi.newSampler(
824 QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
825 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
833struct texture_inputs_storage;
836 requires (avnd::texture_input_introspection<T>::size > 0)
837struct texture_inputs_storage<T>
839 ossia::small_flat_map<const score::gfx::Port*, score::gfx::TextureRenderTarget, 2>
842 QRhiReadbackResult m_readbacks[avnd::texture_input_introspection<T>::size];
844 template <
typename Tex>
849 static constexpr auto flags
850 = QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource;
851 auto texture = renderer.
state.rhi->newTexture(
852 gpp::qrhi::textureFormat(texture_spec), spec.size, 1, flags);
853 SCORE_ASSERT(texture->create());
861 avnd::cpu_texture_input_introspection<T>::for_all_n(
862 avnd::get_inputs<T>(*self.state),
863 [&]<
typename F, std::size_t K>(F& t, avnd::predicate_index<K>) {
865 auto& parent = self.node();
866 auto spec = parent.resolveRenderTargetSpecs(K, renderer);
867 if constexpr(requires {
872 spec.size.rwidth() = t.request_width;
873 spec.size.rheight() = t.request_height;
876 createInput(renderer, parent.
input[K], t.texture, spec);
878 if constexpr(avnd::cpu_fixed_format_texture<
decltype(t.texture)>)
880 t.texture.width = spec.size.width();
881 t.texture.height = spec.size.height();
886 void runInitialPasses(
auto& self, QRhi& rhi)
892 avnd::cpu_texture_input_introspection<T>::for_all_n(
893 avnd::get_inputs<T>(*self.state), [&]<std::size_t K>(
auto& t, avnd::predicate_index<K>) {
894 oscr::loadInputTexture(rhi, m_readbacks, t.texture, K);
902 for(
auto [port, rt] : m_rts)
907 void inputAboutToFinish(
auto& parent,
const score::gfx::Port& p, QRhiResourceUpdateBatch*& res)
909 const auto& inputs = parent.
input;
910 auto index_of_port = ossia::find(inputs, &p) - inputs.begin();
912 auto tex = m_rts[&p].texture;
913 auto& readback = m_readbacks[index_of_port];
915 res->readBackTexture(QRhiReadbackDescription{tex}, &readback);
921 requires (avnd::texture_input_introspection<T>::size == 0)
922struct texture_inputs_storage<T>
924 static void init(
auto&&...) { }
925 static void runInitialPasses(
auto&&...) { }
926 static void release(
auto&&...) { }
927 static void inputAboutToFinish(
auto&&...) { }
932template <avnd::cpu_texture Tex>
935 auto& [sampler, texture] = self.m_samplers[k];
938 auto sz = texture->pixelSize();
939 if(cpu_tex.width == sz.width() && cpu_tex.height == sz.height())
944 if(cpu_tex.width > 0 && cpu_tex.height > 0)
946 QRhiTexture* oldtex = texture;
947 QRhiTexture* newtex = renderer.
state.rhi->newTexture(
948 gpp::qrhi::textureFormat(cpu_tex), QSize{cpu_tex.width, cpu_tex.height}, 1,
949 QRhiTexture::Flag{});
951 for(
auto& [edge, pass] : self.m_p)
953 score::gfx::replaceTexture(*pass.srb, sampler, newtex);
958 oldtex->deleteLater();
965 for(
auto& [edge, pass] : self.m_p)
967 score::gfx::replaceTexture(*pass.srb, sampler, &renderer.emptyTexture());
973template <avnd::cpu_texture Tex>
974static void uploadOutputTexture(
auto& self,
976 QRhiResourceUpdateBatch* res)
980 if(
auto texture = updateTexture(self, renderer, k, cpu_tex))
983 = QByteArray::fromRawData((
const char*)cpu_tex.bytes, cpu_tex.bytesize());
984 if constexpr(
requires { Tex::RGB; })
988 const QByteArray rgb = buf;
990 rgba.resize(cpu_tex.width * cpu_tex.height * 4);
991 auto src = (
const unsigned char*)rgb.constData();
992 auto dst = (
unsigned char*)rgba.data();
993 for(
int rgb_byte = 0, rgba_byte = 0, N = rgb.size(); rgb_byte < N;)
995 dst[rgba_byte + 0] = src[rgb_byte + 0];
996 dst[rgba_byte + 1] = src[rgb_byte + 1];
997 dst[rgba_byte + 2] = src[rgb_byte + 2];
998 dst[rgba_byte + 3] = 255;
1007 QRhiTextureSubresourceUploadDescription sd(buf);
1008 QRhiTextureUploadDescription desc{QRhiTextureUploadEntry{0, 0, sd}};
1010 res->uploadTexture(texture, desc);
1013 cpu_tex.changed =
false;
1018static const constexpr auto generic_texgen_vs = R
"_(#version 450
1019layout(location = 0) in vec2 position;
1020layout(location = 1) in vec2 texcoord;
1022layout(binding=3) uniform sampler2D y_tex;
1023layout(location = 0) out vec2 v_texcoord;
1025layout(std140, binding = 0) uniform renderer_t {
1026 mat4 clipSpaceCorrMatrix;
1030out gl_PerVertex { vec4 gl_Position; };
1034#if defined(QSHADER_SPIRV) || defined(QSHADER_GLSL)
1035 v_texcoord = vec2(texcoord.x, 1. - texcoord.y);
1037 v_texcoord = texcoord;
1039 gl_Position = renderer.clipSpaceCorrMatrix * vec4(position.xy, 0.0, 1.);
1043static const constexpr auto generic_texgen_fs = R
"_(#version 450
1044layout(location = 0) in vec2 v_texcoord;
1045layout(location = 0) out vec4 fragColor;
1047layout(std140, binding = 0) uniform renderer_t {
1048mat4 clipSpaceCorrMatrix;
1052layout(binding=3) uniform sampler2D y_tex;
1056 fragColor = texture(y_tex, v_texcoord);
1061struct texture_outputs_storage;
1065 requires (avnd::texture_output_introspection<T>::size > 0)
1066struct texture_outputs_storage<T>
1071 self.defaultMeshInit(renderer, mesh, res);
1072 self.processUBOInit(renderer);
1076 std::tie(self.m_vertexS, self.m_fragmentS)
1079 avnd::cpu_texture_output_introspection<T>::for_all(
1080 avnd::get_outputs<T>(*self.state), [&](
auto& t) {
1081 self.m_samplers.push_back(
1082 createOutputTexture(renderer, t.texture, QSize{t.texture.width, t.texture.height}));
1085 self.defaultPassesInit(renderer, mesh);
1088 void runInitialPasses(
auto& self,
1090 QRhiResourceUpdateBatch*& res)
1092 avnd::cpu_texture_output_introspection<T>::for_all_n(
1093 avnd::get_outputs<T>(*self.state), [&]<std::size_t N>(
auto& t, avnd::predicate_index<N>) {
1094 uploadOutputTexture(self, renderer, N, t.texture, res);
1101 for(
auto& [sampl, texture] : self.m_samplers)
1104 texture->deleteLater();
1112 requires (avnd::texture_output_introspection<T>::size == 0)
1113struct texture_outputs_storage<T>
1119 static void runInitialPasses(
auto& self,
1121 QRhiResourceUpdateBatch*& res)
1130struct geometry_outputs_storage;
1133 requires (avnd::geometry_output_introspection<T>::size > 0)
1134struct geometry_outputs_storage<T>
1136 ossia::geometry_spec specs[avnd::geometry_output_introspection<T>::size];
1138 template <avnd::geometry_port Field, std::
size_t N>
1141 auto edge_sink = edge.sink;
1144 ossia::geometry_spec& spc = specs[N];
1147 spc.meshes = std::make_shared<ossia::mesh_list>();
1148 auto& ossia_meshes = *spc.meshes;
1149 if constexpr(avnd::static_geometry_type<Field> || avnd::dynamic_geometry_type<Field>)
1151 ossia_meshes.meshes.resize(1);
1152 load_geometry(ctrl, ossia_meshes.meshes[0]);
1155 avnd::static_geometry_type<
decltype(Field::mesh)>
1156 || avnd::dynamic_geometry_type<
decltype(Field::mesh)>)
1158 ossia_meshes.meshes.resize(1);
1159 load_geometry(ctrl.mesh, ossia_meshes.meshes[0]);
1163 load_geometry(ctrl, ossia_meshes);
1175 auto& ossia_meshes = *spc.meshes;
1177 bool need_reload =
false;
1178 if constexpr(avnd::static_geometry_type<Field> || avnd::dynamic_geometry_type<Field>)
1180 SCORE_ASSERT(ossia_meshes.meshes.size() == 1);
1181 need_reload = update_geometry(ctrl, ossia_meshes.meshes[0]);
1184 avnd::static_geometry_type<
decltype(Field::mesh)>
1185 || avnd::dynamic_geometry_type<
decltype(Field::mesh)>)
1187 SCORE_ASSERT(ossia_meshes.meshes.size() == 1);
1188 need_reload = update_geometry(ctrl.mesh, ossia_meshes.meshes[0]);
1192 need_reload = update_geometry(ctrl, ossia_meshes);
1201 ctrl.dirty_mesh =
false;
1204 auto it = std::find(edge_sink->node->input.begin(), edge_sink->node->input.end(), edge_sink);
1205 SCORE_ASSERT(it != edge_sink->node->input.end());
1206 int n = it - edge_sink->node->input.begin();
1207 pnode->process(n, spc);
1214 avnd::geometry_output_introspection<T>::for_all_n(
1215 avnd::get_outputs(state), [&](
auto& field,
auto pred) {
1216 upload(field, edge, pred);
1223 requires (avnd::geometry_output_introspection<T>::size == 0)
1224struct geometry_outputs_storage<T>
1226 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:231
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:196
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