implemented "value hints" for choosing output values to use
This commit is contained in:
@@ -100,7 +100,7 @@ void Block::InputValueChangedEvent(const QString &input, int element)
|
||||
}
|
||||
}
|
||||
|
||||
bool Block::HashPassthrough(const QString &input, const Node::ValueHint &output, QCryptographicHash &hash, const NodeGlobals &globals, const VideoParams &video_params) const
|
||||
bool Block::HashPassthrough(const QString &input, QCryptographicHash &hash, const NodeGlobals &globals, const VideoParams &video_params) const
|
||||
{
|
||||
if (IsInputConnected(input)) {
|
||||
TimeRange t = InputTimeAdjustment(input, -1, globals.time());
|
||||
|
||||
@@ -134,7 +134,7 @@ signals:
|
||||
protected:
|
||||
virtual void InputValueChangedEvent(const QString& input, int element) override;
|
||||
|
||||
bool HashPassthrough(const QString &input, const ValueHint &output, QCryptographicHash &hash, const NodeGlobals &globals, const VideoParams& video_params) const;
|
||||
bool HashPassthrough(const QString &input, QCryptographicHash &hash, const NodeGlobals &globals, const VideoParams& video_params) const;
|
||||
|
||||
Block* previous_;
|
||||
Block* next_;
|
||||
|
||||
@@ -266,7 +266,7 @@ void ClipBlock::Retranslate()
|
||||
|
||||
void ClipBlock::Hash(const ValueHint &out, QCryptographicHash &hash, const NodeGlobals &globals, const VideoParams &video_params) const
|
||||
{
|
||||
HashPassthrough(kBufferIn, GetValueHintForInput(kBufferIn, -1), hash, globals, video_params);
|
||||
HashPassthrough(kBufferIn, hash, globals, video_params);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -139,9 +139,9 @@ double TransitionBlock::GetInProgress(const double &time) const
|
||||
|
||||
void TransitionBlock::Hash(const ValueHint &output, QCryptographicHash &hash, const NodeGlobals &globals, const VideoParams &video_params) const
|
||||
{
|
||||
if (HashPassthrough(kInBlockInput, GetValueHintForInput(kInBlockInput, -1), hash, globals, video_params)
|
||||
|| HashPassthrough(kOutBlockInput, GetValueHintForInput(kOutBlockInput, -1), hash, globals, video_params)) {
|
||||
HashAddNodeSignature(hash, output);
|
||||
if (HashPassthrough(kInBlockInput, hash, globals, video_params)
|
||||
|| HashPassthrough(kOutBlockInput, hash, globals, video_params)) {
|
||||
HashAddNodeSignature(hash);
|
||||
|
||||
double time_dbl = globals.time().in().toDouble();
|
||||
double all_prog = GetTotalProgress(time_dbl);
|
||||
|
||||
@@ -61,17 +61,15 @@ void TransformDistortNode::Value(const NodeValueRow &value, const NodeGlobals &g
|
||||
|
||||
// Pop texture
|
||||
NodeValue texture_meta = value[kTextureInput];
|
||||
TexturePtr texture = texture_meta.data().value<TexturePtr>();
|
||||
|
||||
bool pushed_job = false;
|
||||
|
||||
// If we have a texture, generate a matrix and make it happen
|
||||
if (texture) {
|
||||
if (TexturePtr texture = texture_meta.data().value<TexturePtr>()) {
|
||||
// Adjust our matrix by the resolutions involved
|
||||
QMatrix4x4 real_matrix = GenerateAutoScaledMatrix(generated_matrix, value, globals, texture->params());
|
||||
|
||||
if (real_matrix.isIdentity()) {
|
||||
// We don't expect any changes, just push as normal
|
||||
table->Push(texture_meta);
|
||||
} else {
|
||||
if (!real_matrix.isIdentity()) {
|
||||
// The matrix will transform things
|
||||
ShaderJob job;
|
||||
job.InsertValue(QStringLiteral("ove_maintex"), NodeValue(NodeValue::kTexture, QVariant::fromValue(texture), this));
|
||||
@@ -83,8 +81,15 @@ void TransformDistortNode::Value(const NodeValueRow &value, const NodeGlobals &g
|
||||
job.SetAlphaChannelRequired(GenerateJob::kAlphaForceOn);
|
||||
|
||||
table->Push(NodeValue::kShaderJob, QVariant::fromValue(job), this);
|
||||
|
||||
pushed_job = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!pushed_job) {
|
||||
// Re-push whatever value we received
|
||||
table->Push(texture_meta);
|
||||
}
|
||||
}
|
||||
|
||||
ShaderCode TransformDistortNode::GetShaderCode(const QString &shader_id) const
|
||||
@@ -328,7 +333,7 @@ void TransformDistortNode::Hash(const ValueHint &output, QCryptographicHash &has
|
||||
|
||||
if (!matrix.isIdentity()) {
|
||||
// Add fingerprint
|
||||
HashAddNodeSignature(hash, output);
|
||||
HashAddNodeSignature(hash);
|
||||
hash.addData(reinterpret_cast<const char*>(&matrix), sizeof(matrix));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,6 +98,7 @@ void NodeGraph::childEvent(QChildEvent *event)
|
||||
connect(node, &Node::InputConnected, this, &NodeGraph::InputConnected);
|
||||
connect(node, &Node::InputDisconnected, this, &NodeGraph::InputDisconnected);
|
||||
connect(node, &Node::ValueChanged, this, &NodeGraph::ValueChanged);
|
||||
connect(node, &Node::InputValueHintChanged, this, &NodeGraph::InputValueHintChanged);
|
||||
|
||||
emit NodeAdded(node);
|
||||
emit node->AddedToGraph(this);
|
||||
@@ -110,6 +111,7 @@ void NodeGraph::childEvent(QChildEvent *event)
|
||||
disconnect(node, &Node::InputConnected, this, &NodeGraph::InputConnected);
|
||||
disconnect(node, &Node::InputDisconnected, this, &NodeGraph::InputDisconnected);
|
||||
disconnect(node, &Node::ValueChanged, this, &NodeGraph::ValueChanged);
|
||||
disconnect(node, &Node::InputValueHintChanged, this, &NodeGraph::InputValueHintChanged);
|
||||
|
||||
emit NodeRemoved(node);
|
||||
emit node->RemovedFromGraph(this);
|
||||
|
||||
@@ -129,6 +129,8 @@ signals:
|
||||
|
||||
void ValueChanged(const NodeInput& input);
|
||||
|
||||
void InputValueHintChanged(const NodeInput& input);
|
||||
|
||||
void NodePositionAdded(Node *node, Node *relative, const QPointF &position);
|
||||
|
||||
void NodePositionRemoved(Node *node, Node *relative);
|
||||
|
||||
@@ -359,6 +359,8 @@ void MathNodeBase::ValueInternal(Operation operation, Pairing pairing, const QSt
|
||||
} else {
|
||||
output->Push(NodeValue::kSampleJob, QVariant::fromValue(job), this);
|
||||
}
|
||||
} else {
|
||||
output->Push(NodeValue::kSampleJob, QVariant::fromValue(job), this);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ void MergeNode::Hash(const ValueHint &output, QCryptographicHash &hash, const No
|
||||
|
||||
if (!passthrough_base && !passthrough_blend) {
|
||||
// This merge will actually do something so we add a fingerprint
|
||||
HashAddNodeSignature(hash, output);
|
||||
HashAddNodeSignature(hash);
|
||||
}
|
||||
|
||||
if (!passthrough_base) {
|
||||
|
||||
@@ -139,6 +139,28 @@ void Node::Load(QXmlStreamReader *reader, XMLNodeData& xml_node_data, uint versi
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
} else if (reader->name() == QStringLiteral("hints")) {
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("hint")) {
|
||||
QString input;
|
||||
int element;
|
||||
|
||||
XMLAttributeLoop(reader, attr) {
|
||||
if (attr.name() == QStringLiteral("input")) {
|
||||
input = attr.value().toString();
|
||||
} else if (attr.name() == QStringLiteral("element")) {
|
||||
element = attr.value().toInt();
|
||||
}
|
||||
}
|
||||
|
||||
ValueHint vh;
|
||||
vh.Load(reader);
|
||||
|
||||
value_hints_.insert({input, element}, vh);
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
@@ -179,6 +201,19 @@ void Node::Save(QXmlStreamWriter *writer) const
|
||||
}
|
||||
writer->writeEndElement(); // connections
|
||||
|
||||
writer->writeStartElement(QStringLiteral("hints"));
|
||||
for (auto it=value_hints_.cbegin(); it!=value_hints_.cend(); it++) {
|
||||
writer->writeStartElement(QStringLiteral("hint"));
|
||||
|
||||
writer->writeAttribute(QStringLiteral("input"), it.key().input);
|
||||
writer->writeAttribute(QStringLiteral("element"), QString::number(it.key().element));
|
||||
|
||||
it.value().Save(writer);
|
||||
|
||||
writer->writeEndElement(); // hint
|
||||
}
|
||||
writer->writeEndElement();
|
||||
|
||||
writer->writeStartElement(QStringLiteral("custom"));
|
||||
SaveCustom(writer);
|
||||
writer->writeEndElement(); // custom
|
||||
@@ -980,6 +1015,15 @@ int Node::InputArraySize(const QString &id) const
|
||||
}
|
||||
}
|
||||
|
||||
void Node::SetValueHintForInput(const QString &input, int element, const ValueHint &hint)
|
||||
{
|
||||
value_hints_.insert({input, element}, hint);
|
||||
|
||||
emit InputValueHintChanged(NodeInput(this, input, element));
|
||||
|
||||
InvalidateAll(input, element);
|
||||
}
|
||||
|
||||
const NodeKeyframeTrack &Node::GetTrackFromKeyframe(NodeKeyframe *key) const
|
||||
{
|
||||
return GetImmediate(key->input(), key->element())->keyframe_tracks().at(key->track());
|
||||
@@ -1256,6 +1300,7 @@ bool Node::AreLinked(Node *a, Node *b)
|
||||
|
||||
void Node::HashAddNodeSignature(QCryptographicHash &hash) const
|
||||
{
|
||||
// Add node ID
|
||||
hash.addData(id().toUtf8());
|
||||
}
|
||||
|
||||
@@ -1529,6 +1574,9 @@ void Node::CopyValuesOfElement(const Node *src, Node *dst, const QString &input,
|
||||
if (src_element == -1 && dst_element == -1) {
|
||||
dst->ArrayResizeInternal(input, src->InputArraySize(input));
|
||||
}
|
||||
|
||||
// Copy value hint
|
||||
dst->SetValueHintForInput(input, dst_element, src->GetValueHintForInput(input, src_element));
|
||||
}
|
||||
|
||||
bool Node::CanBeDeleted() const
|
||||
@@ -2408,4 +2456,50 @@ void NodeRemovePositionFromAllContextsCommand::undo()
|
||||
}
|
||||
}
|
||||
|
||||
void Node::ValueHint::Hash(QCryptographicHash &hash) const
|
||||
{
|
||||
// Add value hint
|
||||
foreach (NodeValue::Type t, type) {
|
||||
hash.addData(reinterpret_cast<const char*>(&t), sizeof(t));
|
||||
}
|
||||
hash.addData(reinterpret_cast<const char *>(&index), sizeof(index));
|
||||
hash.addData(tag.toUtf8());
|
||||
}
|
||||
|
||||
void Node::ValueHint::Load(QXmlStreamReader *reader)
|
||||
{
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("types")) {
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("type")) {
|
||||
type.append(static_cast<NodeValue::Type>(reader->readElementText().toInt()));
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
} else if (reader->name() == QStringLiteral("index")) {
|
||||
index = reader->readElementText().toInt();
|
||||
} else if (reader->name() == QStringLiteral("tag")) {
|
||||
tag = reader->readElementText();
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Node::ValueHint::Save(QXmlStreamWriter *writer) const
|
||||
{
|
||||
writer->writeStartElement(QStringLiteral("types"));
|
||||
|
||||
for (auto it=type.cbegin(); it!=type.cend(); it++) {
|
||||
writer->writeTextElement(QStringLiteral("type"), QString::number(*it));
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // types
|
||||
|
||||
writer->writeTextElement(QStringLiteral("index"), QString::number(index));
|
||||
|
||||
writer->writeTextElement(QStringLiteral("tag"), tag);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+10
-4
@@ -466,6 +466,11 @@ public:
|
||||
QVector<NodeValue::Type> type;
|
||||
int index = -1;
|
||||
QString tag;
|
||||
|
||||
void Hash(QCryptographicHash &hash) const;
|
||||
|
||||
void Load(QXmlStreamReader *reader);
|
||||
void Save(QXmlStreamWriter *writer) const;
|
||||
};
|
||||
|
||||
ValueHint GetValueHintForInput(const QString &input, int element) const
|
||||
@@ -473,10 +478,7 @@ public:
|
||||
return value_hints_.value({input, element});
|
||||
}
|
||||
|
||||
void SetValueHintForInput(const QString &input, int element, const ValueHint &hint)
|
||||
{
|
||||
value_hints_.insert({input, element}, hint);
|
||||
}
|
||||
void SetValueHintForInput(const QString &input, int element, const ValueHint &hint);
|
||||
|
||||
const NodeKeyframeTrack& GetTrackFromKeyframe(NodeKeyframe* key) const;
|
||||
|
||||
@@ -965,6 +967,8 @@ signals:
|
||||
|
||||
void OutputDisconnected(Node *output, const NodeInput& input);
|
||||
|
||||
void InputValueHintChanged(const NodeInput& input);
|
||||
|
||||
void InputPropertyChanged(const QString& input, const QString& key, const QVariant& value);
|
||||
|
||||
void LinksChanged();
|
||||
@@ -1519,4 +1523,6 @@ private:
|
||||
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(olive::Node::ValueHint);
|
||||
|
||||
#endif // NODE_H
|
||||
|
||||
@@ -221,6 +221,7 @@ uint qHash(const NodeKeyframeTrackReference& i);
|
||||
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(olive::NodeInput)
|
||||
Q_DECLARE_METATYPE(olive::NodeKeyframeTrackReference)
|
||||
|
||||
#endif // NODEPARAM_H
|
||||
|
||||
+19
-7
@@ -48,7 +48,8 @@ NodeValueRow NodeTraverser::GenerateRow(NodeValueDatabase *database, const Node
|
||||
NodeValueRow row;
|
||||
for (auto it=database->begin(); it!=database->end(); it++) {
|
||||
// Get hint for which value should be pulled
|
||||
row.insert(it.key(), GenerateRowValue(node, it.key(), &it.value()));
|
||||
NodeValue value = GenerateRowValue(node, it.key(), &it.value());
|
||||
row.insert(it.key(), value);
|
||||
}
|
||||
|
||||
return row;
|
||||
@@ -82,6 +83,17 @@ NodeValue NodeTraverser::GenerateRowValue(const Node *node, const QString &input
|
||||
}
|
||||
|
||||
NodeValue NodeTraverser::GenerateRowValueElement(const Node *node, const QString &input, int element, NodeValueTable *table)
|
||||
{
|
||||
int value_index = GenerateRowValueElementIndex(node, input, element, table);
|
||||
|
||||
if (value_index == -1) {
|
||||
return NodeValue();
|
||||
} else {
|
||||
return table->TakeAt(value_index);
|
||||
}
|
||||
}
|
||||
|
||||
int NodeTraverser::GenerateRowValueElementIndex(const Node *node, const QString &input, int element, const NodeValueTable *table)
|
||||
{
|
||||
Node::ValueHint hint = node->GetValueHintForInput(input, element);
|
||||
QVector<NodeValue::Type> types = hint.type;
|
||||
@@ -92,23 +104,23 @@ NodeValue NodeTraverser::GenerateRowValueElement(const Node *node, const QString
|
||||
|
||||
if (hint.index == -1) {
|
||||
// Get most recent value with this type and tag
|
||||
return table->TakeWithMeta(types, hint.tag);
|
||||
return table->GetValueIndex(types, hint.tag);
|
||||
} else {
|
||||
// Try to find value at this index
|
||||
int index = table->Count() - hint.index;
|
||||
int index = table->Count() - 1 - hint.index;
|
||||
int diff = 0;
|
||||
|
||||
while (index + diff < table->Count() && index - diff >= 0) {
|
||||
if (index + diff < table->Count() && types.contains(table->at(index + diff).type())) {
|
||||
return table->TakeAt(index + diff);
|
||||
return index + diff;
|
||||
}
|
||||
if (index - diff >= 0 && types.contains(table->at(index - diff).type())) {
|
||||
return table->TakeAt(index - diff);
|
||||
return index - diff;
|
||||
}
|
||||
diff++;
|
||||
}
|
||||
|
||||
return NodeValue();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -270,7 +282,7 @@ QVariant NodeTraverser::ProcessSamples(const Node *node, const TimeRange &range,
|
||||
Q_UNUSED(range)
|
||||
Q_UNUSED(job)
|
||||
|
||||
return QVariant();
|
||||
return QVariant::fromValue(SampleBuffer::Create());
|
||||
}
|
||||
|
||||
QVariant NodeTraverser::ProcessFrameGeneration(const Node *node, const GenerateJob &job)
|
||||
|
||||
@@ -45,6 +45,7 @@ public:
|
||||
|
||||
NodeValue GenerateRowValue(const Node *node, const QString &input, NodeValueTable *table);
|
||||
NodeValue GenerateRowValueElement(const Node *node, const QString &input, int element, NodeValueTable *table);
|
||||
int GenerateRowValueElementIndex(const Node *node, const QString &input, int element, const NodeValueTable *table);
|
||||
|
||||
static NodeGlobals GenerateGlobals(const VideoParams ¶ms, const TimeRange &time);
|
||||
static NodeGlobals GenerateGlobals(const VideoParams ¶ms, const rational &time)
|
||||
|
||||
+3
-3
@@ -326,7 +326,7 @@ QString NodeValue::GetPrettyDataTypeName(Type type)
|
||||
|
||||
NodeValue NodeValueTable::GetWithMeta(const QVector<NodeValue::Type> &type, const QString &tag) const
|
||||
{
|
||||
int value_index = GetInternal(type, tag);
|
||||
int value_index = GetValueIndex(type, tag);
|
||||
|
||||
if (value_index >= 0) {
|
||||
return values_.at(value_index);
|
||||
@@ -337,7 +337,7 @@ NodeValue NodeValueTable::GetWithMeta(const QVector<NodeValue::Type> &type, cons
|
||||
|
||||
NodeValue NodeValueTable::TakeWithMeta(const QVector<NodeValue::Type> &type, const QString &tag)
|
||||
{
|
||||
int value_index = GetInternal(type, tag);
|
||||
int value_index = GetValueIndex(type, tag);
|
||||
|
||||
if (value_index >= 0) {
|
||||
return values_.takeAt(value_index);
|
||||
@@ -407,7 +407,7 @@ NodeValueTable NodeValueTable::Merge(QList<NodeValueTable> tables)
|
||||
return merged_table;
|
||||
}
|
||||
|
||||
int NodeValueTable::GetInternal(const QVector<NodeValue::Type>& types, const QString &tag) const
|
||||
int NodeValueTable::GetValueIndex(const QVector<NodeValue::Type>& types, const QString &tag) const
|
||||
{
|
||||
int index = -1;
|
||||
|
||||
|
||||
+2
-2
@@ -391,11 +391,11 @@ public:
|
||||
return values_.isEmpty();
|
||||
}
|
||||
|
||||
int GetValueIndex(const QVector<NodeValue::Type> &type, const QString& tag) const;
|
||||
|
||||
static NodeValueTable Merge(QList<NodeValueTable> tables);
|
||||
|
||||
private:
|
||||
int GetInternal(const QVector<NodeValue::Type> &type, const QString& tag) const;
|
||||
|
||||
QVector<NodeValue> values_;
|
||||
|
||||
};
|
||||
|
||||
@@ -52,6 +52,11 @@ public:
|
||||
node_view_->ClearGraph();
|
||||
}
|
||||
|
||||
const QVector<Node*> &GetCurrentContexts() const
|
||||
{
|
||||
return node_view_->GetCurrentContexts();
|
||||
}
|
||||
|
||||
virtual void SelectAll() override
|
||||
{
|
||||
node_view_->SelectAll();
|
||||
|
||||
@@ -14,7 +14,6 @@ namespace olive {
|
||||
PreviewAutoCacher::PreviewAutoCacher() :
|
||||
viewer_node_(nullptr),
|
||||
paused_(false),
|
||||
has_changed_(false),
|
||||
use_custom_range_(false),
|
||||
single_frame_render_(nullptr)
|
||||
{
|
||||
@@ -141,7 +140,9 @@ void PreviewAutoCacher::HashesProcessed()
|
||||
// The cacher might be waiting for this job to finish
|
||||
if (!graph_update_queue_.isEmpty()) {
|
||||
TryRender();
|
||||
} else if (hash_iterator_.HasNext()) {
|
||||
}
|
||||
|
||||
if (hash_iterator_.HasNext()) {
|
||||
// Launch next hashes
|
||||
QueueNextHashTask();
|
||||
}
|
||||
@@ -203,7 +204,12 @@ void PreviewAutoCacher::AudioRendered()
|
||||
foreach (TimeRange r, intersections) {
|
||||
// For each range, adjust it relative to the block and write it
|
||||
r -= block->in();
|
||||
block->waveform().OverwriteSums(waveform_info.waveform, r.in(), r.in() - waveform_info.range.in(), r.length());
|
||||
|
||||
if (waveform_info.silence) {
|
||||
block->waveform().OverwriteSilence(r.in(), r.length());
|
||||
} else {
|
||||
block->waveform().OverwriteSums(waveform_info.waveform, r.in(), r.in() - waveform_info.range.in(), r.length());
|
||||
}
|
||||
}
|
||||
|
||||
emit block->PreviewChanged();
|
||||
@@ -316,6 +322,9 @@ void PreviewAutoCacher::ProcessUpdateQueue()
|
||||
case QueuedJob::kValueChanged:
|
||||
CopyValue(job.input);
|
||||
break;
|
||||
case QueuedJob::kValueHintChanged:
|
||||
CopyValueHint(job.input);
|
||||
break;
|
||||
}
|
||||
}
|
||||
graph_update_queue_.clear();
|
||||
@@ -379,6 +388,13 @@ void PreviewAutoCacher::CopyValue(const NodeInput &input)
|
||||
Node::CopyValuesOfElement(input.node(), our_input, input.input(), input.element());
|
||||
}
|
||||
|
||||
void PreviewAutoCacher::CopyValueHint(const NodeInput &input)
|
||||
{
|
||||
Node* our_input = copy_map_.value(input.node());
|
||||
Node::ValueHint hint = input.node()->GetValueHintForInput(input.input(), input.element());
|
||||
our_input->SetValueHintForInput(input.input(), input.element(), hint);
|
||||
}
|
||||
|
||||
void PreviewAutoCacher::InsertIntoCopyMap(Node *node, Node *copy)
|
||||
{
|
||||
// Insert into map
|
||||
@@ -412,7 +428,6 @@ void PreviewAutoCacher::SetPlayhead(const rational &playhead)
|
||||
cache_range_ = TimeRange(playhead - Config::Current()[QStringLiteral("DiskCacheBehind")].value<rational>(),
|
||||
playhead + Config::Current()[QStringLiteral("DiskCacheAhead")].value<rational>());
|
||||
|
||||
has_changed_ = true;
|
||||
use_custom_range_ = false;
|
||||
|
||||
RequeueFrames();
|
||||
@@ -439,7 +454,6 @@ void PreviewAutoCacher::ClearVideoQueue(bool hard)
|
||||
{
|
||||
ClearQueueInternal(video_tasks_, hard, &PreviewAutoCacher::VideoRendered);
|
||||
|
||||
has_changed_ = true;
|
||||
use_custom_range_ = false;
|
||||
queued_frame_iterator_.reset();
|
||||
}
|
||||
@@ -486,6 +500,12 @@ void PreviewAutoCacher::ValueChanged(const NodeInput &input)
|
||||
UpdateGraphChangeValue();
|
||||
}
|
||||
|
||||
void PreviewAutoCacher::ValueHintChanged(const NodeInput &input)
|
||||
{
|
||||
graph_update_queue_.append({QueuedJob::kValueHintChanged, nullptr, input, nullptr});
|
||||
UpdateGraphChangeValue();
|
||||
}
|
||||
|
||||
void PreviewAutoCacher::TryRender()
|
||||
{
|
||||
if (!graph_update_queue_.isEmpty()) {
|
||||
@@ -564,7 +584,6 @@ void PreviewAutoCacher::RequeueFrames()
|
||||
if (viewer_node_
|
||||
&& viewer_node_->video_frame_cache()->HasInvalidatedRanges(viewer_node_->GetVideoLength())
|
||||
&& hash_tasks_.isEmpty()
|
||||
&& has_changed_
|
||||
&& VideoParams::FormatIsFloat(viewer_node_->GetVideoParams().format())
|
||||
&& (!paused_ || use_custom_range_)) {
|
||||
TimeRange using_range;
|
||||
@@ -580,8 +599,6 @@ void PreviewAutoCacher::RequeueFrames()
|
||||
queued_frame_iterator_ = TimeRangeListFrameIterator(invalidated, viewer_node_->video_frame_cache()->GetTimebase());
|
||||
|
||||
QueueNextFrameInRange(RenderManager::GetNumberOfIdealConcurrentJobs());
|
||||
|
||||
has_changed_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -599,7 +616,6 @@ void PreviewAutoCacher::ConformFinished()
|
||||
|
||||
void PreviewAutoCacher::ForceCacheRange(const TimeRange &range)
|
||||
{
|
||||
has_changed_ = true;
|
||||
use_custom_range_ = true;
|
||||
custom_autocache_range_ = range;
|
||||
|
||||
@@ -655,6 +671,7 @@ void PreviewAutoCacher::SetViewerNode(ViewerOutput *viewer_node)
|
||||
disconnect(graph, &NodeGraph::InputConnected, this, &PreviewAutoCacher::EdgeAdded);
|
||||
disconnect(graph, &NodeGraph::InputDisconnected, this, &PreviewAutoCacher::EdgeRemoved);
|
||||
disconnect(graph, &NodeGraph::ValueChanged, this, &PreviewAutoCacher::ValueChanged);
|
||||
disconnect(graph, &NodeGraph::InputValueHintChanged, this, &PreviewAutoCacher::ValueHintChanged);
|
||||
|
||||
// Disconnect signal (will be a no-op if the signal was never connected)
|
||||
disconnect(viewer_node_->video_frame_cache(),
|
||||
@@ -705,6 +722,7 @@ void PreviewAutoCacher::SetViewerNode(ViewerOutput *viewer_node)
|
||||
connect(graph, &NodeGraph::InputConnected, this, &PreviewAutoCacher::EdgeAdded);
|
||||
connect(graph, &NodeGraph::InputDisconnected, this, &PreviewAutoCacher::EdgeRemoved);
|
||||
connect(graph, &NodeGraph::ValueChanged, this, &PreviewAutoCacher::ValueChanged);
|
||||
connect(graph, &NodeGraph::InputValueHintChanged, this, &PreviewAutoCacher::ValueHintChanged);
|
||||
|
||||
// Copy invalidated ranges - used to determine which frames need hashing
|
||||
invalidated_video_ = viewer_node_->video_frame_cache()->GetInvalidatedRanges(viewer_node_->GetVideoLength());
|
||||
|
||||
@@ -90,6 +90,7 @@ private:
|
||||
void AddEdge(Node *output, const NodeInput& input);
|
||||
void RemoveEdge(Node *output, const NodeInput& input);
|
||||
void CopyValue(const NodeInput& input);
|
||||
void CopyValueHint(const NodeInput& input);
|
||||
|
||||
void InsertIntoCopyMap(Node* node, Node* copy);
|
||||
|
||||
@@ -124,7 +125,8 @@ private:
|
||||
kNodeRemoved,
|
||||
kEdgeAdded,
|
||||
kEdgeRemoved,
|
||||
kValueChanged
|
||||
kValueChanged,
|
||||
kValueHintChanged
|
||||
};
|
||||
|
||||
Type type;
|
||||
@@ -147,8 +149,6 @@ private:
|
||||
|
||||
TimeRange cache_range_;
|
||||
|
||||
bool has_changed_;
|
||||
|
||||
bool use_custom_range_;
|
||||
TimeRange custom_autocache_range_;
|
||||
|
||||
@@ -220,6 +220,8 @@ private slots:
|
||||
|
||||
void ValueChanged(const NodeInput& input);
|
||||
|
||||
void ValueHintChanged(const NodeInput &input);
|
||||
|
||||
/**
|
||||
* @brief Generic function called whenever the frames to render need to be (re)queued
|
||||
*/
|
||||
|
||||
@@ -176,7 +176,7 @@ void RenderProcessor::Run()
|
||||
|
||||
QVariant sample_variant = table.Get(NodeValue::kSamples);
|
||||
SampleBufferPtr samples = sample_variant.value<SampleBufferPtr>();
|
||||
if (samples && ticket_->property("enablewaveforms").toBool()) {
|
||||
if (ticket_->property("enablewaveforms").toBool()) {
|
||||
AudioVisualWaveform vis;
|
||||
vis.set_channel_count(samples->audio_params().channel_count());
|
||||
vis.OverwriteSamples(samples, samples->audio_params().sample_rate());
|
||||
@@ -264,52 +264,56 @@ NodeValueTable RenderProcessor::GenerateBlockTable(const Track *track, const Tim
|
||||
// Destination buffer
|
||||
NodeValueTable table = GenerateTable(b, track->GetValueHintForInput(Track::kBlockInput, track->GetArrayIndexFromBlock(b)),Track::TransformRangeForBlock(b, range_for_block));
|
||||
SampleBufferPtr samples_from_this_block = table.Take(NodeValue::kSamples).value<SampleBufferPtr>();
|
||||
ClipBlock *clip_cast = dynamic_cast<ClipBlock*>(b);
|
||||
|
||||
if (!samples_from_this_block) {
|
||||
// If we retrieved no samples from this block, do nothing
|
||||
continue;
|
||||
if (samples_from_this_block) {
|
||||
// If this is a clip, we might have extra speed/reverse information
|
||||
if (clip_cast) {
|
||||
double speed_value = clip_cast->speed();
|
||||
bool reversed = clip_cast->reverse();
|
||||
|
||||
if (qIsNull(speed_value)) {
|
||||
// Just silence, don't think there's any other practical application of 0 speed audio
|
||||
samples_from_this_block->fill(0);
|
||||
} else if (!qFuzzyCompare(speed_value, 1.0)) {
|
||||
// Multiply time
|
||||
samples_from_this_block->speed(speed_value);
|
||||
}
|
||||
|
||||
if (reversed) {
|
||||
samples_from_this_block->reverse();
|
||||
}
|
||||
}
|
||||
|
||||
int copy_length = qMin(max_dest_sz, samples_from_this_block->sample_count());
|
||||
|
||||
// Copy samples into destination buffer
|
||||
for (int i=0; i<samples_from_this_block->audio_params().channel_count(); i++) {
|
||||
block_range_buffer->set(i, samples_from_this_block->data(i), destination_offset, copy_length);
|
||||
}
|
||||
|
||||
NodeValueTable::Merge({merged_table, table});
|
||||
}
|
||||
|
||||
// If this is a clip, we might have extra speed/reverse information
|
||||
if (ClipBlock *clip_cast = dynamic_cast<ClipBlock*>(b)) {
|
||||
double speed_value = clip_cast->speed();
|
||||
bool reversed = clip_cast->reverse();
|
||||
// Create block waveforms if requested
|
||||
if (ticket_->property("enablewaveforms").toBool() && clip_cast) {
|
||||
// Format information for use in the main thread
|
||||
RenderedWaveform waveform_info;
|
||||
waveform_info.block = clip_cast;
|
||||
waveform_info.range = range_for_block - b->in();
|
||||
|
||||
if (qIsNull(speed_value)) {
|
||||
// Just silence, don't think there's any other practical application of 0 speed audio
|
||||
samples_from_this_block->fill(0);
|
||||
} else if (!qFuzzyCompare(speed_value, 1.0)) {
|
||||
// Multiply time
|
||||
samples_from_this_block->speed(speed_value);
|
||||
}
|
||||
|
||||
if (reversed) {
|
||||
samples_from_this_block->reverse();
|
||||
}
|
||||
|
||||
// Create block waveforms if requested
|
||||
if (ticket_->property("enablewaveforms").toBool()) {
|
||||
if (!(waveform_info.silence = !samples_from_this_block.get())) {
|
||||
// Generate a visual waveform from the samples acquired from this block
|
||||
AudioVisualWaveform visual_waveform;
|
||||
visual_waveform.set_channel_count(audio_params.channel_count());
|
||||
visual_waveform.OverwriteSamples(samples_from_this_block, audio_params.sample_rate());
|
||||
|
||||
// Format it for use back int eh maint hread
|
||||
RenderedWaveform waveform_info = {clip_cast, visual_waveform, range_for_block - b->in()};
|
||||
QVector<RenderedWaveform> waveform_list = ticket_->property("waveforms").value< QVector<RenderedWaveform> >();
|
||||
waveform_list.append(waveform_info);
|
||||
ticket_->setProperty("waveforms", QVariant::fromValue(waveform_list));
|
||||
waveform_info.waveform = visual_waveform;
|
||||
}
|
||||
|
||||
QVector<RenderedWaveform> waveform_list = ticket_->property("waveforms").value< QVector<RenderedWaveform> >();
|
||||
waveform_list.append(waveform_info);
|
||||
ticket_->setProperty("waveforms", QVariant::fromValue(waveform_list));
|
||||
}
|
||||
|
||||
int copy_length = qMin(max_dest_sz, samples_from_this_block->sample_count());
|
||||
|
||||
// Copy samples into destination buffer
|
||||
for (int i=0; i<samples_from_this_block->audio_params().channel_count(); i++) {
|
||||
block_range_buffer->set(i, samples_from_this_block->data(i), destination_offset, copy_length);
|
||||
}
|
||||
|
||||
NodeValueTable::Merge({merged_table, table});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ public:
|
||||
const ClipBlock* block;
|
||||
AudioVisualWaveform waveform;
|
||||
TimeRange range;
|
||||
bool silence;
|
||||
};
|
||||
|
||||
protected:
|
||||
|
||||
@@ -33,6 +33,7 @@ add_subdirectory(nodecombobox)
|
||||
add_subdirectory(nodeparamview)
|
||||
add_subdirectory(nodetableview)
|
||||
add_subdirectory(nodetreeview)
|
||||
add_subdirectory(nodevaluetree)
|
||||
add_subdirectory(nodeview)
|
||||
add_subdirectory(panel)
|
||||
add_subdirectory(path)
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "common/qtutils.h"
|
||||
#include "core.h"
|
||||
#include "node/node.h"
|
||||
#include "widget/collapsebutton/collapsebutton.h"
|
||||
#include "widget/menu/menu.h"
|
||||
#include "widget/nodeview/nodeviewundo.h"
|
||||
|
||||
@@ -35,20 +36,29 @@ NodeParamViewConnectedLabel::NodeParamViewConnectedLabel(const NodeInput &input,
|
||||
input_(input),
|
||||
connected_node_(nullptr)
|
||||
{
|
||||
QHBoxLayout* layout = new QHBoxLayout(this);
|
||||
layout->setSpacing(QtUtils::QFontMetricsWidth(fontMetrics(), QStringLiteral(" ")));
|
||||
QVBoxLayout *layout = new QVBoxLayout(this);
|
||||
layout->setMargin(0);
|
||||
|
||||
layout->addWidget(new QLabel(tr("Connected to")));
|
||||
// Set up label area
|
||||
QHBoxLayout *label_layout = new QHBoxLayout();
|
||||
label_layout->setSpacing(QtUtils::QFontMetricsWidth(fontMetrics(), QStringLiteral(" ")));
|
||||
label_layout->setMargin(0);
|
||||
layout->addLayout(label_layout);
|
||||
|
||||
CollapseButton *collapse_btn = new CollapseButton();
|
||||
collapse_btn->setChecked(false);
|
||||
label_layout->addWidget(collapse_btn);
|
||||
|
||||
label_layout->addWidget(new QLabel(tr("Connected to")));
|
||||
|
||||
connected_to_lbl_ = new ClickableLabel();
|
||||
connected_to_lbl_->setCursor(Qt::PointingHandCursor);
|
||||
connected_to_lbl_->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(connected_to_lbl_, &ClickableLabel::MouseClicked, this, &NodeParamViewConnectedLabel::ConnectionClicked);
|
||||
connect(connected_to_lbl_, &ClickableLabel::customContextMenuRequested, this, &NodeParamViewConnectedLabel::ShowLabelContextMenu);
|
||||
layout->addWidget(connected_to_lbl_);
|
||||
label_layout->addWidget(connected_to_lbl_);
|
||||
|
||||
layout->addStretch();
|
||||
label_layout->addStretch();
|
||||
|
||||
// Set up "link" font
|
||||
QFont link_font = connected_to_lbl_->font();
|
||||
@@ -64,6 +74,21 @@ NodeParamViewConnectedLabel::NodeParamViewConnectedLabel(const NodeInput &input,
|
||||
|
||||
connect(input_.node(), &Node::InputConnected, this, &NodeParamViewConnectedLabel::InputConnected);
|
||||
connect(input_.node(), &Node::InputDisconnected, this, &NodeParamViewConnectedLabel::InputDisconnected);
|
||||
|
||||
// Set up table area
|
||||
value_tree_ = new NodeValueTree();
|
||||
value_tree_->setVisible(false);
|
||||
layout->addWidget(value_tree_);
|
||||
connect(collapse_btn, &CollapseButton::toggled, this, &NodeParamViewConnectedLabel::SetValueTreeVisible);
|
||||
}
|
||||
|
||||
void NodeParamViewConnectedLabel::SetTime(const rational &time)
|
||||
{
|
||||
time_ = time;
|
||||
|
||||
if (value_tree_->isVisible()) {
|
||||
UpdateValueTree();
|
||||
}
|
||||
}
|
||||
|
||||
void NodeParamViewConnectedLabel::InputConnected(Node *output, const NodeInput& input)
|
||||
@@ -122,4 +147,18 @@ void NodeParamViewConnectedLabel::UpdateLabel()
|
||||
connected_to_lbl_->setText(s);
|
||||
}
|
||||
|
||||
void NodeParamViewConnectedLabel::UpdateValueTree()
|
||||
{
|
||||
value_tree_->SetNode(input_, time_);
|
||||
}
|
||||
|
||||
void NodeParamViewConnectedLabel::SetValueTreeVisible(bool e)
|
||||
{
|
||||
value_tree_->setVisible(e);
|
||||
|
||||
if (e) {
|
||||
UpdateValueTree();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
#include "node/param.h"
|
||||
#include "widget/clickablelabel/clickablelabel.h"
|
||||
#include "widget/nodevaluetree/nodevaluetree.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
@@ -31,6 +32,8 @@ class NodeParamViewConnectedLabel : public QWidget {
|
||||
public:
|
||||
NodeParamViewConnectedLabel(const NodeInput& input, QWidget* parent = nullptr);
|
||||
|
||||
void SetTime(const rational &time);
|
||||
|
||||
signals:
|
||||
void RequestSelectNode(const QVector<Node*>& node);
|
||||
|
||||
@@ -46,12 +49,21 @@ private slots:
|
||||
private:
|
||||
void UpdateLabel();
|
||||
|
||||
void UpdateValueTree();
|
||||
|
||||
ClickableLabel* connected_to_lbl_;
|
||||
|
||||
NodeInput input_;
|
||||
|
||||
Node *connected_node_;
|
||||
|
||||
NodeValueTree *value_tree_;
|
||||
|
||||
rational time_;
|
||||
|
||||
private slots:
|
||||
void SetValueTreeVisible(bool e);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -383,6 +383,10 @@ void NodeParamViewItemBody::SetTime(const rational &time)
|
||||
ui_obj.key_control->SetTime(time);
|
||||
}
|
||||
|
||||
if (ui_obj.connected_label) {
|
||||
ui_obj.connected_label->SetTime(time);
|
||||
}
|
||||
|
||||
ui_obj.widget_bridge->SetTime(time);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2021 Olive Team
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
widget/nodevaluetree/nodevaluetree.cpp
|
||||
widget/nodevaluetree/nodevaluetree.h
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -0,0 +1,83 @@
|
||||
#include "nodevaluetree.h"
|
||||
|
||||
#include <QEvent>
|
||||
|
||||
#include "node/traverser.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
#define super QTreeWidget
|
||||
|
||||
NodeValueTree::NodeValueTree(QWidget *parent) :
|
||||
super(parent)
|
||||
{
|
||||
setColumnWidth(0, 0);
|
||||
setColumnCount(4);
|
||||
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
|
||||
static const int kMinimumRows = 10;
|
||||
setMinimumHeight(fontMetrics().height() * kMinimumRows);
|
||||
|
||||
Retranslate();
|
||||
}
|
||||
|
||||
void NodeValueTree::SetNode(const NodeInput &input, const rational &time)
|
||||
{
|
||||
clear();
|
||||
|
||||
NodeTraverser traverser;
|
||||
|
||||
Node *connected_node = input.GetConnectedOutput();
|
||||
Node::ValueHint value_hint = input.node()->GetValueHintForInput(input.input(), input.element());
|
||||
|
||||
NodeValueTable table = traverser.GenerateTable(connected_node, value_hint, TimeRange(time, time));
|
||||
|
||||
int index = traverser.GenerateRowValueElementIndex(input.node(), input.input(), input.element(), &table);
|
||||
|
||||
for (int i=0; i<table.Count(); i++) {
|
||||
const NodeValue &value = table.at(i);
|
||||
QTreeWidgetItem *item = new QTreeWidgetItem(this);
|
||||
|
||||
Node::ValueHint hint = {{value.type()}, table.Count()-1-i, value.tag()};
|
||||
|
||||
QRadioButton *radio = new QRadioButton(this);
|
||||
radio->setProperty("input", QVariant::fromValue(input));
|
||||
radio->setProperty("hint", QVariant::fromValue(hint));
|
||||
if (i == index) {
|
||||
radio->setChecked(true);
|
||||
}
|
||||
connect(radio, &QRadioButton::clicked, this, &NodeValueTree::RadioButtonChecked);
|
||||
|
||||
setItemWidget(item, 0, radio);
|
||||
item->setText(1, NodeValue::GetPrettyDataTypeName(value.type()));
|
||||
item->setText(2, NodeValue::ValueToString(value.type(), value.data(), false));
|
||||
item->setText(3, value.source()->GetLabelAndName());
|
||||
}
|
||||
}
|
||||
|
||||
void NodeValueTree::changeEvent(QEvent *event)
|
||||
{
|
||||
if (event->type() == QEvent::LanguageChange) {
|
||||
Retranslate();
|
||||
}
|
||||
|
||||
super::changeEvent(event);
|
||||
}
|
||||
|
||||
void NodeValueTree::Retranslate()
|
||||
{
|
||||
setHeaderLabels({QString(), tr("Type"), tr("Value"), tr("Source")});
|
||||
}
|
||||
|
||||
void NodeValueTree::RadioButtonChecked(bool e)
|
||||
{
|
||||
if (e) {
|
||||
QRadioButton *btn = static_cast<QRadioButton*>(sender());
|
||||
Node::ValueHint hint = btn->property("hint").value<Node::ValueHint>();
|
||||
NodeInput input = btn->property("input").value<NodeInput>();
|
||||
|
||||
input.node()->SetValueHintForInput(input.input(), input.element(), hint);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
#ifndef NODEVALUETREE_H
|
||||
#define NODEVALUETREE_H
|
||||
|
||||
#include <QRadioButton>
|
||||
#include <QTreeWidget>
|
||||
|
||||
#include "node/node.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class NodeValueTree : public QTreeWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
NodeValueTree(QWidget *parent = nullptr);
|
||||
|
||||
void SetNode(const NodeInput &input, const rational &time);
|
||||
|
||||
protected:
|
||||
virtual void changeEvent(QEvent *event) override;
|
||||
|
||||
private:
|
||||
void Retranslate();
|
||||
|
||||
private slots:
|
||||
void RadioButtonChecked(bool e);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // NODEVALUETREE_H
|
||||
@@ -79,6 +79,11 @@ public:
|
||||
|
||||
void ZoomOut();
|
||||
|
||||
const QVector<Node*> &GetCurrentContexts() const
|
||||
{
|
||||
return filter_nodes_;
|
||||
}
|
||||
|
||||
public slots:
|
||||
void SetMiniMapEnabled(bool e)
|
||||
{
|
||||
|
||||
@@ -741,8 +741,9 @@ void MainWindow::UpdateNodePanelContextFromTimelinePanel(TimelinePanel *panel)
|
||||
context.append(viewer);
|
||||
}
|
||||
|
||||
QVector<Node*> old_contexts = node_panel_->GetCurrentContexts();
|
||||
node_panel_->SetGraph(viewer ? viewer->parent() : nullptr, context);
|
||||
if (viewer) {
|
||||
if (viewer && context != old_contexts) {
|
||||
node_panel_->SelectWithDependencies(context, false);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user