nodes: further optimized merge node to passthrough RGB blend textures

This commit is contained in:
itsmattkc
2021-04-23 11:01:08 +10:00
parent 5e3d42e52a
commit 445d276cd0
18 changed files with 227 additions and 82 deletions
@@ -63,7 +63,7 @@ void CrossDissolveTransition::ShaderJobEvent(NodeValueDatabase &value, ShaderJob
{
Q_UNUSED(value)
job.SetAlphaChannelRequired(true);
job.SetAlphaChannelRequired(GenerateJob::kAlphaForceOn);
}
void CrossDissolveTransition::SampleJobEvent(SampleBufferPtr from_samples, SampleBufferPtr to_samples, SampleBufferPtr out_samples, double time_in) const
+1 -1
View File
@@ -68,7 +68,7 @@ NodeValueTable CropDistortNode::Value(const QString &output, NodeValueDatabase &
job.InsertValue(this, kFeatherInput, value);
job.InsertValue(QStringLiteral("resolution_in"),
NodeValue(NodeValue::kVec2, value[QStringLiteral("global")].Get(NodeValue::kVec2, QStringLiteral("resolution")), this));
job.SetAlphaChannelRequired(true);
job.SetAlphaChannelRequired(GenerateJob::kAlphaForceOn);
NodeValueTable table = value.Merge();
@@ -62,10 +62,11 @@ NodeValueTable TransformDistortNode::Value(const QString &output, NodeValueDatab
QMatrix4x4 generated_matrix = GenerateMatrix(value, true, false, false, false);
// Pop texture
TexturePtr texture = value[kTextureInput].Take(NodeValue::kTexture).value<TexturePtr>();
NodeValue texture_meta = value[kTextureInput].TakeWithMeta(NodeValue::kTexture);
TexturePtr texture = texture_meta.data().value<TexturePtr>();
// Merge table
NodeValueTable table = value.Merge();
NodeValueTable table = value[kTextureInput];
// If we have a texture, generate a matrix and make it happen
if (texture) {
@@ -74,7 +75,7 @@ NodeValueTable TransformDistortNode::Value(const QString &output, NodeValueDatab
if (real_matrix.isIdentity()) {
// We don't expect any changes, just push as normal
table.Push(NodeValue::kTexture, QVariant::fromValue(texture), this);
table.Push(texture_meta);
} else {
// The matrix will transform things
ShaderJob job;
@@ -84,7 +85,7 @@ NodeValueTable TransformDistortNode::Value(const QString &output, NodeValueDatab
// FIXME: This should be optimized, we can use matrix math to determine if this operation will
// end up with gaps in the screen that will require an alpha channel.
job.SetAlphaChannelRequired(true);
job.SetAlphaChannelRequired(GenerateJob::kAlphaForceOn);
table.Push(NodeValue::kShaderJob, QVariant::fromValue(job), this);
}
@@ -103,6 +104,11 @@ ShaderCode TransformDistortNode::GetShaderCode(const QString &shader_id) const
bool TransformDistortNode::GizmoPress(NodeValueDatabase &db, const QPointF &p)
{
TexturePtr tex = db[kTextureInput].Get(NodeValue::kTexture).value<TexturePtr>();
if (!tex) {
return false;
}
// Store cursor position
gizmo_drag_pos_ = p;
@@ -137,7 +143,7 @@ bool TransformDistortNode::GizmoPress(NodeValueDatabase &db, const QPointF &p)
}
// Store texture size
VideoParams texture_params = db[kTextureInput].Get(NodeValue::kTexture).value<VideoParams>();
VideoParams texture_params = tex->params();
QVector2D texture_sz(texture_params.square_pixel_width(), texture_params.height());
gizmo_scale_anchor_ = db[kAnchorInput].Get(NodeValue::kVec2).value<QVector2D>() + texture_sz/2;
@@ -310,9 +316,9 @@ void TransformDistortNode::GizmoRelease()
void TransformDistortNode::Hash(const QString &output, QCryptographicHash &hash, const rational &time, const VideoParams &video_params) const
{
// If not connected to output, this will produce nothing
NodeOutput out = GetConnectedOutput(kTextureInput);
if (!out.IsValid()) {
// No texture connected, this node will produce nothing
return;
}
@@ -321,18 +327,17 @@ void TransformDistortNode::Hash(const QString &output, QCryptographicHash &hash,
traverser.SetCacheVideoParams(video_params);
NodeValueDatabase db = traverser.GenerateDatabase(this, output, TimeRange(time, time + video_params.frame_rate_as_time_base()));
VideoParams tex_params = db[kTextureInput].Get(NodeValue::kTexture).value<VideoParams>();
QMatrix4x4 matrix = GenerateMatrix(db, true, false, false, false);
matrix = GenerateAutoScaledMatrix(matrix, db, tex_params);
TexturePtr tex = db[kTextureInput].Get(NodeValue::kTexture).value<TexturePtr>();
if (tex) {
VideoParams tex_params = tex->params();
QMatrix4x4 matrix = GenerateMatrix(db, true, false, false, false);
matrix = GenerateAutoScaledMatrix(matrix, db, tex_params);
if (matrix.isIdentity()) {
qDebug() << "Detected identity matrix, skipping hashing";
} else {
qDebug() << "Detected NON-IDENTITY, hashing...";
// Add fingerprint
hash.addData(id().toUtf8());
hash.addData(reinterpret_cast<const char*>(&matrix), sizeof(matrix));
if (!matrix.isIdentity()) {
// Add fingerprint
hash.addData(id().toUtf8());
hash.addData(reinterpret_cast<const char*>(&matrix), sizeof(matrix));
}
}
out.node()->Hash(out.output(), hash, time, video_params);
@@ -394,8 +399,6 @@ QMatrix4x4 TransformDistortNode::GenerateAutoScaledMatrix(const QMatrix4x4& gene
QVector2D texture_res(texture_params.square_pixel_width(), texture_params.height());
AutoScaleType autoscale = static_cast<AutoScaleType>(value[kAutoscaleInput].Get(NodeValue::kCombo).toInt());
qDebug() << "Doing transform with" << texture_params.square_pixel_width() << "x" << texture_params.height() << "vs" << sequence_res.x() << "x" << sequence_res.y();
return AdjustMatrixByResolutions(generated_matrix,
sequence_res,
texture_res,
@@ -404,6 +407,11 @@ QMatrix4x4 TransformDistortNode::GenerateAutoScaledMatrix(const QMatrix4x4& gene
void TransformDistortNode::DrawGizmos(NodeValueDatabase &db, QPainter *p)
{
TexturePtr tex = db[kTextureInput].Get(NodeValue::kTexture).value<TexturePtr>();
if (!tex) {
return;
}
// 0 pen width is always 1px wide despite any transform
p->setPen(QPen(Qt::white, 0));
@@ -413,7 +421,7 @@ void TransformDistortNode::DrawGizmos(NodeValueDatabase &db, QPainter *p)
QPointF sequence_half_res_pt = sequence_half_res.toPointF();
// GizmoTraverser just returns the sizes of the textures and no other data
VideoParams tex_params = db[kTextureInput].Get(NodeValue::kTexture).value<VideoParams>();
VideoParams tex_params = tex->params();
QVector2D tex_sz(tex_params.square_pixel_width(), tex_params.height());
// Retrieve autoscale value
+1 -1
View File
@@ -118,7 +118,7 @@ NodeValueTable BlurFilterNode::Value(const QString &output, NodeValueDatabase &v
// If we're not repeating pixels, expect an alpha channel to appear
if (!job.GetValue(kRepeatEdgePixelsInput).data().toBool()) {
job.SetAlphaChannelRequired(true);
job.SetAlphaChannelRequired(GenerateJob::kAlphaForceOn);
}
table.Push(NodeValue::kShaderJob, QVariant::fromValue(job), this);
+1 -1
View File
@@ -95,7 +95,7 @@ NodeValueTable PolygonGenerator::Value(const QString &output, NodeValueDatabase
job.InsertValue(this, kPointsInput, value);
job.InsertValue(this, kColorInput, value);
job.InsertValue(QStringLiteral("resolution_in"), value[QStringLiteral("global")].GetWithMeta(NodeValue::kVec2, QStringLiteral("resolution")));
job.SetAlphaChannelRequired(true);
job.SetAlphaChannelRequired(GenerateJob::kAlphaForceOn);
NodeValueTable table = value.Merge();
table.Push(NodeValue::kShaderJob, QVariant::fromValue(job), this);
+1 -1
View File
@@ -95,7 +95,7 @@ NodeValueTable TextGenerator::Value(const QString &output, NodeValueDatabase &va
job.InsertValue(this, kVAlignInput, value);
job.InsertValue(this, kFontInput, value);
job.InsertValue(this, kFontSizeInput, value);
job.SetAlphaChannelRequired(true);
job.SetAlphaChannelRequired(GenerateJob::kAlphaForceOn);
NodeValueTable table = value.Merge();
+1 -1
View File
@@ -324,7 +324,7 @@ NodeValueTable MathNodeBase::ValueInternal(NodeValueDatabase &value, Operation o
NodeValue(NodeValue::kMatrix, adjusted_matrix, this));
// It's likely an alpha channel will result from this operation
job.SetAlphaChannelRequired(true);
job.SetAlphaChannelRequired(GenerateJob::kAlphaForceOn);
}
}
+28 -24
View File
@@ -20,6 +20,8 @@
#include "merge.h"
#include "node/traverser.h"
namespace olive {
const QString MergeNode::kBaseIn = QStringLiteral("base_in");
@@ -93,6 +95,11 @@ NodeValueTable MergeNode::Value(const QString &output, NodeValueDatabase &value)
table.Push(job.GetValue(kBaseIn));
} else {
// We have both textures, push the job
if (base_tex->channel_count() < VideoParams::kRGBAChannelCount) {
// Base has no alpha, therefore this merge operation will not add an alpha channel
job.SetAlphaChannelRequired(GenerateJob::kAlphaForceOff);
}
table.Push(NodeValue::kShaderJob, QVariant::fromValue(job), this);
}
}
@@ -102,37 +109,34 @@ NodeValueTable MergeNode::Value(const QString &output, NodeValueDatabase &value)
void MergeNode::Hash(const QString &output, QCryptographicHash &hash, const rational &time, const VideoParams &video_params) const
{
// We do some hash optimization here. If only one of the inputs is connected, this node
// functions as a passthrough so there's no alteration to the hash. The same is true if the
// connected node happens to return nothing (a gap for instance). Therefore we only add our
// fingerprint if the base AND the blend change the hash. Otherwise, we assume it's a passthrough.
NodeTraverser traverser;
traverser.SetCacheVideoParams(video_params);
Q_UNUSED(output)
NodeValueDatabase db = traverser.GenerateDatabase(this, output, TimeRange(time, time+video_params.frame_rate_as_time_base()));
QByteArray current_result = hash.result();
TexturePtr base_tex = db[kBaseIn].Get(NodeValue::kTexture).value<TexturePtr>();
TexturePtr blend_tex = db[kBlendIn].Get(NodeValue::kTexture).value<TexturePtr>();
bool base_changed_hash = false;
bool blend_changed_hash = false;
if (base_tex || blend_tex) {
bool passthrough_base = !blend_tex;
bool passthrough_blend = !base_tex || (blend_tex && blend_tex->channel_count() < VideoParams::kRGBAChannelCount);
if (IsInputConnected(kBaseIn)) {
NodeOutput base_output = GetConnectedOutput(kBaseIn);
base_output.node()->Hash(base_output.output(), hash, time, video_params);
if (!passthrough_base && !passthrough_blend) {
// This merge will actually do something so we add a fingerprint
hash.addData(id().toUtf8());
}
QByteArray post_base_hash = hash.result();
base_changed_hash = (post_base_hash != current_result);
current_result = post_base_hash;
}
if (!passthrough_base) {
NodeOutput blend_output = GetConnectedOutput(kBlendIn);
blend_output.node()->Hash(blend_output.output(), hash, time, video_params);
}
if(IsInputConnected(kBlendIn)) {
NodeOutput blend_output = GetConnectedOutput(kBlendIn);
blend_output.node()->Hash(blend_output.output(), hash, time, video_params);
if (!passthrough_blend) {
NodeOutput base_output = GetConnectedOutput(kBaseIn);
base_output.node()->Hash(base_output.output(), hash, time, video_params);
}
blend_changed_hash = (hash.result() != current_result);
}
if (base_changed_hash && blend_changed_hash) {
// Something changed, so we'll add our fingerprint
hash.addData(id().toUtf8());
Q_ASSERT(!passthrough_base || !passthrough_blend);
}
}
+36 -3
View File
@@ -45,6 +45,32 @@ NodeValueDatabase NodeTraverser::GenerateDatabase(const Node* node, const QStrin
return database;
}
int NodeTraverser::GetChannelCountFromJob(const GenerateJob &job)
{
switch (job.GetAlphaChannelRequired()) {
case GenerateJob::kAlphaForceOn:
return VideoParams::kRGBAChannelCount;
case GenerateJob::kAlphaForceOff:
return VideoParams::kRGBChannelCount;
case GenerateJob::kAlphaAuto:
for (auto it=job.GetValues().cbegin(); it!=job.GetValues().cend(); it++) {
if (it.value().type() == NodeValue::kTexture) {
TexturePtr tex = it.value().data().value<TexturePtr>();
if (tex && tex->channel_count() == VideoParams::kRGBAChannelCount) {
// An input texture has an alpha channel so assume we need one too
return VideoParams::kRGBAChannelCount;
}
}
}
// No textures had alpha so assume we don't need one
return VideoParams::kRGBChannelCount;
}
// Default fallback, should never get here
return VideoParams::kRGBAChannelCount;
}
NodeValueTable NodeTraverser::ProcessInput(const Node* node, const QString& input, const TimeRange& range)
{
// If input is connected, retrieve value directly
@@ -134,7 +160,8 @@ QVariant NodeTraverser::ProcessVideoFootage(const FootageJob &stream, const rati
{
Q_UNUSED(input_time)
return QVariant::fromValue(stream.video_params());
// Create dummy texture with footage params
return QVariant::fromValue(std::make_shared<Texture>(stream.video_params()));
}
QVariant NodeTraverser::ProcessAudioFootage(const FootageJob& stream, const TimeRange &input_time)
@@ -151,7 +178,10 @@ QVariant NodeTraverser::ProcessShader(const Node *node, const TimeRange &range,
Q_UNUSED(range)
Q_UNUSED(job)
return QVariant::fromValue(video_params_);
// Create dummy texture with sequence params
VideoParams tex_params = video_params_;
tex_params.set_channel_count(GetChannelCountFromJob(job));
return QVariant::fromValue(std::make_shared<Texture>(tex_params));
}
QVariant NodeTraverser::ProcessSamples(const Node *node, const TimeRange &range, const SampleJob &job)
@@ -168,7 +198,10 @@ QVariant NodeTraverser::ProcessFrameGeneration(const Node *node, const GenerateJ
Q_UNUSED(node)
Q_UNUSED(job)
return QVariant::fromValue(video_params_);
// Create dummy texture with sequence params
VideoParams tex_params = video_params_;
tex_params.set_channel_count(GetChannelCountFromJob(job));
return QVariant::fromValue(std::make_shared<Texture>(tex_params));
}
void NodeTraverser::SaveCachedTexture(const QByteArray &hash, const QVariant &texture)
+2
View File
@@ -54,6 +54,8 @@ public:
video_params_ = params;
}
static int GetChannelCountFromJob(const GenerateJob& job);
protected:
NodeValueTable ProcessInput(const Node *node, const QString &input, const TimeRange &range);