diff --git a/app/node/block/block.cpp b/app/node/block/block.cpp index 21ea6470b..47fecec22 100644 --- a/app/node/block/block.cpp +++ b/app/node/block/block.cpp @@ -230,7 +230,7 @@ void Block::Retranslate() SetInputName(kReverseInput, tr("Reverse")); } -void Block::Hash(const QString &, QCryptographicHash &, const rational &) const +void Block::Hash(const QString &, QCryptographicHash &, const rational &, const VideoParams &) const { // A block does nothing by default, so we hash nothing } diff --git a/app/node/block/block.h b/app/node/block/block.h index 5901e6768..f8c6ab8ed 100644 --- a/app/node/block/block.h +++ b/app/node/block/block.h @@ -153,7 +153,7 @@ public: return GetStandardValue(kReverseInput).toBool(); } - virtual void Hash(const QString& output, QCryptographicHash &hash, const rational &time) const override; + virtual void Hash(const QString& output, QCryptographicHash &hash, const rational &time, const VideoParams& video_params) const override; static const QString kLengthInput; static const QString kMediaInInput; diff --git a/app/node/block/clip/clip.cpp b/app/node/block/clip/clip.cpp index 05dad2efd..a6e2459d7 100644 --- a/app/node/block/clip/clip.cpp +++ b/app/node/block/clip/clip.cpp @@ -109,7 +109,7 @@ void ClipBlock::Retranslate() SetInputName(kBufferIn, tr("Buffer")); } -void ClipBlock::Hash(const QString &out, QCryptographicHash &hash, const rational &time) const +void ClipBlock::Hash(const QString &out, QCryptographicHash &hash, const rational &time, const VideoParams &video_params) const { Q_UNUSED(out) @@ -117,7 +117,7 @@ void ClipBlock::Hash(const QString &out, QCryptographicHash &hash, const rationa rational t = InputTimeAdjustment(kBufferIn, -1, TimeRange(time, time)).in(); NodeOutput output = GetConnectedOutput(kBufferIn); - output.node()->Hash(output.output(), hash, t); + output.node()->Hash(output.output(), hash, t, video_params); } } diff --git a/app/node/block/clip/clip.h b/app/node/block/clip/clip.h index 051f22b37..770be01ea 100644 --- a/app/node/block/clip/clip.h +++ b/app/node/block/clip/clip.h @@ -52,7 +52,7 @@ public: virtual void Retranslate() override; - virtual void Hash(const QString& output, QCryptographicHash &hash, const rational &time) const override; + virtual void Hash(const QString& output, QCryptographicHash &hash, const rational &time, const VideoParams& video_params) const override; static const QString kBufferIn; diff --git a/app/node/block/transition/transition.cpp b/app/node/block/transition/transition.cpp index f503d2cb8..cc77b0dd8 100644 --- a/app/node/block/transition/transition.cpp +++ b/app/node/block/transition/transition.cpp @@ -116,9 +116,9 @@ double TransitionBlock::GetInProgress(const double &time) const return clamp((GetInternalTransitionTime(time) - out_offset().toDouble()) / in_offset().toDouble(), 0.0, 1.0); } -void TransitionBlock::Hash(const QString &output, QCryptographicHash &hash, const rational &time) const +void TransitionBlock::Hash(const QString &output, QCryptographicHash &hash, const rational &time, const VideoParams &video_params) const { - Node::Hash(output, hash, time); + Node::Hash(output, hash, time, video_params); double time_dbl = time.toDouble(); double all_prog = GetTotalProgress(time_dbl); diff --git a/app/node/block/transition/transition.h b/app/node/block/transition/transition.h index 6ca621960..b81e741c1 100644 --- a/app/node/block/transition/transition.h +++ b/app/node/block/transition/transition.h @@ -45,7 +45,7 @@ public: double GetOutProgress(const double &time) const; double GetInProgress(const double &time) const; - virtual void Hash(const QString& output, QCryptographicHash& hash, const rational &time) const override; + virtual void Hash(const QString& output, QCryptographicHash& hash, const rational &time, const VideoParams& video_params) const override; virtual NodeValueTable Value(const QString& output, NodeValueDatabase &value) const override; diff --git a/app/node/distort/transform/transformdistortnode.cpp b/app/node/distort/transform/transformdistortnode.cpp index c03a352d5..02d7514c7 100644 --- a/app/node/distort/transform/transformdistortnode.cpp +++ b/app/node/distort/transform/transformdistortnode.cpp @@ -23,6 +23,7 @@ #include #include "common/range.h" +#include "node/traverser.h" namespace olive { @@ -30,6 +31,8 @@ const QString TransformDistortNode::kTextureInput = QStringLiteral("tex_in"); const QString TransformDistortNode::kAutoscaleInput = QStringLiteral("autoscale_in"); const QString TransformDistortNode::kInterpolationInput = QStringLiteral("interpolation_in"); +#define super Node + TransformDistortNode::TransformDistortNode() { AddInput(kAutoscaleInput, NodeValue::kCombo, 0); @@ -67,14 +70,7 @@ NodeValueTable TransformDistortNode::Value(const QString &output, NodeValueDatab // If we have a texture, generate a matrix and make it happen if (texture) { // Adjust our matrix by the resolutions involved - QVector2D sequence_res = value[QStringLiteral("global")].Get(NodeValue::kVec2, QStringLiteral("resolution")).value(); - QVector2D texture_res(texture->params().square_pixel_width(), texture->params().height()); - AutoScaleType autoscale = static_cast(value[kAutoscaleInput].Get(NodeValue::kCombo).toInt()); - - QMatrix4x4 real_matrix = AdjustMatrixByResolutions(generated_matrix, - sequence_res, - texture_res, - autoscale); + QMatrix4x4 real_matrix = GenerateAutoScaledMatrix(generated_matrix, value, texture->params()); if (real_matrix.isIdentity()) { // We don't expect any changes, just push as normal @@ -312,6 +308,36 @@ void TransformDistortNode::GizmoRelease() gizmo_drag_ = nullptr; } +void TransformDistortNode::Hash(const QString &output, QCryptographicHash &hash, const rational &time, const VideoParams &video_params) const +{ + NodeOutput out = GetConnectedOutput(kTextureInput); + if (!out.IsValid()) { + // No texture connected, this node will produce nothing + return; + } + + // Use a traverser to determine if the matrix is identity + NodeTraverser traverser; + 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(); + 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(&matrix), sizeof(matrix)); + } + + out.node()->Hash(out.output(), hash, time, video_params); +} + QMatrix4x4 TransformDistortNode::AdjustMatrixByResolutions(const QMatrix4x4 &mat, const QVector2D &sequence_res, const QVector2D &texture_res, AutoScaleType autoscale_type) { // First, create an identity matrix @@ -362,6 +388,20 @@ QPointF TransformDistortNode::CreateScalePoint(double x, double y, const QPointF return mat.map(QPointF(x, y)) + half_res; } +QMatrix4x4 TransformDistortNode::GenerateAutoScaledMatrix(const QMatrix4x4& generated_matrix, NodeValueDatabase& value, const VideoParams& texture_params) const +{ + QVector2D sequence_res = value[QStringLiteral("global")].Get(NodeValue::kVec2, QStringLiteral("resolution")).value(); + QVector2D texture_res(texture_params.square_pixel_width(), texture_params.height()); + AutoScaleType autoscale = static_cast(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, + autoscale); +} + void TransformDistortNode::DrawGizmos(NodeValueDatabase &db, QPainter *p) { // 0 pen width is always 1px wide despite any transform diff --git a/app/node/distort/transform/transformdistortnode.h b/app/node/distort/transform/transformdistortnode.h index 6d71e66eb..e9c2016dd 100644 --- a/app/node/distort/transform/transformdistortnode.h +++ b/app/node/distort/transform/transformdistortnode.h @@ -80,6 +80,8 @@ public: virtual void GizmoMove(const QPointF &p, const rational &time) override; virtual void GizmoRelease() override; + virtual void Hash(const QString& output, QCryptographicHash& hash, const rational &time, const VideoParams& video_params) const override; + enum AutoScaleType { kAutoScaleNone, kAutoScaleFit, @@ -99,6 +101,8 @@ public: private: static QPointF CreateScalePoint(double x, double y, const QPointF& half_res, const QMatrix4x4& mat); + QMatrix4x4 GenerateAutoScaledMatrix(const QMatrix4x4 &generated_matrix, NodeValueDatabase &db, const VideoParams &texture_params) const; + // Gizmo variables QString gizmo_drag_; QVector gizmo_start_; diff --git a/app/node/input/time/timeinput.cpp b/app/node/input/time/timeinput.cpp index 92844b7ce..3b974f1bb 100644 --- a/app/node/input/time/timeinput.cpp +++ b/app/node/input/time/timeinput.cpp @@ -66,9 +66,9 @@ NodeValueTable TimeInput::Value(const QString &output, NodeValueDatabase &value) return table; } -void TimeInput::Hash(const QString &output, QCryptographicHash &hash, const rational &time) const +void TimeInput::Hash(const QString &output, QCryptographicHash &hash, const rational &time, const VideoParams &video_params) const { - Node::Hash(output, hash, time); + Node::Hash(output, hash, time, video_params); // Make sure time is hashed hash.addData(NodeValue::ValueToBytes(NodeValue::kRational, QVariant::fromValue(time))); diff --git a/app/node/input/time/timeinput.h b/app/node/input/time/timeinput.h index f00c2dcd0..570acb9e2 100644 --- a/app/node/input/time/timeinput.h +++ b/app/node/input/time/timeinput.h @@ -42,7 +42,7 @@ public: virtual NodeValueTable Value(const QString& output, NodeValueDatabase& value) const override; - virtual void Hash(const QString& output, QCryptographicHash& hash, const rational& time) const override; + virtual void Hash(const QString& output, QCryptographicHash& hash, const rational& time, const VideoParams& video_params) const override; }; diff --git a/app/node/math/merge/merge.cpp b/app/node/math/merge/merge.cpp index c2078610b..c2a56c700 100644 --- a/app/node/math/merge/merge.cpp +++ b/app/node/math/merge/merge.cpp @@ -100,7 +100,7 @@ NodeValueTable MergeNode::Value(const QString &output, NodeValueDatabase &value) return table; } -void MergeNode::Hash(const QString &output, QCryptographicHash &hash, const rational &time) const +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 @@ -116,7 +116,7 @@ void MergeNode::Hash(const QString &output, QCryptographicHash &hash, const rati if (IsInputConnected(kBaseIn)) { NodeOutput base_output = GetConnectedOutput(kBaseIn); - base_output.node()->Hash(base_output.output(), hash, time); + base_output.node()->Hash(base_output.output(), hash, time, video_params); QByteArray post_base_hash = hash.result(); base_changed_hash = (post_base_hash != current_result); @@ -125,7 +125,7 @@ void MergeNode::Hash(const QString &output, QCryptographicHash &hash, const rati if(IsInputConnected(kBlendIn)) { NodeOutput blend_output = GetConnectedOutput(kBlendIn); - blend_output.node()->Hash(blend_output.output(), hash, time); + blend_output.node()->Hash(blend_output.output(), hash, time, video_params); blend_changed_hash = (hash.result() != current_result); } diff --git a/app/node/math/merge/merge.h b/app/node/math/merge/merge.h index 61ca481d1..0c9f9bd3a 100644 --- a/app/node/math/merge/merge.h +++ b/app/node/math/merge/merge.h @@ -48,7 +48,7 @@ public: static const QString kBaseIn; static const QString kBlendIn; - virtual void Hash(const QString& output, QCryptographicHash &hash, const rational &time) const override; + virtual void Hash(const QString& output, QCryptographicHash &hash, const rational &time, const VideoParams& video_params) const override; private: NodeInput* base_in_; diff --git a/app/node/node.cpp b/app/node/node.cpp index dc9b0e54e..a33ae58bd 100644 --- a/app/node/node.cpp +++ b/app/node/node.cpp @@ -1453,7 +1453,7 @@ void Node::SetLabel(const QString &s) } } -void Node::Hash(const QString &output, QCryptographicHash &hash, const rational& time) const +void Node::Hash(const QString &output, QCryptographicHash &hash, const rational& time, const VideoParams &video_params) const { Q_UNUSED(output) @@ -1470,7 +1470,7 @@ void Node::Hash(const QString &output, QCryptographicHash &hash, const rational& int arr_sz = InputArraySize(input); for (int i=-1; i Node::GetDependenciesInternal(bool traverse, bool exclusive_only return list; } -void Node::HashInputElement(QCryptographicHash &hash, const QString& input, int element, const rational &time) const +void Node::HashInputElement(QCryptographicHash &hash, const QString& input, int element, const rational &time, const VideoParams& video_params) const { // Get time adjustment // For a single frame, we only care about one of the times @@ -1603,7 +1603,7 @@ void Node::HashInputElement(QCryptographicHash &hash, const QString& input, int // Traverse down this edge NodeOutput output = GetConnectedOutput(input, element); - output.node()->Hash(output.output(), hash, input_time); + output.node()->Hash(output.output(), hash, input_time, video_params); } else { // Grab the value at this time QVariant value = GetValueAtTime(input, input_time, element); diff --git a/app/node/node.h b/app/node/node.h index 678d9f5c9..71e6e78cd 100644 --- a/app/node/node.h +++ b/app/node/node.h @@ -733,7 +733,7 @@ public: const QString& GetLabel() const; void SetLabel(const QString& s); - virtual void Hash(const QString& output, QCryptographicHash& hash, const rational &time) const; + virtual void Hash(const QString& output, QCryptographicHash& hash, const rational &time, const VideoParams& video_params) const; void InvalidateAll(const QString& input, int element = -1); @@ -1145,7 +1145,7 @@ private: QVector GetDependenciesInternal(bool traverse, bool exclusive_only) const; - void HashInputElement(QCryptographicHash& hash, const QString &input, int element, const rational& time) const; + void HashInputElement(QCryptographicHash& hash, const QString &input, int element, const rational& time, const VideoParams &video_params) const; void ParameterValueChanged(const QString &input, int element, const olive::TimeRange &range); void ParameterValueChanged(const NodeInput& input, const olive::TimeRange &range) diff --git a/app/node/output/track/track.cpp b/app/node/output/track/track.cpp index 55f269093..e908b0941 100644 --- a/app/node/output/track/track.cpp +++ b/app/node/output/track/track.cpp @@ -575,7 +575,7 @@ bool Track::IsLocked() const return locked_; } -void Track::Hash(const QString &output, QCryptographicHash &hash, const rational &time) const +void Track::Hash(const QString &output, QCryptographicHash &hash, const rational &time, const VideoParams &video_params) const { Q_UNUSED(output) @@ -583,7 +583,7 @@ void Track::Hash(const QString &output, QCryptographicHash &hash, const rational // Defer to block at this time, don't add any of our own information to the hash if (b) { - b->Hash(kDefaultOutput, hash, TransformTimeForBlock(b, time)); + b->Hash(kDefaultOutput, hash, TransformTimeForBlock(b, time), video_params); } } diff --git a/app/node/output/track/track.h b/app/node/output/track/track.h index 7179fd314..1f0a47144 100644 --- a/app/node/output/track/track.h +++ b/app/node/output/track/track.h @@ -338,7 +338,7 @@ public: bool IsLocked() const; - virtual void Hash(const QString& output, QCryptographicHash& hash, const rational &time) const override; + virtual void Hash(const QString& output, QCryptographicHash& hash, const rational &time, const VideoParams& video_params) const override; AudioVisualWaveform& waveform() { diff --git a/app/node/project/footage/footage.cpp b/app/node/project/footage/footage.cpp index 3c2c7e4ad..15db443b3 100644 --- a/app/node/project/footage/footage.cpp +++ b/app/node/project/footage/footage.cpp @@ -300,9 +300,9 @@ QString Footage::DescribeAudioStream(const AudioParams ¶ms) QString::number(params.sample_rate())); } -void Footage::Hash(const QString& output, QCryptographicHash &hash, const rational &time) const +void Footage::Hash(const QString& output, QCryptographicHash &hash, const rational &time, const VideoParams &video_params) const { - super::Hash(output, hash, time); + super::Hash(output, hash, time, video_params); // Footage last modified date hash.addData(QString::number(timestamp()).toUtf8()); diff --git a/app/node/project/footage/footage.h b/app/node/project/footage/footage.h index f8583226b..c195f14e1 100644 --- a/app/node/project/footage/footage.h +++ b/app/node/project/footage/footage.h @@ -170,7 +170,7 @@ public: static QString DescribeVideoStream(const VideoParams& params); static QString DescribeAudioStream(const AudioParams& params); - virtual void Hash(const QString& output, QCryptographicHash &hash, const rational &time) const override; + virtual void Hash(const QString& output, QCryptographicHash &hash, const rational &time, const VideoParams& video_params) const override; virtual NodeValueTable Value(const QString &output, NodeValueDatabase& value) const override; diff --git a/app/node/time/timeremap/timeremap.cpp b/app/node/time/timeremap/timeremap.cpp index 4176999cd..c7fefd45e 100644 --- a/app/node/time/timeremap/timeremap.cpp +++ b/app/node/time/timeremap/timeremap.cpp @@ -98,13 +98,13 @@ QVector TimeRemapNode::inputs_for_output(const QString &output) const return {kInputInput}; } -void TimeRemapNode::Hash(const QString &output, QCryptographicHash &hash, const rational &time) const +void TimeRemapNode::Hash(const QString &output, QCryptographicHash &hash, const rational &time, const VideoParams &video_params) const { // Don't hash anything of our own, just pass-through to the connected node at the remapped tmie Q_UNUSED(output) if (IsInputConnected(kInputInput)) { NodeOutput out = GetConnectedOutput(kInputInput); - out.node()->Hash(out.output(), hash, GetRemappedTime(time)); + out.node()->Hash(out.output(), hash, GetRemappedTime(time), video_params); } } diff --git a/app/node/time/timeremap/timeremap.h b/app/node/time/timeremap/timeremap.h index 79d859524..31243be02 100644 --- a/app/node/time/timeremap/timeremap.h +++ b/app/node/time/timeremap/timeremap.h @@ -47,7 +47,7 @@ public: virtual QVector inputs_for_output(const QString &output) const override; - virtual void Hash(const QString &output, QCryptographicHash &hash, const rational &time) const override; + virtual void Hash(const QString &output, QCryptographicHash &hash, const rational &time, const VideoParams& video_params) const override; static const QString kTimeInput; static const QString kInputInput; diff --git a/app/render/framehashcache.cpp b/app/render/framehashcache.cpp index 4934138c0..5a0b33494 100644 --- a/app/render/framehashcache.cpp +++ b/app/render/framehashcache.cpp @@ -22,6 +22,7 @@ #include #include +#include #include #include #include @@ -257,6 +258,8 @@ FramePtr FrameHashCache::LoadCacheFrame(const QString &fn) int height = dw.max.y - dw.min.y + 1; bool has_alpha = file.header().channels().findChannel("A"); + int div = qMax(1, static_cast(file.header()["oliveDivider"]).value()); + VideoParams::Format image_format; if (pix_type == Imf::HALF) { image_format = VideoParams::kFormatFloat16; @@ -267,11 +270,13 @@ FramePtr FrameHashCache::LoadCacheFrame(const QString &fn) int channel_count = has_alpha ? VideoParams::kRGBAChannelCount : VideoParams::kRGBChannelCount; frame = Frame::Create(); - frame->set_video_params(VideoParams(width, - height, + frame->set_video_params(VideoParams(width * div, + height * div, image_format, channel_count, - rational::fromDouble(file.header().pixelAspectRatio()))); + rational::fromDouble(file.header().pixelAspectRatio()), + VideoParams::kInterlaceNone, + div)); frame->allocate(); @@ -445,6 +450,8 @@ bool FrameHashCache::SaveCacheFrame(const QString &filename, char *data, const V header.insert("dwaCompressionLevel", Imf::FloatAttribute(200.0f)); header.pixelAspectRatio() = vparam.pixel_aspect_ratio().toDouble(); + header.insert("oliveDivider", Imf::IntAttribute(vparam.divider())); + Imf::OutputFile out(filename.toUtf8(), header, 0); int bpc = VideoParams::GetBytesPerChannel(vparam.format()); diff --git a/app/render/rendermanager.cpp b/app/render/rendermanager.cpp index 8ef0c99a8..1198145b4 100644 --- a/app/render/rendermanager.cpp +++ b/app/render/rendermanager.cpp @@ -93,7 +93,7 @@ QByteArray RenderManager::Hash(const Node *n, const QString& output, const Video hasher.addData(reinterpret_cast(&format), sizeof(VideoParams::Format)); if (n) { - n->Hash(output, hasher, time); + n->Hash(output, hasher, time, params); } return hasher.result(); diff --git a/app/render/renderprocessor.cpp b/app/render/renderprocessor.cpp index 73f64bccd..5cd1560f4 100644 --- a/app/render/renderprocessor.cpp +++ b/app/render/renderprocessor.cpp @@ -556,15 +556,6 @@ QVariant RenderProcessor::GetCachedTexture(const QByteArray& hash) FramePtr f = FrameHashCache::LoadCacheFrame(cache_dir, hash); if (f) { - // The cached frame won't load with the correct divider by default, so we enforce it here - VideoParams p = f->video_params(); - - p.set_width(f->width() * video_params.divider()); - p.set_height(f->height() * video_params.divider()); - p.set_divider(video_params.divider()); - - f->set_video_params(p); - TexturePtr texture = render_ctx_->CreateTexture(f->video_params(), f->data(), f->linesize_pixels()); qDebug() << "Loaded mid-render frame from cache"; return QVariant::fromValue(texture);