Skip to content

Commit

Permalink
[avnd] Enable loading data files in GPU nodes
Browse files Browse the repository at this point in the history
  • Loading branch information
jcelerier committed Oct 21, 2024
1 parent 4e95fc1 commit 37a0c87
Show file tree
Hide file tree
Showing 10 changed files with 337 additions and 164 deletions.
25 changes: 15 additions & 10 deletions src/plugins/score-plugin-avnd/Crousti/CpuAnalysisNode.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ struct GfxRenderer<Node_T> final : score::gfx::OutputNodeRenderer
using texture_inputs = avnd::texture_input_introspection<Node_T>;
const GfxNode<Node_T>& parent;
Node_T state;
score::gfx::Message m_last_message{};
ossia::small_flat_map<const score::gfx::Port*, score::gfx::TextureRenderTarget, 2>
m_rts;

Expand Down Expand Up @@ -76,10 +77,11 @@ struct GfxRenderer<Node_T> final : score::gfx::OutputNodeRenderer

void init(score::gfx::RenderList& renderer, QRhiResourceUpdateBatch& res) override
{
if constexpr(requires { state.init(); })
if constexpr(requires { state.prepare(); })
{
parent.processControlIn(state, this->parent.last_message);
state.init();
parent.processControlIn(
*this, state, m_last_message, this->parent.last_message, this->parent.m_ctx);
state.prepare();
}

// Init input render targets
Expand Down Expand Up @@ -193,7 +195,8 @@ struct GfxRenderer<Node_T> final : score::gfx::OutputNodeRenderer
});
}

parent.processControlIn(state, this->parent.last_message);
parent.processControlIn(
*this, state, m_last_message, this->parent.last_message, this->parent.m_ctx);

// Run the processor
state();
Expand All @@ -204,16 +207,18 @@ struct GfxRenderer<Node_T> final : score::gfx::OutputNodeRenderer
};

template <typename Node_T>
requires(
avnd::texture_input_introspection<Node_T>::size > 0
&& avnd::texture_output_introspection<Node_T>::size == 0)
struct GfxNode<Node_T> final : CustomGpuOutputNodeBase
requires(avnd::texture_input_introspection<Node_T>::size > 0
&& avnd::texture_output_introspection<Node_T>::size == 0)
struct GfxNode<Node_T> final
: CustomGpuOutputNodeBase
, GpuNodeElements<Node_T>
{
oscr::ProcessModel<Node_T>& processModel;
GfxNode(
oscr::ProcessModel<Node_T>& element,
std::weak_ptr<Execution::ExecutionCommandQueue> q, Gfx::exec_controls ctls, int id)
: CustomGpuOutputNodeBase{std::move(q), std::move(ctls)}
std::weak_ptr<Execution::ExecutionCommandQueue> q, Gfx::exec_controls ctls, int id,
const score::DocumentContext& ctx)
: CustomGpuOutputNodeBase{std::move(q), std::move(ctls), ctx}
, processModel{element}
{
this->instance = id;
Expand Down
17 changes: 12 additions & 5 deletions src/plugins/score-plugin-avnd/Crousti/CpuFilterNode.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ struct GfxRenderer<Node_T> final : score::gfx::GenericNodeRenderer
using texture_outputs = avnd::texture_output_introspection<Node_T>;
const GfxNode<Node_T>& parent;
Node_T state;
score::gfx::Message m_last_message{};
ossia::small_flat_map<const score::gfx::Port*, score::gfx::TextureRenderTarget, 2>
m_rts;

Expand Down Expand Up @@ -154,9 +155,11 @@ struct GfxRenderer<Node_T> final : score::gfx::GenericNodeRenderer

void init(score::gfx::RenderList& renderer, QRhiResourceUpdateBatch& res) override
{
if constexpr(requires { state.init(); })
if constexpr(requires { state.prepare(); })
{
state.init();
parent.processControlIn(
*this, state, m_last_message, this->parent.last_message, this->parent.m_ctx);
state.prepare();
}

const auto& mesh = renderer.defaultTriangle();
Expand Down Expand Up @@ -267,7 +270,8 @@ struct GfxRenderer<Node_T> final : score::gfx::GenericNodeRenderer
});
}

parent.processControlIn(state, this->parent.last_message);
parent.processControlIn(
*this, state, m_last_message, this->parent.last_message, this->parent.m_ctx);

// Run the processor
state();
Expand Down Expand Up @@ -298,12 +302,15 @@ struct GfxNode<Node_T> final
, GpuWorker
, GpuControlIns
, GpuControlOuts
, GpuNodeElements<Node_T>
{
oscr::ProcessModel<Node_T>& processModel;
GfxNode(
oscr::ProcessModel<Node_T>& element,
std::weak_ptr<Execution::ExecutionCommandQueue> q, Gfx::exec_controls ctls, int id)
: GpuControlOuts{std::move(q), std::move(ctls)}
std::weak_ptr<Execution::ExecutionCommandQueue> q, Gfx::exec_controls ctls, int id,
const score::DocumentContext& ctx)
: CustomGfxNodeBase{ctx}
, GpuControlOuts{std::move(q), std::move(ctls)}
, processModel{element}
{
this->instance = id;
Expand Down
17 changes: 12 additions & 5 deletions src/plugins/score-plugin-avnd/Crousti/CpuGeneratorNode.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ struct GfxRenderer<Node_T> final : score::gfx::GenericNodeRenderer
using texture_outputs = avnd::texture_output_introspection<Node_T>;
const GfxNode<Node_T>& parent;
Node_T state;
score::gfx::Message m_last_message{};
ossia::time_value m_last_time{-1};

GfxRenderer(const GfxNode<Node_T>& p)
Expand Down Expand Up @@ -118,9 +119,11 @@ struct GfxRenderer<Node_T> final : score::gfx::GenericNodeRenderer

void init(score::gfx::RenderList& renderer, QRhiResourceUpdateBatch& res) override
{
if constexpr(requires { state.init(); })
if constexpr(requires { state.prepare(); })
{
state.init();
parent.processControlIn(
*this, state, m_last_message, this->parent.last_message, this->parent.m_ctx);
state.prepare();
}

const auto& mesh = renderer.defaultTriangle();
Expand Down Expand Up @@ -168,7 +171,8 @@ struct GfxRenderer<Node_T> final : score::gfx::GenericNodeRenderer
}
m_last_time = parent.last_message.token.date;

parent.processControlIn(state, this->parent.last_message);
parent.processControlIn(
*this, state, m_last_message, this->parent.last_message, parent.m_ctx);

// Run the processor
state();
Expand Down Expand Up @@ -197,12 +201,15 @@ struct GfxNode<Node_T> final
, GpuWorker
, GpuControlIns
, GpuControlOuts
, GpuNodeElements<Node_T>
{
oscr::ProcessModel<Node_T>& processModel;
GfxNode(
oscr::ProcessModel<Node_T>& element,
std::weak_ptr<Execution::ExecutionCommandQueue> q, Gfx::exec_controls ctls, int id)
: GpuControlOuts{std::move(q), std::move(ctls)}
std::weak_ptr<Execution::ExecutionCommandQueue> q, Gfx::exec_controls ctls, int id,
const score::DocumentContext& ctx)
: CustomGfxNodeBase{ctx}
, GpuControlOuts{std::move(q), std::move(ctls)}
, processModel{element}
{
this->instance = id;
Expand Down
124 changes: 9 additions & 115 deletions src/plugins/score-plugin-avnd/Crousti/Executor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <Crousti/CpuAnalysisNode.hpp>
#include <Crousti/CpuFilterNode.hpp>
#include <Crousti/CpuGeneratorNode.hpp>
#include <Crousti/File.hpp>
#include <Crousti/GpuComputeNode.hpp>
#include <Crousti/GpuNode.hpp>
#include <Crousti/MessageBus.hpp>
Expand All @@ -29,8 +30,6 @@
#include <Gfx/GfxApplicationPlugin.hpp>
#endif

#include <Media/AudioDecoder.hpp>

#include <score/tools/ThreadPool.hpp>

#include <ossia/detail/type_if.hpp>
Expand All @@ -45,104 +44,9 @@
#include <avnd/concepts/temporality.hpp>
#include <avnd/concepts/ui.hpp>
#include <avnd/concepts/worker.hpp>
#include <libremidi/reader.hpp>

namespace oscr
{
namespace
{
[[nodiscard]] static QString
filenameFromPort(const ossia::value& value, const score::DocumentContext& ctx)
{
if(auto str = value.target<std::string>())
return score::locateFilePath(QString::fromStdString(*str).trimmed(), ctx);
return {};
}

// TODO refactor this into a generic explicit soundfile loaded mechanism
[[nodiscard]] static auto
loadSoundfile(const ossia::value& value, const score::DocumentContext& ctx, double rate)
{
// Initialize the control with the current soundfile
if(auto str = filenameFromPort(value, ctx); !str.isEmpty())
{
auto dec = Media::AudioDecoder::decode_synchronous(str, rate);

if(dec.has_value())
{
auto hdl = std::make_shared<ossia::audio_data>();
hdl->data = std::move(dec->second);
hdl->path = str.toStdString();
hdl->rate = rate;
return hdl;
}
}
return ossia::audio_handle{};
}

using midifile_handle = std::shared_ptr<oscr::midifile_data>;
[[nodiscard]] inline midifile_handle
loadMidifile(const ossia::value& value, const score::DocumentContext& ctx)
{
// Initialize the control with the current soundfile
if(auto str = filenameFromPort(value, ctx); !str.isEmpty())
{
QFile f(str);
if(!f.open(QIODevice::ReadOnly))
return {};
auto ptr = f.map(0, f.size());

auto hdl = std::make_shared<oscr::midifile_data>();
if(auto ret = hdl->reader.parse((uint8_t*)ptr, f.size());
ret == libremidi::reader::invalid)
return {};

hdl->filename = str.toStdString();
return hdl;
}
return {};
}

using raw_file_handle = std::shared_ptr<raw_file_data>;
[[nodiscard]] inline raw_file_handle loadRawfile(
const ossia::value& value, const score::DocumentContext& ctx, bool text, bool mmap)
{
// Initialize the control with the current soundfile
if(auto filename = filenameFromPort(value, ctx); !filename.isEmpty())
{
if(!QFile::exists(filename))
return {};

auto hdl = std::make_shared<oscr::raw_file_data>();
hdl->file.setFileName(filename);
if(!hdl->file.open(QIODevice::ReadOnly))
return {};

if(mmap)
{
auto map = (char*)hdl->file.map(0, hdl->file.size());
hdl->data = QByteArray::fromRawData(map, hdl->file.size());
}
else
{
if(text)
hdl->file.setTextModeEnabled(true);

hdl->data = hdl->file.readAll();
}
hdl->filename = filename.toStdString();
return hdl;
}
return {};
}
[[nodiscard]] inline auto loadSoundfile(
const ossia::value& value, const score::DocumentContext& ctx,
const std::shared_ptr<ossia::execution_state>& st)
{
const double rate = ossia::exec_state_facade{st.get()}.sampleRate();
return loadSoundfile(value, ctx, rate);
}
}

template <typename ExecNode_T, typename T, std::size_t ControlN>
struct control_updater
Expand Down Expand Up @@ -378,16 +282,6 @@ struct setup_Impl0
}
}

template <typename Field>
static auto executePortPreprocess(auto& file)
{
using field_file_type = decltype(Field::file);
field_file_type ffile;
ffile.bytes = decltype(ffile.bytes)(file.data.constData(), file.file.size());
ffile.filename = file.filename;
return Field::process(ffile);
}

template <avnd::raw_file_port Field, std::size_t N, std::size_t NField>
void operator()(Field& param, avnd::predicate_index<N>, avnd::field_index<NField>)
{
Expand All @@ -396,11 +290,8 @@ struct setup_Impl0
if(auto inlet = qobject_cast<Process::ControlInlet*>(p))
{
// FIXME handle dynamic ports correctly

using file_ports = avnd::raw_file_input_introspection<Node>;
using elt = typename file_ports::template nth_element<N>;
static constexpr bool has_text = requires { decltype(elt::file)::text; };
static constexpr bool has_mmap = requires { decltype(elt::file)::mmap; };
static constexpr bool has_text = requires { decltype(Field::file)::text; };
static constexpr bool has_mmap = requires { decltype(Field::file)::mmap; };

// First we can load it directly since execution hasn't started yet
if(auto hdl = loadRawfile(inlet->value(), ctx.doc, has_text, has_mmap))
Expand Down Expand Up @@ -715,17 +606,20 @@ class Executor final
std::unique_ptr<score::gfx::Node> ptr;
if constexpr(GpuGraphicsNode2<Node>)
{
auto gpu_node = new CustomGpuNode<Node>(qex_ptr, node->control_outs, id);
auto gpu_node
= new CustomGpuNode<Node>(qex_ptr, node->control_outs, id, ctx.doc);
ptr.reset(gpu_node);
}
else if constexpr(GpuComputeNode2<Node>)
{
auto gpu_node = new GpuComputeNode<Node>(qex_ptr, node->control_outs, id);
auto gpu_node
= new GpuComputeNode<Node>(qex_ptr, node->control_outs, id, ctx.doc);
ptr.reset(gpu_node);
}
else if constexpr(GpuNode<Node>)
{
auto gpu_node = new GfxNode<Node>(element, qex_ptr, node->control_outs, id);
auto gpu_node
= new GfxNode<Node>(element, qex_ptr, node->control_outs, id, ctx.doc);
ptr.reset(gpu_node);
}
node->id = gfx_exec.ui->register_node(std::move(ptr));
Expand Down
Loading

0 comments on commit 37a0c87

Please sign in to comment.