transform: hash the generated matrix
Making an exception for the transform node, we use a slightly heavier hash to save more rendering time later. Fixes #1560 Addresses some of #1360
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <QGuiApplication>
|
||||
|
||||
#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>();
|
||||
QVector2D texture_res(texture->params().square_pixel_width(), texture->params().height());
|
||||
AutoScaleType autoscale = static_cast<AutoScaleType>(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<VideoParams>();
|
||||
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));
|
||||
}
|
||||
|
||||
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>();
|
||||
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,
|
||||
autoscale);
|
||||
}
|
||||
|
||||
void TransformDistortNode::DrawGizmos(NodeValueDatabase &db, QPainter *p)
|
||||
{
|
||||
// 0 pen width is always 1px wide despite any transform
|
||||
|
||||
@@ -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<QVariant> gizmo_start_;
|
||||
|
||||
@@ -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)));
|
||||
|
||||
@@ -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;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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_;
|
||||
|
||||
+4
-4
@@ -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<arr_sz; i++) {
|
||||
HashInputElement(hash, input, i, time);
|
||||
HashInputElement(hash, input, i, time, video_params);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1593,7 +1593,7 @@ QVector<Node *> 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);
|
||||
|
||||
+2
-2
@@ -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<Node*> 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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -98,13 +98,13 @@ QVector<QString> 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ public:
|
||||
|
||||
virtual QVector<QString> 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;
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
#include <OpenEXR/ImfFloatAttribute.h>
|
||||
#include <OpenEXR/ImfInputFile.h>
|
||||
#include <OpenEXR/ImfIntAttribute.h>
|
||||
#include <OpenEXR/ImfOutputFile.h>
|
||||
#include <OpenEXR/ImfChannelList.h>
|
||||
#include <QDir>
|
||||
@@ -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<const Imf::IntAttribute&>(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());
|
||||
|
||||
@@ -93,7 +93,7 @@ QByteArray RenderManager::Hash(const Node *n, const QString& output, const Video
|
||||
hasher.addData(reinterpret_cast<const char*>(&format), sizeof(VideoParams::Format));
|
||||
|
||||
if (n) {
|
||||
n->Hash(output, hasher, time);
|
||||
n->Hash(output, hasher, time, params);
|
||||
}
|
||||
|
||||
return hasher.result();
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user