nodes: minor overhaul to functionality

The nodes now have more control over how their accelerated shaders/sample
functions are run, as well as how items are popped off the value tables.
This allows for various optimizations that we didn't have access to before.
This commit is contained in:
itsmattkc
2020-06-12 00:54:15 +10:00
parent 663cf4a020
commit dd7af0e6bd
44 changed files with 1051 additions and 697 deletions
+14 -8
View File
@@ -59,17 +59,23 @@ QString PanNode::Description() const
return tr("Adjust the stereo panning of an audio source.");
}
Node::Capabilities PanNode::GetCapabilities(const NodeValueDatabase &) const
NodeValueTable PanNode::Value(NodeValueDatabase &value) const
{
return kSampleProcessor;
// Create a sample job
SampleJob job(samples_input_);
job.InsertValue(panning_input_, value);
// Push it to our table
NodeValueTable table = value.Merge();
if (!qIsNull(job.GetValue(samples_input_).data().toDouble())) {
table.Push(NodeParam::kSampleJob, QVariant::fromValue(job), this);
}
return table;
}
NodeInput *PanNode::ProcessesSamplesFrom(const NodeValueDatabase &) const
{
return samples_input_;
}
void PanNode::ProcessSamples(const NodeValueDatabase &values, const AudioParams &params, const SampleBufferPtr input, SampleBufferPtr output, int index) const
void PanNode::ProcessSamples(NodeValueDatabase &values, const AudioParams &params, const SampleBufferPtr input, SampleBufferPtr output, int index) const
{
if (params.channel_count() != 2) {
// This node currently only works for stereo audio
+3 -3
View File
@@ -37,9 +37,9 @@ public:
virtual QList<CategoryID> Category() const override;
virtual QString Description() const override;
virtual Capabilities GetCapabilities(const NodeValueDatabase&) const override;
virtual NodeInput* ProcessesSamplesFrom(const NodeValueDatabase &value) const override;
virtual void ProcessSamples(const NodeValueDatabase& values, const AudioParams& params, const SampleBufferPtr input, SampleBufferPtr output, int index) const override;
virtual NodeValueTable Value(NodeValueDatabase &value) const override;
virtual void ProcessSamples(NodeValueDatabase &values, const AudioParams& params, const SampleBufferPtr input, SampleBufferPtr output, int index) const override;
virtual void Retranslate() override;
+12 -13
View File
@@ -58,17 +58,21 @@ QString VolumeNode::Description() const
return tr("Adjusts the volume of an audio source.");
}
Node::Capabilities VolumeNode::GetCapabilities(const NodeValueDatabase &) const
NodeValueTable VolumeNode::Value(NodeValueDatabase &value) const
{
return kSampleProcessor;
SampleJob job(samples_input_);
job.InsertValue(volume_input_, value);
NodeValueTable table = value.Merge();
if (qFuzzyCompare(job.GetValue(volume_input_).data().toDouble(), 1.0)) {
table.Push(NodeParam::kSampleJob, QVariant::fromValue(job), this);
}
return table;
}
NodeInput *VolumeNode::ProcessesSamplesFrom(const NodeValueDatabase &) const
{
return samples_input_;
}
void VolumeNode::ProcessSamples(const NodeValueDatabase &values, const AudioParams& params, const SampleBufferPtr input, SampleBufferPtr output, int index) const
void VolumeNode::ProcessSamples(NodeValueDatabase &values, const AudioParams& params, const SampleBufferPtr input, SampleBufferPtr output, int index) const
{
float volume_val = values[volume_input_].Get(NodeParam::kFloat).toFloat();
@@ -83,9 +87,4 @@ void VolumeNode::Retranslate()
volume_input_->set_name(tr("Volume"));
}
NodeInput *VolumeNode::samples_input() const
{
return samples_input_;
}
OLIVE_NAMESPACE_EXIT
+7 -4
View File
@@ -37,13 +37,16 @@ public:
virtual QList<CategoryID> Category() const override;
virtual QString Description() const override;
virtual Capabilities GetCapabilities(const NodeValueDatabase&) const override;
virtual NodeInput* ProcessesSamplesFrom(const NodeValueDatabase &value) const override;
virtual void ProcessSamples(const NodeValueDatabase& values, const AudioParams& params, const SampleBufferPtr input, SampleBufferPtr output, int index) const override;
virtual NodeValueTable Value(NodeValueDatabase &value) const override;
virtual void ProcessSamples(NodeValueDatabase &values, const AudioParams& params, const SampleBufferPtr input, SampleBufferPtr output, int index) const override;
virtual void Retranslate() override;
NodeInput* samples_input() const;
NodeInput* samples_input() const
{
return samples_input_;
}
private:
NodeInput* samples_input_;
+40 -13
View File
@@ -80,25 +80,52 @@ void BlurFilterNode::Retranslate()
repeat_edge_pixels_input_->set_name(tr("Repeat Edge Pixels"));
}
Node::Capabilities BlurFilterNode::GetCapabilities(const NodeValueDatabase &) const
ShaderCode BlurFilterNode::GetShaderCode(const QByteArray &shader_id) const
{
return kShader;
Q_UNUSED(shader_id)
return ShaderCode(ReadFileAsString(":/shaders/blur.frag"), QString());
}
QString BlurFilterNode::ShaderFragmentCode(const NodeValueDatabase &) const
NodeValueTable BlurFilterNode::Value(NodeValueDatabase &value) const
{
return ReadFileAsString(":/shaders/blur.frag");
}
ShaderJob job;
int BlurFilterNode::ShaderIterations() const
{
// FIXME: Optimize if horiz_in or vert_in is disabled
return 2;
}
job.InsertValue(texture_input_, value);
job.InsertValue(method_input_, value);
job.InsertValue(radius_input_, value);
job.InsertValue(horiz_input_, value);
job.InsertValue(vert_input_, value);
job.InsertValue(repeat_edge_pixels_input_, value);
NodeInput *BlurFilterNode::ShaderIterativeInput() const
{
return texture_input_;
NodeValueTable table = value.Merge();
// If there's no texture, no need to run an operation
if (!job.GetValue(texture_input_).data().isNull()) {
// Check if radius > 0, and both "horiz" and/or "vert" are enabled
if ((job.GetValue(horiz_input_).data().toBool() || job.GetValue(vert_input_).data().toBool())
&& job.GetValue(radius_input_).data().toDouble() > 0.0) {
// Set iteration count to 2 if we're blurring both horizontally and vertically
if (job.GetValue(horiz_input_).data().toBool() && job.GetValue(vert_input_).data().toBool()) {
job.SetIterations(2, texture_input_);
}
// If we're not repeating pixels, expect an alpha channel to appear
if (!job.GetValue(repeat_edge_pixels_input_).data().toBool()) {
job.SetAlphaChannelRequired(true);
}
table.Push(NodeParam::kShaderJob, QVariant::fromValue(job), this);
} else {
// If we're not performing the blur job, just push the texture
table.Push(job.GetValue(texture_input_));
}
}
return table;
}
OLIVE_NAMESPACE_EXIT
+2 -5
View File
@@ -39,11 +39,8 @@ public:
virtual void Retranslate() override;
virtual Capabilities GetCapabilities(const NodeValueDatabase&) const override;
virtual QString ShaderFragmentCode(const NodeValueDatabase&) const override;
virtual int ShaderIterations() const override;
virtual NodeInput* ShaderIterativeInput() const override;
virtual ShaderCode GetShaderCode(const QByteArray &shader_id) const override;
virtual NodeValueTable Value(NodeValueDatabase &value) const override;
private:
NodeInput* texture_input_;
+25 -4
View File
@@ -82,14 +82,35 @@ void StrokeFilterNode::Retranslate()
inner_input_->set_name(tr("Inner"));
}
Node::Capabilities StrokeFilterNode::GetCapabilities(const NodeValueDatabase &) const
NodeValueTable StrokeFilterNode::Value(NodeValueDatabase &value) const
{
return kShader;
ShaderJob job;
job.InsertValue(tex_input_, value);
job.InsertValue(color_input_, value);
job.InsertValue(radius_input_, value);
job.InsertValue(opacity_input_, value);
job.InsertValue(inner_input_, value);
NodeValueTable table = value.Merge();
if (!job.GetValue(tex_input_).data().isNull()) {
if (job.GetValue(radius_input_).data().toDouble() > 0.0
&& job.GetValue(opacity_input_).data().toDouble() > 0.0) {
table.Push(NodeParam::kShaderJob, QVariant::fromValue(job), this);
} else {
table.Push(job.GetValue(tex_input_));
}
}
return table;
}
QString StrokeFilterNode::ShaderFragmentCode(const NodeValueDatabase &) const
ShaderCode StrokeFilterNode::GetShaderCode(const QByteArray &shader_id) const
{
return ReadFileAsString(":/shaders/stroke.frag");
Q_UNUSED(shader_id)
return ShaderCode(ReadFileAsString(":/shaders/stroke.frag"), QString());
}
OLIVE_NAMESPACE_EXIT
+2 -2
View File
@@ -39,8 +39,8 @@ public:
virtual void Retranslate() override;
virtual Capabilities GetCapabilities(const NodeValueDatabase&) const override;
virtual QString ShaderFragmentCode(const NodeValueDatabase&) const override;
virtual NodeValueTable Value(NodeValueDatabase &value) const override;
virtual ShaderCode GetShaderCode(const QByteArray &shader_id) const override;
private:
NodeInput* tex_input_;
+4 -4
View File
@@ -100,7 +100,7 @@ NodeValueTable MatrixGenerator::Value(NodeValueDatabase &value) const
return output;
}
bool MatrixGenerator::GizmoPress(const NodeValueDatabase &db, const QPointF &p, const QVector2D &scale, const QSize &viewport)
bool MatrixGenerator::GizmoPress(NodeValueDatabase &db, const QPointF &p, const QVector2D &scale, const QSize &viewport)
{
GizmoSharedData gizmo_data(viewport, scale);
@@ -167,7 +167,7 @@ bool MatrixGenerator::HasGizmos() const
return true;
}
void MatrixGenerator::DrawGizmos(const NodeValueDatabase &db, QPainter *p, const QVector2D &scale, const QSize& viewport) const
void MatrixGenerator::DrawGizmos(NodeValueDatabase &db, QPainter *p, const QVector2D &scale, const QSize& viewport) const
{
p->setPen(Qt::white);
@@ -219,7 +219,7 @@ QMatrix4x4 MatrixGenerator::GenerateMatrix(NodeValueDatabase &value) const
value[anchor_input_].Take(NodeParam::kVec2).value<QVector2D>());
}
QMatrix4x4 MatrixGenerator::GenerateMatrix(const NodeValueDatabase &value, bool ignore_anchor) const
QMatrix4x4 MatrixGenerator::GenerateMatrix(NodeValueDatabase &value, bool ignore_anchor) const
{
QVector2D anchor;
@@ -262,7 +262,7 @@ QMatrix4x4 MatrixGenerator::GenerateMatrix(const QVector2D& pos,
return mat;
}
QPointF MatrixGenerator::GetGizmoAnchorPoint(const NodeValueDatabase &db,
QPointF MatrixGenerator::GetGizmoAnchorPoint(NodeValueDatabase &db,
const GizmoSharedData& gizmo_data) const
{
QMatrix4x4 matrix;
+4 -4
View File
@@ -47,9 +47,9 @@ public:
virtual NodeValueTable Value(NodeValueDatabase& value) const override;
virtual bool HasGizmos() const override;
virtual void DrawGizmos(const NodeValueDatabase& db, QPainter *p, const QVector2D &scale, const QSize& viewport) const override;
virtual void DrawGizmos(NodeValueDatabase& db, QPainter *p, const QVector2D &scale, const QSize& viewport) const override;
virtual bool GizmoPress(const NodeValueDatabase& db, const QPointF &p, const QVector2D &scale, const QSize& viewport) override;
virtual bool GizmoPress(NodeValueDatabase& db, const QPointF &p, const QVector2D &scale, const QSize& viewport) override;
virtual void GizmoMove(const QPointF &p, const QVector2D &scale, const rational &time) override;
virtual void GizmoRelease() override;
@@ -63,14 +63,14 @@ private:
};
QMatrix4x4 GenerateMatrix(NodeValueDatabase& value) const;
QMatrix4x4 GenerateMatrix(const NodeValueDatabase& value, bool ignore_anchor) const;
QMatrix4x4 GenerateMatrix(NodeValueDatabase &value, bool ignore_anchor) const;
static QMatrix4x4 GenerateMatrix(const QVector2D &pos,
const float &rot,
const QVector2D &scale,
bool uniform_scale,
const QVector2D &anchor);
QPointF GetGizmoAnchorPoint(const NodeValueDatabase &db, const GizmoSharedData &gizmo_data) const;
QPointF GetGizmoAnchorPoint(NodeValueDatabase &db, const GizmoSharedData &gizmo_data) const;
static int GetGizmoAnchorPointRadius();
NodeInput* gizmo_drag_;
+20 -7
View File
@@ -85,14 +85,23 @@ void PolygonGenerator::Retranslate()
color_input_->set_name(tr("Color"));
}
Node::Capabilities PolygonGenerator::GetCapabilities(const NodeValueDatabase &) const
ShaderCode PolygonGenerator::GetShaderCode(const QByteArray &shader_id) const
{
return kShader;
Q_UNUSED(shader_id)
return ShaderCode(Node::ReadFileAsString(":/shaders/polygon.frag"), QString());
}
QString PolygonGenerator::ShaderFragmentCode(const NodeValueDatabase &) const
NodeValueTable PolygonGenerator::Value(NodeValueDatabase &value) const
{
return Node::ReadFileAsString(":/shaders/polygon.frag");
ShaderJob job;
job.InsertValue(points_input_, value);
job.InsertValue(color_input_, value);
NodeValueTable table = value.Merge();
table.Push(NodeParam::kShaderJob, QVariant::fromValue(job), this);
return table;
}
bool PolygonGenerator::HasGizmos() const
@@ -100,8 +109,10 @@ bool PolygonGenerator::HasGizmos() const
return true;
}
void PolygonGenerator::DrawGizmos(const NodeValueDatabase &db, QPainter *p, const QVector2D &scale, const QSize &viewport) const
void PolygonGenerator::DrawGizmos(NodeValueDatabase &db, QPainter *p, const QVector2D &scale, const QSize &viewport) const
{
Q_UNUSED(viewport)
if (!points_input_->GetSize()) {
return;
}
@@ -118,8 +129,10 @@ void PolygonGenerator::DrawGizmos(const NodeValueDatabase &db, QPainter *p, cons
p->drawRects(rects);
}
bool PolygonGenerator::GizmoPress(const NodeValueDatabase &db, const QPointF &p, const QVector2D &scale, const QSize& viewport)
bool PolygonGenerator::GizmoPress(NodeValueDatabase &db, const QPointF &p, const QVector2D &scale, const QSize& viewport)
{
Q_UNUSED(viewport)
QVector<QPointF> points = GetGizmoCoordinates(db, scale);
QVector<QRectF> rects = GetGizmoRects(points);
@@ -158,7 +171,7 @@ void PolygonGenerator::GizmoRelease()
gizmo_y_dragger_.End();
}
QVector<QPointF> PolygonGenerator::GetGizmoCoordinates(const NodeValueDatabase &db, const QVector2D& scale) const
QVector<QPointF> PolygonGenerator::GetGizmoCoordinates(NodeValueDatabase &db, const QVector2D& scale) const
{
QVector<QPointF> points(points_input_->GetSize());
+5 -5
View File
@@ -40,18 +40,18 @@ public:
virtual void Retranslate() override;
virtual Capabilities GetCapabilities(const NodeValueDatabase&) const override;
virtual QString ShaderFragmentCode(const NodeValueDatabase&) const override;
virtual ShaderCode GetShaderCode(const QByteArray& shader_id) const override;
virtual NodeValueTable Value(NodeValueDatabase &value) const override;
virtual bool HasGizmos() const override;
virtual void DrawGizmos(const NodeValueDatabase& db, QPainter *p, const QVector2D &scale, const QSize& viewport) const override;
virtual void DrawGizmos(NodeValueDatabase& db, QPainter *p, const QVector2D &scale, const QSize& viewport) const override;
virtual bool GizmoPress(const NodeValueDatabase& db, const QPointF &p, const QVector2D &scale, const QSize& viewport) override;
virtual bool GizmoPress(NodeValueDatabase &db, const QPointF &p, const QVector2D &scale, const QSize& viewport) override;
virtual void GizmoMove(const QPointF &p, const QVector2D &scale, const rational &time) override;
virtual void GizmoRelease() override;
private:
QVector<QPointF> GetGizmoCoordinates(const NodeValueDatabase &db, const QVector2D &scale) const;
QVector<QPointF> GetGizmoCoordinates(NodeValueDatabase &db, const QVector2D &scale) const;
QVector<QRectF> GetGizmoRects(const QVector<QPointF>& points) const;
+11 -4
View File
@@ -63,14 +63,21 @@ void SolidGenerator::Retranslate()
color_input_->set_name(tr("Color"));
}
Node::Capabilities SolidGenerator::GetCapabilities(const NodeValueDatabase &) const
NodeValueTable SolidGenerator::Value(NodeValueDatabase &value) const
{
return kShader;
ShaderJob job;
job.InsertValue(color_input_, value);
NodeValueTable table = value.Merge();
table.Push(NodeParam::kShaderJob, QVariant::fromValue(job), this);
return table;
}
QString SolidGenerator::ShaderFragmentCode(const NodeValueDatabase &) const
ShaderCode SolidGenerator::GetShaderCode(const QByteArray &shader_id) const
{
return ReadFileAsString(":/shaders/solid.frag");
Q_UNUSED(shader_id)
return ShaderCode(ReadFileAsString(":/shaders/solid.frag"), QString());
}
OLIVE_NAMESPACE_EXIT
+2 -2
View File
@@ -39,8 +39,8 @@ public:
virtual void Retranslate() override;
virtual Capabilities GetCapabilities(const NodeValueDatabase&) const override;
virtual QString ShaderFragmentCode(const NodeValueDatabase&) const override;
virtual NodeValueTable Value(NodeValueDatabase &value) const override;
virtual ShaderCode GetShaderCode(const QByteArray &shader_id) const override;
private:
NodeInput* color_input_;
+1 -7
View File
@@ -57,7 +57,7 @@ void MediaInput::Retranslate()
NodeValueTable MediaInput::Value(NodeValueDatabase &value) const
{
NodeValueTable table;
NodeValueTable table = value.Merge();
if (connected_footage_) {
rational media_duration = Timecode::timestamp_to_time(connected_footage_->duration(),
@@ -66,12 +66,6 @@ NodeValueTable MediaInput::Value(NodeValueDatabase &value) const
table.Push(NodeInput::kRational, QVariant::fromValue(media_duration), this, "length");
}
// Push buffer to the top of the stack
NodeValue buffer = value[footage_input_].GetWithMeta(NodeParam::kBuffer);
if (buffer.type() != NodeParam::kNone) {
table.Push(buffer);
}
return table;
}
+103 -99
View File
@@ -89,45 +89,21 @@ void MathNode::Retranslate()
method_in_->set_combobox_strings(operations);
}
Node::Capabilities MathNode::GetCapabilities(const NodeValueDatabase &input) const
ShaderCode MathNode::GetShaderCode(const QByteArray &shader_id) const
{
PairingCalculator calc(input[param_a_in_], input[param_b_in_]);
QDataStream data(shader_id);
switch (calc.GetMostLikelyPairing()) {
case kPairTextureColor:
case kPairTextureNumber:
case kPairTextureTexture:
case kPairTextureMatrix:
return kShader;
case kPairSampleNumber:
return kSampleProcessor;
default:
return kNormal;
}
}
Pairing pairing;
NodeParam::DataType type_a;
NodeParam::DataType type_b;
QString MathNode::ShaderID(const NodeValueDatabase &input) const
{
QString method = QString::number(GetOperation());
data >> pairing;
data >> type_a;
data >> type_b;
PairingCalculator calc(input[param_a_in_], input[param_b_in_]);
QString operation, frag, vert;
QString type_a = QString::number(calc.GetMostLikelyValueA().type());
QString type_b = QString::number(calc.GetMostLikelyValueB().type());
return id().append(method).append(type_a).append(type_b);
}
QString MathNode::ShaderFragmentCode(const NodeValueDatabase &input) const
{
PairingCalculator calc(input[param_a_in_], input[param_b_in_]);
NodeParam::DataType type_a = calc.GetMostLikelyValueA().type();
NodeParam::DataType type_b = calc.GetMostLikelyValueB().type();
QString operation;
if (calc.GetMostLikelyPairing() == kPairTextureMatrix && GetOperation() == kOpMultiply) {
if (pairing == kPairTextureMatrix && GetOperation() == kOpMultiply) {
// Override the operation for this operation since we multiply texture COORDS by the matrix rather than
NodeParam* tex_in = (type_a == NodeParam::kTexture) ? param_a_in_ : param_b_in_;
@@ -135,6 +111,11 @@ QString MathNode::ShaderFragmentCode(const NodeValueDatabase &input) const
// No-op frag shader (can we return QString() instead?)
operation = QStringLiteral("texture(%1, ove_texcoord)").arg(tex_in->id());
// Override the operation for this operation since we multiply texture COORDS by the matrix rather than
NodeParam* mat_in = (type_a == NodeParam::kTexture) ? param_b_in_ : param_a_in_;
vert = ReadFileAsString(":/shaders/matrix.vert").arg(mat_in->id(), tex_in->id());
} else {
switch (GetOperation()) {
case kOpAdd:
@@ -158,7 +139,7 @@ QString MathNode::ShaderFragmentCode(const NodeValueDatabase &input) const
GetShaderVariableCall(param_b_in_->id(), type_b));
}
return QStringLiteral("#version 150\n"
frag = QStringLiteral("#version 150\n"
"\n"
"uniform %1 %3;\n"
"uniform %2 %4;\n"
@@ -174,44 +155,8 @@ QString MathNode::ShaderFragmentCode(const NodeValueDatabase &input) const
param_a_in_->id(),
param_b_in_->id(),
operation);
}
QString MathNode::ShaderVertexCode(const NodeValueDatabase &input) const
{
PairingCalculator calc(input[param_a_in_], input[param_b_in_]);
if (calc.GetMostLikelyPairing() == kPairTextureMatrix && GetOperation() == kOpMultiply) {
NodeParam::DataType type_a = calc.GetMostLikelyValueA().type();
// Override the operation for this operation since we multiply texture COORDS by the matrix rather than
NodeParam* tex_in = (type_a == NodeParam::kTexture) ? param_a_in_ : param_b_in_;
NodeParam* mat_in = (type_a == NodeParam::kTexture) ? param_b_in_ : param_a_in_;
return ReadFileAsString(":/shaders/matrix.vert").arg(mat_in->id(), tex_in->id());
}
return QString();
}
NodeValue MathNode::InputValueFromTable(NodeInput *input, NodeValueDatabase &db, bool take) const
{
if (input == param_a_in_ || input == param_b_in_) {
PairingCalculator calc(db[param_a_in_], db[param_b_in_]);
NodeValue v = (input == param_a_in_)
? calc.GetMostLikelyValueA()
: calc.GetMostLikelyValueB();
if (take) {
db[input].Remove(v);
}
return v;
}
return Node::InputValueFromTable(input, db, take);
return ShaderCode(frag, vert);
}
NodeValueTable MathNode::Value(NodeValueDatabase &value) const
@@ -220,12 +165,8 @@ NodeValueTable MathNode::Value(NodeValueDatabase &value) const
// FIXME: Add manual override for this
PairingCalculator calc(value[param_a_in_], value[param_b_in_]);
if (!calc.FoundMostLikelyPairing()
|| calc.GetMostLikelyPairing() == kPairSampleNumber
|| calc.GetMostLikelyPairing() == kPairTextureTexture
|| calc.GetMostLikelyPairing() == kPairTextureNumber
|| calc.GetMostLikelyPairing() == kPairTextureColor
|| calc.GetMostLikelyPairing() == kPairTextureMatrix) {
// Do nothing if no pairing was found
if (!calc.FoundMostLikelyPairing()) {
return value.Merge();
}
@@ -347,40 +288,82 @@ NodeValueTable MathNode::Value(NodeValueDatabase &value) const
break;
}
case kPairNone:
case kPairCount:
case kPairTextureColor:
case kPairTextureNumber:
case kPairTextureTexture:
case kPairTextureMatrix:
{
ShaderJob job;
QByteArray shader_id;
QDataStream shader_id_stream(&shader_id, QIODevice::WriteOnly);
shader_id_stream << calc.GetMostLikelyPairing();
shader_id_stream << val_a.type();
shader_id_stream << val_b.type();
job.SetShaderID(shader_id);
job.InsertValue(param_a_in_, val_a);
job.InsertValue(param_b_in_, val_b);
bool operation_is_noop = false;
if (calc.GetMostLikelyPairing() == kPairTextureNumber) {
NodeValue& number_val = val_a.type() == NodeParam::kTexture ? val_b : val_a;
if (NumberIsNoOp(GetOperation(), RetrieveNumber(number_val))) {
operation_is_noop = true;
}
}
if (!operation_is_noop) {
output.Push(NodeParam::kShaderJob, QVariant::fromValue(job), this);
} else {
output.Push(val_a.type() == NodeParam::kTexture ? val_a : val_b);
}
break;
}
case kPairSampleNumber:
// Do nothing
{
// Queue a sample job
SampleJob job(val_a.type() == NodeParam::kSamples ? param_a_in_ : param_b_in_);
NodeValue& number_val = val_a.type() == NodeParam::kSamples ? val_b : val_a;
NodeInput* number_param = val_a.type() == NodeParam::kSamples ? param_b_in_ : param_a_in_;
float number = RetrieveNumber(number_val);
if (!NumberIsNoOp(GetOperation(), number)) {
job.InsertValue(number_param, NodeValue(NodeParam::kFloat, number, this));
output.Push(NodeParam::kSampleJob, QVariant::fromValue(job), this);
} else {
output.Push(val_a.type() == NodeParam::kSamples ? val_a : val_b);
}
break;
}
case kPairNone:
case kPairCount:
break;
}
return output;
}
NodeInput *MathNode::ProcessesSamplesFrom(const NodeValueDatabase &value) const
void MathNode::ProcessSamples(NodeValueDatabase &values, const AudioParams &params, const SampleBufferPtr input, SampleBufferPtr output, int index) const
{
PairingCalculator calc(value[param_a_in_], value[param_b_in_]);
// This function is only used for sample+number pairing
NodeValue number_val = values[param_a_in_].GetWithMeta(NodeParam::kNumber);
if (calc.GetMostLikelyPairing() == kPairSampleNumber) {
if (calc.GetMostLikelyValueA().type() == NodeParam::kSamples) {
return param_a_in_;
} else {
return param_b_in_;
if (number_val.type() == NodeParam::kNone) {
number_val = values[param_b_in_].GetWithMeta(NodeParam::kNumber);
if (number_val.type() == NodeParam::kNone) {
return;
}
}
return nullptr;
}
void MathNode::ProcessSamples(const NodeValueDatabase &values, const AudioParams &params, const SampleBufferPtr input, SampleBufferPtr output, int index) const
{
// This function is only used for sample+number pairing
NodeInput* number_input = (ProcessesSamplesFrom(values) == param_a_in_) ? param_b_in_ : param_a_in_;
NodeValue number_val = values[number_input].GetWithMeta(NodeParam::kNumber);
float number_flt = RetrieveNumber(number_val);
for (int i=0;i<params.channel_count();i++) {
@@ -471,6 +454,27 @@ float MathNode::RetrieveNumber(const NodeValue &val)
}
}
bool MathNode::NumberIsNoOp(const MathNode::Operation &op, const float &number)
{
switch (op) {
case kOpAdd:
case kOpSubtract:
if (qIsNull(number)) {
return true;
}
break;
case kOpMultiply:
case kOpDivide:
case kOpPower:
if (qFuzzyCompare(number, 1.0f)) {
return true;
}
break;
}
return false;
}
MathNode::PairingCalculator::PairingCalculator(const NodeValueTable &table_a, const NodeValueTable &table_b)
{
QVector<int> pair_likelihood_a = GetPairLikelihood(table_a);
@@ -501,8 +505,8 @@ MathNode::PairingCalculator::PairingCalculator(const NodeValueTable &table_a, co
}
if (most_likely_pairing_ != kPairNone) {
most_likely_value_a_ = table_a.At(pair_likelihood_a.at(most_likely_pairing_));
most_likely_value_b_ = table_b.At(pair_likelihood_b.at(most_likely_pairing_));
most_likely_value_a_ = table_a.at(pair_likelihood_a.at(most_likely_pairing_));
most_likely_value_b_ = table_b.at(pair_likelihood_b.at(most_likely_pairing_));
}
}
@@ -513,7 +517,7 @@ QVector<int> MathNode::PairingCalculator::GetPairLikelihood(const NodeValueTable
QVector<int> likelihood(kPairCount, -1);
for (int i=0;i<table.Count();i++) {
NodeParam::DataType type = table.At(i).type();
NodeParam::DataType type = table.at(i).type();
int weight = i;
+4 -9
View File
@@ -39,17 +39,10 @@ public:
virtual void Retranslate() override;
virtual Capabilities GetCapabilities(const NodeValueDatabase&) const override;
virtual QString ShaderID(const NodeValueDatabase&) const override;
virtual QString ShaderFragmentCode(const NodeValueDatabase&) const override;
virtual QString ShaderVertexCode(const NodeValueDatabase&input) const override;
virtual NodeValue InputValueFromTable(NodeInput* input, NodeValueDatabase &db, bool take) const override;
virtual ShaderCode GetShaderCode(const QByteArray &shader_id) const override;
virtual NodeValueTable Value(NodeValueDatabase &value) const override;
virtual NodeInput* ProcessesSamplesFrom(const NodeValueDatabase &value) const override;
virtual void ProcessSamples(const NodeValueDatabase &values, const AudioParams& params, const SampleBufferPtr input, SampleBufferPtr output, int index) const override;
virtual void ProcessSamples(NodeValueDatabase &values, const AudioParams& params, const SampleBufferPtr input, SampleBufferPtr output, int index) const override;
NodeInput* param_a_in() const;
NodeInput* param_b_in() const;
@@ -134,6 +127,8 @@ private:
static float RetrieveNumber(const NodeValue& val);
static bool NumberIsNoOp(const Operation& op, const float& number);
void PushVector(NodeValueTable* output, NodeParam::DataType type, const QVector4D& vec) const;
NodeInput* method_in_;
+27 -4
View File
@@ -62,14 +62,37 @@ void MergeNode::Retranslate()
blend_in_->set_name(tr("Blend"));
}
Node::Capabilities MergeNode::GetCapabilities(const NodeValueDatabase &) const
ShaderCode MergeNode::GetShaderCode(const QByteArray &shader_id) const
{
return kShader;
Q_UNUSED(shader_id)
return ShaderCode(ReadFileAsString(":/shaders/alphaover.frag"), QString());
}
QString MergeNode::ShaderFragmentCode(const NodeValueDatabase &) const
NodeValueTable MergeNode::Value(NodeValueDatabase &value) const
{
return ReadFileAsString(":/shaders/alphaover.frag");
ShaderJob job;
job.InsertValue(base_in_, value);
job.InsertValue(blend_in_, value);
// FIXME: Check if "blend" is RGB-only, in which case it's a no-op
NodeValueTable table = value.Merge();
if (!job.GetValue(base_in_).data().isNull() || !job.GetValue(blend_in_).data().isNull()) {
if (job.GetValue(base_in_).data().isNull()) {
// We only have a blend texture, no need to alpha over
table.Push(job.GetValue(blend_in_));
} else if (job.GetValue(blend_in_).data().isNull()) {
// We only have a base texture, no need to alpha over
table.Push(job.GetValue(base_in_));
} else {
// We have both textures, push the job
table.Push(NodeParam::kShaderJob, QVariant::fromValue(job), this);
}
}
return table;
}
NodeInput *MergeNode::base_in() const
+2 -2
View File
@@ -39,8 +39,8 @@ public:
virtual void Retranslate() override;
virtual Capabilities GetCapabilities(const NodeValueDatabase&) const override;
virtual QString ShaderFragmentCode(const NodeValueDatabase&) const override;
virtual ShaderCode GetShaderCode(const QByteArray &shader_id) const override;
virtual NodeValueTable Value(NodeValueDatabase &value) const override;
NodeInput* base_in() const;
NodeInput* blend_in() const;
+7 -52
View File
@@ -276,11 +276,11 @@ bool Node::HasGizmos() const
return false;
}
void Node::DrawGizmos(const NodeValueDatabase &, QPainter *, const QVector2D &, const QSize &) const
void Node::DrawGizmos(NodeValueDatabase &, QPainter *, const QVector2D &, const QSize &) const
{
}
bool Node::GizmoPress(const NodeValueDatabase &, const QPointF &, const QVector2D &, const QSize &viewport)
bool Node::GizmoPress(NodeValueDatabase &, const QPointF &, const QVector2D &, const QSize &)
{
return false;
}
@@ -457,42 +457,14 @@ QList<Node *> Node::GetImmediateDependencies() const
return GetDependenciesInternal(false, false);
}
Node::Capabilities Node::GetCapabilities(const NodeValueDatabase &) const
ShaderCode Node::GetShaderCode(const QByteArray &shader_id) const
{
return kNormal;
Q_UNUSED(shader_id)
return ShaderCode(QString(), QString());
}
QString Node::ShaderID(const NodeValueDatabase &) const
{
return id();
}
QString Node::ShaderVertexCode(const NodeValueDatabase &) const
{
return QString();
}
QString Node::ShaderFragmentCode(const NodeValueDatabase&) const
{
return QString();
}
int Node::ShaderIterations() const
{
return 1;
}
NodeInput *Node::ShaderIterativeInput() const
{
return nullptr;
}
NodeInput* Node::ProcessesSamplesFrom(const NodeValueDatabase &) const
{
return nullptr;
}
void Node::ProcessSamples(const NodeValueDatabase &, const AudioParams&, const SampleBufferPtr, SampleBufferPtr, int) const
void Node::ProcessSamples(NodeValueDatabase &, const AudioParams&, const SampleBufferPtr, SampleBufferPtr, int) const
{
}
@@ -752,23 +724,6 @@ NodeOutput *Node::output() const
return output_;
}
NodeValue Node::InputValueFromTable(NodeInput *input, NodeValueDatabase &db, bool take) const
{
NodeParam::DataType find_data_type = input->data_type();
// Exception for Footage types (try to get a Texture instead)
if (find_data_type == NodeParam::kFootage) {
find_data_type = NodeParam::kTexture;
}
// Try to get a value from it
if (take) {
return db[input].TakeWithMeta(find_data_type);
} else {
return db[input].GetWithMeta(find_data_type);
}
}
const QPointF &Node::GetPosition() const
{
return position_;
+5 -44
View File
@@ -35,6 +35,7 @@
#include "node/output.h"
#include "node/value.h"
#include "render/audioparams.h"
#include "render/shaderinfo.h"
OLIVE_NAMESPACE_ENTER
@@ -56,12 +57,6 @@ class Node : public QObject
{
Q_OBJECT
public:
enum Capabilities {
kNormal = 0x0,
kShader = 0x1,
kSampleProcessor = 0x2
};
enum CategoryID {
kCategoryUnknown = -1,
@@ -176,47 +171,15 @@ public:
*/
QList<Node*> GetImmediateDependencies() const;
/**
* @brief Return accelerated capabilities of this node (if any)
*/
virtual Capabilities GetCapabilities(const NodeValueDatabase&) const;
/**
* @brief Generate a unique identifier for the shader code (if a node can produce multiple)
*/
virtual QString ShaderID(const NodeValueDatabase&) const;
/**
* @brief Generate hardware accelerated code for this Node
*/
virtual QString ShaderVertexCode(const NodeValueDatabase&) const;
/**
* @brief Generate hardware accelerated code for this Node
*/
virtual QString ShaderFragmentCode(const NodeValueDatabase&) const;
/**
* @brief Number of iterations to run the accelerated code
*
* Some code is faster if it's merely repeated on a resulting texture rather than run once on the same buffer.
*/
virtual int ShaderIterations() const;
/**
* @brief Parameter that should receive the buffer on an iteration past the first
*/
virtual NodeInput* ShaderIterativeInput() const;
/**
* @brief Return whether this node processes samples or not
*/
virtual NodeInput* ProcessesSamplesFrom(const NodeValueDatabase &value) const;
virtual ShaderCode GetShaderCode(const QByteArray& shader_id) const;
/**
* @brief If ProcessesSamples() is true, this is the function that will process them.
*/
virtual void ProcessSamples(const NodeValueDatabase &values, const AudioParams& params, const SampleBufferPtr input, SampleBufferPtr output, int index) const;
virtual void ProcessSamples(NodeValueDatabase &values, const AudioParams& params, const SampleBufferPtr input, SampleBufferPtr output, int index) const;
/**
* @brief Returns the input with the specified ID (or nullptr if it doesn't exist)
@@ -393,8 +356,6 @@ public:
NodeOutput* output() const;
virtual NodeValue InputValueFromTable(NodeInput* input, NodeValueDatabase &db, bool take) const;
const QPointF& GetPosition() const;
void SetPosition(const QPointF& pos);
@@ -407,9 +368,9 @@ public:
virtual bool HasGizmos() const;
virtual void DrawGizmos(const NodeValueDatabase& db, QPainter* p, const QVector2D &scale, const QSize& viewport) const;
virtual void DrawGizmos(NodeValueDatabase& db, QPainter* p, const QVector2D &scale, const QSize& viewport) const;
virtual bool GizmoPress(const NodeValueDatabase& db, const QPointF& p, const QVector2D &scale, const QSize& viewport);
virtual bool GizmoPress(NodeValueDatabase& db, const QPointF& p, const QVector2D &scale, const QSize& viewport);
virtual void GizmoMove(const QPointF& p, const QVector2D &scale, const rational &time);
virtual void GizmoRelease();
+2
View File
@@ -212,6 +212,8 @@ QByteArray NodeParam::ValueToBytes(const NodeParam::DataType &type, const QVaria
case kString:
case kBuffer:
case kVector:
case kShaderJob:
case kSampleJob:
case kAny:
break;
}
+18
View File
@@ -178,6 +178,24 @@ public:
*/
kCombo = 0x8000,
/**
* Job type
*
* An internal type used to indicate to the renderer that an accelerated shader job needs to
* run. This value will usually be taken from a table and a kTexture value will be pushed to
* take its place.
*/
kShaderJob = 0x10000,
/**
* Job type
*
* An internal type used to indicate to the renderer that an accelerated sample job needs to
* take place. This value will usually be taken from a table and a kSamples value will be
* pushed to take its place.
*/
kSampleJob = 0x20000,
/**
****************************** BROAD IDENTIFIERS ******************************
*/
+125 -6
View File
@@ -38,9 +38,7 @@ NodeValueDatabase NodeTraverser::GenerateDatabase(const Node* node, const TimeRa
TimeRange input_time = node->InputTimeAdjustment(input, range);
NodeValueTable table = ProcessInput(input, input_time);
database.Insert(input, table);
database.Insert(input, ProcessInput(input, input_time));
}
// Insert global variables
@@ -84,7 +82,7 @@ NodeValueTable NodeTraverser::GenerateTable(const Node *n, const TimeRange& rang
// By this point, the node should have all the inputs it needs to render correctly
NodeValueTable table = n->Value(database);
ProcessNodeEvent(n, range, database, table);
PostProcessTable(n, range, table);
return table;
}
@@ -108,9 +106,130 @@ NodeValueTable NodeTraverser::GenerateBlockTable(const TrackOutput *track, const
return table;
}
StreamPtr NodeTraverser::ResolveStreamFromInput(NodeInput *input)
QVariant NodeTraverser::ProcessVideoFootage(StreamPtr stream, const rational &input_time)
{
return input->get_standard_value().value<StreamPtr>();
Q_UNUSED(stream)
Q_UNUSED(input_time)
return QVariant();
}
QVariant NodeTraverser::ProcessAudioFootage(StreamPtr stream, const TimeRange &input_time)
{
Q_UNUSED(stream)
Q_UNUSED(input_time)
return QVariant();
}
QVariant NodeTraverser::ProcessShader(const Node *node, const TimeRange &range, const ShaderJob &job)
{
Q_UNUSED(node)
Q_UNUSED(range)
Q_UNUSED(job)
return QVariant();
}
QVariant NodeTraverser::ProcessSamples(const Node *node, const TimeRange &range, const SampleJob &job)
{
Q_UNUSED(node)
Q_UNUSED(range)
Q_UNUSED(job)
return QVariant();
}
QVariant NodeTraverser::GetCachedFrame(const Node *node, const rational &time)
{
Q_UNUSED(node)
Q_UNUSED(time)
return QVariant();
}
void NodeTraverser::PostProcessTable(const Node *node, const TimeRange &range, NodeValueTable &output_params)
{
bool got_cached_frame = false;
// Convert footage to image/sample buffers
QVariant cached_frame = GetCachedFrame(node, range.in());
if (!cached_frame.isNull()) {
output_params.Push(NodeParam::kTexture, cached_frame, node);
// No more to do here
got_cached_frame = true;
}
// Strip out any jobs or footage
QList<NodeValue> video_footage_to_retrieve;
QList<NodeValue> audio_footage_to_retrieve;
QList<NodeValue> shader_jobs_to_run;
QList<NodeValue> sample_jobs_to_run;
for (int i=output_params.Count()-1; i>=0; i--) {
const NodeValue& v = output_params.at(i);
QList<NodeValue>* take_this_value_list = nullptr;
if (v.type() == NodeParam::kFootage) {
StreamPtr s = v.data().value<StreamPtr>();
if (s) {
if (s->type() == Stream::kVideo
|| s->type() == Stream::kImage) {
take_this_value_list = &video_footage_to_retrieve;
} else if (s->type() == Stream::kAudio) {
take_this_value_list = &audio_footage_to_retrieve;
}
}
} else if (v.type() == NodeParam::kShaderJob) {
take_this_value_list = &shader_jobs_to_run;
} else if (v.type() == NodeParam::kSampleJob) {
take_this_value_list = &sample_jobs_to_run;
}
if (take_this_value_list) {
take_this_value_list->append(output_params.TakeAt(i));
}
}
if (!got_cached_frame) {
// Retrieve video frames
foreach (const NodeValue& v, video_footage_to_retrieve) {
QVariant value = ProcessVideoFootage(v.data().value<StreamPtr>(), range.in());
if (!value.isNull()) {
output_params.Push(NodeParam::kTexture, value, node);
}
}
// Run shaders
foreach (const NodeValue& v, shader_jobs_to_run) {
QVariant value = ProcessShader(node, range, v.data().value<ShaderJob>());
if (!value.isNull()) {
output_params.Push(NodeParam::kTexture, value, node);
}
}
}
// Retrieve audio samples
foreach (const NodeValue& v, audio_footage_to_retrieve) {
QVariant value = ProcessAudioFootage(v.data().value<StreamPtr>(), range);
if (!value.isNull()) {
output_params.Push(NodeParam::kSamples, value, node);
}
}
// Run any accelerated shader jobs
foreach (const NodeValue& v, sample_jobs_to_run) {
QVariant value = ProcessSamples(node, range, v.data().value<SampleJob>());
if (!value.isNull()) {
output_params.Push(NodeParam::kSamples, value, node);
}
}
}
OLIVE_NAMESPACE_EXIT
+12 -6
View File
@@ -39,17 +39,23 @@ public:
NodeValueDatabase GenerateDatabase(const Node *node, const TimeRange &range);
static StreamPtr ResolveStreamFromInput(NodeInput* input);
protected:
NodeValueTable ProcessInput(NodeInput *input, const TimeRange &range);
virtual NodeValueTable GenerateBlockTable(const TrackOutput *track, const TimeRange& range);
virtual void ProcessNodeEvent(const Node*,
const TimeRange&,
NodeValueDatabase&,
NodeValueTable&) {}
virtual QVariant ProcessVideoFootage(StreamPtr stream, const rational &input_time);
virtual QVariant ProcessAudioFootage(StreamPtr stream, const TimeRange &input_time);
virtual QVariant ProcessShader(const Node *node, const TimeRange &range, const ShaderJob& job);
virtual QVariant ProcessSamples(const Node *node, const TimeRange &range, const SampleJob &job);
virtual QVariant GetCachedFrame(const Node *node, const rational &time);
private:
void PostProcessTable(const Node *node, const TimeRange &range, NodeValueTable &output_params);
};
+2 -12
View File
@@ -32,16 +32,6 @@ NodeValueTable& NodeValueDatabase::operator[](const NodeInput *input)
return tables_[input->id()];
}
const NodeValueTable NodeValueDatabase::operator[](const QString &input_id) const
{
return tables_[input_id];
}
const NodeValueTable NodeValueDatabase::operator[](const NodeInput *input) const
{
return tables_[input->id()];
}
void NodeValueDatabase::Insert(const QString &key, const NodeValueTable &value)
{
tables_.insert(key, value);
@@ -143,7 +133,7 @@ void NodeValueTable::Prepend(const NodeParam::DataType &type, const QVariant &da
Prepend(NodeValue(type, data, from, tag));
}
const NodeValue &NodeValueTable::At(int index) const
const NodeValue &NodeValueTable::at(int index) const
{
return values_.at(index);
}
@@ -209,7 +199,7 @@ NodeValueTable NodeValueTable::Merge(QList<NodeValueTable> tables)
int row_index = t.Count() - 1 - row;
merged_table.Prepend(t.At(row_index));
merged_table.Prepend(t.at(row_index));
}
return merged_table;
+2 -4
View File
@@ -60,7 +60,7 @@ public:
void Push(const NodeParam::DataType& type, const QVariant& data, const Node *from, const QString& tag = QString());
void Prepend(const NodeValue& value);
void Prepend(const NodeParam::DataType& type, const QVariant& data, const Node *from, const QString& tag = QString());
const NodeValue& At(int index) const;
const NodeValue& at(int index) const;
NodeValue TakeAt(int index);
int Count() const;
bool Has(const NodeParam::DataType& type) const;
@@ -85,9 +85,6 @@ public:
NodeValueTable& operator[](const QString& input_id);
NodeValueTable& operator[](const NodeInput* input);
const NodeValueTable operator[](const QString& input_id) const;
const NodeValueTable operator[](const NodeInput* input) const;
void Insert(const QString& key, const NodeValueTable &value);
void Insert(const NodeInput* key, const NodeValueTable& value);
@@ -100,6 +97,7 @@ private:
OLIVE_NAMESPACE_EXIT
Q_DECLARE_METATYPE(OLIVE_NAMESPACE::NodeValue)
Q_DECLARE_METATYPE(OLIVE_NAMESPACE::NodeValueTable)
Q_DECLARE_METATYPE(OLIVE_NAMESPACE::NodeValueDatabase)