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
+4
View File
@@ -49,6 +49,7 @@
#include "render/colormanager.h"
#include "render/diskmanager.h"
#include "render/pixelformat.h"
#include "render/shaderinfo.h"
#include "task/cache/cache.h"
#include "task/project/import/import.h"
#include "task/project/load/load.h"
@@ -542,6 +543,7 @@ void Core::DeclareTypesForQt()
qRegisterMetaType<rational>();
qRegisterMetaType<OpenGLTexturePtr>();
qRegisterMetaType<OpenGLTextureCache::ReferencePtr>();
qRegisterMetaType<NodeValue>();
qRegisterMetaType<NodeValueTable>();
qRegisterMetaType<NodeValueDatabase>();
qRegisterMetaType<FramePtr>();
@@ -553,6 +555,8 @@ void Core::DeclareTypesForQt()
qRegisterMetaType<Color>();
qRegisterMetaType<OLIVE_NAMESPACE::ProjectPtr>();
qRegisterMetaType<OLIVE_NAMESPACE::AudioVisualWaveform>();
qRegisterMetaType<OLIVE_NAMESPACE::SampleJob>();
qRegisterMetaType<OLIVE_NAMESPACE::ShaderJob>();
}
void Core::StartGUI(bool full_screen)
+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)
+1
View File
@@ -40,6 +40,7 @@ set(OLIVE_SOURCES
render/playbackcache.h
render/playbackcache.cpp
render/rendermodes.h
render/shaderinfo.h
render/videoparams.h
render/videoparams.cpp
PARENT_SCOPE
-4
View File
@@ -1,4 +0,0 @@
#ifndef SHADERINFO_H
#define SHADERINFO_H
#endif // SHADERINFO_H
-1
View File
@@ -28,7 +28,6 @@ set(OLIVE_SOURCES
render/backend/opengl/openglrenderfunctions.cpp
render/backend/opengl/openglshader.h
render/backend/opengl/openglshader.cpp
render/backend/opengl/openglshadercache.h
render/backend/opengl/opengltexture.h
render/backend/opengl/opengltexture.cpp
render/backend/opengl/opengltexturecache.h
+215 -175
View File
@@ -142,9 +142,16 @@ QVariant OpenGLProxy::FrameToValue(FramePtr frame, StreamPtr stream, const Video
frame_params.divider());
}
PixelFormat::Format texture_fmt;
if (PixelFormat::FormatHasAlphaChannel(frame_params.format())) {
texture_fmt = PixelFormat::GetFormatWithAlphaChannel(params.format());
} else {
texture_fmt = PixelFormat::GetFormatWithoutAlphaChannel(params.format());
}
VideoParams dest_params(frame_params.width(),
frame_params.height(),
params.format(),
texture_fmt,
frame_params.divider());
// Create destination texture
@@ -175,6 +182,47 @@ QVariant OpenGLProxy::PreCachedFrameToValue(FramePtr frame)
return QVariant::fromValue(texture_cache_.Get(ctx_, frame));
}
OpenGLShaderPtr OpenGLProxy::ResolveShaderFromCache(const Node *node, const QByteArray &shader_id)
{
// Make a composite of the node ID and the shader ID (if applicable)
QByteArray id = node->id().toUtf8();
id.append(shader_id);
OpenGLShaderPtr shader = shader_cache_.value(id);
if (!shader) {
// Since we have shader code, compile it now
ShaderCode code = node->GetShaderCode(shader_id);
QString vert_code = code.vert_code();
QString frag_code = code.frag_code();
if (frag_code.isEmpty() && vert_code.isEmpty()) {
qWarning() << "No shader code found for" << node->id() << "- operation will be a no-op";
}
if (frag_code.isEmpty()) {
frag_code = OpenGLShader::CodeDefaultFragment();
}
if (vert_code.isEmpty()) {
vert_code = OpenGLShader::CodeDefaultVertex();
}
shader = OpenGLShader::Create();
if (shader
&& shader->create()
&& shader->addShaderFromSourceCode(QOpenGLShader::Fragment, frag_code)
&& shader->addShaderFromSourceCode(QOpenGLShader::Vertex, vert_code)
&& shader->link()) {
shader_cache_.insert(shader_id, shader);
} else {
qWarning() << "Failed to compile shader for" << node->id();
shader = nullptr;
}
}
return shader;
}
void OpenGLProxy::Close()
{
shader_cache_.clear();
@@ -186,185 +234,145 @@ void OpenGLProxy::Close()
}
QVariant OpenGLProxy::RunNodeAccelerated(const Node *node,
const TimeRange &range,
NodeValueDatabase &input_params,
const VideoParams& params)
const TimeRange &range,
const ShaderJob &job,
const VideoParams& params)
{
OpenGLShaderPtr shader = shader_cache_.value(node->ShaderID(input_params));
// If this node is iterative, we'll pick up which input here
GLuint iterative_input = 0;
QList<GLuint> textures_to_bind;
bool input_textures_have_alpha = false;
OpenGLShaderPtr shader = ResolveShaderFromCache(node, job.GetShaderID());
if (!shader) {
// Since we have shader code, compile it now
QString vert_code = node->ShaderVertexCode(input_params);
QString frag_code = node->ShaderFragmentCode(input_params);
if (frag_code.isEmpty()) {
frag_code = OpenGLShader::CodeDefaultFragment();
}
if (vert_code.isEmpty()) {
vert_code = OpenGLShader::CodeDefaultVertex();
}
shader = OpenGLShader::Create();
shader->create();
shader->addShaderFromSourceCode(QOpenGLShader::Fragment, frag_code);
shader->addShaderFromSourceCode(QOpenGLShader::Vertex, vert_code);
shader->link();
shader_cache_.insert(node->id(), shader);
return QVariant();
}
// Create the output textures
QList<OpenGLTextureCache::ReferencePtr> dst_refs;
dst_refs.append(texture_cache_.Get(ctx_, params));
GLuint iterative_input = 0;
// If this node requires multiple iterations, get a texture for it too
if (node->ShaderIterations() > 1 && node->ShaderIterativeInput()) {
dst_refs.append(texture_cache_.Get(ctx_, params));
}
// Lock the shader so no other thread interferes as we set parameters and draw (and we don't interfere with any others)
shader->bind();
unsigned int input_texture_count = 0;
NodeValueMap::const_iterator i;
for (i=job.GetValues().constBegin(); i!=job.GetValues().constEnd(); i++) {
// See if the shader has takes this parameter as an input
int variable_location = shader->uniformLocation(i.key()->id());
foreach (NodeParam* param, node->parameters()) {
if (param->type() == NodeParam::kInput) {
// See if the shader has takes this parameter as an input
int variable_location = shader->uniformLocation(param->id());
if (variable_location == -1) {
continue;
}
if (variable_location > -1) {
// This variable is used in the shader, let's set it to our value
// This variable is used in the shader, let's set it
const QVariant& value = i.value().data();
NodeInput* input = static_cast<NodeInput*>(param);
const NodeParam::DataType& data_type = (i.value().type() != NodeParam::kNone)
? i.value().type()
: i.key()->data_type();
// Get value from database at this input
NodeValue meta_value = node->InputValueFromTable(input, input_params, false);
const QVariant& value = meta_value.data();
switch (data_type) {
case NodeInput::kInt:
shader->setUniformValue(variable_location, value.toInt());
break;
case NodeInput::kFloat:
shader->setUniformValue(variable_location, value.toFloat());
break;
case NodeInput::kVec2:
if (i.key()->IsArray()) {
QVector<NodeValue> nv = value.value< QVector<NodeValue> >();
QVector<QVector2D> a(nv.size());
NodeParam::DataType data_type;
if (meta_value.type() != NodeParam::kNone) {
// Use value's data type
data_type = meta_value.type();
} else {
// Fallback on null value, send the null to the parameter
data_type = input->data_type();
for (int j=0;j<a.size();j++) {
a[j] = nv.at(j).data().value<QVector2D>();
}
switch (data_type) {
case NodeInput::kInt:
shader->setUniformValue(variable_location, value.toInt());
break;
case NodeInput::kFloat:
shader->setUniformValue(variable_location, value.toFloat());
break;
case NodeInput::kVec2:
if (input->IsArray()) {
NodeInputArray* array = static_cast<NodeInputArray*>(input);
QVector<QVector2D> a(array->GetSize());
shader->setUniformValueArray(variable_location, a.constData(), a.size());
for (int i=0;i<a.size();i++) {
a[i] = input_params[array->At(i)].Get(NodeParam::kVec2).value<QVector2D>();
}
shader->setUniformValueArray(variable_location, a.constData(), a.size());
int count_location = shader->uniformLocation(QStringLiteral("%1_count").arg(input->id()));
if (count_location > -1) {
shader->setUniformValue(count_location,
array->GetSize());
}
} else {
shader->setUniformValue(variable_location, value.value<QVector2D>());
}
break;
case NodeInput::kVec3:
shader->setUniformValue(variable_location, value.value<QVector3D>());
break;
case NodeInput::kVec4:
shader->setUniformValue(variable_location, value.value<QVector4D>());
break;
case NodeInput::kMatrix:
shader->setUniformValue(variable_location, value.value<QMatrix4x4>());
break;
case NodeInput::kCombo:
shader->setUniformValue(variable_location, value.value<int>());
break;
case NodeInput::kColor:
{
Color color = value.value<Color>();
shader->setUniformValue(variable_location, color.red(), color.green(), color.blue(), color.alpha());
break;
int count_location = shader->uniformLocation(QStringLiteral("%1_count").arg(i.key()->id()));
if (count_location > -1) {
shader->setUniformValue(count_location, a.size());
}
case NodeInput::kBoolean:
shader->setUniformValue(variable_location, value.toBool());
break;
case NodeInput::kFootage:
case NodeInput::kTexture:
case NodeInput::kBuffer:
{
OpenGLTextureCache::ReferencePtr texture = value.value<OpenGLTextureCache::ReferencePtr>();
} else {
shader->setUniformValue(variable_location, value.value<QVector2D>());
}
break;
case NodeInput::kVec3:
shader->setUniformValue(variable_location, value.value<QVector3D>());
break;
case NodeInput::kVec4:
shader->setUniformValue(variable_location, value.value<QVector4D>());
break;
case NodeInput::kMatrix:
shader->setUniformValue(variable_location, value.value<QMatrix4x4>());
break;
case NodeInput::kCombo:
shader->setUniformValue(variable_location, value.value<int>());
break;
case NodeInput::kColor:
{
Color color = value.value<Color>();
functions_->glActiveTexture(GL_TEXTURE0 + input_texture_count);
shader->setUniformValue(variable_location, color.red(), color.green(), color.blue(), color.alpha());
break;
}
case NodeInput::kBoolean:
shader->setUniformValue(variable_location, value.toBool());
break;
case NodeInput::kTexture:
{
OpenGLTextureCache::ReferencePtr texture = value.value<OpenGLTextureCache::ReferencePtr>();
GLuint tex_id = texture ? texture->texture()->texture() : 0;
functions_->glBindTexture(GL_TEXTURE_2D, tex_id);
// Set value to bound texture
shader->setUniformValue(variable_location, input_texture_count);
// Set enable flag if shader wants it
int enable_param_location = shader->uniformLocation(QStringLiteral("%1_enabled").arg(input->id()));
if (enable_param_location > -1) {
shader->setUniformValue(enable_param_location,
tex_id > 0);
}
if (tex_id > 0) {
// Set texture resolution if shader wants it
int res_param_location = shader->uniformLocation(QStringLiteral("%1_resolution").arg(input->id()));
if (res_param_location > -1) {
shader->setUniformValue(res_param_location,
static_cast<GLfloat>(texture->texture()->width() * texture->texture()->divider()),
static_cast<GLfloat>(texture->texture()->height() * texture->texture()->divider()));
}
}
// If this texture binding is the iterative input, set it here
if (input == node->ShaderIterativeInput()) {
iterative_input = input_texture_count;
}
OpenGLRenderFunctions::PrepareToDraw(functions_);
input_texture_count++;
break;
}
case NodeInput::kSamples:
case NodeInput::kText:
case NodeInput::kRational:
case NodeInput::kFont:
case NodeInput::kFile:
case NodeInput::kDecimal:
case NodeInput::kNumber:
case NodeInput::kString:
case NodeInput::kVector:
case NodeInput::kNone:
case NodeInput::kAny:
break;
if (texture) {
if (PixelFormat::FormatHasAlphaChannel(texture->texture()->format())) {
input_textures_have_alpha = true;
}
}
// Set value to bound texture
shader->setUniformValue(variable_location, textures_to_bind.size());
// If this texture binding is the iterative input, set it here
if (i.key() == job.GetIterativeInput()) {
iterative_input = textures_to_bind.size();
}
GLuint tex_id = texture ? texture->texture()->texture() : 0;
textures_to_bind.append(tex_id);
// Set enable flag if shader wants it
int enable_param_location = shader->uniformLocation(QStringLiteral("%1_enabled").arg(i.key()->id()));
if (enable_param_location > -1) {
shader->setUniformValue(enable_param_location,
tex_id > 0);
}
if (tex_id > 0) {
// Set texture resolution if shader wants it
int res_param_location = shader->uniformLocation(QStringLiteral("%1_resolution").arg(i.key()->id()));
if (res_param_location > -1) {
shader->setUniformValue(res_param_location,
static_cast<GLfloat>(texture->texture()->width() * texture->texture()->divider()),
static_cast<GLfloat>(texture->texture()->height() * texture->texture()->divider()));
}
}
break;
}
case NodeInput::kSamples:
case NodeInput::kText:
case NodeInput::kRational:
case NodeInput::kFont:
case NodeInput::kFile:
case NodeInput::kDecimal:
case NodeInput::kNumber:
case NodeInput::kString:
case NodeInput::kVector:
case NodeInput::kShaderJob:
case NodeInput::kSampleJob:
case NodeInput::kFootage:
case NodeInput::kBuffer:
case NodeInput::kNone:
case NodeInput::kAny:
break;
}
}
// Set up OpenGL parameters as necessary
functions_->glViewport(0, 0, params.effective_width(), params.effective_height());
// Provide some standard args
shader->setUniformValue("ove_resolution",
static_cast<GLfloat>(params.width()),
@@ -383,25 +391,65 @@ QVariant OpenGLProxy::RunNodeAccelerated(const Node *node,
shader->setUniformValue("ove_tprog_in", static_cast<GLfloat>(transition_node->GetInProgress(range.in())));
}
shader->release();
// Create the output textures
PixelFormat::Format output_format = (input_textures_have_alpha || job.GetAlphaChannelRequired())
? PixelFormat::GetFormatWithAlphaChannel(params.format())
: PixelFormat::GetFormatWithoutAlphaChannel(params.format());
VideoParams output_params(params.width(),
params.height(),
params.time_base(),
output_format,
params.divider());
int real_iteration_count;
if (job.GetIterationCount() > 1 && job.GetIterativeInput()) {
real_iteration_count = job.GetIterationCount();
} else {
real_iteration_count = 1;
}
OpenGLTextureCache::ReferencePtr dst_refs[2];
dst_refs[0] = texture_cache_.Get(ctx_, output_params);
// If this node requires multiple iterations, get a texture for it too
if (real_iteration_count > 1) {
dst_refs[1] = texture_cache_.Get(ctx_, output_params);
}
// Some nodes use multiple iterations for optimization
OpenGLTextureCache::ReferencePtr output_tex;
OpenGLTextureCache::ReferencePtr input_tex, output_tex;
for (int iteration=0;iteration<node->ShaderIterations();iteration++) {
// If this is not the first iteration, set the parameter that will receive the last iteration's texture
OpenGLTextureCache::ReferencePtr source_tex = dst_refs.at((iteration+1)%dst_refs.size());
OpenGLTextureCache::ReferencePtr destination_tex = dst_refs.at(iteration%dst_refs.size());
// Set up OpenGL parameters as necessary
functions_->glViewport(0, 0, params.effective_width(), params.effective_height());
// Bind all textures
for (int i=0; i<textures_to_bind.size(); i++) {
functions_->glActiveTexture(GL_TEXTURE0 + i);
functions_->glBindTexture(GL_TEXTURE_2D, textures_to_bind.at(i));
OpenGLRenderFunctions::PrepareToDraw(functions_);
}
for (int iteration=0; iteration<real_iteration_count; iteration++) {
// Set iteration number
shader->bind();
shader->setUniformValue("ove_iteration", iteration);
shader->release();
if (iteration > 0) {
// Replace iterative input
if (iteration == 0) {
output_tex = dst_refs[0];
} else {
input_tex = dst_refs[(iteration+1)%2];
output_tex = dst_refs[iteration%2];
functions_->glActiveTexture(GL_TEXTURE0 + iterative_input);
functions_->glBindTexture(GL_TEXTURE_2D, source_tex->texture()->texture());
functions_->glBindTexture(GL_TEXTURE_2D, input_tex->texture()->texture());
OpenGLRenderFunctions::PrepareToDraw(functions_);
}
buffer_.Attach(destination_tex->texture(), true);
buffer_.Attach(output_tex->texture());
buffer_.Bind();
// Blit this texture through this shader
@@ -409,22 +457,14 @@ QVariant OpenGLProxy::RunNodeAccelerated(const Node *node,
buffer_.Release();
buffer_.Detach();
// Update output reference to the last texture we wrote to
output_tex = destination_tex;
}
// Release any textures we bound before
while (input_texture_count > 0) {
input_texture_count--;
// Release texture here
functions_->glActiveTexture(GL_TEXTURE0 + input_texture_count);
for (int i=textures_to_bind.size()-1; i>=0; i--) {
functions_->glActiveTexture(GL_TEXTURE0 + i);
functions_->glBindTexture(GL_TEXTURE_2D, 0);
}
shader->release();
return QVariant::fromValue(output_tex);
}
+5 -3
View File
@@ -28,8 +28,8 @@
#include "node/value.h"
#include "openglcolorprocessor.h"
#include "openglframebuffer.h"
#include "openglshadercache.h"
#include "opengltexturecache.h"
#include "render/shaderinfo.h"
OLIVE_NAMESPACE_ENTER
@@ -69,7 +69,7 @@ public:
public slots:
QVariant RunNodeAccelerated(const OLIVE_NAMESPACE::Node *node,
const OLIVE_NAMESPACE::TimeRange &range,
OLIVE_NAMESPACE::NodeValueDatabase &input_params,
const OLIVE_NAMESPACE::ShaderJob &job,
const OLIVE_NAMESPACE::VideoParams &params);
void TextureToBuffer(const QVariant& texture,
@@ -84,6 +84,8 @@ public slots:
QVariant PreCachedFrameToValue(OLIVE_NAMESPACE::FramePtr frame);
private:
OpenGLShaderPtr ResolveShaderFromCache(const Node* node, const QByteArray& shader_id);
QOpenGLContext* ctx_;
QOffscreenSurface surface_;
@@ -95,7 +97,7 @@ private:
OpenGLShaderPtr copy_pipeline_;
OpenGLShaderCache shader_cache_;
QHash<QByteArray, OpenGLShaderPtr> shader_cache_;
OpenGLTextureCache texture_cache_;
+7 -2
View File
@@ -67,7 +67,7 @@ QVariant OpenGLWorker::CachedFrameToTexture(FramePtr frame) const
return value;
}
QVariant OpenGLWorker::ProcessShader(const Node *node, const TimeRange &range, NodeValueDatabase &input_params)
QVariant OpenGLWorker::ProcessShader(const Node *node, const TimeRange &range, const ShaderJob &job)
{
QVariant value;
@@ -77,10 +77,15 @@ QVariant OpenGLWorker::ProcessShader(const Node *node, const TimeRange &range, N
Q_RETURN_ARG(QVariant, value),
OLIVE_NS_CONST_ARG(Node*, node),
OLIVE_NS_CONST_ARG(TimeRange&, range),
OLIVE_NS_ARG(NodeValueDatabase&, input_params),
OLIVE_NS_CONST_ARG(ShaderJob&, job),
OLIVE_NS_CONST_ARG(VideoParams&, video_params()));
return value;
}
bool OpenGLWorker::TextureHasAlpha(const QVariant &v) const
{
return PixelFormat::FormatHasAlphaChannel(v.value<OpenGLTextureCache::ReferencePtr>()->texture()->format());
}
OLIVE_NAMESPACE_EXIT
+3 -1
View File
@@ -38,7 +38,9 @@ protected:
virtual QVariant CachedFrameToTexture(FramePtr frame) const override;
virtual QVariant ProcessShader(const Node *node, const TimeRange &range, NodeValueDatabase &input_params) override;
virtual QVariant ProcessShader(const Node *node, const TimeRange &range, const ShaderJob& job) override;
virtual bool TextureHasAlpha(const QVariant& v) const override;
private:
OpenGLProxy* proxy_;
+114 -151
View File
@@ -47,8 +47,19 @@ void RenderWorker::RenderFrame(RenderTicketPtr ticket, ViewerOutput* viewer, con
QVariant texture = table.Get(NodeParam::kTexture);
PixelFormat::Format output_format;
if (!texture.isNull() && TextureHasAlpha(texture)) {
output_format = PixelFormat::GetFormatWithAlphaChannel(video_params_.format());
} else {
output_format = PixelFormat::GetFormatWithoutAlphaChannel(video_params_.format());
}
FramePtr frame = Frame::Create();
frame->set_video_params(video_params_);
frame->set_video_params(VideoParams(video_params_.width(),
video_params_.height(),
video_params_.time_base(),
output_format,
video_params_.divider()));
frame->set_timestamp(time);
frame->allocate();
@@ -172,10 +183,11 @@ NodeValueTable RenderWorker::GenerateBlockTable(const TrackOutput *track, const
}
}
QVariant RenderWorker::ProcessSamples(const Node *node, const TimeRange &range, NodeValueDatabase &input_params_in)
QVariant RenderWorker::ProcessSamples(const Node *node, const TimeRange &range, const SampleJob& job)
{
// Copy database so we can make some temporary modifications to it
NodeValueDatabase input_params = input_params_in;
// FIX THIS CODE:
return QVariant();
/*
NodeInput* sample_input = node->ProcessesSamplesFrom(input_params);
// Try to find the sample buffer in the table
@@ -225,134 +237,20 @@ QVariant RenderWorker::ProcessSamples(const Node *node, const TimeRange &range,
}
return QVariant::fromValue(output_buffer);
*/
}
void RenderWorker::ProcessNodeEvent(const Node *node, const TimeRange &range, NodeValueDatabase &input_params_in, NodeValueTable &output_params)
QVariant RenderWorker::GetCachedFrame(const Node* node, const rational& time)
{
// Convert footage to image/sample buffers
if (node->id() == QStringLiteral("org.olivevideoeditor.Olive.videoinput")) {
QByteArray hash = RenderBackend::HashNode(node, video_params(), range.in());
QByteArray hash = RenderBackend::HashNode(node, video_params(), time);
QString fn = FrameHashCache::CachePathName(hash);
if (QFileInfo::exists(fn)) {
FramePtr f = FrameHashCache::LoadCacheFrame(hash);
QVariant cached = CachedFrameToTexture(f);
if (!cached.isNull()) {
output_params.Push(NodeParam::kTexture, cached, node);
// No more to do here
return;
}
}
}
QList<NodeInput*> inputs = node->GetInputsIncludingArrays();
foreach (NodeInput* input, inputs) {
if (input->data_type() == NodeParam::kFootage) {
TimeRange input_time = node->InputTimeAdjustment(input, range);
StreamPtr stream = ResolveStreamFromInput(input);
if (stream) {
QVariant v = ProcessFootage(stream, input_time);
if (!v.isNull()) {
if (stream->type() == Stream::kVideo || stream->type() == Stream::kImage) {
output_params.Push(NodeParam::kTexture, v, node);
} else if (stream->type() == Stream::kAudio) {
output_params.Push(NodeParam::kSamples, v, node);
}
}
}
}
}
// Check if node has a shader
if (node->GetCapabilities(input_params_in) & Node::kShader) {
QVariant v = ProcessShader(node, range, input_params_in);
if (!v.isNull()) {
output_params.Push(NodeParam::kTexture, v, node);
}
}
// Check if node processes samples
if (node->GetCapabilities(input_params_in) & Node::kSampleProcessor) {
QVariant v = ProcessSamples(node, range, input_params_in);
if (!v.isNull()) {
output_params.Push(NodeParam::kSamples, v, node);
}
}
}
QVariant RenderWorker::GetDataFromStream(StreamPtr stream, const TimeRange &input_time)
{
DecoderPtr decoder = ResolveDecoderFromInput(stream);
if (decoder) {
if (stream->type() == Stream::kVideo || stream->type() == Stream::kImage) {
FramePtr frame = decoder->RetrieveVideo(input_time.in(),
video_params().divider());
if (frame) {
// Return a texture from the derived class
return FootageFrameToTexture(stream, frame);
}
} else if (stream->type() == Stream::kAudio) {
// See if we have a conformed version of this audio
if (!decoder->HasConformedVersion(audio_params())) {
// If not, check what audio mode we're in
if (audio_mode_is_preview_) {
// For preview, we report the conform is missing and finish the render without it
// temporarily. The backend that picks up this signal will recache this section once the
// conform is available.
emit AudioConformUnavailable(decoder->stream(),
audio_render_time_,
input_time.out(),
audio_params());
} else {
// For online rendering/export, it's a waste of time to render the audio until we have
// all we need, so we try to handle the conform ourselves
AudioStreamPtr as = std::static_pointer_cast<AudioStream>(stream);
// Check if any other threads are conforming this audio
if (as->try_start_conforming(audio_params())) {
// If not, conform it ourselves
decoder->ConformAudio(&IsCancelled(), audio_params());
} else {
// If another thread is conforming already, hackily try to wait until it's done.
do {
QThread::msleep(1000);
} while (!as->has_conformed_version(audio_params()) && !IsCancelled());
}
}
}
if (decoder->HasConformedVersion(audio_params())) {
SampleBufferPtr frame = decoder->RetrieveAudio(input_time.in(), input_time.length(),
audio_params());
if (frame) {
return QVariant::fromValue(frame);
}
}
return CachedFrameToTexture(f);
}
}
@@ -382,52 +280,117 @@ DecoderPtr RenderWorker::ResolveDecoderFromInput(StreamPtr stream)
return decoder;
}
QVariant RenderWorker::ProcessFootage(StreamPtr stream, const TimeRange &input_time)
QVariant RenderWorker::ProcessVideoFootage(StreamPtr stream, const rational &input_time)
{
if (stream->type() == Stream::kVideo || stream->type() == Stream::kImage) {
ImageStreamPtr video_stream = std::static_pointer_cast<ImageStream>(stream);
rational time_match = (stream->type() == Stream::kImage) ? rational() : input_time;
QString colorspace_match = video_stream->get_colorspace_match_string();
ImageStreamPtr video_stream = std::static_pointer_cast<ImageStream>(stream);
rational time_match = (stream->type() == Stream::kImage) ? rational() : input_time.in();
QString colorspace_match = video_stream->get_colorspace_match_string();
QVariant value;
bool found_cache = false;
QVariant value;
bool found_cache = false;
if (still_image_cache_.contains(stream.get())) {
const CachedStill& cs = still_image_cache_[stream.get()];
if (still_image_cache_.contains(stream.get())) {
const CachedStill& cs = still_image_cache_[stream.get()];
if (cs.colorspace == colorspace_match
&& cs.alpha_is_associated == video_stream->premultiplied_alpha()
&& cs.divider == video_params_.divider()
&& cs.time == time_match) {
value = cs.texture;
found_cache = true;
} else {
still_image_cache_.remove(stream.get());
}
}
if (cs.colorspace == colorspace_match
&& cs.alpha_is_associated == video_stream->premultiplied_alpha()
&& cs.divider == video_params_.divider()
&& cs.time == time_match) {
value = cs.texture;
found_cache = true;
if (!found_cache) {
DecoderPtr decoder = ResolveDecoderFromInput(stream);
if (decoder) {
FramePtr frame = decoder->RetrieveVideo(input_time,
video_params().divider());
if (frame) {
// Return a texture from the derived class
value = FootageFrameToTexture(stream, frame);
if (value.isNull()) {
qDebug() << "Texture from derivative was blank";
} else {
// Put this into the image cache instead
still_image_cache_.insert(stream.get(), {value,
colorspace_match,
video_stream->premultiplied_alpha(),
video_params_.divider(),
time_match});
}
} else {
still_image_cache_.remove(stream.get());
qDebug() << "Frame from decoder was blank";
}
}
if (!found_cache) {
}
value = GetDataFromStream(stream, input_time);
return value;
}
still_image_cache_.insert(stream.get(), {value,
colorspace_match,
video_stream->premultiplied_alpha(),
video_params_.divider(),
time_match});
QVariant RenderWorker::ProcessAudioFootage(StreamPtr stream, const TimeRange &input_time)
{
QVariant value;
DecoderPtr decoder = ResolveDecoderFromInput(stream);
if (decoder) {
// See if we have a conformed version of this audio
if (!decoder->HasConformedVersion(audio_params())) {
// If not, check what audio mode we're in
if (audio_mode_is_preview_) {
// For preview, we report the conform is missing and finish the render without it
// temporarily. The backend that picks up this signal will recache this section once the
// conform is available.
emit AudioConformUnavailable(decoder->stream(),
audio_render_time_,
input_time.out(),
audio_params());
} else {
// For online rendering/export, it's a waste of time to render the audio until we have
// all we need, so we try to handle the conform ourselves
AudioStreamPtr as = std::static_pointer_cast<AudioStream>(stream);
// Check if any other threads are conforming this audio
if (as->try_start_conforming(audio_params())) {
// If not, conform it ourselves
decoder->ConformAudio(&IsCancelled(), audio_params());
} else {
// If another thread is conforming already, hackily try to wait until it's done.
do {
QThread::msleep(1000);
} while (!as->has_conformed_version(audio_params()) && !IsCancelled());
}
}
}
return value;
} else if (stream->type() == Stream::kAudio) {
return GetDataFromStream(stream, input_time);
if (decoder->HasConformedVersion(audio_params())) {
SampleBufferPtr frame = decoder->RetrieveAudio(input_time.in(), input_time.length(),
audio_params());
if (frame) {
value = QVariant::fromValue(frame);
}
}
}
return QVariant();
return value;
}
OLIVE_NAMESPACE_EXIT
+8 -8
View File
@@ -120,9 +120,15 @@ protected:
virtual NodeValueTable GenerateBlockTable(const TrackOutput *track, const TimeRange &range) override;
virtual void ProcessNodeEvent(const Node *node, const TimeRange &range, NodeValueDatabase &input_params_in, NodeValueTable &output_params) override;
virtual QVariant ProcessVideoFootage(StreamPtr stream, const rational &input_time) override;
virtual QVariant ProcessShader(const Node *node, const TimeRange &range, NodeValueDatabase &input_params) = 0;
virtual QVariant ProcessAudioFootage(StreamPtr stream, const TimeRange &input_time) override;
virtual QVariant ProcessSamples(const Node *node, const TimeRange &range, const SampleJob &job) override;
virtual QVariant GetCachedFrame(const Node *node, const rational &time) override;
virtual bool TextureHasAlpha(const QVariant& v) const = 0;
const VideoParams& video_params() const
{
@@ -148,14 +154,8 @@ signals:
void WaveformGenerated(OLIVE_NAMESPACE::TrackOutput* track, OLIVE_NAMESPACE::AudioVisualWaveform samples, OLIVE_NAMESPACE::TimeRange start);
private:
QVariant ProcessSamples(const Node *node, const TimeRange &range, NodeValueDatabase &input_params_in);
QVariant GetDataFromStream(StreamPtr stream, const TimeRange& input_time);
DecoderPtr ResolveDecoderFromInput(StreamPtr stream);
QVariant ProcessFootage(StreamPtr stream, const TimeRange &input_time);
RenderBackend* parent_;
VideoParams video_params_;
+8 -4
View File
@@ -338,7 +338,9 @@ bool FrameHashCache::SaveCacheFrame(const QString &filename, char *data, const V
header.channels().insert("R", Imf::Channel(pix_type));
header.channels().insert("G", Imf::Channel(pix_type));
header.channels().insert("B", Imf::Channel(pix_type));
header.channels().insert("A", Imf::Channel(pix_type));
if (PixelFormat::FormatHasAlphaChannel(vparam.format())) {
header.channels().insert("A", Imf::Channel(pix_type));
}
header.compression() = Imf::DWAA_COMPRESSION;
header.insert("dwaCompressionLevel", Imf::FloatAttribute(200.0f));
@@ -347,14 +349,16 @@ bool FrameHashCache::SaveCacheFrame(const QString &filename, char *data, const V
int bpc = PixelFormat::BytesPerChannel(vparam.format());
size_t xs = kRGBAChannels * bpc;
size_t ys = vparam.effective_width() * kRGBAChannels * bpc;
size_t xs = PixelFormat::ChannelCount(vparam.format()) * bpc;
size_t ys = vparam.effective_width() * PixelFormat::ChannelCount(vparam.format()) * bpc;
Imf::FrameBuffer framebuffer;
framebuffer.insert("R", Imf::Slice(pix_type, data, xs, ys));
framebuffer.insert("G", Imf::Slice(pix_type, data + bpc, xs, ys));
framebuffer.insert("B", Imf::Slice(pix_type, data + 2*bpc, xs, ys));
framebuffer.insert("A", Imf::Slice(pix_type, data + 3*bpc, xs, ys));
if (PixelFormat::FormatHasAlphaChannel(vparam.format())) {
framebuffer.insert("A", Imf::Slice(pix_type, data + 3*bpc, xs, ys));
}
out.setFrameBuffer(framebuffer);
out.writePixels(vparam.effective_height());
+157
View File
@@ -0,0 +1,157 @@
#ifndef SHADERINFO_H
#define SHADERINFO_H
#include "node/input.h"
#include "node/inputarray.h"
#include "node/value.h"
OLIVE_NAMESPACE_ENTER
using NodeValueMap = QHash<NodeInput*, NodeValue>;
class AcceleratedJob {
public:
AcceleratedJob() = default;
NodeValue GetValue(NodeInput* input) const
{
return value_map_.value(input);
}
void InsertValue(NodeInput* input, NodeValueDatabase& value)
{
if (input->IsArray()) {
NodeInputArray* array = static_cast<NodeInputArray*>(input);
QVector<NodeValue> values(array->GetSize());
for (int j=0;j<array->GetSize();j++) {
NodeInput* subparam = array->At(j);
values[j] = value[subparam].TakeWithMeta(subparam->data_type());
}
value_map_.insert(input, NodeValue(NodeParam::kVec2, QVariant::fromValue(values), input->parentNode()));
} else {
value_map_.insert(input, value[input].TakeWithMeta(input->data_type()));
}
}
void InsertValue(NodeInput* input, const NodeValue& value)
{
value_map_.insert(input, value);
}
const NodeValueMap &GetValues() const
{
return value_map_;
}
private:
NodeValueMap value_map_;
};
class SampleJob : public AcceleratedJob {
public:
SampleJob(NodeInput* from = nullptr) :
from_(from)
{
}
NodeInput* from() const
{
return from_;
}
private:
NodeInput* from_;
};
class ShaderJob : public AcceleratedJob {
public:
ShaderJob()
{
iterations_ = 1;
iterative_input_ = nullptr;
alpha_channel_required_ = false;
}
const QByteArray& GetShaderID() const
{
return id_;
}
void SetShaderID(const QByteArray& id)
{
id_ = id;
}
void SetIterations(int iterations, NodeInput* iterative_input)
{
iterations_ = iterations;
iterative_input_ = iterative_input;
}
int GetIterationCount() const
{
return iterations_;
}
NodeInput* GetIterativeInput() const
{
return iterative_input_;
}
bool GetAlphaChannelRequired() const
{
return alpha_channel_required_;
}
void SetAlphaChannelRequired(bool e)
{
alpha_channel_required_ = e;
}
private:
QByteArray id_;
int iterations_;
NodeInput* iterative_input_;
bool alpha_channel_required_;
};
class ShaderCode {
public:
ShaderCode(const QString& frag_code, const QString& vert_code) :
frag_code_(frag_code),
vert_code_(vert_code)
{
}
const QString& frag_code() const
{
return frag_code_;
}
const QString& vert_code() const
{
return vert_code_;
}
private:
QString frag_code_;
QString vert_code_;
};
OLIVE_NAMESPACE_EXIT
Q_DECLARE_METATYPE(OLIVE_NAMESPACE::ShaderJob)
Q_DECLARE_METATYPE(OLIVE_NAMESPACE::SampleJob)
#endif // SHADERINFO_H
+36 -5
View File
@@ -21,6 +21,11 @@ out vec4 fragColor;
#define METHOD_BOX_BLUR 0
#define METHOD_GAUSSIAN_BLUR 1
// Mode
#define MODE_NONE 0
#define MODE_HORIZONTAL 1
#define MODE_VERTICAL 2
// Single gaussian formula (unused, mainly here for documentation/just in case)
//float gaussian(float x, float sigma) {
// return (1.0/(sigma*sqrt(2.0*M_PI)))*exp(-0.5*pow(x/sigma, 2.0));
@@ -32,10 +37,36 @@ float gaussian2(float x, float y, float sigma) {
return (1.0/((sigma*sigma)*2.0*M_PI))*exp(-0.5*(((x*x) + (y*y))/(sigma*sigma)));
}
int determine_mode() {
if (radius_in == 0.0) {
return MODE_NONE;
}
if (!horiz_in && !vert_in) {
return MODE_NONE;
}
if (horiz_in && !vert_in) {
return MODE_HORIZONTAL;
}
if (vert_in && !horiz_in) {
return MODE_VERTICAL;
}
if (ove_iteration == 0) {
return MODE_HORIZONTAL;
}
if (ove_iteration == 1) {
return MODE_VERTICAL;
}
}
void main(void) {
if (radius_in == 0.0
|| (ove_iteration == 0 && !horiz_in)
|| (ove_iteration == 1 && !vert_in)) {
int mode = determine_mode();
if (mode == MODE_NONE) {
fragColor = texture(tex_in, ove_texcoord);
return;
}
@@ -77,9 +108,9 @@ void main(void) {
}
vec2 pixel_coord = ove_texcoord;
if (ove_iteration == 0) {
if (mode == MODE_HORIZONTAL) {
pixel_coord.x += i / ove_resolution.x;
} else if (ove_iteration == 1) {
} else if (mode == MODE_VERTICAL) {
pixel_coord.y += i / ove_resolution.y;
}
@@ -88,6 +88,8 @@ void NodeParamViewWidgetBridge::CreateWidgets()
case NodeParam::kString:
case NodeParam::kBuffer:
case NodeParam::kVector:
case NodeParam::kShaderJob:
case NodeParam::kSampleJob:
break;
case NodeParam::kInt:
{
@@ -262,6 +264,8 @@ void NodeParamViewWidgetBridge::WidgetCallback()
case NodeParam::kString:
case NodeParam::kVector:
case NodeParam::kBuffer:
case NodeParam::kShaderJob:
case NodeParam::kSampleJob:
break;
case NodeParam::kInt:
{
@@ -392,6 +396,8 @@ void NodeParamViewWidgetBridge::UpdateWidgetValues()
case NodeParam::kNumber:
case NodeParam::kString:
case NodeParam::kBuffer:
case NodeParam::kShaderJob:
case NodeParam::kSampleJob:
case NodeParam::kVector:
break;
case NodeParam::kInt:
+13 -17
View File
@@ -22,26 +22,22 @@
OLIVE_NAMESPACE_ENTER
void GizmoTraverser::ProcessNodeEvent(const Node *node, const TimeRange &range, NodeValueDatabase &input_params_in, NodeValueTable &output_params)
QVariant GizmoTraverser::ProcessVideoFootage(StreamPtr stream, const rational &input_time)
{
// Convert footage to image/sample buffers
QList<NodeInput*> inputs = node->GetInputsIncludingArrays();
foreach (NodeInput* input, inputs) {
if (input->data_type() == NodeParam::kFootage) {
StreamPtr stream = ResolveStreamFromInput(input);
Q_UNUSED(input_time)
if (stream
&& (stream->type() == Stream::kVideo || stream->type() == Stream::kImage)) {
ImageStreamPtr image_stream = std::static_pointer_cast<ImageStream>(stream);
ImageStreamPtr image_stream = std::static_pointer_cast<ImageStream>(stream);
output_params.Push(NodeParam::kTexture,
QSize(image_stream->width(), image_stream->height()),
node);
} else if (stream->type() == Stream::kAudio) {
// FIXME: Do something...
}
}
}
return QSize(image_stream->width(), image_stream->height());
}
QVariant GizmoTraverser::ProcessShader(const Node *node, const TimeRange &range, const ShaderJob &job)
{
Q_UNUSED(node)
Q_UNUSED(range)
Q_UNUSED(job)
return size_;
}
OLIVE_NAMESPACE_EXIT
+12 -2
View File
@@ -28,10 +28,20 @@ OLIVE_NAMESPACE_ENTER
class GizmoTraverser : public NodeTraverser
{
public:
GizmoTraverser() = default;
GizmoTraverser(const QSize& sequence_resolution) :
size_(sequence_resolution)
{
}
protected:
virtual void ProcessNodeEvent(const Node *node, const TimeRange &range, NodeValueDatabase &input_params_in, NodeValueTable &output_params) override;
virtual QVariant ProcessVideoFootage(StreamPtr stream, const rational &input_time);
virtual QVariant ProcessShader(const Node *node, const TimeRange &range, const ShaderJob& job);
// FIXME: Do something about audio?
private:
QSize size_;
};
+1 -1
View File
@@ -241,7 +241,7 @@ void ViewerDisplayWidget::paintGL()
// Draw gizmos if we have any
if (gizmos_) {
GizmoTraverser gt;
GizmoTraverser gt(QSize(gizmo_params_.width(), gizmo_params_.height()));
rational node_time = GetGizmoTime();